<> <> <> <> <> <> <> <> <> <<>> DIRECTORY Atom, Basics, CodeTimer, ColorTool, Feedback, FeedbackTypes, GGAlign, GGBasicTypes, GGCaret, GGDescribe, GGDragTypes, GGEvent, GGHistory, GGHistoryTypes, GGInterfaceTypes, GGModelTypes, GGMouseEvent, GGMultiGravity, GGOutline, GGParent, GGRefresh, GGRefreshTypes, GGScene, GGSegment, GGSegmentTypes, GGSelect, GGSequence, GGSlice, GGSliceOps, GGState, GGTraj, GGUserInput, GGUtility, GGWindow, Imager, ImagerTransformation, InputFocus, IO, Menus, PBasics, RealFns, Rope, Vectors2d; GGMouseEventImplB: CEDAR PROGRAM IMPORTS CodeTimer, ColorTool, Feedback, GGAlign, GGCaret, GGDescribe, GGEvent, GGHistory, GGMouseEvent, GGMultiGravity, GGOutline, GGParent, GGRefresh, GGScene, GGSegment, GGSelect, GGSequence, GGSlice, GGSliceOps, GGState, GGTraj, GGUtility, GGWindow, ImagerTransformation, IO, PBasics, Rope, Vectors2d EXPORTS GGMouseEvent, GGInterfaceTypes = BEGIN DragDataObj: PUBLIC TYPE = GGDragTypes.DragDataObj; AlignBag: TYPE = GGInterfaceTypes.AlignBag; AlignmentPoint: TYPE = GGInterfaceTypes.AlignmentPoint; BoundBox: TYPE = GGModelTypes.BoundBox; Caret: TYPE = GGInterfaceTypes.Caret; Color: TYPE = Imager.Color; DefaultData: TYPE = GGModelTypes.DefaultData; ExtendMode: TYPE = GGInterfaceTypes.ExtendMode; FeatureCycler: TYPE = GGInterfaceTypes.FeatureCycler; FeatureData: TYPE = GGModelTypes.FeatureData; MsgRouter: TYPE = FeedbackTypes.MsgRouter; GGData: TYPE = GGInterfaceTypes.GGData; HistoryEvent: TYPE = GGHistoryTypes.HistoryEvent; Joint: TYPE = GGModelTypes.Joint; MouseButton: TYPE = Menus.MouseButton; MouseProc: TYPE = GGMouseEvent.MouseProc; Point: TYPE = GGBasicTypes.Point; RefreshDataObj: PUBLIC TYPE = GGRefreshTypes.RefreshDataObj; Scene: TYPE = GGModelTypes.Scene; Segment: TYPE = GGSegmentTypes.Segment; SelectMode: TYPE = GGModelTypes.SelectMode; Sequence: TYPE = GGModelTypes.Sequence; Slice: TYPE = GGModelTypes.Slice; SliceClass: TYPE = GGModelTypes.SliceClass; SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor; SliceDescriptorGenerator: TYPE = GGModelTypes.SliceDescriptorGenerator; SliceGenerator: TYPE = GGModelTypes.SliceGenerator; SliceParts: TYPE = GGModelTypes.SliceParts; StartProc: TYPE = GGMouseEvent.StartProc; TouchGroup: TYPE = GGSegmentTypes.TouchGroup; Traj: TYPE = GGModelTypes.Traj; TrajData: TYPE = GGModelTypes.TrajData; TrajEnd: TYPE = GGModelTypes.TrajEnd; TrajParts: TYPE = GGModelTypes.TrajParts; TrajPartType: TYPE = GGModelTypes.TrajPartType; TriggerBag: TYPE = GGAlign.TriggerBag; UserInputProc: TYPE = GGUserInput.UserInputProc; Vector: TYPE = GGBasicTypes.Vector; Problem: SIGNAL [msg: Rope.ROPE] = Feedback.Problem; SetCaretAttractorEndpoint: PUBLIC PROC [ggData: GGData, mapPoint: Point, normal: Vector, testPoint: Point, feature: FeatureData, hitData: REF ANY] = { IF feature=NIL THEN GGCaret.SetAttractor[ggData.caret, mapPoint, normal, NIL] ELSE { shape: REF ANY _ feature.shape; SELECT feature.type FROM slice => { pos: Point; sliceD: SliceDescriptor _ NARROW[shape]; jointD: SliceDescriptor; [jointD, pos, normal] _ GGSliceOps.ClosestJointToHitData[sliceD, mapPoint, testPoint, hitData]; IF Vectors2d.MagnitudeSquared[normal] = 0 THEN { normal _ [0, -1];}; GGCaret.SetAttractor[ggData.caret, pos, normal, jointD]; }; ENDCASE => GGCaret.SetAttractor[ggData.caret, mapPoint, normal, NIL]; }; }; <> SetStrokeColorRemote: PUBLIC PROC [ggData: GGData, sliceD: SliceDescriptor] RETURNS [doNormalBehavior: BOOL _ FALSE, done: BOOL _ FALSE] = { theirData: GGData _ GGState.GetGGInputFocus[]; theirScene: Scene; ourColor: Imager.Color; success: BOOL _ FALSE; IF theirData = NIL THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "SetStrokeColorRemote failed: Place input focus in a Gargoyle viewer"]; RETURN; } <> <> <> <<}>> ELSE theirScene _ theirData.scene; [ourColor, success] _ GGSliceOps.GetStrokeColor[sliceD.slice, sliceD.parts]; IF success THEN { GGEvent.LineColorAux[theirData, ourColor, GGDescribe.ColorToRope[ourColor], TRUE, $Set]; SendColorToColorTool[ggData, ourColor]; } ELSE { Feedback.Append[ggData.router, oneLiner, $Complaint, "SetStrokeColorRemote: Select an object with a unique stroke color"]; }; }; SetFillColorRemote: PUBLIC PROC [ggData: GGData, sliceD: SliceDescriptor] RETURNS [doNormalBehavior: BOOL _ FALSE, done: BOOL _ FALSE] = { theirData: GGData _ GGState.GetGGInputFocus[]; theirScene: Scene; ourColor: Imager.Color; success: BOOL _ FALSE; IF theirData = NIL THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "SetFillColorRemote failed: Place input focus in a Gargoyle viewer"]; RETURN; } <> <> <> <<}>> ELSE theirScene _ theirData.scene; [ourColor, success] _ GGSliceOps.GetFillColor[sliceD.slice, sliceD.parts]; IF success THEN { GGEvent.AreaColorAux[theirData, ourColor, GGDescribe.ColorToRope[ourColor], TRUE, $Set]; SendColorToColorTool[ggData, ourColor]; } ELSE { Feedback.Append[ggData.router, oneLiner, $Complaint, "SetFillColorRemote failed: Select an object with a unique fill color"]; }; }; LocalSymbols: TYPE = REF LocalSymbolsObj; LocalSymbolsObj: TYPE = RECORD [ sliceD: SliceDescriptor, rotation: ImagerTransformation.Transformation, ggData: GGData, currentEvent: HistoryEvent ]; ColorToolIsBound: PROC [ggData: GGData] RETURNS [BOOL _ FALSE] ~ { <> RETURN[PBasics.IsBound[LOOPHOLE[ColorTool.GetColor]]]; }; SendColorToColorTool: PROC [ggData: GGData, color: Color] = { IF ColorToolIsBound[ggData] THEN { IF color=NIL THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "NIL fill colornot sent to ColorTool"] ELSE { noColorTool: BOOL _ FALSE; ColorTool.SetColor[color, NIL ! ColorTool.NoColorToolViewer => { noColorTool _ TRUE; CONTINUE }]; }; }; }; <<>> <> <<>> StartSelect: PUBLIC StartProc = { SELECT input.first FROM $StartSelectJoint => ggData.drag.selectState _ joint; $StartSelectSegment => ggData.drag.selectState _ segment; $StartSelectTrajectory => ggData.drag.selectState _ traj; $StartSelectTopLevel => ggData.drag.selectState _ topLevel; ENDCASE => ERROR; IF NOT GGRefresh.EmptyOverlay[ggData] THEN ERROR; GGMouseEvent.SaveSavedState[ggData]; DuringSelect[NIL, ggData, worldPt]; }; DuringSelect: PUBLIC MouseProc = { <> opRope: Rope.ROPE = "Selecting"; resultPoint: Point; normal: Vector; feature: FeatureData; hitData: REF ANY; CodeTimer.StartInt[$DuringSelect, $Gargoyle]; GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, caret: TRUE, selectedCPs: TRUE, attractor: TRUE]; SELECT GGState.GetSelectMode[ggData] FROM joint => [resultPoint, normal, feature, hitData] _ GGMultiGravity.PointsPreferred[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData, FALSE]; segment, segmentRange => [resultPoint, normal, feature, hitData] _ GGMultiGravity.LinesPreferred[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData]; traj, topLevel, slice => [resultPoint, normal, feature, hitData] _ GGMultiGravity.FacesPreferred[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData]; none => { resultPoint _ worldPt; normal _ [0, -1]; feature _ NIL; hitData _ NIL; }; ENDCASE => ERROR; GGMouseEvent.SetCaretAttractorEndpoint[ggData, resultPoint, normal, worldPt, feature, hitData]; GGSelect.DeselectAll[ggData.scene, normal]; IF feature#NIL THEN [] _ SelectAndDescribeSlicePart[feature, hitData, ggData, ggData.drag.selectState, opRope] ELSE Feedback.PutF[ggData.router, oneLiner, $DuringMouse, "%g nothing", [rope[opRope]]]; GGWindow.RestoreScreenAndInvariants[paintAction: $DuringSelect, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE]; CodeTimer.StopInt[$DuringSelect, $Gargoyle]; }; -- end DuringSelect EndSelect: PUBLIC MouseProc = { resultPoint: Point; normal: Vector; feature: FeatureData; hitData: REF ANY; featureCycler: GGInterfaceTypes.FeatureCycler; SELECT GGState.GetSelectMode[ggData] FROM joint => featureCycler _ GGMultiGravity.PointsPreferredCycler[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData, FALSE]; segment => featureCycler _ GGMultiGravity.LinesPreferredCycler[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData]; ENDCASE => featureCycler _ GGMultiGravity.FacesPreferredCycler[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData]; GGState.SetSelectionCycler[ggData, featureCycler]; [resultPoint, normal, feature, hitData] _ GGMultiGravity.FirstFeature[featureCycler]; <> GGMouseEvent.SetCaretAttractorEndpoint[ggData, resultPoint, normal, worldPt, feature, hitData]; GGWindow.NewCaretPos[ggData]; <> GGSelect.DeselectAll[ggData.scene, normal]; <> SELECT ggData.drag.selectState FROM joint => EndSelectAux[ggData, resultPoint, feature, hitData, joint, "joint"]; segment => EndSelectAux[ggData, resultPoint, feature, hitData, segment, "segment"]; traj => EndSelectAux[ggData, resultPoint, feature, hitData, traj, "trajectory"]; topLevel => EndSelectAux[ggData, resultPoint, feature, hitData, topLevel, "object"]; ENDCASE => ERROR; GGWindow.RestoreScreenAndInvariants[paintAction: $None, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: FALSE]; -- The Start and During will already have updated the screen properly }; -- end EndSelect SelectFromFeature: PUBLIC PROC [ggData: GGData, testPoint: Point, point: Point, normal: Vector, feature: FeatureData, hitData: REF ANY] = { <> selectMode: SelectMode _ GGState.GetSelectMode[ggData]; IF selectMode = none THEN GOTO NoSelectMode; GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, caret: TRUE, selectedParts: TRUE, selectedCPs: TRUE, attractor: TRUE]; SetCaretAttractorEndpoint[ggData, point, normal, point, feature, hitData]; GGWindow.NewCaretPos[ggData]; GGSelect.DeselectAll[ggData.scene, normal]; SELECT selectMode FROM joint => EndSelectAux[ggData, point, feature, hitData, joint, "joint"]; segment => EndSelectAux[ggData, point, feature, hitData, segment, "segment"]; traj => EndSelectAux[ggData, point, feature, hitData, traj, "trajectory"]; topLevel => EndSelectAux[ggData, point, feature, hitData, topLevel, "object"]; ENDCASE => ERROR; GGWindow.RestoreScreenAndInvariants[paintAction: $EndSelect, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: FALSE]; EXITS NoSelectMode => { Feedback.Append[ggData.router, oneLiner, $Complaint, "SelectFromFeature failed: unknown selection grain. Make a selection first."]; }; }; EndSelectAux: PROC [ggData: GGData, resultPoint: Point, feature: FeatureData, hitData: REF ANY, mode: SelectMode, opName: Rope.ROPE] = { <> IF feature = NIL THEN { GGCaret.SitOn[ggData.caret, NIL]; Feedback.PutF[ggData.router, oneLiner, $Feedback, "No near %g found.", [rope[opName]]]; } ELSE { <> SELECT mode FROM joint => GGState.SetExtendMode[ggData, joint]; segment => GGState.SetExtendMode[ggData, segmentRange]; traj => GGState.SetExtendMode[ggData, traj]; topLevel => GGState.SetExtendMode[ggData, topLevel]; ENDCASE => ERROR; BEGIN sliceD: SliceDescriptor _ SelectSlicePart[feature, hitData, ggData, mode]; featureCycler: FeatureCycler _ GGState.GetSelectionCycler[ggData]; cycleCount: NAT _ featureCycler.count; sliceType: ATOM _ GGSliceOps.GetType[sliceD.slice]; SitTheCaret[ggData.caret, sliceD, hitData, mode]; GGState.SetSliceToExtend[ggData, sliceD]; IF cycleCount > 1 THEN Feedback.PutF[ggData.router, oneLiner, $Feedback, Rope.Concat[GGSliceOps.Describe[sliceD], " selected (%g more)"], [integer[cycleCount-1]]] ELSE Feedback.Append[ggData.router, oneLiner, $Feedback, Rope.Concat[GGSliceOps.Describe[sliceD], " selected"]]; END; }; }; -- end EndSelectAux SelectAndDescribeSlicePart: PROC [feature: FeatureData, hitData: REF ANY, ggData: GGData, mode: SelectMode, opRope: Rope.ROPE] RETURNS [selectedD: SliceDescriptor]= { selectedD _ SelectSlicePart[feature, hitData, ggData, mode]; Feedback.PutF[ggData.router, oneLiner, $Feedback, "%g %g", [rope[opRope]], [rope[GGSliceOps.Describe[selectedD]]] ]; }; SelectSlicePart: PUBLIC PROC [feature: FeatureData, hitData: REF ANY, ggData: GGData, mode: SelectMode] RETURNS [selectedD: SliceDescriptor] = { <> SELECT feature.type FROM slice => { slice: Slice _ NARROW[feature.shape, SliceDescriptor].slice; selectedD _ GGSliceOps.NewParts[slice, hitData, mode]; GGSelect.SelectSlice[selectedD, ggData.scene, normal]; }; slopeLine, angleLine, distanceLine, intersectionPoint, radiiCircle, midpoint => ERROR; ENDCASE => ERROR Problem[msg: "unimplemented result type"]; }; SitTheCaret: PROC [caret: Caret, sliceD: SliceDescriptor, hitData: REF ANY, mode: SelectMode] = { <> childD: SliceDescriptor _ NIL; IF GGParent.IsParent[sliceD.slice] THEN { childD _ GGParent.FirstIncludedChild[sliceD.slice, sliceD.parts, leaf, $Traj]; }; IF childD = NIL THEN GGCaret.SitOn[caret, NIL] ELSE { SELECT mode FROM joint => { GGCaret.SitOn[caret, sliceD]; }; segment, traj, topLevel => { jointD: SliceDescriptor; jointD _ GGSliceOps.ClosestJointToHitData[sliceD, caret.point, caret.point, hitData].jointD; GGCaret.SitOn[caret, jointD]; }; ENDCASE => ERROR; }; }; <<>> <> <<>> StartDeselect: PUBLIC StartProc = { SELECT input.first FROM $StartDeselectJoint => ggData.drag.selectState _ joint; $StartDeselectSegment => ggData.drag.selectState _ segment; $StartDeselectTrajectory => ggData.drag.selectState _ traj; $StartDeselectTopLevel => ggData.drag.selectState _ topLevel; ENDCASE => ERROR; IF NOT GGRefresh.EmptyOverlay[ggData] THEN ERROR; -- nothing on overlay GGMouseEvent.SaveSavedState[ggData]; ggData.drag.transform _ ImagerTransformation.Scale[1.0]; -- needed for DuringDeselect to work properly DuringDeselect[NIL, ggData, worldPt]; }; DuringDeselect: PUBLIC MouseProc = { <> opRope: Rope.ROPE = "Deselecting"; resultPoint: Point; normal: Vector; feature: FeatureData; hitData: REF ANY; ggData.drag.currentPoint _ worldPt; GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, caret: TRUE, selectedParts: TRUE, selectedCPs: TRUE, attractor: TRUE]; SELECT GGState.GetSelectMode[ggData] FROM joint => [resultPoint, normal, feature, hitData] _ GGMultiGravity.PointsPreferred[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData, FALSE]; segment => [resultPoint, normal, feature, hitData] _ GGMultiGravity.LinesPreferred[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData]; ENDCASE => [resultPoint, normal, feature, hitData] _ GGMultiGravity.LinesPreferred[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData]; <> GGScene.RestoreSelections[ggData.scene]; IF feature#NIL THEN DuringDeselectAux[feature, hitData, resultPoint, ggData, ggData.drag.selectState, opRope] ELSE Feedback.PutF[ggData.router, oneLiner, $DuringMouse, "%g nothing", [rope[opRope]]]; GGWindow.RestoreScreenAndInvariants[paintAction: $DuringSelect, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE]; }; -- end DuringDeselect DuringDeselectAux: PROC [feature: FeatureData, hitData: REF ANY, caretPt: Point, ggData: GGData, mode: GGModelTypes.SelectMode, opRope: Rope.ROPE] = { description: Rope.ROPE; SELECT feature.type FROM slice => { deselectedD: SliceDescriptor _ SliceDeselectAux[feature, hitData, mode, ggData.scene]; description _ IF deselectedD=NIL THEN "nothing" ELSE GGSliceOps.Describe[deselectedD]; Feedback.PutF[ggData.router, oneLiner, $DuringMouse, "%g %g", [rope[opRope]], [rope[description]] ]; }; slopeLine, angleLine, distanceLine, intersectionPoint, radiiCircle, midpoint => ERROR; ENDCASE => ERROR; }; SliceDeselectAux: PROC [feature: FeatureData, hitData: REF ANY, mode: GGModelTypes.SelectMode, scene: Scene] RETURNS [deselectedD: SliceDescriptor] = { shapeD: SliceDescriptor _ NARROW[feature.shape]; sliceD: SliceDescriptor _ GGSelect.FindSelectedSlice[shapeD.slice, normal]; IF sliceD#NIL THEN { newD: SliceDescriptor _ GGSliceOps.NewParts[sliceD.slice, hitData, mode]; diffD: SliceDescriptor _ GGSliceOps.DifferenceParts[sliceD, newD]; GGSelect.DeselectSlice[sliceD.slice, sliceD.parts, scene, normal]; IF NOT GGSliceOps.IsEmptyParts[diffD] THEN GGSelect.SelectSlice[diffD, scene, normal]; deselectedD _ newD; } ELSE deselectedD _ shapeD; -- must have already deselected the slice DuringDeselect }; EndDeselect: PUBLIC MouseProc = { resultPoint: Point; normal: Vector; feature: FeatureData; hitData: REF ANY; SELECT GGState.GetSelectMode[ggData] FROM joint => [resultPoint, normal, feature, hitData] _ GGMultiGravity.PointsPreferred[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData, FALSE]; segment => [resultPoint, normal, feature, hitData] _ GGMultiGravity.LinesPreferred[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData]; ENDCASE => [resultPoint, normal, feature, hitData] _ GGMultiGravity.LinesPreferred[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData]; <> <> <> SELECT ggData.drag.selectState FROM joint => EndDeselectAux[ggData, resultPoint, feature, hitData, joint, "joint"]; segment => EndDeselectAux[ggData, resultPoint, feature, hitData, segment, "segment"]; traj => EndDeselectAux[ggData, resultPoint, feature, hitData, traj, "trajectory"]; topLevel => EndDeselectAux[ggData, resultPoint, feature, hitData, topLevel, "top level object"]; ENDCASE => ERROR; GGWindow.RestoreScreenAndInvariants[paintAction: $None, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: FALSE]; -- The Start and During will already have updated the screen properly ggData.drag.selectState _ none; -- added to help DescribeFeature work. KAP. }; EndDeselectAux: PROC [ggData: GGData, resultPoint: Point, feature: FeatureData, hitData: REF ANY, mode: SelectMode, selectRope: Rope.ROPE] = { GGCaret.SitOn[ggData.caret, NIL]; IF feature = NIL THEN { Feedback.PutF[ggData.router, oneLiner, $Feedback, "No near %g found", [rope[selectRope]]]; } ELSE { SELECT feature.type FROM slice => { gone: SliceDescriptor _ SliceDeselectAux[feature, hitData, mode, ggData.scene]; IF gone#NIL AND gone.parts#NIL THEN Feedback.PutF[ggData.router, oneLiner, $Feedback, "%g %g", [rope[GGSliceOps.Describe[gone]]], [rope["deselected"]]]; }; ENDCASE => SIGNAL Problem [msg: IO.PutFR["Unexpected feature type for deselect %g.", [rope[selectRope]]]]; }; <> }; <<>> <> <<>> StartExtendSelect: PUBLIC StartProc = { SELECT input.first FROM $StartExtSelectJoint => GGState.SetExtendMode[ggData, joint]; $StartExtSelectSegment => GGState.SetExtendMode[ggData, segment]; $StartExtSelectTrajectory => GGState.SetExtendMode[ggData, traj]; $StartExtSelectTopLevel => GGState.SetExtendMode[ggData, topLevel]; ENDCASE => ERROR; RETURN StartExtendSelection[input, ggData, worldPt]; }; StartExtendSelection: PUBLIC StartProc = { IF NOT GGRefresh.EmptyOverlay[ggData] THEN ERROR; GGMouseEvent.SaveSavedState[ggData]; -- must do this before any possible aborts occur ggData.drag.currentPoint _ worldPt; ggData.drag.transform _ ImagerTransformation.Scale[1.0]; DuringExtendSelection[LIST[$StartExtendSelection], ggData, worldPt]; }; DuringExtendSelection: PUBLIC MouseProc= { opRope: Rope.ROPE = IF input.first=$EndExtendSelection THEN "Extended to" ELSE "Extending to"; resultPoint: Point; normal: Vector; feature: FeatureData; hitData: REF ANY; extendMode: ExtendMode _ GGState.GetExtendMode[ggData]; GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, caret: TRUE, selectedCPs: TRUE, attractor: TRUE]; SELECT extendMode FROM joint => [resultPoint, normal, feature, hitData] _ GGMultiGravity.PointsPreferred[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData, FALSE]; segment, segmentRange => [resultPoint, normal, feature, hitData] _ GGMultiGravity.LinesPreferred[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData]; traj, topLevel, slice => [resultPoint, normal, feature, hitData] _ GGMultiGravity.FacesPreferred[worldPt, ggData.hitTest.t, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData]; none => { resultPoint _ worldPt; normal _ [0, -1]; feature _ NIL; hitData _ NIL; }; ENDCASE => ERROR; <> SetCaretAttractorEndpoint[ggData, resultPoint, normal, worldPt, feature, hitData]; <> GGSelect.DeselectAll[ggData.scene, normal]; GGScene.RestoreSelections[ggData.scene]; IF feature#NIL THEN { SELECT extendMode FROM joint, segment, segmentRange, traj, topLevel => DuringExtendSelectionFeedback[feature, hitData, resultPoint, ggData, opRope]; slice, none => NULL; -- NoOp ENDCASE => ERROR; } ELSE Feedback.PutF[ggData.router, oneLiner, $DuringMouse, "%g nothing", [rope[opRope]]]; GGWindow.RestoreScreenAndInvariants[paintAction: $DuringSelect, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE]; }; -- end DuringExtendSelection DuringExtendSelectionFeedback: PROC [feature: FeatureData, hitData: REF ANY, caretPt: Point, ggData: GGData, opRope: Rope.ROPE] = { <> extendMode: ExtendMode _ GGState.GetExtendMode[ggData]; SELECT extendMode FROM joint, segment, traj, topLevel => [] _ SelectAndDescribeSlicePart[feature, hitData, ggData, extendMode, opRope]; segmentRange => { IF ISTYPE[feature.shape, SliceDescriptor] THEN { sliceD: SliceDescriptor _ NARROW[feature.shape]; -- clumsy code traj: Traj; trajToExtend: SliceDescriptor; hitType: GGModelTypes.TrajPartType; segNum, cpNum, jointNum, segToExtendNum: INT; hitPoint: Point; SELECT GGSliceOps.GetType[sliceD.slice] FROM $Outline => [traj, hitType, segNum, cpNum, jointNum, hitPoint] _ GGOutline.UnpackHitData[hitData]; $Traj => { traj _ sliceD.slice; [hitType, segNum, cpNum, jointNum, hitPoint] _ GGTraj.UnpackHitData[hitData]; }; ENDCASE => GOTO RegularSelectMechanism; IF (hitType = segment OR hitType = controlPoint) THEN { [trajToExtend, segToExtendNum] _ GGOutline.UnpackOneSegmentDescriptor[GGState.GetSliceToExtend[ggData]]; IF trajToExtend#NIL AND trajToExtend.slice = traj THEN { -- we're extending segments in the same trajectory seq: TrajParts _ GGSequence.CreateFromSegments[NARROW[traj.data], segToExtendNum, segNum]; GGSelect.SelectSlice[GGSlice.DescriptorFromParts[traj, seq], ggData.scene, normal]; -- select current hit Feedback.PutF[ggData.router, oneLiner, $DuringMouse, "%g %g", [rope[opRope]], [rope[GGUtility.DescribeSegment[traj, segNum] ]] ]; } ELSE { -- you extended to a different traj GGState.SetSliceToExtend[ggData, SelectAndDescribeSlicePart[feature, hitData, ggData, segment, opRope]]; } } ELSE GOTO RegularSelectMechanism; } ELSE GOTO RegularSelectMechanism; EXITS RegularSelectMechanism => [] _ SelectAndDescribeSlicePart[feature, hitData, ggData, segment, opRope]; }; ENDCASE => ERROR; -- should have been weeded about before calling this Proc }; EndExtendSelection: PUBLIC MouseProc = { <> <> DuringExtendSelection[LIST[$EndExtendSelection], ggData, worldPt]; GGWindow.RestoreScreenAndInvariants[paintAction: $None, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: FALSE]; -- During should have made the screen OK }; -- end EndExtendSelection <> StartAddBezier: PUBLIC StartProc = { <> IF GGCaret.Exists[ggData.caret] THEN { caret: Caret; oldTraj, newTraj: Traj; ancestor, oldOutline: Slice; trajEnd: TrajEnd; newNormalD, newHotD: SliceDescriptor; GGHistory.NewCapture["Add Bezier spline", ggData]; -- capture scene BEFORE UPDATE caret _ ggData.caret; GGMouseEvent.SaveSavedState[ggData]; -- used for abort [oldTraj, newTraj, trajEnd, ancestor] _ UpdateSceneForAddBezier[ggData.scene, worldPt, caret, ggData.defaults, ggData]; oldOutline _ IF oldTraj=NIL THEN NIL ELSE GGParent.GetTopLevelAncestor[oldTraj]; [newNormalD, newHotD] _ GGMouseEvent.UpdateSelectionsForAdd[ggData.scene, oldTraj, newTraj, trajEnd]; GGMouseEvent.UpdateCaretForAdd[caret, ancestor, newNormalD, worldPt]; [] _ GGAlign.UpdateBagsForAdd[oldOutline, newNormalD, trajEnd, ggData]; -- should be ok <> [] _ GGMouseEvent.StartMotion[ggData: ggData, opName: "add", bagType: $Drag, worldPt: worldPt, saveState: FALSE, needAnchor: FALSE, backgroundOK: TRUE]; -- boundbox may be wrong DuringBezierDrag[NIL, ggData, worldPt]; -- draw it the first time } ELSE Feedback.Append[ggData.router, oneLiner, $Complaint, "Start Bezier failed: caret required for adding new Bezier"]; }; UpdateSceneForAddBezier: PROC [scene: Scene, worldPt: Point, caret: Caret, defaults: DefaultData, ggData: GGData] RETURNS [oldTraj, newTraj: Traj, trajEnd: TrajEnd, newOutline: Slice] = { caretPoint, newCaretPoint: Point; jointNum: NAT; newSeg, extendFromSeg, newBezier: Segment; chair: SliceDescriptor; thisTraj: SliceDescriptor; thisTrajParts: TrajParts; thisTrajData: TrajData; thisOutlineSlice: Slice; addToBezier: BOOL _ FALSE; <> <<>> sliceDesc: SliceDescriptor _ GGScene.FirstSelectedSlice[scene, leaf, normal, $Outline]; caretPoint _ GGCaret.GetPoint[caret]; newCaretPoint _ GGMouseEvent.DragTheCaret[worldPt, ggData, "Dragging:"]; IF sliceDesc # NIL THEN { -- otherwise nothing selected. thisOutlineSlice _ sliceDesc.slice; thisTraj _ GGParent.FirstIncludedChild[sliceDesc.slice, sliceDesc.parts, first, $Traj]; thisTrajParts _ NARROW[thisTraj.parts]; thisTrajData _ NARROW[thisTraj.slice.data]; IF thisTrajData.role = open AND GGSequence.ContainsJoint[thisTrajParts, 0] THEN { IF GGTraj.FetchSegment[thisTraj.slice, 0].class.type = $Bezier THEN { trajEnd _ lo; addToBezier _ TRUE; }; } ELSE { IF thisTrajData.role = open AND GGSequence.ContainsJoint[thisTrajParts, GGTraj.HiJoint[thisTraj.slice]] THEN { IF GGTraj.FetchSegment[thisTraj.slice, GGTraj.HiSegment[thisTraj.slice]].class.type = $Bezier THEN { trajEnd _ hi; addToBezier _ TRUE; }; }; }; }; <<>> IF addToBezier AND ggData.drag.editConstraints # none THEN { -- Lay down a new Bezier and a tangent guide. oldCtrlPt, newCtrlPt: Point; oldTraj _ newTraj _ thisTraj.slice; -- the traj that the Bezier belongs to. extendFromSeg _ GGTraj.FetchSegment[newTraj, IF trajEnd=lo THEN 0 ELSE GGTraj.HiSegment[newTraj]]; IF trajEnd = lo THEN { oldCtrlPt _ extendFromSeg.class.controlPointGet[extendFromSeg, 0]; newCtrlPt _ Vectors2d.Add[extendFromSeg.lo, Vectors2d.VectorFromPoints[oldCtrlPt, extendFromSeg.lo]]; newBezier _ GGSegment.MakeBezier[extendFromSeg.lo, newCtrlPt, newCaretPoint, newCaretPoint, NIL]; -- by the lo in AddSegment. } ELSE { oldCtrlPt _ extendFromSeg.class.controlPointGet[extendFromSeg, 1]; newCtrlPt _ Vectors2d.Add[extendFromSeg.hi, Vectors2d.VectorFromPoints[oldCtrlPt, extendFromSeg.hi]]; newBezier _ GGSegment.MakeBezier[extendFromSeg.hi, newCtrlPt, newCaretPoint, newCaretPoint, NIL]; }; newSeg _ GGSegment.MakeLine[newCaretPoint, worldPt, NIL]; -- ctrl tangent. GGSegment.SetDefaults[newSeg, defaults]; GGSegment.CopyLooks[extendFromSeg, newBezier]; newSeg.strokeWidth _ 1; [] _ GGTraj.AddSegment[newTraj, trajEnd, newBezier, lo]; [] _ GGTraj.AddSegment[newTraj, trajEnd, newSeg, lo]; ggData.drag.bezierDrag.draggingBezier _ TRUE; ggData.drag.bezierDrag.trajEnd _ trajEnd; ggData.drag.bezierDrag.bezierNum _ IF trajEnd=lo THEN 1 ELSE GGTraj.HiSegment[newTraj] - 1; ggData.drag.bezierDrag.traj _ newTraj; ggData.drag.bezierDrag.outlineSlice _ thisOutlineSlice; newOutline _ thisOutlineSlice; -- that the Bezier was in. } ELSE { -- We are not adding to a Bezier in constrained mode, so can only add first tangent guide. newSeg _ GGSegment.MakeLine[caretPoint, worldPt, NIL]; -- maybe drag caret first!!!? GGSegment.SetDefaults[newSeg, defaults]; newSeg.strokeWidth _ 1; IF GGCaret.SittingOnEnd[caret] THEN { -- Extending a Traj [chair, newTraj, jointNum] _ GGMouseEvent.SafelyGetCaretTraj[caret]; oldTraj _ newTraj; trajEnd _ SELECT jointNum FROM 0 => lo, GGTraj.HiJoint[newTraj] => hi, ENDCASE => ERROR; extendFromSeg _ GGTraj.FetchSegment[newTraj, IF trajEnd=lo THEN 0 ELSE GGTraj.HiSegment[newTraj]]; [] _ GGTraj.AddSegment[newTraj, trajEnd, newSeg, lo]; newOutline _ chair.slice; } ELSE { -- create a new traj. oldTraj _ NIL; trajEnd _ hi; newTraj _ GGTraj.CreateTraj[caretPoint]; GGSliceOps.SetStrokeJoint[newTraj, NIL, defaults.strokeJoint, NIL]; [] _ GGTraj.AddSegment[newTraj, trajEnd, newSeg, lo]; newOutline _ GGOutline.CreateOutline[newTraj, defaults.fillColor]; GGScene.AddSlice[scene, newOutline, -1]; }; ggData.drag.bezierDrag.trajEnd _ trajEnd; -- here used to track tangent. ggData.drag.bezierDrag.bezierNum _ IF trajEnd=lo THEN 0 ELSE GGTraj.HiSegment[newTraj]; ggData.drag.bezierDrag.traj _ newTraj; ggData.drag.bezierDrag.outlineSlice _ newOutline; }; }; DuringBezierDrag: PUBLIC MouseProc = { totalDragVector: Vector; mapPoint: Point; mapPoint _ GGMouseEvent.DragTheCaret[worldPt, ggData, "Dragging:"]; totalDragVector _ Vectors2d.Sub[mapPoint, ggData.drag.startPoint]; ggData.drag.transform _ ImagerTransformation.Translate[[totalDragVector.x, totalDragVector.y]]; IF ggData.drag.bezierDrag.draggingBezier THEN { newCP: Point; dragCP: Vector; bezier: Segment _ GGTraj.FetchSegment[ggData.drag.bezierDrag.traj, ggData.drag.bezierDrag.bezierNum]; IF ggData.drag.bezierDrag.trajEnd = lo THEN { dragCP _ Vectors2d.Sub[mapPoint, bezier.lo]; newCP _ Vectors2d.Sub[bezier.lo, dragCP] } ELSE { dragCP _ Vectors2d.Sub[mapPoint, bezier.hi]; newCP _ Vectors2d.Sub[bezier.hi, dragCP]; }; GGSegment.BZControlPointMovedTo[bezier, newCP, IF ggData.drag.bezierDrag.trajEnd = lo THEN 0 ELSE 1]; }; GGWindow.RestoreScreenAndInvariants[paintAction: $DuringDrag, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE]; }; -- end DuringBezierDrag ContinueBezierAdd: PUBLIC StartProc = { <> bezierSetCP, bezierSetJoint, caretPoint, newCaretPoint: Point; trajEnd: TrajEnd; extendFromSeg, newBezier, newSeg: Segment; oldTraj: Traj _ ggData.drag.bezierDrag.traj; tangentSeg: Segment; thisTraj: Traj; hiSegNum: NAT _ GGTraj.HiSegment[oldTraj]; priority: INT; newNormal, newHot: Sequence; delSeq: TrajParts; newOutlines: LIST OF Slice; thisOutlineSlice, oldOutlineSlice: Slice; thisOutline: GGOutline.OutlineData; ancestorD: SliceDescriptor; GGMouseEvent.TransformObjectsAfterMove[ggData.scene, ggData.drag.transform, ggData.drag.editConstraints, NIL]; -- update the tangent line. don't try to record undoable transforms here. A capture event is the current event. GGHistory.PushCurrent[ggData]; -- push captured history event onto history list GGRefresh.MoveOverlayToBackground[ggData]; GGWindow.NewCaretPos[ggData]; GGMouseEvent.SaveSavedState[ggData]; -- used for abort newCaretPoint _ GGMouseEvent.DragTheCaret[worldPt, ggData, "Dragging:"]; -- use Gravity for now. caretPoint _ GGCaret.GetPoint[ggData.caret]; IF ggData.drag.bezierDrag.draggingBezier THEN { IF ggData.drag.bezierDrag.trajEnd = hi THEN tangentSeg _ GGTraj.FetchSegment[oldTraj, ggData.drag.bezierDrag.bezierNum + 1] ELSE tangentSeg _ GGTraj.FetchSegment[oldTraj, 0]; } ELSE tangentSeg _ GGTraj.FetchSegment[oldTraj, ggData.drag.bezierDrag.bezierNum]; IF ggData.drag.bezierDrag.trajEnd = hi THEN { trajEnd _ hi; bezierSetJoint _ tangentSeg.lo; bezierSetCP _ tangentSeg.hi; IF hiSegNum # 0 THEN { extendFromSeg _ GGTraj.FetchSegment[oldTraj, hiSegNum -1]; }; } ELSE { trajEnd _ lo; bezierSetJoint _ tangentSeg.hi; bezierSetCP _ tangentSeg.lo; IF hiSegNum # 0 THEN { extendFromSeg _ GGTraj.FetchSegment[oldTraj, 1]; }; }; oldOutlineSlice _ ggData.drag.bezierDrag.outlineSlice; priority _ GGScene.GetPriority[scene: ggData.scene, slice: oldOutlineSlice]; newBezier _ GGSegment.MakeBezier[bezierSetJoint, bezierSetCP, newCaretPoint, newCaretPoint, NIL]; newSeg _ GGSegment.MakeLine[newCaretPoint, worldPt, NIL]; IF hiSegNum # 0 THEN { -- The traj contains more than just control vector. GGSegment.CopyLooks[extendFromSeg, newBezier]} ELSE GGSegment.SetDefaults[newBezier, ggData.defaults]; GGSegment.SetDefaults[newSeg, ggData.defaults]; newSeg.strokeWidth _ 1; delSeq _ GGSequence.LastSegAndJoint[NARROW[ggData.drag.bezierDrag.traj.data], ggData.drag.bezierDrag.trajEnd]; -- the tangent segment. newOutlines _ GGScene.DeleteSequence[GGSlice.DescriptorFromParts[ggData.drag.bezierDrag.traj, delSeq], ggData.scene].newOutlines; ancestorD _ GGParent.TopLevelDescriptorFromChildDescriptor[GGSlice.DescriptorFromParts[ggData.drag.bezierDrag.traj, delSeq]]; IF newOutlines = NIL THEN { -- Only a control vector in this outline. thisTraj _ GGTraj.CreateTraj[newBezier.lo]; thisOutlineSlice _ GGOutline.CreateOutline[thisTraj, ggData.defaults.fillColor]; GGSliceOps.SetStrokeJoint[thisTraj, NIL, ggData.defaults.strokeJoint, NIL]; [] _ GGTraj.AddSegment[thisTraj, trajEnd, newBezier, lo]; [] _ GGTraj.AddSegment[thisTraj, trajEnd, newSeg, lo]; GGScene.AddSlice[ggData.scene, thisOutlineSlice, priority]; } ELSE { thisOutlineSlice _ newOutlines.first; thisOutline _ NARROW[thisOutlineSlice.data]; thisTraj _ thisOutline.children.first; [] _ GGTraj.AddSegment[thisTraj, trajEnd, newBezier, lo]; [] _ GGTraj.AddSegment[thisTraj, trajEnd, newSeg, lo]; }; ggData.drag.bezierDrag.draggingBezier _ TRUE; ggData.drag.bezierDrag.bezierNum _ IF trajEnd=lo THEN 1 ELSE hiSegNum; ggData.drag.bezierDrag.traj _ thisTraj; ggData.drag.bezierDrag.outlineSlice _ thisOutlineSlice; GGHistory.NewCapture["Add Bezier spline", ggData]; -- capture scene BEFORE UPDATE [newNormal, newHot] _ GGMouseEvent.UpdateSelectionsForAdd[ggData.scene, oldTraj, thisTraj, trajEnd]; GGMouseEvent.UpdateCaretForAdd[ggData.caret, thisOutlineSlice, newNormal, worldPt]; [] _ GGAlign.UpdateBagsForAdd[oldOutlineSlice, ancestorD, trajEnd, ggData]; [] _ GGMouseEvent.ContinueMotion[ggData: ggData, opName: "add", bagType: $Drag, worldPt: worldPt, startBox: NIL]; DuringBezierDrag[NIL, ggData, worldPt]; -- draw it the first time }; EndBezierAdd: PUBLIC MouseProc = { <> <> delSeq: TrajParts; newOutlines: LIST OF Slice; point: Point; normal: Vector; newEndJoint: NAT; CodeTimer.StartInt[$EndBezierAdd, $Gargoyle]; SELECT ggData.drag.bezierDrag.trajEnd FROM lo => newEndJoint _ 1; hi => newEndJoint _ GGTraj.HiJoint[ggData.drag.bezierDrag.traj] -1; ENDCASE; point _ GGTraj.FetchJointPos[ggData.drag.bezierDrag.traj, newEndJoint]; normal _ GGTraj.FetchJointNormal[ggData.drag.bezierDrag.traj, newEndJoint]; delSeq _ GGSequence.LastSegAndJoint[NARROW[ggData.drag.bezierDrag.traj.data], ggData.drag.bezierDrag.trajEnd]; -- the tangent segment. [----, newOutlines] _ GGScene.DeleteSequence[GGSlice.DescriptorFromParts[ggData.drag.bezierDrag.traj, delSeq], ggData.scene]; IF newOutlines = NIL THEN { -- we removed the last segment GGCaret.SitOn[ggData.caret, NIL]; } ELSE { jointParts: TrajParts; jointD, outD: SliceDescriptor; shortOutline: Slice; shortTraj: Traj; shortOutline _ newOutlines.first; shortTraj _ GGParent.FirstChild[shortOutline, first, $Traj]; jointParts _ GGSequence.CreateFromJoint[NARROW[shortTraj.data], IF ggData.drag.bezierDrag.trajEnd = lo THEN 0 ELSE newEndJoint]; jointD _ GGSlice.DescriptorFromParts[shortTraj, jointParts]; -- now have traj descriptor. Make an outline descriptor outD _ GGParent.TopLevelDescriptorFromChildDescriptor[jointD]; GGCaret.SitOn[ggData.caret, outD]; GGSelect.SelectSlice[sliceD: outD, scene: ggData.scene, selectClass: normal]; }; GGCaret.SetAttractor[ggData.caret, point, normal, NIL]; ggData.refresh.startBoundBox^ _ GGSliceOps.GetBoundBox[ggData.drag.bezierDrag.traj]^; ggData.drag.bezierDrag.draggingBezier _ FALSE; ggData.drag.bezierDrag.outlineSlice _ NIL; ggData.drag.bezierDrag.traj _ NIL; GGHistory.PushCurrent[ggData]; -- put captured scene on history list GGRefresh.MoveOverlayToBackground[ggData]; GGWindow.NewCaretPos[ggData]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE]; CodeTimer.StopInt[$EndBezierAdd, $Gargoyle]; }; -- end EndBezierAdd AbortBezierAdd: PUBLIC PROC [input: LIST OF REF ANY, ggData: GGData, worldPt: Point] = { delSeq, partSeq: TrajParts; delD: SliceDescriptor; newOutlines: LIST OF Slice; newEndJoint: NAT; SELECT ggData.drag.bezierDrag.trajEnd FROM lo => newEndJoint _ 1; hi => newEndJoint _ GGTraj.HiJoint[ggData.drag.bezierDrag.traj] -1; ENDCASE; delSeq _ GGSequence.LastSegAndJoint[NARROW[ggData.drag.bezierDrag.traj.data], ggData.drag.bezierDrag.trajEnd]; -- the tangent segment. delD _ GGSlice.DescriptorFromParts[ggData.drag.bezierDrag.traj, delSeq]; IF ggData.drag.bezierDrag.draggingBezier THEN { -- also delete the bezier ggData.drag.bezierDrag.draggingBezier _ FALSE; partSeq _ GGSequence.CreateFromSegment[NARROW[ggData.drag.bezierDrag.traj.data], ggData.drag.bezierDrag.bezierNum]; delD _ GGSequence.Union[delD, GGSlice.DescriptorFromParts[ggData.drag.bezierDrag.traj, partSeq]]; IF ggData.drag.bezierDrag.trajEnd = hi THEN newEndJoint _ newEndJoint -1 ELSE newEndJoint _ newEndJoint +1; }; [----, newOutlines] _ GGScene.DeleteSequence[delD, ggData.scene]; IF newOutlines = NIL THEN { -- we removed the last segment GGCaret.SitOn[ggData.caret, NIL]; } ELSE { jointParts: TrajParts; jointD, outD: SliceDescriptor; shortOutline: Slice; shortTraj: Traj; IF newOutlines.rest # NIL THEN SIGNAL Problem[msg: "failed assertion"]; shortOutline _ newOutlines.first; shortTraj _ GGParent.FirstChild[shortOutline, first, $Traj]; jointParts _ GGSequence.CreateFromJoint[NARROW[shortTraj.data], IF ggData.drag.bezierDrag.trajEnd = lo THEN 0 ELSE newEndJoint]; jointD _ GGSlice.DescriptorFromParts[shortTraj, jointParts]; -- traj descriptor outD _ GGParent.TopLevelDescriptorFromChildDescriptor[jointD]; GGCaret.SitOn[ggData.caret, outD]; GGSelect.SelectSlice[sliceD: outD, scene: ggData.scene, selectClass: normal]; }; GGCaret.SetAttractor[ggData.caret, GGTraj.FetchJointPos[ggData.drag.bezierDrag.traj, newEndJoint], GGTraj.FetchJointNormal[ggData.drag.bezierDrag.traj, newEndJoint], NIL]; ggData.drag.bezierDrag.outlineSlice _ NIL; ggData.drag.bezierDrag.traj _ NIL; GGMouseEvent.FinishAbort[ggData]; }; END.