GGEventImplB.mesa
Last edited by Bier on January 28, 1987 3:34:27 pm PST
Contents: Once an event reaches the front of the slack-process queue, it is dispatched to one of the procedures in this module.
Pier, February 18, 1987 11:12:31 pm PST
DIRECTORY
CubicSplines, GGAlign, GGAngle, GGBasicTypes, GGBoundBox, AtomButtons, GGCaret, GGEditTool, GGError, GGEvent, GGGraphicsButton, GGGravity, GGInterface, GGInterfaceTypes, GGMeasure, GGModelTypes, GGObjects, GGOutline, GGRefresh, GGParseIn, GGSegmentTypes, GGSelect, GGSequence, GGTraj, GGUserInput, GGVector, GGViewerOps, GGWindow, IO, List, Real, RealFns, Rope, TiogaButtons, ViewerClasses, ViewerTools;
GGEventImplB: CEDAR PROGRAM
IMPORTS AtomButtons, GGAlign, GGAngle, GGBoundBox, GGCaret, GGEditTool, GGError, GGGraphicsButton, GGInterface, GGMeasure, GGObjects, GGParseIn, GGSelect, GGRefresh, GGSequence, GGOutline, GGTraj, GGVector, GGUserInput, GGViewerOps, GGWindow, IO, List, RealFns, Rope, TiogaButtons, ViewerTools
EXPORTS GGEvent = BEGIN
BoundBox: TYPE = GGModelTypes.BoundBox;
Caret: TYPE = GGInterfaceTypes.Caret;
EntityGenerator: TYPE = GGModelTypes.EntityGenerator;
FeatureData: TYPE = GGGravity.FeatureData;
GargoyleData: TYPE = GGInterfaceTypes.GargoyleData;
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;
Segment: TYPE = GGSegmentTypes.Segment;
SegmentGenerator: TYPE = GGModelTypes.SegmentGenerator;
Sequence: TYPE = GGModelTypes.Sequence;
SequenceGenerator: TYPE = GGModelTypes.SequenceGenerator;
Slice: TYPE = GGModelTypes.Slice;
SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor;
SliceParts: TYPE = GGModelTypes.SliceParts;
SliceGenerator: TYPE = GGModelTypes.SliceGenerator;
Traj: TYPE = GGModelTypes.Traj;
TrajEnd: TYPE = GGModelTypes.TrajEnd;
TrajGenerator: TYPE = GGModelTypes.TrajGenerator;
SegAndIndex: TYPE = GGSequence.SegAndIndex;
TwoState: TYPE = AtomButtons.TwoState;
Vector: TYPE = GGBasicTypes.Vector;
Viewer: TYPE = ViewerClasses.Viewer;
SequenceOfReal: TYPE = GGBasicTypes.SequenceOfReal;
pointsPerIn: REAL = 72.0;
pointsPerCm: REAL = 72.0/2.54;
Problem: SIGNAL [msg: Rope.ROPE] = GGError.Problem;
Overlap Operations
Top: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
trueEntity: REF ANY;
scene: Scene ← gargoyleData.scene;
selected: LIST OF REF ANY ← OrderedSelectionList[gargoyleData, decr];
FOR entity: LIST OF REF ANY ← selected, entity.rest UNTIL entity=NIL DO
WITH entity.first SELECT FROM
slice: Slice => trueEntity ← slice;
outline: Outline => trueEntity ← outline;
traj: Traj => trueEntity ← GGOutline.OutlineOfTraj[traj];
seq: Sequence => trueEntity ← GGOutline.OutlineOfTraj[seq.traj];
ENDCASE => ERROR;
GGObjects.DeleteEntity[scene, trueEntity];
GGObjects.AddEntity[scene, trueEntity, -1];
ENDLOOP;
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedInPlace, gargoyleData: gargoyleData, remake: none, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE];
};
Bottom: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
trueEntity: REF ANY;
scene: Scene ← gargoyleData.scene;
selected: LIST OF REF ANY ← OrderedSelectionList[gargoyleData, incr];
FOR entity: LIST OF REF ANY ← selected, entity.rest UNTIL entity=NIL DO
WITH entity.first SELECT FROM
slice: Slice => trueEntity ← slice;
outline: Outline => trueEntity ← outline;
traj: Traj => trueEntity ← GGOutline.OutlineOfTraj[traj];
seq: Sequence => trueEntity ← GGOutline.OutlineOfTraj[seq.traj];
ENDCASE => ERROR;
GGObjects.DeleteEntity[scene, trueEntity];
GGObjects.AddEntity[scene, trueEntity, 0];
ENDLOOP;
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedInPlace, gargoyleData: gargoyleData, remake: none, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE];
};
UpOne: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
selected: LIST OF REF ANY ← OrderedSelectionList[gargoyleData, incr];
FOR list: LIST OF REF ANY ← selected, list.rest UNTIL list=NIL DO
GGObjects.UpOne[gargoyleData.scene, list.first];
ENDLOOP;
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedInPlace, gargoyleData: gargoyleData, remake: none, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE];
};
DownOne: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
selected: LIST OF REF ANY ← OrderedSelectionList[gargoyleData, decr];
FOR list: LIST OF REF ANY ← selected, list.rest UNTIL list=NIL DO
GGObjects.DownOne[gargoyleData.scene, list.first];
ENDLOOP;
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedInPlace, gargoyleData: gargoyleData, remake: none, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE];
};
OrderedSelectionList: PROC [gargoyleData: GargoyleData, order: OverlapOrder] RETURNS [list: LIST OF REF ANY] = {
traverse the scene.entities from beginning (back) to end (front)
entityGen: EntityGenerator;
list ← NIL;
entityGen ← GGObjects.TopLevelEntitiesInScene[gargoyleData.scene];
FOR entity: REF ANY ← GGObjects.NextEntity[entityGen], GGObjects.NextEntity[entityGen] UNTIL entity = NIL DO
IF GGSelect.IsSelectedInPart[entity, gargoyleData.scene, normal] THEN list ← CONS[entity, list];
ENDLOOP;
IF order=decr THEN list ← List.Reverse[list];
};
Style Operations
LineWidth: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
entityGen: GGModelTypes.EntityGenerator;
strokeWidth: REAL;
bBox: BoundBox;
widthRef: REF REALNARROW[event.rest.first];
strokeWidth ← widthRef^;
IF strokeWidth < 0.0 THEN {
GGError.AppendHerald[gargoyleData.feedback, "Select a positive number for stroke width", oneLiner];
RETURN;
};
GGError.Append[gargoyleData.feedback, IO.PutFR["Selected objects will have stroke width %1.2f", [real[strokeWidth]]], oneLiner];
bBox ← GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal];
entityGen ← GGSelect.SelectedStuff[gargoyleData.scene, normal];
FOR entity: REF ANY ← GGObjects.NextEntity[entityGen], GGObjects.NextEntity[entityGen] UNTIL entity = NIL DO
WITH entity SELECT FROM
sliceD: SliceDescriptor => sliceD.slice.class.setStrokeWidth[sliceD.slice, sliceD.parts, strokeWidth];
outlineD: OutlineDescriptor => outlineD.slice.class.setStrokeWidth[outlineD.slice, outlineD.parts, strokeWidth];
ENDCASE => ERROR;
ENDLOOP;
GGBoundBox.EnlargeByBox[bBox: bBox, by: GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal]];
gargoyleData.refresh.startBoundBox^ ← bBox^;
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: none, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE];
};
DashesFromSelection: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
ENABLE GGParseIn.SyntaxError => GOTO SyntaxError;
gargoyleData: GargoyleData ← 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
entityGen: GGModelTypes.EntityGenerator;
bBox: BoundBox;
bBox ← GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal];
entityGen ← GGSelect.SelectedStuff[gargoyleData.scene, normal];
FOR entity: REF ANY ← GGObjects.NextEntity[entityGen], GGObjects.NextEntity[entityGen] UNTIL entity = NIL DO
WITH entity SELECT FROM
sliceD: SliceDescriptor => sliceD.slice.class.setDashed[sliceD.slice, sliceD.parts, TRUE, pattern, offset, length];
outlineD: OutlineDescriptor => outlineD.slice.class.setDashed[outlineD.slice, outlineD.parts, TRUE, pattern, offset, length];
ENDCASE => ERROR;
ENDLOOP;
GGBoundBox.EnlargeByBox[bBox: bBox, by: GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal]];
gargoyleData.refresh.startBoundBox^ ← bBox^;
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: none, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE];
END;
EXITS
SyntaxError => {GGError.AppendHerald[NARROW[clientData, GargoyleData].feedback, "Select a legal dash specification", oneLiner]; GGError.Blink[NARROW[clientData, GargoyleData].feedback];};
};
DashesOff: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
BEGIN
entityGen: GGModelTypes.EntityGenerator;
bBox: BoundBox;
bBox ← GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal];
entityGen ← GGSelect.SelectedStuff[gargoyleData.scene, normal];
FOR entity: REF ANY ← GGObjects.NextEntity[entityGen], GGObjects.NextEntity[entityGen] UNTIL entity = NIL DO
WITH entity SELECT FROM
sliceD: SliceDescriptor => sliceD.slice.class.setDashed[sliceD.slice, sliceD.parts, FALSE];
outlineD: OutlineDescriptor => outlineD.slice.class.setDashed[outlineD.slice, outlineD.parts, FALSE];
ENDCASE => ERROR;
ENDLOOP;
GGBoundBox.EnlargeByBox[bBox: bBox, by: GGBoundBox.BoundBoxOfSelected[gargoyleData.scene, normal]];
gargoyleData.refresh.startBoundBox^ ← bBox^;
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: none, backgndOK: FALSE, edited: TRUE, okToClearFeedback: FALSE];
END;
};
PrintStrokeValues: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
dashed: BOOLFALSE;
pattern: SequenceOfReal;
offset, length: REAL;
entity: REF ANY;
entityGen: EntityGenerator ← GGSelect.SelectedStuff[gargoyleData.scene, normal];
IF entityGen=NIL OR (entity ← GGObjects.NextEntity[entityGen])=NIL OR GGObjects.NextEntity[entityGen]#NIL THEN GGError.AppendHerald[gargoyleData.feedback, "Select exactly one entity for Show Stroke Value", oneLiner]
ELSE WITH entity SELECT FROM
outlineD: OutlineDescriptor => {
[dashed, pattern, offset, length] ← outlineD.slice.class.getDashed[outlineD.slice, outlineD.parts];
GGError.Append[gargoyleData.feedback, IO.PutFR["Width: %1.2f Dashes: %g", [real[outlineD.slice.class.getStrokeWidth[outlineD.slice, outlineD.parts]]], [rope[GetDashesRope[dashed, pattern, offset, length]]] ], oneLiner];
};
sliceD: SliceDescriptor => {
[dashed, pattern, offset, length] ← sliceD.slice.class.getDashed[sliceD.slice, sliceD.parts];
GGError.Append[gargoyleData.feedback, IO.PutFR["Width: %1.2f Dashes: %g", [real[sliceD.slice.class.getStrokeWidth[sliceD.slice, sliceD.parts]]], [rope[GetDashesRope[dashed, pattern, offset, length]]] ], oneLiner];
};
ENDCASE => ERROR;
};
GetDashesRope: PROC [dashed: BOOL, pattern: SequenceOfReal, offset, length: REAL] RETURNS [r: Rope.ROPE] = {
s: IO.STREAM;
IF NOT dashed THEN RETURN["Not Dashed"];
s ← IO.ROS[];
s.PutChar[ '[ ]; -- open bracket
FOR index: NAT IN [0..pattern.len) DO -- list of reals
s.PutF["%g ", [real[pattern[index]]] ];
ENDLOOP;
s.PutF["] %g %g", [real[offset]], [real[length]] ];
r ← IO.RopeFromROS[s];
};
SelectMatchingWidth: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
epsilon: REAL = 1.0E-3;
gargoyleData: GargoyleData ← NARROW[clientData];
width: REALNARROW[event.rest.first, REF REAL]^;
trajGen: TrajGenerator ← GGObjects.TrajsInScene[gargoyleData.scene];
IF width<0.0 THEN GOTO SyntaxError;
GGSelect.DeselectAll[gargoyleData.scene, normal];
FOR traj: Traj ← GGObjects.NextTraj[trajGen], GGObjects.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, gargoyleData.scene, normal];
};
ENDLOOP;
ENDLOOP;
FOR slice: Slice ← GGObjects.NextSlice[sliceGen], GGObjects.NextSlice[sliceGen] UNTIL slice = NIL DO
SLICES CAN'T ENUMERATE THEIR PARTS!!
ENDLOOP;
GGError.PutFHerald[gargoyleData.feedback, oneLiner, "Segments of width %1.2f selected", [real[width]] ];
GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
EXITS
SyntaxError => {GGError.AppendHerald[NARROW[clientData, GargoyleData].feedback, "Select a real number >=0.0 for matching width", oneLiner]; GGError.Blink[NARROW[clientData, GargoyleData].feedback];};
};
SelectMatchingDashes: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
ENABLE GGParseIn.SyntaxError => GOTO SyntaxError;
gargoyleData: GargoyleData ← NARROW[clientData];
argRope: Rope.ROPENARROW[event.rest.first];
argStream: IO.STREAMIO.RIS[argRope];
dashed: BOOL ← FALSE;
pattern: SequenceOfReal;
offset, length: REAL;
trajGen: GGModelTypes.TrajGenerator;
pattern ← GGParseIn.ReadArrayOfReal[argStream];
offset ← GGParseIn.ReadBlankAndReal[argStream];
length ← GGParseIn.ReadBlankAndReal[argStream];
trajGen ← GGObjects.TrajsInScene[gargoyleData.scene];
GGSelect.DeselectAll[gargoyleData.scene, normal];
FOR traj: Traj ← GGObjects.NextTraj[trajGen], GGObjects.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, gargoyleData.scene, normal];
};
EXITS
ExitLoop => NULL;
};
ENDLOOP;
ENDLOOP;
FOR slice: Slice ← GGObjects.NextSlice[sliceGen], GGObjects.NextSlice[sliceGen] UNTIL slice = NIL DO
SLICES CAN'T ENUMERATE THEIR PARTS!!
ENDLOOP;
GGError.PutFHerald[gargoyleData.feedback, oneLiner, "Segments of Dashes %g selected", [rope[argRope]] ];
GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE];
EXITS
SyntaxError => {GGError.AppendHerald[NARROW[clientData, GargoyleData].feedback, "Select a legal dash pattern for matching dashes", oneLiner]; GGError.Blink[NARROW[clientData, GargoyleData].feedback];};
};
Arrows: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
arrowType: INTNARROW[event.rest.first, REF INT]^;
leftArrows, rightArrows: BOOL;
sequenceGen: SequenceGenerator;
loIsLeft: BOOL;
traj: Traj;
SELECT arrowType FROM
0 => {
GGError.PutF[gargoyleData.feedback, oneLiner, "Selected objects will have no arrows."];
leftArrows ← FALSE;
rightArrows ← FALSE;
};
1 => {
GGError.PutF[gargoyleData.feedback, oneLiner, "Selected objects will have left/down arrows."];
leftArrows ← TRUE;
rightArrows ← FALSE;
};
2 => {
GGError.PutF[gargoyleData.feedback, oneLiner, "Selected objects will have right/up arrows."];
leftArrows ← FALSE;
rightArrows ← TRUE;
};
3 => {
GGError.PutF[gargoyleData.feedback, oneLiner, "Selected objects will have arrows on both ends."];
leftArrows ← TRUE;
rightArrows ← TRUE;
};
ENDCASE => {
GGError.PutF[gargoyleData.feedback, oneLiner, "Illegal argument to Arrows."];
RETURN;
};
sequenceGen ← GGSelect.SelectedSequences[gargoyleData.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, gargoyleData: gargoyleData, 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 [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
entityGen: EntityGenerator;
outDGen: GGModelTypes.OutlineDescriptorGenerator;
sliceDescGen: GGSelect.SliceDescriptorGenerator;
entityGen ← GGSelect.SelectedStuff[gargoyleData.scene, normal];
Make sequences and slices hot.
FOR entity: REF ANY ← GGObjects.NextEntity[entityGen], GGObjects.NextEntity[entityGen] UNTIL entity = NIL DO
WITH entity SELECT FROM
cD: SliceDescriptor => GGSelect.SelectSlice[cD.slice, cD.parts, gargoyleData.scene, hot];
oD: OutlineDescriptor => GGSelect.SelectOutline[oD.slice, oD.parts, gargoyleData.scene, hot];
ENDCASE => ERROR;
ENDLOOP;
Fix the trigger bags, object bags, and Foreground plane (for efficiency).
outDGen ← GGSelect.SelectedOutlines[gargoyleData.scene, hot];
FOR outlineD: OutlineDescriptor ← GGSelect.NextOutlineDescriptor[outDGen], GGSelect.NextOutlineDescriptor[outDGen] UNTIL outlineD = NIL DO
outFeature: FeatureData ← GGAlign.CreateOutlineTrigger[outlineD, gargoyleData.hitTest.currentTriggerBag]; -- fix triggerBag
alignObjects: LIST OF FeatureData;
alignObjects ← GGAlign.IncrementalFilters[outFeature, gargoyleData.hitTest.currentObjectBag, gargoyleData]; -- fix object bag
GGRefresh.NoteNewForeground[alignObjects, gargoyleData]; -- fix foreground plane
ENDLOOP;
sliceDescGen ← GGSelect.SelectedSlices[gargoyleData.scene, hot];
FOR sliceD: GGSelect.SliceDescriptor ← GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen] UNTIL sliceD = NIL DO
sliceFeature: FeatureData ← GGAlign.CreateSliceTrigger[sliceD, gargoyleData.hitTest.currentTriggerBag]; -- fix triggerBag
alignObjects: LIST OF FeatureData;
alignObjects ← GGAlign.IncrementalFilters[sliceFeature, gargoyleData.hitTest.currentObjectBag, gargoyleData]; -- fix object bag
GGRefresh.NoteNewForeground[alignObjects, gargoyleData]; -- fix foreground plane
ENDLOOP;
Repaint
GGWindow.RestoreScreenAndInvariants[paintAction: $SequencesMadeHot, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
};
MakeAllHot: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
entityGen: EntityGenerator;
outDGen: GGModelTypes.OutlineDescriptorGenerator;
entityGen ← GGObjects.TopLevelEntitiesInScene[gargoyleData.scene];
FOR entity: REF ANY ← GGObjects.NextEntity[entityGen], GGObjects.NextEntity[entityGen] UNTIL entity = NIL DO
WITH entity SELECT FROM
slice: Slice => {
sliceParts: SliceParts ← slice.class.newParts[slice, NIL, topLevel];
GGSelect.SelectSlice[slice, sliceParts, gargoyleData.scene, hot];
};
outline: Outline => {
GGSelect.SelectEntireOutline[outline, gargoyleData.scene, hot];
};
ENDCASE => ERROR;
ENDLOOP;
Update the hot and current trigger bags.
outDGen ← GGSelect.SelectedOutlines[gargoyleData.scene, hot];
FOR outlineD: OutlineDescriptor ← GGSelect.NextOutlineDescriptor[outDGen], GGSelect.NextOutlineDescriptor[outDGen] UNTIL outlineD = NIL DO
outFeature: FeatureData ← GGAlign.CreateOutlineTrigger[outlineD, NARROW[gargoyleData.hitTest.currentTriggerBag]];
alignObjects: LIST OF FeatureData;
alignObjects ← GGAlign.IncrementalFilters[outFeature, gargoyleData.hitTest.currentObjectBag, gargoyleData];
GGRefresh.NoteNewForeground[alignObjects, gargoyleData];
ENDLOOP;
GGWindow.RestoreScreenAndInvariants[paintAction: $SequencesMadeHot, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
};
MakeCold: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
entityGen: EntityGenerator;
entityGen ← GGSelect.SelectedStuff[gargoyleData.scene, normal];
FOR entity: REF ANY ← GGObjects.NextEntity[entityGen], GGObjects.NextEntity[entityGen] UNTIL entity = NIL DO
GGSelect.DeselectEntity[entity, gargoyleData.scene, hot];
ENDLOOP;
GGWindow.RestoreScreenAndInvariants[paintAction: $SequencesMadeCold, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
};
MakeAllCold: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
entityGen: EntityGenerator;
entityGen ← GGSelect.SelectedStuff[gargoyleData.scene, hot];
FOR entity: REF ANY ← GGObjects.NextEntity[entityGen], GGObjects.NextEntity[entityGen] UNTIL entity = NIL DO
GGSelect.DeselectEntity[entity, gargoyleData.scene, hot];
ENDLOOP;
GGWindow.RestoreScreenAndInvariants[paintAction: $SequencesMadeCold, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
};
ShowHot: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
gargoyleData.camera.hideHot ← FALSE;
GGWindow.RestoreScreenAndInvariants[paintAction: $SequencesMadeHot, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
};
HideHot: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
gargoyleData.camera.hideHot ← TRUE;
GGWindow.RestoreScreenAndInvariants[paintAction: $SequencesMadeCold, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
};
DropAnchor: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
GGCaret.Copy[gargoyleData.anchor, gargoyleData.caret];
[] ← GGAlign.CreateAnchorTrigger[gargoyleData.anchor, gargoyleData.hitTest.currentTriggerBag]; -- anchor can trigger
[] ← GGAlign.CreateAnchorTrigger[gargoyleData.anchor, gargoyleData.hitTest.sceneTriggerBag]; -- anchor is itself gravity active
GGWindow.RestoreScreenAndInvariants[paintAction: $AnchorAdded, gargoyleData: gargoyleData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
};
KillAnchor: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
IF GGCaret.Exists[gargoyleData.anchor] THEN {
GGCaret.Kill[gargoyleData.anchor];
GGAlign.DeleteAnchorTrigger[gargoyleData.hitTest.currentTriggerBag]; -- don't trigger
GGAlign.DeleteAnchorTrigger[gargoyleData.hitTest.sceneTriggerBag]; -- don't be gravity active
GGWindow.RestoreScreenAndInvariants[paintAction: $AnchorRemoved, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
};
};
StandardAlignments: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
StandardSlopes[event, clientData];
StandardAngles[event, clientData];
StandardRadii[event, clientData];
StandardDistances[event, clientData];
GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsDeselected, gargoyleData: gargoyleData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
};
AllAlignmentsOff: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
SlopePrompt[event, gargoyleData];
AnglePrompt[event, gargoyleData];
RadiusPrompt[event, gargoyleData];
DistancePrompt[event, gargoyleData];
IF AtomButtons.GetButtonState[gargoyleData.hitTest.midpointButton] = on THEN ToggleMidpoints[NIL, gargoyleData];
};
InitializeAlignments: PUBLIC PROC [event: LIST OF REF ANY, clientData: 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.
gargoyleData: GargoyleData ← NARROW[clientData];
AllAlignmentsOff[NIL, gargoyleData];
IF AtomButtons.GetButtonState[gargoyleData.refresh.alignments] = off THEN ToggleAlignments[NIL, gargoyleData];
GravityExtentChange[LIST[$GravityExtentChange, $InitialValue], gargoyleData];
IF AtomButtons.GetButtonState[gargoyleData.hitTest.gravButton] = off THEN ToggleGravity[NIL, gargoyleData];
IF Rope.Equal[gargoyleData.hitTest.gravityTypeMenu.flipLabel.name, "StrictDistance", TRUE] THEN GravityChoiceChange[LIST[$GravityChoiceChange, $FlipForward], gargoyleData];
InchScaleUnit[NIL, gargoyleData];
IF AtomButtons.GetButtonState[gargoyleData.hitTest.heuristicsButton] = off THEN ToggleHeuristics[NIL, gargoyleData];
};
Gravity Operations
GravityChoiceChange: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
info: AtomButtons.EnumTypeRef ← gargoyleData.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] => gargoyleData.hitTest.gravityType ← strictDistance;
Rope.Equal[name, "PreferPoints", TRUE] => gargoyleData.hitTest.gravityType ← innerCircle;
ENDCASE => ERROR;
UpdateGravityChoice[gargoyleData];
};
GravityExtentChange: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
value: REAL;
success: BOOLTRUE;
graphicsState: GGGraphicsButton.GraphicsState ← gargoyleData.hitTest.gravityExtentButton;
value ← GGGraphicsButton.GetValue[graphicsState];
SELECT event.rest.first FROM
$ValueUp => {
IF value < 18432.0 THEN value ← value*2.0
ELSE {
GGError.PutF[gargoyleData.feedback, oneLiner, "Can't extend gravity further than 256 inches."];
GGError.Blink[gargoyleData.feedback];
success ← FALSE;
};
};
$ValueDown => value ← value/2.0;
ENDCASE => value ← GGEditTool.GetDefaultGravityExtent[];
IF success THEN {
GGWindow.SetGravityExtent[gargoyleData, value/72.0];
};
};
SetGravityExtent: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
inches: REAL;
graphicsState: GGGraphicsButton.GraphicsState ← gargoyleData.hitTest.gravityExtentButton;
inches ← NARROW[event.rest.first, REF REAL]^;
GGWindow.SetGravityExtent[gargoyleData, inches];
};
UpdateGravityChoice: PROC [clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
SELECT AtomButtons.GetButtonState[gargoyleData.hitTest.gravButton] FROM
on => SELECT gargoyleData.hitTest.gravityType FROM
strictDistance => GGWindow.SetCursorLooks[strictDistance, gargoyleData];
innerCircle => GGWindow.SetCursorLooks[innerCircle, gargoyleData];
ENDCASE => ERROR; -- SHOULD NOT USE off STATE
off => GGWindow.SetCursorLooks[off, gargoyleData];
ENDCASE => ERROR;
};
ScreenChoiceChange: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
name: Rope.ROPE;
gargoyleData: GargoyleData ← NARROW[clientData];
info: AtomButtons.EnumTypeRef ← gargoyleData.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] => {
gargoyleData.camera.quality ← fast;
gargoyleData.camera.displayStyle ← print;
};
Rope.Equal[name, "ScreenFonts", TRUE] => {
gargoyleData.camera.quality ← fast;
gargoyleData.camera.displayStyle ← screen;
};
Rope.Equal[name, "WYSIWYG", TRUE] => {
gargoyleData.camera.quality ← quality;
gargoyleData.camera.displayStyle ← print;
};
ENDCASE => ERROR;
GGWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireViewer, gargoyleData: gargoyleData, remake: none, backgndOK: FALSE, edited: FALSE, okToClearFeedback: TRUE];
};
ToggleShowColors: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
stateInfo: TwoState ← gargoyleData.refresh.showColors;
AtomButtons.SwitchState[stateInfo];
GGWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireViewer, gargoyleData: gargoyleData, remake: none, backgndOK: FALSE, edited: FALSE, okToClearFeedback: TRUE];
};
ToggleAlignments: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
stateInfo: TwoState ← gargoyleData.refresh.alignments;
AtomButtons.SwitchState[stateInfo];
SELECT AtomButtons.GetButtonState[stateInfo] FROM
on => gargoyleData.camera.hideAlignments ← FALSE;
off => gargoyleData.camera.hideAlignments ← TRUE;
ENDCASE => ERROR;
GGWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireViewer, gargoyleData: gargoyleData, remake: objectBag, backgndOK: FALSE, edited: FALSE, okToClearFeedback: TRUE];
};
ToggleMidpoints: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
stateInfo: TwoState ← gargoyleData.hitTest.midpointButton;
AtomButtons.SwitchState[stateInfo];
GGWindow.RestoreScreenAndInvariants[paintAction: $None, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: FALSE, okToClearFeedback: TRUE];
};
SetMidpointsInternal: PUBLIC PROC [gargoyleData: GargoyleData, on: BOOL] = {
stateInfo: TwoState ← gargoyleData.hitTest.midpointButton;
wasOn: BOOL ← AtomButtons.GetButtonState[stateInfo] = on;
IF wasOn # on THEN AtomButtons.SwitchState[stateInfo];
};
ToggleHeuristics: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
stateInfo: TwoState ← gargoyleData.hitTest.heuristicsButton;
AtomButtons.SwitchState[stateInfo];
GGWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireViewer, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: FALSE, okToClearFeedback: TRUE];
};
ToggleGravity: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
stateInfo: TwoState ← gargoyleData.hitTest.gravButton;
AtomButtons.SwitchState[stateInfo];
UpdateGravityChoice[gargoyleData];
};
Units Menu
ScaleUnitAux: PROC [gargoyleData: GargoyleData, distance: REAL] = {
IF distance <=0.0 THEN {
GGError.AppendHerald[gargoyleData.feedback, "Zero or Illegal unit value. Set Units ignored.", oneLiner];
GGError.Blink[gargoyleData.feedback];
}
ELSE {
gargoyleData.hitTest.scaleUnit ← distance; -- in screen dots
GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, gargoyleData: gargoyleData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
};
};
ScaleUnitFromSegment: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
seqGen: SequenceGenerator;
segGen: SegmentGenerator;
seq, next: Sequence;
seg, nextSeg: Segment;
distance: REAL;
seqGen ← GGSelect.SelectedSequences[gargoyleData.scene, normal];
seq ← GGSequence.NextSequence[seqGen];
next ← GGSequence.NextSequence[seqGen];
IF seq = NIL OR next # NIL THEN {
GGError.AppendHerald[gargoyleData.feedback, "Select a single segment to set Units.", oneLiner];
GGError.Blink[gargoyleData.feedback];
RETURN;
};
segGen ← GGSequence.SegmentsInSequence[seq];
seg ← GGSequence.NextSegment[segGen];
nextSeg ← GGSequence.NextSegment[segGen];
IF seg = NIL OR nextSeg # NIL THEN {
GGError.AppendHerald[gargoyleData.feedback, "Select a single segment to set Units.", oneLiner];
GGError.Blink[gargoyleData.feedback];
RETURN;
};
distance ← GGVector.Distance[seg.lo, seg.hi];
ScaleUnitAux[gargoyleData, distance];
};
ScaleUnitFromValue: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
ScaleUnitAux[gargoyleData, gargoyleData.measure.radiusViewValue*gargoyleData.hitTest.scaleUnit];
};
ScaleUnitFromSelection: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
distance: REALNARROW[event.rest.first, REF REAL]^; -- in inches
BEGIN
IF distance < 0.0 THEN GOTO BadUnit;
ScaleUnitAux[gargoyleData, distance*72.0]; -- screen dots
EXITS
BadUnit => {
GGError.Append[gargoyleData.feedback, "Please select a real number (in inches) to be the scale unit.", oneLiner];
GGError.Blink[gargoyleData.feedback];
};
END;
};
InchScaleUnit: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
ScaleUnitAux[gargoyleData, pointsPerIn]; -- one inch in screen dots
};
CentimeterScaleUnit: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
ScaleUnitAux[gargoyleData, pointsPerCm]; -- 1 cm in screen dots
};
PrintScaleUnit: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
GGError.PutF[gargoyleData.feedback, oneLiner, "Current scale is %1.2f points = %1.2f inches = %1.2f centimeters", [real[gargoyleData.hitTest.scaleUnit]], [real[gargoyleData.hitTest.scaleUnit/pointsPerIn]], [real[gargoyleData.hitTest.scaleUnit/pointsPerCm]] ];
};
Slope Line
StandardSlopes: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
SlopePrompt[event, gargoyleData];
AtomButtons.BuildScalarButtons[gargoyleData.hitTest.slopeHeader, gargoyleData, GGUserInput.MenuNotify, 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, gargoyleData: gargoyleData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
};
SlopePrompt: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
firstButton: ScalarButtonClient ← gargoyleData.hitTest.slopeHeader.scalarButtons;
FOR thisButton: ScalarButtonClient ← firstButton, thisButton.next UNTIL thisButton = NIL DO
IF thisButton.on THEN ToggleSlope[LIST[NIL, NEW[REAL ← thisButton.value]], gargoyleData];
ENDLOOP;
};
AddSlope: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← 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[gargoyleData.measure.slopeView, Real.LargestNumber];
IF slope=Real.LargestNumber THEN {
GGError.AppendHerald[gargoyleData.feedback, "Attempt to add illegal slope value", oneLiner];
GGError.Blink[gargoyleData.feedback];
RETURN;
};
slope ← GGAngle.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, gargoyleData.measure.slopeViewValue, -10] THEN slope ← gargoyleData.measure.slopeViewValue ELSE gargoyleData.measure.slopeViewValue ← slope;
oldFoundButton ← AtomButtons.AddValueSorted[clientData: gargoyleData, scalarButtonHandle: gargoyleData.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, gargoyleData: gargoyleData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
};
};
AddSlopeInternal: PUBLIC PROC [gargoyleData: GargoyleData, 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;
gargoyleData.measure.slopeViewValue ← degrees;
oldFoundButton ← AtomButtons.AddValueSorted[clientData: gargoyleData, scalarButtonHandle: gargoyleData.hitTest.slopeHeader, value: [NIL, degrees, LIST[$ToggleSlope, NEW[REAL ← degrees]], off], order: decr];
};
DeleteSlope: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
NEVER delete very first button
prevButton: ScalarButtonClient ← gargoyleData.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;
gargoyleData.measure.slopeViewValue ← Real.LargestNumber; -- invalidate cached value
GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsDeselected, gargoyleData: gargoyleData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
};
GetSlope: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← 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[gargoyleData.scene, normal];
seq ← GGSequence.NextSequence[seqGen];
next ← GGSequence.NextSequence[seqGen];
IF seq = NIL OR next # NIL THEN {
GGError.Append[gargoyleData.feedback, "Select a single sequence for a GetSlope.", oneLiner];
GGError.Blink[gargoyleData.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[gargoyleData, gargoyleData.hitTest.slopeHeader, [NIL, degrees, LIST[$ToggleSlope, NEW[REAL ← degrees]], on], decr];
ENDLOOP;
GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, gargoyleData: gargoyleData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
};
ToggleSlope: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
slope: REAL;
firstButton, pushedButton: ScalarButtonClient;
tiogaButton: TiogaButtons.TiogaButton;
epsilon: REAL = 0.001;
WITH event.rest.first SELECT FROM
int: REF INT => slope ← int^;
real: REF REAL => slope ← real^;
ENDCASE => ERROR;
firstButton ← gargoyleData.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, gargoyleData: gargoyleData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
}
ELSE {
pushedButton.on ← TRUE;
TiogaButtons.ChangeButtonLooks[tiogaButton, "b", ""];
GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, gargoyleData: gargoyleData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
};
};
Angle Line
StandardAngles: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
AnglePrompt[event, gargoyleData];
AtomButtons.BuildScalarButtons[gargoyleData.hitTest.angleHeader, gargoyleData, GGUserInput.MenuNotify, 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, gargoyleData: gargoyleData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
};
AnglePrompt: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
firstButton: ScalarButtonClient ← gargoyleData.hitTest.angleHeader.scalarButtons;
FOR thisButton: ScalarButtonClient ← firstButton, thisButton.next UNTIL thisButton = NIL DO
IF thisButton.on THEN ToggleAngle[LIST[NIL, NEW[REAL ← thisButton.value]], gargoyleData];
ENDLOOP;
};
AddAngle: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
adds angle from angle viewer in measure line
oldFoundButton: AtomButtons.ScalarButtonClient;
See comments in AddSlope
angle: REAL ← GGViewerOps.GetReal[gargoyleData.measure.angleView, Real.LargestNumber];
IF angle=Real.LargestNumber THEN {
GGError.AppendHerald[gargoyleData.feedback, "Attempt to add illegal angle value", oneLiner];
GGError.Blink[gargoyleData.feedback];
RETURN;
};
angle ← GGAngle.Normalize[angle];
IF angle=360.0 THEN angle ← 0.0;
put the most accurate value we have into variable angle
IF RealFns.AlmostEqual[angle, gargoyleData.measure.angleViewValue, -10] THEN angle ← gargoyleData.measure.angleViewValue ELSE gargoyleData.measure.angleViewValue ← angle;
oldFoundButton ← AtomButtons.AddValueSorted[clientData: gargoyleData, scalarButtonHandle: gargoyleData.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, gargoyleData: gargoyleData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
}
};
AddAngleInternal: PUBLIC PROC [gargoyleData: GargoyleData, degrees: REAL] = {
oldFoundButton: AtomButtons.ScalarButtonClient;
IF degrees=360.0 THEN degrees ← 0.0;
gargoyleData.measure.angleViewValue ← degrees;
oldFoundButton ← AtomButtons.AddValueSorted[clientData: gargoyleData, scalarButtonHandle: gargoyleData.hitTest.angleHeader, value: [NIL, degrees, LIST[$ToggleAngle, NEW[REAL ← degrees]], off], order: decr];
};
DeleteAngle: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
NEVER delete very first button
prevButton: ScalarButtonClient ← gargoyleData.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;
gargoyleData.measure.angleViewValue ← Real.LargestNumber; -- invalidate cached value
GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsDeselected, gargoyleData: gargoyleData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
};
GetAngle: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← 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[gargoyleData.scene, normal];
seq ← GGSequence.NextSequence[seqGen];
next ← GGSequence.NextSequence[seqGen];
IF seq = NIL OR next # NIL THEN {
GGError.Append[gargoyleData.feedback, "Select a single sequence for a GetAngle.", oneLiner];
GGError.Blink[gargoyleData.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[gargoyleData, gargoyleData.hitTest.angleHeader, [NIL, degrees, LIST[$ToggleAngle, NEW[REAL ← degrees]], on], decr];
firstSeg ← secondSeg;
secondSeg← NIL;
ENDLOOP;
GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, gargoyleData: gargoyleData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
};
ToggleAngle: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
angle: REAL;
firstButton, pushedButton: ScalarButtonClient;
tiogaButton: TiogaButtons.TiogaButton;
epsilon: REAL = 0.001;
WITH event.rest.first SELECT FROM
int: REF INT => angle ← int^;
real: REF REAL => angle ← real^;
ENDCASE => ERROR;
firstButton ← gargoyleData.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, gargoyleData: gargoyleData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
}
ELSE {
pushedButton.on ← TRUE;
TiogaButtons.ChangeButtonLooks[tiogaButton, "b", ""];
GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, gargoyleData: gargoyleData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
};
};
Radius Line
StandardRadii: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
RadiusPrompt[event, gargoyleData];
AtomButtons.BuildScalarButtons[ gargoyleData.hitTest.radiusHeader, gargoyleData, GGUserInput.MenuNotify, 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, gargoyleData: gargoyleData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
};
RadiusPrompt: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
firstButton: ScalarButtonClient ← gargoyleData.hitTest.radiusHeader.scalarButtons;
FOR thisButton: ScalarButtonClient ← firstButton, thisButton.next UNTIL thisButton = NIL DO
IF thisButton.on THEN ToggleRadius[LIST[NIL, NEW[REAL ← thisButton.value]], gargoyleData];
ENDLOOP;
};
AddRadius: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
adds radius from Radius viewer in measure line
oldFoundButton: AtomButtons.ScalarButtonClient;
See comments in AddSlope
radius: REAL ← GGViewerOps.GetPositiveReal[gargoyleData.measure.radiusView, Real.LargestNumber];
IF radius=Real.LargestNumber THEN {
GGError.Append[gargoyleData.feedback, "Attempt to add illegal radius value", oneLiner];
GGError.Blink[gargoyleData.feedback];
RETURN;
};
put the most accurate value we have into variable radius
IF RealFns.AlmostEqual[radius, gargoyleData.measure.radiusViewValue, -10] THEN radius ← gargoyleData.measure.radiusViewValue ELSE gargoyleData.measure.radiusViewValue ← radius;
oldFoundButton ← AtomButtons.AddValueSorted[clientData: gargoyleData, scalarButtonHandle: gargoyleData.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, gargoyleData: gargoyleData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
};
};
AddRadiusInternal: PUBLIC PROC [gargoyleData: GargoyleData, radius: REAL] = {
oldFoundButton: AtomButtons.ScalarButtonClient;
gargoyleData.measure.radiusViewValue ← radius;
oldFoundButton ← AtomButtons.AddValueSorted[clientData: gargoyleData, scalarButtonHandle: gargoyleData.hitTest.radiusHeader, value: [NIL, radius, LIST[$ToggleRadius, NEW[REAL ← radius]], off], order: incr];
};
DeleteRadius: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
NEVER delete very first button
prevButton: ScalarButtonClient ← gargoyleData.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;
gargoyleData.measure.radiusViewValue ← Real.LargestNumber; -- invalidate cached value
GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsDeselected, gargoyleData: gargoyleData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
};
GetRadius: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
oldFoundButton: AtomButtons.ScalarButtonClient;
seqGen: SequenceGenerator;
segGen: SegmentGenerator;
seq, next: Sequence;
radius: REAL;
seqGen ← GGSelect.SelectedSequences[gargoyleData.scene, normal];
seq ← GGSequence.NextSequence[seqGen];
next ← GGSequence.NextSequence[seqGen];
IF seq = NIL OR next # NIL THEN {
GGError.Append[gargoyleData.feedback, "Select a single segment for GetRadius.", oneLiner];
GGError.Blink[gargoyleData.feedback];
RETURN;
};
segGen ← GGSequence.SegmentsInSequence[seq];
FOR seg: Segment ← GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen] UNTIL seg = NIL DO
radius ← GGVector.Distance[seg.lo, seg.hi]/gargoyleData.hitTest.scaleUnit;
oldFoundButton ← AtomButtons.AddValueSorted[gargoyleData, gargoyleData.hitTest.radiusHeader, [NIL, radius, LIST[$ToggleRadius, NEW[REAL ← radius]], on], incr];
ENDLOOP;
GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, gargoyleData: gargoyleData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
};
GetRadius: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
oldFoundButton: AtomButtons.ScalarButtonClient;
sliceDescGen: GGModelTypes.SliceDescriptorGenerator;
outDGen: GGModelTypes.OutlineDescriptorGenerator;
pointGen: GGModelTypes.PointGenerator;
oPointGen: GGModelTypes.OutlinePointGenerator;
radius: REAL;
pointAndDone: GGModelTypes.PointAndDone;
sliceD, nextD: GGModelTypes.SliceDescriptor;
outlineD, nextOutlineD: GGModelTypes.OutlineDescriptor;
p0, p1: Point;
BEGIN
sliceDescGen ← GGSelect.SelectedSlices[gargoyleData.scene, normal];
sliceD ← GGSelect.NextSliceDescriptor[sliceDescGen];
IF sliceD = NIL THEN {
outDGen ← GGSelect.SelectedOutlines[gargoyleData.scene, normal];
outlineD ← GGSelect.NextOutlineDescriptor[outDGen];
IF outlineD = NIL THEN GOTO Problem;
nextOutlineD ← GGSelect.NextOutlineDescriptor[outDGen];
IF nextOutlineD # NIL THEN GOTO Problem;
oPointGen ← outlineD.slice.class.pointsInDescriptor[outlineD];
pointAndDone ← outlineD.slice.class.nextPoint[oPointGen];
IF pointAndDone.done THEN GOTO Problem;
p0 ← pointAndDone.point;
pointAndDone ← outlineD.slice.class.nextPoint[oPointGen];
IF pointAndDone.done THEN GOTO Problem;
p1 ← pointAndDone.point;
pointAndDone ← outlineD.slice.class.nextPoint[oPointGen];
IF NOT pointAndDone.done THEN GOTO Problem;
}
ELSE {
nextD ← GGSelect.NextSliceDescriptor[sliceDescGen];
IF nextD # NIL THEN GOTO Problem;
pointGen ← sliceD.slice.class.pointsInDescriptor[sliceD];
pointAndDone ← sliceD.slice.class.nextPoint[pointGen];
IF pointAndDone.done THEN GOTO Problem;
p0 ← pointAndDone.point;
pointAndDone ← sliceD.slice.class.nextPoint[pointGen];
IF pointAndDone.done THEN GOTO Problem;
p1 ← pointAndDone.point;
pointAndDone ← sliceD.slice.class.nextPoint[pointGen];
IF NOT pointAndDone.done THEN GOTO Problem;
};
EXITS
Problem => {
GGError.Append[gargoyleData.feedback, "Select a single segment for GetRadius.", oneLiner];
GGError.Blink[gargoyleData.feedback];
RETURN;
};
END;
radius ← GGMeasure.DistanceBetweenPoints[p0, p1]/gargoyleData.hitTest.scaleUnit;
oldFoundButton ← AtomButtons.AddValueSorted[gargoyleData, gargoyleData.hitTest.radiusHeader, [NIL, radius, LIST[$ToggleRadius, NEW[REAL ← radius]], on], incr];
GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, gargoyleData: gargoyleData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
};
ToggleRadius: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
radius: REAL;
epsilon: REAL = 0.001;
firstButton, pushedButton: ScalarButtonClient;
tiogaButton: TiogaButtons.TiogaButton;
WITH event.rest.first SELECT FROM
int: REF INT => radius ← int^;
real: REF REAL => radius ← real^;
ENDCASE => ERROR;
firstButton ← gargoyleData.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, gargoyleData: gargoyleData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
}
ELSE {
pushedButton.on ← TRUE;
TiogaButtons.ChangeButtonLooks[tiogaButton, "b", ""];
GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, gargoyleData: gargoyleData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
};
};
Distance Line
StandardDistances: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
DistancePrompt[event, gargoyleData];
AtomButtons.BuildScalarButtons[gargoyleData.hitTest.distanceHeader, gargoyleData, GGUserInput.MenuNotify, 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, gargoyleData: gargoyleData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
};
DistancePrompt: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
firstButton: ScalarButtonClient ← gargoyleData.hitTest.distanceHeader.scalarButtons;
FOR thisButton: ScalarButtonClient ← firstButton, thisButton.next UNTIL thisButton = NIL DO
IF thisButton.on THEN ToggleDistance[LIST[NIL, NEW[REAL ← thisButton.value]], gargoyleData];
ENDLOOP;
};
AddDistance: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
adds distance from LineDistance viewer in measure line
oldFoundButton: AtomButtons.ScalarButtonClient;
See comments in AddSlope
distance: REAL ← GGViewerOps.GetReal[gargoyleData.measure.lineDistView, Real.LargestNumber];
IF distance=Real.LargestNumber THEN {
GGError.Append[gargoyleData.feedback, "Attempt to add illegal line distance value", oneLiner];
GGError.Blink[gargoyleData.feedback];
RETURN;
};
put the most accurate value we have into variable distance
IF RealFns.AlmostEqual[distance, gargoyleData.measure.lineDistViewValue, -10] THEN distance ← gargoyleData.measure.lineDistViewValue ELSE gargoyleData.measure.lineDistViewValue ← distance;
oldFoundButton ← AtomButtons.AddValueSorted[clientData: gargoyleData, scalarButtonHandle: gargoyleData.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, gargoyleData: gargoyleData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
};
};
AddDistanceInternal: PUBLIC PROC [gargoyleData: GargoyleData, distance: REAL] = {
oldFoundButton: AtomButtons.ScalarButtonClient;
gargoyleData.measure.lineDistViewValue ← distance;
oldFoundButton ← AtomButtons.AddValueSorted[clientData: gargoyleData, scalarButtonHandle: gargoyleData.hitTest.distanceHeader, value: [NIL, distance, LIST[$ToggleDistance, NEW[REAL ← distance]], off], order: incr];
};
DeleteDistance: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
NEVER delete very first button
prevButton: ScalarButtonClient ← gargoyleData.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;
gargoyleData.measure.lineDistViewValue ← Real.LargestNumber; -- invalidate cached value
GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsDeselected, gargoyleData: gargoyleData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
};
GetDistance: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
oldFoundButton: AtomButtons.ScalarButtonClient;
seqGen: SequenceGenerator;
segGen: SegmentGenerator;
seq, next: Sequence;
dist: REAL;
seqGen ← GGSelect.SelectedSequences[gargoyleData.scene, normal];
seq ← GGSequence.NextSequence[seqGen];
next ← GGSequence.NextSequence[seqGen];
IF seq = NIL OR next # NIL THEN {
GGError.Append[gargoyleData.feedback, "Select a single sequence for a GetDistance.", oneLiner];
GGError.Blink[gargoyleData.feedback];
RETURN;
};
segGen ← GGSequence.SegmentsInSequence[seq];
FOR seg: Segment ← GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen] UNTIL seg = NIL DO
dist ← GGMeasure.LengthOfSegment[seg]/gargoyleData.hitTest.scaleUnit;
oldFoundButton ← AtomButtons.AddValueSorted[gargoyleData, gargoyleData.hitTest.distanceHeader, [NIL, dist, LIST[$ToggleDistance, NEW[REAL ← dist]], on], incr];
ENDLOOP;
GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, gargoyleData: gargoyleData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
};
ToggleDistance: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
dist: REAL;
epsilon: REAL = 0.001;
firstButton, pushedButton: ScalarButtonClient;
tiogaButton: TiogaButtons.TiogaButton;
WITH event.rest.first SELECT FROM
int: REF INT => dist ← int^;
real: REF REAL => dist ← real^;
ENDCASE => ERROR;
firstButton ← gargoyleData.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, gargoyleData: gargoyleData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
}
ELSE {
pushedButton.on ← TRUE;
TiogaButtons.ChangeButtonLooks[tiogaButton, "b", ""];
GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, gargoyleData: gargoyleData, remake: objectBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE];
};
};
Coordinate/Measure Line
MeasureSlopeHit: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
viewer: Viewer ← gargoyleData.measure.slopeView;
ViewerTools.SetSelection[viewer];
};
MeasureAngleHit: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
viewer: Viewer ← gargoyleData.measure.angleView;
ViewerTools.SetSelection[viewer];
};
MeasureRadiusHit: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
viewer: Viewer ← gargoyleData.measure.radiusView;
ViewerTools.SetSelection[viewer];
};
MeasureLineDistHit: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← NARROW[clientData];
viewer: Viewer ← gargoyleData.measure.lineDistView;
ViewerTools.SetSelection[viewer];
};
Miscellaneous
DeleteCaretSegment: PUBLIC PROC [event: LIST OF REF ANY, clientData: REF ANY] = {
gargoyleData: GargoyleData ← 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 ← gargoyleData.caret;
point: Point;
repaintBox: BoundBox ← GGCaret.BoundBoxOfCaret[caret, gargoyleData]; -- start with old caret
IF GGCaret.SittingOnEnd[caret] THEN { -- delete the joint and segment near the caret
chair ← GGCaret.GetChair[caret];
outlineD ← NARROW[chair];
[traj: traj, jointNum: jointNum] ← GGOutline.UnpackSimpleDescriptorOld[outlineD];
bBoxOfTraj ← GGTraj.GetBoundBox[traj];
SELECT jointNum FROM
0 => {newEndJoint ← 1; trajEnd ← lo};
GGTraj.HiJoint[traj] => {newEndJoint ← jointNum -1; trajEnd ← hi};
ENDCASE => SIGNAL Problem[msg: "failed assertion"];
point ← GGTraj.FetchJointPos[traj, newEndJoint];
delSeq ← GGSequence.LastSegAndJoint[traj, trajEnd];
[----, newOutlines] ← GGInterface.DeleteSequence[delSeq, gargoyleData.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[gargoyleData.caret, jointD];
};
GGCaret.SetAttractor[gargoyleData.caret, point, NIL];
GGBoundBox.EnlargeByBox[repaintBox, bBoxOfTraj]; -- repaint deleted traj
GGBoundBox.EnlargeByBox[repaintBox, GGCaret.BoundBoxOfCaret[caret, gargoyleData] ];
gargoyleData.refresh.startBoundBox^ ← repaintBox^;
GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE];
}
ELSE {
GGError.Append[gargoyleData.feedback, "Place the caret on the end of a trajectory to BackSpace.", oneLiner];
};
};
END.
Pier, October 21, 1986 9:20:05 pm PDT
changed all "edited: TRUE" to "edited: FALSE" in all NewAlignment* procs