DIRECTORY Angles2d, AtomButtons, AtomButtonsTypes, CubicSplines, Feedback, GGAlign, GGBasicTypes, GGBoundBox, GGCaret, GGEditTool, GGEvent, GGGravity, GGInterface, GGInterfaceTypes, GGMeasure, GGModelTypes, GGOutline, GGParseIn, GGRefresh, GGScene, GGSegmentTypes, GGSelect, GGSequence, GGSlice, GGState, GGTraj, GGUserInput, GGUtility, GGViewerOps, GGWindow, GraphicsButton, Imager, IO, Real, RealFns, Rope, TiogaButtons, Vectors2d, ViewerClasses, ViewerTools; GGEventImplB: CEDAR PROGRAM IMPORTS Angles2d, AtomButtons, Feedback, GGAlign, GGBoundBox, GGCaret, GGEditTool, GGInterface, GGMeasure, GGOutline, GGParseIn, GGRefresh, GGScene, GGSelect, GGSequence, GGSlice, GGState, GGTraj, GGUserInput, GGUtility, GGViewerOps, GGWindow, GraphicsButton, IO, RealFns, Rope, TiogaButtons, Vectors2d, ViewerTools EXPORTS GGEvent = BEGIN WalkProc: TYPE = GGModelTypes.WalkProc; BoundBox: TYPE = GGModelTypes.BoundBox; Caret: TYPE = GGInterfaceTypes.Caret; FeatureData: TYPE = GGGravity.FeatureData; GGData: TYPE = GGInterfaceTypes.GGData; AlignBag: TYPE = GGInterfaceTypes.AlignBag; Outline: TYPE = GGModelTypes.Outline; OutlineDescriptor: TYPE = REF OutlineDescriptorObj; OutlineDescriptorObj: TYPE = GGModelTypes.OutlineDescriptorObj; OverlapOrder: TYPE = {incr, decr}; Point: TYPE = GGBasicTypes.Point; ScalarButtonClient: TYPE = AtomButtons.ScalarButtonClient; ScalarButtonHandle: TYPE = AtomButtons.ScalarButtonHandle; Scene: TYPE = GGModelTypes.Scene; SegAndIndex: TYPE = GGSequence.SegAndIndex; Segment: TYPE = GGSegmentTypes.Segment; SegmentGenerator: TYPE = GGModelTypes.SegmentGenerator; Sequence: TYPE = GGModelTypes.Sequence; SequenceGenerator: TYPE = GGModelTypes.SequenceGenerator; SequenceOfReal: TYPE = GGBasicTypes.SequenceOfReal; SequenceOfRealObj: TYPE = GGBasicTypes.SequenceOfRealObj; Slice: TYPE = GGModelTypes.Slice; SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor; SliceDescriptorGenerator: TYPE = GGModelTypes.SliceDescriptorGenerator; SliceGenerator: TYPE = GGModelTypes.SliceGenerator; SliceParts: TYPE = GGModelTypes.SliceParts; StrokeEnd: TYPE = Imager.StrokeEnd; StrokeJoint: TYPE = Imager.StrokeJoint; Traj: TYPE = GGModelTypes.Traj; TrajEnd: TYPE = GGModelTypes.TrajEnd; TrajGenerator: TYPE = GGModelTypes.TrajGenerator; TriggerBag: TYPE = GGInterfaceTypes.TriggerBag; TwoState: TYPE = AtomButtons.TwoState; Vector: TYPE = GGBasicTypes.Vector; Viewer: TYPE = ViewerClasses.Viewer; pointsPerIn: REAL = 72.0; pointsPerCm: REAL = 72.0/2.54; Problem: SIGNAL [msg: Rope.ROPE] = Feedback.Problem; Top: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; scene: Scene _ ggData.scene; selected: LIST OF Slice _ OrderedSelectionList[ggData, decr]; FOR slice: LIST OF Slice _ selected, slice.rest UNTIL slice=NIL DO GGScene.DeleteSlice[scene, slice.first]; GGScene.AddSlice[scene, slice.first, -1]; ENDLOOP; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedInPlace, ggData: ggData, remake: none, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; }; Bottom: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; scene: Scene _ ggData.scene; selected: LIST OF Slice _ OrderedSelectionList[ggData, incr]; FOR slice: LIST OF Slice _ selected, slice.rest UNTIL slice=NIL DO GGScene.DeleteSlice[scene, slice.first]; GGScene.AddSlice[scene, slice.first, 0]; ENDLOOP; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedInPlace, ggData: ggData, remake: none, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; }; UpOne: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; selected: LIST OF Slice _ OrderedSelectionList[ggData, incr]; FOR list: LIST OF Slice _ selected, list.rest UNTIL list=NIL DO GGScene.UpOne[ggData.scene, list.first]; ENDLOOP; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedInPlace, ggData: ggData, remake: none, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; }; DownOne: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; selected: LIST OF Slice _ OrderedSelectionList[ggData, decr]; FOR list: LIST OF Slice _ selected, list.rest UNTIL list=NIL DO GGScene.DownOne[ggData.scene, list.first]; ENDLOOP; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedInPlace, ggData: ggData, remake: none, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; }; OrderedSelectionList: PROC [ggData: GGData, order: OverlapOrder] RETURNS [list: LIST OF Slice] = { sliceGen: SliceGenerator _ GGScene.TopLevelEntitiesInScene[ggData.scene]; FOR slice: Slice _ GGScene.NextSlice[sliceGen], GGScene.NextSlice[sliceGen] UNTIL slice = NIL DO IF GGSelect.IsSelectedInPart[slice, ggData.scene, normal] THEN list _ CONS[slice, list]; ENDLOOP; IF order=decr THEN list _ ReverseList[list]; }; ReverseList: PROC [list: LIST OF Slice] RETURNS [val: LIST OF Slice] = { val _ NIL; UNTIL list = NIL DO val _ CONS[list.first, val]; list _ list.rest; ENDLOOP; RETURN[val]; }; LineWidth: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; sliceDescGen: SliceDescriptorGenerator; bBox, thisBox: BoundBox; bBoxes: LIST OF BoundBox _ NIL; strokeWidth: REAL _ WITH event.rest.first SELECT FROM real: REF REAL => real^, int: REF INT => REAL[int^], ENDCASE => -1.0; IF strokeWidth < 0.0 THEN { Feedback.AppendHerald[ggData.feedback, "Select a positive number for stroke width", oneLiner]; RETURN; }; Feedback.Append[ggData.feedback, IO.PutFR["Selected objects will have stroke width %g", [real[strokeWidth]]], oneLiner]; bBox _ GGBoundBox.BoundBoxOfSelected[ggData.scene, normal]; sliceDescGen _ GGSelect.SelectedSlices[ggData.scene, normal]; FOR sliceD: SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen] UNTIL sliceD=NIL DO thisBox _ sliceD.slice.class.setStrokeWidth[sliceD.slice, sliceD.parts, strokeWidth]; IF thisBox # NIL THEN bBoxes _ CONS[thisBox, bBoxes]; ENDLOOP; ggData.refresh.startBoundBox^ _ GGBoundBox.BoundBoxOfBoxes[bBoxes]^; GGBoundBox.EnlargeByBox[ggData.refresh.startBoundBox, bBox]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: none, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE]; }; LineEnds: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; sliceDescGen: SliceDescriptorGenerator; bBox: BoundBox; strokeEnd: StrokeEnd; argRope: Rope.ROPE _ NARROW[event.rest.first]; SELECT TRUE FROM Rope.Equal[argRope, "square", FALSE] => strokeEnd _ square; Rope.Equal[argRope, "butt", FALSE] => strokeEnd _ butt; Rope.Equal[argRope, "round", FALSE] => strokeEnd _ round; ENDCASE => { Feedback.AppendHerald[ggData.feedback, "Select square, butt, or round for stroke end", oneLiner]; RETURN; }; Feedback.Append[ggData.feedback, IO.PutFR["Selected objects will have stroke end %g", [rope[argRope]]], oneLiner]; bBox _ GGBoundBox.BoundBoxOfSelected[ggData.scene, normal]; sliceDescGen _ GGSelect.SelectedSlices[ggData.scene, normal]; FOR sliceD: SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen] UNTIL sliceD=NIL DO sliceD.slice.class.setStrokeEnd[sliceD.slice, sliceD.parts, strokeEnd]; ENDLOOP; GGBoundBox.EnlargeByBox[bBox: bBox, by: GGBoundBox.BoundBoxOfSelected[ggData.scene, normal]]; ggData.refresh.startBoundBox^ _ bBox^; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: none, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE]; }; TrajJoints: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; sliceDescGen: SliceDescriptorGenerator; bBox: BoundBox; strokeJoint: StrokeJoint; argRope: Rope.ROPE _ NARROW[event.rest.first]; SELECT TRUE FROM Rope.Equal[argRope, "round", FALSE] => strokeJoint _ round; Rope.Equal[argRope, "miter", FALSE] => strokeJoint _ miter; Rope.Equal[argRope, "bevel", FALSE] => strokeJoint _ bevel; ENDCASE => { Feedback.AppendHerald[ggData.feedback, "Select round, miter, or bevel for trajectory joints", oneLiner]; RETURN; }; Feedback.Append[ggData.feedback, IO.PutFR["Selected trajectories will have stroke joints %g", [rope[argRope]]], oneLiner]; bBox _ GGBoundBox.BoundBoxOfSelected[ggData.scene, normal]; sliceDescGen _ GGSelect.SelectedSlices[ggData.scene, normal]; FOR sliceD: SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen] UNTIL sliceD=NIL DO sliceD.slice.class.setStrokeJoint[sliceD.slice, sliceD.parts, strokeJoint]; ENDLOOP; GGBoundBox.EnlargeByBox[bBox: bBox, by: GGBoundBox.BoundBoxOfSelected[ggData.scene, normal]]; ggData.refresh.startBoundBox^ _ bBox^; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: none, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE]; }; DashesFromSelection: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ENABLE GGParseIn.SyntaxError => GOTO SyntaxError; ggData: GGData _ NARROW[clientData]; argRope: Rope.ROPE _ NARROW[event.rest.first]; argStream: IO.STREAM _ IO.RIS[argRope]; pattern: SequenceOfReal; offset, length: REAL; pattern _ GGParseIn.ReadArrayOfReal[argStream]; offset _ GGParseIn.ReadBlankAndReal[argStream]; length _ GGParseIn.ReadBlankAndReal[argStream]; BEGIN sliceDescGen: SliceDescriptorGenerator; bBox: BoundBox; bBox _ GGBoundBox.BoundBoxOfSelected[ggData.scene, normal]; sliceDescGen _ GGSelect.SelectedSlices[ggData.scene, normal]; FOR sliceD: SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen] UNTIL sliceD=NIL DO sliceD.slice.class.setDashed[sliceD.slice, sliceD.parts, TRUE, pattern, offset, length]; ENDLOOP; GGBoundBox.EnlargeByBox[bBox: bBox, by: GGBoundBox.BoundBoxOfSelected[ggData.scene, normal]]; ggData.refresh.startBoundBox^ _ bBox^; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: none, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE]; END; EXITS SyntaxError => {Feedback.AppendHerald[NARROW[clientData, GGData].feedback, "Select a legal dash specification", oneLiner]; Feedback.Blink[NARROW[clientData, GGData].feedback];}; }; DashesOff: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; sliceDescGen: SliceDescriptorGenerator; bBox: BoundBox; bBox _ GGBoundBox.BoundBoxOfSelected[ggData.scene, normal]; sliceDescGen _ GGSelect.SelectedSlices[ggData.scene, normal]; FOR sliceD: SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen] UNTIL sliceD=NIL DO sliceD.slice.class.setDashed[sliceD.slice, sliceD.parts, FALSE]; ENDLOOP; GGBoundBox.EnlargeByBox[bBox: bBox, by: GGBoundBox.BoundBoxOfSelected[ggData.scene, normal]]; ggData.refresh.startBoundBox^ _ bBox^; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: none, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE]; }; PrintStrokeValues: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; dashed: BOOL _ FALSE; pattern: SequenceOfReal; offset, length: REAL; sliceD: SliceDescriptor; sliceDescGen: SliceDescriptorGenerator _ GGSelect.SelectedSlices[ggData.scene, normal]; IF sliceDescGen=NIL OR (sliceD _ GGSelect.NextSliceDescriptor[sliceDescGen])=NIL OR GGSelect.NextSliceDescriptor[sliceDescGen]#NIL THEN Feedback.AppendHerald[ggData.feedback, "Select exactly one entity for Show Stroke Values", oneLiner] ELSE { [dashed, pattern, offset, length] _ sliceD.slice.class.getDashed[sliceD.slice, sliceD.parts]; Feedback.Append[ggData.feedback, IO.PutFR["Width: %g End: %g Joint: %g Dashes: %g", [real[sliceD.slice.class.getStrokeWidth[sliceD.slice, sliceD.parts]]], [rope[GetEndRope[sliceD.slice.class.getStrokeEnd[sliceD.slice, sliceD.parts]]]], [rope[GetJointRope[sliceD.slice.class.getStrokeJoint[sliceD.slice, sliceD.parts]]]], [rope[GetDashesRope[dashed, pattern, offset, length]]] ], oneLiner]; }; }; GetDashesRope: PROC [dashed: BOOL, pattern: SequenceOfReal, offset, length: REAL] RETURNS [r: Rope.ROPE] = { s: IO.STREAM; IF NOT dashed THEN RETURN["Not Dashed"]; s _ IO.ROS[]; s.PutChar[ '[ ]; -- open bracket FOR index: NAT IN [0..pattern.len) DO -- list of reals s.PutF["%g ", [real[pattern[index]]] ]; ENDLOOP; s.PutF["] %g %g", [real[offset]], [real[length]] ]; r _ IO.RopeFromROS[s]; }; GetEndRope: PROC [strokeEnd: StrokeEnd] RETURNS [r: Rope.ROPE] = { r _ SELECT strokeEnd FROM round => "round", butt => "butt", square => "square", ENDCASE => "none"; }; GetJointRope: PROC [jointEnd: StrokeJoint] RETURNS [r: Rope.ROPE] = { r _ SELECT jointEnd FROM round => "round", bevel => "bevel", miter => "miter", ENDCASE => "none"; }; SelectMatchingWidth: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { epsilon: REAL = 1.0E-3; ggData: GGData _ NARROW[clientData]; width: REAL _ WITH event.rest.first SELECT FROM real: REF REAL => real^, int: REF INT =>REAL [int^], ENDCASE => -1.0; sliceGen: SliceGenerator _ GGScene.SlicesInScene[ggData.scene]; IF width<0.0 THEN GOTO SyntaxError; GGSelect.DeselectAll[ggData.scene, normal]; FOR slice: Slice _ GGScene.NextSlice[sliceGen], GGScene.NextSlice[sliceGen] UNTIL slice = NIL DO -- for every slice in the scene WidthProc: WalkProc = { RETURN [seg.strokeWidth=width OR ABS[seg.strokeWidth-width] {Feedback.AppendHerald[NARROW[clientData, GGData].feedback, "Select a real number >=0.0 for matching width", oneLiner]; Feedback.Blink[NARROW[clientData, GGData].feedback];}; }; SelectMatchingDashes: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ENABLE GGParseIn.SyntaxError => GOTO SyntaxError; ggData: GGData _ NARROW[clientData]; argRope: Rope.ROPE _ NARROW[event.rest.first]; argStream: IO.STREAM _ IO.RIS[argRope]; dashed: BOOL _ FALSE; pattern: SequenceOfReal; offset, length: REAL; sliceGen: SliceGenerator _ GGScene.SlicesInScene[ggData.scene]; pattern _ GGParseIn.ReadArrayOfReal[argStream]; offset _ GGParseIn.ReadBlankAndReal[argStream]; length _ GGParseIn.ReadBlankAndReal[argStream]; GGSelect.DeselectAll[ggData.scene, normal]; FOR slice: Slice _ GGScene.NextSlice[sliceGen], GGScene.NextSlice[sliceGen] UNTIL slice = NIL DO -- for every slice in the scene DashProc: WalkProc = { keep _ FALSE; IF seg.dashed AND seg.pattern.len=pattern.len AND seg.offset=offset AND seg.length=length THEN { FOR index: NAT IN [0..pattern.len) DO -- list of reals IF seg.pattern[index]#pattern[index] THEN GOTO ExitLoop; ENDLOOP; RETURN[TRUE]; -- if you get here, you matched exactly EXITS ExitLoop => NULL; }; }; sliceD: SliceDescriptor _ GGSlice.WalkSegments[slice, DashProc]; -- get a descriptor of matching parts GGSelect.SelectSlice[sliceD, ggData.scene, normal]; -- and select it ENDLOOP; Feedback.PutFHerald[ggData.feedback, oneLiner, "Segments of Dashes %g selected", [rope[argRope]] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, ggData: ggData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; EXITS SyntaxError => {Feedback.AppendHerald[NARROW[clientData, GGData].feedback, "Select a legal dash pattern for matching dashes", oneLiner]; Feedback.Blink[NARROW[clientData, GGData].feedback];}; }; SetDefaultStrokeValues: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; dashed: BOOL _ FALSE; pattern: SequenceOfReal; offset, length, strokeWidth: REAL; strokeJoint: StrokeJoint; strokeEnd: StrokeEnd; sliceD: SliceDescriptor; sliceDescGen: SliceDescriptorGenerator _ GGSelect.SelectedSlices[ggData.scene, normal]; IF sliceDescGen=NIL OR (sliceD _ GGSelect.NextSliceDescriptor[sliceDescGen])=NIL OR GGSelect.NextSliceDescriptor[sliceDescGen]#NIL THEN Feedback.AppendHerald[ggData.feedback, "Select exactly one entity for Default Stroke Values", oneLiner] ELSE { [dashed, pattern, offset, length] _ sliceD.slice.class.getDashed[sliceD.slice, sliceD.parts]; strokeWidth _ sliceD.slice.class.getStrokeWidth[sliceD.slice, sliceD.parts]; strokeJoint _ sliceD.slice.class.getStrokeJoint[sliceD.slice, sliceD.parts]; strokeEnd _ sliceD.slice.class.getStrokeEnd[sliceD.slice, sliceD.parts]; }; ggData.defaults^ _ [strokeWidth: strokeWidth, strokeJoint: strokeJoint, strokeEnd: strokeEnd, dashed: dashed, pattern: GGUtility.CopyPattern[pattern], offset: offset, length: length, strokeColor: ggData.defaults.strokeColor, fillColor: ggData.defaults.fillColor, font: ggData.defaults.font]; Feedback.Append[ggData.feedback, IO.PutFR["Default Stroke Values: Width: %g End: %g Joint: %g Dashes: %g", [real[strokeWidth]], [rope[GetEndRope[strokeEnd]]], [rope[GetJointRope[strokeJoint]]], [rope[GetDashesRope[dashed, pattern, offset, length]]] ], oneLiner]; }; ShowDefaultStrokeValues: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; Feedback.Append[ggData.feedback, IO.PutFR["Default Stroke Values: Width: %g End: %g Joint: %g Dashes: %g", [real[ggData.defaults.strokeWidth]], [rope[GetEndRope[ggData.defaults.strokeEnd]]], [rope[GetJointRope[ggData.defaults.strokeJoint]]], [rope[GetDashesRope[ggData.defaults.dashed, ggData.defaults.pattern, ggData.defaults.offset, ggData.defaults.length]]] ], oneLiner]; }; Arrows: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; arrowType: INT _ NARROW[event.rest.first, REF INT]^; leftArrows, rightArrows: BOOL; sequenceGen: SequenceGenerator; loIsLeft: BOOL; traj: Traj; SELECT arrowType FROM 0 => { Feedback.PutF[ggData.feedback, oneLiner, "Selected objects will have no arrows."]; leftArrows _ FALSE; rightArrows _ FALSE; }; 1 => { Feedback.PutF[ggData.feedback, oneLiner, "Selected objects will have left/down arrows."]; leftArrows _ TRUE; rightArrows _ FALSE; }; 2 => { Feedback.PutF[ggData.feedback, oneLiner, "Selected objects will have right/up arrows."]; leftArrows _ FALSE; rightArrows _ TRUE; }; 3 => { Feedback.PutF[ggData.feedback, oneLiner, "Selected objects will have arrows on both ends."]; leftArrows _ TRUE; rightArrows _ TRUE; }; ENDCASE => { Feedback.PutF[ggData.feedback, oneLiner, "Illegal argument to Arrows."]; RETURN; }; sequenceGen _ GGSelect.SelectedSequences[ggData.scene, normal]; FOR seq: Sequence _ GGSequence.NextSequence[sequenceGen], GGSequence.NextSequence[sequenceGen] UNTIL seq = NIL DO traj _ seq.traj; loIsLeft _ LoIsLeft[traj]; IF loIsLeft THEN GGTraj.SetArrows[seq.traj, leftArrows, rightArrows] ELSE GGTraj.SetArrows[seq.traj, rightArrows, leftArrows]; ENDLOOP; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedInPlace, ggData: ggData, remake: none, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE]; }; LoIsLeft: PROC [traj: Traj] RETURNS [BOOL] = { loPoint, hiPoint: Point; loPoint _ GGTraj.FetchJointPos[traj, 0]; hiPoint _ GGTraj.LastJointPos[traj]; SELECT TRUE FROM loPoint.x < hiPoint.x => RETURN[TRUE]; loPoint.x = hiPoint.x AND loPoint.y <= hiPoint.y => RETURN[TRUE]; ENDCASE => RETURN[FALSE]; }; MakeHot: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; sliceDescGen: SliceDescriptorGenerator _ GGSelect.SelectedSlices[ggData.scene, normal]; FOR sliceD: SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen] UNTIL sliceD=NIL DO GGSelect.SelectSlice[sliceD, ggData.scene, hot]; ENDLOOP; sliceDescGen _ GGSelect.SelectedSlices[ggData.scene, hot]; FOR sliceD: SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen] UNTIL sliceD=NIL DO sliceFeature: FeatureData _ GGAlign.AddHotSlice[sliceD, ggData.hitTest.triggerBag]; -- fix triggerBag alignObjects: LIST OF FeatureData; alignObjects _ GGAlign.IncrementalFilters[sliceFeature, ggData.hitTest, NOT GGState.ShowAlignments[ggData], ggData.hitTest.alignBag]; GGRefresh.NoteNewForeground[alignObjects, ggData]; -- fix foreground plane ENDLOOP; GGWindow.RestoreScreenAndInvariants[paintAction: $SequencesMadeHot, ggData: ggData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; MakeAllHot: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; triggerBag: TriggerBag _ ggData.hitTest.triggerBag; alignBag: AlignBag _ ggData.hitTest.alignBag; sliceDescGen: SliceDescriptorGenerator; sliceGen: SliceGenerator _ GGScene.TopLevelSlicesInScene[ggData.scene]; FOR slice: Slice _ GGScene.NextSlice[sliceGen], GGScene.NextSlice[sliceGen] UNTIL slice = NIL DO sliceD: SliceDescriptor _ slice.class.newParts[slice, NIL, topLevel]; GGSelect.SelectSlice[sliceD, ggData.scene, hot]; ENDLOOP; sliceDescGen _ GGSelect.SelectedSlices[ggData.scene, hot]; -- expects only outline types !! FOR sliceD: SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen] UNTIL sliceD=NIL DO feature: FeatureData _ GGAlign.AddHotSlice[sliceD, triggerBag]; alignObjects: LIST OF FeatureData; alignObjects _ GGAlign.IncrementalFilters[feature, ggData.hitTest, NOT GGState.ShowAlignments[ggData], alignBag]; GGRefresh.NoteNewForeground[alignObjects, ggData]; ENDLOOP; GGWindow.RestoreScreenAndInvariants[paintAction: $SequencesMadeHot, ggData: ggData, remake: triggerBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; MakeCold: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; sliceDescGen: SliceDescriptorGenerator _ GGSelect.SelectedSlices[ggData.scene, normal]; FOR sliceD: SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen] UNTIL sliceD=NIL DO GGSelect.DeselectSlice[sliceD.slice, sliceD.parts, ggData.scene, hot]; ENDLOOP; GGWindow.RestoreScreenAndInvariants[paintAction: $SequencesMadeCold, ggData: ggData, remake: triggerBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; MakeAllCold: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; sliceDescGen: SliceDescriptorGenerator _ GGSelect.SelectedSlices[ggData.scene, hot]; FOR sliceD: SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen] UNTIL sliceD=NIL DO GGSelect.DeselectSlice[sliceD.slice, sliceD.parts, ggData.scene, hot]; ENDLOOP; GGWindow.RestoreScreenAndInvariants[paintAction: $SequencesMadeCold, ggData: ggData, remake: triggerBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; ShowHot: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; ggData.camera.hideHot _ FALSE; GGWindow.RestoreScreenAndInvariants[paintAction: $SequencesMadeHot, ggData: ggData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; HideHot: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; ggData.camera.hideHot _ TRUE; GGWindow.RestoreScreenAndInvariants[paintAction: $SequencesMadeCold, ggData: ggData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; DropAnchor: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; GGCaret.Copy[ggData.anchor, ggData.caret]; [] _ GGAlign.CreateAnchorTrigger[ggData.anchor, ggData.hitTest.triggerBag]; -- anchor can trigger [] _ GGAlign.CreateAnchorTrigger[ggData.anchor, ggData.hitTest.sceneBag]; -- anchor is itself gravity active GGWindow.RestoreScreenAndInvariants[paintAction: $AnchorAdded, ggData: ggData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; KillAnchor: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; IF GGCaret.Exists[ggData.anchor] THEN { GGCaret.Kill[ggData.anchor]; GGAlign.RemoveAnchorTrigger[ggData.hitTest.triggerBag]; -- don't trigger GGAlign.RemoveAnchorTrigger[ggData.hitTest.sceneBag]; -- don't be gravity active GGWindow.RestoreScreenAndInvariants[paintAction: $AnchorRemoved, ggData: ggData, remake: triggerBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; }; StandardAlignments: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; StandardSlopes[clientData, event]; StandardAngles[clientData, event]; StandardRadii[clientData, event]; StandardDistances[clientData, event]; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsDeselected, ggData: ggData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; AllAlignmentsOff: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; SlopePrompt[ggData, event]; AnglePrompt[ggData, event]; RadiusPrompt[ggData, event]; DistancePrompt[ggData, event]; IF AtomButtons.GetButtonState[ggData.hitTest.midpointButton] = on THEN ToggleMidpoints[ggData, NIL]; }; InitializeAlignments: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; AllAlignmentsOff[ggData, NIL]; IF AtomButtons.GetButtonState[ggData.refresh.alignments] = off THEN ToggleAlignments[ggData, NIL]; IF AtomButtons.GetButtonState[ggData.hitTest.gravButton] = off THEN ToggleGravity[ggData, NIL]; IF Rope.Equal[ggData.hitTest.gravityTypeMenu.flipLabel.name, "StrictDistance", TRUE] THEN GravityChoiceChange[ggData, LIST[$GravityChoiceChange, $FlipForward]]; InchScaleUnit[ggData, NIL]; IF AtomButtons.GetButtonState[ggData.hitTest.heuristicsButton] = off THEN ToggleHeuristics[ggData, NIL]; }; GravityChoiceChange: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; info: AtomButtons.EnumTypeRef _ ggData.hitTest.gravityTypeMenu; name: Rope.ROPE; IF event.rest.first = $FlipForward THEN AtomButtons.TimeToFlipThru[LIST[$FlipForward, info]] ELSE AtomButtons.TimeToFlipThru[LIST[$FlipBackward, info]]; name _ info.flipLabel.name; SELECT TRUE FROM Rope.Equal[name, "StrictDistance", TRUE] => ggData.hitTest.gravityType _ strictDistance; Rope.Equal[name, "PreferPoints", TRUE] => ggData.hitTest.gravityType _ innerCircle; ENDCASE => ERROR; UpdateGravityChoice[ggData]; }; GravityExtentChange: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; ged: GGInterfaceTypes.GravityExtentData _ NARROW[GraphicsButton.GetValue[ggData.hitTest.gravityExtentButton].buttonData]; extent: REAL _ ged.extent; success: BOOL _ TRUE; SELECT event.rest.first FROM $ValueUp => { IF extent < 18432.0 THEN extent _ extent*2.0 ELSE { Feedback.PutF[ggData.feedback, oneLiner, "Can't extend gravity further than 256 inches."]; Feedback.Blink[ggData.feedback]; success _ FALSE; }; }; $ValueDown => extent _ extent/2.0; ENDCASE => extent _ GGEditTool.GetDefaultGravityExtent[]; IF success THEN { GGWindow.SetGravityExtent[ggData, extent/72.0]; }; }; UpdateGravityChoice: PROC [clientData: REF ANY] = { ggData: GGData _ NARROW[clientData]; SELECT AtomButtons.GetButtonState[ggData.hitTest.gravButton] FROM on => SELECT ggData.hitTest.gravityType FROM strictDistance => GGWindow.SetCursorLooks[strictDistance, ggData]; innerCircle => GGWindow.SetCursorLooks[innerCircle, ggData]; ENDCASE => ERROR; -- SHOULD NOT USE off STATE off => GGWindow.SetCursorLooks[off, ggData]; ENDCASE => ERROR; }; ScreenChoiceChange: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { name: Rope.ROPE; ggData: GGData _ NARROW[clientData]; info: AtomButtons.EnumTypeRef _ ggData.refresh.screenStyle; IF event.rest.first = $FlipForward THEN AtomButtons.TimeToFlipThru[LIST[$FlipForward, info]] ELSE AtomButtons.TimeToFlipThru[LIST[$FlipBackward, info]]; name _ info.flipLabel.name; SELECT TRUE FROM Rope.Equal[name, "PrintFonts", TRUE] => { ggData.camera.quality _ fast; ggData.camera.displayStyle _ print; }; Rope.Equal[name, "ScreenFonts", TRUE] => { ggData.camera.quality _ fast; ggData.camera.displayStyle _ screen; }; Rope.Equal[name, "WYSIWYG", TRUE] => { ggData.camera.quality _ quality; ggData.camera.displayStyle _ print; }; ENDCASE => ERROR; GGWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireViewer, ggData: ggData, remake: none, backgndOK: FALSE, edited: FALSE, okToClearFeedback: TRUE]; }; ToggleShowColors: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; stateInfo: TwoState _ ggData.refresh.showColors; AtomButtons.SwitchState[stateInfo]; GGWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireViewer, ggData: ggData, remake: none, backgndOK: FALSE, edited: FALSE, okToClearFeedback: TRUE]; }; ToggleAlignments: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; stateInfo: TwoState _ ggData.refresh.alignments; AtomButtons.SwitchState[stateInfo]; SELECT AtomButtons.GetButtonState[stateInfo] FROM on => ggData.camera.hideAlignments _ FALSE; off => ggData.camera.hideAlignments _ TRUE; ENDCASE => ERROR; GGWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireViewer, ggData: ggData, remake: objectBag, backgndOK: FALSE, edited: FALSE, okToClearFeedback: TRUE]; }; ToggleMidpoints: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; stateInfo: TwoState _ ggData.hitTest.midpointButton; AtomButtons.SwitchState[stateInfo]; GGWindow.RestoreScreenAndInvariants[paintAction: $None, ggData: ggData, remake: triggerBag, backgndOK: FALSE, edited: FALSE, okToClearFeedback: TRUE]; }; SetMidpointsInternal: PUBLIC PROC [ggData: GGData, on: BOOL] = { stateInfo: TwoState _ ggData.hitTest.midpointButton; wasOn: BOOL _ AtomButtons.GetButtonState[stateInfo] = on; IF wasOn # on THEN AtomButtons.SwitchState[stateInfo]; }; ToggleHeuristics: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; stateInfo: TwoState _ ggData.hitTest.heuristicsButton; AtomButtons.SwitchState[stateInfo]; GGWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireViewer, ggData: ggData, remake: triggerBag, backgndOK: FALSE, edited: FALSE, okToClearFeedback: TRUE]; }; ToggleGravity: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; stateInfo: TwoState _ ggData.hitTest.gravButton; AtomButtons.SwitchState[stateInfo]; UpdateGravityChoice[ggData]; }; ScaleUnitAux: PROC [ggData: GGData, distance: REAL] = { IF distance <=0.0 THEN { Feedback.AppendHerald[ggData.feedback, "Zero or Illegal unit value. Set Units ignored.", oneLiner]; Feedback.Blink[ggData.feedback]; } ELSE { ggData.hitTest.scaleUnit _ distance; -- in screen dots PrintScaleUnit[event: NIL, clientData: ggData]; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, ggData: ggData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; }; ScaleUnitFromSegment: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; seqGen: SequenceGenerator; segGen: SegmentGenerator; seq, next: Sequence; seg, nextSeg: Segment; distance: REAL; seqGen _ GGSelect.SelectedSequences[ggData.scene, normal]; seq _ GGSequence.NextSequence[seqGen]; next _ GGSequence.NextSequence[seqGen]; IF seq = NIL OR next # NIL THEN { Feedback.AppendHerald[ggData.feedback, "Select a single segment to set Units.", oneLiner]; Feedback.Blink[ggData.feedback]; RETURN; }; segGen _ GGSequence.SegmentsInSequence[seq]; seg _ GGSequence.NextSegment[segGen]; nextSeg _ GGSequence.NextSegment[segGen]; IF seg = NIL OR nextSeg # NIL THEN { Feedback.AppendHerald[ggData.feedback, "Select a single segment to set Units.", oneLiner]; Feedback.Blink[ggData.feedback]; RETURN; }; distance _ Vectors2d.Distance[seg.lo, seg.hi]; ScaleUnitAux[ggData, distance]; }; ScaleUnitFromValue: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; radius: REAL _ GGViewerOps.GetPositiveReal[ggData.measure.radiusView, Real.LargestNumber]; -- ADDED. March 30, 1987. KAP. IF radius=Real.LargestNumber THEN { Feedback.Append[ggData.feedback, "Attempt to use illegal radius value", oneLiner]; Feedback.Blink[ggData.feedback]; RETURN; }; ggData.measure.radiusViewValue _ radius; -- ADDED. March 30, 1987. KAP. ScaleUnitAux[ggData, radius*ggData.hitTest.scaleUnit]; }; ScaleUnitFromSelection: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; distance: REAL _ WITH event.rest.first SELECT FROM real: REF REAL => real^, int: REF INT => REAL[int^], ENDCASE => -1.0; IF distance <= 0.0 THEN { Feedback.Append[ggData.feedback, "Select a positive real number (in inches) for the scale unit.", oneLiner]; Feedback.Blink[ggData.feedback]; } ELSE ScaleUnitAux[ggData, distance*72.0]; -- screen dots }; InchScaleUnit: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; ScaleUnitAux[ggData, pointsPerIn]; -- one inch in screen dots }; CentimeterScaleUnit: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; ScaleUnitAux[ggData, pointsPerCm]; -- 1 cm in screen dots }; PointsScaleUnit: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; ScaleUnitAux[ggData, 1]; -- 1 cm in screen dots }; PrintScaleUnit: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; Feedback.PutF[ggData.feedback, oneLiner, "Current scale is %g points = %g inches = %g centimeters", [real[ggData.hitTest.scaleUnit]], [real[ggData.hitTest.scaleUnit/pointsPerIn]], [real[ggData.hitTest.scaleUnit/pointsPerCm]] ]; }; StandardSlopes: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; SlopePrompt[ggData, event]; AtomButtons.BuildScalarButtons[ggData.hitTest.slopeHeader, ggData, GGUserInput.EventNotify, NIL, LIST[ ["", 999.9, LIST[$NoOp, NEW[REAL _ 999.9]], off], -- dummy first button ["150", 150.0, LIST[$ToggleSlope, NEW[REAL _ 150.0]], off], ["135", 135.0, LIST[$ToggleSlope, NEW[REAL _ 135.0]], off], ["120", 120.0, LIST[$ToggleSlope, NEW[REAL _ 120.0]], off], ["90", 90.0, LIST[$ToggleSlope, NEW[REAL _ 90.0]], off], ["60", 60.0, LIST[$ToggleSlope, NEW[REAL _ 60.0]], off], ["45", 45.0, LIST[$ToggleSlope, NEW[REAL _ 45.0]], off], ["30", 30.0, LIST[$ToggleSlope, NEW[REAL _ 30.0]], off], ["0", 0.0, LIST[$ToggleSlope, NEW[REAL _ 0.0]], off] ]]; IF event.first = $StandardSlopes THEN GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsDeselected, ggData: ggData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; SlopePrompt: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; firstButton: ScalarButtonClient _ ggData.hitTest.slopeHeader.scalarButtons; FOR thisButton: ScalarButtonClient _ firstButton, thisButton.next UNTIL thisButton = NIL DO IF thisButton.on THEN ToggleSlope[ggData, LIST[NIL, NEW[REAL _ thisButton.value]]]; ENDLOOP; }; AddSlope: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; oldFoundButton: AtomButtons.ScalarButtonClient; slope: REAL _ GGViewerOps.GetReal[ggData.measure.slopeView, Real.LargestNumber]; IF slope=Real.LargestNumber THEN { Feedback.AppendHerald[ggData.feedback, "Attempt to add illegal slope value", oneLiner]; Feedback.Blink[ggData.feedback]; RETURN; }; slope _ Angles2d.Normalize[slope]; IF slope<0.0 THEN slope _ slope+180.0; IF slope=360.0 THEN slope _ 0.0; IF RealFns.AlmostEqual[slope, ggData.measure.slopeViewValue, -10] THEN slope _ ggData.measure.slopeViewValue ELSE ggData.measure.slopeViewValue _ slope; oldFoundButton _ AtomButtons.AddValueSorted[clientData: ggData, scalarButtonHandle: ggData.hitTest.slopeHeader, value: [NIL, slope, LIST[$ToggleSlope, NEW[REAL _ slope]], on], order: decr]; IF oldFoundButton = NIL THEN { -- a new button was added GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, ggData: ggData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; }; AddSlopeInternal: PUBLIC PROC [ggData: GGData, degrees: REAL] = { oldFoundButton: AtomButtons.ScalarButtonClient; IF degrees<0.0 THEN degrees _ degrees+180.0; IF degrees=360.0 THEN degrees _ 0.0; ggData.measure.slopeViewValue _ degrees; oldFoundButton _ AtomButtons.AddValueSorted[clientData: ggData, scalarButtonHandle: ggData.hitTest.slopeHeader, value: [NIL, degrees, LIST[$ToggleSlope, NEW[REAL _ degrees]], off], order: decr]; }; DeleteSlope: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; prevButton: ScalarButtonClient _ ggData.hitTest.slopeHeader.scalarButtons; FOR thisButton: ScalarButtonClient _ prevButton.next, thisButton.next UNTIL thisButton = NIL DO IF thisButton.on THEN { TiogaButtons.DeleteButton[thisButton.button]; prevButton.next _ thisButton.next; } ELSE prevButton _ thisButton; ENDLOOP; ggData.measure.slopeViewValue _ Real.LargestNumber; -- invalidate cached value GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsDeselected, ggData: ggData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; GetSlope: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; oldFoundButton: AtomButtons.ScalarButtonClient; seqGen: SequenceGenerator; segGen: SegmentGenerator; seq, next: Sequence; degrees: REAL; seqGen _ GGSelect.SelectedSequences[ggData.scene, normal]; seq _ GGSequence.NextSequence[seqGen]; next _ GGSequence.NextSequence[seqGen]; IF seq = NIL OR next # NIL THEN { Feedback.Append[ggData.feedback, "Select a single sequence for a GetSlope.", oneLiner]; Feedback.Blink[ggData.feedback]; RETURN; }; segGen _ GGSequence.SegmentsInSequence[seq]; FOR seg: Segment _ GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen] UNTIL seg = NIL DO degrees _ GGMeasure.SlopeOfSegment[seg]; oldFoundButton _ AtomButtons.AddValueSorted[ggData, ggData.hitTest.slopeHeader, [NIL, degrees, LIST[$ToggleSlope, NEW[REAL _ degrees]], on], decr]; ENDLOOP; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, ggData: ggData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; ToggleSlope: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; firstButton, pushedButton: ScalarButtonClient; tiogaButton: TiogaButtons.TiogaButton; epsilon: REAL = 0.001; slope: REAL _ WITH event.rest.first SELECT FROM real: REF REAL => real^, int: REF INT => REAL[int^], ENDCASE => 999.0; firstButton _ ggData.hitTest.slopeHeader.scalarButtons; FOR thisButton: ScalarButtonClient _ firstButton, thisButton.next UNTIL thisButton = NIL DO IF ABS[thisButton.value-slope] < epsilon THEN { pushedButton _ thisButton; EXIT; }; REPEAT FINISHED => ERROR; ENDLOOP; tiogaButton _ pushedButton.button; IF pushedButton.on THEN { pushedButton.on _ FALSE; TiogaButtons.ChangeButtonLooks[tiogaButton, "", "b"]; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsDeselected, ggData: ggData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; } ELSE { pushedButton.on _ TRUE; TiogaButtons.ChangeButtonLooks[tiogaButton, "b", ""]; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, ggData: ggData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; }; StandardAngles: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; AnglePrompt[ggData, event]; AtomButtons.BuildScalarButtons[ggData.hitTest.angleHeader, ggData, GGUserInput.EventNotify, NIL, LIST[ ["", 999.9, LIST[$NoOp, NEW[REAL _ 999.9]], off], -- dummy first button ["90", 90.0, LIST[$ToggleAngle, NEW[REAL _ 90.0]], off], ["60", 60.0, LIST[$ToggleAngle, NEW[REAL _ 60.0]], off], ["45", 45.0, LIST[$ToggleAngle, NEW[REAL _ 45.0]], off], ["30", 30.0, LIST[$ToggleAngle, NEW[REAL _ 30.0]], off], ["0", 0.0, LIST[$ToggleAngle, NEW[REAL _ 0.0]], off], ["-30", -30.0, LIST[$ToggleAngle, NEW[REAL _ -30.0]], off], ["-45", -45.0, LIST[$ToggleAngle, NEW[REAL _ -45.0]], off], ["-60", -60.0, LIST[$ToggleAngle, NEW[REAL _ -60.0]], off], ["-90", -90.0, LIST[$ToggleAngle, NEW[REAL _ -90.0]], off] ]]; IF event.first = $StandardAngles THEN GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsDeselected, ggData: ggData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; AnglePrompt: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; firstButton: ScalarButtonClient _ ggData.hitTest.angleHeader.scalarButtons; FOR thisButton: ScalarButtonClient _ firstButton, thisButton.next UNTIL thisButton = NIL DO IF thisButton.on THEN ToggleAngle[ggData, LIST[NIL, NEW[REAL _ thisButton.value]]]; ENDLOOP; }; AddAngle: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; oldFoundButton: AtomButtons.ScalarButtonClient; angle: REAL _ GGViewerOps.GetReal[ggData.measure.angleView, Real.LargestNumber]; IF angle=Real.LargestNumber THEN { Feedback.AppendHerald[ggData.feedback, "Attempt to add illegal angle value", oneLiner]; Feedback.Blink[ggData.feedback]; RETURN; }; angle _ Angles2d.Normalize[angle]; IF angle=360.0 THEN angle _ 0.0; IF RealFns.AlmostEqual[angle, ggData.measure.angleViewValue, -10] THEN angle _ ggData.measure.angleViewValue ELSE ggData.measure.angleViewValue _ angle; oldFoundButton _ AtomButtons.AddValueSorted[clientData: ggData, scalarButtonHandle: ggData.hitTest.angleHeader, value: [NIL, angle, LIST[$ToggleAngle, NEW[REAL _ angle]], on], order: decr]; IF oldFoundButton = NIL THEN { -- a button was added GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, ggData: ggData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; } }; AddAngleInternal: PUBLIC PROC [ggData: GGData, degrees: REAL] = { oldFoundButton: AtomButtons.ScalarButtonClient; IF degrees=360.0 THEN degrees _ 0.0; ggData.measure.angleViewValue _ degrees; oldFoundButton _ AtomButtons.AddValueSorted[clientData: ggData, scalarButtonHandle: ggData.hitTest.angleHeader, value: [NIL, degrees, LIST[$ToggleAngle, NEW[REAL _ degrees]], off], order: decr]; }; DeleteAngle: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; prevButton: ScalarButtonClient _ ggData.hitTest.angleHeader.scalarButtons; FOR thisButton: ScalarButtonClient _ prevButton.next, thisButton.next UNTIL thisButton = NIL DO IF thisButton.on THEN { TiogaButtons.DeleteButton[thisButton.button]; prevButton.next _ thisButton.next; } ELSE prevButton _ thisButton; ENDLOOP; ggData.measure.angleViewValue _ Real.LargestNumber; -- invalidate cached value GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsDeselected, ggData: ggData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; GetAngle: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; oldFoundButton: AtomButtons.ScalarButtonClient; seqGen: SequenceGenerator; segGen: SegmentGenerator; seq, next: Sequence; firstSeg, secondSeg: Segment; degrees: REAL; seqGen _ GGSelect.SelectedSequences[ggData.scene, normal]; seq _ GGSequence.NextSequence[seqGen]; next _ GGSequence.NextSequence[seqGen]; IF seq = NIL OR next # NIL THEN { Feedback.Append[ggData.feedback, "Select a single sequence for a GetAngle.", oneLiner]; Feedback.Blink[ggData.feedback]; RETURN; }; segGen _ GGSequence.SegmentsInSequence[seq]; FOR seg: Segment _ GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen] UNTIL seg = NIL DO IF firstSeg=NIL THEN {firstSeg _ seg; LOOP;}; IF secondSeg=NIL THEN secondSeg _ seg; degrees _ GGMeasure.CounterClockwiseBetweenSegments[firstSeg, secondSeg]; oldFoundButton _ AtomButtons.AddValueSorted[ggData, ggData.hitTest.angleHeader, [NIL, degrees, LIST[$ToggleAngle, NEW[REAL _ degrees]], on], decr]; firstSeg _ secondSeg; secondSeg_ NIL; ENDLOOP; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, ggData: ggData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; ToggleAngle: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; firstButton, pushedButton: ScalarButtonClient; tiogaButton: TiogaButtons.TiogaButton; epsilon: REAL = 0.001; angle: REAL _ WITH event.rest.first SELECT FROM real: REF REAL => real^, int: REF INT => REAL[int^], ENDCASE => 999.0; firstButton _ ggData.hitTest.angleHeader.scalarButtons; FOR thisButton: ScalarButtonClient _ firstButton, thisButton.next UNTIL thisButton = NIL DO IF ABS[thisButton.value-angle] < epsilon THEN { pushedButton _ thisButton; EXIT; }; REPEAT FINISHED => ERROR; ENDLOOP; tiogaButton _ pushedButton.button; IF pushedButton.on THEN { pushedButton.on _ FALSE; TiogaButtons.ChangeButtonLooks[tiogaButton, "", "b"]; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsDeselected, ggData: ggData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; } ELSE { pushedButton.on _ TRUE; TiogaButtons.ChangeButtonLooks[tiogaButton, "b", ""]; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, ggData: ggData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; }; StandardRadii: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; RadiusPrompt[ggData, event]; AtomButtons.BuildScalarButtons[ ggData.hitTest.radiusHeader, ggData, GGUserInput.EventNotify, NIL, LIST[ ["", -9999.99, LIST[$NoOp, NEW[REAL _ -9999.99]], off], -- dummy first button ["1/18", 1.0/18.0, LIST[$ToggleRadius, NEW[REAL _ 1.0/18.0]], off], ["1/9", 1.0/9.0, LIST[$ToggleRadius, NEW[REAL _ 1.0/9.0]], off], ["1/8", 0.125, LIST[$ToggleRadius, NEW[REAL _ 0.125]], off], ["1/4", 0.25, LIST[$ToggleRadius, NEW[REAL _ 0.25]], off], ["1/3", 1.0/3.0, LIST[$ToggleRadius, NEW[REAL _ 1.0/3.0]], off], ["1/2", 0.5, LIST[$ToggleRadius, NEW[REAL _ 0.5]], off], ["2/3", 2.0/3.0, LIST[$ToggleRadius, NEW[REAL _ 2.0/3.0]], off], ["3/4", 0.75, LIST[$ToggleRadius, NEW[REAL _ 0.75]], off], ["1", 1.0, LIST[$ToggleRadius, NEW[REAL _ 1.0]], off], ["2", 2.0, LIST[$ToggleRadius, NEW[REAL _ 2.0]], off], ["4", 4.0, LIST[$ToggleRadius, NEW[REAL _ 4.0]], off] ]]; IF event.first = $StandardRadii THEN GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsDeselected, ggData: ggData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; RadiusPrompt: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; firstButton: ScalarButtonClient _ ggData.hitTest.radiusHeader.scalarButtons; FOR thisButton: ScalarButtonClient _ firstButton, thisButton.next UNTIL thisButton = NIL DO IF thisButton.on THEN ToggleRadius[ggData, LIST[NIL, NEW[REAL _ thisButton.value]]]; ENDLOOP; }; AddRadius: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; oldFoundButton: AtomButtons.ScalarButtonClient; radius: REAL _ GGViewerOps.GetPositiveReal[ggData.measure.radiusView, Real.LargestNumber]; IF radius=Real.LargestNumber THEN { Feedback.Append[ggData.feedback, "Attempt to add illegal radius value", oneLiner]; Feedback.Blink[ggData.feedback]; RETURN; }; IF RealFns.AlmostEqual[radius, ggData.measure.radiusViewValue, -10] THEN radius _ ggData.measure.radiusViewValue ELSE ggData.measure.radiusViewValue _ radius; oldFoundButton _ AtomButtons.AddValueSorted[clientData: ggData, scalarButtonHandle: ggData.hitTest.radiusHeader, value: [NIL, radius, LIST[$ToggleRadius, NEW[REAL _ radius]], on], order: incr]; IF oldFoundButton = NIL THEN { -- a new button is added GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, ggData: ggData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; }; AddRadiusInternal: PUBLIC PROC [ggData: GGData, radius: REAL] = { oldFoundButton: AtomButtons.ScalarButtonClient; ggData.measure.radiusViewValue _ radius; oldFoundButton _ AtomButtons.AddValueSorted[clientData: ggData, scalarButtonHandle: ggData.hitTest.radiusHeader, value: [NIL, radius, LIST[$ToggleRadius, NEW[REAL _ radius]], off], order: incr]; }; DeleteRadius: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; prevButton: ScalarButtonClient _ ggData.hitTest.radiusHeader.scalarButtons; FOR thisButton: ScalarButtonClient _ prevButton.next, thisButton.next UNTIL thisButton = NIL DO IF thisButton.on THEN { TiogaButtons.DeleteButton[thisButton.button]; prevButton.next _ thisButton.next; } ELSE prevButton _ thisButton; ENDLOOP; ggData.measure.radiusViewValue _ Real.LargestNumber; -- invalidate cached value GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsDeselected, ggData: ggData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; GetRadius: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; oldFoundButton: AtomButtons.ScalarButtonClient; sliceDescGen: GGModelTypes.SliceDescriptorGenerator; pointGen: GGModelTypes.PointGenerator; radius: REAL; pointAndDone: GGModelTypes.PointAndDone; sliceD, nextD: GGModelTypes.SliceDescriptor; p0, p1: Point; BEGIN sliceDescGen _ GGSelect.SelectedSlices[ggData.scene, normal]; sliceD _ GGSelect.NextSliceDescriptor[sliceDescGen]; IF sliceD = NIL THEN GOTO Problem ELSE { nextD _ GGSelect.NextSliceDescriptor[sliceDescGen]; IF nextD # NIL THEN GOTO Problem; pointGen _ sliceD.slice.class.pointsInDescriptor[sliceD]; pointAndDone _ sliceD.slice.class.nextPoint[pointGen]; IF pointAndDone.done THEN GOTO Problem; p0 _ pointAndDone.point; pointAndDone _ sliceD.slice.class.nextPoint[pointGen]; IF pointAndDone.done THEN GOTO Problem; p1 _ pointAndDone.point; pointAndDone _ sliceD.slice.class.nextPoint[pointGen]; IF NOT pointAndDone.done THEN GOTO Problem; }; EXITS Problem => { Feedback.Append[ggData.feedback, "Select a single segment for GetRadius.", oneLiner]; Feedback.Blink[ggData.feedback]; RETURN; }; END; radius _ GGMeasure.DistanceBetweenPoints[p0, p1]/ggData.hitTest.scaleUnit; oldFoundButton _ AtomButtons.AddValueSorted[ggData, ggData.hitTest.radiusHeader, [NIL, radius, LIST[$ToggleRadius, NEW[REAL _ radius]], on], incr]; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, ggData: ggData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; ToggleRadius: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; firstButton, pushedButton: ScalarButtonClient; tiogaButton: TiogaButtons.TiogaButton; epsilon: REAL = 0.001; radius: REAL _ WITH event.rest.first SELECT FROM real: REF REAL => real^, int: REF INT => REAL[int^], ENDCASE => 999.0; firstButton _ ggData.hitTest.radiusHeader.scalarButtons; FOR thisButton: ScalarButtonClient _ firstButton, thisButton.next UNTIL thisButton = NIL DO IF ABS[thisButton.value-radius] < epsilon THEN { pushedButton _ thisButton; EXIT; }; REPEAT FINISHED => ERROR; ENDLOOP; tiogaButton _ pushedButton.button; IF pushedButton.on THEN { pushedButton.on _ FALSE; TiogaButtons.ChangeButtonLooks[tiogaButton, "", "b"]; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsDeselected, ggData: ggData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; } ELSE { pushedButton.on _ TRUE; TiogaButtons.ChangeButtonLooks[tiogaButton, "b", ""]; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, ggData: ggData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; }; StandardDistances: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; DistancePrompt[ggData, event]; AtomButtons.BuildScalarButtons[ggData.hitTest.distanceHeader, ggData, GGUserInput.EventNotify, NIL, LIST[ ["", -9999.99, LIST[$NoOp, NEW[REAL _ -9999.99]], off], -- dummy first button ["0", 0.0, LIST[$ToggleDistance, NEW[REAL _ 0.0]], off], ["1/18", 1.0/18.0, LIST[$ToggleDistance, NEW[REAL _ 1.0/18.0]], off], ["1/9", 1.0/9.0, LIST[$ToggleDistance, NEW[REAL _ 1.0/9.0]], off], ["1/2", 0.5, LIST[$ToggleDistance, NEW[REAL _ 0.5]], off], ["1", 1.0, LIST[$ToggleDistance, NEW[REAL _ 1.0]], off] ]]; IF event.first = $StandardDistances THEN GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsDeselected, ggData: ggData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; DistancePrompt: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; firstButton: ScalarButtonClient _ ggData.hitTest.distanceHeader.scalarButtons; FOR thisButton: ScalarButtonClient _ firstButton, thisButton.next UNTIL thisButton = NIL DO IF thisButton.on THEN ToggleDistance[ggData, LIST[NIL, NEW[REAL _ thisButton.value]]]; ENDLOOP; }; AddDistance: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; oldFoundButton: AtomButtons.ScalarButtonClient; distance: REAL _ GGViewerOps.GetReal[ggData.measure.lineDistView, Real.LargestNumber]; IF distance=Real.LargestNumber THEN { Feedback.Append[ggData.feedback, "Attempt to add illegal line distance value", oneLiner]; Feedback.Blink[ggData.feedback]; RETURN; }; IF RealFns.AlmostEqual[distance, ggData.measure.lineDistViewValue, -10] THEN distance _ ggData.measure.lineDistViewValue ELSE ggData.measure.lineDistViewValue _ distance; oldFoundButton _ AtomButtons.AddValueSorted[clientData: ggData, scalarButtonHandle: ggData.hitTest.distanceHeader, value: [NIL, distance, LIST[$ToggleDistance, NEW[REAL _ distance]], on], order: incr]; IF oldFoundButton=NIL THEN { -- a new button is added GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, ggData: ggData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; }; AddDistanceInternal: PUBLIC PROC [ggData: GGData, distance: REAL] = { oldFoundButton: AtomButtons.ScalarButtonClient; ggData.measure.lineDistViewValue _ distance; oldFoundButton _ AtomButtons.AddValueSorted[clientData: ggData, scalarButtonHandle: ggData.hitTest.distanceHeader, value: [NIL, distance, LIST[$ToggleDistance, NEW[REAL _ distance]], off], order: incr]; }; DeleteDistance: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; prevButton: ScalarButtonClient _ ggData.hitTest.distanceHeader.scalarButtons; FOR thisButton: ScalarButtonClient _ prevButton.next, thisButton.next UNTIL thisButton = NIL DO IF thisButton.on THEN { TiogaButtons.DeleteButton[thisButton.button]; prevButton.next _ thisButton.next; } ELSE prevButton _ thisButton; ENDLOOP; ggData.measure.lineDistViewValue _ Real.LargestNumber; -- invalidate cached value GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsDeselected, ggData: ggData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; GetDistance: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; oldFoundButton: AtomButtons.ScalarButtonClient; seqGen: SequenceGenerator; segGen: SegmentGenerator; seq, next: Sequence; dist: REAL; seqGen _ GGSelect.SelectedSequences[ggData.scene, normal]; seq _ GGSequence.NextSequence[seqGen]; next _ GGSequence.NextSequence[seqGen]; IF seq = NIL OR next # NIL THEN { Feedback.Append[ggData.feedback, "Select a single sequence for a GetDistance.", oneLiner]; Feedback.Blink[ggData.feedback]; RETURN; }; segGen _ GGSequence.SegmentsInSequence[seq]; FOR seg: Segment _ GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen] UNTIL seg = NIL DO dist _ GGMeasure.LengthOfSegment[seg]/ggData.hitTest.scaleUnit; oldFoundButton _ AtomButtons.AddValueSorted[ggData, ggData.hitTest.distanceHeader, [NIL, dist, LIST[$ToggleDistance, NEW[REAL _ dist]], on], incr]; ENDLOOP; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, ggData: ggData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; ToggleDistance: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; firstButton, pushedButton: ScalarButtonClient; tiogaButton: TiogaButtons.TiogaButton; epsilon: REAL = 0.001; dist: REAL _ WITH event.rest.first SELECT FROM real: REF REAL => real^, int: REF INT => REAL[int^], ENDCASE => 999.0; firstButton _ ggData.hitTest.distanceHeader.scalarButtons; FOR thisButton: ScalarButtonClient _ firstButton, thisButton.next UNTIL thisButton = NIL DO IF ABS[thisButton.value-dist] < epsilon THEN { pushedButton _ thisButton; EXIT; }; REPEAT FINISHED => ERROR; ENDLOOP; tiogaButton _ pushedButton.button; IF pushedButton.on THEN { pushedButton.on _ FALSE; TiogaButtons.ChangeButtonLooks[tiogaButton, "", "b"]; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsDeselected, ggData: ggData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; } ELSE { pushedButton.on _ TRUE; TiogaButtons.ChangeButtonLooks[tiogaButton, "b", ""]; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, ggData: ggData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; }; MeasureSlopeHit: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; viewer: Viewer _ ggData.measure.slopeView; ViewerTools.SetSelection[viewer]; }; MeasureAngleHit: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; viewer: Viewer _ ggData.measure.angleView; ViewerTools.SetSelection[viewer]; }; MeasureRadiusHit: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; viewer: Viewer _ ggData.measure.radiusView; ViewerTools.SetSelection[viewer]; }; MeasureLineDistHit: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; viewer: Viewer _ ggData.measure.lineDistView; ViewerTools.SetSelection[viewer]; }; DeleteCaretSegment: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; chair: REF ANY; jointNum, newEndJoint: NAT; traj, shortTraj: Traj; trajEnd: TrajEnd; shortOutline: Outline; outlineD: OutlineDescriptor; delSeq: Sequence; bBoxOfTraj: BoundBox; newOutlines: LIST OF Outline; caret: Caret _ ggData.caret; point: Point; IF GGCaret.SittingOnEnd[caret] THEN { -- delete the joint and segment near the caret chair _ GGCaret.GetChair[caret]; outlineD _ NARROW[chair]; [traj: traj, jointNum: jointNum] _ GGOutline.UnpackSimpleDescriptor[outlineD]; bBoxOfTraj _ GGTraj.GetBoundBox[traj]; SELECT jointNum FROM 0 => {newEndJoint _ 1; trajEnd _ lo}; GGTraj.HiJoint[traj] => {newEndJoint _ jointNum -1; trajEnd _ hi}; ENDCASE => SIGNAL Problem[msg: "failed assertion"]; point _ GGTraj.FetchJointPos[traj, newEndJoint]; delSeq _ GGSequence.LastSegAndJoint[traj, trajEnd]; [----, newOutlines] _ GGInterface.DeleteSequence[delSeq, ggData.scene]; IF newOutlines = NIL THEN { -- we removed the last segment GGCaret.SitOn[caret, NIL]; } ELSE { jointSeq: Sequence; jointParts: SliceParts; jointD: OutlineDescriptor; IF newOutlines.rest # NIL THEN SIGNAL Problem[msg: "failed assertion"]; shortOutline _ newOutlines.first; shortTraj _ GGOutline.FenceOfOutline[shortOutline]; IF trajEnd = lo THEN jointSeq _ GGSequence.CreateFromJoint[shortTraj, 0] ELSE jointSeq _ GGSequence.CreateFromJoint[shortTraj, newEndJoint]; jointParts _ GGOutline.PartsFromSequence[shortOutline, jointSeq]; jointD _ NEW[OutlineDescriptorObj _ [shortOutline, jointParts]]; GGCaret.SitOn[ggData.caret, jointD]; }; GGCaret.SetAttractor[ggData.caret, point, NIL]; ggData.refresh.startBoundBox^ _ bBoxOfTraj^; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; } ELSE { Feedback.Append[ggData.feedback, "Place the caret on the end of a trajectory to BackSpace.", oneLiner]; }; }; END. ˆGGEventImplB.mesa Last edited by Bier on April 27, 1987 11:11:25 pm PDT Contents: Once an event reaches the front of the slack-process queue, it is dispatched to one of the procedures in this module. Pier, May 14, 1987 4:25:16 pm PDT Overlap Operations traverse the scene.entities from beginning (back) to end (front) Style Operations trajGen: TrajGenerator _ GGScene.TrajsInScene[ggData.scene]; FOR traj: Traj _ GGScene.NextTraj[trajGen], GGScene.NextTraj[trajGen] UNTIL traj = NIL DO segGen: SegmentGenerator _ GGSequence.SegmentsInTraj[traj]; FOR segAndIndex: SegAndIndex _ GGSequence.NextSegmentAndIndex[segGen], GGSequence.NextSegmentAndIndex[segGen] UNTIL segAndIndex.seg = NIL DO IF segAndIndex.seg.strokeWidth=width OR ABS[segAndIndex.seg.strokeWidth-width] NULL; }; ENDLOOP; ENDLOOP; WalkProc: TYPE = PROC [seg: Segment] RETURNS [keep: BOOL]; Alignment Operations Make slices hot. Fix the trigger bags, object bags, and Foreground plane (for efficiency). outDGen _ GGSelect.SelectedOutlines[ggData.scene, hot]; FOR outlineD: OutlineDescriptor _ GGSelect.NextOutlineDescriptor[outDGen], GGSelect.NextOutlineDescriptor[outDGen] UNTIL outlineD = NIL DO outFeature: FeatureData _ GGAlign.AddHotOutline[outlineD, ggData.hitTest.triggerBag]; -- fix triggerBag alignObjects: LIST OF FeatureData; alignObjects _ GGAlign.IncrementalFilters[outFeature, ggData.hitTest, NOT GGState.ShowAlignments[ggData], ggData.hitTest.alignBag]; GGRefresh.NoteNewForeground[alignObjects, ggData]; -- fix foreground plane ENDLOOP; Repaint outDGen: GGModelTypes.OutlineDescriptorGenerator; Update the hot and current trigger bags. outDGen _ GGSelect.SelectedOutlines[ggData.scene, hot]; -- expects only outline types !! FOR outlineD: OutlineDescriptor _ GGSelect.NextOutlineDescriptor[outDGen], GGSelect.NextOutlineDescriptor[outDGen] UNTIL outlineD = NIL DO outFeature: FeatureData _ GGAlign.AddHotOutline[outlineD, triggerBag]; alignObjects: LIST OF FeatureData; alignObjects _ GGAlign.IncrementalFilters[outFeature, ggData.hitTest, NOT GGState.ShowAlignments[ggData], alignBag]; GGRefresh.NoteNewForeground[alignObjects, ggData]; ENDLOOP; Does AllAlignmentsOff, turns alignment processing on, sets the gravity extent to a default value, turns gravity on, sets gravity type to PreferPoints, resets the radius unit and turns heuristics on. This is done before creating or playing a session log to get repeatable results. GravityExtentChange[LIST[$GravityExtentChange, $InitialValue], ggData]; Gravity Operations Units Menu Slope Line adds slope from angle viewer in measure line If the slopeViewValue was set internally, the viewer and the value will be consistent. If the SlopeValue viewer was set by typein, the viewer and the value will be inconsistent. only positive slopes, please put the most accurate value we have into variable slope only positive slopes, please NEVER delete very first button Gets selected segment slope, adds to slope menu and turns it on Angle Line adds angle from angle viewer in measure line See comments in AddSlope put the most accurate value we have into variable angle NEVER delete very first button Gets selected segment angle, adds to angle menu and turns it on Radius Line adds radius from Radius viewer in measure line See comments in AddSlope put the most accurate value we have into variable radius NEVER delete very first button GetRadius: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; oldFoundButton: AtomButtons.ScalarButtonClient; seqGen: SequenceGenerator; segGen: SegmentGenerator; seq, next: Sequence; radius: REAL; seqGen _ GGSelect.SelectedSequences[ggData.scene, normal]; seq _ GGSequence.NextSequence[seqGen]; next _ GGSequence.NextSequence[seqGen]; IF seq = NIL OR next # NIL THEN { Feedback.Append[ggData.feedback, "Select a single segment for GetRadius.", oneLiner]; Feedback.Blink[ggData.feedback]; RETURN; }; segGen _ GGSequence.SegmentsInSequence[seq]; FOR seg: Segment _ GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen] UNTIL seg = NIL DO radius _ Vectors2d.Distance[seg.lo, seg.hi]/ggData.hitTest.scaleUnit; oldFoundButton _ AtomButtons.AddValueSorted[ggData, ggData.hitTest.radiusHeader, [NIL, radius, LIST[$ToggleRadius, NEW[REAL _ radius]], on], incr]; ENDLOOP; GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, ggData: ggData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; IF sliceD = NIL THEN { outDGen: GGModelTypes.OutlineDescriptorGenerator; oPointGen: GGModelTypes.OutlinePointGenerator; outlineD, nextOutlineD: GGModelTypes.OutlineDescriptor; outDGen _ GGSelect.SelectedOutlines[ggData.scene, normal]; outlineD _ GGSelect.NextOutlineDescriptor[outDGen]; IF outlineD = NIL THEN GOTO Problem; nextOutlineD _ GGSelect.NextOutlineDescriptor[outDGen]; IF nextOutlineD # NIL THEN GOTO Problem; oPointGen _ outlineD.slice.class.pointsInDescriptor[outlineD]; pointAndDone _ outlineD.slice.class.nextPoint[oPointGen]; IF pointAndDone.done THEN GOTO Problem; p0 _ pointAndDone.point; pointAndDone _ outlineD.slice.class.nextPoint[oPointGen]; IF pointAndDone.done THEN GOTO Problem; p1 _ pointAndDone.point; pointAndDone _ outlineD.slice.class.nextPoint[oPointGen]; IF NOT pointAndDone.done THEN GOTO Problem; } Distance Line adds distance from LineDistance viewer in measure line See comments in AddSlope put the most accurate value we have into variable distance NEVER delete very first button Coordinate/Measure Line Miscellaneous ÊF¢˜Icode™Kšœ2Ïk™5šÏnœx™€Kšœ!™!—K™š ˜ Jšœ÷œK˜ÄK˜—šž œœ˜Jšœýœ5˜»Kšœ ˜—˜Kšœ œ˜'Kšœ œ˜'Kšœœ˜%Kšœ œ˜*Kšœœ˜'Kšœ œ˜+Kšœ œ˜%Kšœœœ˜3Kšœœ%˜?Kšœœ˜"Kšœœ˜!Kšœœ"˜:Kšœœ"˜:Kšœœ˜!Kšœ œ˜+Kšœ œ˜'Kšœœ!˜7Kšœ œ˜'Kšœœ"˜9Kšœœ˜3Kšœœ"˜9Kšœœ˜!Kšœœ ˜5Kšœœ)˜GKšœœ˜3Kšœ œ˜+Kšœ œ˜#Kšœ œ˜'Kšœœ˜Kšœ œ˜%Kšœœ˜1Kšœ œ˜/Kšœ œ˜&Kšœœ˜#Kšœœ˜$K˜Kšœ œ˜Kšœ œ ˜K˜—Kšžœœ œ˜4K˜K™šžœœœœœ œœœœ˜BKšœœ ˜$Kšœ˜Kšœ œœ,˜=•StartOfExpansion#[g: GGModelTypes.EntityGenerator]š œœœœœ˜BKšœ(˜(Kšœ)˜)Kšœ˜—Kšœqœ œœ˜ŸKšœ˜K˜—šžœœœœœ œœœœ˜EKšœœ ˜$Kšœ˜Kšœ œœ,˜=–#[g: GGModelTypes.EntityGenerator]š œœœœœ˜BKšœ(˜(Kšœ(˜(Kšœ˜—Kšœqœ œœ˜ŸKšœ˜K˜—šžœœœœœ œœœœ˜DKšœœ ˜$Kšœ œœ,˜=–#[g: GGModelTypes.EntityGenerator]š œœœœœ˜?Kšœ(˜(Kšœ˜—Kšœqœ œœ˜ŸKšœ˜K˜—šžœœœœœ œœœœ˜FKšœœ ˜$Kšœ œœ,˜=–#[g: GGModelTypes.EntityGenerator]š œœœœœ˜?Kšœ*˜*Kšœ˜—Kšœqœ œœ˜ŸKšœ˜K˜—š žœœ'œœœ ˜bK™@JšœI˜IšœIœ œ˜`Kšœ8œœ˜XKšœ˜—K–[list: LIST OF REF ANY]šœ œ˜,K˜K˜—šž œœœœœœœ ˜HKšœœ˜ šœœ˜Kšœœ˜K˜Kšœ˜—Kšœ˜ K˜—K˜K™šž œœœœœ œœœœ˜HKšœœ ˜$Jšœ'˜'Kšœ˜Kšœœœ œ˜šœ œœœ˜5Kšœœœ ˜Kšœœœœ˜Kšœ ˜—šœœ˜Kšœ^˜^Kšœ˜K˜—Kšœ!œU˜xJšœ;˜;Jšœ=˜=šœrœœ˜ˆJšœU˜UJšœ œœ œ˜5Jšœ˜—KšœD˜DKšœ<˜™>Jšœ9™9Jšœœœ ™'Jšœ™Jšœ9™9Jšœœœ ™'Jšœ™Jšœ9™9Jšœœœœ ™+J™—Jšœ œœœ˜!šœ˜Jšœ3˜3Jšœ œœœ ˜!Jšœ9˜9Jšœ6˜6Jšœœœ ˜'Jšœ˜Jšœ6˜6Jšœœœ ˜'Jšœ˜Jšœ6˜6Jšœœœœ ˜+J˜—š˜šœ ˜ KšœU˜UKšœ ˜ Kšœ˜K˜——šœ˜J˜——KšœJ˜JKš œRœ œœœ˜“Kšœwœ œœ˜¥Kšœ˜K˜K˜—šž œœœœœ œœœœ˜KKšœœ ˜$Kšœ.˜.Kšœ&˜&Kšœ œ ˜šœœœœ˜0Kšœœœ ˜Kšœœœœ˜Kšœ ˜—Kšœ8˜8šœ?œœ˜[šœœ$œ˜0Kšœ˜Kšœ˜K˜——š˜Kšœœ˜—Kšœ˜Kšœ"˜"šœœ˜Kšœœ˜Kšœ5˜5Kšœyœ œœ˜§K˜—šœ˜Kšœœ˜Kšœ5˜5Kšœwœ œœ˜¥K˜—K˜—K˜K™ šžœœœœœ œœœœ˜PKšœœ ˜$Kšœ˜šœ_œœ˜iKšœœœœ ˜NKšœ œœœ˜8Kšœœœœ˜EKšœœœœ˜BKšœ œœœ˜:Kšœ œœœ˜7K˜—šœ"˜(Kšœyœ œœ˜§—K˜K˜—šžœœœœœ œœœœ˜MKšœœ ˜$KšœN˜Nšœ?œœ˜[Kš œœœœœœ˜VKšœ˜—K˜K˜—šž œœœœœ œœœœ˜JKšœœ ˜$K™6Kšœ/˜/K™Kšœ œH˜Všœœ˜%K–B[gargoyleData: REF ANY, msg: ROPE, msgType: GGError.MsgType]šœY˜YKšœ ˜ Kšœ˜K˜K˜—Kšœ:™:KšœFœ-œ-˜ªKš œ{œ œœœ¡œ˜Éšœœœ ˜5Kšœwœ œœ˜¥K˜—K˜K˜—šžœœœœ˜EKšœ/˜/Kšœ,˜,Kš œ{œ œœœ¡œ˜ÊK˜K˜—šžœœœœœ œœœœ˜MKšœœ ˜$Kšœ™KšœM˜MšœCœœ˜_šœœ˜Kšœ-˜-Kšœ"˜"K˜—Kšœ˜Kšœ˜—Kšœ7 ˜QKšœyœ œœ˜§Kšœ˜K˜—šž œœœœœ œœœœ˜JKšœœ ˜$Kšœ/˜/Kšœ˜Kšœ˜Kšœ˜Kšœœ˜ Kšœ:˜:Kšœ&˜&Kšœ'˜'š œœœœœ˜!KšœZ˜ZKšœ ˜ Kšœ˜K˜—Kšœ,˜,šœOœœ˜dKšœ?˜?Kš œTœœœœ˜“Kšœ˜—Kšœwœ œœ˜¥Kšœ˜K˜—šžœœœœœ œœœœ˜MKšœœ ˜$Kšœ.˜.Kšœ&˜&Kšœ œ ˜šœœœœ˜.Kšœœœ ˜Kšœœœœ˜Kšœ ˜—Kšœ:˜:šœ?œœ˜[šœœ"œ˜.Kšœ˜Kšœ˜K˜——š˜Kšœœ˜—Kšœ˜Kšœ"˜"šœœ˜Kšœœ˜Kšœ5˜5Kšœyœ œœ˜§K˜—šœ˜Kšœœ˜Kšœ5˜5Kšœwœ œœ˜¥K˜—K˜—K˜K™šžœœœœœ œœœœ˜NKšœœ ˜$Kšœ*˜*Kšœ!˜!K˜K˜—šžœœœœœ œœœœ˜NKšœœ ˜$Kšœ*˜*Kšœ!˜!K˜K˜—šžœœœœœ œœœœ˜OKšœœ ˜$Kšœ+˜+Kšœ!˜!K˜K˜—šžœœœœœ œœœœ˜QKšœœ ˜$Kšœ-˜-Kšœ!˜!K˜—K˜K™ šžœœœœœ œœœœ˜QKšœœ ˜$Kšœœœ˜Kšœœ˜Kšœ˜K˜Kšœ˜Kšœ˜Kšœ˜K˜Kšœ œœ ˜Kšœ˜K˜ šœœ .˜TKšœ ˜ Kšœ œ˜K˜NKšœ&˜&šœ ˜Kšœ%˜%KšœB˜BKšœœ"˜3—Kšœ0˜0Kšœ3˜3Kšœ œB˜Gšœœœ ˜:Kšœœ˜K˜—šœ˜Kšœ˜Kšœ˜Kšœ˜Kšœœœœ"˜GKšœ!˜!Kšœ3˜3Kšœœ4˜HKšœ?˜CKšœA˜AKšœ œ4˜@Kšœ$˜$K˜—Kšœ*œ˜/Kšœ,˜,Kšœ€œ œœ˜®K˜—šœ˜Kšœg˜gK˜—Kšœ˜—K˜Kšœ˜J˜J˜—…—íhM’