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 REAL ← NARROW[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.ROPE ← NARROW[event.rest.first];
argStream: IO.STREAM ← IO.RIS[argRope];
pattern: SequenceOfReal;
offset, length: REAL;
pattern ← GGParseIn.ReadArrayOfReal[argStream];
offset ← GGParseIn.ReadBlankAndReal[argStream];
length ← GGParseIn.ReadBlankAndReal[argStream];
BEGIN
entityGen: GGModelTypes.EntityGenerator;
bBox: BoundBox;
bBox ← GGBoundBox.BoundBoxOfSelected[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: BOOL← FALSE;
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: REAL ← NARROW[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.ROPE ← NARROW[event.rest.first];
argStream: IO.STREAM ← IO.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];
};
};
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: INT ← NARROW[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: BOOL ← TRUE;
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: REAL ← NARROW[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;
};
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