DIRECTORY Atom, Basics, CodeTimer, Feedback, GGAlign, GGBasicTypes, GGBoundBox, GGCaret, GGDescribe, GGEvent, GGGravity, GGInterfaceTypes, GGModelTypes, GGMouseEvent, GGMultiGravity, GGScene, GGOutline, GGRefresh, GGSegment, GGSegmentTypes, GGSelect, GGSequence, GGSlice, GGState, GGTraj, GGTransform, GGWindow, GList, Imager, ImagerTransformation, InputFocus, Menus, RealFns, Rope, Vectors2d; GGMouseEventImplA: CEDAR PROGRAM IMPORTS Atom, Basics, CodeTimer, Feedback, GGAlign, GGBoundBox, GGCaret, GGDescribe, GGEvent, GGMouseEvent, GGMultiGravity, GGScene, GGOutline, GGRefresh, GGSegment, GGSelect, GGSequence, GGSlice, GGTraj, GGTransform, GGWindow, GList, ImagerTransformation, InputFocus, RealFns, Rope, Vectors2d EXPORTS GGMouseEvent = BEGIN AlignmentPoint: TYPE = GGInterfaceTypes.AlignmentPoint; BoundBox: TYPE = GGModelTypes.BoundBox; Caret: TYPE = GGInterfaceTypes.Caret; DefaultData: TYPE = GGModelTypes.DefaultData; EntityGenerator: TYPE = GGModelTypes.EntityGenerator; FeatureData: TYPE = GGModelTypes.FeatureData; FeedbackData: TYPE = Feedback.FeedbackData; GGData: TYPE = GGInterfaceTypes.GGData; Joint: TYPE = GGModelTypes.Joint; MouseButton: TYPE = Menus.MouseButton; AlignBag: TYPE = GGGravity.AlignBag; Outline: TYPE = GGModelTypes.Outline; OutlineDescriptor: TYPE = REF OutlineDescriptorObj; OutlineDescriptorObj: TYPE = GGModelTypes.OutlineDescriptorObj; Point: TYPE = GGBasicTypes.Point; ResultFeatureType: TYPE = GGModelTypes.ResultFeatureType; Scene: TYPE = GGModelTypes.Scene; Segment: TYPE = GGSegmentTypes.Segment; Sequence: TYPE = GGModelTypes.Sequence; Slice: TYPE = GGModelTypes.Slice; SliceParts: TYPE = GGModelTypes.SliceParts; SliceGenerator: TYPE = GGModelTypes.SliceGenerator; SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor; SliceDescriptorObj: TYPE = GGModelTypes.SliceDescriptorObj; SliceDescriptorGenerator: TYPE = GGModelTypes.SliceDescriptorGenerator; Traj: TYPE = GGModelTypes.Traj; TrajEnd: TYPE = GGModelTypes.TrajEnd; TrajGenerator: TYPE = GGScene.TrajGenerator; TrajPartType: TYPE = GGModelTypes.TrajPartType; TriggerBag: TYPE = GGAlign.TriggerBag; Vector: TYPE = GGBasicTypes.Vector; MouseProc: TYPE = GGMouseEvent.MouseProc; StartProc: TYPE = GGMouseEvent.StartProc; Problem: PUBLIC SIGNAL [msg: Rope.ROPE] = Feedback.Problem; EasyAbort: PROC [input: LIST OF REF ANY, ggData: GGData, worldPt: Point] = { scene: Scene _ ggData.scene; GGScene.RestoreSelections[scene]; GGCaret.Copy[from: ggData.drag.savedCaret, to: ggData.caret]; --restore original caret FinishAbort[ggData]; }; AbortAdd: PROC [input: LIST OF REF ANY, ggData: GGData, worldPt: Point] = { GGEvent.DeleteCaretSegment[ggData, NIL]; { sliceD: OutlineDescriptor _ NARROW[GGCaret.GetChair[ggData.caret]]; IF sliceD#NIL THEN GGSelect.SelectSlice[sliceD: sliceD, scene: ggData.scene, selectClass: normal]; FinishAbort[ggData]; }; }; AbortCopyAndDrag: PROC [input: LIST OF REF ANY, ggData: GGData, worldPt: Point] = { [] _ GGScene.DeleteAllSelected[ggData.scene]; GGScene.RestoreSelections[ggData.scene]; GGCaret.Copy[from: ggData.drag.savedCaret, to: ggData.caret]; FinishAbort[ggData]; }; AbortBox: PROC [input: LIST OF REF ANY, ggData: GGData, worldPt: Point] = { FixupAbortedBox[ggData]; FinishAbort[ggData]; }; FinishAbort: PROC [ggData: GGData] = { GGRefresh.MoveOverlayToBackground[ggData]; Feedback.AppendHerald[ggData.feedback, ". . . Aborted.", end]; GGWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireScene, ggData: ggData, remake: triggerBag, backgndOK: FALSE, edited: FALSE, okToClearFeedback: FALSE]; }; InitializeFSM: PUBLIC PROC [ggData: GGData] = { ggData.state _ $None; ggData.mouseMode _ $None; }; ResetMouseMachinery: PUBLIC PROC [ggData: GGData] = { ggData.mouseMode _ $None; ggData.state _ $None; GGRefresh.MoveOverlayToBackground[ggData]; }; SaveSavedState: PROC [ggData: GGData] = { GGScene.SaveSelections[ggData.scene]; GGWindow.SaveCaretPos[ggData]; GGCaret.Copy[from: ggData.caret, to: ggData.drag.savedCaret]; }; HandleMouseless: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { event _ LIST[event.first, NEW[Point _ [0.0, 0.0]]]; HandleMouse[clientData, event]; }; SortNewEntities: PROC [scene: Scene, entityList: LIST OF Slice] RETURNS [sorted: LIST OF Slice] = { CompareProc: GList.CompareProc = { priority1, priority2: INT; WITH ref1 SELECT FROM slice: Slice => priority1 _ GGScene.SlicePriority[scene, slice]; ENDCASE => ERROR; WITH ref2 SELECT FROM slice: Slice => priority2 _ GGScene.SlicePriority[scene, slice]; ENDCASE => ERROR; RETURN[Basics.CompareInt[priority1, priority2]]; }; sorted _ NARROW[GList.Sort[entityList, CompareProc]]; }; HandleMouse: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; atom: ATOM _ NARROW[event.first]; point: Point _ NARROW[event.rest.first, REF Point]^; SELECT ggData.mouseMode FROM $CaretPos => HandleGuarded[StartCaretPos, DuringCaretPos, EndCaretPos, EasyAbort, NIL, event, ggData, point]; $Box => HandleGuarded[StartBox, DuringDrag, EndBox, AbortBox, NIL, event, ggData, point]; $Add => HandleGuarded[StartAdd, DuringDrag, EndMotion, AbortAdd, ContinueAdd, event, ggData, point]; $Drag => HandleGuarded[StartDrag, DuringDrag, EndMotion, EasyAbort, NIL, event, ggData, point]; $CopyAndDrag => HandleGuarded[StartCopyAndDrag, DuringDrag, EndMotion, AbortCopyAndDrag, NIL, event, ggData, point]; $Rotate => HandleGuarded[StartRotate, DuringRotate, EndMotion, EasyAbort, NIL, event, ggData, point]; $Scale => HandleGuarded[StartScale, DuringScale, EndMotion, EasyAbort, NIL, event, ggData, point]; $SixPoint => HandleGuarded[StartSixPoint, DuringSixPoint, EndMotion, EasyAbort, NIL, event, ggData, point]; $SelectWithBox => HandleUnGuarded[StartSelectWithBox, DuringDrag, EndSelectWithBox, AbortBox, event, ggData, point]; $SelectJoint => HandleUnGuarded[GGMouseEvent.StartSelectJoint, GGMouseEvent.DuringSelect, GGMouseEvent.EndSelect, EasyAbort, event, ggData, point]; $ExtSelectJoint => HandleGuarded[GGMouseEvent.StartExtendSelectJoint, GGMouseEvent.DuringExtendSelection, GGMouseEvent.EndExtendSelection, EasyAbort, NIL, event, ggData, point]; $SelectSegment => HandleUnGuarded[GGMouseEvent.StartSelectSegment, GGMouseEvent.DuringSelect, GGMouseEvent.EndSelect, EasyAbort, event, ggData, point]; $ExtSelectSegment => HandleGuarded[GGMouseEvent.StartExtendSelectSegment, GGMouseEvent.DuringExtendSelection, GGMouseEvent.EndExtendSelection, EasyAbort, NIL, event, ggData, point]; $SelectTrajectory => HandleUnGuarded[GGMouseEvent.StartSelectTrajectory, GGMouseEvent.DuringSelect, GGMouseEvent.EndSelect, EasyAbort, event, ggData, point]; $ExtSelectTrajectory => HandleGuarded[GGMouseEvent.StartExtendSelectTraj, GGMouseEvent.DuringExtendSelection, GGMouseEvent.EndExtendSelection, EasyAbort, NIL, event, ggData, point]; $SelectTopLevel => HandleUnGuarded[GGMouseEvent.StartSelectTopLevel, GGMouseEvent.DuringSelect, GGMouseEvent.EndSelect, EasyAbort, event, ggData, point]; $ExtSelectTopLevel => HandleGuarded[GGMouseEvent.StartExtendSelectTopLevel, GGMouseEvent.DuringExtendSelection, GGMouseEvent.EndExtendSelection, EasyAbort, NIL, event, ggData, point]; $ExtendSelection => HandleUnGuarded[GGMouseEvent.StartExtendSelection, GGMouseEvent.DuringExtendSelection, GGMouseEvent.EndExtendSelection, EasyAbort, event, ggData, point]; $DeselectJoint => HandleGuarded[GGMouseEvent.StartDeselectJoint, GGMouseEvent.DuringDeselect, GGMouseEvent.EndDeselect, EasyAbort, NIL, event, ggData, point]; $DeselectSegment => HandleGuarded[GGMouseEvent.StartDeselectSegment, GGMouseEvent.DuringDeselect, GGMouseEvent.EndDeselect, EasyAbort, NIL, event, ggData, point]; $DeselectTrajectory => HandleGuarded[GGMouseEvent.StartDeselectTrajectory, GGMouseEvent.DuringDeselect, GGMouseEvent.EndDeselect, EasyAbort, NIL, event, ggData, point]; $DeselectTopLevel => HandleGuarded[GGMouseEvent.StartDeselectTopLevel, GGMouseEvent.DuringDeselect, GGMouseEvent.EndDeselect, EasyAbort, NIL, event, ggData, point]; $None => { SELECT atom FROM $StartCaretPos => { ggData.mouseMode _ $CaretPos; HandleMouse[ggData, event]; }; $StartAdd => { ggData.mouseMode _ $Add; HandleMouse[ggData, event]; }; $StartBox => { ggData.mouseMode _ $Box; HandleMouse[ggData, event]; }; $StartSelectWithBox => { ggData.mouseMode _ $SelectWithBox; HandleMouse[ggData, event]; }; $StartDrag => { ggData.mouseMode _ $Drag; HandleMouse[ggData, event]; }; $StartCopyAndDrag => { ggData.mouseMode _ $CopyAndDrag; HandleMouse[ggData, event]; }; $StartRotate => { ggData.mouseMode _ $Rotate; HandleMouse[ggData, event]; }; $StartScale => { ggData.mouseMode _ $Scale; HandleMouse[ggData, event]; }; $StartSixPoint => { ggData.mouseMode _ $SixPoint; HandleMouse[ggData, event]; }; $StartSelectJoint => { ggData.mouseMode _ $SelectJoint; HandleMouse[ggData, event]; }; $StartExtSelectJoint => { ggData.mouseMode _ $ExtSelectJoint; HandleMouse[ggData, event]; }; $StartSelectSegment => { ggData.mouseMode _ $SelectSegment; HandleMouse[ggData, event]; }; $StartExtSelectSegment => { ggData.mouseMode _ $ExtSelectSegment; HandleMouse[ggData, event]; }; $StartSelectTrajectory => { ggData.mouseMode _ $SelectTrajectory; HandleMouse[ggData, event]; }; $StartExtSelectTrajectory => { ggData.mouseMode _ $ExtSelectTrajectory; HandleMouse[ggData, event]; }; $StartSelectTopLevel => { ggData.mouseMode _ $SelectTopLevel; HandleMouse[ggData, event]; }; $StartExtSelectTopLevel => { ggData.mouseMode _ $ExtSelectTopLevel; HandleMouse[ggData, event]; }; $StartExtendSelection => { ggData.mouseMode _ $ExtendSelection; HandleMouse[ggData, event]; }; $StartDeselectJoint => { ggData.mouseMode _ $DeselectJoint; HandleMouse[ggData, event]; }; $StartDeselectSegment => { ggData.mouseMode _ $DeselectSegment; HandleMouse[ggData, event]; }; $StartDeselectTrajectory => { ggData.mouseMode _ $DeselectTrajectory; HandleMouse[ggData, event]; }; $StartDeselectTopLevel => { ggData.mouseMode _ $DeselectTopLevel; HandleMouse[ggData, event]; }; ENDCASE; -- ignore other actions }; ENDCASE => SIGNAL Problem[msg: "Unimplemented Mode"]; }; HandleGuarded: PROC [startProc: StartProc, duringProc, endProc, abortProc: MouseProc, continueProc: StartProc, input: LIST OF REF ANY, ggData: GGData, worldPt: Point] = { genericAction, atom: ATOM; atomName: Rope.ROPE; CodeTimer.StartInt[$HandleGuarded, $Gargoyle]; WITH input.first SELECT FROM refChar: REF CHAR => RETURN; -- ignore characters during mouse actions ENDCASE; atom _ NARROW[input.first]; atomName _ Atom.GetPName[atom]; genericAction _ IF Rope.Equal[Rope.Substr[atomName, 0, 5], "Start", TRUE] THEN $Start ELSE atom; HandleGuardedAux[startProc, duringProc, endProc, abortProc, continueProc, genericAction, input, ggData, worldPt]; CodeTimer.StopInt[$HandleGuarded, $Gargoyle]; }; Restart: PROC [input: LIST OF REF ANY, ggData: GGData] RETURNS [BOOL] = { mouseMode: ATOM _ ggData.mouseMode; state: ATOM _ NARROW[input.first]; RETURN[ (mouseMode = $CaretPos AND state = $StartCaretPos) OR (mouseMode = $Add AND state = $StartAdd) OR (mouseMode = $Box AND state = $StartBox) OR (mouseMode = $Drag AND state = $StartDrag) OR (mouseMode = $CopyAndDrag AND state = $StartCopyAndDrag) OR (mouseMode = $Rotate AND state = $StartRotate) OR (mouseMode = $Scale AND state = $StartScale) OR (mouseMode = $SelectJoint AND state = $StartSelectJoint) OR (mouseMode = $SelectSegment AND state = $StartSelectSegment) OR (mouseMode = $SelectTrajectory AND state = $StartSelectTrajectory) OR (mouseMode = $SelectTopLevel AND state = $StartSelectTopLevel) OR (mouseMode = $ExtendSelection AND state = $StartExtendSelection) OR (mouseMode = $DeselectJoint AND state = $StartDeselectJoint) OR (mouseMode = $DeselectSegment AND state = $StartDeselectSegment) OR (mouseMode = $DeselectTrajectory AND state = $StartDeselectTrajectory) OR (mouseMode = $DeselectTopLevel AND state = $StartDeselectTopLevel)]; }; HandleGuardedAux: PROC [startProc: StartProc, duringProc, endProc, abortProc: MouseProc, continueProc: StartProc, genericAction: ATOM, input: LIST OF REF ANY, ggData: GGData, worldPt: Point] = { SELECT ggData.state FROM $None => { SELECT genericAction FROM $Start => { ggData.drag.currentPoint _ worldPt; [] _ InputFocus.SetInputFocus[ggData.actionArea]; IF startProc[input, ggData, worldPt] THEN ggData.state _ $Main ELSE {abortProc[input, ggData, worldPt]; ggData.state _ $Aborted;}; }; ENDCASE; }; $Main => { SELECT genericAction FROM $During => { duringProc[input, ggData, worldPt]; ggData.drag.currentPoint _ worldPt; }; $SawMouseFinish, $Abort => {abortProc[input, ggData, worldPt]; ggData.state _ $Aborted}; $GuardUp => ggData.state _ $GuardUp; $MouseUp => ggData.state _ $MouseUp; $Start => { -- the mouse must have gone out of the window while the mouse button was done. Abort the current action and start a new one. abortProc[input, ggData, worldPt]; ggData.state _ $None; ggData.mouseMode _ $None; HandleMouse[ggData, input]; }; ENDCASE; }; $GuardUp => { SELECT genericAction FROM $AllUp => { endProc[input, ggData, ggData.drag.currentPoint]; ggData.mouseMode _ $None; ggData.state _ $None; }; $SawMouseFinish, $Abort => {abortProc[input, ggData, worldPt]; ggData.state _ $Aborted}; ENDCASE; }; $MouseUp => { SELECT genericAction FROM $SawMouseFinish, $AllUp => { endProc[input, ggData, ggData.drag.currentPoint]; ggData.mouseMode _ $None; ggData.state _ $None; }; $Abort => {abortProc[input, ggData, worldPt]; ggData.state _ $Aborted}; $Start => { -- we may be starting another action of this mode or some other mode. IF Restart[input, ggData] AND continueProc # NIL THEN { IF continueProc[input, ggData, worldPt] THEN { ggData.state _ $Main; ggData.drag.currentPoint _ worldPt; } ELSE {abortProc[input, ggData, worldPt]; ggData.state _ $Aborted;}; } ELSE { ggData.mouseMode _ $None; endProc[input, ggData, ggData.drag.currentPoint]; ggData.state _ $None; HandleMouse[ggData, input]; }; }; ENDCASE; }; $Aborted => { SELECT genericAction FROM $AllUp => {ggData.state _ $None; ggData.mouseMode _ $None}; ENDCASE; }; ENDCASE => SIGNAL Problem[msg: "Unknown generic state"]; }; HandleUnGuarded: PROC [startProc: StartProc, duringProc, endProc, abortProc: MouseProc, input: LIST OF REF ANY, ggData: GGData, worldPt: Point] = { genericAction, atom: ATOM; atomName: Rope.ROPE; WITH input.first SELECT FROM refChar: REF CHAR => RETURN; -- ignore characters during mouse actions ENDCASE; atom _ NARROW[input.first]; atomName _ Atom.GetPName[atom]; genericAction _ IF Rope.Equal[Rope.Substr[atomName, 0, 5], "Start", TRUE] THEN $Start ELSE atom; HandleUnGuardedAux[startProc, duringProc, endProc, abortProc, genericAction, input, ggData, worldPt]; }; HandleUnGuardedAux: PROC [startProc: StartProc, duringProc, endProc, abortProc: MouseProc, genericAction: ATOM, input: LIST OF REF ANY, ggData: GGData, worldPt: Point] = { SELECT ggData.state FROM $None => { SELECT genericAction FROM $Start => { [] _ InputFocus.SetInputFocus[ggData.actionArea]; IF startProc[input, ggData, worldPt] THEN ggData.state _ $Main ELSE {abortProc[input, ggData, worldPt]; ggData.state _ $Aborted} }; ENDCASE; }; $Main => { SELECT genericAction FROM $During => duringProc[input, ggData, worldPt]; $MouseUp, $AllUp => { endProc[input, ggData, worldPt]; ggData.state _ $None; ggData.mouseMode _ $None; }; $Abort => {abortProc[input, ggData, worldPt]; ggData.state _ $Aborted}; $Start => { -- the mouse must have gone out of the window while the mouse button was done. Abort the current action and start a new one. abortProc[input, ggData, worldPt]; ggData.state _ $None; ggData.mouseMode _ $None; HandleMouse[ggData, input]; }; ENDCASE; }; $Aborted => { SELECT genericAction FROM $AllUp => {ggData.state _ $None; ggData.mouseMode _ $None}; $MouseUp => {ggData.state _ $None; ggData.mouseMode _ $None}; ENDCASE; }; ENDCASE => SIGNAL Problem[msg: "Unknown generic state"]; }; StartCaretPos: PUBLIC StartProc = { CodeTimer.StartInt[$StartCaretPos, $Gargoyle]; IF NOT GGRefresh.EmptyOverlay[ggData] THEN ERROR; SaveSavedState[ggData]; DuringCaretPos[NIL, ggData, worldPt]; CodeTimer.StopInt[$StartCaretPos, $Gargoyle]; }; DuringCaretPos: PUBLIC MouseProc = { resultPoint: Point; feature: FeatureData; hitData: REF ANY; CodeTimer.StartInt[$DuringCaretPos, $Gargoyle]; [resultPoint, feature, hitData] _ GGMultiGravity.Map[worldPt, ggData.hitTest.criticalR, ggData.hitTest.alignBag, ggData.hitTest.sceneBag, ggData, TRUE]; SetCaretAttractor[ggData, resultPoint, feature, hitData]; -- move caret to feature point GGWindow.RestoreScreenAndInvariants[paintAction: $DuringCaretPos, ggData: ggData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; -- show caret in new position CodeTimer.StopInt[$DuringCaretPos, $Gargoyle]; }; -- end of DuringCaretPos EndCaretPos: PUBLIC MouseProc = { resultPoint: Point; feature: FeatureData; hitData: REF ANY; currentObjects: AlignBag _ NARROW[ggData.hitTest.alignBag]; sceneObjects: TriggerBag _ NARROW[ggData.hitTest.sceneBag]; CodeTimer.StartInt[$EndCaretPos, $Gargoyle]; [resultPoint, feature, hitData] _ GGMultiGravity.Map[worldPt, ggData.hitTest.criticalR, currentObjects, sceneObjects, ggData, TRUE]; SetCaretAttractor[ggData, resultPoint, feature, hitData, "Final"]; -- move caret to feature point GGWindow.NewCaretPos[ggData]; GGCaret.SitOn[ggData.caret, NIL]; -- subsequent Add operations will start a NEW trajectory. GGWindow.RestoreScreenAndInvariants[paintAction: $CaretMoved, ggData: ggData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; CodeTimer.StopInt[$EndCaretPos, $Gargoyle]; }; -- end EndCaretPos SitOnFeature: PROC [caret: Caret, feature: FeatureData] = { IF feature = NIL THEN GGCaret.SitOn[caret, NIL] ELSE { -- feature.shape type is OutlineD or SliceD WITH feature.shape SELECT FROM outlineD: OutlineDescriptor => GGCaret.SitOn[caret, outlineD]; sliceD: SliceDescriptor => GGCaret.SitOn[caret, sliceD]; ENDCASE => GGCaret.SitOn[caret, NIL]; }; }; SetCaretAttractor: PROC [ggData: GGData, mapPoint: Point, feature: FeatureData, hitData: REF ANY, action: Rope.ROPE _ ""] = { IF feature = NIL THEN GGCaret.SetAttractor[ggData.caret, mapPoint, NIL] ELSE { SELECT feature.type FROM outline, slice => { partsD: SliceDescriptor; slideD: SliceDescriptor _ NARROW[feature.shape]; partsD _ slideD.slice.class.newParts[slideD.slice, hitData, literal]; GGCaret.SetAttractor[ggData.caret, mapPoint, partsD]; }; intersectionPoint => { alignPoint: AlignmentPoint _ NARROW[feature.shape]; curve1, curve2: FeatureData; curve1 _ alignPoint.curve1; curve2 _ alignPoint.curve2; IF hitData = NIL THEN GGCaret.SetAttractor[ggData.caret, mapPoint, NIL] ELSE { attractorD: SliceDescriptor; IF curve1.type = outline OR curve1.type = slice THEN { WITH curve1.shape SELECT FROM sliceD: SliceDescriptor => attractorD _ sliceD.slice.class.newParts[sliceD.slice, hitData, literal]; ENDCASE => ERROR; } ELSE IF curve2.type = outline OR curve2.type = slice THEN { WITH curve2.shape SELECT FROM sliceD: SliceDescriptor => attractorD _ sliceD.slice.class.newParts[sliceD.slice, hitData, literal]; ENDCASE => ERROR; } ELSE ERROR; GGCaret.SetAttractor[ggData.caret, mapPoint, attractorD] }; }; midpoint => { IF hitData = NIL THEN GGCaret.SetAttractor[ggData.caret, mapPoint, NIL] ELSE { attractorD: SliceDescriptor; alignPoint: AlignmentPoint _ NARROW[feature.shape]; curveFeature: FeatureData _ alignPoint.curve1; WITH curveFeature.shape SELECT FROM sliceD: SliceDescriptor => attractorD _ sliceD.slice.class.newParts[sliceD.slice, hitData, literal]; ENDCASE => ERROR; GGCaret.SetAttractor[ggData.caret, mapPoint, attractorD]; }; }; ENDCASE => GGCaret.SetAttractor[ggData.caret, mapPoint, NIL]; }; Feedback.PutFHerald[ggData.feedback, oneLiner, "%g Caret on %g at [%g, %g]", [rope[action]], [rope[GGDescribe.DescribeFeature[feature, hitData, ggData]]], [real[mapPoint.x]], [real[mapPoint.y]] ]; }; UpdateSceneForCopy: PROC [scene: Scene, feedback: FeedbackData] RETURNS [newSlices: LIST OF Slice, success: BOOL _ TRUE] = { sliceDGen: SliceDescriptorGenerator; outSeqGen: GGSelect.OutlineSequenceGenerator; newOutline, outline: Outline; newTraj: Traj; newSlice: Slice; sliceDGen _ GGSelect.SelectedSlices[scene, normal]; FOR sliceD: SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDGen], GGSelect.NextSliceDescriptor[sliceDGen] UNTIL sliceD = NIL DO newSlice _ GGSlice.CopySlice[sliceD.slice]; -- make the new one GGSelect.DeselectSlice[sliceD.slice, sliceD.parts, scene, normal]; newSlices _ CONS[newSlice, newSlices]; newSlice.priority _ GGScene.SlicePriority[scene, sliceD.slice]; ENDLOOP; BEGIN outSeqGen _ GGSelect.SelectedOutlineSequences[scene, normal]; FOR outSeq: GGSelect.OutlineSequence _ GGSelect.NextOutlineSequences[outSeqGen], GGSelect.NextOutlineSequences[outSeqGen] UNTIL outSeq = NIL DO outline _ outSeq.outline; IF outSeq.fenceSeq # NIL THEN { IF NOT GGSequence.ContainsSomeSegment[outSeq.fenceSeq] THEN GOTO NoSegmentsSelected; IF GGSelect.IsSelectedInFull[outline, scene, normal] THEN { newOutline _ outline.class.copy[outline]; GGSelect.DeselectEntireSlice[outline, scene, normal]; -- deselect the old one newSlices _ CONS[newOutline, newSlices]; newOutline.priority _ outline.priority; LOOP; } ELSE { newTraj _ GGTraj.CopyTrajFromRun[outSeq.fenceSeq]; newOutline _ GGOutline.CreateOutline[newTraj, outline.class.getFillColor[outline]]; GGSelect.DeselectSequence[outSeq.fenceSeq, scene, normal]; newSlices _ CONS[newOutline, newSlices]; newOutline.priority _ outline.priority; }; }; FOR holeSeq: Sequence _ GGSequence.NextSequence[outSeq.holeSeqs], GGSequence.NextSequence[outSeq.holeSeqs] UNTIL holeSeq = NIL DO IF NOT GGSequence.ContainsSomeSegment[holeSeq] THEN GOTO NoSegmentsSelected; newTraj _ GGTraj.CopyTrajFromRun[holeSeq]; newOutline _ GGOutline.CreateOutline[newTraj, outline.class.getFillColor[outline]]; GGSelect.DeselectSequence[holeSeq, scene, normal]; newSlices _ CONS[newOutline, newSlices]; newOutline.priority _ outline.priority; ENDLOOP; ENDLOOP; newSlices _ SortNewEntities[scene, newSlices]; FOR sliceList: LIST OF Slice _ newSlices, sliceList.rest UNTIL sliceList = NIL DO allParts: SliceDescriptor; GGScene.AddSlice[scene, sliceList.first, -1]; allParts _ sliceList.first.class.newParts[sliceList.first, NIL, slice]; GGSelect.SelectSlice[allParts, scene, normal]; ENDLOOP; EXITS NoSegmentsSelected => { Feedback.AppendHerald[feedback, ". . . Cannot Copy Joints or CPs", begin]; Feedback.Blink[feedback]; GGSelect.DeselectAll[scene, normal]; RETURN[NIL, FALSE]; }; END; }; StartCopyAndDrag: PUBLIC StartProc = { sliceList: LIST OF Slice; scene: Scene _ ggData.scene; SaveSavedState[ggData]; -- must do this before any possible aborts occur [sliceList, success] _ UpdateSceneForCopy[scene, ggData.feedback]; -- adds new shapes and selects them IF NOT success THEN RETURN; GGAlign.UpdateBagsForNewSlices[sliceList, ggData]; success _ StartMotion[ggData: ggData, opName: "copy", bagType: $Drag, worldPt: worldPt, saveState: FALSE, needAnchor: FALSE, backgroundOK: TRUE]; IF NOT success THEN RETURN[FALSE]; DuringDrag[NIL, ggData, worldPt]; }; TransformObjectsAfterMove: PROC [scene: Scene, transform: ImagerTransformation.Transformation] = { sliceDescGen: SliceDescriptorGenerator _ GGSelect.SelectedSlices[scene, normal]; FOR sliceD: SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen] UNTIL sliceD=NIL DO sliceD.slice.class.transform[sliceD, transform]; ENDLOOP; }; ContinueMotion: PROC [ggData: GGData, opName: Rope.ROPE, bagType: ATOM, worldPt: Point, startBox: BoundBox _ NIL] RETURNS [success: BOOL _ TRUE] = { movingBox: BoundBox; IF NOT GGRefresh.EmptyOverlay[ggData] THEN ERROR; -- nothing on overlay GGRefresh.MoveAllSelectedToOverlay[ggData, normal]; ggData.drag.startPoint _ GGCaret.GetPoint[ggData.caret]; GGCaret.SetAttractor[ggData.caret, ggData.drag.startPoint, NIL]; ggData.drag.transform _ ImagerTransformation.Scale[1.0]; movingBox _ GGBoundBox.BoundBoxOfMoving[ggData.scene]; GGBoundBox.EnlargeByBox[bBox: movingBox, by: startBox]; ggData.refresh.startBoundBox^ _ movingBox^; [] _ GGAlign.StaticToDynamicBags[ggData]; GGRefresh.UpdateForegroundForMotion[ggData]; }; StartMotion: PROC [ggData: GGData, opName: Rope.ROPE, bagType: ATOM, worldPt: Point, saveState: BOOL _ TRUE, needAnchor: BOOL _ FALSE, backgroundOK: BOOL _ FALSE] RETURNS [success: BOOL _ TRUE] = { movingBox: BoundBox; repaintNeeded: BOOL; CodeTimer.StartInt[$StartMotion, $Gargoyle]; IF saveState THEN SaveSavedState[ggData]; -- must do this before any possible aborts occur BEGIN IF NOT GGRefresh.EmptyOverlay[ggData] THEN ERROR; -- nothing on overlay IF GGSelect.NoSelections[ggData.scene, normal] THEN GOTO NoSelections; IF needAnchor AND NOT GGCaret.Exists[ggData.anchor] THEN GOTO NoAnchor; GGRefresh.MoveAllSelectedToOverlay[ggData, normal]; ggData.drag.startPoint _ GGCaret.GetPoint[ggData.caret]; GGCaret.NoAttractor[ggData.caret]; -- is this really needed? Bier, March 26, 1987 ggData.drag.transform _ ImagerTransformation.Scale[1.0]; movingBox _ GGBoundBox.BoundBoxOfMoving[ggData.scene]; ggData.refresh.startBoundBox^ _ movingBox^; IF NOT backgroundOK THEN GGRefresh.SplitBackgroundAndOverlay[ggData, movingBox]; repaintNeeded _ GGAlign.StaticToDynamicBags[ggData]; IF repaintNeeded THEN GGWindow.RestoreScreenAndInvariants[paintAction: $None, ggData: ggData, remake: bitMap, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE] ELSE GGWindow.RestoreScreenAndInvariants[paintAction: $None, ggData: ggData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; CodeTimer.StopInt[$StartMotion, $Gargoyle]; EXITS NoSelections => { Feedback.PutFHerald[ggData.feedback, oneLiner, "Select some objects to %g", [rope[opName]]]; Feedback.Blink[ggData.feedback]; success _ FALSE; CodeTimer.StopInt[$StartMotion, $Gargoyle]; }; NoAnchor => { Feedback.PutFHerald[ggData.feedback, oneLiner, "Anchor needed to %g", [rope[opName]]]; Feedback.Blink[ggData.feedback]; success _ FALSE; CodeTimer.StopInt[$StartMotion, $Gargoyle]; }; END; }; StartDrag: PUBLIC StartProc = { startSuccess: BOOL _ StartMotion[ggData, "drag", $Drag, worldPt]; IF NOT startSuccess THEN RETURN[FALSE]; DuringDrag[NIL, ggData, worldPt]; }; StartRotate: PUBLIC StartProc = { startSuccess: BOOL _ StartMotion[ggData, "rotate", $Drag, worldPt, TRUE, TRUE]; IF NOT startSuccess THEN RETURN[FALSE]; DuringRotate[NIL, ggData, worldPt]; }; StartScale: PUBLIC StartProc = { anchorPoint: Point; originalVector: Vector; startSuccess: BOOL _ StartMotion[ggData, "scale", $Drag, worldPt, TRUE, TRUE]; IF NOT startSuccess THEN RETURN[FALSE]; anchorPoint _ GGCaret.GetPoint[ggData.anchor]; originalVector _ Vectors2d.Sub[ggData.drag.startPoint, anchorPoint]; IF originalVector = [0.0, 0.0] THEN { Feedback.AppendHerald[ggData.feedback, "Move caret away from anchor before scaling.", oneLiner]; Feedback.Blink[ggData.feedback]; RETURN[FALSE]; }; DuringScale[NIL, ggData, worldPt]; }; StartSixPoint: PUBLIC StartProc = { OPEN Vectors2d; epsilon: REAL = 1.0e-3; p0, p1, p2: Point; crossProduct: REAL; startSuccess: BOOL _ StartMotion[ggData, "six point", $Drag, worldPt, TRUE, TRUE]; IF NOT startSuccess THEN RETURN[FALSE]; p0 _ GGCaret.GetPoint[ggData.anchor]; p2 _ ggData.drag.startPoint; p1 _ Add[p0, VectorPlusAngle[Sub[p2, p0], 90.0]]; crossProduct _ CrossProductScalar[Sub[p1,p0], Sub[p2, p0]]; IF ABS[crossProduct] < epsilon THEN { Feedback.AppendHerald[ggData.feedback, "Move caret away from anchor before six point.", oneLiner]; Feedback.Blink[ggData.feedback]; RETURN[FALSE]; }; DuringSixPoint[NIL, ggData, worldPt]; }; DragTheCaret: PROC [worldPt: Point, ggData: GGData, opName: Rope.ROPE] RETURNS [mapPoint: Point] = { feature: FeatureData; hitData: REF ANY; [mapPoint, feature, hitData] _ GGMultiGravity.Map[worldPt, ggData.hitTest.criticalR, ggData.hitTest.alignBag, ggData.hitTest.sceneBag, ggData, TRUE]; SetCaretAttractor[ggData, mapPoint, feature, hitData, opName]; }; DuringDrag: PUBLIC MouseProc = { totalDragVector: Vector; mapPoint: Point; CodeTimer.StartInt[$DuringDrag, $Gargoyle]; mapPoint _ DragTheCaret[worldPt, ggData, "Dragging:"]; totalDragVector _ Vectors2d.Sub[mapPoint, ggData.drag.startPoint]; ggData.drag.transform _ ImagerTransformation.Translate[[totalDragVector.x, totalDragVector.y]]; GGWindow.RestoreScreenAndInvariants[paintAction: $DuringMotion, ggData: ggData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; CodeTimer.StopInt[$DuringDrag, $Gargoyle]; }; -- end DuringDrag DuringRotate: PUBLIC MouseProc = { originalVector, newVector: Vector; mapPoint: Point; degrees: REAL; anchorPoint: Point _ GGCaret.GetPoint[ggData.anchor]; mapPoint _ DragTheCaret[worldPt, ggData, "Rotating:"]; originalVector _ Vectors2d.Sub[ggData.drag.startPoint, anchorPoint]; newVector _ Vectors2d.Sub[mapPoint, anchorPoint]; degrees _ Vectors2d.AngleCCWBetweenVectors[originalVector, newVector]; ggData.drag.transform _ GGTransform.RotateAboutPoint[anchorPoint, degrees]; GGWindow.RestoreScreenAndInvariants[paintAction: $DuringMotion, ggData: ggData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; }; DuringScale: PUBLIC MouseProc = { epsilon: REAL = 1.0e-3; originalVector, newVector: Vector; mapPoint: Point; ratio: REAL; anchorPoint: Point _ GGCaret.GetPoint[ggData.anchor]; mapPoint _ DragTheCaret[worldPt, ggData, "Scaling:"]; originalVector _ Vectors2d.Sub[ggData.drag.startPoint, anchorPoint]; newVector _ Vectors2d.Sub[mapPoint, anchorPoint]; IF RealFns.AlmostZero[newVector.x, -10] AND RealFns.AlmostZero[newVector.y, -10] THEN RETURN; -- can't scale to zero ratio _ Vectors2d.Magnitude[newVector]/Vectors2d.Magnitude[originalVector]; ggData.drag.transform _ GGTransform.ScaleAboutPoint[anchorPoint, ratio]; GGWindow.RestoreScreenAndInvariants[paintAction: $DuringMotion, ggData: ggData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; }; DuringSixPoint: PUBLIC MouseProc = { OPEN Vectors2d; epsilon: REAL = 1.0e-3; pts: ARRAY [0..5] OF Point; crossProduct: REAL; mapPoint: Point; pts[0] _ pts[3] _ GGCaret.GetPoint[ggData.anchor]; pts[2] _ ggData.drag.startPoint; pts[1] _ pts[4] _ Add[pts[0], VectorPlusAngle[Sub[pts[2], pts[0]], 90.0]]; mapPoint _ DragTheCaret[worldPt, ggData, "Six Point Transform:"]; pts[5] _ mapPoint; crossProduct _ CrossProductScalar[Sub[pts[4],pts[3]], Sub[pts[5],pts[3]]]; IF ABS[crossProduct] < epsilon THEN RETURN; -- illegal six point transform ggData.drag.transform _ GGTransform.SixPoints[pts]; GGWindow.RestoreScreenAndInvariants[paintAction: $DuringMotion, ggData: ggData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; }; ContinueAdd: PUBLIC StartProc = { CodeTimer.StartInt[$ContinueAdd, $Gargoyle]; TransformObjectsAfterMove[ggData.scene, ggData.drag.transform]; GGRefresh.MoveOverlayToBackground[ggData]; GGWindow.NewCaretPos[ggData]; [] _ GGAlign.DynamicToStaticBags[ggData]; CodeTimer.StopInt[$ContinueAdd, $Gargoyle]; success _ StartAdd[LIST[$ContinueAdd], ggData, worldPt]; }; EndMotion: PUBLIC MouseProc = { CodeTimer.StartInt[$EndMotion, $Gargoyle]; TransformObjectsAfterMove[ggData.scene, ggData.drag.transform]; GGRefresh.MoveOverlayToBackground[ggData]; GGWindow.NewCaretPos[ggData]; [] _ GGAlign.DynamicToStaticBags[ggData]; GGWindow.RestoreScreenAndInvariants[paintAction: $FinishedDragging, ggData: ggData, remake: bitMap, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE]; CodeTimer.StopInt[$EndMotion, $Gargoyle]; }; SafelyGetCaretTraj: PROC [caret: Caret] RETURNS [chair: SliceDescriptor, partType: TrajPartType, traj: Traj, jointNum: NAT] = { success: BOOL _ FALSE; chair _ GGCaret.GetChair[caret]; [success, partType, traj, ----, jointNum] _ GGOutline.UnpackSimpleDescriptor[chair]; IF NOT success OR partType # joint THEN ERROR Problem[msg: "Attempt to extend a trajectory without the caret on its end"]; }; UpdateSceneForAdd: PROC [scene: Scene, worldPt: Point, caret: Caret, defaults: DefaultData] RETURNS [oldTraj, newTraj: Traj, trajEnd: TrajEnd, newOutline: Outline] = { caretPoint: Point; jointNum: NAT; newSeg, extendSeg: Segment; chair: SliceDescriptor; partType: TrajPartType; success: BOOL; caretPoint _ GGCaret.GetPoint[caret]; newSeg _ GGSegment.MakeLine[worldPt, caretPoint, NIL]; IF GGCaret.SittingOnEnd[caret] THEN { [chair, partType, newTraj, jointNum] _ SafelyGetCaretTraj[caret]; oldTraj _ newTraj; trajEnd _ SELECT jointNum FROM 0 => lo, GGTraj.HiJoint[newTraj] => hi, ENDCASE => ERROR; extendSeg _ GGTraj.FetchSegment[newTraj, IF trajEnd=lo THEN 0 ELSE GGTraj.HiSegment[newTraj]]; GGSegment.CopyLooks[extendSeg, newSeg]; success _ GGTraj.AddSegment[newTraj, trajEnd, newSeg, hi]; IF NOT success THEN RETURN; newOutline _ chair.slice; } ELSE { oldTraj _ NIL; trajEnd _ hi; newTraj _ GGTraj.CreateTraj[caretPoint]; GGTraj.SetTrajStrokeJoint[newTraj, defaults.strokeJoint]; GGSegment.SetDefaults[newSeg, defaults]; success _ GGTraj.AddSegment[newTraj, trajEnd, newSeg, hi]; IF NOT success THEN RETURN; newOutline _ GGOutline.CreateOutline[newTraj, defaults.fillColor]; GGScene.AddOutline[scene, newOutline, -1]; }; }; UpdateSelectionsForAdd: PROC [scene: Scene, oldTraj, newTraj: Traj, trajEnd: TrajEnd] RETURNS [newNormal, newHot: Sequence] = { jointNum: NAT; jointNum _ SELECT trajEnd FROM lo => 0, hi => GGTraj.HiJoint[newTraj], ENDCASE => ERROR; IF oldTraj # NIL THEN { newHot _ GGSelect.ReselectTraj[newTraj, trajEnd, scene, TRUE]; newNormal _ GGSequence.CreateFromJoint[newTraj, jointNum]; } ELSE { newHot _ GGSequence.CreateEmpty[newTraj]; newNormal _ GGSequence.CreateFromJoint[newTraj, 1]; }; GGSelect.DeselectAll[scene, normal]; GGSelect.SelectSequence[newNormal, scene, normal]; }; UpdateCaretForAdd: PROC [caret: Caret, newOutline: Outline, newNormal: Sequence, worldPt: Point] = { jointD: OutlineDescriptor; GGCaret.SetAttractor[caret, worldPt, NIL]; jointD _ GGOutline.DescriptorFromSequence[newOutline, newNormal]; GGCaret.SitOn[caret, jointD]; }; StartAdd: PUBLIC StartProc = { continue: BOOL _ FALSE; caret: Caret _ ggData.caret; scene: Scene _ ggData.scene; oldTraj, newTraj: Traj; newOutline, oldOutline: Outline; trajEnd: TrajEnd; newNormal, newHot: Sequence; startBox: BoundBox; CodeTimer.StartInt[$StartAdd, $Gargoyle]; continue _ NARROW[input.first, ATOM] = $ContinueAdd; SaveSavedState[ggData]; [oldTraj, newTraj, trajEnd, newOutline] _ UpdateSceneForAdd[scene, worldPt, caret, ggData.defaults]; oldOutline _ IF oldTraj = NIL THEN NIL ELSE GGOutline.OutlineOfTraj[oldTraj]; [newNormal, newHot] _ UpdateSelectionsForAdd[scene, oldTraj, newTraj, trajEnd]; UpdateCaretForAdd[caret, newOutline, newNormal, worldPt]; [] _ GGAlign.UpdateBagsForAdd[oldOutline, newOutline, trajEnd, ggData]; IF continue THEN { startBox _ ggData.refresh.startBoundBox; success _ ContinueMotion[ggData: ggData, opName: "add", bagType: $Drag, worldPt: worldPt, startBox: startBox]; } ELSE { success _ StartMotion[ggData: ggData, opName: "add", bagType: $Drag, worldPt: worldPt, saveState: FALSE, needAnchor: FALSE, backgroundOK: TRUE]; }; IF NOT success THEN RETURN[FALSE]; DuringDrag[NIL, ggData, worldPt]; CodeTimer.StopInt[$StartAdd, $Gargoyle]; }; -- end StartAdd AddNewBoxSlice: PROC [from, to: Point, ggData: GGData] RETURNS [sliceD: SliceDescriptor] = { completeSliceD: SliceDescriptor; box: BoundBox; corner: GGSlice.Corner _ none; loX: REAL _ MIN[from.x, to.x ]; loY: REAL _ MIN[from.y, to.y ]; hiX: REAL _ MAX[from.x, to.x ]; hiY: REAL _ MAX[from.y, to.y ]; IF to.x=loX THEN IF to.y=loY THEN corner _ ll ELSE corner _ ul; IF to.x=hiX THEN IF to.y=loY THEN corner _ lr ELSE corner _ ur; box _ GGBoundBox.CreateBoundBox[loX, loY, hiX, hiY]; ggData.drag.boxInProgress _ sliceD _ GGSlice.MakeBoxSlice[box, corner, GGTransform.Identity[]]; completeSliceD _ sliceD.slice.class.newParts[sliceD.slice, NIL, slice]; GGSlice.SetDefaults[completeSliceD.slice, completeSliceD.parts, ggData.defaults]; GGScene.AddSlice[ggData.scene, sliceD.slice, -1]; }; StartBox: PUBLIC StartProc = { sliceD: SliceDescriptor; caretPos: Point; CodeTimer.StartInt[$StartBox, $Gargoyle]; caretPos _ GGCaret.GetPoint[ggData.caret]; SaveSavedState[ggData]; -- must do this before any possible aborts occur IF NOT GGRefresh.EmptyOverlay[ggData] THEN ERROR; -- nothing on overlay sliceD _ AddNewBoxSlice[caretPos, worldPt, ggData]; GGSelect.DeselectAll[ggData.scene, normal]; GGSelect.SelectSlice[sliceD: sliceD, scene: ggData.scene, selectClass: normal]; GGRefresh.MoveToOverlay[sliceD, ggData]; -- slice on overlay to be rubberbanded ggData.drag.startPoint _ worldPt; ggData.drag.transform _ ImagerTransformation.Scale[1.0]; GGRefresh.SplitBackgroundAndOverlay[ggData, GGBoundBox.emptyBoundBox]; GGWindow.RestoreScreenAndInvariants[paintAction: $None, ggData: ggData, remake: bitMap, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; DuringDrag[NIL, ggData, worldPt]; CodeTimer.StopInt[$StartBox, $Gargoyle]; }; StartSelectWithBox: PUBLIC StartProc = { sliceD: SliceDescriptor; caretPos: Point; CodeTimer.StartInt[$StartBox, $Gargoyle]; caretPos _ GGCaret.GetPoint[ggData.caret]; SaveSavedState[ggData]; -- must do this before any possible aborts occur IF NOT GGRefresh.EmptyOverlay[ggData] THEN ERROR; -- nothing on overlay sliceD _ AddNewBoxSlice[caretPos, worldPt, ggData]; sliceD.slice.class.setFillColor[sliceD.slice, NIL]; GGSelect.DeselectAll[ggData.scene, normal]; GGSelect.SelectSlice[sliceD: sliceD, scene: ggData.scene, selectClass: normal]; GGRefresh.MoveToOverlay[sliceD, ggData]; -- slice on overlay to be rubberbanded ggData.drag.startPoint _ worldPt; ggData.drag.transform _ ImagerTransformation.Scale[1.0]; GGRefresh.SplitBackgroundAndOverlay[ggData, GGBoundBox.emptyBoundBox]; GGWindow.RestoreScreenAndInvariants[paintAction: $None, ggData: ggData, remake: bitMap, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; DuringDrag[NIL, ggData, worldPt]; CodeTimer.StopInt[$StartBox, $Gargoyle]; }; EndBox: PUBLIC MouseProc = { sliceD: SliceDescriptor _ NARROW[ggData.drag.boxInProgress]; slice: Slice _ sliceD.slice; slice.class.transform[sliceD, ggData.drag.transform]; -- update the slice GGRefresh.MoveOverlayToBackground[ggData]; ggData.refresh.startBoundBox^ _ slice.boundBox^; --newly updated by slice.class.transform ggData.refresh.addedObject _ slice; GGWindow.RestoreScreenAndInvariants[paintAction: $FinishedAdding, ggData: ggData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE]; }; EndSelectWithBox: PUBLIC MouseProc = { sliceD: SliceDescriptor _ NARROW[ggData.drag.boxInProgress]; slice: Slice _ sliceD.slice; slice.class.transform[sliceD, ggData.drag.transform]; -- update the slice GGRefresh.MoveOverlayToBackground[ggData]; GGSelect.SelectSlice[sliceD: slice.class.newParts[slice, NIL, topLevel], scene: ggData.scene, selectClass: normal]; GGEvent.AreaSelectNewAndDelete[ggData, NIL]; ggData.refresh.startBoundBox^ _ slice.boundBox^; --newly updated by slice.class.transform GGWindow.RestoreScreenAndInvariants[paintAction: $FinishedAdding, ggData: ggData, remake: triggerBag, backgndOK: FALSE, edited: FALSE, okToClearFeedback: FALSE]; }; FixupAbortedBox: PROC [ggData: GGData] = { sliceD: SliceDescriptor _ NARROW[ggData.drag.boxInProgress]; slice: Slice _ sliceD.slice; repaintBox: BoundBox _ GGCaret.BoundBoxOfCaret[ggData.caret, ggData]; -- start with caret GGBoundBox.EnlargeByBox[repaintBox, slice.boundBox]; -- repaint deleted box slice GGSelect.DeselectEntityAllClasses[slice, ggData.scene]; GGScene.DeleteSlice[ggData.scene, slice]; ggData.refresh.startBoundBox^ _ repaintBox^; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, backgndOK: FALSE, edited: FALSE, okToClearFeedback: FALSE]; }; InitStats: PROC [] = { interval: CodeTimer.Interval; interval _ CodeTimer.CreateInterval[$StartAdd]; CodeTimer.AddInt[interval, $Gargoyle]; interval _ CodeTimer.CreateInterval[$DuringDrag]; CodeTimer.AddInt[interval, $Gargoyle]; interval _ CodeTimer.CreateInterval[$StartCaretPos]; CodeTimer.AddInt[interval, $Gargoyle]; interval _ CodeTimer.CreateInterval[$DuringCaretPos]; CodeTimer.AddInt[interval, $Gargoyle]; interval _ CodeTimer.CreateInterval[$EndCaretPos]; CodeTimer.AddInt[interval, $Gargoyle]; interval _ CodeTimer.CreateInterval[$ContinueAdd]; CodeTimer.AddInt[interval, $Gargoyle]; interval _ CodeTimer.CreateInterval[$StartMotion]; CodeTimer.AddInt[interval, $Gargoyle]; interval _ CodeTimer.CreateInterval[$EndMotion]; CodeTimer.AddInt[interval, $Gargoyle]; interval _ CodeTimer.CreateInterval[$StartBox]; CodeTimer.AddInt[interval, $Gargoyle]; interval _ CodeTimer.CreateInterval[$HandleGuarded]; CodeTimer.AddInt[interval, $Gargoyle]; }; InitStats[]; END. DGGMouseEventImplA.mesa Last edited by Bier on May 13, 1987 5:07:17 pm PDT Contents: Once a mouse event reaches the front of the slack-process queue, it is dispatched to one of the procedures in this module. Pier, May 14, 1987 3:51:28 pm PDT Kurlander August 7, 1986 10:54:44 am PDT PROC [input: LIST OF REF ANY, ggData: GGData, worldPt: Point]; PROC [input: LIST OF REF ANY, ggData: GGData, worldPt: Point] RETURNS [success: BOOL _ TRUE]; Many actions don't change the scene at all until the End proc is called. For these cases, we just restore the selections and caret position. GGScene.RestoreSelections[ggData.scene]; -- can't do this. Obsolete saved selection GGEvent.Delete[NIL, ggData]; The FSM An easy way to handle the Abort action. Sort back to front (in increasing order). [ref1: REF ANY, ref2: REF ANY] RETURNS [Basics.Comparison] outline: Outline => priority1 _ GGScene.SlicePriority[scene, outline]; outline: Outline => priority2 _ GGScene.SlicePriority[scene, outline]; event will be in the format LIST[ATOM, REF Point]; (mouseMode = $Circle AND state = $StartCircle) OR Caret Procs The user wishes to place the caret without changing the current selection. Only hot objects trigger alignment lines. While the caret is being placed, the normal gravity scheme applies. The only feedback is the appearance of text strings and the position of the caret itself. Move caret to new position. Moves the caret and records the attractor which is a simple descriptor. This code very similar to SetCaretAttractorEndpoint in GGMouseEventImplB. Called by EndCaretPos and all the "During" procs. outline => { partsD: OutlineDescriptor; outlineD: OutlineDescriptor _ NARROW[feature.shape]; partsD _ outlineD.slice.class.newParts[outlineD.slice, hitData, literal]; GGCaret.SetAttractor[ggData.caret, mapPoint, partsD]; }; See GGMultiGravityImpl.FindIntersections to see where this was made. outlineD: OutlineDescriptor => attractorD _ outlineD.slice.class.newParts[outlineD.slice, hitData, literal]; outlineD: OutlineDescriptor => attractorD _ outlineD.slice.class.newParts[outlineD.slice, hitData, literal]; outlineD: OutlineDescriptor => attractorD _ outlineD.slice.class.newParts[outlineD.slice, hitData, literal]; Copy and Drag Copy selected slices. If the whole outline is selected, copy it as a whole. Otherwise, each piece becomes a new slice. Sort the new slices Add all the new slices to the scene. We are about to self-abort. We deselect all so that we don't delete any of the original geometry (see AbortCopyAndDrag above). Create copies of all selected objects. The selected objects can be part of trajectories, trajectories, outlines, or whole slices. Sort the resulting slices by the priority ordering of the objects they came from. Add the new objects on top. This can be done in two ways: Sort the selections by priority order to begin with, or sort the new objects after they are created. backgroundOK is TRUE because we are strictly adding. Motion Procs We begin with correct static bags. Add is done. Begin the drag. Set Caret Set Transform Prepare Refresh Bags are now correct and dynamic. Prepare Foreground Plane Set Caret Set Transform Prepare Refresh Prepare Bags The dragging is done. Update the endpoint with the current transform. Then begin a new add operation. We are done with the last add and the bags are correct and static. The dragging is done. Update all of the drag entities with the total drag vector and repaint the entire scene. Addition and Extension Procs SafelyGetCaretTrajOLDD: PROC [caret: Caret] RETURNS [outlineD: OutlineDescriptor, partType: TrajPartType, traj: Traj, jointNum: NAT] = { chair: REF ANY; success: BOOL; chair _ GGCaret.GetChair[caret]; IF ISTYPE[chair, SliceDescriptor] THEN ERROR Problem[msg: "Slices are Outlines now. Update this code."]; outlineD _ NARROW[chair]; [success, partType, traj, ----, jointNum] _ GGOutline.UnpackSimpleDescriptorOld[outlineD]; IF NOT success OR partType # joint THEN ERROR Problem[msg: "Attempt to extend a trajectory without the caret on its end"]; }; Extend the existing trajectory Create a new trajectory starting at the caret. The new outline should be hot in the same places as the old. StartAdd must update the caret, the scene, the selections, and the bags. Then, it transfers control to StartDrag. We have added the NEW segment and the bags are correct and static. backgroundOK is TRUE even though this will leave some residue on the background, because we are adding, so the traj is open (not filled). do no painting yet Move new box to overlay Initialize the transformation and remember the starting position. THIS IS A TEMPORARY PROC TO GET SELECTWITHBOX WITHOUT DEFAULT FILL COLOR GETTING IN THE WAY Move new box to overlay Initialize the transformation and remember the starting position. The dragging is done. Update the box slice with the current transform. The box is cached in the ggData. The dragging is done. Update the box slice with the current transform. The box is cached in the ggData. Then, select the entire box and call AreaSelectNewAndDelete. This routine is called instead of EndBox. Find the box that is being added and delete it. Ê+&˜Icode™Kšœ2™2šœ…™…Kšœ!™!K™(—K™šÏk ˜ Jšœÿ˜ÿK˜—šÏnœœ˜ Jšœž˜¥Kšœ˜—˜Kšœœ#˜7Kšœ œ˜'Kšœœ˜%Kšœ œ˜-Kšœœ ˜5Kšœ œ˜-Kšœœ˜+Kšœœ˜'Kšœœ˜!Kšœ œ˜&Kšœ œ˜$Kšœ œ˜%Kšœœœ˜3Kšœœ%˜?Kšœœ˜!Kšœœ"˜9Kšœœ˜!Kšœ œ˜'Kšœ œ˜'Kšœœ˜!Kšœ œ˜+Kšœœ˜3Kšœœ ˜5Kšœœ#˜;Kšœœ)˜GKšœœ˜Kšœ œ˜%Kšœœ˜,Kšœœ˜/Kšœ œ˜&Kšœœ˜#K˜šœ œ˜)Kš œ œœœœ"™>—Kšœ œ˜)Kšœ œœœœ"œ œ™]—K˜Kšžœœœ œ˜;K˜š ž œœ œœœœ%˜LK™Kšœ˜Kšœ!˜!Kšœ>Ïc˜VKšœ˜K˜K˜—š žœœ œœœœ%˜KKšœ#œ˜(Kšœ)Ÿ*™S•StartOfExpansionu[sliceD: GGModelTypes.OutlineDescriptor, scene: GGModelTypes.Scene, selectClass: GGSegmentTypes.SelectionClass]˜Kšœœ!˜CKšœœœP˜bKšœ˜K˜—K˜K˜—š žœœ œœœœ%˜SKšœœ ™Kšœ-˜-Kšœ(˜(K˜=Kšœ˜K˜K˜—š žœœ œœœœ%˜KKšœ˜Kšœ˜K˜K˜—šž œœ˜&Kšœ*˜*Kšœ>˜>Kšœsœ œœ˜£K˜K˜—šž œœœ˜/Kšœ˜Kšœ˜K˜K˜—šžœœœ˜5Kšœ˜Kšœ˜Kšœ*˜*K˜K˜—šžœœ˜)Kšœ%˜%Kšœ˜K˜=K˜—K™K™šžœœœœœ œœœœ˜NK™'Kšœœœ˜3Kšœ˜K˜K˜—šžœœœœœ œœ ˜cK–> -- [ref1: REF ANY, ref2: REF ANY] RETURNS [Basics.Comparison]™)šž œ˜"KšÐck:™:Kšœœ˜šœœ ˜Kšœ$ž œ˜@Kšœ(ž œ™FKšœ˜—šœœ˜Kšœ$ž œ˜@Kšœ(ž œ™FKšœ˜—Kšœ*˜0K˜—Kšœ œ&˜5K˜K˜—šž œœœœœ œœœœ˜JK™K™Kšœœ ˜$Kšœœœ˜!Kšœœœ ˜4šœ˜KšœRœ˜mKšœ>œ˜YKšœd˜dKšœDœ˜_KšœYœ˜tKšœJœ˜eKšœGœ˜bKšœPœ˜kKšœt˜tKšœ“˜“Kšœ–œ˜±Kšœ—˜—Kšœšœ˜µKšœ˜Kšœšœ˜µKšœ™˜™Kšœœœ˜·Kšœ­˜­Kšœƒœ˜žKšœ‡œ˜¢Kšœœ˜¨Kšœ‰œ˜¤˜ šœ˜šœ˜Kšœ9˜9K˜—šœ˜Kšœ4˜4K˜—šœ˜Kšœ4˜4K˜—šœ˜Kšœ>˜>K˜—šœ˜Kšœ5˜5K˜—šœ˜Kšœ<˜˜>K˜—šœ˜KšœA˜AK˜—šœ˜KšœA˜AK˜—šœ˜KšœD˜DK˜—šœ˜Kšœ?˜?K˜—šœ˜KšœB˜BK˜—šœ˜Kšœ@˜@K˜—šœ˜Kšœ>˜>K˜—šœ˜Kšœ@˜@K˜—šœ˜KšœC˜CK˜—šœ˜KšœA˜AK˜—KšœŸ˜ —K˜—Kšœœ$˜5—˜K˜—K™—š ž œœcœœœœ%˜ªKšœœ˜Kšœœ˜Kšœ.˜.šœ œ˜Kšœ œœœŸ)˜FKšœ˜—Kšœœ˜Kšœ˜Kš œœ2œœœ˜`Kšœq˜qKšœ-˜-K˜K˜—šžœœ œœœœœœ˜IKšœ œ˜#Kšœœœ˜"šœ˜Kšœœ˜5Kšœœ˜+Kšœœ˜+Kšœœ™1Kšœœ˜-Kšœœ˜;Kšœœ˜1Kšœœ˜/Kšœœ˜;Kšœœ˜?Kšœœ!˜EKšœœ˜AKšœœ ˜CKšœœ˜?Kšœœ ˜CKšœ!œ#˜IKšœœ"˜D—˜K˜——šžœœkœ œœœœ%˜Âšœ˜˜ šœ˜šœ ˜ Kšœ#˜#Kšœ1˜1KšœÏb œœ˜>Kšœ¡ œ7˜F—Kšœ˜—K˜—˜ šœ˜šœ ˜ Kš¡ œ˜#Kšœ#˜#K˜—Kšœ¡ œ3˜XKšœ$˜$Kšœ$˜$šœ Ÿ}˜‰Kš¡ œ/˜8Kšœ˜Kšœ˜K˜—Kšœ˜—K˜—šœ ˜ šœ˜šœ ˜ Kš¡œ¡œ˜1Kšœ˜Kšœ˜K˜—Kšœ¡ œ3˜XKšœ˜—K˜—˜ šœ˜šœ˜Kš¡œ¡œ˜1Kšœ˜Kšœ˜K˜—Kšœ ¡ œ3˜Gšœ ŸE˜Qšœœœœ˜7šœ¡ œ˜.Kšœ˜Kšœ#˜#K˜—Kšœ¡ œ4˜CK˜—šœ˜Kšœ˜Kš¡œ¡œ˜1Kšœ˜Kšœ˜K˜—K˜—Kšœ˜—K˜—šœ ˜ Kšœ˜Kšœ;˜;Kšœ˜K˜—Kšœœ'˜8—K˜K˜—š žœœJœœœœ%˜“Kšœœ˜Kšœœ˜šœ œ˜Kšœ œœœŸ)˜FKšœ˜—Kšœœ˜Kšœ˜Kš œœ2œœœ˜`Kšœe˜eK˜K˜—šžœœRœ œœœœ%˜«šœ˜˜ šœ˜šœ ˜ Kšœ1˜1Kšœ¡ œœ˜>Kšœ¡ œ2˜AKšœ˜—Kšœ˜—K˜—˜ šœ˜Kšœ ¡ œ˜.šœ˜Kš¡œ˜ Kšœ˜Kšœ˜K˜—Kšœ ¡ œ3˜Gšœ Ÿ}˜‰Kš¡ œ/˜8Kšœ˜Kšœ˜K˜—Kšœ˜—K˜—šœ ˜ šœ˜Kšœ;˜;Kšœ=˜=Kšœ˜—K˜—Kšœœ'˜8—K˜K˜—K™K™ K˜šž œœ˜#K™uKšœ.˜.Kšœœ œœ˜1K˜Kš¡œœ˜%Kšœ-˜-K˜K˜—šžœœ˜$K™žKšœ˜Kšœ˜Kšœ œœ˜K˜Kšœ/˜/Kšœ1¡œ^œ˜˜Kšœ:Ÿ˜XKšœkœ œœŸ˜¹Kšœ.˜.KšœŸ˜K˜—šž œœ˜!Kšœ˜Kšœ˜Kšœ œœ˜K˜Kšœœ˜;Kšœœ˜;Kšœ,˜,šœ1¡œJœ˜„Kš¡™—KšœCŸ˜aKšœ˜KšœœŸ9˜[Kšœgœ œœ˜–Kšœ+˜+Kšœ˜K˜—šž œœ)˜;Kšœ œœœ˜/šœŸ+˜2šœœ˜Kšœ>˜>Kšœ8˜8Kšœœ˜%—K˜—K˜K˜—š žœœBœœœ ˜}šœG™GKšœI™IKšœ1™1—Kšœ œœ.œ˜Gšœ˜šœ˜šœ ™ Kšœ™Kšœœ™4KšœI™IKšœ5™5K™—šœ˜Kšœ˜Kšœœ˜0KšœE˜EKšœ5˜5K˜—˜KšœD™DKšœœ˜3Kšœ˜Kšœ˜Kšœ˜Kšœ œœ.œ˜Gšœ˜Kšœ˜šœœœ˜6šœœ˜Kšœl™lKšœd˜dKšœœ˜—K˜—šœœœœ˜;šœœ˜Kšœl™lKšœd˜dKšœœ˜—K˜—Kšœœ˜ Kšœ8˜8K˜—K˜—˜ Kšœ œœ.œ˜Gšœ˜Kšœ˜Kšœœ˜3Kšœ.˜.šœœ˜#Kšœl™lKšœd˜dKšœœ˜—Kšœ9˜9K˜—K˜—Kšœ1œ˜=—K˜—šœL˜LKšœM˜MKšœ&˜&Kšœ˜—K˜—K™K™ K˜šžœœ(œ œœœœ˜|Kšœ$˜$K˜-Kšœ˜Kšœ˜K˜Kš¡™Kšœ+¡œ˜3šœlœ œ˜„Jšœ,Ÿ˜?JšœB˜BJšœ œ˜&Jšœž œ˜?Jšœ˜—š˜Jšœ=˜=šœwœ œ˜Jšœ˜šœœœ˜Kšœœ1œœ˜TJš¡5™5šœ3œ˜;Jšœ)˜)Jšœ6Ÿ˜MJšœ œ˜(Jšœ'˜'JšÐbkœ˜J˜—Jš¡*™*šœ˜Jšœ2˜2J–f[traj: GGModelTypes.Traj, lineEnds: Imager.StrokeEnd _ square, fillColor: ImagerColorDefs.Color]šœS˜SJšœ:˜:Jšœ œ˜(Jšœ'˜'J˜—J˜—šœhœ œ˜Kšœœ)œœ˜LJšœ*˜*J–f[traj: GGModelTypes.Traj, lineEnds: Imager.StrokeEnd _ square, fillColor: ImagerColorDefs.Color]šœS˜SJšœ2˜2Jšœ œ˜(Jšœ'˜'Jšœ˜—Jšœ˜—Jš¡™Jšœ.˜.Jš¡$™$š œ œœ#œ œ˜QJšœ˜Jšœ-˜-Jšœ;œ ˜GJšœ.˜.Jšœ˜—š˜šœ˜KšœJ˜JKšœ˜šœ$˜$K™—Kšœœœ˜J˜——Jšœ˜—˜J˜——šžœœ˜&Kšœ÷™÷Kšœ œœ˜Kšœ˜K˜KšœŸ0˜HJšœCŸ#˜fJšœœ œœ˜Jšœ2˜2–Î[gargoyleData: GGInterfaceTypes.GargoyleData, opName: ROPE, bagType: ATOM, worldPt: Lines2dTypes.Point, startBox: GGBasicTypes.BoundBox _ NIL, saveState: BOOL _ TRUE, needAnchor: BOOL _ FALSE]šœcœœœ˜‘Kšœ4™4—Kš œœ œœœ˜"Kšœ œ˜!J˜K˜—K™K™ K™šžœœC˜bKšœP˜Pšœrœœ˜ˆKšœ0˜0Kšœ˜—K˜K˜—šžœœœ œ'œœ œœ˜”K™AKšœ˜š œœ œœŸ˜HK˜—Kšœ3˜3Kšœ8˜8Kš¡ ™ Kšœ;œ˜@Kš¡ ™ Kšœ8˜8Kš¡™Kšœ6˜6K–<[bBox: GGBasicTypes.BoundBox, by: GGBasicTypes.BoundBox]šœ7˜7Kšœ+˜+Kšœ)˜)K™!Kš¡™Kšœ,˜,K˜K˜—šž œœœ œœœœœœœœ œœ˜ÅKšœ˜Kšœœ˜K˜Kšœ,˜,Kšœ œŸ0˜Zš˜Kš œœ œœŸ˜HKšœ-œœ˜FKš œ œœœœ ˜GK˜Kšœ3˜3šœ8˜8Kš¡ ™ —šœ#Ÿ/˜RKš¡ ™ —šœ8˜8Kš¡™—Kšœ6˜6Kšœ+˜+Kšœœœ8˜Pšœ4˜4Kš¡ ™ —Kš œœQ¡œ œ œœ˜§Kš œQ¡œ œ œœ˜•Kšœ+˜+š˜˜Kšœ\˜\Kšœ ˜ Kšœœ˜Kšœ+˜+K˜—˜ KšœV˜VKšœ ˜ Kšœœ˜Kšœ+˜+K˜——Kšœ˜—Kšœ˜K˜—šž œœ˜Kšœœ/˜AKš œœœœœ˜'Kšœ œ˜!K˜K˜—šž œœ˜!Kšœœ1œœ˜OKš œœœœœ˜'Kšœ œ˜#K˜K˜—šž œœ˜ Kšœ˜Kšœ˜Kšœœ0œœ˜NKš œœœœœ˜'Kšœ.˜.KšœD˜Dšœœ˜%Kšœ`˜`Kšœ ˜ Kšœœ˜K˜—Kšœ œ˜"K˜K˜—šž œœ˜#Kšœ ˜Kšœ œ ˜Kšœ˜Kšœœ˜Kšœœ4œœ˜RKš œœœœœ˜'Kšœ%˜%Kšœ˜Kšœ1˜1Kšœ;˜;šœœœ˜%Kšœb˜bKšœ ˜ Kšœœ˜K˜—Kšœœ˜%K˜K˜—K™šž œœ/œœ˜dKšœ˜Kšœ œœ˜K˜Kšœ.¡œ^œ˜•Kšœ>˜>K˜K˜K˜—šž œœ˜ K˜Kšœ˜K˜Kšœ+˜+Kšœ6˜6KšœB˜BKšœ-¡ œ)˜_Kšœiœ œœ˜˜Kšœ*˜*KšœŸ˜K˜—šž œœ˜"K˜"Kšœ˜Kšœ œ˜Kšœ5˜5Kšœ6˜6KšœD˜DKšœ1˜1KšœF˜FKšœ$¡œ˜KKšœiœ œœ˜˜K˜K˜—šž œœ˜!Kšœ œ ˜K˜"Kšœ˜Kšœœ˜ Kšœ5˜5Kšœ5˜5KšœD˜DKšœ1˜1Kš œ&œ&œœŸ˜tKšœK˜KKšœ$¡œ˜HKšœiœ œœ˜˜K˜K˜—šžœœ˜$Kšœ ˜Kšœ œ ˜Kšœœœ˜Kšœœ˜Kšœ˜Kšœ2˜2Kšœ ˜ KšœJ˜JKšœA˜AKšœ˜KšœJ˜JKš œœœœŸ˜JKšœ3˜3Kšœiœ œœ˜˜K˜K˜—šž œœ˜!K™gKšœ,˜,K˜Kšœ?˜?Kšœ*˜*Kšœ˜šœ)˜)K™B—K˜Kšœ+˜+K–h[input: LIST OF REF ANY, gargoyleData: GGInterfaceTypes.GargoyleData, worldPt: GGBasicTypes.Point]šœœ!˜8K˜K˜—šž œœ˜K™oKšœ*˜*Kšœ?˜?Kšœ*˜*Kšœ˜Kšœ)˜)Kšœoœ œœ˜žKšœ)˜)K˜—K˜K™K˜šžœœœMœ™ˆKšœœœ™Kšœ œ™Kšœ ™ Kšœœœœ=™iKšœ œ™KšœZ™ZKš œœ œœœM™zK™K™—šžœœœHœ˜Kšœ œ˜Kšœ ˜ KšœT˜TKš œœ œœœM˜zK˜K˜—šžœœEœD˜§K˜Kšœ œ˜Kšœ˜Kšœ˜K˜Kšœ œ˜K˜Kšœ%˜%Kšœ1œ˜6Kš¡™šœœ˜%KšœA˜AKšœ˜šœ œ ˜Kšœ˜Kšœ˜Kšœœ˜—Kšœ)œ œœ˜^Kšœ'˜'Kšœ:˜:Kšœœ œœ˜Kšœ˜K˜—Kš¡.™.šœ˜Kšœ œ˜Kšœ ˜ Kšœ(˜(Kšœ9˜9K˜(Kšœ:˜:Kšœœ œœ˜KšœB˜BKšœ*˜*K˜—K˜K˜—šžœœ:œ"˜Kšœ œ˜šœ œ ˜K˜Kšœ˜Kšœœ˜—šœ œœ˜Kš¡<™Kšœ:˜:K˜—šœ˜Kšœ)˜)Kšœ3˜3K˜—Kšœ$˜$Kšœ2˜2K˜K˜—šžœœM˜dKšœ˜Kšœ%œ˜*KšœA˜AKšœ˜K˜K˜—K˜šžœœ˜K™rKšœ œ˜Kšœ˜Kšœ˜Kšœ˜Kšœ ˜ Kšœ˜K˜K˜K˜Kšœ)˜)K˜Kšœ œœ˜4Kšœ˜K˜Kšœd˜dKš œ œ œœœœ"˜MKšœO˜OKšœ9˜9šœG˜GK™B—K˜šœ œ˜Kšœ(˜(Kšœn˜nK˜—šœ˜–Î[gargoyleData: GGInterfaceTypes.GargoyleData, opName: ROPE, bagType: ATOM, worldPt: Lines2dTypes.Point, startBox: GGBasicTypes.BoundBox _ NIL, saveState: BOOL _ TRUE, needAnchor: BOOL _ FALSE]šœbœœœ˜Kšœœu™‰K˜——Kš œœ œœœ˜"Kšœ œ˜!K˜Kšœ(˜(KšœŸ˜K˜—šžœœ#œ˜\K˜ Kšœ˜Kšœ˜Kšœœœ˜Kšœœœ˜Kšœœœ˜Kšœœœ˜Kš œ œœ œ œ ˜?Kš œ œœ œ œ ˜?Kšœ4˜4Kšœ_˜_Kšœ;œ ˜GKšœQ˜Qšœ1˜1K™—K˜K˜—šžœœ˜Kšœ˜Kšœ˜Kšœ)˜)Kšœ*˜*KšœŸ0˜HKš œœ œœŸ˜HK˜3K–][gargoyleData: GGInterfaceTypes.GargoyleData, selectClass: GGSegmentTypes.SelectionClass]˜+–œ[slice: GGModelTypes.Slice, parts: GGModelTypes.SliceParts, gargoyleData: GGInterfaceTypes.GargoyleData, selectClass: GGSegmentTypes.SelectionClass]˜OKš¡™—šœ*Ÿ&˜PK™A—Kšœ!˜!Kšœ8˜8KšœF˜FKšœcœ œœ˜’Kšœ œ˜!Kšœ(˜(Kšœ˜K˜—šžœœ˜(Kš¡[™[Kšœ˜Kšœ˜Kšœ)˜)Kšœ*˜*KšœŸ0˜HKš œœ œœŸ˜HK˜3Kšœ.œ˜3K–][gargoyleData: GGInterfaceTypes.GargoyleData, selectClass: GGSegmentTypes.SelectionClass]˜+–œ[slice: GGModelTypes.Slice, parts: GGModelTypes.SliceParts, gargoyleData: GGInterfaceTypes.GargoyleData, selectClass: GGSegmentTypes.SelectionClass]˜OKš¡™—šœ*Ÿ&˜PK™A—Kšœ!˜!Kšœ8˜8KšœF˜FKšœcœ œœ˜’Kšœ œ˜!Kšœ(˜(Kšœ˜K˜—šžœœ˜K™hKšœœ˜