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, List, 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, List, RealFns, Rope, TiogaButtons, Vectors2d, ViewerTools EXPORTS GGEvent = BEGIN WalkProc: TYPE = GGModelTypes.WalkProc; BoundBox: TYPE = GGModelTypes.BoundBox; Caret: TYPE = GGInterfaceTypes.Caret; EntityGenerator: TYPE = GGModelTypes.EntityGenerator; 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; 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]; trueEntity: REF ANY; scene: Scene _ ggData.scene; selected: LIST OF REF ANY _ OrderedSelectionList[ggData, decr]; FOR entity: LIST OF REF ANY _ selected, entity.rest UNTIL entity=NIL DO WITH entity.first SELECT FROM slice: Slice => trueEntity _ slice; outline: Outline => trueEntity _ outline; ENDCASE => ERROR; GGScene.DeleteEntity[scene, trueEntity]; GGScene.AddEntity[scene, trueEntity, -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]; trueEntity: REF ANY; scene: Scene _ ggData.scene; selected: LIST OF REF ANY _ OrderedSelectionList[ggData, incr]; FOR entity: LIST OF REF ANY _ selected, entity.rest UNTIL entity=NIL DO WITH entity.first SELECT FROM slice: Slice => trueEntity _ slice; outline: Outline => trueEntity _ outline; ENDCASE => ERROR; GGScene.DeleteEntity[scene, trueEntity]; GGScene.AddEntity[scene, trueEntity, 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 REF ANY _ OrderedSelectionList[ggData, incr]; FOR list: LIST OF REF ANY _ 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 REF ANY _ OrderedSelectionList[ggData, decr]; FOR list: LIST OF REF ANY _ 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 REF ANY] = { entityGen: EntityGenerator; list _ NIL; entityGen _ GGScene.TopLevelEntitiesInScene[ggData.scene]; FOR entity: REF ANY _ GGScene.NextEntity[entityGen], GGScene.NextEntity[entityGen] UNTIL entity = NIL DO IF GGSelect.IsSelectedInPart[entity, ggData.scene, normal] THEN list _ CONS[entity, list]; ENDLOOP; IF order=decr THEN list _ List.Reverse[list]; }; LineWidth: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; entityGen: GGModelTypes.EntityGenerator; 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]; entityGen _ GGSelect.SelectedStuff[ggData.scene, normal]; FOR entity: REF ANY _ GGScene.NextEntity[entityGen], GGScene.NextEntity[entityGen] UNTIL entity = NIL DO WITH entity SELECT FROM sliceD: SliceDescriptor => { thisBox _ sliceD.slice.class.setStrokeWidth[sliceD.slice, sliceD.parts, strokeWidth]; IF thisBox # NIL THEN bBoxes _ CONS[thisBox, bBoxes]; }; outlineD: OutlineDescriptor => { thisBox _ outlineD.slice.class.setStrokeWidth[outlineD.slice, outlineD.parts, strokeWidth]; IF thisBox # NIL THEN bBoxes _ CONS[thisBox, bBoxes]; }; ENDCASE => ERROR; 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]; entityGen: GGModelTypes.EntityGenerator; 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]; entityGen _ GGSelect.SelectedStuff[ggData.scene, normal]; FOR entity: REF ANY _ GGScene.NextEntity[entityGen], GGScene.NextEntity[entityGen] UNTIL entity = NIL DO WITH entity SELECT FROM sliceD: SliceDescriptor => sliceD.slice.class.setStrokeEnd[sliceD.slice, sliceD.parts, strokeEnd]; outlineD: OutlineDescriptor => outlineD.slice.class.setStrokeEnd[outlineD.slice, outlineD.parts, strokeEnd]; ENDCASE => ERROR; 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]; entityGen: GGModelTypes.EntityGenerator; 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]; entityGen _ GGSelect.SelectedStuff[ggData.scene, normal]; FOR entity: REF ANY _ GGScene.NextEntity[entityGen], GGScene.NextEntity[entityGen] UNTIL entity = NIL DO WITH entity SELECT FROM sliceD: SliceDescriptor => sliceD.slice.class.setStrokeJoint[sliceD.slice, sliceD.parts, strokeJoint]; outlineD: OutlineDescriptor => outlineD.slice.class.setStrokeJoint[outlineD.slice, outlineD.parts, strokeJoint]; ENDCASE => ERROR; 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 entityGen: GGModelTypes.EntityGenerator; bBox: BoundBox; bBox _ GGBoundBox.BoundBoxOfSelected[ggData.scene, normal]; entityGen _ GGSelect.SelectedStuff[ggData.scene, normal]; FOR entity: REF ANY _ GGScene.NextEntity[entityGen], GGScene.NextEntity[entityGen] UNTIL entity = NIL DO WITH entity SELECT FROM sliceD: SliceDescriptor => sliceD.slice.class.setDashed[sliceD.slice, sliceD.parts, TRUE, pattern, offset, length]; outlineD: OutlineDescriptor => outlineD.slice.class.setDashed[outlineD.slice, outlineD.parts, TRUE, pattern, offset, length]; ENDCASE => ERROR; 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]; BEGIN entityGen: GGModelTypes.EntityGenerator; bBox: BoundBox; bBox _ GGBoundBox.BoundBoxOfSelected[ggData.scene, normal]; entityGen _ GGSelect.SelectedStuff[ggData.scene, normal]; FOR entity: REF ANY _ GGScene.NextEntity[entityGen], GGScene.NextEntity[entityGen] UNTIL entity = NIL DO WITH entity SELECT FROM sliceD: SliceDescriptor => sliceD.slice.class.setDashed[sliceD.slice, sliceD.parts, FALSE]; outlineD: OutlineDescriptor => outlineD.slice.class.setDashed[outlineD.slice, outlineD.parts, FALSE]; ENDCASE => ERROR; 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; }; PrintStrokeValues: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = { ggData: GGData _ NARROW[clientData]; dashed: BOOL_ FALSE; pattern: SequenceOfReal; offset, length: REAL; entity: REF ANY; entityGen: EntityGenerator _ GGSelect.SelectedStuff[ggData.scene, normal]; IF entityGen=NIL OR (entity _ GGScene.NextEntity[entityGen])=NIL OR GGScene.NextEntity[entityGen]#NIL THEN Feedback.AppendHerald[ggData.feedback, "Select exactly one entity for Show Stroke Values", oneLiner] ELSE WITH entity SELECT FROM outlineD: OutlineDescriptor => { [dashed, pattern, offset, length] _ outlineD.slice.class.getDashed[outlineD.slice, outlineD.parts]; Feedback.Append[ggData.feedback, IO.PutFR["Width: %g End: %g Joint: %g Dashes: %g", [real[outlineD.slice.class.getStrokeWidth[outlineD.slice, outlineD.parts]]], [rope[GetEndRope[outlineD.slice.class.getStrokeEnd[outlineD.slice, outlineD.parts]]]], [rope[GetJointRope[outlineD.slice.class.getStrokeJoint[outlineD.slice, outlineD.parts]]]], [rope[GetDashesRope[dashed, pattern, offset, length]]] ], oneLiner]; }; sliceD: SliceDescriptor => { [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]; }; ENDCASE => ERROR; }; 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; trajGen: TrajGenerator _ GGScene.TrajsInScene[ggData.scene]; sliceGen: SliceGenerator _ GGScene.SlicesInScene[ggData.scene]; IF width<0.0 THEN GOTO SyntaxError; GGSelect.DeselectAll[ggData.scene, normal]; 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] {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; trajGen: GGModelTypes.TrajGenerator _ GGScene.TrajsInScene[ggData.scene]; sliceGen: SliceGenerator _ GGScene.SlicesInScene[ggData.scene]; pattern _ GGParseIn.ReadArrayOfReal[argStream]; offset _ GGParseIn.ReadBlankAndReal[argStream]; length _ GGParseIn.ReadBlankAndReal[argStream]; GGSelect.DeselectAll[ggData.scene, normal]; 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 seg: Segment _ segAndIndex.seg; 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; { -- if you get here, you matched exactly seq: Sequence _ GGSequence.CreateFromSegment[traj, segAndIndex.index]; GGSelect.SelectSequence[seq, ggData.scene, normal]; }; EXITS ExitLoop => NULL; }; ENDLOOP; ENDLOOP; 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; entity: REF ANY; entityGen: EntityGenerator _ GGSelect.SelectedStuff[ggData.scene, normal]; IF entityGen=NIL OR (entity _ GGScene.NextEntity[entityGen])=NIL OR GGScene.NextEntity[entityGen]#NIL THEN Feedback.AppendHerald[ggData.feedback, "Select exactly one entity for Default Stroke Values", oneLiner] ELSE WITH entity SELECT FROM outlineD: OutlineDescriptor => { [dashed, pattern, offset, length] _ outlineD.slice.class.getDashed[outlineD.slice, outlineD.parts]; strokeWidth _ outlineD.slice.class.getStrokeWidth[outlineD.slice, outlineD.parts]; strokeJoint _ outlineD.slice.class.getStrokeJoint[outlineD.slice, outlineD.parts]; strokeEnd _ outlineD.slice.class.getStrokeEnd[outlineD.slice, outlineD.parts]; }; sliceD: SliceDescriptor => { [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]; }; ENDCASE => ERROR; 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]; entityGen: EntityGenerator; outDGen: GGModelTypes.OutlineDescriptorGenerator; sliceDescGen: GGSelect.SliceDescriptorGenerator; entityGen _ GGSelect.SelectedStuff[ggData.scene, normal]; FOR entity: REF ANY _ GGScene.NextEntity[entityGen], GGScene.NextEntity[entityGen] UNTIL entity = NIL DO WITH entity SELECT FROM cD: SliceDescriptor => GGSelect.SelectSlice[cD, ggData.scene, hot]; oD: OutlineDescriptor => GGSelect.SelectOutline[oD, ggData.scene, hot]; ENDCASE => ERROR; ENDLOOP; 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; sliceDescGen _ GGSelect.SelectedSlices[ggData.scene, hot]; FOR sliceD: GGSelect.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]; entityGen: EntityGenerator; triggerBag: TriggerBag _ ggData.hitTest.triggerBag; alignBag: AlignBag _ ggData.hitTest.alignBag; outDGen: GGModelTypes.OutlineDescriptorGenerator; entityGen _ GGScene.TopLevelEntitiesInScene[ggData.scene]; FOR entity: REF ANY _ GGScene.NextEntity[entityGen], GGScene.NextEntity[entityGen] UNTIL entity = NIL DO WITH entity SELECT FROM slice: Slice => { sliceParts: SliceDescriptor _ slice.class.newParts[slice, NIL, topLevel]; GGSelect.SelectSlice[sliceParts, ggData.scene, hot]; }; outline: Outline => { sliceParts: OutlineDescriptor _ outline.class.newParts[outline, NIL, topLevel]; GGSelect.SelectOutline[sliceParts, ggData.scene, hot]; }; ENDCASE => ERROR; ENDLOOP; outDGen _ GGSelect.SelectedOutlines[ggData.scene, hot]; 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; 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]; entityGen: EntityGenerator; entityGen _ GGSelect.SelectedStuff[ggData.scene, normal]; FOR entity: REF ANY _ GGScene.NextEntity[entityGen], GGScene.NextEntity[entityGen] UNTIL entity = NIL DO GGSelect.DeselectEntity[entity, 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]; entityGen: EntityGenerator; entityGen _ GGSelect.SelectedStuff[ggData.scene, hot]; FOR entity: REF ANY _ GGScene.NextEntity[entityGen], GGScene.NextEntity[entityGen] UNTIL entity = NIL DO GGSelect.DeselectEntity[entity, 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; outDGen: GGModelTypes.OutlineDescriptorGenerator; pointGen: GGModelTypes.PointGenerator; oPointGen: GGModelTypes.OutlinePointGenerator; radius: REAL; pointAndDone: GGModelTypes.PointAndDone; sliceD, nextD: GGModelTypes.SliceDescriptor; outlineD, nextOutlineD: GGModelTypes.OutlineDescriptor; p0, p1: Point; BEGIN sliceDescGen _ GGSelect.SelectedSlices[ggData.scene, normal]; sliceD _ GGSelect.NextSliceDescriptor[sliceDescGen]; IF sliceD = NIL THEN { 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; } 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.UnpackSimpleDescriptorOld[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. rGGEventImplB.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 5, 1987 6:59:10 pm PDT Overlap Operations traverse the scene.entities from beginning (back) to end (front) Style Operations WalkProc: TYPE = PROC [seg: Segment] RETURNS [keep: BOOL]; WalkProc: TYPE = PROC [seg: Segment] RETURNS [keep: BOOL]; Alignment Operations Make sequences and slices hot. Fix the trigger bags, object bags, and Foreground plane (for efficiency). Repaint Update the hot and current trigger bags. 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]; }; 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 ÊA5˜Icode™Kšœ5™5šœ€™€Kšœ ™ —K™šÏk ˜ Jšœ÷œQ˜ÊK˜—šÏn œœ˜Jšœûœ;˜ÁKšœ ˜—˜Kšœ œ˜'Kšœ œ˜'Kšœœ˜%Kšœœ ˜5Kšœ œ˜*Kšœœ˜'Kšœ œ˜+Kšœ œ˜%Kšœœœ˜3Kšœœ%˜?Kšœœ˜"Kšœœ˜!Kšœœ"˜:Kšœœ"˜:Kšœœ˜!Kšœ œ˜+Kšœ œ˜'Kšœœ!˜7Kšœ œ˜'Kšœœ"˜9Kšœœ˜3Kšœœ"˜9Kšœœ˜!Kšœœ ˜5Kšœœ˜3Kšœ œ˜+Kšœ œ˜#Kšœ œ˜'Kšœœ˜Kšœ œ˜%Kšœœ˜1Kšœ œ˜/Kšœ œ˜&Kšœœ˜#Kšœœ˜$K˜Kšœ œ˜Kšœ œ ˜K˜—Kšžœœ œ˜4K˜K™šžœ9œ˜BKšœœ ˜$Kšœ œœ˜Kšœ˜Kš œ œœœœ&˜?•StartOfExpansion#[g: GGModelTypes.EntityGenerator]šœ œœœœœœ˜Gšœœ˜Kšœ#˜#Kšœ)˜)Kšœœ˜—Kšœ(˜(Kšœ)˜)Kšœ˜—Kšœqœ œœ˜ŸKšœ˜K˜—šžœ9œ˜EKšœœ ˜$Kšœ œœ˜Kšœ˜Kš œ œœœœ&˜?–#[g: GGModelTypes.EntityGenerator]šœ œœœœœœ˜Gšœœ˜Kšœ#˜#Kšœ)˜)Kšœœ˜—Kšœ(˜(Kšœ(˜(Kšœ˜—Kšœqœ œœ˜ŸKšœ˜K˜—šžœ9œ˜DKšœœ ˜$Kš œ œœœœ&˜?–#[g: GGModelTypes.EntityGenerator]šœœœœœœœ˜AKšœ(˜(Kšœ˜—Kšœqœ œœ˜ŸKšœ˜K˜—šžœ9œ˜FKšœœ ˜$Kš œ œœœœ&˜?–#[g: GGModelTypes.EntityGenerator]šœœœœœœœ˜AKšœ*˜*Kšœ˜—Kšœqœ œœ˜ŸKšœ˜K˜—šžœœ'œœœœœ˜dK™@J˜Kšœœ˜ Kšœ:˜:š œ œœ@œ œ˜hKšœ9œœ˜ZKšœ˜—K–[list: LIST OF REF ANY]šœ œ˜-K˜—K˜K™šž œ9œ˜HKšœœ ˜$Jšœ(˜(Kšœ˜Kšœœœ œ˜šœ œœœ˜5Kšœœœ ˜Kšœœœœ˜Kšœ ˜—šœœ˜Kšœ^˜^Kšœ˜K˜—Kšœ!œU˜xJšœ;˜;Jšœ9˜9š œ œœ@œ œ˜hšœœ˜šœ˜JšœU˜UJšœ œœ œ˜5J˜—šœ ˜ Jšœ[˜[Jšœ œœ œ˜5J˜—Jšœœ˜—Jšœ˜—KšœD˜DKšœ<˜˜>Jšœ9˜9Jšœœœ ˜'Jšœ˜Jšœ9˜9Jšœœœ ˜'Jšœ˜Jšœ9˜9Jšœœœœ ˜+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˜—šž œ9œ˜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™ šžœ9œ˜PKšœœ ˜$Kšœ˜šœ_œœ˜iKšœœœœ ˜NKšœ œœœ˜8Kšœœœœ˜EKšœœœœ˜BKšœ œœœ˜:Kšœ œœœ˜7K˜—šœ"˜(Kšœyœ œœ˜§—K˜K˜—šžœ9œ˜MKšœœ ˜$KšœN˜Nšœ?œœ˜[Kš œœœœœœ˜VKšœ˜—K˜K˜—šž œ9œ˜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˜—šžœ9œ˜MKšœœ ˜$K™KšœM˜MšœCœœ˜_šœœ˜Kšœ-˜-Kšœ"˜"K˜—Kšœ˜Kšœ˜—Kšœ7 ˜QKšœyœ œœ˜§Kšœ˜K˜—šž œ9œ˜JKšœœ ˜$Kšœ/˜/Kšœ˜Kšœ˜Kšœ˜Kšœœ˜ Kšœ:˜:Kšœ&˜&Kšœ'˜'š œœœœœ˜!KšœZ˜ZKšœ ˜ Kšœ˜K˜—Kšœ,˜,šœOœœ˜dKšœ?˜?Kš œTœœœœ˜“Kšœ˜—Kšœwœ œœ˜¥Kšœ˜K˜—šžœ9œ˜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™šžœ9œ˜NKšœœ ˜$Kšœ*˜*Kšœ!˜!K˜K˜—šžœ9œ˜NKšœœ ˜$Kšœ*˜*Kšœ!˜!K˜K˜—šžœ9œ˜OKšœœ ˜$Kšœ+˜+Kšœ!˜!K˜K˜—šžœ9œ˜QKšœœ ˜$Kšœ-˜-Kšœ!˜!K˜—K˜K™ šžœ9œ˜QKšœœ ˜$Kšœœœ˜Kšœœ˜Kšœ˜K˜Kšœ˜Kšœ˜Kšœ˜K˜Kšœ œœ ˜Kšœ˜K˜ šœœ .˜TKšœ ˜ Kšœ œ˜K˜QKšœ&˜&šœ ˜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˜—…—4NÛ