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
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;
Overlap Operations
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] = {
traverse the scene.entities from beginning (back) to end (front)
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];
};
Style Operations
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: REALWITH 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.ROPENARROW[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.ROPENARROW[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.ROPENARROW[event.rest.first];
argStream: IO.STREAMIO.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: REALWITH 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]<epsilon THEN {
seq: Sequence ← GGSequence.CreateFromSegment[traj, segAndIndex.index];
GGSelect.SelectSequence[seq, ggData.scene, normal];
};
ENDLOOP;
ENDLOOP;
FOR slice: Slice ← GGScene.NextSlice[sliceGen], GGScene.NextSlice[sliceGen] UNTIL slice = NIL DO -- for every slice in the scene
WidthProc: WalkProc = {
WalkProc: TYPE = PROC [seg: Segment] RETURNS [keep: BOOL];
RETURN [seg.strokeWidth=width OR ABS[seg.strokeWidth-width]<epsilon];
};
sliceD: SliceDescriptor ← GGSlice.WalkSegments[slice, WidthProc]; -- get a descriptor of matching parts
GGSelect.SelectSlice[sliceD, ggData.scene, normal]; -- and select it
ENDLOOP;
Feedback.PutFHerald[ggData.feedback, oneLiner, "Segments of width %g selected", [real[width]] ];
GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, ggData: ggData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
EXITS
SyntaxError => {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.ROPENARROW[event.rest.first];
argStream: IO.STREAMIO.RIS[argRope];
dashed: BOOLFALSE;
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 = {
WalkProc: TYPE = PROC [seg: Segment] RETURNS [keep: BOOL];
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: BOOLFALSE;
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: INTNARROW[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];
};
Alignment Operations
MakeHot: PUBLIC PROC [clientData: REF ANY, event: LIST OF REF ANY] = {
ggData: GGData ← NARROW[clientData];
sliceDescGen: SliceDescriptorGenerator ← GGSelect.SelectedSlices[ggData.scene, normal];
Make slices hot.
FOR sliceD: SliceDescriptor ← GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen] UNTIL sliceD=NIL DO
GGSelect.SelectSlice[sliceD, ggData.scene, hot];
ENDLOOP;
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;
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;
Repaint
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;
outDGen: GGModelTypes.OutlineDescriptorGenerator;
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;
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;
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] = {
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.
ggData: GGData ← NARROW[clientData];
AllAlignmentsOff[ggData, NIL];
IF AtomButtons.GetButtonState[ggData.refresh.alignments] = off THEN ToggleAlignments[ggData, NIL];
GravityExtentChange[LIST[$GravityExtentChange, $InitialValue], ggData];
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];
};
Gravity Operations
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: BOOLTRUE;
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];
};
Units Menu
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: REALWITH 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]] ];
};
Slope Line
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];
adds slope from angle viewer in measure line
oldFoundButton: AtomButtons.ScalarButtonClient;
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.
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];
only positive slopes, please
IF slope<0.0 THEN slope ← slope+180.0;
IF slope=360.0 THEN slope ← 0.0;
put the most accurate value we have into variable slope
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;
only positive slopes, please
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];
NEVER delete very first button
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];
Gets selected segment slope, adds to slope menu and turns it on
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: REALWITH 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];
};
};
Angle Line
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];
adds angle from angle viewer in measure line
oldFoundButton: AtomButtons.ScalarButtonClient;
See comments in AddSlope
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;
put the most accurate value we have into variable angle
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];
NEVER delete very first button
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];
Gets selected segment angle, adds to angle menu and turns it on
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: REALWITH 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];
};
};
Radius Line
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];
adds radius from Radius viewer in measure line
oldFoundButton: AtomButtons.ScalarButtonClient;
See comments in AddSlope
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;
};
put the most accurate value we have into variable radius
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];
NEVER delete very first button
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;
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];
};
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 {
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;
}
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: REALWITH 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];
};
};
Distance Line
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];
adds distance from LineDistance viewer in measure line
oldFoundButton: AtomButtons.ScalarButtonClient;
See comments in AddSlope
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;
};
put the most accurate value we have into variable distance
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];
NEVER delete very first button
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: REALWITH 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];
};
};
Coordinate/Measure Line
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];
};
Miscellaneous
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.