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:
REAL ←
WITH event.rest.first
SELECT
FROM
real: REF REAL => real^,
int: REF INT => REAL[int^],
ENDCASE => -1.0;
IF strokeWidth < 0.0
THEN {
Feedback.AppendHerald[ggData.feedback, "Select a positive number for stroke width", oneLiner];
RETURN;
};
Feedback.Append[ggData.feedback, IO.PutFR["Selected objects will have stroke width %g", [real[strokeWidth]]], oneLiner];
bBox ← GGBoundBox.BoundBoxOfSelected[ggData.scene, normal];
sliceDescGen ← GGSelect.SelectedSlices[ggData.scene, normal];
FOR sliceD: SliceDescriptor ← GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen]
UNTIL sliceD=
NIL
DO
thisBox ← sliceD.slice.class.setStrokeWidth[sliceD.slice, sliceD.parts, strokeWidth];
IF thisBox # NIL THEN bBoxes ← CONS[thisBox, bBoxes];
ENDLOOP;
ggData.refresh.startBoundBox^ ← GGBoundBox.BoundBoxOfBoxes[bBoxes]^;
GGBoundBox.EnlargeByBox[ggData.refresh.startBoundBox, bBox];
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: none, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE];
};
LineEnds:
PUBLIC
PROC [clientData:
REF
ANY, event:
LIST
OF
REF
ANY] = {
ggData: GGData ← NARROW[clientData];
sliceDescGen: SliceDescriptorGenerator;
bBox: BoundBox;
strokeEnd: StrokeEnd;
argRope: Rope.ROPE ← NARROW[event.rest.first];
SELECT
TRUE
FROM
Rope.Equal[argRope, "square", FALSE] => strokeEnd ← square;
Rope.Equal[argRope, "butt", FALSE] => strokeEnd ← butt;
Rope.Equal[argRope, "round", FALSE] => strokeEnd ← round;
ENDCASE => {
Feedback.AppendHerald[ggData.feedback, "Select square, butt, or round for stroke end", oneLiner];
RETURN;
};
Feedback.Append[ggData.feedback, IO.PutFR["Selected objects will have stroke end %g", [rope[argRope]]], oneLiner];
bBox ← GGBoundBox.BoundBoxOfSelected[ggData.scene, normal];
sliceDescGen ← GGSelect.SelectedSlices[ggData.scene, normal];
FOR sliceD: SliceDescriptor ← GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen]
UNTIL sliceD=
NIL
DO
sliceD.slice.class.setStrokeEnd[sliceD.slice, sliceD.parts, strokeEnd];
ENDLOOP;
GGBoundBox.EnlargeByBox[bBox: bBox, by: GGBoundBox.BoundBoxOfSelected[ggData.scene, normal]];
ggData.refresh.startBoundBox^ ← bBox^;
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: none, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE];
};
TrajJoints:
PUBLIC
PROC [clientData:
REF
ANY, event:
LIST
OF
REF
ANY] = {
ggData: GGData ← NARROW[clientData];
sliceDescGen: SliceDescriptorGenerator;
bBox: BoundBox;
strokeJoint: StrokeJoint;
argRope: Rope.ROPE ← NARROW[event.rest.first];
SELECT
TRUE
FROM
Rope.Equal[argRope, "round", FALSE] => strokeJoint ← round;
Rope.Equal[argRope, "miter", FALSE] => strokeJoint ← miter;
Rope.Equal[argRope, "bevel", FALSE] => strokeJoint ← bevel;
ENDCASE => {
Feedback.AppendHerald[ggData.feedback, "Select round, miter, or bevel for trajectory joints", oneLiner];
RETURN;
};
Feedback.Append[ggData.feedback, IO.PutFR["Selected trajectories will have stroke joints %g", [rope[argRope]]], oneLiner];
bBox ← GGBoundBox.BoundBoxOfSelected[ggData.scene, normal];
sliceDescGen ← GGSelect.SelectedSlices[ggData.scene, normal];
FOR sliceD: SliceDescriptor ← GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen]
UNTIL sliceD=
NIL
DO
sliceD.slice.class.setStrokeJoint[sliceD.slice, sliceD.parts, strokeJoint];
ENDLOOP;
GGBoundBox.EnlargeByBox[bBox: bBox, by: GGBoundBox.BoundBoxOfSelected[ggData.scene, normal]];
ggData.refresh.startBoundBox^ ← bBox^;
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: none, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE];
};
DashesFromSelection:
PUBLIC
PROC [clientData:
REF
ANY, event:
LIST
OF
REF
ANY] = {
ENABLE GGParseIn.SyntaxError => GOTO SyntaxError;
ggData: GGData ← NARROW[clientData];
argRope: Rope.ROPE ← NARROW[event.rest.first];
argStream: IO.STREAM ← IO.RIS[argRope];
pattern: SequenceOfReal;
offset, length: REAL;
pattern ← GGParseIn.ReadArrayOfReal[argStream];
offset ← GGParseIn.ReadBlankAndReal[argStream];
length ← GGParseIn.ReadBlankAndReal[argStream];
BEGIN
sliceDescGen: SliceDescriptorGenerator;
bBox: BoundBox;
bBox ← GGBoundBox.BoundBoxOfSelected[ggData.scene, normal];
sliceDescGen ← GGSelect.SelectedSlices[ggData.scene, normal];
FOR sliceD: SliceDescriptor ← GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen]
UNTIL sliceD=
NIL
DO
sliceD.slice.class.setDashed[sliceD.slice, sliceD.parts, TRUE, pattern, offset, length];
ENDLOOP;
GGBoundBox.EnlargeByBox[bBox: bBox, by: GGBoundBox.BoundBoxOfSelected[ggData.scene, normal]];
ggData.refresh.startBoundBox^ ← bBox^;
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: none, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE];
END;
EXITS
SyntaxError => {Feedback.AppendHerald[NARROW[clientData, GGData].feedback, "Select a legal dash specification", oneLiner]; Feedback.Blink[NARROW[clientData, GGData].feedback];};
};
DashesOff:
PUBLIC
PROC [clientData:
REF
ANY, event:
LIST
OF
REF
ANY] = {
ggData: GGData ← NARROW[clientData];
sliceDescGen: SliceDescriptorGenerator;
bBox: BoundBox;
bBox ← GGBoundBox.BoundBoxOfSelected[ggData.scene, normal];
sliceDescGen ← GGSelect.SelectedSlices[ggData.scene, normal];
FOR sliceD: SliceDescriptor ← GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen]
UNTIL sliceD=
NIL
DO
sliceD.slice.class.setDashed[sliceD.slice, sliceD.parts, FALSE];
ENDLOOP;
GGBoundBox.EnlargeByBox[bBox: bBox, by: GGBoundBox.BoundBoxOfSelected[ggData.scene, normal]];
ggData.refresh.startBoundBox^ ← bBox^;
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: none, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE];
};
PrintStrokeValues:
PUBLIC
PROC [clientData:
REF
ANY, event:
LIST
OF
REF
ANY] = {
ggData: GGData ← NARROW[clientData];
dashed: BOOL ← FALSE;
pattern: SequenceOfReal;
offset, length: REAL;
sliceD: SliceDescriptor;
sliceDescGen: SliceDescriptorGenerator ← GGSelect.SelectedSlices[ggData.scene, normal];
IF sliceDescGen=NIL OR (sliceD ← GGSelect.NextSliceDescriptor[sliceDescGen])=NIL OR GGSelect.NextSliceDescriptor[sliceDescGen]#NIL THEN Feedback.AppendHerald[ggData.feedback, "Select exactly one entity for Show Stroke Values", oneLiner]
ELSE {
[dashed, pattern, offset, length] ← sliceD.slice.class.getDashed[sliceD.slice, sliceD.parts];
Feedback.Append[ggData.feedback,
IO.PutFR["Width: %g End: %g Joint: %g Dashes: %g",
[real[sliceD.slice.class.getStrokeWidth[sliceD.slice, sliceD.parts]]],
[rope[GetEndRope[sliceD.slice.class.getStrokeEnd[sliceD.slice, sliceD.parts]]]],
[rope[GetJointRope[sliceD.slice.class.getStrokeJoint[sliceD.slice, sliceD.parts]]]],
[rope[GetDashesRope[dashed, pattern, offset, length]]] ], oneLiner];
};
};
GetDashesRope:
PROC [dashed:
BOOL, pattern: SequenceOfReal, offset, length:
REAL]
RETURNS [r: Rope.
ROPE] = {
s: IO.STREAM;
IF NOT dashed THEN RETURN["Not Dashed"];
s ← IO.ROS[];
s.PutChar[ '[ ]; -- open bracket
FOR index:
NAT
IN [0..pattern.len)
DO
-- list of reals
s.PutF["%g ", [real[pattern[index]]] ];
ENDLOOP;
s.PutF["] %g %g", [real[offset]], [real[length]] ];
r ← IO.RopeFromROS[s];
};
GetEndRope:
PROC [strokeEnd: StrokeEnd]
RETURNS [r: Rope.
ROPE] = {
r ←
SELECT strokeEnd
FROM
round => "round",
butt => "butt",
square => "square",
ENDCASE => "none";
};
GetJointRope:
PROC [jointEnd: StrokeJoint]
RETURNS [r: Rope.
ROPE] = {
r ←
SELECT jointEnd
FROM
round => "round",
bevel => "bevel",
miter => "miter",
ENDCASE => "none";
};
SelectMatchingWidth:
PUBLIC
PROC [clientData:
REF
ANY, event:
LIST
OF
REF
ANY] = {
epsilon: REAL = 1.0E-3;
ggData: GGData ← NARROW[clientData];
width:
REAL ←
WITH event.rest.first
SELECT
FROM
real: REF REAL => real^,
int: REF INT =>REAL [int^],
ENDCASE => -1.0;
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.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];
};
};
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
};
};
sliceD: SliceDescriptor ← GGSlice.WalkSegments[slice, DashProc]; -- get a descriptor of matching parts
GGSelect.SelectSlice[sliceD, ggData.scene, normal]; -- and select it
ENDLOOP;
Feedback.PutFHerald[ggData.feedback, oneLiner, "Segments of Dashes %g selected", [rope[argRope]] ];
GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, ggData: ggData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
EXITS
SyntaxError => {Feedback.AppendHerald[NARROW[clientData, GGData].feedback, "Select a legal dash pattern for matching dashes", oneLiner]; Feedback.Blink[NARROW[clientData, GGData].feedback];};
};
SetDefaultStrokeValues:
PUBLIC
PROC [clientData:
REF
ANY, event:
LIST
OF
REF
ANY] = {
ggData: GGData ← NARROW[clientData];
dashed: BOOL ← FALSE;
pattern: SequenceOfReal;
offset, length, strokeWidth: REAL;
strokeJoint: StrokeJoint;
strokeEnd: StrokeEnd;
sliceD: SliceDescriptor;
sliceDescGen: SliceDescriptorGenerator ← GGSelect.SelectedSlices[ggData.scene, normal];
IF sliceDescGen=NIL OR (sliceD ← GGSelect.NextSliceDescriptor[sliceDescGen])=NIL OR GGSelect.NextSliceDescriptor[sliceDescGen]#NIL THEN Feedback.AppendHerald[ggData.feedback, "Select exactly one entity for Default Stroke Values", oneLiner]
ELSE {
[dashed, pattern, offset, length] ← sliceD.slice.class.getDashed[sliceD.slice, sliceD.parts];
strokeWidth ← sliceD.slice.class.getStrokeWidth[sliceD.slice, sliceD.parts];
strokeJoint ← sliceD.slice.class.getStrokeJoint[sliceD.slice, sliceD.parts];
strokeEnd ← sliceD.slice.class.getStrokeEnd[sliceD.slice, sliceD.parts];
};
ggData.defaults^ ← [strokeWidth: strokeWidth, strokeJoint: strokeJoint, strokeEnd: strokeEnd, dashed: dashed, pattern: GGUtility.CopyPattern[pattern], offset: offset, length: length, strokeColor: ggData.defaults.strokeColor, fillColor: ggData.defaults.fillColor, font: ggData.defaults.font];
Feedback.Append[ggData.feedback,
IO.PutFR["Default Stroke Values: Width: %g End: %g Joint: %g Dashes: %g",
[real[strokeWidth]],
[rope[GetEndRope[strokeEnd]]],
[rope[GetJointRope[strokeJoint]]],
[rope[GetDashesRope[dashed, pattern, offset, length]]] ], oneLiner];
};
ShowDefaultStrokeValues:
PUBLIC
PROC [clientData:
REF
ANY, event:
LIST
OF
REF
ANY] = {
ggData: GGData ← NARROW[clientData];
Feedback.Append[ggData.feedback,
IO.PutFR["Default Stroke Values: Width: %g End: %g Joint: %g Dashes: %g",
[real[ggData.defaults.strokeWidth]],
[rope[GetEndRope[ggData.defaults.strokeEnd]]],
[rope[GetJointRope[ggData.defaults.strokeJoint]]],
[rope[GetDashesRope[ggData.defaults.dashed, ggData.defaults.pattern, ggData.defaults.offset, ggData.defaults.length]]] ], oneLiner];
};
Arrows:
PUBLIC
PROC [clientData:
REF
ANY, event:
LIST
OF
REF
ANY] = {
ggData: GGData ← NARROW[clientData];
arrowType: INT ← NARROW[event.rest.first, REF INT]^;
leftArrows, rightArrows: BOOL;
sequenceGen: SequenceGenerator;
loIsLeft: BOOL;
traj: Traj;
SELECT arrowType FROM
0 => {
Feedback.PutF[ggData.feedback, oneLiner, "Selected objects will have no arrows."];
leftArrows ← FALSE;
rightArrows ← FALSE;
};
1 => {
Feedback.PutF[ggData.feedback, oneLiner, "Selected objects will have left/down arrows."];
leftArrows ← TRUE;
rightArrows ← FALSE;
};
2 => {
Feedback.PutF[ggData.feedback, oneLiner, "Selected objects will have right/up arrows."];
leftArrows ← FALSE;
rightArrows ← TRUE;
};
3 => {
Feedback.PutF[ggData.feedback, oneLiner, "Selected objects will have arrows on both ends."];
leftArrows ← TRUE;
rightArrows ← TRUE;
};
ENDCASE => {
Feedback.PutF[ggData.feedback, oneLiner, "Illegal argument to Arrows."];
RETURN;
};
sequenceGen ← GGSelect.SelectedSequences[ggData.scene, normal];
FOR seq: Sequence ← GGSequence.NextSequence[sequenceGen], GGSequence.NextSequence[sequenceGen]
UNTIL seq =
NIL
DO
traj ← seq.traj;
loIsLeft ← LoIsLeft[traj];
IF loIsLeft THEN GGTraj.SetArrows[seq.traj, leftArrows, rightArrows]
ELSE GGTraj.SetArrows[seq.traj, rightArrows, leftArrows];
ENDLOOP;
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedInPlace, ggData: ggData, remake: none, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE];
};
LoIsLeft:
PROC [traj: Traj]
RETURNS [
BOOL] = {
loPoint, hiPoint: Point;
loPoint ← GGTraj.FetchJointPos[traj, 0];
hiPoint ← GGTraj.LastJointPos[traj];
SELECT
TRUE
FROM
loPoint.x < hiPoint.x => RETURN[TRUE];
loPoint.x = hiPoint.x AND loPoint.y <= hiPoint.y => RETURN[TRUE];
ENDCASE => RETURN[FALSE];
};
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: 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];
};
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:
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]] ];
};
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:
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];
};
};
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:
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];
};
};
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;
};
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];
};
};
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:
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];
};
};
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.