DIRECTORY Ascii, Atom, BasicTime, CodeTimer, CubicSplines, Feedback, FeedbackTypes, FS, GGAlign, GGBasicTypes, GGBoundBox, GGBuiltinShapes, GGCaret, GGControlPanelTypes, GGCoreOps, GGCoreTypes, GGDescribe, GGEvent, GGEventExtras, GGFileIn, GGFileOps, GGFileOut, GGHistory, GGHistoryTypes, GGInterfaceTypes, GGModelTypes, GGMouseEvent, GGMultiGravity, GGOutline, GGParent, GGParseIn, GGParseOut, GGRefresh, GGRefreshTypes, GGScene, GGSegment, GGSegmentTypes, GGSelect, GGSequence, GGSessionLog, GGShapes, GGSlice, GGSliceOps, GGState, GGTraj, GGTransform, GGUserInput, GGUserProfile, GGUIUtility, GGUtility, GGWindow, Imager, ImagerArtwork, ImagerInterpress, ImagerTransformation, Interpress, IO, Lines2d, PBasics, Real, RealFns, Rope, SlackProcess, TiogaOps, TiogaOpsDefs, Vectors2d, ViewerClasses, ViewerTools; GGEventImplF: CEDAR PROGRAM IMPORTS BasicTime, CodeTimer, Feedback, FS, GGAlign, GGBoundBox, GGBuiltinShapes, GGCaret, GGEvent, GGFileIn, GGFileOps, GGFileOut, GGHistory, GGMouseEvent, GGMultiGravity, GGOutline, GGParent, GGParseIn, GGParseOut, GGRefresh, GGScene, GGSegment, GGSelect, GGSessionLog, GGShapes, GGSlice, GGSliceOps, GGState, GGTraj, GGTransform, GGUserInput, GGUIUtility, GGUtility, GGWindow, Imager, ImagerArtwork, IO, Lines2d, PBasics, Rope, SlackProcess, TiogaOps, Vectors2d, ViewerTools EXPORTS GGEvent, GGEventExtras, GGHistoryTypes, GGInterfaceTypes = BEGIN BoundBox: TYPE = GGModelTypes.BoundBox; Color: TYPE = Imager.Color; ControlsObj: PUBLIC TYPE = GGControlPanelTypes.ControlsObj; -- exported to GGInterfaceTypes DisplayStyle: TYPE = GGModelTypes.DisplayStyle; FeatureCycler: TYPE = GGInterfaceTypes.FeatureCycler; FeatureData: TYPE = GGModelTypes.FeatureData; MsgRouter: TYPE = FeedbackTypes.MsgRouter; GGData: TYPE = GGInterfaceTypes.GGData; HistoryEvent: TYPE = GGHistoryTypes.HistoryEvent; HistoryTool: TYPE = REF HistoryToolObj; HistoryToolObj: PUBLIC TYPE = GGHistory.HistoryToolObj; -- exported to GGHistoryTypes Orientation: TYPE = GGModelTypes.Orientation; Point: TYPE = GGBasicTypes.Point; RefreshDataObj: PUBLIC TYPE = GGRefreshTypes.RefreshDataObj; Scene: TYPE = GGModelTypes.Scene; Segment: TYPE = GGSegmentTypes.Segment; SelectionClass: TYPE = GGModelTypes.SelectionClass; Sequence: TYPE = GGModelTypes.Sequence; SequenceOfReal: TYPE = GGCoreTypes.SequenceOfReal; Slice: TYPE = GGModelTypes.Slice; SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor; StrokeEnd: TYPE = Imager.StrokeEnd; StrokeJoint: TYPE = Imager.StrokeJoint; Traj: TYPE = GGModelTypes.Traj; TrajData: TYPE = GGModelTypes.TrajData; TrajEnd: TYPE = GGModelTypes.TrajEnd; TrajParts: TYPE = GGModelTypes.TrajParts; Transformation: TYPE = Imager.Transformation; UserInputProc: TYPE = GGEvent.UserInputProc; Vector: TYPE = GGBasicTypes.Vector; Viewer: TYPE = ViewerClasses.Viewer; WalkProc: TYPE = GGModelTypes.WalkProc; borderWidth: REAL _ 2.0; reallyBigReal: REAL = 1.0e37; ScriptAction: UserInputProc = { atom: ATOM _ NARROW[event.rest.first]; SELECT atom FROM $Open => OpenScript[ggData, event]; $Append => AppendToScript[ggData, event]; $Close => CloseScript[ggData, event]; $Playback => PlaybackScript[ggData, event]; $FastPlay => FastPlayScript[ggData, event]; ENDCASE => ERROR }; ShowScripts: UserInputProc = { stream: IO.STREAM _ IO.ROS[]; GGParseOut.WriteListOfRope[stream, ggData.debug.autoScriptNames.list]; Feedback.Append[ggData.router, oneLiner, $Show, IO.RopeFromROS[stream]]; }; OpenScript: PROC [ggData: GGData, event: LIST OF REF ANY] = { name: Rope.ROPE _ ViewerTools.GetSelectionContents[]; [ggData.debug.writeScriptStream, ggData.debug.writeScriptName] _ GGSessionLog.OpenScript[name, ggData, ggData.debug.writeScriptStream, ggData.debug.writeScriptName]; [] _ SlackProcess.EnableSessionLogging[ggData.slackHandle]; }; AppendToScript: PROC [ggData: GGData, event: LIST OF REF ANY] = { name: Rope.ROPE _ ViewerTools.GetSelectionContents[]; [ggData.debug.writeScriptStream, ggData.debug.writeScriptName] _ GGSessionLog.AppendScript[name, ggData, ggData.debug.writeScriptStream, ggData.debug.writeScriptName]; [] _ SlackProcess.EnableSessionLogging[ggData.slackHandle]; }; CloseScript: PROC [ggData: GGData, event: LIST OF REF ANY] = { [] _ SlackProcess.DisableSessionLogging[ggData.slackHandle]; [ggData.debug.writeScriptStream, ggData.debug.writeScriptName] _ GGSessionLog.CloseScript[ggData.debug.writeScriptStream, ggData.debug.writeScriptName, ggData.router]; }; PlaybackScript: PROC [ggData: GGData, event: LIST OF REF ANY] = { name: Rope.ROPE _ ViewerTools.GetSelectionContents[]; GGSessionLog.PlaybackFromFile[name, ggData]; }; FastPlayScript: PROC [ggData: GGData, event: LIST OF REF ANY] = { name: Rope.ROPE _ ViewerTools.GetSelectionContents[]; GGUserInput.EventNotify[ggData, LIST[$DisableRefresh]]; -- more kosher GGSessionLog.PlaybackFromFile[name, ggData]; GGUserInput.EventNotify[ggData, LIST[$EnableRefresh]]; GGUserInput.EventNotify[ggData, LIST[$Refresh]]; }; MergeFromGargoyle: UserInputProc = { theirData: GGData _ GGState.GetGGInputFocus[]; IF theirData = NIL THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "MergeFromGargoyle failed: place input focus in source Gargoyle viewer"] ELSE { newSlices: LIST OF Slice; theirScene: Scene _ theirData.scene; GGHistory.NewCapture["Merge from Gargoyle", ggData]; -- capture before update newSlices _ GGScene.CopySelectedParts[fromScene: theirScene, toScene: ggData.scene]; IF newSlices#NIL THEN { GGAlign.UpdateBagsForNewSlices[newSlices, ggData]; GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedParts: TRUE]; GGHistory.PushCurrent[ggData]; Feedback.Append[ggData.router, oneLiner, $Feedback, "Merge from Gargoyle: completed"]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: sceneBag, edited: TRUE, okToSkipCapture: FALSE]; }; }; }; StuffIt: PUBLIC UserInputProc = { IF GGSelect.NoSelections[ggData.scene, normal] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "Stuff failed: select some objects for stuffing"] ELSE { op: ATOM _ NARROW[event.first]; fileName, fullName: Rope.ROPE; tempQuality: GGInterfaceTypes.QualityMode _ ggData.camera.quality; tempStyle: GGInterfaceTypes.DisplayStyle _ ggData.camera.displayStyle; displayStyle: GGInterfaceTypes.DisplayStyle _ ggData.camera.displayStyle; toFile, ipOnly, bordered, fit: BOOL _ FALSE; success, versionSpecified, noName: BOOL _ FALSE; ipOnly _ SELECT op FROM $IPToTiogaAlt, $IPToTioga, $IPToTiogaBordered, $IPToTiogaFit, $IPToTiogaBorderedAndFit => TRUE, ENDCASE => FALSE; displayStyle _ SELECT op FROM $StuffToTiogaAlt, $StuffToFileAlt, $IPToTiogaAlt => screen, ENDCASE => print; toFile _ SELECT op FROM $StuffToFile, $StuffToFileAlt => TRUE, ENDCASE => FALSE; bordered _ SELECT op FROM $StuffToTiogaBordered, $StuffToTiogaBorderedAndFit, $IPToTiogaBordered, $IPToTiogaBorderedAndFit => TRUE, ENDCASE => FALSE; fit _ SELECT op FROM $StuffToTiogaFit, $StuffToTiogaBorderedAndFit, $IPToTiogaFit, $IPToTiogaBorderedAndFit => TRUE, ENDCASE => FALSE; IF toFile THEN { -- get a filename, build a scene [fileName, fullName, success, versionSpecified, noName] _ GGEvent.FileNameFromEvent["StuffToFile", event.rest, ggData.currentWDir, ggData.router]; IF NOT success THEN GOTO Abort ELSE { startTime: BasicTime.GMT; totalTime: INT; fileScene: Scene _ GGScene.CreateScene[]; f: IO.STREAM _ FS.StreamOpen[fullName, $create ! FS.Error, IO.Error => GOTO Abort;]; Feedback.PutF[ggData.router, begin, $Statistics, "Stuffing to file %g", [rope[fullName]] ]; startTime _ BasicTime.Now[]; FOR slice: LIST OF Slice _ GGUtility.OrderedSelectionList[ggData, decr], slice.rest UNTIL slice=NIL DO GGScene.AddSlice[fileScene, slice.first]; ENDLOOP; GGFileOut.FileoutSceneAndOptions[f, ggData, fullName, fileScene]; GGUIUtility.SafeClose[f, ggData.router]; totalTime _ BasicTime.Period[startTime, BasicTime.Now[]]; Feedback.PutF[ggData.router, end, $Statistics, " Done in time (%r)", [integer[totalTime]] ]; GGEvent.SawTextFinish[ggData, NIL]; }; EXITS Abort => Feedback.PutF[ggData.router, oneLiner, $Complaint, IF noName THEN "StuffToFile failed: no file name selected" ELSE "StuffToFile failed: could not open %g", [rope[fileName]] ]; } ELSE { DoStuff: PROC [context: Imager.Context] = { ENABLE UNWIND => { ggData.camera.quality _ tempQuality; ggData.camera.displayStyle _ tempStyle; }; DoStuffIt: PROC = { ggData.camera.quality _ quality; -- the enumerated type value "quality" ggData.camera.displayStyle _ displayStyle; context.TranslateT[t: [-bRect.x, -bRect.y]]; FOR slice: LIST OF Slice _ GGUtility.OrderedSelectionList[ggData, decr], slice.rest UNTIL slice=NIL DO GGSliceOps.DrawParts[slice.first, NIL, context, ggData.camera, FALSE]; ENDLOOP; IF bordered THEN { context.SetStrokeWidth[borderWidth]; context.SetGray[1]; GGShapes.DrawBoundBox[context, bBox]; }; ggData.camera.quality _ tempQuality; ggData.camera.displayStyle _ tempStyle; }; Imager.DoSave[context, DoStuffIt]; }; bBox: BoundBox _ GGScene.BoundBoxOfSelected[ggData.scene, normal, TRUE]; bRect: Imager.Rectangle _ GGBoundBox.RectangleFromBoundBox[bBox]; selViewer: Viewer; selLevel: TiogaOps.SelectionGrain; selStart: TiogaOpsDefs.Location; pendingDelete: BOOL _ FALSE; IF NOT PBasics.IsBound[LOOPHOLE[ImagerArtwork.PasteArtwork]] THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "Stuff failed: install Artwork, then retry Stuff operation"]; RETURN; }; [viewer: selViewer, start: selStart, level: selLevel, pendingDelete: pendingDelete] _ TiogaOps.GetSelection[primary]; IF selViewer=NIL OR (selLevel#node AND selLevel#branch) THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "Stuff failed: stuffing requires a node level Tioga selection (pending delete for replace)"]; RETURN; }; fullName _ IF GGState.GetFullName[ggData]#NIL THEN GGFileOps.GetGargoyleFileName["Stuff", GGState.GetFullName[ggData], ggData.currentWDir, ggData.router, FALSE].fullName ELSE "Gargoyle"; -- should check success here Feedback.PutF[ggData.router, begin, $Feedback, "Stuffing from file %g. ", [rope[fullName]] ]; IF NOT pendingDelete THEN { TiogaOps.Break[]; [viewer: selViewer, start: selStart] _ TiogaOps.GetSelection[primary]; TiogaOps.SetSelection[selViewer, selStart, selStart, node, TRUE, TRUE, primary]; }; ImagerArtwork.PasteArtwork[action: DoStuff, bounds: [0.0, 0.0, bRect.w, bRect.h], m: ImagerArtwork.Points[], clip: FALSE, fit: fit, caption: FALSE]; IF NOT ipOnly THEN { fileRope: Rope.ROPE; viewer: Viewer; stuffedLoc: TiogaOpsDefs.Location; fileScene: Scene _ GGScene.CreateScene[]; rs: IO.STREAM _ IO.ROS[]; [viewer, stuffedLoc] _ TiogaOps.GetSelection[primary]; IF stuffedLoc.node#NIL THEN { FOR slice: LIST OF Slice _ GGUtility.OrderedSelectionList[ggData, decr], slice.rest UNTIL slice=NIL DO GGScene.AddSlice[fileScene, slice.first]; ENDLOOP; GGFileOut.FileoutSceneAndOptions[rs, ggData, IO.PutFR["stuffed from %g at %g", [rope[GGFileOps.FNameToGName[fullName]]], IO.time[] ], fileScene ]; fileRope _ IO.RopeFromROS[self: rs, close: TRUE]; TiogaOps.PutProp[n: stuffedLoc.node, name: $GGFile, value: fileRope]; }; }; Feedback.Append[ggData.router, end, $Feedback, "Stuffing complete"]; }; }; }; Grab: PUBLIC UserInputProc = { ggFileProp: Rope.ROPE; selectedLoc: TiogaOpsDefs.Location _ TiogaOps.GetSelection[primary].start; ggFileProp _ NARROW[TiogaOps.GetProp[selectedLoc.node, $GGFile]]; IF ggFileProp#NIL THEN { success: BOOL _ FALSE; sceneName: Rope.ROPE; f: IO.STREAM; IF event.first=$GetFromTioga THEN GGEvent.Clear[ggData, LIST[$Clear]]; -- full clear Feedback.Append[ggData.router, begin, $Feedback, "Grabbing node ... "]; f _ IO.RIS[ggFileProp]; [success, sceneName] _ GGFileIn.FileinSceneOnly[f, ggData.scene, TRUE, ggData.camera]; IF success THEN { GGEvent.SawTextFinish[ggData, LIST[$SawTextFinish]]; Feedback.Append[ggData.router, end, $Feedback, sceneName]; GGWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireScene, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE]; } ELSE Feedback.Append[ggData.router, oneLiner, $Complaint, "FromTioga failed: malformed Gargoyle property on selected node"]; GGUIUtility.SafeClose[f]; } ELSE Feedback.Append[ggData.router, oneLiner, $Complaint, "FromTioga failed: no Gargoyle property on selected Tioga node"]; }; Top: PUBLIC UserInputProc = { IF GGSelect.NoSelections[ggData.scene, normal] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "Front failed: select some objects for moving to front"] ELSE { selected: LIST OF Slice; GGHistory.NewCapture["Top", ggData]; -- capture scene BEFORE UPDATE selected _ GGUtility.OrderedSelectionList[ggData, decr]; FOR slices: LIST OF Slice _ selected, slices.rest UNTIL slices=NIL DO GGEvent.SelectEntireSlice[slices.first, ggData.scene, normal, ggData]; GGScene.PutAtPriority[ggData.scene, slices.first, -1]; ENDLOOP; GGHistory.PushCurrent[ggData]; -- push captured history event onto history list Feedback.Append[ggData.router, oneLiner, $Feedback, "Front: selected objects moved to the front"]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedInPlace, ggData: ggData, remake: none, edited: TRUE, okToSkipCapture: FALSE]; }; }; ShowPriorityValue: PUBLIC UserInputProc = { sliceD: SliceDescriptor; scene: Scene _ ggData.scene; count: CARD _ GGScene.CountSelectedSlices[scene, first, normal]; IF count#1 THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "ShowPriority failed: select exactly one slice to show priority"] ELSE { sliceD _ GGScene.FirstSelectedSlice[scene, first, normal]; Feedback.PutF[ggData.router, oneLiner, $Show, "ShowPriority: %g", [integer[GGScene.GetPriority[scene, sliceD.slice]]] ]; }; }; Bottom: PUBLIC UserInputProc = { IF GGSelect.NoSelections[ggData.scene, normal] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "Back failed: select some objects for moving to back"] ELSE { selected: LIST OF Slice; GGHistory.NewCapture["Bottom", ggData]; -- capture scene BEFORE UPDATE selected _ GGUtility.OrderedSelectionList[ggData, incr]; FOR slices: LIST OF Slice _ selected, slices.rest UNTIL slices=NIL DO GGEvent.SelectEntireSlice[slices.first, ggData.scene, normal, ggData]; GGScene.PutAtPriority[ggData.scene, slices.first, 0]; ENDLOOP; GGHistory.PushCurrent[ggData]; -- push captured history event onto history list Feedback.PutF[ggData.router, oneLiner, $Feedback, "Back: selected objects moved to the back"]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedInPlace, ggData: ggData, remake: none, edited: TRUE, okToSkipCapture: FALSE]; }; }; UpOne: PUBLIC UserInputProc = { IF GGSelect.NoSelections[ggData.scene, normal] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "ForwardOne failed: select some objects for moving forward"] ELSE { selected: LIST OF Slice; GGHistory.NewCapture["Up one", ggData]; -- capture scene BEFORE UPDATE selected _ GGUtility.OrderedSelectionList[ggData, incr]; FOR slices: LIST OF Slice _ selected, slices.rest UNTIL slices=NIL DO GGEvent.SelectEntireSlice[slices.first, ggData.scene, normal, ggData]; GGScene.UpOne[ggData.scene, slices.first]; ENDLOOP; GGHistory.PushCurrent[ggData]; -- push captured history event onto history list Feedback.PutF[ggData.router, oneLiner, $Feedback, "ForwardOne: selected objects moved one slice toward the front"]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedInPlace, ggData: ggData, remake: none, edited: TRUE, okToSkipCapture: FALSE]; }; }; DownOne: PUBLIC UserInputProc = { IF GGSelect.NoSelections[ggData.scene, normal] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "BackOne failed: select some objects for moving backwards"] ELSE { selected: LIST OF Slice; GGHistory.NewCapture["Down one", ggData]; -- capture scene BEFORE UPDATE selected _ GGUtility.OrderedSelectionList[ggData, decr]; FOR slices: LIST OF Slice _ selected, slices.rest UNTIL slices=NIL DO GGEvent.SelectEntireSlice[slices.first, ggData.scene, normal, ggData]; GGScene.DownOne[ggData.scene, slices.first]; ENDLOOP; GGHistory.PushCurrent[ggData]; -- push captured history event onto history list Feedback.PutF[ggData.router, oneLiner, $Feedback, "BackOne: selected objects moved one slice toward the back"]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedInPlace, ggData: ggData, remake: none, edited: TRUE, okToSkipCapture: FALSE]; }; }; FindPriorityFromSelection: PUBLIC UserInputProc = { priority: INT _ NARROW[event.rest.first, REF INT]^; IF priority=LAST[INT] OR priority<0 THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "MatchPriority failed: select a reasonable priority number"] ELSE { slice: Slice _ GGScene.GetAtPriority[ggData.scene, priority]; IF slice#NIL THEN { GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedCPs: TRUE]; GGSelect.DeselectAll[scene: ggData.scene, selectClass: normal]; GGEvent.SelectEntireSlice[slice, ggData.scene, normal, ggData]; Feedback.PutF[ggData.router, oneLiner, $Feedback, "MatchPriority: object at priority %g selected", [integer[slice.priority]] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: FALSE]; }; }; }; PutInFront: PUBLIC UserInputProc = { scene: Scene _ ggData.scene; count: CARD _ GGScene.CountSelectedSlices[scene, first, normal]; IF count<2 THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "PutInFront failed: select at least two slices for put in front"] ELSE { lastDesc: SliceDescriptor _ GGScene.LastSelectedSlice[scene, first, normal]; IF lastDesc#NIL THEN { selected: LIST OF Slice; atSlice: Slice _ lastDesc.slice; GGHistory.NewCapture["Put in front", ggData]; -- capture scene BEFORE UPDATE GGSelect.DeselectEntireSlice[atSlice, scene, normal]; selected _ GGUtility.OrderedSelectionList[ggData, decr, TRUE]; [] _ GGScene.DeleteAllSelected[scene]; GGScene.PutInFront[scene: scene, slice: atSlice, slices: selected]; GGEvent.SelectEntireSlice[atSlice, scene, normal, ggData]; GGHistory.PushCurrent[ggData]; -- push captured history event onto history list Feedback.Append[ggData.router, oneLiner, $Feedback, "PutInFront: first selected objects moved in front of last selected object"]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedInPlace, ggData: ggData, remake: none, edited: TRUE, okToSkipCapture: FALSE]; }; }; }; PutBehind: PUBLIC UserInputProc = { scene: Scene _ ggData.scene; count: CARD _ GGScene.CountSelectedSlices[scene, first, normal]; IF count<2 THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "PutBehind failed: select at least two slices for put behind"] ELSE { lastDesc: SliceDescriptor _ GGScene.LastSelectedSlice[scene, first, normal]; IF lastDesc#NIL THEN { selected: LIST OF Slice; atSlice: Slice _ lastDesc.slice; GGHistory.NewCapture["Put behind", ggData]; -- capture scene BEFORE UPDATE GGSelect.DeselectEntireSlice[atSlice, scene, normal]; selected _ GGUtility.OrderedSelectionList[ggData, decr, TRUE]; [] _ GGScene.DeleteAllSelected[scene]; GGScene.PutBehind[scene: scene, slice: atSlice, slices: selected]; GGEvent.SelectEntireSlice[atSlice, scene, normal, ggData]; GGHistory.PushCurrent[ggData]; -- push captured history event onto history list Feedback.Append[ggData.router, oneLiner, $Feedback, "PutBehind: first selected objects moved behind last selected object"]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedInPlace, ggData: ggData, remake: none, edited: TRUE, okToSkipCapture: FALSE]; }; }; }; Exchange: PUBLIC UserInputProc = { sliceD1, sliceD2: SliceDescriptor; index: NAT _ 0; aborted: BOOL _ FALSE; scene: Scene _ ggData.scene; FindFirstTwo: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { IF index = 0 THEN sliceD1 _ sliceD ELSE IF index = 1 THEN sliceD2 _ sliceD ELSE done _ TRUE; -- there are too many selections index _ index + 1; }; aborted _ GGScene.WalkSelectedSlices[scene, first, FindFirstTwo, normal]; IF index#2 THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "Exchange failed: select exactly two top level slices for Exchange"] ELSE { priority1: INT _ GGScene.GetPriority[scene: ggData.scene, slice: sliceD1.slice]; priority2: INT _ GGScene.GetPriority[scene: ggData.scene, slice: sliceD2.slice]; GGHistory.NewCapture["Exchange priorities", ggData]; -- capture scene BEFORE UPDATE GGEvent.SelectEntireSlice[sliceD1.slice, ggData.scene, normal, ggData]; GGEvent.SelectEntireSlice[sliceD2.slice, ggData.scene, normal, ggData]; GGScene.RemoveSlice[ggData.scene, sliceD1.slice]; GGScene.RemoveSlice[ggData.scene, sliceD2.slice]; IF priority2>=priority1 THEN { GGScene.AddSlice[ggData.scene, sliceD2.slice, priority1]; -- first insert lower slice GGScene.AddSlice[ggData.scene, sliceD1.slice, priority2]; -- then insert upper slice } ELSE { GGScene.AddSlice[ggData.scene, sliceD1.slice, priority2]; -- first insert lower slice GGScene.AddSlice[ggData.scene, sliceD2.slice, priority1]; -- then insert upper slice }; GGHistory.PushCurrent[ggData]; -- push captured history event onto history list Feedback.Append[ggData.router, oneLiner, $Feedback, "Exchange: selected objects exchanged"]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedInPlace, ggData: ggData, remake: none, edited: TRUE, okToSkipCapture: FALSE]; }; }; UpFromSelection: PUBLIC UserInputProc = { deltaPriority: INT _ NARROW[event.rest.first, REF INT]^; IF deltaPriority=LAST[INT] OR deltaPriority<=0 THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "ForwardFromSelection failed: select a positive integer for moving forward"] ELSE { selected: LIST OF Slice _ GGUtility.OrderedSelectionList[ggData, incr]; GGHistory.NewCapture["Up from selection", ggData]; -- capture scene BEFORE UPDATE THROUGH [0..deltaPriority) DO FOR slices: LIST OF Slice _ selected, slices.rest UNTIL slices=NIL DO GGEvent.SelectEntireSlice[slices.first, ggData.scene, normal, ggData]; GGScene.UpOne[ggData.scene, slices.first]; ENDLOOP; ENDLOOP; GGHistory.PushCurrent[ggData]; -- push captured history event onto history list Feedback.PutF[ggData.router, oneLiner, $Feedback, "ForwardFromSelection: selected objects moved toward the front %g slice%g", [integer[deltaPriority]], [character[IF deltaPriority>1 THEN 's ELSE ' ]] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedInPlace, ggData: ggData, remake: none, edited: TRUE, okToSkipCapture: FALSE]; }; }; DownFromSelection: PUBLIC UserInputProc = { deltaPriority: INT _ NARROW[event.rest.first, REF INT]^; IF deltaPriority=LAST[INT] OR deltaPriority<=0 THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "BackFromSelection failed: select a positive integer for moving backward"] ELSE { selected: LIST OF Slice _ GGUtility.OrderedSelectionList[ggData, decr]; GGHistory.NewCapture["Down from selection", ggData]; -- capture scene BEFORE UPDATE THROUGH [0..deltaPriority) DO FOR slices: LIST OF Slice _ selected, slices.rest UNTIL slices=NIL DO GGEvent.SelectEntireSlice[slices.first, ggData.scene, normal, ggData]; GGScene.DownOne[ggData.scene, slices.first]; ENDLOOP; ENDLOOP; GGHistory.PushCurrent[ggData]; -- push captured history event onto history list Feedback.PutF[ggData.router, oneLiner, $Feedback, "BackFromSelection: selected objects moved toward the back %g slice%g", [integer[deltaPriority]], [character[IF deltaPriority>1 THEN 's ELSE ' ]] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedInPlace, ggData: ggData, remake: none, edited: TRUE, okToSkipCapture: FALSE]; }; }; PutAtSelection: PUBLIC UserInputProc = { newPriority: INT _ NARROW[event.rest.first, REF INT]^; IF newPriority=LAST[INT] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "PutAtSelection failed: select an integer for PutAtSelection"] ELSE SELECT TRUE FROM newPriority <= -2 => Feedback.Append[ggData.router, oneLiner, $Complaint, "PutAtSelection failed: provide an integer > -2 for PutAtSelection"]; newPriority=-1 => Top[ggData, event]; newPriority=0 => Bottom[ggData, event]; ENDCASE => { selected: LIST OF Slice _ GGUtility.OrderedSelectionList[ggData, decr, TRUE]; GGHistory.NewCapture["Put at selection", ggData]; -- capture scene BEFORE UPDATE [] _ GGScene.DeleteAllSelected[ggData.scene]; GGScene.AddSlices[ggData.scene, selected, newPriority]; GGEvent.SelectEntireSlice[selected.first, ggData.scene, normal, ggData]; GGHistory.PushCurrent[ggData]; -- push captured history event onto history list IF newPriority<=0 THEN Feedback.PutF[ggData.router, oneLiner, $Feedback, "PutAtSelection: selected objects moved to %g", IF newPriority=0 THEN [rope["back"]] ELSE [rope["front"]] ] ELSE Feedback.PutF[ggData.router, oneLiner, $Feedback, "PutAtSelection: selected objects moved to priority %g", [integer[newPriority]] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedInPlace, ggData: ggData, remake: none, edited: TRUE, okToSkipCapture: FALSE]; }; }; EndNewObject: PROC [slice: Slice, ggData: GGData, object: Rope.ROPE] = { GGHistory.NewCapture[Rope.Concat["Add new ", object], ggData]; -- capture scene BEFORE UPDATE GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedCPs: TRUE]; GGScene.AddSlice[ggData.scene, slice, -1]; GGSelect.DeselectAll[ggData.scene, normal]; GGEvent.SelectEntireSlice[slice, ggData.scene, normal, ggData]; GGRefresh.EnlargeStartBox[ggData, GGScene.BoundBoxOfSelected[ggData.scene, normal], NIL]; ggData.refresh.addedObject _ slice; GGHistory.PushCurrent[ggData]; -- push captured history event onto history list Feedback.PutF[ggData.router, oneLiner, $Feedback, "New%g: new %g added to scene", [rope[object]], [rope[object]] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectAdded, ggData: ggData, remake: sceneBag, edited: TRUE, okToSkipCapture: FALSE]; }; PolygonInCircle: UserInputProc = { sideCount: INT _ NARROW[event.rest.first, REF INT]^; exists: BOOL _ GGCaret.Exists[ggData.caret]; IF NOT exists THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "NewPolygon failed: caret required for NewPolygon"] ELSE IF sideCount<=0.0 OR sideCount=LAST[INT] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "NewPolygon failed: select a reasonable number of polygon sides"] ELSE { caretPoint: Point _ GGCaret.GetPoint[ggData.caret]; slice: Slice _ GGBuiltinShapes.PolygonInCircle[sideCount, caretPoint, ggData.hitTest.scaleUnit, ggData.defaults]; EndNewObject[slice, ggData, "Polygon"]; }; }; NewBox: UserInputProc = { sideLength: REAL _ NARROW[event.rest.first, REF REAL]^; exists: BOOL _ GGCaret.Exists[ggData.caret]; IF NOT exists THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "NewBox failed: caret required for NewBox"] ELSE IF sideLength>reallyBigReal THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "NewBox failed: select a reasonable side length for a new box"] ELSE { caretPoint: Point _ GGCaret.GetPoint[ggData.caret]; slice: Slice _ GGBuiltinShapes.Box[caretPoint, sideLength*ggData.hitTest.scaleUnit, ggData.defaults]; EndNewObject[slice, ggData, "Box"]; }; }; NewCircle: UserInputProc = { radius: REAL _ NARROW[event.rest.first, REF REAL]^; exists: BOOL _ GGCaret.Exists[ggData.caret]; IF NOT exists THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "NewCircle failed: caret required for NewCircle"] ELSE IF radius>reallyBigReal THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "NewCircle failed: select a reasonable radius for a new circle"] ELSE { caretPoint: Point _ GGCaret.GetPoint[ggData.caret]; slice: Slice _ GGBuiltinShapes.Circle[caretPoint, radius*ggData.hitTest.scaleUnit, ggData.defaults]; EndNewObject[slice, ggData, "Circle"]; }; }; NewKnotchedLine: UserInputProc = { length: REAL _ NARROW[event.rest.first, REF REAL]^; segCount: INT _ NARROW[event.rest.rest.first, REF INT]^; exists: BOOL _ GGCaret.Exists[ggData.caret]; IF NOT exists THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "NewKnotchedLine failed: caret required for NewKnotchedLine"] ELSE IF length>reallyBigReal OR segCount=LAST[INT] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "NewKnotchedLine failed: select reasonable length and count for a new knotched line"] ELSE { caretPoint: Point _ GGCaret.GetPoint[ggData.caret]; p1: Point _ Vectors2d.Add[caretPoint, [length*ggData.hitTest.scaleUnit, 0.0]]; slice: Slice _ GGBuiltinShapes.KnotchedLine[p0: caretPoint, p1: p1, segmentCount: segCount]; EndNewObject[slice, ggData, "Knotched line"]; }; }; NewArrow: UserInputProc = { shaftLength: REAL _ NARROW[event.rest.first, REF REAL]^; barbLength: REAL _ NARROW[event.rest.rest.first, REF REAL]^; exists: BOOL _ GGCaret.Exists[ggData.caret]; IF NOT exists THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "NewArrow failed: caret required for NewArrow"] ELSE IF shaftLength>reallyBigReal OR barbLength>reallyBigReal THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "NewArrow failed: select reasonable shaft and barb lengths for a new arrow"] ELSE { OPEN Vectors2d; slice: Slice; traj: Traj; seg: Segment; success: BOOL _ FALSE; shaftBottom, shaftTop, barbLeft, barbRight: Point; shaftBottom _ GGCaret.GetPoint[ggData.caret]; shaftLength _ shaftLength * ggData.hitTest.scaleUnit; -- convert to screen dots. barbLength _ barbLength * ggData.hitTest.scaleUnit; -- convert to screen dots. shaftTop _ Add[shaftBottom, [0.0, shaftLength]]; barbLeft _ Add[shaftBottom, Scale[Normalize[[-1.0,1.0]], barbLength]]; barbRight _ Add[shaftBottom, Scale[Normalize[[1.0,1.0]], barbLength]]; traj _ GGTraj.CreateTraj[shaftTop]; seg _ GGSegment.MakeLine[shaftTop, shaftBottom, NIL]; success _ GGTraj.AddSegment[traj, hi, seg, lo]; IF NOT success THEN ERROR; seg _ GGSegment.MakeLine[shaftBottom, barbLeft, NIL]; success _ GGTraj.AddSegment[traj, hi, seg, lo]; IF NOT success THEN ERROR; seg _ GGSegment.MakeLine[barbLeft, shaftBottom, NIL]; success _ GGTraj.AddSegment[traj, hi, seg, lo]; IF NOT success THEN ERROR; seg _ GGSegment.MakeLine[shaftBottom, barbRight, NIL]; success _ GGTraj.AddSegment[traj, hi, seg, lo]; IF NOT success THEN ERROR; slice _ GGOutline.CreateOutline[child: traj, fillColor: Imager.black]; EndNewObject[slice, ggData, "Arrow"]; }; }; Frame: UserInputProc = { frameWidth, frameHeight, frameXOffset, frameYOffset: REAL _ 0.0; box: GGBoundBox.BoundBox; sliceD: SliceDescriptor; eventList: LIST OF REF _ event.rest; frameWidth _ NARROW[eventList.first, REF REAL]^; -- in Gargoyle units (points) eventList _ eventList.rest; frameHeight _ NARROW[eventList.first, REF REAL]^; -- in Gargoyle units (points) eventList _ eventList.rest; IF eventList#NIL THEN { frameXOffset _ NARROW[eventList.first, REF REAL]^; -- in Gargoyle units (points) eventList _ eventList.rest; frameYOffset _ NARROW[eventList.first, REF REAL]^; -- in Gargoyle units (points) }; IF frameWidth>reallyBigReal OR frameHeight>reallyBigReal OR frameXOffset>reallyBigReal OR frameYOffset>reallyBigReal THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "NewFrame failed: select reasonable dimensions for frame"]; RETURN; }; box _ GGBoundBox.CreateBoundBox[frameXOffset, frameYOffset, frameXOffset+frameWidth, frameYOffset+frameHeight]; sliceD _ GGSlice.MakeBoxSlice[box, none, GGTransform.Identity[]]; GGHistory.NewCapture["Add frame", ggData]; -- capture scene BEFORE UPDATE GGScene.AddSlice[ggData.scene, sliceD.slice, -1]; GGRefresh.NullStartBox[ggData]; GGRefresh.EnlargeStartBox[ggData, GGSliceOps.GetBoundBox[sliceD.slice], NIL]; ggData.refresh.addedObject _ sliceD.slice; GGHistory.PushCurrent[ggData]; -- push captured history event onto history list Feedback.Append[ggData.router, oneLiner, $Feedback, "NewFrame: New frame added to scene"]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectAdded, ggData: ggData, remake: sceneBag, edited: TRUE, okToSkipCapture: FALSE]; }; SelectedBBox: UserInputProc = { IF GGSelect.NoSelections[ggData.scene, normal] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "SelectedBBox failed: no selections"] ELSE { box: GGBoundBox.BoundBox _ GGScene.SelectionBoxOfSelected[ggData.scene, normal, TRUE]; sliceD: SliceDescriptor _ GGSlice.MakeBoxSlice[box, none, GGTransform.Identity[]]; GGHistory.NewCapture["Add bound box of selected objects", ggData]; -- capture scene BEFORE UPDATE GGScene.AddSlice[ggData.scene, sliceD.slice, -1]; GGRefresh.NullStartBox[ggData]; GGRefresh.EnlargeStartBox[ggData, GGSliceOps.GetBoundBox[sliceD.slice], NIL]; ggData.refresh.addedObject _ sliceD.slice; GGHistory.PushCurrent[ggData]; -- push captured history event onto history list Feedback.Append[ggData.router, oneLiner, $Feedback, "SelectedBBox: bound box of selected objects added"]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectAdded, ggData: ggData, remake: sceneBag, edited: TRUE, okToSkipCapture: FALSE]; }; }; LineWidth: PUBLIC UserInputProc = { strokeWidth: REAL _ WITH event.rest.first SELECT FROM real: REF REAL => real^, int: REF INT => REAL[int^], ENDCASE => -1.0; SetStrokeWidth[ggData, strokeWidth]; }; SetStrokeWidth: PUBLIC PROC [ggData: GGData, strokeWidth: REAL] = { IF GGSelect.NoSelections[ggData.scene, normal] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "SetStrokeWidth failed: select some strokes for set stroke width"] ELSE IF strokeWidth>reallyBigReal OR strokeWidth=LAST[INT] OR strokeWidth < 0.0 THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "SetStrokeWidth failed: select a reasonable positive number for stroke width"] ELSE { DoLineWidth: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { thisBox _ GGSliceOps.SetStrokeWidth[sliceD.slice, sliceD.parts, strokeWidth, currentEvent]; IF thisBox#NIL THEN bBoxes _ CONS[thisBox, bBoxes]; }; bBoxes: LIST OF BoundBox; thisBox: BoundBox; currentEvent: HistoryEvent _ GGHistory.NewCurrent["Set stroke width", ggData]; GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedParts: TRUE]; [] _ GGScene.WalkSelectedSlices[ggData.scene, leaf, DoLineWidth, normal]; GGRefresh.EnlargeStartBox[ggData, GGBoundBox.BoundBoxOfBoxes[bBoxes], NIL]; GGHistory.PushCurrent[ggData]; Feedback.PutF[ggData.router, oneLiner, $Feedback, "SetStrokeWidth: selected objects have stroke width %g", [real[strokeWidth]]]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: none, edited: TRUE, okToSkipCapture: FALSE]; }; }; LineEnds: PUBLIC UserInputProc = { strokeEnd: StrokeEnd; argRope: Rope.ROPE _ NARROW[event.rest.first]; SELECT TRUE FROM Rope.Equal[argRope, "square", FALSE] => strokeEnd _ square; Rope.Equal[argRope, "butt", FALSE] => strokeEnd _ butt; Rope.Equal[argRope, "round", FALSE] => strokeEnd _ round; ENDCASE => { Feedback.Append[ggData.router, oneLiner, $Complaint, "EndFromSelection failed: select square, butt, or round for set stroke ends"]; RETURN; }; SetStrokeEnd[ggData, strokeEnd]; }; SetStrokeEnd: PUBLIC PROC [ggData: GGData, strokeEnd: Imager.StrokeEnd] = { IF GGSelect.NoSelections[ggData.scene, normal] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "SetStrokeEnd failed: select some objects for set stroke ends"] ELSE { DoLineEnds: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { GGSliceOps.SetStrokeEnd[sliceD.slice, sliceD.parts, strokeEnd, currentEvent]; }; currentEvent: HistoryEvent; GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedParts: TRUE]; currentEvent _ GGHistory.NewCurrent["Set stroke end", ggData]; [] _ GGScene.WalkSelectedSlices[ggData.scene, leaf, DoLineEnds, normal]; GGRefresh.EnlargeStartBox[ggData, GGScene.BoundBoxOfSelected[ggData.scene, normal], NIL]; GGHistory.PushCurrent[ggData]; Feedback.PutF[ggData.router, oneLiner, $Feedback, "Selected objects have stroke end %g", [rope[GetEndRope[strokeEnd]]]]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: none, edited: TRUE, okToSkipCapture: FALSE]; }; }; TrajJoints: PUBLIC UserInputProc = { IF GGSelect.NoSelections[ggData.scene, normal] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "JointFromSelection failed: select some objects for set stroke joints"] ELSE { DoTrajJoints: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { GGSliceOps.SetStrokeJoint[sliceD.slice, sliceD.parts, strokeJoint, currentEvent]; }; currentEvent: HistoryEvent; strokeJoint: StrokeJoint; argRope: Rope.ROPE _ NARROW[event.rest.first]; SELECT TRUE FROM Rope.Equal[argRope, "round", FALSE] => strokeJoint _ round; Rope.Equal[argRope, "miter", FALSE] => strokeJoint _ miter; Rope.Equal[argRope, "bevel", FALSE] => strokeJoint _ bevel; ENDCASE => { Feedback.Append[ggData.router, oneLiner, $Complaint, "JointFromSelection failed: select round, miter, or bevel for trajectory joints"]; RETURN; }; GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedParts: TRUE]; currentEvent _ GGHistory.NewCurrent["Set stroke joint", ggData]; [] _ GGScene.WalkSelectedSlices[ggData.scene, leaf, DoTrajJoints, normal]; GGRefresh.EnlargeStartBox[ggData, GGScene.BoundBoxOfSelected[ggData.scene, normal], NIL]; GGHistory.PushCurrent[ggData]; Feedback.PutF[ggData.router, oneLiner, $Feedback, "JointFromSelection: selected trajectories have stroke joints %g", [rope[argRope]]]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: none, edited: TRUE, okToSkipCapture: FALSE]; }; }; DashesFromSelection: PUBLIC UserInputProc = { ENABLE GGParseIn.SyntaxError => GOTO SyntaxError; pattern: SequenceOfReal; offset, length: REAL; allZeroes: BOOL _ TRUE; argRope: Rope.ROPE _ NARROW[event.rest.first]; argStream: IO.STREAM _ IO.RIS[argRope]; pattern _ GGParseIn.ReadArrayOfReal[argStream]; offset _ GGParseIn.ReadWReal[argStream]; length _ GGParseIn.ReadWReal[argStream]; FOR i: NAT IN [0..pattern.len) DO allZeroes _ allZeroes AND pattern[i]=0.0; ENDLOOP; IF allZeroes THEN GOTO SyntaxError; SetDashed[ggData, TRUE, pattern, offset, length]; EXITS SyntaxError => Feedback.Append[ggData.router, oneLiner, $Complaint, "DashesFromSelection failed: select a legal specification with non-zero pattern (e.g. [4 10] 2 0)"]; }; SetDashed: PUBLIC PROC [ggData: GGData, dashed: BOOL _ FALSE, pattern: SequenceOfReal _ NIL, offset: REAL _ 0.0, length: REAL _ -1.0] = { DoDashes: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { GGSliceOps.SetDashed[sliceD.slice, sliceD.parts, dashed, pattern, offset, length, currentEvent]; }; currentEvent: HistoryEvent; IF GGSelect.NoSelections[ggData.scene, normal] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "DashesFromSelection failed: select some objects for set dashes"] ELSE { currentEvent _ GGHistory.NewCurrent[IF dashed THEN "Dashes on" ELSE "Dashes off", ggData]; GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedParts: TRUE]; [] _ GGScene.WalkSelectedSlices[ggData.scene, leaf, DoDashes, normal]; GGHistory.PushCurrent[ggData]; Feedback.PutF[ggData.router, oneLiner, $Feedback, "Selected objects have dashes %g", [rope[GetDashesRope[dashed, pattern, offset, length]]] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: none, edited: TRUE, okToSkipCapture: FALSE]; }; }; DashesOff: PUBLIC UserInputProc = { SetDashed[ggData, FALSE]; }; GetSelectedDashPattern: PROC [ggData: GGData] RETURNS [dashed: BOOL _ FALSE, pattern: SequenceOfReal, offset, length: REAL _ 0.0, success: BOOL _ TRUE] = { DoCheckStrokeValues: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { thisDashed: BOOL _ FALSE; thisOffset, thisLength: REAL; thisPattern: SequenceOfReal; isUnique: BOOL _ FALSE; [thisDashed, thisPattern, thisOffset, thisLength, isUnique] _ GGSliceOps.GetDashed[sliceD.slice, sliceD.parts]; IF NOT isUnique THEN { dashed _ thisDashed; offset _ thisOffset; length _ thisLength; pattern _ thisPattern; RETURN[TRUE]; }; IF found THEN { done _ thisDashed # dashed OR (thisDashed AND (thisOffset # offset OR thisLength # length OR NOT GGUtility.EquivalentPatterns[thisPattern, pattern])); } ELSE { found _ TRUE; dashed _ thisDashed; offset _ thisOffset; length _ thisLength; pattern _ thisPattern; }; }; scene: Scene _ ggData.scene; found: BOOL _ FALSE; success _ NOT GGScene.WalkSelectedSlices[scene, first, DoCheckStrokeValues, normal]; }; GetSelectedStrokeWidth: PROC [ggData: GGData] RETURNS [strokeWidth: REAL _ 17.0, success: BOOL _ TRUE] = { DoCheckWidth: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { thisWidth: REAL; isUnique: BOOL _ FALSE; [thisWidth, isUnique] _ GGSliceOps.GetStrokeWidth[sliceD.slice, sliceD.parts]; IF NOT isUnique THEN { strokeWidth _ thisWidth; RETURN[TRUE]; }; IF found THEN done _ thisWidth # strokeWidth ELSE { found _ TRUE; strokeWidth _ thisWidth; }; }; scene: Scene _ ggData.scene; found: BOOL _ FALSE; success _ NOT GGScene.WalkSelectedSlices[scene, first, DoCheckWidth, normal]; }; GetSelectedStrokeEnd: PROC [ggData: GGData] RETURNS [strokeEnd: StrokeEnd _ square, success: BOOL _ TRUE] = { DoCheckEnd: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { thisEnd: StrokeEnd; isUnique: BOOL _ FALSE; [thisEnd, isUnique] _ GGSliceOps.GetStrokeEnd[sliceD.slice, sliceD.parts]; IF NOT isUnique THEN { strokeEnd _ thisEnd; RETURN[TRUE]; }; IF found THEN done _ thisEnd # strokeEnd ELSE { found _ TRUE; strokeEnd _ thisEnd; }; }; scene: Scene _ ggData.scene; found: BOOL _ FALSE; success _ NOT GGScene.WalkSelectedSlices[scene, first, DoCheckEnd, normal]; }; GetSelectedStrokeJoint: PROC [ggData: GGData] RETURNS [strokeJoint: StrokeJoint _ bevel, success: BOOL _ TRUE] = { DoCheckJoint: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { thisJoint: StrokeJoint; isUnique: BOOL _ FALSE; [thisJoint, isUnique] _ GGSliceOps.GetStrokeJoint[sliceD.slice, sliceD.parts]; IF NOT isUnique THEN { strokeJoint _ thisJoint; RETURN[TRUE]; }; IF found THEN done _ thisJoint # strokeJoint ELSE { found _ TRUE; strokeJoint _ thisJoint; }; }; scene: Scene _ ggData.scene; found: BOOL _ FALSE; success _ NOT GGScene.WalkSelectedSlices[scene, first, DoCheckJoint, normal]; }; PrintStrokeValues: PUBLIC UserInputProc = { IF GGSelect.NoSelections[ggData.scene, normal] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "ShowStrokeValues failed: select at least one stroke for ShowStrokeValues"] ELSE { dashed: BOOL _ FALSE; pattern: SequenceOfReal; offset, length, strokeWidth: REAL; strokeJoint: StrokeJoint; strokeEnd: StrokeEnd; success: BOOL _ TRUE; [strokeWidth, success] _ GetSelectedStrokeWidth[ggData]; IF NOT success THEN GOTO NotUnique; [strokeEnd, success] _ GetSelectedStrokeEnd[ggData]; IF NOT success THEN GOTO NotUnique; [strokeJoint, success] _ GetSelectedStrokeJoint[ggData]; IF NOT success THEN GOTO NotUnique; [dashed, pattern, offset, length, success] _ GetSelectedDashPattern[ggData]; IF NOT success THEN GOTO NotUnique; Feedback.PutF[ggData.router, oneLiner, $Show, "Stroke values: width: %g end: %g joint: %g dashes: %g", [real[strokeWidth]], [rope[GetEndRope[strokeEnd]]], [rope[GetJointRope[strokeJoint]]], [rope[GetDashesRope[dashed, pattern, offset, length]]] ]; EXITS NotUnique => Feedback.Append[ggData.router, oneLiner, $Complaint, "ShowStrokeValues failed: multiple stroke values are selected"]; }; }; CopyStrokeValues: PUBLIC UserInputProc = { scene: Scene _ ggData.scene; lastDesc: SliceDescriptor _ GGSelect.GetLastSelection[scene]; count: INT _ GGScene.CountSelectedSlices[scene, leaf, normal]; IF count<2 OR lastDesc=NIL THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "CopyStrokeValues failed: select at least one destination object, then one source stroke"] ELSE { DoCopyStroke: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { IF sliceD#lastDesc THEN { [] _ GGSliceOps.SetStrokeWidth[sliceD.slice, sliceD.parts, width, currentEvent]; GGSliceOps.SetStrokeEnd[sliceD.slice, sliceD.parts, end, currentEvent]; GGSliceOps.SetStrokeJoint[sliceD.slice, sliceD.parts, joint, currentEvent]; GGSliceOps.SetDashed[slice: sliceD.slice, parts: sliceD.parts, dashed: dashed, pattern: pattern, offset: offset, length: length, history: currentEvent]; }; }; dashed: BOOL _ FALSE; pattern: SequenceOfReal; offset, length, width: REAL; end: StrokeEnd; joint: StrokeJoint; success: BOOL _ FALSE; currentEvent: HistoryEvent; [width, success] _ GGSliceOps.GetStrokeWidth[lastDesc.slice, lastDesc.parts]; IF NOT success THEN GOTO NotUnique; [end, success] _ GGSliceOps.GetStrokeEnd[lastDesc.slice, lastDesc.parts]; IF NOT success THEN GOTO NotUnique; [joint, success] _ GGSliceOps.GetStrokeJoint[lastDesc.slice, lastDesc.parts]; IF NOT success THEN GOTO NotUnique; [dashed, pattern, offset, length, success] _ GGSliceOps.GetDashed[lastDesc.slice, lastDesc.parts]; IF NOT success THEN GOTO NotUnique; currentEvent _ GGHistory.NewCurrent["Copy stroke values", ggData]; GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedParts: TRUE]; [] _ GGScene.WalkSelectedSlices[scene, leaf, DoCopyStroke, normal]; GGRefresh.EnlargeStartBox[ggData, GGScene.BoundBoxOfSelected[ggData.scene, normal], NIL]; GGHistory.PushCurrent[ggData]; Feedback.PutF[ggData.router, oneLiner, $Feedback, "Copied: stroke width: %g end: %g joint: %g dashes: %g", [real[width]], [rope[GetEndRope[end]]], [rope[GetJointRope[joint]]], [rope[GetDashesRope[dashed, pattern, offset, length]]] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: none, edited: TRUE, okToSkipCapture: FALSE]; EXITS NotUnique => Feedback.Append[ggData.router, oneLiner, $Complaint, "CopyStrokeValues failed: there are multiple stroke values in the source (last selected) object."]; }; }; GetDashesRope: PROC [dashed: BOOL, pattern: SequenceOfReal, offset, length: REAL] RETURNS [r: Rope.ROPE] = { s: IO.STREAM; IF NOT dashed THEN RETURN["Not Dashed"]; s _ IO.ROS[]; s.PutChar[ '[ ]; -- open bracket FOR index: NAT IN [0..pattern.len) DO -- list of reals s.PutF["%g ", [real[pattern[index]]] ]; ENDLOOP; s.PutF["] %g %g", [real[offset]], [real[length]] ]; r _ IO.RopeFromROS[s]; }; GetEndRope: PROC [strokeEnd: StrokeEnd] RETURNS [r: Rope.ROPE] = { r _ SELECT strokeEnd FROM round => "round", butt => "butt", square => "square", ENDCASE => "none"; }; GetJointRope: PROC [jointEnd: StrokeJoint] RETURNS [r: Rope.ROPE] = { r _ SELECT jointEnd FROM round => "round", bevel => "bevel", miter => "miter", ENDCASE => "none"; }; SelectMatchingWidth: PUBLIC UserInputProc = { scene: Scene _ ggData.scene; width: REAL _ WITH event.rest.first SELECT FROM real: REF REAL => real^, int: REF INT =>REAL [int^], ENDCASE => Real.LargestNumber; IF width>reallyBigReal OR width=LAST[INT] OR width<0.0 THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "MatchSelectedWidth failed: select a real number >=0.0 for matching width"] ELSE { epsilon: REAL = 1.0E-3; DoSelectMatching: PROC [slice: Slice] RETURNS [done: BOOL _ FALSE] = { WidthProc: WalkProc = { RETURN [seg.strokeWidth=width OR ABS[seg.strokeWidth-width] GOTO SyntaxError; DoSelectMatching: PROC [slice: Slice] RETURNS [done: BOOL _ FALSE] = { DashProc: WalkProc = { keep _ FALSE; IF seg.dashed AND seg.pattern.len=pattern.len AND seg.offset=offset AND seg.length=length THEN { FOR index: NAT IN [0..pattern.len) DO -- list of reals IF seg.pattern[index]#pattern[index] THEN GOTO ExitLoop; ENDLOOP; RETURN[TRUE]; -- if you get here, you matched exactly EXITS ExitLoop => NULL; }; }; sliceD: SliceDescriptor _ GGSliceOps.WalkSegments[slice, DashProc]; -- get a descriptor of matching parts GGSelect.SelectSlice[sliceD, ggData.scene, normal]; -- and select it }; scene: Scene _ ggData.scene; argRope: Rope.ROPE _ NARROW[event.rest.first]; argStream: IO.STREAM _ IO.RIS[argRope]; dashed: BOOL _ FALSE; pattern: SequenceOfReal; offset, length: REAL; pattern _ GGParseIn.ReadArrayOfReal[argStream]; offset _ GGParseIn.ReadWReal[argStream]; length _ GGParseIn.ReadWReal[argStream]; GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedCPs: TRUE]; GGSelect.DeselectAll[ggData.scene, normal]; [] _ GGScene.WalkSlices[ggData.scene, leaf, DoSelectMatching]; Feedback.PutF[ggData.router, oneLiner, $Feedback, "MatchSelectedDashes: strokes with dashes %g selected", [rope[argRope]] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: FALSE]; EXITS SyntaxError => Feedback.Append[ggData.router, oneLiner, $Complaint, "MatchSelectedDashes failed: select a legal dash pattern for matching dashes"]; }; SetDefaultStrokeValues: PUBLIC UserInputProc = { IF GGSelect.NoSelections[ggData.scene, normal] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "SetDefaultStrokeValues failed: no stroke selected for new default values"] ELSE { dashed: BOOL _ FALSE; pattern: SequenceOfReal; offset, length, strokeWidth: REAL; strokeJoint: StrokeJoint; strokeEnd: StrokeEnd; success: BOOL _ FALSE; [dashed, pattern, offset, length, success] _ GetSelectedDashPattern[ggData]; IF NOT success THEN GOTO NotUnique; [strokeWidth, success] _ GetSelectedStrokeWidth[ggData]; IF NOT success THEN GOTO NotUnique; [strokeJoint, success] _ GetSelectedStrokeJoint[ggData]; IF NOT success THEN GOTO NotUnique; [strokeEnd, success] _ GetSelectedStrokeEnd[ggData]; IF NOT success THEN GOTO NotUnique; ggData.defaults^ _ [strokeWidth: strokeWidth, strokeJoint: strokeJoint, strokeEnd: strokeEnd, dashed: dashed, pattern: GGUtility.CopyPattern[pattern], offset: offset, length: length, strokeColor: ggData.defaults.strokeColor, fillColor: ggData.defaults.fillColor, font: ggData.defaults.font]; ShowDefaultStrokeValues[ggData, event]; EXITS NotUnique => Feedback.Append[ggData.router, oneLiner, $Complaint, "SetDefaultStrokeValues failed: there are multiple stroke values in the source (last selected) object"]; }; }; ShowDefaultStrokeValues: PUBLIC UserInputProc = { Feedback.PutF[ggData.router, oneLiner, $Show, "Default Stroke Values: width: %g end: %g joint: %g dashes: %g", [real[ggData.defaults.strokeWidth]], [rope[GetEndRope[ggData.defaults.strokeEnd]]], [rope[GetJointRope[ggData.defaults.strokeJoint]]], [rope[GetDashesRope[ggData.defaults.dashed, ggData.defaults.pattern, ggData.defaults.offset, ggData.defaults.length]]] ]; }; Arrows: PUBLIC UserInputProc = { IF GGSelect.NoSelections[ggData.scene, normal] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "Arrows failed: select some objects for arrows"] ELSE { DoArrows: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { loIsLeft: BOOL _ FALSE; loPoint: Point _ GGTraj.FetchJointPos[sliceD.slice, 0]; hiPoint: Point _ GGTraj.LastJointPos[sliceD.slice]; loIsLeft _ SELECT TRUE FROM loPoint.x < hiPoint.x => TRUE, loPoint.x = hiPoint.x AND loPoint.y <= hiPoint.y => TRUE, ENDCASE => FALSE; IF loIsLeft THEN GGSliceOps.SetArrows[sliceD.slice, NIL, leftArrows, rightArrows, currentEvent] ELSE GGSliceOps.SetArrows[sliceD.slice, NIL, rightArrows, leftArrows, currentEvent]; }; currentEvent: HistoryEvent; leftArrows, rightArrows: BOOL _ FALSE; arrowType: INT _ NARROW[event.rest.first, REF INT]^; SELECT arrowType FROM 0 => { Feedback.Append[ggData.router, oneLiner, $Feedback, "Selected objects will have no arrows."]; leftArrows _ FALSE; rightArrows _ FALSE; }; 1 => { Feedback.Append[ggData.router, oneLiner, $Feedback, "Selected objects will have left/down arrows."]; leftArrows _ TRUE; rightArrows _ FALSE; }; 2 => { Feedback.Append[ggData.router, oneLiner, $Feedback, "Selected objects will have right/up arrows."]; leftArrows _ FALSE; rightArrows _ TRUE; }; 3 => { Feedback.Append[ggData.router, oneLiner, $Feedback, "Selected objects will have arrows on both ends."]; leftArrows _ TRUE; rightArrows _ TRUE; }; ENDCASE => { Feedback.Append[ggData.router, oneLiner, $Feedback, "Arrows failed: illegal argument to Arrows."]; RETURN; }; currentEvent _ GGHistory.NewCurrent["Set arrows", ggData]; [] _ GGScene.WalkSelectedSlices[ggData.scene, leaf, DoArrows, normal, $Traj]; GGHistory.PushCurrent[ggData]; Feedback.Append[ggData.router, oneLiner, $Feedback, "Arrows: completed"]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedInPlace, ggData: ggData, remake: none, edited: TRUE, okToSkipCapture: FALSE]; }; }; SelectAll: UserInputProc = { CodeTimer.StartInt[$SelectAll, $Gargoyle]; GGSelect.SelectAll[ggData.scene, normal]; GGEvent.SawTextFinish[ggData, NIL]; GGRefresh.NullStartBox[ggData]; GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: FALSE]; CodeTimer.StopInt[$SelectAll, $Gargoyle]; Feedback.Append[ggData.router, oneLiner, $Feedback, "Select All: selected everything"]; }; DeselectAll: UserInputProc = { GGSelect.DeselectAll[ggData.scene, normal]; GGEvent.SawTextFinish[ggData, NIL]; GGRefresh.NullStartBox[ggData]; GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: FALSE]; Feedback.Append[ggData.router, oneLiner, $Feedback, "Deselect All: deselected everything"]; }; CycleSelection: UserInputProc = { point: Point; normal: Vector; feature: FeatureData; hitData: REF ANY; featureCycler: FeatureCycler _ GGState.GetSelectionCycler[ggData]; direction: ATOM _ NARROW[event.rest.first]; IF featureCycler=NIL THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "CycleSelection failed: no selection cycler available"]; RETURN; }; SELECT direction FROM $Forward => [point, normal, feature, hitData] _ GGMultiGravity.NextFeature[featureCycler]; $Backward => [point, normal, feature, hitData] _ GGMultiGravity.PreviousFeature[featureCycler]; ENDCASE => ERROR; GGMouseEvent.SelectFromFeature[ggData, featureCycler.testPoint, point, normal, feature, hitData]; }; MoveSelection: UserInputProc = { action: ATOM _ NARROW[event.rest.first]; scene: Scene _ ggData.scene; normalLast: SliceDescriptor _ GGSelect.GetLastSelection[scene]; newLast: SliceDescriptor; opName: Rope.ROPE _ SELECT action FROM $Forward => "SelectForward", $Backward => "SelectBackward", $ShrinkForward => "ShrinkForward", $ShrinkBackward => "ShrinkBackward", $Grow => "Grow", ENDCASE => ERROR; BEGIN IF normalLast = NIL THEN GOTO NoLastSelection; newLast _ GGSliceOps.AlterParts[normalLast, action]; IF newLast=NIL THEN Feedback.Append[ggData.router, oneLiner, $Complaint, Rope.Concat[opName, " failed: can't walk any further in this direction"]] ELSE { GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedCPs: TRUE]; GGSelect.DeselectSlice[normalLast.slice, normalLast.parts, scene, normal]; GGSelect.SelectSlice[newLast, scene, normal]; Feedback.Append[ggData.router, oneLiner, $Feedback, Rope.Concat[opName, ": completed"]]; GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: FALSE]; }; EXITS NoLastSelection => { Feedback.Append[ggData.router, oneLiner, $Complaint, Rope.Concat[opName, " failed: please make an intial selection"]]; }; END; }; AreaSelectNew: UserInputProc = { AreaSelectAux[ggData, TRUE, TRUE]; Feedback.Append[ggData.router, oneLiner, $Feedback, "AreaSelectNew: new area selected"]; }; AreaSelectNewAndDelete: PUBLIC UserInputProc = { scene: Scene _ ggData.scene; IF GGSelect.NoSelections[scene, normal] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "AreaSelectNew failed: Select bounding objects for Area Select New And Delete"] ELSE { -- there were some original selections DoDelete: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { [newSelectList, ptr] _ GGUtility.AddSlice[sliceD.slice, newSelectList, ptr]; }; newSelectList, ptr: LIST OF Slice; [newSelectList, ptr] _ GGUtility.StartSliceList[]; GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedParts: TRUE]; AreaSelectAux[ggData: ggData, new: TRUE, paint: FALSE]; [] _ GGScene.WalkSelectedSlices[scene, first, DoDelete, normal]; FOR list: LIST OF Slice _ newSelectList, list.rest UNTIL list = NIL DO GGScene.DeleteSlice[ggData.scene, list.first]; ENDLOOP; GGCaret.NoAttractor[ggData.caret]; GGCaret.SitOn[ggData.caret, NIL]; Feedback.Append[ggData.router, oneLiner, $Feedback, "AreaSelectNew: new area selected"]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, edited: FALSE, okToSkipCapture: FALSE]; }; }; AreaSelectExtend: UserInputProc = { AreaSelectAux[ggData, FALSE, TRUE]; Feedback.Append[ggData.router, oneLiner, $Feedback, "AreaSelectExtend: new area extended"]; }; AreaSelectAux: PROC [ggData: GGData, new: BOOL _ TRUE, paint: BOOL _ TRUE] = { newSelectList, ptr: LIST OF Slice; scene: Scene _ ggData.scene; DoTestAllSlices: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { sBox: BoundBox _ GGSliceOps.GetTightBox[sliceD.slice, sliceD.parts]; DoTestSlice: PROC [next: Slice] RETURNS [done: BOOL _ FALSE] = { IF next=sliceD.slice THEN RETURN; IF NOT GGSelect.IsSelectedInFull[next, scene, normal] AND GGSliceOps.WithinBoundBox[next, sBox] THEN [newSelectList, ptr] _ GGUtility.AddSlice[sliceD.slice, newSelectList, ptr]; }; [] _ GGScene.WalkSlices[scene, first, DoTestSlice]; }; IF GGSelect.NoSelections[scene, normal] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "Area select failed: select bounding objects for new area select"] ELSE { GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedCPs: TRUE]; [newSelectList, ptr] _ GGUtility.StartSliceList[]; [] _ GGScene.WalkSelectedSlices[scene, first, DoTestAllSlices, normal]; IF new THEN GGSelect.DeselectAll[scene, normal]; -- get rid of old selection FOR list: LIST OF Slice _ newSelectList, list.rest UNTIL list = NIL DO GGSelect.SelectEntireSlice[list.first, scene, normal]; ENDLOOP; IF paint THEN GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: FALSE]; }; }; -- end AreaSelectAux SelectCoincident: UserInputProc = { DoCheckSegments: PROC [slice: Slice] RETURNS [done: BOOL _ FALSE] = { CoincidentProc: PROC [seg: Segment, transform: ImagerTransformation.Transformation] RETURNS [keep: BOOL] = { RETURN [seg.lo.x=seg.hi.x AND seg.lo.y=seg.hi.y]; -- what about transformation ?? }; sliceD: SliceDescriptor _ GGSliceOps.WalkSegments[slice, CoincidentProc]; IF sliceD#NIL AND NOT GGSliceOps.IsEmptyParts[sliceD] THEN { GGSelect.SelectSlice[sliceD, ggData.scene, normal]; -- and select count _ count + 1; }; }; count: INT _ 0; GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedCPs: TRUE]; GGSelect.DeselectAll[ggData.scene, normal]; [] _ GGScene.WalkSlices[ggData.scene, leaf, DoCheckSegments]; Feedback.PutF[ggData.router, oneLiner, $Feedback, "Coincident: %g coincident segments selected", [integer[count]] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: FALSE]; }; SelectUnseeableSegs: UserInputProc = { DoCheckSegments: PROC [slice: Slice] RETURNS [done: BOOL _ FALSE] = { type: ATOM _ GGSliceOps.GetType[slice]; IF type#$Text AND type#$IP THEN { UnseeProc: WalkProc = { RETURN [seg.strokeWidth<=0.0 OR seg.color=NIL]; }; sliceD: SliceDescriptor _ GGSliceOps.WalkSegments[slice, UnseeProc]; IF sliceD#NIL AND NOT GGSliceOps.IsEmptyParts[sliceD] THEN { GGSelect.SelectSlice[sliceD, ggData.scene, normal]; -- and select count _ count + 1; }; }; }; count: INT _ 0; GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedCPs: TRUE]; GGSelect.DeselectAll[ggData.scene, normal]; [] _ GGScene.WalkSlices[ggData.scene, leaf, DoCheckSegments]; Feedback.PutF[ggData.router, oneLiner, $Feedback, "UnSeeableSegs: %g unseeable segments selected", [integer[count]] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: FALSE]; }; SelectUnseeableObjs: UserInputProc = { DoCheckSegments: PROC [slice: Slice] RETURNS [done: BOOL _ FALSE] = { UnseeProc: WalkProc = { RETURN [seg.strokeWidth<=0.0 OR seg.color=NIL]; }; TallyUnseeableObjects: PROC [slice: Slice] RETURNS [visitChildren: BOOL, keep: BOOL _ FALSE, done: BOOL _ FALSE] = { IsBlackHole: PROC [sliceD: SliceDescriptor] RETURNS [BOOL] = { AllColinear: PROC [point: Point] RETURNS [done: BOOL _ TRUE] = { IF p1.x=Real.LargestNumber THEN {p1 _ point; RETURN[FALSE];}; -- first point IF p2.x=Real.LargestNumber THEN {p2 _ point; RETURN[FALSE];}; -- second point IF line=NIL THEN line _ Lines2d.LineFromPoints[p1, p2]; -- third point IF Lines2d.LineDistance[point, line]>epsilon THEN colinear _ FALSE ELSE RETURN[FALSE]; }; p1, p2: Point _ [Real.LargestNumber, Real.LargestNumber]; line: Lines2d.Line _ NIL; colinear: BOOL _ TRUE; -- important initialization to TRUE GGSliceOps.WalkPointsInDescriptor[sliceD, AllColinear]; RETURN[colinear]; }; sliceD: SliceDescriptor; IF GGParent.IsParent[slice] THEN RETURN[TRUE, FALSE, FALSE]; sliceD _ GGSliceOps.WalkSegments[slice, UnseeProc]; -- get a descriptor of unseeable segments IF NOT GGSliceOps.IsCompleteParts[sliceD] THEN RETURN[FALSE, FALSE, FALSE]; SELECT GGSliceOps.GetType[sliceD.slice] FROM $Traj => { color: Color; success: BOOL _ FALSE; [color, success] _ GGSliceOps.GetFillColor[GGParent.GetParent[sliceD.slice], NIL]; keep _ GGTraj.GetTrajRole[sliceD.slice]=open OR (success AND color=NIL) -- evaluation order guarantees closed OR IsBlackHole[sliceD]; }; $Box, $Circle => { -- Boxes, circles are closed keep _ GGSliceOps.GetFillColor[sliceD.slice, NIL].color=NIL OR IsBlackHole[sliceD]; }; $Text => { keep _ GGSlice.IsWhitespace[sliceD.slice]; }; $IP => { box: BoundBox _ GGSliceOps.GetTightBox[sliceD.slice]; keep _ ABS[box.hiX-box.loX] Feedback.Append[ggData.router, oneLiner, $Complaint, "SelectUnseeableObjects found an unknown object type"]; RETURN[FALSE, keep, FALSE]; }; tallyD: SliceDescriptor; aborted: BOOL _ FALSE; [tallyD, aborted] _ GGParent.TallyChildren[slice, TallyUnseeableObjects]; IF NOT aborted AND tallyD#NIL AND NOT GGSliceOps.IsEmptyParts[tallyD] THEN { GGSelect.SelectSlice[tallyD, ggData.scene, normal]; count _ count + 1; }; }; count: INT _ 0; epsilon: REAL _ 1.0/300.0; -- one pixel on a 300 spi printer GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedCPs: TRUE]; GGSelect.DeselectAll[ggData.scene, normal]; [] _ GGScene.WalkSlices[ggData.scene, all, DoCheckSegments]; Feedback.PutF[ggData.router, oneLiner, $Feedback, "UnSeeableObj: %g unseeable objects selected", [integer[count]] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: FALSE]; }; RegisterEventProcs: PROC = { OPEN GGUserInput; RegisterAction[$LineWidth, LineWidth, refReal]; RegisterAction[$LineEnd, LineEnds, rope]; RegisterAction[$TrajJoints, TrajJoints, rope]; RegisterAction[$DashesFromSelection, DashesFromSelection, rope]; RegisterAction[$DashesOff, DashesOff, none]; RegisterAction[$PrintStrokeValues, PrintStrokeValues, none]; RegisterAction[$CopyStrokeValues, CopyStrokeValues, none]; RegisterAction[$SelectMatchingWidth, SelectMatchingWidth, refReal]; RegisterAction[$SelectMatchingDashes, SelectMatchingDashes, rope]; RegisterAction[$SetDefaultStrokeValues, SetDefaultStrokeValues, none]; RegisterAction[$ShowDefaultStrokeValues, ShowDefaultStrokeValues, none]; RegisterAction[$Arrows, Arrows, none]; RegisterAction[$PolygonInCircle, PolygonInCircle, refInt]; RegisterAction[$KnotchedLine, NewKnotchedLine, none]; RegisterAction[$NewCircle, NewCircle, none]; RegisterAction[$NewBox, NewBox, none]; RegisterAction[$NewArrow, NewArrow, none]; RegisterAction[$Frame, Frame, none]; RegisterAction[$SelectedBBox, SelectedBBox, none]; RegisterAction[$Top, Top, none]; RegisterAction[$ShowPriorityValue, ShowPriorityValue, none]; RegisterAction[$Bottom, Bottom, none]; RegisterAction[$UpOne, UpOne, none]; RegisterAction[$FindPriorityFromSelection, FindPriorityFromSelection, refInt]; RegisterAction[$DownOne, DownOne, none]; RegisterAction[$PutInFront, PutInFront, none]; RegisterAction[$Exchange, Exchange, none]; RegisterAction[$PutBehind, PutBehind, none]; RegisterAction[$UpFromSelection, UpFromSelection, refInt]; RegisterAction[$PutAtSelection, PutAtSelection, refInt]; RegisterAction[$DownFromSelection, DownFromSelection, refInt]; RegisterAction[$IPToTioga, StuffIt, none]; RegisterAction[$IPToTiogaBordered, StuffIt, none]; RegisterAction[$IPToTiogaFit, StuffIt, none]; RegisterAction[$IPToTiogaBorderedAndFit, StuffIt, none]; RegisterAction[$IPToTiogaAlt, StuffIt, none]; RegisterAction[$MergeFromGargoyle, MergeFromGargoyle, none]; RegisterAction[$StuffToTioga, StuffIt, none]; RegisterAction[$StuffToTiogaAlt, StuffIt, none]; RegisterAction[$StuffToTiogaBordered, StuffIt, none]; RegisterAction[$StuffToTiogaFit, StuffIt, none]; RegisterAction[$StuffToTiogaBorderedAndFit, StuffIt, none]; RegisterAction[$StuffToFile, StuffIt, rope]; RegisterAction[$GetFromTioga, Grab, none]; RegisterAction[$GrabFromTioga, Grab, none]; RegisterAction[$MergeFromTioga, Grab, none]; RegisterAction[$ScriptAction, ScriptAction, none]; RegisterAction[$ShowScripts, ShowScripts, none]; RegisterAction[$EndOfSessionLogMessage, GGSessionLog.EndOfScriptMessage, none]; RegisterAction[$CycleSelection, CycleSelection, none]; RegisterAction[$MoveSelection, MoveSelection, none]; RegisterAction[$AreaSelectNew, AreaSelectNew, none]; RegisterAction[$AreaSelectNewAndDelete, AreaSelectNewAndDelete, none]; RegisterAction[$AreaSelectExtend, AreaSelectExtend, none]; RegisterAction[$SelectAll, SelectAll, none]; RegisterAction[$DeselectAll, DeselectAll, none]; RegisterAction[$SelectCoincident, SelectCoincident, none]; RegisterAction[$SelectUnseeableSegs, SelectUnseeableSegs, none]; RegisterAction[$SelectUnseeableObjs, SelectUnseeableObjs, none]; }; RegisterEventProcs[]; END. ζGGEventImplF.mesa Contents: Once an event reaches the front of the slack-process queue, it is dispatched to one of the procedures in this module. Copyright Σ 1988, 1989 by Xerox Corporation. All rights reserved. Bier, May 21, 1992 2:01 pm PDT Pier, April 28, 1992 5:14 pm PDT Doug Wyatt, December 15, 1989 8:13:30 pm PST Script Menu About scripting: GGWindowImpl.CreateWindow calls SlackProcess.Create each time a window is created, passing GGSessionLog.EnterAction as a parameter. SlackProcess will start calling this proc if EnableSessionLogging is called. ggData.debug.logStream is set by GGSessionLog.OpenScript. It is an IO.STREAM representing the currently open script for this ggData (there can be only one per ggData). About automatic scripting: GGWindowImpl.CreateWindow calls GGEventImplD.OpenAutoScript to make the first script. After that, GGEventImplD.NotNewVersion closes the last script and opens a new one by calling GGEventImplD.OpenAutoScript. The list of scripts that made the current picture is pruned back to one element by GGEventImplD.ClearAux whenever a Clear, Get, or Restore is performed. GGEventImplD.OpenAutoScript then updates this list. ggData.refresh.suppressRefresh _ TRUE; Stuff Menu Get Scene from InputFocus' Viewer ggData.refresh.startBoundBox _ GGScene.BoundBoxOfSelected[ggData.scene, normal]; The lower left corner of the bound box of the Gargoyle selection will be used as origin . The Gargoyle scene will be translated so the box lower left corner is at [0.0, 0.0]. The selected objects will be stuffed. create a new node and immediately replace it with the Stuff Now create a Gargoyle scene and hang it on the just created Artwork node. really Get/Grab/Merge from Tioga Overlap Operations Shape Menu Operations add a "picture frame" of a specified size to the image, origin at [frameXOffset, frameYOffset] ggData.refresh.startBoundBox^ _ GGSliceOps.GetBoundBox[sliceD.slice]^; add the bounding box of the selected items to the scene ggData.refresh.startBoundBox^ _ GGSliceOps.GetBoundBox[sliceD.slice]^; Style Operations bBox: BoundBox _ GGScene.BoundBoxOfSelected[ggData.scene, normal]; -- remember box before walking slices ggData.refresh.startBoundBox^ _ GGBoundBox.BoundBoxOfBoxes[bBoxes]^; GGBoundBox.EnlargeByBox[ggData.refresh.startBoundBox, bBox]; bBox: BoundBox _ GGScene.BoundBoxOfSelected[ggData.scene, normal]; GGBoundBox.EnlargeByBox[bBox: bBox, by: GGScene.BoundBoxOfSelected[ggData.scene, normal]]; ggData.refresh.startBoundBox^ _ bBox^; bBox: BoundBox; bBox _ GGScene.BoundBoxOfSelected[ggData.scene, normal]; GGBoundBox.EnlargeByBox[bBox: bBox, by: GGScene.BoundBoxOfSelected[ggData.scene, normal]]; ggData.refresh.startBoundBox^ _ bBox^; ggData.refresh.startBoundBox^ _ GGScene.BoundBoxOfSelected[ggData.scene, normal]^; This code assumes that all of the select segment stroke values are consistent. If not, the copy is not done. ggData.refresh.startBoundBox^ _ GGScene.BoundBoxOfSelected[scene, normal]^; GGBoundBox.EnlargeByBox[ggData.refresh.startBoundBox, GGScene.BoundBoxOfSelected[scene, normal]]; WalkProc: TYPE = PROC [seg: Segment] RETURNS [keep: BOOL]; WalkProc: TYPE = PROC [seg: Segment] RETURNS [keep: BOOL]; loIsLeft _ LoIsLeft[sliceD.slice]; Area Selection Operations CodeTimer.StartInt[$DeselectAll, $Gargoyle]; CodeTimer.StopInt[$DeselectAll, $Gargoyle]; Feedback.PutF[ggData.router, oneLiner, $Feedback, "CycleSelection: selection cycled"]; -- not needed. GGMouseEvent.SelectFromFeature will print a router message. Select all objects within area(s) bounded by original selection(s) and deselect original selection Select all objects within area(s) bounded by original selection(s) and delete original selection ggData.refresh.startBoundBox^ _ GGScene.BoundBoxOfSelected[scene, normal]^; Add all objects within area(s) bounded by original selection(s) to current selection For each selected object, see which scene objects are within its bounding box. Select all segments having co-located endpoints. A segment is unseeable if it has stroke width=0 or stroke color=none. Select all complete objects that are unseeable no matter what resolution, scale or overlap. A segment is unseeable if it has stroke width=0 or stroke color = none. An object is unseeable if it is whitespace-only text, if all of its segments are unseeable and it is an open object, if all of its segments are unseeable and it is a closed but unfilled object, or if all of its segments are unseeable and colinear. All segments are unseeable Three cases: an open traj, a closed and unfilled traj, a closed and filled traj with all its segments colinear (a black hole) Stroke Menu ArrowsMenu Shapes Menu Overlap Menu Interpress Menu Stuff Menu Script Menu Miscellaneous Keyboard only Select Menu RegisterAction[$AreaSelectDegenerate, AreaSelectDegenerate, none]; ΚJά•NewlineDelimiter – "cedar" style˜Icodešœ™šΟnœx™€K™BK™K™ K™,—K™šΟk ˜ JšœJžœήžœu˜‘—K˜š œžœž˜Jšžœ!žœιžœH˜έJšžœ<ž˜HK˜Kšœ žœ˜'Kšœžœ˜Kšœ žœžœ$Οc˜[Kšœžœ˜/Kšœžœ"˜5Kšœ žœ˜-Kšœ žœ˜*Kšœžœ˜'Kšœžœ˜1Kšœ žœžœ˜'KšœžœžœŸ˜UKšœ žœ˜-Kšœžœ˜!Kšœžœžœ!˜Kšœ<˜Kšœ’˜’Kšžœ˜K˜—Kš œ žœžœžœhžœ žœ Ÿ˜ΧKšœ]˜]šžœžœžœ˜Kšœ;™;Kšœ˜KšœF˜FKšœ;žœžœ ˜PK˜—KšœsΠbk‘œžœ˜”K™Išžœžœžœ˜Kšœžœ˜K˜Kšœ"˜"K˜)K–ldStream: STREAM _ NIL]š œžœžœžœžœ˜Kšœ6˜6šžœžœžœ˜š žœžœžœ ‘œžœžœž˜fKšœ)˜)Kšžœ˜—Kšœ-žœKžœ˜“K–&[self: STREAM, close: BOOL _ TRUE]šœ žœžœ˜1KšœE˜EK˜—Kšœ˜—KšœD˜DK˜—K˜—K˜K˜—šΠbnœžœ˜Kšœ ™ Kšœžœ˜KšœJ˜JKšœ žœ.˜A–ldStream: STREAM _ NIL]šžœ žœžœ˜Kšœ žœžœ˜Kšœžœ˜Kšœžœžœ˜ Kšžœžœžœ Ÿ ˜TK–M[feedback: Feedback.FeedbackData, msg: ROPE, msgType: Feedback.MsgType]˜GKšœžœžœ ˜Kšœ ‘œžœ˜Všžœ žœ˜Kšœžœ˜4K–M[feedback: Feedback.FeedbackData, msg: ROPE, msgType: Feedback.MsgType]˜:Kšœpžœžœ˜ŽK˜—Kšžœx˜|Jšœ˜Kšœ˜—Kšžœw˜{K˜—K™K™šœžœ˜Kšžœ-žœn˜‘šžœ˜Kšœ žœžœ˜KšœΟtœ Ÿ˜CKšœ8˜8–#[g: GGModelTypes.EntityGenerator]š žœ žœžœžœžœž˜EKšœF˜FKšœ6˜6Kšžœ˜—Kšœ ‘ œ Ÿ0˜PJšœb˜bKšœnžœžœ˜ŒKšœ˜—Kšœ˜K˜—šœžœ˜+K˜K˜Kšœžœ5˜@Kšžœ žœw˜†šžœ˜K˜:Kšœx˜xK˜—K˜K˜—šœžœ˜ Kšžœ-žœl˜Ÿšžœ˜Kšœ žœžœ˜Kšœ€œ Ÿ˜FKšœ8˜8–#[g: GGModelTypes.EntityGenerator]š žœ žœžœžœžœž˜EKšœF˜FKšœ5˜5Kšžœ˜—Kšœ ‘ œ Ÿ0˜PJšœ^˜^Kšœnžœžœ˜ŒKšœ˜—Kšœ˜K˜—šœžœ˜Kšžœ-žœr˜₯šžœ˜Kšœ žœžœ˜Kšœ€œ Ÿ˜FKšœ8˜8–#[g: GGModelTypes.EntityGenerator]š žœ žœžœžœžœž˜EKšœF˜FKšœ*˜*Kšžœ˜—Kšœ ‘ œ Ÿ0˜PJšœs˜sKšœnžœžœ˜ŒKšœ˜—Kšœ˜K˜—šœžœ˜!Kšžœ-žœq˜€šžœ˜Kšœ žœžœ˜Kšœ€œ Ÿ˜HKšœ8˜8–#[g: GGModelTypes.EntityGenerator]š žœ žœžœžœžœž˜EKšœF˜FKšœ,˜,Kšžœ˜—Kšœ ‘ œ Ÿ0˜PJšœo˜oKšœnžœžœ˜ŒKšœ˜—Kšœ˜K˜—šœžœ˜3Kš œ žœžœžœžœ˜3Kš žœ žœžœžœ žœr˜ššžœ˜Kšœ=˜=šžœžœžœ˜Jšœ ‘ œ!žœžœ˜PK–K[scene: GGModelTypes.Scene, selectClass: GGSegmentTypes.SelectionClass]šœ?˜?K–h[slice: GGModelTypes.Slice, scene: GGModelTypes.Scene, selectClass: GGSegmentTypes.SelectionClass]šœ?˜?K–M[feedback: Feedback.FeedbackData, msg: ROPE, msgType: Feedback.MsgType]šœ˜Kšœjžœžœ˜‰K˜—K˜—K˜K˜—š œžœ˜$K˜Kšœžœ5˜@šžœ ž˜Kšœv˜v—šžœ˜KšœL˜Lšžœ žœžœ˜Kšœ žœžœ˜K–`[scene: GGModelTypes.Scene, slice: GGModelTypes.Slice, slices: LIST OF GGModelTypes.Slice]šœ ˜ Kšœ€ œ Ÿ˜LK˜5Kšœ8žœ˜>Kšœ&˜&K–`[scene: GGModelTypes.Scene, slice: GGModelTypes.Slice, slices: LIST OF GGModelTypes.Slice]šœC˜CKšœ:˜:Kšœ ‘ œ Ÿ0˜PJšœ˜Kšœnžœžœ˜ŒK˜—K˜—K˜K˜—š œžœ˜#K˜Kšœžœ5˜@šžœ ž˜Kšœs˜s—šžœ˜KšœL˜Lšžœ žœžœ˜Kšœ žœžœ˜K–`[scene: GGModelTypes.Scene, slice: GGModelTypes.Slice, slices: LIST OF GGModelTypes.Slice]šœ ˜ Kšœ€ œ Ÿ˜JKšœ5˜5Kšœ8žœ˜>Kšœ&˜&KšœB˜BKšœ:˜:Kšœ ‘ œ Ÿ0˜PJšœ{˜{Kšœnžœžœ˜ŒK˜—K˜—K˜K˜—šœžœ˜"K˜"Kšœžœ˜Kšœ žœžœ˜K˜š  œžœžœžœžœ˜MJšžœ žœ˜"Jšžœžœ žœ˜'JšžœžœŸ ˜2Jšœ˜J˜—Kšœ1€œ €œ€˜IKšžœ žœz˜‰šžœ˜K–:[scene: GGModelTypes.Scene, slice: GGModelTypes.Slice]šœ žœB˜PK–:[scene: GGModelTypes.Scene, slice: GGModelTypes.Slice]šœ žœB˜PKšœ€œ Ÿ˜SKšœG˜GKšœG˜GK–K[scene: GGModelTypes.Scene, slice: GGModelTypes.Slice, priority: INT]šœ1˜1K–K[scene: GGModelTypes.Scene, slice: GGModelTypes.Slice, priority: INT]šœ1˜1–K[scene: GGModelTypes.Scene, slice: GGModelTypes.Slice, priority: INT]šžœžœ˜K–K[scene: GGModelTypes.Scene, slice: GGModelTypes.Slice, priority: INT]šœ:Ÿ˜UKšœ:Ÿ˜TK˜—šžœ˜Kšœ:Ÿ˜UK–K[scene: GGModelTypes.Scene, slice: GGModelTypes.Slice, priority: INT]šœ:Ÿ˜TK˜—Kšœ ‘ œ Ÿ0˜PJšœ\˜\Kšœnžœžœ˜ŒK˜—K˜K˜—šœžœ˜)Kš œžœžœžœžœ˜8Kš žœžœžœžœžœ‚˜΅šžœ˜Kšœ žœžœ6˜GKšœ€œ Ÿ˜Qšžœž˜–#[g: GGModelTypes.EntityGenerator]š žœ žœžœžœžœž˜EKšœF˜FKšœ*˜*Kšžœ˜—Kšžœ˜—Kšœ ‘ œ Ÿ0˜PJšœ£žœžœžœ ˜ΛKšœnžœžœ˜ŒK˜—K˜K˜—šœžœ˜+Kš œžœžœžœžœ˜8Kš žœžœžœžœžœ€˜³šžœ˜Kšœ žœžœ6˜GKšœ€œ Ÿ˜Sšžœž˜–#[g: GGModelTypes.EntityGenerator]š žœ žœžœžœžœž˜EKšœF˜FKšœ,˜,Kšžœ˜—Kšžœ˜—Kšœ ‘ œ Ÿ0˜PJšœŸžœžœžœ ˜ΗKšœnžœžœ˜ŒK˜—K˜K˜—šœžœ˜(Kš œ žœžœžœžœ˜6Kšžœ žœžœžœt˜‘šžœžœžœž˜Kšœ‘˜‘Kšœ%˜%Kšœ'˜'šžœ˜ Kšœ žœžœ6žœ˜MKšœ€œ Ÿ˜PKšœ-˜-Kšœ7˜7KšœH˜HKšœ ‘ œ Ÿ0˜PKš žœžœcžœžœžœ˜΄Kšžœ…˜‰Kšœnžœžœ˜ŒK˜——K˜K˜—K™K™š œžœ-žœ˜HKšœ ‘ œ+Ÿ˜]Kšœ ‘ œ!žœžœ˜PKšœ*˜*Kšœ+˜+K–€[outline: GGModelTypes.Outline, gargoyleData: GGInterfaceTypes.GargoyleData, selectClass: GGInterfaceTypes.SelectionClass]šœ?˜?Kšœ ‘œ;žœ˜YKšœ#˜#Kšœ ‘œŸ0˜PKšœs˜sKšœižœžœ˜‡K˜K˜—šœ˜"Kš œ žœžœžœžœ˜4Kšœžœ ˜,Kšžœžœžœi˜{Kš žœžœžœ žœžœžœw˜©šžœ˜Kšœ3˜3Kšœq˜qKšœ'˜'Kšœ˜—Kšœ˜K˜—šœ˜Kš œ žœžœžœžœ˜7Kšœžœ ˜,Kšžœžœžœa˜sKšžœžœžœu˜ššžœ˜Kšœ3˜3Kšœe˜eKšœ#˜#Kšœ˜—K˜K˜—š œ˜Kš œžœžœžœžœ˜3Kšœžœ ˜,Kšžœžœžœg˜yKšžœžœžœv˜—šžœ˜Kšœ3˜3Kšœd˜dKšœ&˜&K˜—K˜K˜—šœ˜"Kš œžœžœžœžœ˜3Kš œ žœžœžœžœ˜8Kšœžœ ˜,Kšžœžœžœs˜…Kš žœžœžœ žœžœžœ‹˜Βšžœ˜Kšœ3˜3KšœN˜NKšœ\˜\Kšœ-˜-K˜—K˜K˜—šœ˜Kš œ žœžœžœžœ˜8Kš œ žœžœžœžœ˜˜>KšœH˜HKšœZ™ZKšœ&™&Jšœ ‘œ;žœ˜YKšœ˜Kšœx˜xKšœwžœžœ˜•K˜—K˜K˜—š œžœ˜$Kšžœ-žœ}˜°šžœ˜š  œžœžœžœžœ˜MKšœQ˜QJ˜—Kšœ˜Kšœ™Kšœ˜Kšœžœžœ˜.šžœžœž˜Kšœžœ˜;Kšœžœ˜;Kšœžœ˜;šžœ˜ Kšœ‡˜‡Kšžœ˜K˜——Jšœ8™8Kšœ ‘ œ!žœžœ˜RKšœ@˜@KšœJ˜JKšœZ™ZKšœ&™&Jšœ ‘œ;žœ˜YKšœ˜Kšœ†˜†Kšœwžœžœ˜•K˜—K˜K˜—šœžœ˜-Kšžœžœ ˜1K˜Kšœžœ˜Kšœ žœžœ˜Kšœžœžœ˜.Kš œ žœžœžœžœ ˜'K˜Kšœ/˜/Kšœ(˜(Kšœ(˜(Kš žœžœžœžœžœžœ˜TKšžœ žœžœ ˜#K˜Kš‘ œ žœ˜1K˜šž˜Kšœ©˜©—K˜K˜—š œžœžœžœžœžœ žœžœ ˜‰š œžœžœžœžœ˜IJšœ`˜`J˜—Kšœ˜K˜Kšžœ-žœw˜ͺšžœ˜Kšœ$žœžœ žœ˜ZJšœR™RKšœ ‘ œ!žœžœ˜RKšœF˜FKšœ˜KšœŽ˜ŽKšœwžœžœ˜•K˜—K˜K˜—š œžœ˜#Kšœžœ˜K˜K˜—šœžœžœ žœžœ+žœžœžœ˜›š œžœžœžœžœ˜TKšœ žœžœ˜Kšœžœ˜Kšœ˜Jšœ žœžœ˜Kšœo˜ošžœžœ žœ˜KšœU˜UKšžœžœ˜ K˜—šžœ˜ šžœ˜šœ˜Jšœžœ ž˜&Jšœž˜Jšœž˜Jšžœ6˜9—K˜—šžœ˜Kšœžœ˜ Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜——Jšœ˜—J˜Jšœžœžœ˜Kšœ žœ(€œ€œ€˜TK˜K˜—š œžœžœžœžœžœ˜jš  œžœžœžœžœ˜MKšœ žœ˜Jšœ žœžœ˜KšœN˜Nšžœžœ žœ˜Kšœ˜Kšžœžœ˜ K˜—šžœ˜ Kšžœ˜#šžœ˜Kšœžœ˜ Kšœ˜Kšœ˜——Jšœ˜—J˜Jšœžœžœ˜Kšœ žœ(€œ €œ€˜MK˜K˜—š œžœžœ*žœžœ˜mš  œžœžœžœžœ˜KKšœ˜Jšœ žœžœ˜KšœJ˜Jšžœžœ žœ˜Kšœ˜Kšžœžœ˜ K˜—šžœ˜ Kšžœ˜šžœ˜Kšœžœ˜ Kšœ˜Kšœ˜——Jšœ˜—J˜Jšœžœžœ˜Kšœ žœ(€œ €œ€˜KK˜K˜—š œžœžœ-žœžœ˜rš  œžœžœžœžœ˜MKšœ˜Jšœ žœžœ˜KšœN˜Nšžœžœ žœ˜Kšœ˜Kšžœžœ˜ K˜—šžœ˜ Kšžœ˜#šžœ˜Kšœžœ˜ Kšœ˜Kšœ˜——Jšœ˜—J˜Jšœžœžœ˜Kšœ žœ(€œ €œ€˜MK˜K˜—šœžœ˜+Kšžœ-žœ˜΄šžœ˜Kšœžœžœ˜K˜Kšœžœ˜"Kšœ˜K˜Kšœ žœžœ˜K˜Kšœ8˜8Kšžœžœ žœžœ ˜#Kšœ4˜4Kšžœžœ žœžœ ˜#Kšœ8˜8Kšžœžœ žœžœ ˜#KšœL˜LKšžœžœ žœžœ ˜#K˜šœf˜fKšœ˜Kšœ˜Kšœ"˜"Kšœ9˜9K˜—šž˜Kšœ‚˜‚—K˜—K˜K˜—šœžœ˜*Kšœm™mK˜Kšœ=˜=Kšœžœ4˜>Kšžœ žœ žœžœ˜―šžœ˜š  œžœžœžœžœ˜Mšžœžœ˜JšœP˜PJšœG˜GJšœK˜KJ–Ω[slice: GGModelTypes.Slice, parts: GGModelTypes.SliceParts, dashed: BOOL _ FALSE, pattern: GGCoreTypes.SequenceOfReal _ NIL, offset: REAL _ 0.0, length: REAL _ -1.0, history: GGHistoryTypes.HistoryEvent]šœ˜˜˜J˜—J˜—Kšœžœžœ˜K˜Kšœžœ˜Kšœ˜Kšœ˜Kšœ žœžœ˜Jšœ˜J˜KšœM˜MKšžœžœ žœžœ ˜#KšœI˜IKšžœžœ žœžœ ˜#KšœM˜MKšžœžœ žœžœ ˜#Kšœb˜bKšžœžœ žœžœ ˜#JšœB˜BKšœK™KKšœ ‘ œ!žœžœ˜RKšœC˜CKšœa™aJšœ ‘œ;žœ˜YKšœ˜šœj˜jšœ˜Kšœ˜Kšœ˜Kšœ9˜9——Kšœwžœžœ˜•šž˜Kšœ₯˜₯—K˜—K˜K˜—š  œžœ žœ+žœžœ žœ˜lKšœžœžœ˜ Kšžœžœžœžœ˜(Kšœžœžœ˜ KšœŸ˜ š žœžœžœžœŸ˜7Kšœ'˜'Kšžœ˜—Kšœ3˜3Kšœžœ˜Kšœ˜K˜—š œžœžœ žœ˜Bšœžœ ž˜K˜K˜K˜Kšžœ ˜—Kšœ˜K˜—š œžœžœ žœ˜Ešœžœ ž˜K˜K˜Kšœ˜Kšžœ ˜—Kšœ˜K˜—šœžœ˜-K˜šœžœžœžœž˜/Kšœžœžœ ˜Kšœžœžœžœ˜Kšžœ˜—Kš žœžœžœžœžœ žœ˜Όšžœ˜Kšœ žœ ˜š œžœžœžœžœ˜Fš œ˜Kš ‘œžœžœžœžœ™:Kšœžœžœžœ!˜FK˜—KšœEŸ%˜jK–s[sliceD: GGModelTypes.SliceDescriptor, scene: GGModelTypes.Scene, selectClass: GGSegmentTypes.SelectionClass]šœ-Ÿ˜=K˜—Jšœ ‘ œ!žœžœ˜PKšœ$˜$Kšœ7˜7K–Ϋ[feedback: ViewerClasses.Viewer, msgType: GGError.MsgType, format: ROPE _ NIL, v1: IO.Value _ [null[]], v2: IO.Value _ [null[]], v3: IO.Value _ [null[]], v4: IO.Value _ [null[]], v5: IO.Value _ [null[]]]šœv˜vKšœjžœžœ˜‰K˜—K˜K˜—šœžœ˜.Kš’œžœ ˜1š œžœžœžœžœ˜Fšœ˜Kš ‘œžœžœžœžœ™:Kšœžœ˜ š žœ žœžœžœžœ˜`š žœžœžœžœŸ˜7Kšžœ#žœžœ ˜8Kšžœ˜—JšžœžœŸ'˜5šž˜Jšœ žœ˜—J˜—K˜—KšœDŸ%˜iK–s[sliceD: GGModelTypes.SliceDescriptor, scene: GGModelTypes.Scene, selectClass: GGSegmentTypes.SelectionClass]šœ4Ÿ˜DK˜—K˜Kšœžœžœ˜.Kš œ žœžœžœžœ ˜'Kšœžœžœ˜K˜Kšœžœ˜K˜Kšœ/˜/Kšœ(˜(Kšœ(˜(Kšœ ‘ œ!žœžœ˜PKšœ+˜+Kšœ>˜>K–Ϋ[feedback: ViewerClasses.Viewer, msgType: GGError.MsgType, format: ROPE _ NIL, v1: IO.Value _ [null[]], v2: IO.Value _ [null[]], v3: IO.Value _ [null[]], v4: IO.Value _ [null[]], v5: IO.Value _ [null[]]]šœ|˜|Kšœjžœžœ˜‰šž˜Kšœ“˜“—K˜K˜—šœžœ˜0Kšžœ-žœ˜΄šžœ˜Kšœžœžœ˜K˜Kšœžœ˜"K˜K˜šœ žœžœ˜K˜—KšœL˜LKšžœžœ žœžœ ˜#Kšœ8˜8Kšžœžœ žœžœ ˜#Kšœ8˜8Kšžœžœ žœžœ ˜#Kšœ4˜4Kšžœžœ žœžœ ˜#Kšœ£˜£Kšœ'˜'šž˜Kšœͺ˜ͺ—K˜—K˜K˜—šœžœ˜1šœn˜nšœ$˜$Kšœ.˜.Kšœ2˜2Kšœy˜y——K˜—K˜šœžœ˜ Kšžœ-žœf˜™šžœ˜š œžœžœžœžœ˜IKšœ"™"Jšœ žœžœ˜Kšœ7˜7Kšœ3˜3šœ žœžœž˜Kšœžœ˜Kšœžœžœ˜9Kšžœžœ˜—Kšžœ žœ$žœ(˜_Kšžœ$žœ)˜TJ˜J˜—Kšœ˜Kšœžœžœ˜&Kš œ žœžœžœžœ˜4šžœ ž˜šœ˜Jšœ]˜]Jšœ žœ˜Jšœžœ˜J˜—šœ˜Jšœd˜dJšœ žœ˜Jšœžœ˜J˜—˜Jšœc˜cJšœ žœ˜Jšœžœ˜J˜—˜Jšœg˜gJšœ žœ˜Jšœžœ˜J˜—šžœ˜ Jšœb˜bJšžœ˜J˜——Kšœ:˜:KšœM˜MKšœ˜KšœI˜IKšœnžœžœ˜ŒKšœ˜—Kšœ˜—K˜K™š œ˜Kšœ*˜*Kšœ)˜)Kšœžœ˜#Kšœ ‘ œ ˜Kšœjžœžœ˜‰Kšœ)˜)JšœX˜XK˜K˜—š œ˜Kšœ,™,Kšœ+˜+Kšœžœ˜#Kšœ ‘ œ ˜Kšœjžœžœ˜‰Kšœ+™+Jšœ[˜[K˜K˜—šœ˜!Kšœ ˜ Kšœ˜Kšœ˜Kšœ žœžœ˜IprocšœB˜BKšœ žœžœ˜+šžœžœžœ˜K–M[feedback: Feedback.FeedbackData, msg: ROPE, msgType: Feedback.MsgType]šœm˜mKšžœ˜K˜—šžœ ž˜KšœZ˜ZKšœ_˜_Kšžœžœ˜—Lšœa˜aJšœ’™’Kšœ˜K˜—š œ˜ Kšœžœžœ˜(K–K[scene: GGModelTypes.Scene, selectClass: GGSegmentTypes.SelectionClass]˜K˜?K˜šœ žœžœž˜&Kšœ˜Kšœ˜Kšœ"˜"Kšœ$˜$Kšœ˜Kšžœžœ˜—šž˜Kšžœžœžœžœ˜.Kšœ4˜4Kšžœ žœžœ˜’šžœ˜Kšœ ‘ œ!žœžœ˜PK˜JK˜-JšœX˜XKšœjžœžœ˜‰K˜—šž˜šœ˜Kšœv˜vK˜——Kšžœ˜—K˜K˜—š œ˜ K™bKšœžœžœ˜"JšœX˜XK˜K˜—šœžœ˜0K™`K–K[scene: GGModelTypes.Scene, selectClass: GGSegmentTypes.SelectionClass]˜Kšžœ&žœ…˜±šžœŸ&˜-š œžœžœžœžœ˜IKšœL˜LJ˜—Kšœžœžœ˜"Kšœ2˜2KšœK™KKšœ ‘ œ!žœžœ˜RKšœ#žœ žœ˜7Kšœ,€œ€œ€˜@š žœžœžœ"žœžœž˜FJšœ.˜.Jšžœ˜—Kšœ"˜"Jšœžœ˜!JšœX˜XKšœ}žœžœ˜œKšœ˜—Kšœ˜K˜—šœ˜#K™TKšœžœžœ˜#Jšœ[˜[Kšœ˜K˜—š  œžœžœžœ žœžœ˜NKšœN™NKšœžœžœ˜"K–_[gargoyleData: GGInterfaceTypes.GargoyleData, selectClass: GGInterfaceTypes.SelectionClass]˜š œžœžœžœžœ˜PKšœE˜Eš  œžœžœžœžœ˜@Kšžœžœžœ˜!Kšžœžœ0žœ&˜_KšžœM˜QJ˜—Kšœ$€œ €˜3J˜—Kšžœ&žœx˜€šžœ˜Kšœ ‘ œ!žœžœ˜PKšœ2˜2Kšœ,€œ€œ€˜GKšžœžœ&Ÿ˜Lš žœžœžœ"žœžœž˜FJšœ6˜6Jšžœ˜—Kšžœžœkžœžœ˜—K˜—KšœŸ˜K˜—šœ˜#Kšœ0™0š œžœžœžœžœ˜Ešœžœ@žœžœ˜lKšžœžœŸ˜QK˜—KšœI˜I–s[sliceD: GGModelTypes.SliceDescriptor, scene: GGModelTypes.Scene, selectClass: GGSegmentTypes.SelectionClass]š žœžœžœžœ!žœ˜š  œžœžœžœžœ˜@Kš žœžœžœžœŸ˜LKš žœžœžœžœŸ˜MKšžœžœžœ(Ÿ˜FKšžœ+žœ ž˜BKšžœžœžœ˜K˜—Kšœ9˜9Kšœžœ˜Kšœ žœžœŸ#˜:Kšœ*‘ œ˜7K–s[sliceD: GGModelTypes.SliceDescriptor, scene: GGModelTypes.Scene, selectClass: GGSegmentTypes.SelectionClass]šžœ ˜K˜—Kšœ˜Kš žœžœžœžœžœžœ˜=Kšœ4Ÿ)˜]Kšžœžœ$žœžœžœžœžœ˜LK™šžœ"ž˜,˜ K™}K˜ Kšœ žœžœ˜KšœMžœ˜Ršœ,˜,Kšžœ žœžœŸ%˜@Kšžœ˜—K˜—šœŸ˜0šœ-žœžœ˜˜>K˜Kš‘™Kšœ*˜*Kšœ2˜2Kšœ-˜-Kšœ8˜8Kšœ-˜-K˜Kš‘ ™ Kšœ<˜