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šœ�œ˜