DIRECTORY Atom, Basics, GGAlign, GGBasicTypes, GGBoundBox, AtomButtons, GGSlice, GGCaret, GGDescribe, GGMouseEvent, GGMultiGravity, GGEvent, GGError, GGGravity, GGInterfaceTypes, GGModelTypes, GGObjects, GGOutline, GGRefresh, GGSegment, GGSegmentTypes, GGSelect, GGSequence, GGStatistics, GGTouch, GGTraj, GGTransform, GGVector, GGWindow, GList, Imager, ImagerTransformation, InputFocus, Menus, RealFns, Rope; GGMouseEventImplA: CEDAR PROGRAM IMPORTS Atom, Basics, GGAlign, GGBoundBox, AtomButtons, GGSlice, GGCaret, GGDescribe, GGError, GGEvent, GGGravity, GGMouseEvent, GGMultiGravity, GGObjects, GGOutline, GGRefresh, GGSegment, GGSelect, GGSequence, GGStatistics, GGTouch, GGTraj, GGTransform, GGVector, GGWindow, GList, Imager, ImagerTransformation, InputFocus, RealFns, Rope EXPORTS GGMouseEvent = BEGIN AlignmentPoint: TYPE = GGInterfaceTypes.AlignmentPoint; BoundBox: TYPE = GGModelTypes.BoundBox; Caret: TYPE = GGInterfaceTypes.Caret; EntityGenerator: TYPE = GGModelTypes.EntityGenerator; FeatureData: TYPE = GGModelTypes.FeatureData; GargoyleData: TYPE = GGInterfaceTypes.GargoyleData; Joint: TYPE = GGModelTypes.Joint; MouseButton: TYPE = Menus.MouseButton; ObjectBag: TYPE = GGGravity.ObjectBag; 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; TouchGroup: TYPE = GGSegmentTypes.TouchGroup; TouchItem: TYPE = GGSegmentTypes.TouchItem; TouchItemGenerator: TYPE = GGTouch.TouchItemGenerator; Traj: TYPE = GGModelTypes.Traj; TrajEnd: TYPE = GGModelTypes.TrajEnd; TrajGenerator: TYPE = GGObjects.TrajGenerator; TrajPartType: TYPE = GGModelTypes.TrajPartType; TriggerBag: TYPE = GGAlign.TriggerBag; Vector: TYPE = GGBasicTypes.Vector; MouseProc: TYPE = GGMouseEvent.MouseProc; StartProc: TYPE = GGMouseEvent.StartProc; NotYetImplemented: PUBLIC SIGNAL = CODE; Problem: PUBLIC SIGNAL [msg: Rope.ROPE] = GGError.Problem; EasyAbort: PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { scene: Scene _ gargoyleData.scene; GGObjects.RestoreSelections[scene]; GGCaret.Copy[from: gargoyleData.drag.savedCaret, to: gargoyleData.caret]; --restore original caret FinishAbort[gargoyleData]; }; AbortAdd: PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { GGEvent.DeleteCaretSegment[NIL, gargoyleData]; FinishAbort[gargoyleData]; }; AbortBox: PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { FixupAbortedBox[gargoyleData]; FinishAbort[gargoyleData]; }; FinishAbort: PROC [gargoyleData: GargoyleData] = { GGRefresh.MoveOverlayToBackground[gargoyleData]; GGError.AppendHerald[gargoyleData.feedback, ". . . Aborted.", end]; GGWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireScene, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: FALSE, okToClearFeedback: FALSE]; }; ResetMouseMachinery: PUBLIC PROC [gargoyleData: GargoyleData] = { gargoyleData.mouseMode _ $None; gargoyleData.state _ $None; GGRefresh.MoveOverlayToBackground[gargoyleData]; }; SaveSavedState: PROC [gargoyleData: GargoyleData] = { GGObjects.SaveSelections[gargoyleData.scene]; GGWindow.SaveCaretPos[gargoyleData]; GGCaret.Copy[from: gargoyleData.caret, to: gargoyleData.drag.savedCaret]; }; HandleMouseless: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = { HandleMouse[event, [0.0, 0.0], clientData]; }; HandleMouse: PUBLIC PROC [event: LIST OF REF ANY, point: Point, clientData: REF ANY] = { atom: ATOM; gargoyleData: GargoyleData _ NARROW[clientData]; SELECT gargoyleData.mouseMode FROM $CaretPos => HandleGuarded[StartCaretPos, DuringCaretPos, EndCaretPos, EasyAbort, NIL, event, gargoyleData, point]; $Add => HandleGuarded[StartAdd, DuringAdd, EndAdd, AbortAdd, ContinueAdd, event, gargoyleData, point]; $Box => HandleGuarded[StartBox, DuringAdd, EndBox, AbortBox, NIL, event, gargoyleData, point]; $Drag => HandleGuarded[StartDrag, DuringDrag, EndMotion, EasyAbort, NIL, event, gargoyleData, point]; $CopyAndDrag => HandleGuarded[CopySelected, DuringDrag, EndMotion, EasyAbort, NIL, event, gargoyleData, point]; $Rotate => HandleGuarded[StartRotate, DuringRotate, EndMotion, EasyAbort, NIL, event, gargoyleData, point]; $Scale => HandleGuarded[StartScale, DuringScale, EndMotion, EasyAbort, NIL, event, gargoyleData, point]; $SixPoint => HandleGuarded[StartSixPoint, DuringSixPoint, EndMotion, EasyAbort, NIL, event, gargoyleData, point]; $SelectWithBox => HandleUnGuarded[StartBox, DuringAdd, EndSelectWithBox, AbortBox, event, gargoyleData, point]; $SelectJoint => HandleUnGuarded[StartSelectJoint, DuringSelect, EndSelect, EasyAbort, event, gargoyleData, point]; $ExtSelectJoint => HandleGuarded[GGMouseEvent.StartExtendSelectJoint, GGMouseEvent.DuringExtendSelection, GGMouseEvent.EndExtendSelection, EasyAbort, NIL, event, gargoyleData, point]; $SelectSegment => HandleUnGuarded[StartSelectSegment, DuringSelect, EndSelect, EasyAbort, event, gargoyleData, point]; $ExtSelectSegment => HandleGuarded[GGMouseEvent.StartExtendSelectSegment, GGMouseEvent.DuringExtendSelection, GGMouseEvent.EndExtendSelection, EasyAbort, NIL, event, gargoyleData, point]; $SelectTrajectory => HandleUnGuarded[StartSelectTrajectory, DuringSelect, EndSelect, EasyAbort, event, gargoyleData, point]; $ExtSelectTrajectory => HandleGuarded[GGMouseEvent.StartExtendSelectTraj, GGMouseEvent.DuringExtendSelection, GGMouseEvent.EndExtendSelection, EasyAbort, NIL, event, gargoyleData, point]; $SelectTopLevel => HandleUnGuarded[StartSelectTopLevel, DuringSelect, EndSelect, EasyAbort, event, gargoyleData, point]; $ExtSelectTopLevel => HandleGuarded[GGMouseEvent.StartExtendSelectTopLevel, GGMouseEvent.DuringExtendSelection, GGMouseEvent.EndExtendSelection, EasyAbort, NIL, event, gargoyleData, point]; $Extend => HandleUnGuarded[GGMouseEvent.StartExtendSelection, GGMouseEvent.DuringExtendSelection, GGMouseEvent.EndExtendSelection, EasyAbort, event, gargoyleData, point]; $DeselectJoint => HandleGuarded[GGMouseEvent.StartDeselectJoint, GGMouseEvent.DuringDeselect, GGMouseEvent.EndDeselect, EasyAbort, NIL, event, gargoyleData, point]; $DeselectSegment => HandleGuarded[GGMouseEvent.StartDeselectSegment, GGMouseEvent.DuringDeselect, GGMouseEvent.EndDeselect, EasyAbort, NIL, event, gargoyleData, point]; $DeselectTrajectory => HandleGuarded[GGMouseEvent.StartDeselectTrajectory, GGMouseEvent.DuringDeselect, GGMouseEvent.EndDeselect, EasyAbort, NIL, event, gargoyleData, point]; $DeselectTopLevel => HandleGuarded[GGMouseEvent.StartDeselectTopLevel, GGMouseEvent.DuringDeselect, GGMouseEvent.EndDeselect, EasyAbort, NIL, event, gargoyleData, point]; $None => { atom _ NARROW[event.first]; SELECT atom FROM $StartCaretPos => { gargoyleData.mouseMode _ $CaretPos; HandleMouse[event, point, gargoyleData]; }; $StartAdd => { gargoyleData.mouseMode _ $Add; HandleMouse[event, point, gargoyleData]; }; $StartBox => { gargoyleData.mouseMode _ $Box; HandleMouse[event, point, gargoyleData]; }; $StartSelectWithBox => { gargoyleData.mouseMode _ $SelectWithBox; HandleMouse[event, point, gargoyleData]; }; $StartDrag => { gargoyleData.mouseMode _ $Drag; HandleMouse[event, point, gargoyleData]; }; $StartCopyAndDrag => { gargoyleData.mouseMode _ $CopyAndDrag; HandleMouse[event, point, gargoyleData]; }; $StartRotate => { gargoyleData.mouseMode _ $Rotate; HandleMouse[event, point, gargoyleData]; }; $StartScale => { gargoyleData.mouseMode _ $Scale; HandleMouse[event, point, gargoyleData]; }; $StartSixPoint => { gargoyleData.mouseMode _ $SixPoint; HandleMouse[event, point, gargoyleData]; }; $StartSelectJoint => { gargoyleData.mouseMode _ $SelectJoint; HandleMouse[event, point, gargoyleData]; }; $StartExtSelectJoint => { gargoyleData.mouseMode _ $ExtSelectJoint; HandleMouse[event, point, gargoyleData]; }; $StartSelectSegment => { gargoyleData.mouseMode _ $SelectSegment; HandleMouse[event, point, gargoyleData]; }; $StartExtSelectSegment => { gargoyleData.mouseMode _ $ExtSelectSegment; HandleMouse[event, point, gargoyleData]; }; $StartSelectTrajectory => { gargoyleData.mouseMode _ $SelectTrajectory; HandleMouse[event, point, gargoyleData]; }; $StartExtSelectTrajectory => { gargoyleData.mouseMode _ $ExtSelectTrajectory; HandleMouse[event, point, gargoyleData]; }; $StartSelectTopLevel => { gargoyleData.mouseMode _ $SelectTopLevel; HandleMouse[event, point, gargoyleData]; }; $StartExtSelectTopLevel => { gargoyleData.mouseMode _ $ExtSelectTopLevel; HandleMouse[event, point, gargoyleData]; }; $StartExtendSelection => { gargoyleData.mouseMode _ $Extend; HandleMouse[event, point, gargoyleData]; }; $StartDeselectJoint => { gargoyleData.mouseMode _ $DeselectJoint; HandleMouse[event, point, gargoyleData]; }; $StartDeselectSegment => { gargoyleData.mouseMode _ $DeselectSegment; HandleMouse[event, point, gargoyleData]; }; $StartDeselectTrajectory => { gargoyleData.mouseMode _ $DeselectTrajectory; HandleMouse[event, point, gargoyleData]; }; $StartDeselectTopLevel => { gargoyleData.mouseMode _ $DeselectTopLevel; HandleMouse[event, point, gargoyleData]; }; 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, gargoyleData: GargoyleData, worldPt: Point] = { genericAction, atom: ATOM; atomName: Rope.ROPE; GGStatistics.StartInterval[$HandleGuarded, GGStatistics.GlobalTable[]]; 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, gargoyleData, worldPt]; GGStatistics.StopInterval[$HandleGuarded, GGStatistics.GlobalTable[]]; }; Restart: PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData] RETURNS [BOOL] = { mouseMode: ATOM _ gargoyleData.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, gargoyleData: GargoyleData, worldPt: Point] = { SELECT gargoyleData.state FROM $None => { SELECT genericAction FROM $Start => { gargoyleData.drag.currentPoint _ worldPt; [] _ InputFocus.SetInputFocus[gargoyleData.actionArea]; IF startProc[input, gargoyleData, worldPt] THEN gargoyleData.state _ $Main ELSE {abortProc[input, gargoyleData, worldPt]; gargoyleData.state _ $Aborted;}; }; ENDCASE; }; $Main => { SELECT genericAction FROM $During => { duringProc[input, gargoyleData, worldPt]; gargoyleData.drag.currentPoint _ worldPt; }; $Abort => {abortProc[input, gargoyleData, worldPt]; gargoyleData.state _ $Aborted}; $GuardUp => gargoyleData.state _ $GuardUp; $MouseUp => gargoyleData.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, gargoyleData, worldPt]; gargoyleData.state _ $None; gargoyleData.mouseMode _ $None; HandleMouse[input, worldPt, gargoyleData]; }; ENDCASE; }; $GuardUp => { SELECT genericAction FROM $AllUp => { endProc[input, gargoyleData, gargoyleData.drag.currentPoint]; gargoyleData.mouseMode _ $None; gargoyleData.state _ $None; }; $Abort => {abortProc[input, gargoyleData, worldPt]; gargoyleData.state _ $Aborted}; ENDCASE; }; $MouseUp => { SELECT genericAction FROM $AllUp => { endProc[input, gargoyleData, gargoyleData.drag.currentPoint]; gargoyleData.mouseMode _ $None; gargoyleData.state _ $None; }; $Abort => {abortProc[input, gargoyleData, worldPt]; gargoyleData.state _ $Aborted}; $Start => { -- we may be starting another action of this mode or some other mode. IF Restart[input, gargoyleData] AND continueProc # NIL THEN { IF continueProc[input, gargoyleData, worldPt] THEN { gargoyleData.state _ $Main; gargoyleData.drag.currentPoint _ worldPt; } ELSE {abortProc[input, gargoyleData, worldPt]; gargoyleData.state _ $Aborted;}; } ELSE { gargoyleData.mouseMode _ $None; endProc[input, gargoyleData, gargoyleData.drag.currentPoint]; gargoyleData.state _ $None; HandleMouse[input, worldPt, gargoyleData]; }; }; ENDCASE; }; $Aborted => { SELECT genericAction FROM $AllUp => {gargoyleData.state _ $None; gargoyleData.mouseMode _ $None}; ENDCASE; }; ENDCASE => SIGNAL Problem[msg: "Unknown generic state"]; }; HandleUnGuarded: PROC [startProc: StartProc, duringProc, endProc, abortProc: MouseProc, input: LIST OF REF ANY, gargoyleData: GargoyleData, 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, gargoyleData, worldPt]; }; HandleUnGuardedAux: PROC [startProc: StartProc, duringProc, endProc, abortProc: MouseProc, genericAction: ATOM, input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point] = { SELECT gargoyleData.state FROM $None => { SELECT genericAction FROM $Start => { [] _ InputFocus.SetInputFocus[gargoyleData.actionArea]; IF startProc[input, gargoyleData, worldPt] THEN gargoyleData.state _ $Main ELSE {abortProc[input, gargoyleData, worldPt]; gargoyleData.state _ $Aborted} }; ENDCASE; }; $Main => { SELECT genericAction FROM $During => duringProc[input, gargoyleData, worldPt]; $MouseUp, $AllUp => { endProc[input, gargoyleData, worldPt]; gargoyleData.state _ $None; gargoyleData.mouseMode _ $None; }; $Abort => {abortProc[input, gargoyleData, worldPt]; gargoyleData.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, gargoyleData, worldPt]; gargoyleData.state _ $None; gargoyleData.mouseMode _ $None; HandleMouse[input, worldPt, gargoyleData]; }; ENDCASE; }; $Aborted => { SELECT genericAction FROM $AllUp => {gargoyleData.state _ $None; gargoyleData.mouseMode _ $None}; $MouseUp => {gargoyleData.state _ $None; gargoyleData.mouseMode _ $None}; ENDCASE; }; ENDCASE => SIGNAL Problem[msg: "Unknown generic state"]; }; SortNewEntities: PROC [entityList: LIST OF REF ANY] RETURNS [sorted: LIST OF REF ANY] = { CompareProc: GList.CompareProc = { priority1, priority2: INT; WITH ref1 SELECT FROM slice: Slice => priority1 _ slice.priority; outline: Outline => priority1 _ outline.priority; ENDCASE; WITH ref2 SELECT FROM slice: Slice => priority2 _ slice.priority; outline: Outline => priority2 _ outline.priority; ENDCASE; RETURN[Basics.CompareINT[priority1, priority2]]; }; sorted _ NARROW[GList.Sort[entityList, CompareProc]]; }; ComputePriorities: PROC [scene: Scene] = { count: INT _ 0; entityGen: EntityGenerator _ GGObjects.TopLevelEntitiesInScene[scene]; FOR entity: REF ANY _ GGObjects.NextEntity[entityGen], GGObjects.NextEntity[entityGen] UNTIL entity = NIL DO WITH entity SELECT FROM slice: Slice => slice.priority _ count; outline: Outline => outline.priority _ count; ENDCASE => ERROR; count _ count + 1; ENDLOOP; }; CopySelected: PUBLIC StartProc = { newTraj: Traj; newSlice: Slice; entityList: LIST OF REF ANY; allParts: SliceParts; newOutline, outline: Outline; outSeqGen: GGSelect.OutlineSequenceGenerator; sliceDGen: SliceDescriptorGenerator; scene: Scene _ gargoyleData.scene; SaveSavedState[gargoyleData]; -- must do this before any possible aborts occur ComputePriorities[gargoyleData.scene]; 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]; entityList _ CONS[newSlice, entityList]; newSlice.priority _ sliceD.slice.priority; ENDLOOP; 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 GGSequence.NextSegment[GGSequence.SegmentsInSequence[outSeq.fenceSeq]]=NIL THEN { GGError.AppendHerald[gargoyleData.feedback, ". . . Cannot Copy Joints or CPs", oneLiner]; GGError.Blink[gargoyleData.feedback]; RETURN[FALSE]; }; IF GGSelect.IsSelectedInFull[outline, scene, normal] THEN { newOutline _ outline.class.copy[outline]; GGSelect.DeselectEntireOutline[outline, scene, normal]; -- deselect the old one entityList _ CONS[newOutline, entityList]; newOutline.priority _ outline.priority; LOOP; } ELSE { newTraj _ GGTraj.CopyTrajFromRun[outSeq.fenceSeq]; newOutline _ GGOutline.CreateOutline[newTraj, outline.lineEnds, outline.fillColor]; GGSelect.DeselectSequence[outSeq.fenceSeq, scene, normal]; entityList _ CONS[newOutline, entityList]; newOutline.priority _ outline.priority; }; }; FOR holeSeq: Sequence _ GGSequence.NextSequence[outSeq.holeSeqs], GGSequence.NextSequence[outSeq.holeSeqs] UNTIL holeSeq = NIL DO IF GGSequence.NextSegment[GGSequence.SegmentsInSequence[holeSeq]]=NIL THEN { GGError.AppendHerald[gargoyleData.feedback, ". . . Cannot Copy Joints or CPs", oneLiner]; GGError.Blink[gargoyleData.feedback]; RETURN[FALSE]; }; newTraj _ GGTraj.CopyTrajFromRun[holeSeq]; newOutline _ GGOutline.CreateOutline[newTraj, outline.lineEnds, outline.fillColor]; GGSelect.DeselectSequence[holeSeq, scene, normal]; entityList _ CONS[newOutline, entityList]; newOutline.priority _ outline.priority; ENDLOOP; ENDLOOP; entityList _ SortNewEntities[entityList]; FOR sliceList: LIST OF REF ANY _ entityList, sliceList.rest UNTIL sliceList = NIL DO WITH sliceList.first SELECT FROM newSlice: Slice => { GGObjects.AddSlice[gargoyleData.scene, newSlice, -1]; allParts _ newSlice.class.newParts[newSlice, NIL, slice]; GGSelect.SelectSlice[newSlice, allParts, scene, normal]; }; newOutline: Outline => { GGObjects.AddOutline[gargoyleData.scene, newOutline, -1]; allParts _ newOutline.class.newParts[newOutline, NIL, slice]; GGSelect.SelectOutline[newOutline, allParts, scene, normal]; }; ENDCASE => ERROR; ENDLOOP; [] _ StartDrag[NIL, gargoyleData, worldPt]; }; StartSelectJoint: PUBLIC StartProc = { gargoyleData.drag.selectState _ joint; StartSelectAux[gargoyleData, worldPt]; DuringSelect[NIL, gargoyleData, worldPt]; }; StartSelectSegment: PUBLIC StartProc = { gargoyleData.drag.selectState _ segment; StartSelectAux[gargoyleData, worldPt]; DuringSelect[NIL, gargoyleData, worldPt]; }; StartSelectTrajectory: PUBLIC StartProc = { gargoyleData.drag.selectState _ traj; StartSelectAux[gargoyleData, worldPt]; DuringSelect[NIL, gargoyleData, worldPt]; }; StartSelectTopLevel: PUBLIC StartProc = { gargoyleData.drag.selectState _ topLevel; StartSelectAux[gargoyleData, worldPt]; DuringSelect[NIL, gargoyleData, worldPt]; }; StartSelectAux: PROC [gargoyleData: GargoyleData, worldPt: Point] = { IF NOT GGRefresh.EmptyOverlay[gargoyleData] THEN ERROR; SaveSavedState[gargoyleData]; }; DuringSelect: PUBLIC MouseProc = { resultPoint: Point; feature: FeatureData; IF gargoyleData.drag.selectState = joint THEN [resultPoint, feature] _ GGMultiGravity.InnerCircle[worldPt, gargoyleData.hitTest.criticalR, gargoyleData.hitTest.innerR, GGGravity.emptyObjectBag, gargoyleData.hitTest.sceneTriggerBag, gargoyleData, FALSE] ELSE [resultPoint, feature] _ GGMultiGravity.StrictDistance[worldPt, gargoyleData.hitTest.criticalR, GGGravity.emptyObjectBag, gargoyleData.hitTest.sceneTriggerBag, gargoyleData]; GGMouseEvent.SetCaretAttractorEndpoint[gargoyleData, resultPoint, feature]; GGMouseEvent.DescribeSelectionAction[gargoyleData, feature, gargoyleData.drag.selectState, "Selecting"]; GGSelect.DeselectAll[gargoyleData.scene, normal]; IF feature = NIL THEN { -- no near trajectories, caret in free space } ELSE { SELECT gargoyleData.drag.selectState FROM joint => SelectJointOrCP[feature: feature, caretPt: resultPoint, gargoyleData: gargoyleData]; segment => SelectSegment[feature: feature, caretPt: resultPoint, gargoyleData: gargoyleData]; traj => SelectTraj[feature: feature, caretPt: resultPoint, gargoyleData: gargoyleData]; topLevel => SelectTopLevel[feature: feature, caretPt: resultPoint, gargoyleData: gargoyleData]; ENDCASE => ERROR; }; GGWindow.RestoreScreenAndInvariants[paintAction: $DuringSelect, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; }; -- end DuringSelect SelectJointOrCP: PUBLIC PROC [feature: FeatureData, caretPt: Point, gargoyleData: GargoyleData] = { [----] _ SelectJointOrCPInternal[feature, caretPt, gargoyleData]; }; SelectJointOrCPInternal: PUBLIC PROC [feature: FeatureData, caretPt: Point, gargoyleData: GargoyleData] RETURNS [parts: SliceParts _ NIL]= { SELECT feature.resultType FROM slice => { sliceD: SliceDescriptor _ NARROW[feature.shape]; parts _ sliceD.slice.class.newParts[sliceD.slice, feature.hitPart, joint]; GGSelect.SelectSlice[sliceD.slice, parts, gargoyleData.scene, normal]; }; outline => { outlineD: OutlineDescriptor _ NARROW[feature.shape]; parts _ outlineD.slice.class.newParts[outlineD.slice, feature.hitPart, joint]; GGSelect.SelectOutline[outlineD.slice, parts, gargoyleData.scene, normal]; }; midpoint => NULL; -- temporarily ignore midpoints in bags. KAP. June 26, 1986 slopeLine, angleLine, distanceLine, intersectionPoint, radiiCircle, midpoint => ERROR; ENDCASE => ERROR NotYetImplemented; }; -- end SelectJointOrCPInternal SelectSegment: PUBLIC PROC [feature: FeatureData, caretPt: Point, gargoyleData: GargoyleData] = { [----] _ SelectSegmentInternal[feature, caretPt, gargoyleData]; }; SelectSegmentInternal: PROC [feature: FeatureData, caretPt: Point, gargoyleData: GargoyleData] RETURNS [parts: SliceParts _ NIL] = { SELECT feature.resultType FROM outline => { outline: Outline _ NARROW[feature.shape, OutlineDescriptor].slice; parts _ outline.class.newParts[outline, feature.hitPart, segment]; GGSelect.SelectOutline[outline, parts, gargoyleData.scene, normal]; }; slice => { slice: Slice _ NARROW[feature.shape, SliceDescriptor].slice; parts _ slice.class.newParts[slice, feature.hitPart, segment]; GGSelect.SelectSlice[slice, parts, gargoyleData.scene, normal]; }; midpoint => NULL; -- temporarily ignore midpoints in bags. KAP. June 26, 1986 slopeLine, angleLine, distanceLine, intersectionPoint, radiiCircle, midpoint => ERROR; ENDCASE => ERROR NotYetImplemented; }; -- end of SelectSegment SelectTraj: PUBLIC PROC [feature: FeatureData, caretPt: Point, gargoyleData: GargoyleData] = { [] _ SelectTrajInternal[feature, caretPt, gargoyleData]; }; SelectTrajInternal: PROC [feature: FeatureData, caretPt: Point, gargoyleData: GargoyleData] RETURNS [parts: SliceParts _ NIL] = { SELECT feature.type FROM outline => { outline: Outline _ NARROW[feature.shape, OutlineDescriptor].slice; parts _ outline.class.newParts[outline, feature.hitPart, traj]; GGSelect.SelectOutline[outline, parts, gargoyleData.scene, normal]; }; slice => { slice: Slice _ NARROW[feature.shape, SliceDescriptor].slice; parts _ slice.class.newParts[slice, feature.hitPart, traj]; GGSelect.SelectSlice[slice, parts, gargoyleData.scene, normal]; }; ENDCASE => SIGNAL Problem[msg: "Unexpected feature type"]; }; -- end SelectTraj SelectTopLevel: PUBLIC PROC [feature: FeatureData, caretPt: Point, gargoyleData: GargoyleData] = { [] _ SelectTopLevelInternal[feature, caretPt, gargoyleData]; }; SelectTopLevelInternal: PROC [feature: FeatureData, caretPt: Point, gargoyleData: GargoyleData] RETURNS [parts: SliceParts _ NIL] = { SELECT feature.resultType FROM outline => { outline: Outline _ NARROW[feature.shape, OutlineDescriptor].slice; parts _ outline.class.newParts[outline, feature.hitPart, topLevel]; GGSelect.SelectOutline[outline, parts, gargoyleData.scene, normal]; }; slice => { slice: Slice _ NARROW[feature.shape, SliceDescriptor].slice; parts _ slice.class.newParts[slice, feature.hitPart, topLevel]; GGSelect.SelectSlice[slice, parts, gargoyleData.scene, normal]; }; midpoint => NULL; -- temporarily ignore midpoints in bags. KAP. June 26, 1986 slopeLine, angleLine, distanceLine, intersectionPoint, radiiCircle, midpoint => ERROR; ENDCASE => ERROR NotYetImplemented; }; EndSelect: PUBLIC MouseProc = { resultPoint: Point; feature: FeatureData; IF gargoyleData.drag.selectState = joint THEN [resultPoint, feature] _ GGMultiGravity.InnerCircle[worldPt, gargoyleData.hitTest.criticalR, gargoyleData.hitTest.innerR, GGGravity.emptyObjectBag, gargoyleData.hitTest.sceneTriggerBag, gargoyleData, FALSE] ELSE [resultPoint, feature] _ GGMultiGravity.StrictDistance[worldPt, gargoyleData.hitTest.criticalR, GGGravity.emptyObjectBag, gargoyleData.hitTest.sceneTriggerBag, gargoyleData]; GGMouseEvent.SetCaretAttractorEndpoint[gargoyleData, resultPoint, feature]; GGWindow.NewCaretPos[gargoyleData]; GGSelect.DeselectAll[gargoyleData.scene, normal]; SELECT gargoyleData.drag.selectState FROM joint => EndSelectJoint[gargoyleData, resultPoint, feature]; segment => EndSelectSegment[gargoyleData, resultPoint, feature]; traj => EndSelectTrajectory[gargoyleData, resultPoint, feature]; topLevel => EndSelectTopLevel[gargoyleData, resultPoint, feature]; ENDCASE => ERROR; gargoyleData.drag.selectState _ none; -- added to help DescribeFeature work. KAP. }; -- end EndSelect EndSelectJoint: PROC [gargoyleData: GargoyleData, resultPoint: Point, feature: FeatureData] = { IF feature = NIL THEN { -- no preparation needed GGCaret.SitOn[gargoyleData.caret, NIL]; GGError.AppendHerald[gargoyleData.feedback, "No near joint found.", oneLiner]; } ELSE { gargoyleData.drag.extendMode _ joint; SELECT feature.resultType FROM outline => { outlineD: OutlineDescriptor _ NEW[OutlineDescriptorObj _ [slice: NARROW[feature.shape, OutlineDescriptor].slice] ]; outlineD.parts _ SelectJointOrCPInternal[feature, resultPoint, gargoyleData]; GGCaret.SitOn[gargoyleData.caret, outlineD]; GGError.AppendHerald[gargoyleData.feedback, Rope.Concat[outlineD.slice.class.describe[outlineD.slice, outlineD.parts], " selected"], oneLiner]; gargoyleData.drag.outlineToExtend _ outlineD; }; slice => { sliceD: SliceDescriptor _ NEW[SliceDescriptorObj _ [slice: NARROW[feature.shape, SliceDescriptor].slice] ]; sliceD.parts _ SelectJointOrCPInternal[feature, resultPoint, gargoyleData]; GGCaret.SitOn[gargoyleData.caret, sliceD]; GGError.AppendHerald[gargoyleData.feedback, Rope.Concat[sliceD.slice.class.describe[sliceD.slice, sliceD.parts], " selected"], oneLiner]; gargoyleData.drag.sliceToExtend _ sliceD; }; ENDCASE => ERROR Problem[msg: "Unexpected feature type"]; }; GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; }; -- end EndSelectJoint EndSelectSegment: PROC [gargoyleData: GargoyleData, resultPoint: Point, feature: FeatureData] = { IF feature = NIL THEN { GGCaret.SitOn[gargoyleData.caret, NIL]; GGError.AppendHerald[gargoyleData.feedback, "No near segment found.", oneLiner]; } ELSE { gargoyleData.drag.extendMode _ segmentRange; SELECT feature.resultType FROM outline => { jointParts: SliceParts; jointSeq: Sequence; jointD: OutlineDescriptor; traj: Traj; jointNum: NAT; outlineD: OutlineDescriptor _ NEW[OutlineDescriptorObj _ [slice: NARROW[feature.shape, OutlineDescriptor].slice]]; outlineD.parts _ SelectSegmentInternal[feature, resultPoint, gargoyleData]; IF outlineD.slice.class.type = $Outline THEN { [jointNum, traj] _ GGOutline.NearestJointToHitData[feature.hitPart]; jointSeq _ GGSequence.CreateFromJoint[traj, jointNum]; jointParts _ GGOutline.PartsFromSequence[outlineD.slice, jointSeq]; jointD _ NEW[OutlineDescriptorObj _ [outlineD.slice, jointParts]]; GGCaret.SitOn[gargoyleData.caret, jointD]; } ELSE GGCaret.SitOn[gargoyleData.caret, NIL]; GGError.AppendHerald[gargoyleData.feedback, Rope.Concat[outlineD.slice.class.describe[outlineD.slice, outlineD.parts], " selected"], oneLiner]; gargoyleData.drag.outlineToExtend _ outlineD; }; slice => { sliceD: SliceDescriptor _ NEW[SliceDescriptorObj _ [slice: NARROW[feature.shape, SliceDescriptor].slice]]; sliceD.parts _ SelectSegmentInternal[feature, resultPoint, gargoyleData]; IF sliceD.slice.class.type = $Outline THEN { ERROR Problem[msg: "Outlines must be Slices now."]; } ELSE GGCaret.SitOn[gargoyleData.caret, NIL]; GGError.AppendHerald[gargoyleData.feedback, Rope.Concat[sliceD.slice.class.describe[sliceD.slice, sliceD.parts], " selected"], oneLiner]; gargoyleData.drag.sliceToExtend _ sliceD; }; ENDCASE => SIGNAL Problem["Unexpected feature type"]; }; GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; }; -- end EndSelectSegment EndSelectTrajectory: PROC [gargoyleData: GargoyleData, resultPoint: Point, feature: FeatureData] = { IF feature = NIL THEN { GGCaret.SitOn[gargoyleData.caret, NIL]; GGError.AppendHerald[gargoyleData.feedback, "No near trajectory found.", oneLiner]; } ELSE { parts: SliceParts _ SelectTrajInternal[feature, resultPoint, gargoyleData]; gargoyleData.drag.extendMode _ traj; SELECT feature.type FROM outline => { jointParts: SliceParts; jointSeq: Sequence; jointD: OutlineDescriptor; traj: Traj; jointNum: NAT; outlineD: OutlineDescriptor _ NEW[OutlineDescriptorObj _ [slice: NARROW[feature.shape, OutlineDescriptor].slice, parts: parts]]; IF outlineD.slice.class.type = $Outline THEN { [jointNum, traj] _ GGOutline.NearestJointToHitData[feature.hitPart]; jointSeq _ GGSequence.CreateFromJoint[traj, jointNum]; jointParts _ GGOutline.PartsFromSequence[outlineD.slice, jointSeq]; jointD _ NEW[OutlineDescriptorObj _ [outlineD.slice, jointParts]]; GGCaret.SitOn[gargoyleData.caret, jointD]; } ELSE GGCaret.SitOn[gargoyleData.caret, NIL]; GGError.AppendHerald[gargoyleData.feedback, Rope.Concat[outlineD.slice.class.describe[outlineD.slice, outlineD.parts], " selected"], oneLiner]; gargoyleData.drag.outlineToExtend _ outlineD; }; slice => { sliceD: SliceDescriptor _ NEW[SliceDescriptorObj _ [slice: NARROW[feature.shape, SliceDescriptor].slice, parts: parts] ]; IF sliceD.slice.class.type = $Outline THEN { ERROR Problem[msg: "Outlines must be Slices now."]; } ELSE GGCaret.SitOn[gargoyleData.caret, NIL]; GGError.AppendHerald[gargoyleData.feedback, Rope.Concat[sliceD.slice.class.describe[sliceD.slice, sliceD.parts], " selected"], oneLiner]; gargoyleData.drag.sliceToExtend _ sliceD; }; ENDCASE => SIGNAL Problem[msg: "Unexpected feature type"]; }; GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; }; -- end SelectTrajectory EndSelectTopLevel: PROC [gargoyleData: GargoyleData, resultPoint: Point, feature: FeatureData] = { IF feature = NIL THEN { -- no selection GGCaret.SitOn[gargoyleData.caret, NIL]; GGError.AppendHerald[gargoyleData.feedback, "No near object found.", oneLiner]; } ELSE { parts: SliceParts _ SelectTopLevelInternal[feature, resultPoint, gargoyleData]; gargoyleData.drag.extendMode _ topLevel; SELECT feature.resultType FROM outline => { jointParts: SliceParts; jointSeq: Sequence; jointD: OutlineDescriptor; traj: Traj; jointNum: NAT; outlineD: OutlineDescriptor _ NEW[OutlineDescriptorObj _ [slice: NARROW[feature.shape, OutlineDescriptor].slice, parts: parts]]; IF outlineD.slice.class.type = $Outline THEN { [jointNum, traj] _ GGOutline.NearestJointToHitData[feature.hitPart]; jointSeq _ GGSequence.CreateFromJoint[traj, jointNum]; jointParts _ GGOutline.PartsFromSequence[outlineD.slice, jointSeq]; jointD _ NEW[OutlineDescriptorObj _ [outlineD.slice, jointParts]]; GGCaret.SitOn[gargoyleData.caret, jointD]; } ELSE GGCaret.SitOn[gargoyleData.caret, NIL]; GGError.AppendHerald[gargoyleData.feedback, "Top level outline selected.", oneLiner]; gargoyleData.drag.outlineToExtend _ outlineD; }; slice => { sliceD: SliceDescriptor _ NEW[SliceDescriptorObj _ [slice: NARROW[feature.shape, SliceDescriptor].slice, parts: parts]]; IF sliceD.slice.class.type = $Outline THEN { ERROR Problem[msg: "Outlines must be Slices now."]; } ELSE GGCaret.SitOn[gargoyleData.caret, NIL]; GGError.AppendHerald[gargoyleData.feedback, Rope.Concat[sliceD.slice.class.describe[sliceD.slice, sliceD.parts], " selected"], oneLiner]; gargoyleData.drag.sliceToExtend _ sliceD; }; ENDCASE => ERROR; -- nothing else should be in the object bag }; GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; }; -- end SelectTopLevel TransformObjectsAfterMove: PROC [gargoyleData: GargoyleData] = { entityGen: EntityGenerator; GGTouch.InitializeTouching[gargoyleData]; entityGen _ GGSelect.SelectedStuff[gargoyleData.scene, normal]; FOR entity: REF ANY _ GGObjects.NextEntity[entityGen], GGObjects.NextEntity[entityGen] UNTIL entity = NIL DO WITH entity SELECT FROM outlineD: OutlineDescriptor => { outlineD.slice.class.transform[outlineD.slice, outlineD.parts, gargoyleData.drag.transform]; }; sliceD: SliceDescriptor => { sliceD.slice.class.transform[sliceD.slice, sliceD.parts, gargoyleData.drag.transform]; }; ENDCASE => ERROR NotYetImplemented; ENDLOOP; }; StartCaretPos: PUBLIC StartProc = { GGStatistics.StartInterval[$StartCaretPos, GGStatistics.GlobalTable[]]; StartSelectAux[gargoyleData, worldPt]; DuringCaretPos[NIL, gargoyleData, worldPt]; GGStatistics.StopInterval[$StartCaretPos, GGStatistics.GlobalTable[]]; }; DuringCaretPos: PUBLIC MouseProc = { resultPoint: Point; feature: FeatureData; GGStatistics.StartInterval[$DuringCaretPos, GGStatistics.GlobalTable[]]; [resultPoint, feature] _ GGMultiGravity.Map[worldPt, gargoyleData.hitTest.criticalR, gargoyleData.hitTest.currentObjectBag, gargoyleData.hitTest.sceneTriggerBag, gargoyleData, TRUE]; SetCaretAttractor[gargoyleData, resultPoint, feature]; -- move caret to feature point GGWindow.RestoreScreenAndInvariants[paintAction: $DuringCaretPos, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; -- show caret in new position GGStatistics.StopInterval[$DuringCaretPos, GGStatistics.GlobalTable[]]; }; -- end of DuringCaretPos EndCaretPos: PUBLIC MouseProc = { resultPoint: Point; feature: FeatureData; currentObjects: ObjectBag _ NARROW[gargoyleData.hitTest.currentObjectBag]; sceneObjects: TriggerBag _ NARROW[gargoyleData.hitTest.sceneTriggerBag]; GGStatistics.StartInterval[$EndCaretPos, GGStatistics.GlobalTable[]]; [resultPoint, feature] _ GGMultiGravity.Map[worldPt, gargoyleData.hitTest.criticalR, currentObjects, sceneObjects, gargoyleData, TRUE]; SetCaretAttractor[gargoyleData, resultPoint, feature, "Final"]; -- move caret to feature point GGWindow.NewCaretPos[gargoyleData]; GGCaret.SitOn[gargoyleData.caret, NIL]; -- subsequent Add operations will start a NEW trajectory. GGWindow.RestoreScreenAndInvariants[paintAction: $CaretMoved, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; GGStatistics.StopInterval[$EndCaretPos, GGStatistics.GlobalTable[]]; }; -- 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 [gargoyleData: GargoyleData, mapPoint: Point, feature: FeatureData, action: Rope.ROPE _ ""] = { IF feature = NIL THEN GGCaret.SetAttractor[gargoyleData.caret, mapPoint, NIL] ELSE { SELECT feature.resultType FROM outline => { traj: Traj; hitType: GGModelTypes.TrajPartType; segNum, cpNum, jointNum: INT; seq: Sequence; parts: SliceParts; partsD: OutlineDescriptor; outlineD: OutlineDescriptor _ NARROW[feature.shape]; [traj, hitType, segNum, cpNum, jointNum] _ GGOutline.UnpackHitData[feature.hitPart]; SELECT hitType FROM joint => { seq _ GGSequence.CreateFromJoint[traj, jointNum]; }; controlPoint => { seq _ GGSequence.CreateFromControlPoint[traj, segNum, cpNum]; }; segment => { seq _ GGSequence.CreateSimpleFromSegment[traj, segNum]; }; ENDCASE => ERROR; parts _ GGOutline.PartsFromSequence[outlineD.slice, seq]; partsD _ NEW[OutlineDescriptorObj _ [outlineD.slice, parts]]; GGCaret.SetAttractor[gargoyleData.caret, mapPoint, partsD]; }; slice => { GGCaret.SetAttractor[gargoyleData.caret, mapPoint, feature.shape]; }; ENDCASE => GGCaret.SetAttractor[gargoyleData.caret, mapPoint, NIL]; }; GGError.PutFHerald[gargoyleData.feedback, oneLiner, "%g Caret on %g at [%g, %g]", [rope[action]], [rope[GGDescribe.DescribeFeature[feature, gargoyleData]]], [real[mapPoint.x]], [real[mapPoint.y]] ]; }; StartMotionAux: PROC [gargoyleData: GargoyleData, opName: Rope.ROPE, bagType: ATOM, worldPt: Point, needAnchor: BOOL _ FALSE] RETURNS [BOOL] = { restoreBox: BoundBox; repaintNeeded: BOOL; IF NOT GGRefresh.EmptyOverlay[gargoyleData] THEN ERROR; -- nothing on overlay SaveSavedState[gargoyleData]; -- must do this before any possible aborts occur IF GGSelect.NoSelections[gargoyleData.scene, normal] THEN { GGError.PutFHerald[gargoyleData.feedback, oneLiner, "Select some objects to %g", [rope[opName]]]; GGError.Blink[gargoyleData.feedback]; RETURN[FALSE]; }; IF needAnchor AND NOT GGCaret.Exists[gargoyleData.anchor] THEN { GGError.PutFHerald[gargoyleData.feedback, oneLiner, "Anchor needed to %g", [rope[opName]]]; GGError.Blink[gargoyleData.feedback]; RETURN[FALSE]; }; GGRefresh.MoveAllSelectedToOverlay[gargoyleData, normal]; gargoyleData.drag.startPoint _ GGCaret.GetPoint[gargoyleData.caret]; GGCaret.SetAttractor[gargoyleData.caret, gargoyleData.drag.startPoint, NIL]; -- move caret to start point gargoyleData.drag.transform _ ImagerTransformation.Scale[1.0]; restoreBox _ GGBoundBox.BoundBoxOfMoving[gargoyleData.scene]; gargoyleData.refresh.startBoundBox^ _ restoreBox^; GGRefresh.SplitBackgroundAndOverlay[gargoyleData, restoreBox]; repaintNeeded _ GGAlign.UpdateBagsForAction[gargoyleData, bagType]; IF repaintNeeded THEN GGWindow.RestoreScreenAndInvariants[paintAction: $None, gargoyleData: gargoyleData, remake: bitMap, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE] ELSE GGWindow.RestoreScreenAndInvariants[paintAction: $None, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; RETURN[TRUE]; }; StartDrag: PUBLIC StartProc = { startSuccess: BOOL _ StartMotionAux[gargoyleData, "drag", $Drag, worldPt]; IF NOT startSuccess THEN RETURN[FALSE]; DuringDrag[NIL, gargoyleData, worldPt]; }; StartRotate: PUBLIC StartProc = { startSuccess: BOOL _ StartMotionAux[gargoyleData, "rotate", $Drag, worldPt, TRUE]; IF NOT startSuccess THEN RETURN[FALSE]; DuringRotate[NIL, gargoyleData, worldPt]; }; StartScale: PUBLIC StartProc = { anchorPoint: Point; originalVector: Vector; startSuccess: BOOL _ StartMotionAux[gargoyleData, "scale", $Drag, worldPt, TRUE]; IF NOT startSuccess THEN RETURN[FALSE]; anchorPoint _ GGCaret.GetPoint[gargoyleData.anchor]; originalVector _ GGVector.Sub[gargoyleData.drag.startPoint, anchorPoint]; IF originalVector = [0.0, 0.0] THEN { GGError.AppendHerald[gargoyleData.feedback, "Move caret away from anchor before scaling.", oneLiner]; GGError.Blink[gargoyleData.feedback]; RETURN[FALSE]; }; DuringScale[NIL, gargoyleData, worldPt]; }; StartSixPoint: PUBLIC StartProc = { OPEN GGVector; epsilon: REAL = 1.0e-3; p0, p1, p2: Point; crossProduct: REAL; startSuccess: BOOL _ StartMotionAux[gargoyleData, "six point", $Drag, worldPt, TRUE]; IF NOT startSuccess THEN RETURN[FALSE]; p0 _ GGCaret.GetPoint[gargoyleData.anchor]; p2 _ gargoyleData.drag.startPoint; p1 _ Add[p0, VectorPlusAngle[Sub[p2, p0], 90.0]]; crossProduct _ CrossProductScalar[Sub[p1,p0], Sub[p2, p0]]; IF ABS[crossProduct] < epsilon THEN { GGError.AppendHerald[gargoyleData.feedback, "Move caret away from anchor before six point.", oneLiner]; GGError.Blink[gargoyleData.feedback]; RETURN[FALSE]; }; DuringSixPoint[NIL, gargoyleData, worldPt]; }; DragTheCaret: PROC [worldPt: Point, gargoyleData: GargoyleData, opName: Rope.ROPE] RETURNS [mapPoint: Point] = { feature: FeatureData; [mapPoint, feature] _ GGMultiGravity.Map[worldPt, gargoyleData.hitTest.criticalR, gargoyleData.hitTest.currentObjectBag, gargoyleData.hitTest.sceneTriggerBag, gargoyleData, TRUE]; SetCaretAttractor[gargoyleData, mapPoint, feature, opName]; }; DuringDrag: PUBLIC MouseProc = { totalDragVector: Vector; mapPoint: Point; mapPoint _ DragTheCaret[worldPt, gargoyleData, "Dragging:"]; totalDragVector _ GGVector.Sub[mapPoint, gargoyleData.drag.startPoint]; gargoyleData.drag.transform _ ImagerTransformation.Translate[[totalDragVector.x, totalDragVector.y]]; GGWindow.RestoreScreenAndInvariants[paintAction: $DuringMotion, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; }; -- end DuringDrag DuringRotate: PUBLIC MouseProc = { originalVector, newVector: Vector; mapPoint: Point; degrees: REAL; anchorPoint: Point _ GGCaret.GetPoint[gargoyleData.anchor]; mapPoint _ DragTheCaret[worldPt, gargoyleData, "Rotating:"]; originalVector _ GGVector.Sub[gargoyleData.drag.startPoint, anchorPoint]; newVector _ GGVector.Sub[mapPoint, anchorPoint]; degrees _ GGVector.AngleCCWBetweenVectors[originalVector, newVector]; gargoyleData.drag.transform _ GGTransform.RotateAboutPoint[anchorPoint, degrees]; GGWindow.RestoreScreenAndInvariants[paintAction: $DuringMotion, gargoyleData: gargoyleData, 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[gargoyleData.anchor]; mapPoint _ DragTheCaret[worldPt, gargoyleData, "Scaling:"]; originalVector _ GGVector.Sub[gargoyleData.drag.startPoint, anchorPoint]; newVector _ GGVector.Sub[mapPoint, anchorPoint]; IF RealFns.AlmostZero[newVector.x, -10] AND RealFns.AlmostZero[newVector.y, -10] THEN RETURN; -- can't scale to zero ratio _ GGVector.Magnitude[newVector]/GGVector.Magnitude[originalVector]; gargoyleData.drag.transform _ GGTransform.ScaleAboutPoint[anchorPoint, ratio]; GGWindow.RestoreScreenAndInvariants[paintAction: $DuringMotion, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; }; DuringSixPoint: PUBLIC MouseProc = { OPEN GGVector; epsilon: REAL = 1.0e-3; pts: ARRAY [0..5] OF Point; crossProduct: REAL; mapPoint: Point; pts[0] _ pts[3] _ GGCaret.GetPoint[gargoyleData.anchor]; pts[2] _ gargoyleData.drag.startPoint; pts[1] _ pts[4] _ Add[pts[0], VectorPlusAngle[Sub[pts[2], pts[0]], 90.0]]; mapPoint _ DragTheCaret[worldPt, gargoyleData, "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 gargoyleData.drag.transform _ GGTransform.SixPoints[pts]; GGWindow.RestoreScreenAndInvariants[paintAction: $DuringMotion, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; }; EndMotion: PUBLIC MouseProc = { GGStatistics.StartInterval[$EndMotion, GGStatistics.GlobalTable[]]; TransformObjectsAfterMove[gargoyleData]; GGRefresh.MoveOverlayToBackground[gargoyleData]; GGWindow.NewCaretPos[gargoyleData]; GGWindow.RestoreScreenAndInvariants[paintAction: $FinishedDragging, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE]; GGStatistics.StopInterval[$EndMotion, GGStatistics.GlobalTable[]]; }; ExtendTrajToMouse: PRIVATE PROC [scene: Scene, worldPt: Point, gargoyleData: GargoyleData] RETURNS [traj: Traj, movingJointSeq: Sequence, success: BOOL] = { caret: Caret _ gargoyleData.caret; chair: REF ANY; caretPoint: Point; jointNum: NAT; newLine: Segment; jointParts: SliceParts; jointD: OutlineDescriptor; partType: TrajPartType; caretPoint _ GGCaret.GetPoint[caret]; IF GGCaret.SittingOnEnd[caret] THEN { -- extend the existing trajectory outlineD: OutlineDescriptor; chair _ GGCaret.GetChair[caret]; -- better be a traj !! IF ISTYPE[chair, SliceDescriptor] THEN ERROR Problem[msg: "Slices are Outlines now."]; outlineD _ NARROW[chair]; [success, partType, traj, ----, jointNum] _ GGOutline.UnpackSimpleDescriptorOld[outlineD]; IF NOT success OR partType # joint THEN ERROR; IF jointNum = 0 THEN { -- add to the low end of the trajectory newLine _ GGSegment.MakeLine[worldPt, caretPoint, NIL]; success _ GGTraj.AddSegment[traj, lo, newLine, hi]; IF NOT success THEN RETURN; GGSelect.ReselectTraj[traj, lo, gargoyleData.scene, TRUE]; GGCaret.SetAttractor[gargoyleData.caret, worldPt, NIL]; movingJointSeq _ GGSequence.CreateFromJoint[traj, 0]; jointParts _ GGOutline.PartsFromSequence[outlineD.slice, movingJointSeq]; jointD _ NEW[OutlineDescriptorObj _ [outlineD.slice, jointParts]]; GGCaret.SitOn[gargoyleData.caret, jointD]; } ELSE IF jointNum = GGTraj.HiJoint[traj] THEN { -- add to the high end of the trajectory newLine _ GGSegment.MakeLine[caretPoint, worldPt, NIL]; success _ GGTraj.AddSegment[traj, hi, newLine, lo]; IF NOT success THEN RETURN; GGSelect.ReselectTraj[traj, hi, gargoyleData.scene, TRUE]; GGCaret.SetAttractor[gargoyleData.caret, worldPt, NIL]; movingJointSeq _ GGSequence.CreateFromJoint[traj, GGTraj.HiJoint[traj]]; jointParts _ GGOutline.PartsFromSequence[outlineD.slice, movingJointSeq]; jointD _ NEW[OutlineDescriptorObj _ [outlineD.slice, jointParts]]; GGCaret.SitOn[gargoyleData.caret, jointD]; } ELSE ERROR; } ELSE { -- Create a new trajectory starting at the caret (making touching constraints, if any). newOutline: Outline; newLine _ GGSegment.MakeLine[caretPoint, worldPt, NIL]; traj _ GGTraj.CreateTraj[caretPoint]; success _ GGTraj.AddSegment[traj, hi, newLine, lo]; IF NOT success THEN RETURN; newOutline _ GGOutline.CreateOutline[traj]; GGObjects.AddOutline[scene, newOutline, -1]; GGCaret.SetAttractor[gargoyleData.caret, worldPt, NIL]; movingJointSeq _ GGSequence.CreateFromJoint[traj, 1]; jointParts _ GGOutline.PartsFromSequence[newOutline, movingJointSeq]; jointD _ NEW[OutlineDescriptorObj _ [newOutline, jointParts]]; GGCaret.SitOn[gargoyleData.caret, jointD]; }; GGSelect.DeselectAll[gargoyleData.scene, normal]; GGSelect.SelectSequence[movingJointSeq, gargoyleData.scene, normal]; }; ContinueAdd: PUBLIC StartProc = { selSeq: Sequence _ gargoyleData.drag.seqInProgress; GGTraj.TransformSequence[selSeq, gargoyleData.drag.transform]; GGWindow.NewCaretPos[gargoyleData]; GGRefresh.MoveOverlayToBackground[gargoyleData]; success _ StartAdd[LIST[$ContinueAdd], gargoyleData, worldPt]; }; StartAdd: PUBLIC StartProc = { continue, repaintNeeded: BOOL; newOutline: Outline; GGStatistics.StartInterval[$StartAdd, GGStatistics.GlobalTable[]]; continue _ NARROW[input.first, ATOM] = $ContinueAdd; [newOutline, success] _ StartAddAux[gargoyleData, worldPt]; IF NOT success THEN RETURN; GGRefresh.SplitBackgroundAndOverlay[gargoyleData, GGBoundBox.emptyBoundBox]; repaintNeeded _ GGAlign.UpdateBagsForAction[gargoyleData, $Drag]; IF NOT repaintNeeded OR (continue AND AtomButtons.GetButtonState[gargoyleData.hitTest.heuristicsButton] = off) THEN { GGWindow.RestoreScreenAndInvariants[paintAction: $None, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; } ELSE { GGWindow.RestoreScreenAndInvariants[paintAction: $None, gargoyleData: gargoyleData, remake: bitMap, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; }; DuringAdd[NIL, gargoyleData, worldPt]; GGStatistics.StopInterval[$StartAdd, GGStatistics.GlobalTable[]]; }; -- end StartAdd StartAddAux: PROC [gargoyleData: GargoyleData, worldPt: Point] RETURNS [newOutline: Outline, success: BOOL] = { jointSeq: Sequence; trajUnderCaret: Traj; jointNum: NAT; chair: REF ANY; movingJointSeq: Sequence; oldOutline: Outline; outlineD: OutlineDescriptor; outlineParts: SliceParts; SaveSavedState[gargoyleData]; -- must do this before any possible aborts occur IF NOT GGRefresh.EmptyOverlay[gargoyleData] THEN ERROR; -- nothing on overlay [trajUnderCaret, movingJointSeq, success] _ ExtendTrajToMouse[gargoyleData.scene, worldPt, gargoyleData]; IF NOT success THEN RETURN[NIL, FALSE]; oldOutline _ GGOutline.OutlineOfTraj[trajUnderCaret]; newOutline _ GGOutline.OutlineOfTraj[movingJointSeq.traj]; GGAlign.ReplaceObsoleteOutlineTrigger[gargoyleData, oldOutline, newOutline]; chair _ GGCaret.GetChair[gargoyleData.caret]; outlineD _ NARROW[chair]; [jointNum: jointNum] _ GGOutline.UnpackSimpleDescriptorOld[outlineD]; jointSeq _ GGSequence.CreateJointToJoint[trajUnderCaret, jointNum, jointNum]; gargoyleData.drag.seqInProgress _ jointSeq; -- remember this sequence outlineParts _ GGOutline.PartsFromSequence[newOutline, jointSeq]; outlineD _ NEW[OutlineDescriptorObj _ [newOutline, outlineParts]]; GGRefresh.MoveToOverlay[outlineD, gargoyleData]; gargoyleData.drag.startPoint _ worldPt; -- used in DuringAddMotion gargoyleData.drag.transform _ ImagerTransformation.Scale[1.0]; }; -- end StartAddAux DuringAdd: PUBLIC MouseProc = { DuringAddMotion[gargoyleData, worldPt]; GGStatistics.StartInterval[$AddPaint, GGStatistics.GlobalTable[]]; GGWindow.RestoreScreenAndInvariants[paintAction: $DuringMotion, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; GGStatistics.StopInterval[$AddPaint, GGStatistics.GlobalTable[]]; }; -- end During Add DuringAddMotion: PROC [gargoyleData: GargoyleData, worldPt: Point] = { totalDragVector: Vector; mapPoint: Point; feature: FeatureData; currentObjects: ObjectBag _ NARROW[gargoyleData.hitTest.currentObjectBag]; sceneObjects: TriggerBag _ NARROW[gargoyleData.hitTest.sceneTriggerBag]; [mapPoint, feature] _ GGMultiGravity.Map[worldPt, gargoyleData.hitTest.criticalR, currentObjects, sceneObjects, gargoyleData, TRUE]; SetCaretAttractor[gargoyleData, mapPoint, feature, "Adding:"]; totalDragVector _ GGVector.Sub[mapPoint, gargoyleData.drag.startPoint]; gargoyleData.drag.transform _ ImagerTransformation.Translate[[totalDragVector.x, totalDragVector.y]]; }; -- end DuringAddMotion EndAdd: PUBLIC MouseProc = { selSeq: Sequence; GGStatistics.StartInterval[$EndAdd, GGStatistics.GlobalTable[]]; DuringAddMotion[gargoyleData, worldPt]; selSeq _ gargoyleData.drag.seqInProgress; GGTraj.TransformSequence[selSeq, gargoyleData.drag.transform]; GGWindow.NewCaretPos[gargoyleData]; GGRefresh.MoveOverlayToBackground[gargoyleData]; gargoyleData.refresh.startBoundBox^ _ selSeq.traj.boundBox^; gargoyleData.refresh.addedObject _ GGOutline.OutlineOfTraj[selSeq.traj]; GGWindow.RestoreScreenAndInvariants[paintAction: $FinishedAdding, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE]; GGStatistics.StopInterval[$EndAdd, GGStatistics.GlobalTable[]]; }; AddNewBoxSlice: PROC [from, to: Point, gargoyleData: GargoyleData] RETURNS [sliceD: 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]; gargoyleData.drag.boxInProgress _ sliceD _ GGSlice.MakeBoxSlice[box, corner, GGTransform.Identity[]]; GGObjects.AddSlice[gargoyleData.scene, sliceD.slice, -1]; }; StartBox: PUBLIC StartProc = { sliceD: SliceDescriptor; caretPos: Point; GGStatistics.StartInterval[$StartBox, GGStatistics.GlobalTable[]]; caretPos _ GGCaret.GetPoint[gargoyleData.caret]; SaveSavedState[gargoyleData]; -- must do this before any possible aborts occur IF NOT GGRefresh.EmptyOverlay[gargoyleData] THEN ERROR; -- nothing on overlay sliceD _ AddNewBoxSlice[caretPos, worldPt, gargoyleData]; GGSelect.DeselectAll[gargoyleData.scene, normal]; GGSelect.SelectSlice[slice: sliceD.slice, parts: sliceD.parts, scene: gargoyleData.scene, selectClass: normal]; GGRefresh.MoveToOverlay[sliceD, gargoyleData]; -- slice on overlay to be rubberbanded gargoyleData.drag.startPoint _ worldPt; gargoyleData.drag.transform _ ImagerTransformation.Scale[1.0]; GGRefresh.SplitBackgroundAndOverlay[gargoyleData, GGBoundBox.emptyBoundBox]; GGWindow.RestoreScreenAndInvariants[paintAction: $None, gargoyleData: gargoyleData, remake: bitMap, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; DuringAdd[NIL, gargoyleData, worldPt]; GGStatistics.StopInterval[$StartBox, GGStatistics.GlobalTable[]]; }; EndBox: PUBLIC MouseProc = { sliceD: SliceDescriptor _ NARROW[gargoyleData.drag.boxInProgress]; slice: Slice _ sliceD.slice; slice.class.transform[slice, sliceD.parts, gargoyleData.drag.transform]; -- update the slice GGRefresh.MoveOverlayToBackground[gargoyleData]; gargoyleData.refresh.startBoundBox^ _ slice.boundBox^; --newly updated by slice.class.transform gargoyleData.refresh.addedObject _ slice; GGWindow.RestoreScreenAndInvariants[paintAction: $FinishedAdding, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE]; }; EndSelectWithBox: PUBLIC MouseProc = { sliceD: SliceDescriptor _ NARROW[gargoyleData.drag.boxInProgress]; slice: Slice _ sliceD.slice; slice.class.transform[slice, sliceD.parts, gargoyleData.drag.transform]; -- update the slice GGRefresh.MoveOverlayToBackground[gargoyleData]; GGSelect.SelectSlice[slice: slice, parts: slice.class.newParts[slice, NIL, topLevel], scene: gargoyleData.scene, selectClass: normal]; GGEvent.AreaSelectNewAndDelete[NIL, gargoyleData]; gargoyleData.refresh.startBoundBox^ _ slice.boundBox^; --newly updated by slice.class.transform GGWindow.RestoreScreenAndInvariants[paintAction: $FinishedAdding, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: FALSE, okToClearFeedback: FALSE]; }; FixupAbortedBox: PROC [gargoyleData: GargoyleData] = { sliceD: SliceDescriptor _ NARROW[gargoyleData.drag.boxInProgress]; slice: Slice _ sliceD.slice; repaintBox: BoundBox _ GGCaret.BoundBoxOfCaret[gargoyleData.caret, gargoyleData]; -- start with caret GGBoundBox.EnlargeByBox[repaintBox, slice.boundBox]; -- repaint deleted box slice GGSelect.DeselectEntityAllClasses[slice, gargoyleData.scene]; GGSlice.DeleteSlice[gargoyleData.scene, slice]; gargoyleData.refresh.startBoundBox^ _ repaintBox^; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: FALSE, okToClearFeedback: FALSE]; }; InitStats: PROC [] = { interval: GGStatistics.Interval; bags, paint, background: GGStatistics.Interval; bags _ GGStatistics.CreateInterval[$SetBags]; paint _ GGStatistics.CreateInterval[$AddPaint]; background _ GGStatistics.CreateInterval[$StoreBackground]; interval _ GGStatistics.CreateInterval[$StartAdd, LIST[bags, paint, background]]; GGStatistics.AddInterval[interval, GGStatistics.GlobalTable[]]; interval _ GGStatistics.CreateInterval[$EndAdd]; GGStatistics.AddInterval[interval, GGStatistics.GlobalTable[]]; interval _ GGStatistics.CreateInterval[$StartCaretPos]; GGStatistics.AddInterval[interval, GGStatistics.GlobalTable[]]; interval _ GGStatistics.CreateInterval[$DuringCaretPos]; GGStatistics.AddInterval[interval, GGStatistics.GlobalTable[]]; interval _ GGStatistics.CreateInterval[$EndCaretPos]; GGStatistics.AddInterval[interval, GGStatistics.GlobalTable[]]; interval _ GGStatistics.CreateInterval[$EndMotion]; GGStatistics.AddInterval[interval, GGStatistics.GlobalTable[]]; interval _ GGStatistics.CreateInterval[$StartBox]; GGStatistics.AddInterval[interval, GGStatistics.GlobalTable[]]; interval _ GGStatistics.CreateInterval[$HandleGuarded]; GGStatistics.AddInterval[interval, GGStatistics.GlobalTable[]]; }; InitStats[]; END. ´GGMouseEventImplA.mesa Last edited by Bier on January 28, 1987 3:03:29 pm PST 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, January 15, 1987 1:27:43 pm PST Kurlander August 7, 1986 10:54:44 am PDT PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, worldPt: Point]; PROC [input: LIST OF REF ANY, gargoyleData: GargoyleData, 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. The FSM An easy way to handle the Abort action. $Circle => HandleGuarded[StartCircle, DuringAdd, EndCircle, AbortCircle, NIL, event, gargoyleData, point]; (mouseMode = $Circle AND state = $StartCircle) OR Selection Procs Sort back to front (in increasing order). [ref1: REF ANY, ref2: REF ANY] RETURNS [Basics.Comparison] Make 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. -- no segments, only joints selected If the whole outline is selected, copy it as a whole. Otherwise, each piece becomes a new slice. -- no segments, only joints selected Sort the new slices Add all the new slices to the scene. While a joint, segment, traj, or top level object is being selected, gravity is forced to be StrictDistance. The object bag should consist only of trajectories and slices. The caret is moved to the segment endpoint of the nearest segment or traj as appropriate or tracks the cursor if none are nearby. Feedback is in the form of highlighted joints. Use StrictDistance gravity except for SelectJoint. Put Caret on a joint (if any). Deselect all. Do the Selections Find out which point is being selected and highlight it. There shouldn't be any alignment lines in the object bag. Find out which segment is being selected and highlight it. There shouldn't be any alignment lines in the object bag. SelectFeedbackProc Find out which traj is being selected and highlight it. SelectFeedbackProc Find out which traj is being selected and highlight it. There shouldn't be any alignment lines in the object bag. Use StrictDistance gravity except for SelectJoint. Put Caret on a joint (if any). Use the commented code for more explicit messages. GGMouseEvent.DescribeSelectionAction[gargoyleData, feature, gargoyleData.drag.selectState, "Selected"]; Deselect all. Dispatch to the proper EndSelect handler for final selection. Prepare for a subsequent Add operation. Make Selection and Prepare for Extend. Prepare for a subsequent Add operation and for Extend. [jointNum, traj] _ GGOutline.NearestJointToHitData[feature.hitPart]; jointSeq _ GGSequence.CreateFromJoint[traj, jointNum]; jointParts _ GGOutline.PartsFromSequence[sliceD.slice, jointSeq]; jointD _ NEW[OutlineDescriptorObj _ [sliceD.slice, jointParts]]; GGCaret.SitOn[gargoyleData.caret, jointD]; Select the Trajectory or Slice. Prepare for Add and for Extend. [jointNum, traj] _ GGOutline.NearestJointToHitData[feature.hitPart]; jointSeq _ GGSequence.CreateFromJoint[traj, jointNum]; jointParts _ GGOutline.PartsFromSequence[sliceD.slice, jointSeq]; jointD _ NEW[OutlineDescriptorObj _ [sliceD.slice, jointParts]]; GGCaret.SitOn[gargoyleData.caret, jointD]; Perform the selection. Prepare for a subsequent Add or Extend. [jointNum, traj] _ GGOutline.NearestJointToHitData[feature.hitPart]; jointSeq _ GGSequence.CreateFromJoint[traj, jointNum]; jointParts _ GGOutline.PartsFromSequence[sliceD.slice, jointSeq]; jointD _ NEW[OutlineDescriptorObj _ [sliceD.slice, jointParts]]; GGCaret.SitOn[gargoyleData.caret, jointD]; 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. Called by EndCaretPos and all the "During" procs. Moves the caret and records the attractor which is a simple descriptor. This code very similar to SetCaretAttractorEndpoint in GGMouseEventImplB. Motion Procs We keep track of the total vector of motion since the drag began (i.e. the difference between the initial mouse position and the current mouse position. We transform all of the dragged objects before redrawing them. This style should be better than changing the actual data since there is less accumulated error, and the undo transformation is easily derived. The dragging is done. Update all of the drag entities with the total drag vector and repaint the entire scene. Addition and Extension Procs The trajectory is about to change size. Any sequences stored on the selection lists are obsolete. Update them. The trajectory is about to change size. Any sequences stored on the selection lists are obsolete. Update them. The dragging is done. Update the endpoint with the current transform. Then begin a new add operation. Update the Drawing data structures. Add a new segment. Move the trajectory's outline to the overlay plane. Initialize the transformation and remember the starting position. Map the endpoint and the caret and reposition them. The dragging is done. Update the endpoint with the current transform. do no painting yet 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 gargoyleData. The dragging is done. Update the box slice with the current transform. The box is cached in the gargoyleData. 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. Ê0¸˜Icode™Kšœ6™6šœ…™…Kšœ%™%K™(—K™šÏk ˜ Kšœ˜K˜—šÏnœœ˜ KšœÊ˜ÑKšœ˜—˜Kšœœ#˜7Kšœ œ˜'Kšœœ˜%Kšœœ ˜5Kšœ œ˜-Kšœœ!˜3Kšœœ˜!Kšœ œ˜&Kšœ œ˜&Kšœ œ˜%Kšœœœ˜3Kšœœ%˜?Kšœœ˜!Kšœœ"˜9Kšœœ˜!Kšœ œ˜'Kšœ œ˜'Kšœœ˜!Kšœ œ˜+Kšœœ˜3Kšœœ ˜5Kšœœ#˜;Kšœœ)˜GKšœ œ˜-Kšœ œ˜+Kšœœ˜6Kšœœ˜Kšœ œ˜%Kšœœ˜.Kšœœ˜/Kšœ œ˜&Kšœœ˜#K˜šœ œ˜)Kš œ œœœœ.™J—Kšœ œ˜)Kšœ œœœœ.œ œ™i—K˜Kšžœœœœ˜(Kšžœœœ œ˜:˜K˜—š ž œœ œœœœ1˜XK™Kšœ"˜"Kšœ#˜#KšœJÏc˜bKšœ˜K˜K˜—š žœœ œœœœ1˜WKšœœ˜.Kšœ˜K˜K˜—š žœœ œœœœ1˜WKšœ˜Kšœ˜K˜K˜—šž œœ!˜2Kšœ0˜0KšœC˜CKšœœ œœ˜¯K˜K˜—šžœœœ!˜AKšœ˜Kšœ˜Kšœ0˜0K˜K˜—šžœœ!˜5Kšœ-˜-Kšœ$˜$K˜IK˜—K™K™šžœœœ œœœœœœ˜NK™'Kšœ+˜+K˜K˜—šž œœœ œœœœœœ˜XKšœœ˜ Kšœœ ˜0šœ˜"KšœRœ˜sKšœf˜fKšœ=œ˜^KšœIœ™jKšœDœ˜eKšœNœ˜oKšœJœ˜kKšœGœ˜hKšœPœ˜qKšœo˜oKšœr˜rKšœ–œ˜·Kšœv˜vKšœšœ˜»Kšœ|˜|Kšœšœ˜»Kšœx˜xKšœœœ˜½Kšœª˜ªKšœƒœ˜¤Kšœ‡œ˜¨Kšœœ˜®Kšœ‰œ˜ª˜ Kšœœ˜šœ˜šœ˜KšœL˜LK˜—šœ˜KšœG˜GK˜—šœ˜KšœG˜GK˜—šœ˜KšœQ˜QK˜—šœ˜KšœH˜HK˜—šœ˜KšœO˜OK˜—šœ˜KšœJ˜JK˜—šœ˜KšœI˜IK˜—šœ˜KšœL˜LK˜—šœ˜KšœO˜OK˜—šœ˜KšœR˜RK˜—šœ˜KšœQ˜QK˜—šœ˜KšœT˜TK˜—šœ˜KšœT˜TK˜—šœ˜KšœW˜WK˜—šœ˜KšœR˜RK˜—šœ˜KšœU˜UK˜—šœ˜KšœJ˜JK˜—šœ˜KšœQ˜QK˜—šœ˜KšœS˜SK˜—šœ˜KšœV˜VK˜—šœ˜KšœT˜TK˜—KšœŸ˜ —K˜—Kšœœ$˜5—˜K˜—K™—š ž œœcœœœœ1˜¶Kšœœ˜Kšœœ˜KšœG˜Gšœ œ˜Kšœ œœœŸ)˜FKšœ˜—Kšœœ˜Kšœ˜Kš œœ2œœœ˜`Kšœw˜wKšœF˜FK˜K˜—šžœœ œœœœœœ˜UKšœ œ˜)Kšœœœ˜"šœ˜Kšœœ˜5Kšœœ˜+Kšœœ˜+Kšœœ™1Kšœœ˜-Kšœœ˜;Kšœœ˜1Kšœœ˜/Kšœœ˜;Kšœœ˜?Kšœœ!˜EKšœœ˜AKšœœ ˜CKšœœ˜?Kšœœ ˜CKšœ!œ#˜IKšœœ"˜D—˜K˜——šžœœkœ œœœœ1˜Îšœ˜˜ šœ˜šœ ˜ Kšœ)˜)Kšœ7˜7KšœÏb œœ˜JKšœ  œC˜R—Kšœ˜—K˜—˜ šœ˜šœ ˜ Kš  œ˜)Kšœ)˜)K˜—Kšœ   œ?˜SKšœ*˜*Kšœ*˜*šœ Ÿ}˜‰Kš  œ;˜DKšœ˜Kšœ*˜*K˜—Kšœ˜—K˜—šœ ˜ šœ˜šœ ˜ Kš œ œ˜=Kšœ˜Kšœ˜K˜—Kšœ   œ?˜SKšœ˜—K˜—˜ šœ˜šœ ˜ Kš œ œ˜=Kšœ˜Kšœ˜K˜—Kšœ   œ?˜Sšœ ŸE˜Qšœœœœ˜=šœ  œ˜4Kšœ˜Kšœ)˜)K˜—Kšœ  œ@˜OK˜—šœ˜Kšœ˜Kš œ œ˜=Kšœ˜Kšœ*˜*K˜—K˜—Kšœ˜—K˜—šœ ˜ Kšœ˜KšœG˜GKšœ˜K˜—Kšœœ'˜8—K˜K˜—š žœœJœœœœ1˜ŸKšœœ˜Kšœœ˜šœ œ˜Kšœ œœœŸ)˜FKšœ˜—Kšœœ˜Kšœ˜Kš œœ2œœœ˜`Kšœk˜kK˜K˜—šžœœRœ œœœœ1˜·šœ˜˜ šœ˜šœ ˜ Kšœ7˜7Kšœ  œœ˜JKšœ  œ>˜MKšœ˜—Kšœ˜—K˜—˜ šœ˜Kšœ   œ˜4šœ˜Kš œ˜&Kšœ˜Kšœ˜K˜—Kšœ   œ?˜Sšœ Ÿ}˜‰Kš  œ;˜DKšœ˜Kšœ*˜*K˜—Kšœ˜—K˜—šœ ˜ šœ˜KšœG˜GKšœI˜IKšœ˜—K˜—Kšœœ'˜8—K˜K˜—K™K™K™šžœœœœœœœ œœœœ˜YK•StartOfExpansion> -- [ref1: REF ANY, ref2: REF ANY] RETURNS [Basics.Comparison]™)šž œ˜"KšÐck:™:Kšœœ˜šœœ ˜K˜+K˜1Kšœ˜—šœœ˜K˜+K˜1Kšœ˜—Kšœ*˜0K˜—Kšœ œ&˜5K˜K˜—šžœœ˜*Kšœœ˜KšœF˜Fš œ œœDœ œ˜lšœœ˜K˜'K˜-Kšœœ˜—Kšœ˜Kšœ˜—K˜K˜—šž œœ˜"Kšœõ™õKšœ˜K˜Kš œ œœœœ˜Kšœ˜Kšœ˜K˜-Kšœ$˜$Kšœ"˜"KšœŸ0˜NKšœ&˜&Kšœ+ œ˜3šœlœ œ˜„Jšœ?˜?JšœB˜BJšœ œ˜(Jšœ*˜*Jšœ˜—Jšœ=˜=šœwœ œ˜Jšœ˜šœœœ˜šœHœœ˜TKšœ$™$KšœY˜YKšœ%˜%Kšœœ˜K˜—Jš 5™5šœ3œ˜;Jšœ)˜)JšœO˜OJšœ œ˜*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œ œ˜šœ@œœ˜LKšœ$™$KšœY˜YKšœ%˜%Kšœœ˜K˜—Jšœ*˜*J–f[traj: GGModelTypes.Traj, lineEnds: Imager.StrokeEnd _ square, fillColor: ImagerColorDefs.Color]šœS˜SJšœ2˜2Jšœ œ˜*Jšœ'˜'Jšœ˜—Jšœ˜—Jš ™Jšœ)˜)Jš $™$š œ œœœ œ˜Tšœœ˜ šœ˜Jšœ5˜5Jšœ-œ ˜9Jšœ8˜8J˜—šœ˜Jšœ9˜9Jšœ1œ ˜=Jšœ<˜˜>Kšœ?˜?Kšœ˜—Kšœ œŸ;˜MšœPœ˜VK™9—Kšœœ˜#—KšœŸ˜K˜—šž œ œG˜^Kšœ8˜8K˜K˜—šžœœDœœ˜K™K™7šœ˜šœ ˜ Kšœœ)˜BKšœ?˜?KšœC˜CKšœ˜—šœ ˜ Kšœœ'˜˜>Kšœ8˜8Kšœœ˜%—K˜—K˜K˜—šžœœRœ ˜wKšœÅ™ÅKšœ œœ4œ˜Mšœ˜šœ˜šœ ˜ K˜ Kšœ#˜#Kšœœ˜Kšœ˜K˜K˜Kšœœ˜4KšœT˜Tšœ ˜šœ ˜ Kšœ1˜1K˜—šœ˜Kšœ=˜=K˜—šœ ˜ Kšœ7˜7K˜—Kšœœ˜—Kšœ9˜9Kšœ œ1˜=Kšœ;˜;K˜—šœ ˜ KšœB˜BK˜—Kšœ7œ˜C—K˜—šœQ˜QKšœJ˜JKšœ&˜&Kšœ˜—K˜—K™K™ K™šžœœ+œ œœœœœ˜Kšœ˜Kšœœ˜Kš œœ&œœŸ˜NKšœŸ0˜Nšœ3œ˜;Kšœa˜aKšœ%˜%Kšœœ˜K˜—šœ œœ%œ˜@Kšœ[˜[Kšœ%˜%Kšœœ˜K˜—Kšœ9˜9KšœD˜DKšœGœŸ˜iKšœ>˜>Kšœ=˜=Kšœ2˜2Kšœ>˜>KšœC˜CKš œœ] œ œ œœ˜³Kš œ] œ œ œœ˜¡Kšœœ˜ Kšœ˜K˜—šž œœ˜Kšœœ8˜JKš œœœœœ˜'Kšœ œ˜'K˜K˜—šž œœ˜!Kšœœ:œ˜RKš œœœœœ˜'Kšœ œ˜)K˜K˜—šž œœ˜ Kšœ˜Kšœ˜Kšœœ9œ˜QKš œœœœœ˜'Kšœ4˜4KšœI˜Išœœ˜%Kšœe˜eKšœ%˜%Kšœœ˜K˜—Kšœ œ˜(K˜K˜—šž œœ˜#Kšœ ˜Kšœ œ ˜Kšœ˜Kšœœ˜Kšœœ=œ˜UKš œœœœœ˜'Kšœ+˜+Kšœ"˜"Kšœ1˜1Kšœ;˜;šœœœ˜%Kšœg˜gKšœ%˜%Kšœœ˜K˜—Kšœœ˜+K˜K˜—K™K™éK˜šž œœ;œœ˜pKšœ˜Kšœ% œ…œ˜³Kšœ;˜;K˜K˜K˜—šž œœ˜ K˜Kšœ˜Kšœ<˜Kšœ2œ˜7Kšœ3˜3šœœ œœ˜K™p—Kšœ œœ˜:Kšœ2œ˜7Kšœ5˜5KšœI˜IKšœ œ6˜BKšœ*˜*K˜—šœœ!œŸ(˜WKšœ2œ˜7Kšœ3˜3Kšœœ œœ˜šœ œœ˜:K™p—Kšœ2œ˜7KšœH˜HKšœI˜IKšœ œ6˜BKšœ*˜*K˜—Kšœœ˜ K˜—šœŸW˜^K˜Kšœ2œ˜7Kšœ%˜%Kšœ3˜3Kšœœ œœ˜Kšœ+˜+Kšœ,˜,Kšœ2œ˜7Kšœ5˜5KšœE˜EKšœ œ2˜>Kšœ*˜*K˜—Kšœ1˜1KšœD˜DK˜K˜—šž œœ˜!K™gKšœ3˜3Kšœ>˜>Kšœ#˜#Kšœ0˜0K–h[input: LIST OF REF ANY, gargoyleData: GGInterfaceTypes.GargoyleData, worldPt: GGBasicTypes.Point]šœœ'˜>K˜K˜—šžœœ˜Kšœœ˜Kšœ˜K˜BKšœ œœ˜4Kšœ;˜;šœœ œœ˜Kš #™#—KšœL˜LKšœA˜Ašœœ˜Kšœ œJœ˜]Kš œ\ œ œ œœ˜œK˜—šœ˜Kš œ\ œ œ œœ˜žK˜—Kšœ œ˜&K˜AKšœŸ˜K˜—šž œœ.œ œ˜oK˜Kšœ˜Kšœ œ˜Kšœœœ˜Kšœ˜K˜K˜K˜KšœŸ0˜Nš œœ&œœŸ˜NKš ™—Kšœ, œ,˜iKš œœ œœœœ˜'Kšœ5˜5Kšœ:˜:šœL˜LKš 3™3—Kšœ-˜-Kšœ œ˜K˜EKšœM˜MKšœ-Ÿ˜FK–?[outline: GGModelTypes.Outline, seq: GGModelTypes.Sequence]šœA˜AKšœ œ4˜Bšœ0˜0Kš A™A—Kšœ(Ÿ˜BKšœ>˜>KšœŸ˜K˜—šž œœ˜Kšœ'˜'K˜BKšœuœ œœ˜¤K˜AK˜K˜—šžœœ1˜FK™3K˜Kšœ˜Kšœ˜Kšœœ(˜JKšœœ'˜HK˜Kšœ~œ˜„Kšœ>˜>KšœG˜GKšœe˜eKšœ˜K˜—šžœœ˜K™FKšœ˜K˜@Kšœ'˜'Kšœ)˜)Kšœ>˜>Kšœ#˜#Kšœ0˜0Kšœ<˜˜>KšœL˜LKšœoœ œœ˜žKšœ œ˜&KšœA˜AKšœ˜K˜—šžœœ˜K™nKšœœ"˜BKšœ˜KšœIŸ˜\Kšœ  œ˜0Kšœ7Ÿ(˜_Kšœ)˜)Kšœ}œ œœ˜¬K˜K˜—šžœœ˜&Kšœ«™«Kšœœ"˜BKšœ˜KšœIŸ˜\Kšœ  œ˜0KšœFœ=˜†Kšœœ˜2Kšœ7Ÿ(˜_Kšœ}œ œœ˜­K˜K˜—šžœœ!˜6KšœZ™ZKšœœ"˜BKšœ˜KšœRŸ˜eKšœ5Ÿ˜QKšœ=˜=Kšœ/˜/Kšœ2˜2KšœŒœ œœ˜¼K˜—K˜šž œœ˜K˜ K˜/K˜-K˜/K˜;Kšœ2œ˜QKšœ?˜?K˜Kšœ0˜0Kšœ?˜?K˜Kšœ7˜7Kšœ?˜?Kšœ8˜8Kšœ?˜?Kšœ5˜5Kšœ?˜?Kšœ3˜3Kšœ?˜?Kšœ2˜2Kšœ?˜?Kšœ7˜7Kšœ?˜?K˜—K˜K˜ K˜Kšœ˜—…—át)à