DIRECTORY Ascii, Atom, Basics, BasicTime, CodeTimer, CubicSplines, Feedback, FeedbackTypes, FS, GGAlign, GGBasicTypes, GGBoundBox, GGBuiltinShapes, GGCaret, 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, GGUIUtility, GGUserInput, GGUserProfile, GGUtility, GGViewerOps, GGWindow, Imager, ImagerArtwork, ImagerInterpress, ImagerTransformation, IO, Lines2d, Real, RealFns, Rope, SlackProcess, TiogaOps, TiogaOpsDefs, Vectors2d, ViewerClasses; GGEventImplF: CEDAR PROGRAM IMPORTS Basics, 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, GGUIUtility, GGUserInput, GGUtility, GGViewerOps, GGWindow, Imager, ImagerArtwork, IO, Lines2d, Rope, SlackProcess, TiogaOps, Vectors2d EXPORTS GGEvent, GGEventExtras, GGHistoryTypes, GGInterfaceTypes = BEGIN BoundBox: TYPE = GGModelTypes.BoundBox; Color: TYPE = Imager.Color; 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] = { name: Rope.ROPE ¬ GGViewerOps.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] = { name: Rope.ROPE ¬ GGViewerOps.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] = { [] ¬ 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] = { name: Rope.ROPE ¬ GGViewerOps.GetSelectionContents[]; GGSessionLog.PlaybackFromFile[name, ggData]; }; FastPlayScript: PROC [ggData: GGData, event: LIST OF REF] = { name: Rope.ROPE ¬ GGViewerOps.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 Basics.IsBound[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 = { v: Viewer; selectedLoc: TiogaOpsDefs.Location; [viewer: v, start: selectedLoc] ¬ TiogaOps.GetSelection[primary]; IF v#NIL THEN { ggFileProp: Rope.ROPE ¬ NARROW[TiogaOps.GetProp[selectedLoc.node, $GGFile]]; IF ggFileProp#NIL THEN { success: BOOL ¬ FALSE; sceneName: Rope.ROPE; IF event.first=$GetFromTioga THEN GGEvent.Clear[ggData, LIST[$Clear]]; -- full clear Feedback.Append[ggData.router, begin, $Feedback, "Grabbing node ... "]; [success, sceneName] ¬ GGFileIn.FileinSceneOnly[IO.RIS[ggFileProp], 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"]; RETURN; }; }; 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.Append[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.Append[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.Append[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.PutFL[ggData.router, oneLiner, $Feedback, "ForwardFromSelection: selected objects moved toward the front %g slice%g", LIST[[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.PutFL[ggData.router, oneLiner, $Feedback, "BackFromSelection: selected objects moved toward the back %g slice%g", LIST[[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.PutFL[ggData.router, oneLiner, $Feedback, "New%g: new %g added to scene", LIST[[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.ReadReal[argStream]; length ¬ GGParseIn.ReadReal[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.PutFL[ggData.router, oneLiner, $Show, "Stroke values: width: %g end: %g joint: %g dashes: %g", LIST[[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.PutFL[ggData.router, oneLiner, $Feedback, "Copied: stroke width: %g end: %g joint: %g dashes: %g", LIST[[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.PutF1["%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.ReadReal[argStream]; length ¬ GGParseIn.ReadReal[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, selectionChanged: TRUE, 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.PutFL[ggData.router, oneLiner, $Show, "Default Stroke Values: width: %g end: %g joint: %g dashes: %g", LIST[[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, selectionChanged: TRUE, okToSkipCapture: FALSE]; CodeTimer.StopInt[$SelectAll, $Gargoyle]; Feedback.Append[ggData.router, oneLiner, $Feedback, "Select All: selected everything"]; }; DeselectAll: UserInputProc = { GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedCPs: TRUE]; GGSelect.DeselectAll[ggData.scene, normal]; GGEvent.SawTextFinish[ggData, NIL]; GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, ggData: ggData, remake: none, edited: FALSE, selectionChanged: TRUE, okToSkipCapture: FALSE]; Feedback.Append[ggData.router, oneLiner, $Feedback, "Deselect All: deselected everything"]; }; CycleSelection: UserInputProc = { point: Point; normal: Vector; feature: FeatureData; hitData: REF; 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, selectionChanged: TRUE, 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: TRUE, selectionChanged: TRUE, 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, selectionChanged: TRUE, 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, selectionChanged: TRUE, 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, selectionChanged: TRUE, 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, selectionChanged: TRUE, 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 Copyright Ó 1988, 1989, 1991, 1992 by Xerox Corporation. All rights reserved. Contents: Once an event reaches the front of the slack-process queue, it is dispatched to one of the procedures in this module. Bier, June 23, 1993 9:58 pm PDT Pier, November 10, 1992 9:28 am PST Doug Wyatt, April 17, 1992 5:20 pm PDT 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]; remember original selection feedback boundBox CodeTimer.StopInt[$DeselectAll, $Gargoyle]; 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]; ÊH„–(cedarcode) style•NewlineDelimiter ˜codešœ™Kšœ ÏeœC™NKšÏnœx™€K™K™#K™&K™—šÏk ˜ JšœRŸœÊŸœ_˜ÿK˜—šž œŸœŸ˜JšŸœ)ŸœöŸœ2˜ÜKšŸœ<Ÿ˜HK˜Kšœ Ÿœ˜'KšœŸœ˜KšœŸœ˜/KšœŸœ"˜5Kšœ Ÿœ˜-Kšœ Ÿœ˜*KšœŸœ˜'KšœŸœ˜1Kšœ ŸœŸœ˜'KšœŸœŸœÏc˜UKšœ Ÿœ˜-KšœŸœ˜!KšœŸœŸœ!˜Kšœ’˜’KšŸœ˜K˜—Kš œ ŸœŸœŸœhŸœ Ÿœ  ˜×Kšœ]˜]šŸœŸœŸœ˜Kšœ;™;Kšœ˜K˜FKšœ;ŸœŸœ ˜PK˜—KšœsÐbk¢œŸœ˜”K™IšŸœŸœŸœ˜KšœŸœ˜K˜Kšœ"˜"K˜)K–ldStream: STREAM _ NIL]š œŸœŸœŸœŸœ˜K˜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˜#K˜AšŸœŸœŸœ˜KšœŸœŸœ.˜M–ldStream: STREAM _ NIL]šŸœ ŸœŸœ˜Kšœ ŸœŸœ˜KšœŸœ˜KšŸœŸœŸœ   ˜TK–M[feedback: Feedback.FeedbackData, msg: ROPE, msgType: Feedback.MsgType]˜GKš œ ¢œŸœŸœŸœ˜gšŸœ Ÿœ˜KšœŸœ˜4K–M[feedback: Feedback.FeedbackData, msg: ROPE, msgType: Feedback.MsgType]˜:KšœpŸœŸœ˜ŽK˜—KšŸœx˜|KšŸœ˜K˜—K˜—K˜vK˜—K™K™šžœŸœ˜KšŸœ-Ÿœn˜¡šŸœ˜Kšœ ŸœŸœ˜KšœÏtœ  ˜CK˜8–#[g: GGModelTypes.EntityGenerator]š Ÿœ ŸœŸœŸœŸœŸ˜EKšœF˜FKšœ6˜6KšŸœ˜—Kšœ ¢ œ  0˜PKšœb˜bKšœnŸœŸœ˜ŒKšœ˜—Kšœ˜K˜—šžœŸœ˜+K˜K˜KšœŸœ5˜@KšŸœ Ÿœw˜†šŸœ˜K˜:Kšœx˜xK˜—K˜K˜—šžœŸœ˜ KšŸœ-Ÿœl˜ŸšŸœ˜Kšœ ŸœŸœ˜Kšœ¥œ  ˜FK˜8–#[g: GGModelTypes.EntityGenerator]š Ÿœ ŸœŸœŸœŸœŸ˜EKšœF˜FKšœ5˜5KšŸœ˜—Kšœ ¢ œ  0˜PK˜`KšœnŸœŸœ˜ŒKšœ˜—Kšœ˜K˜—šžœŸœ˜KšŸœ-Ÿœr˜¥šŸœ˜Kšœ ŸœŸœ˜Kšœ¥œ  ˜FK˜8–#[g: GGModelTypes.EntityGenerator]š Ÿœ ŸœŸœŸœŸœŸ˜EKšœF˜FKšœ*˜*KšŸœ˜—Kšœ ¢ œ  0˜PK˜uKšœnŸœŸœ˜ŒKšœ˜—Kšœ˜K˜—šžœŸœ˜!KšŸœ-Ÿœq˜¤šŸœ˜Kšœ ŸœŸœ˜Kšœ¥œ  ˜HK˜8–#[g: GGModelTypes.EntityGenerator]š Ÿœ ŸœŸœŸœŸœŸ˜EKšœF˜FKšœ,˜,KšŸœ˜—Kšœ ¢ œ  0˜PK˜qKšœnŸœŸœ˜ŒKšœ˜—Kšœ˜K˜—šžœŸœ˜3Kš œ ŸœŸœŸœŸœ˜3Kš Ÿœ ŸœŸœŸœ Ÿœr˜ššŸœ˜K˜=šŸœŸœŸœ˜Kšœ ¢ œ!ŸœŸœ˜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šŸœ ŸœŸœ˜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˜PKšœ˜KšœnŸœŸœ˜ŒK˜—K˜—K˜K˜—šž œŸœ˜#K˜KšœŸœ5˜@šŸœ Ÿ˜Kšœs˜s—šŸœ˜K˜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˜PKšœ{˜{KšœnŸœŸœ˜ŒK˜—K˜—K˜K˜—šžœŸœ˜"K˜"KšœŸœ˜Kšœ ŸœŸœ˜K˜š ž œŸœŸœŸœŸœ˜MKšŸœ Ÿœ˜"KšŸœŸœ Ÿœ˜'KšŸœŸœ  ˜2K˜K˜—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˜PKšœ\˜\KšœnŸœŸœ˜ŒK˜—K˜K˜—šžœŸœ˜)Kš œŸœŸœŸœŸœ˜8Kš ŸœŸœŸœŸœŸœ‚˜µšŸœ˜Kšœ ŸœŸœ6˜GKšœ¥œ  ˜QšŸœŸ˜–#[g: GGModelTypes.EntityGenerator]š Ÿœ ŸœŸœŸœŸœŸ˜EKšœF˜FKšœ*˜*KšŸœ˜—KšŸœ˜—Kšœ ¢ œ  0˜PKš œŸœ&ŸœŸœŸœ ˜ÒKšœnŸœŸœ˜ŒK˜—K˜K˜—šžœŸœ˜+Kš œŸœŸœŸœŸœ˜8Kš ŸœŸœŸœŸœŸœ€˜³šŸœ˜Kšœ ŸœŸœ6˜GKšœ¥œ  ˜SšŸœŸ˜–#[g: GGModelTypes.EntityGenerator]š Ÿœ ŸœŸœŸœŸœŸ˜EKšœF˜FKšœ,˜,KšŸœ˜—KšŸœ˜—Kšœ ¢ œ  0˜PKš œ{Ÿœ&ŸœŸœŸœ ˜Î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Ÿœ#˜zKšœiŸœŸœ˜‡K˜K˜—šžœ˜"Kš œ ŸœŸœŸœŸœ˜4KšœŸœ ˜,KšŸœŸœŸœi˜{Kš ŸœŸœŸœ ŸœŸœŸœw˜©šŸœ˜K˜3K˜qKšœ'˜'Kšœ˜—Kšœ˜K˜—šžœ˜Kš œ ŸœŸœŸœŸœ˜7KšœŸœ ˜,KšŸœŸœŸœa˜sKšŸœŸœŸœu˜ššŸœ˜K˜3K˜eKšœ#˜#Kšœ˜—K˜K˜—šž œ˜Kš œŸœŸœŸœŸœ˜3KšœŸœ ˜,KšŸœŸœŸœg˜yKšŸœŸœŸœv˜—šŸœ˜K˜3K˜dKšœ&˜&K˜—K˜K˜—šžœ˜"Kš œŸœŸœŸœŸœ˜3Kš œ ŸœŸœŸœŸœ˜8KšœŸœ ˜,KšŸœŸœŸœs˜…Kš ŸœŸœŸœ ŸœŸœŸœ‹˜ÂšŸœ˜K˜3K˜NK˜\Kšœ-˜-K˜—K˜K˜—šžœ˜Kš œ ŸœŸœŸœŸœ˜8Kš œ ŸœŸœŸœŸœ˜K˜HKšœZ™ZKšœ&™&Kšœ ¢œ;Ÿœ˜YKšœ˜Kšœx˜xKšœwŸœŸœ˜•K˜—K˜K˜—šž œŸœ˜$KšŸœ-Ÿœ}˜°šŸœ˜š ž œŸœŸœŸœŸœ˜MKšœQ˜QK˜—Kšœ˜Kšœ™Kšœ˜KšœŸœŸœ˜.šŸœŸœŸ˜KšœŸœ˜;KšœŸœ˜;KšœŸœ˜;šŸœ˜ Kšœ‡˜‡KšŸœ˜K˜——Jšœ8™8Kšœ ¢ œ!ŸœŸœ˜RK˜@K˜JKšœZ™ZKšœ&™&Kšœ ¢œ;Ÿœ˜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˜—šž œŸœŸœŸœŸœŸœ ŸœŸœ ˜‰š žœŸœŸœŸœŸœ˜IKšœ`˜`K˜—Kšœ˜K˜KšŸœ-Ÿœw˜ªšŸœ˜Kšœ$ŸœŸœ Ÿœ˜ZJšœR™RKšœ ¢ œ!ŸœŸœ˜RK˜FKšœ˜KšœŽ˜ŽKšœwŸœŸœ˜•K˜—K˜K˜—šž œŸœ˜#KšœŸœ˜K˜K˜—šžœŸœŸœ ŸœŸœ+ŸœŸœŸœ˜›š žœŸœŸœŸœŸœ˜TKšœ ŸœŸœ˜KšœŸœ˜Kšœ˜Kšœ ŸœŸœ˜K˜ošŸœŸœ Ÿœ˜K˜UKšŸœŸœ˜ K˜—šŸœ˜ šŸœ˜˜KšœŸœ Ÿ˜&KšœŸ˜KšœŸ˜KšŸœ6˜9—K˜—šŸœ˜KšœŸœ˜ K˜K˜K˜K˜Kšœ˜——Kšœ˜—K˜KšœŸœŸœ˜Kšœ Ÿœ(¥œ¥œ¥˜TK˜K˜—š žœŸœŸœŸœŸœŸœ˜jš ž œŸœŸœŸœŸœ˜MKšœ Ÿœ˜Kšœ ŸœŸœ˜K˜NšŸœŸœ Ÿœ˜K˜KšŸœŸœ˜ K˜—šŸœ˜ KšŸœ˜#šŸœ˜KšœŸœ˜ K˜Kšœ˜——Kšœ˜—K˜KšœŸœŸœ˜Kšœ Ÿœ(¥œ ¥œ¥˜MK˜K˜—š žœŸœŸœ*ŸœŸœ˜mš ž œŸœŸœŸœŸœ˜KKšœ˜Kšœ ŸœŸœ˜K˜JšŸœŸœ Ÿœ˜K˜KšŸœŸœ˜ K˜—šŸœ˜ KšŸœ˜šŸœ˜KšœŸœ˜ K˜Kšœ˜——Kšœ˜—K˜KšœŸœŸœ˜Kšœ Ÿœ(¥œ ¥œ¥˜KK˜K˜—š žœŸœŸœ-ŸœŸœ˜rš ž œŸœŸœŸœŸœ˜MKšœ˜Kšœ ŸœŸœ˜K˜NšŸœŸœ Ÿœ˜K˜KšŸœŸœ˜ K˜—šŸœ˜ KšŸœ˜#šŸœ˜KšœŸœ˜ K˜Kšœ˜——Kšœ˜—K˜KšœŸœŸœ˜Kšœ Ÿœ(¥œ ¥œ¥˜MK˜K˜—šžœŸœ˜+KšŸœ-Ÿœ˜´šŸœ˜KšœŸœŸœ˜K˜KšœŸœ˜"Kšœ˜K˜Kšœ ŸœŸœ˜K˜K˜8KšŸœŸœ ŸœŸœ ˜#K˜4KšŸœŸœ ŸœŸœ ˜#K˜8KšŸœŸœ ŸœŸœ ˜#K˜LKšŸœŸœ ŸœŸœ ˜#K˜šœhŸœ’˜þK˜—šŸ˜Kšœ‚˜‚—K˜—K˜K˜—šžœŸœ˜*Kšœm™mK˜K˜=KšœŸœ4˜>KšŸœ Ÿœ ŸœŸœ˜¯šŸœ˜š ž œŸœŸœŸœŸœ˜MšŸœŸœ˜K˜PKšœG˜GKšœK˜KK–Ù[slice: GGModelTypes.Slice, parts: GGModelTypes.SliceParts, dashed: BOOL _ FALSE, pattern: GGCoreTypes.SequenceOfReal _ NIL, offset: REAL _ 0.0, length: REAL _ -1.0, history: GGHistoryTypes.HistoryEvent]šœ˜˜˜K˜—K˜—KšœŸœŸœ˜K˜KšœŸœ˜Kšœ˜Kšœ˜Kšœ ŸœŸœ˜Kšœ˜K˜K˜MKšŸœŸœ ŸœŸœ ˜#K˜IKšŸœŸœ ŸœŸœ ˜#K˜MKšŸœŸœ ŸœŸœ ˜#K˜bKšŸœŸœ ŸœŸœ ˜#K˜BKšœK™KKšœ ¢ œ!ŸœŸœ˜RK˜CKšœa™aKšœ ¢œ;Ÿœ˜YKšœ˜KšœlŸœ€˜ð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˜—Kšœ ¢ œ!ŸœŸœ˜PKšœ$˜$K˜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šŸœ˜—KšŸœŸœ '˜5šŸ˜Kšœ Ÿœ˜—K˜—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˜LKšŸœŸœ ŸœŸœ ˜#K˜8KšŸœŸœ ŸœŸœ ˜#K˜8KšŸœŸœ ŸœŸœ ˜#K˜4KšŸœŸœ ŸœŸœ ˜#K˜£Kšœ'˜'šŸ˜Kšœª˜ª—K˜—K˜K˜—šžœŸœ˜1KšœpŸœ‚˜öK˜—K˜šžœŸœ˜ KšŸœ-Ÿœf˜™šŸœ˜š žœŸœŸœŸœŸœ˜IKšœ"™"Kšœ ŸœŸœ˜K˜7K˜3šœ ŸœŸœŸ˜KšœŸœ˜KšœŸœŸœ˜9KšŸœŸœ˜—KšŸœ Ÿœ$Ÿœ(˜_KšŸœ$Ÿœ)˜TK˜K˜—Kšœ˜KšœŸœŸœ˜&Kš œ ŸœŸœŸœŸœ˜4šŸœ Ÿ˜šœ˜Kšœ]˜]Kšœ Ÿœ˜KšœŸœ˜K˜—šœ˜Kšœd˜dKšœ Ÿœ˜KšœŸœ˜K˜—˜Kšœc˜cKšœ Ÿœ˜KšœŸœ˜K˜—˜Kšœg˜gKšœ Ÿœ˜KšœŸœ˜K˜—šŸœ˜ Kšœb˜bKšŸœ˜K˜——K˜:K˜MKšœ˜KšœI˜IKšœnŸœŸœ˜ŒKšœ˜—Kšœ˜—K˜K™šž œ˜Kšœ*˜*Kšœ)˜)KšœŸœ˜#Kšœ ¢ œ ˜KšœjŸœŸœœŸœ˜¡Kšœ)˜)KšœX˜XK˜K˜—šž œ˜Kšœ,™,šœ ¢ œ!ŸœŸœ˜PK™-—Kšœ+˜+KšœŸœ˜#KšœjŸœŸœŸœ˜¡Kšœ+™+Kšœ[˜[K˜K˜—šžœ˜!Kšœ ˜ Kšœ˜Kšœ˜Kšœ Ÿœ˜ Iproc˜BKšœ ŸœŸœ˜+šŸœŸœŸœ˜K–M[feedback: Feedback.FeedbackData, msg: ROPE, msgType: Feedback.MsgType]šœm˜mKšŸœ˜K˜—šŸœ Ÿ˜K˜ZK˜_KšŸœŸœ˜—Lšœa˜aKšœ˜K˜—šž œ˜ KšœŸœŸœ˜(K–K[scene: GGModelTypes.Scene, selectClass: GGSegmentTypes.SelectionClass]˜K˜?K˜šœ ŸœŸœŸ˜&Kšœ˜Kšœ˜Kšœ"˜"Kšœ$˜$Kšœ˜KšŸœŸœ˜—šŸ˜KšŸœŸœŸœŸœ˜.K˜4KšŸœ ŸœŸœ˜’šŸœ˜Kšœ ¢ œ!ŸœŸœ˜PK˜JK˜-KšœX˜XKšœjŸœŸœŸœ˜¡K˜—šŸ˜šœ˜Kšœv˜vK˜——KšŸœ˜—K˜K˜—šž œ˜ K™bKšœŸœŸœ˜"KšœX˜XK˜K˜—šžœŸœ˜0K™`K–K[scene: GGModelTypes.Scene, selectClass: GGSegmentTypes.SelectionClass]˜KšŸœ&Ÿœ…˜±šŸœ &˜-š žœŸœŸœŸœŸœ˜IK˜LK˜—KšœŸœŸœ˜"K˜2KšœK™KKšœ ¢ œ!ŸœŸœ˜RKšœ#Ÿœ Ÿœ˜7Kšœ,¥œ¥œ¥˜@š ŸœŸœŸœ"ŸœŸœŸ˜FKšœ.˜.KšŸœ˜—Kšœ"˜"KšœŸœ˜!KšœX˜XKšœ}ŸœŸœŸœ˜³Kšœ˜—Kšœ˜K˜—šžœ˜#K™TKšœŸœŸœ˜#Kšœ[˜[Kšœ˜K˜—š ž œŸœŸœŸœ ŸœŸœ˜NKšœN™NKšœŸœŸœ˜"K–_[gargoyleData: GGInterfaceTypes.GargoyleData, selectClass: GGInterfaceTypes.SelectionClass]˜š žœŸœŸœŸœŸœ˜PK˜Eš ž œŸœŸœŸœŸœ˜@KšŸœŸœŸœ˜!KšŸœŸœ0Ÿœ&˜_KšŸœM˜QK˜—Kšœ$¥œ ¥˜3K˜—KšŸœ&Ÿœx˜¤šŸœ˜Kšœ ¢ œ!ŸœŸœ˜PK˜2Kšœ,¥œ¥œ¥˜GKšŸœŸœ& ˜Lš ŸœŸœŸœ"ŸœŸœŸ˜FKšœ6˜6KšŸœ˜—Kš ŸœŸœkŸœŸœŸœ˜¯K˜—Kšœ ˜K˜—šžœ˜#Kšœ0™0š žœŸœŸœŸœŸœ˜EšžœŸœ@ŸœŸœ˜lKšŸœŸœ ˜QK˜—K˜I–s[sliceD: GGModelTypes.SliceDescriptor, scene: GGModelTypes.Scene, selectClass: GGSegmentTypes.SelectionClass]š ŸœŸœŸœŸœ!Ÿœ˜š ž œŸœŸœŸœŸœ˜@Kš ŸœŸœŸœŸœ ˜LKš ŸœŸœŸœŸœ ˜MKšŸœŸœŸœ( ˜FKšŸœ+Ÿœ Ÿ˜BKšŸœŸœŸœ˜K˜—K˜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šœ<˜