GGAlignImpl.mesa
Created by Eric Bier on June 5, 1985 3:41:39 pm PDT.
Last edited by Bier on April 20, 1987 6:46:44 pm PDT.
Pier, May 15, 1987 6:15:56 pm PDT
DIRECTORY
AtomButtons, CodeTimer, Feedback, GGAlign, GGBasicTypes, GGCaret, GGGravity, GGInterfaceTypes, GGModelTypes, GGScene, GGOutline, GGSegmentTypes, GGSelect, GGSequence, GGSlice, GGState, GGTraj, GList, Imager, Process, Rope, Vectors2d;
GGAlignImpl:
CEDAR
PROGRAM
IMPORTS CodeTimer, Feedback, GGCaret, GGGravity, GGScene, GGOutline, GGSelect, GGSequence, GGSlice, GGState, GGTraj, GList, Process, Vectors2d
EXPORTS GGAlign = BEGIN
AlignmentLine: TYPE = GGInterfaceTypes.AlignmentLine;
AlignmentObject: TYPE = GGModelTypes.AlignmentObject;
Angle: TYPE = GGBasicTypes.Angle;
Caret: TYPE = GGInterfaceTypes.Caret;
Circle: TYPE = GGBasicTypes.Circle;
ControlPointGenerator: TYPE = GGModelTypes.ControlPointGenerator;
Edge: TYPE = GGBasicTypes.Edge;
EntityGenerator: TYPE = GGScene.EntityGenerator;
FeatureData: TYPE = REF FeatureDataObj;
FeatureDataObj: TYPE = GGModelTypes.FeatureDataObj;
FilterOutlineProc: TYPE = GGAlign.FilterOutlineProc;
FilterSliceProc: TYPE = GGAlign.FilterSliceProc;
Filters: TYPE = GGInterfaceTypes.Filters;
GGData: TYPE = GGInterfaceTypes.GGData;
Joint: TYPE = GGModelTypes.Joint;
JointGenerator: TYPE = GGModelTypes.JointGenerator;
Line: TYPE = GGBasicTypes.Line;
AlignBag: TYPE = GGInterfaceTypes.AlignBag;
AlignBagObj: TYPE = GGInterfaceTypes.AlignBagObj;
Outline: TYPE = GGModelTypes.Outline;
OutlineData: TYPE = GGOutline.OutlineData;
OutlineDescriptor: TYPE = REF OutlineDescriptorObj;
OutlineDescriptorObj: TYPE = GGModelTypes.OutlineDescriptorObj;
Point: TYPE = GGBasicTypes.Point;
PointAndDone: TYPE = GGModelTypes.PointAndDone;
PointGenerator: TYPE = GGModelTypes.PointGenerator;
PointPairGenerator: TYPE = GGModelTypes.PointPairGenerator;
ScalarButtonClient: TYPE = AtomButtons.ScalarButtonClient;
Scene: TYPE = GGModelTypes.Scene;
Segment: TYPE = GGSegmentTypes.Segment;
SegmentGenerator: TYPE = GGModelTypes.SegmentGenerator;
SelectionClass: TYPE = GGInterfaceTypes.SelectionClass;
Sequence: TYPE = GGModelTypes.Sequence;
SequenceGenerator: TYPE = GGModelTypes.SequenceGenerator;
Slice: TYPE = GGModelTypes.Slice;
SliceDescriptor: TYPE = REF SliceDescriptorObj;
SliceDescriptorObj: TYPE = GGModelTypes.SliceDescriptorObj;
SliceDescriptorGenerator: TYPE = GGModelTypes.SliceDescriptorGenerator;
SliceGenerator: TYPE = GGModelTypes.SliceGenerator;
SliceParts: TYPE = GGModelTypes.SliceParts;
Traj: TYPE = GGModelTypes.Traj;
TrajEnd: TYPE = GGModelTypes.TrajEnd;
TrajGenerator: TYPE = GGScene.TrajGenerator;
TriggerBag: TYPE = REF TriggerBagObj;
TriggerBagObj: TYPE = GGInterfaceTypes.TriggerBagObj;
Vector: TYPE = GGBasicTypes.Vector;
Problem: SIGNAL [msg: Rope.ROPE] = Feedback.Problem;
The TriggerBag, the SceneBag and the AlignBag
The set of alignment objects is computed in three steps (see Figure 1). First, we determine which joints, control points, and segments in the scene are triggers. We put these in triggerBag. Next we determine which scene objects are gravity active. We put these in sceneBag. Finally, we determine the set of alignment objects from the triggerBag using the current filters and from the sceneBag if midpoints are activated. This set is represented by the selected slopes, angles, radii, and distances in the user interface.
The bags are recomputed at these times:
triggerBag: Updated whenever an Add or Drag operation is performed, or whenever anything becomes hot or cold.
sceneBag: Updated whenever an Add or Drag operation is performed.
objectBag. Updated whenever an Add or Drag operation is performed, whenever anything becomes hot or cold, or whenever the set of current filters changes.
The bags are represented as follows:
triggerBag and sceneBag: several LIST OF FeatureData. The FeatureData will point to a SliceDescriptor, --an OutlineDescriptor,-- or the anchor.
objectBag: several LIST OF FeatureData. The FeatureData will point to an AlignmentLine, an AlignmentCircle, an AlignmentPoint, or a Caret (e.g. the anchor).
EntityNotFound: PUBLIC SIGNAL = CODE;
UnexpectedType: PUBLIC ERROR = CODE;
BrokenInvariant: PUBLIC ERROR = CODE;
[Artwork node; type 'ArtworkInterpress on' to command tool]
Creating a TriggerBag
emptyTriggerBag: PUBLIC TriggerBag;
CreateTriggerBag:
PUBLIC
PROC []
RETURNS [triggerBag: TriggerBag] = {
triggerBag ← NEW[TriggerBagObj ← [slices: NIL, intersectionPoints: NIL, anchor: NIL]];
};
FlushTriggerBag:
PUBLIC PROC [triggerBag: TriggerBag] = {
triggerBag.outlines ← NIL;
triggerBag.slices ← NIL;
triggerBag.intersectionPoints ← NIL;
triggerBag.anchor ← NIL;
};
CopyTriggerBag:
PUBLIC
PROC [triggerBag: TriggerBag]
RETURNS [copy: TriggerBag] = {
copy ← CreateTriggerBag[];
copy.outlines ← NARROW[GList.Copy[triggerBag.outlines]];
copy.slices ← NARROW[GList.Copy[triggerBag.slices]];
copy.intersectionPoints ← NIL;
copy.anchor ← triggerBag.anchor;
};
Creating an AlignBag
emptyAlignBag: PUBLIC AlignBag;
CreateAlignBag:
PUBLIC
PROC []
RETURNS [alignBag: AlignBag] = {
alignBag ←
NEW[AlignBagObj ← [
slopeLines: NIL,
angleLines: NIL,
radiiCircles: NIL,
distanceLines: NIL,
midpoints: NIL,
intersectionPoints: NIL,
anchor: NIL
]];
};
FlushAlignBag:
PUBLIC
PROC [alignBag: AlignBag] = {
alignBag.slopeLines ← NIL;
alignBag.angleLines ← NIL;
alignBag.radiiCircles ← NIL;
alignBag.distanceLines ← NIL;
alignBag.midpoints ← NIL;
alignBag.intersectionPoints ← NIL;
alignBag.anchor ← NIL;
};
Filling the Trigger Bag
FillStaticTriggerBag:
PUBLIC PROC [anchor: Caret, scene: Scene, heuristics:
BOOL, triggerBag: TriggerBag] = {
AddAnchorTrigger[anchor, triggerBag];
AddAllHotOutlines[scene, triggerBag];
AddAllHotSlices[scene, triggerBag];
};
FillDynamicTriggerBag:
PUBLIC PROC [anchor: Caret, scene: Scene, heuristics:
BOOL, triggerBag: TriggerBag] = {
AddAnchorTrigger[anchor, triggerBag];
AddAllHotOutlines[scene, triggerBag];
AddAllHotSlices[scene, triggerBag];
AddHeuristics[scene, heuristics, $Drag, triggerBag]; -- nothing is being dragged
RemoveMoving[scene, triggerBag]; -- nothing is moving
};
Filling the SceneBag
FillStaticSceneBag:
PUBLIC
PROC [scene: Scene, sceneBag: TriggerBag] = {
AddAllTrajectories[scene, sceneBag];
AddAllSlices[scene, sceneBag];
ComputeAllBoundingBoxes[sceneBag];
};
FillDynamicSceneBag:
PUBLIC
PROC [scene: Scene, sceneBag: TriggerBag] = {
AddAllTrajectories[scene, sceneBag];
AddAllSlices[scene, sceneBag];
RemoveMoving[scene, sceneBag];
ComputeAllBoundingBoxes[sceneBag];
};
Filling the Align Bag
FillStaticAlignBag:
PUBLIC
PROC [triggerBag: TriggerBag, sceneBag: TriggerBag, filters: Filters, hideAlignments:
BOOL, midpoints:
BOOL, alignBag: AlignBag] = {
BuiltInFilters[triggerBag, filters, hideAlignments, alignBag];
AddAllMidpoints[sceneBag, midpoints, alignBag];
};
FillDynamicAlignBag:
PUBLIC
PROC [triggerBag: TriggerBag, sceneBag: TriggerBag, filters: Filters, hideAlignments:
BOOL, midpoints:
BOOL, action:
ATOM, alignBag: AlignBag] = {
BuiltInFilters[triggerBag, filters, hideAlignments, alignBag];
AddAllMidpoints[sceneBag, midpoints, alignBag];
};
Filling all three Bags at Once
SetStaticBags:
PUBLIC
PROC [ggData: GGData] = {
We have two trigger bags and one object bag to get into shape.
The Trigger Bag should already contain the anchor, and all hot objects. For now, however, we will put these into the bag here. Next, we remove all objects which are going to be moving during the upcoming operation. For selection operations, we remove all triggers.
The Scene Bag will be empty. We fill it with all of the scene objects. Then, we remove those objects which are going to be moving during the upcoming operation.
The Align Bag is made by filtering the Trigger Bag through the currently selected filters.
triggerBag: TriggerBag ← ggData.hitTest.triggerBag;
alignBag: AlignBag ← ggData.hitTest.alignBag;
sceneBag: TriggerBag ← ggData.hitTest.sceneBag;
scene: Scene ← ggData.scene;
anchor: Caret ← ggData.anchor;
heuristics: BOOL ← GGState.Heuristics[ggData];
filters: Filters ← ggData.hitTest;
hideAlignments: BOOL ← NOT GGState.ShowAlignments[ggData];
midpoints: BOOL ← GGState.Midpoints[ggData];
IF hideAlignments
THEN {
FlushAlignBag[alignBag];
FlushTriggerBag[sceneBag];
FillStaticSceneBag[scene, sceneBag];
}
ELSE {
CodeTimer.StartInt[$SetBagsForAction, $Gargoyle];
Fill TriggerBag
FlushTriggerBag[triggerBag];
FillStaticTriggerBag[anchor, scene, heuristics, triggerBag];
Fill Scene Bag
FlushTriggerBag[sceneBag];
FillStaticSceneBag[scene, sceneBag];
Fill Align Bag
FlushAlignBag[alignBag];
FillStaticAlignBag[triggerBag, sceneBag, filters, hideAlignments, midpoints, alignBag];
CodeTimer.StopInt[$SetBagsForAction, $Gargoyle];
};
};
SetDynamicBags:
PUBLIC
PROC [ggData: GGData, action:
ATOM] = {
We have two trigger bags and one object bag to get into shape.
The Trigger Bag should already contain the anchor, and all hot objects. For now, however, we will put these into the bag here. Next, we remove all objects which are going to be moving during the upcoming operation. For selection operations, we remove all triggers.
The Scene Bag will be empty. We fill it with all of the scene objects. Then, we remove those objects which are going to be moving during the upcoming operation.
The Align Bag is made by filtering the Trigger Bag through the currently selected filters.
triggerBag: TriggerBag ← ggData.hitTest.triggerBag;
alignBag: AlignBag ← ggData.hitTest.alignBag;
sceneBag: TriggerBag ← ggData.hitTest.sceneBag;
scene: Scene ← ggData.scene;
anchor: Caret ← ggData.anchor;
heuristics: BOOL ← GGState.Heuristics[ggData];
filters: Filters ← ggData.hitTest;
hideAlignments: BOOL ← NOT GGState.ShowAlignments[ggData];
midpoints: BOOL ← GGState.Midpoints[ggData];
IF hideAlignments
THEN {
FlushAlignBag[alignBag];
FlushTriggerBag[sceneBag];
FlushTriggerBag[triggerBag]; -- added March 30, 1987. KAP
FillDynamicSceneBag[scene, sceneBag];
}
ELSE {
CodeTimer.StartInt[$SetBagsForAction, $Gargoyle];
Fill TriggerBag
FlushTriggerBag[triggerBag];
FillDynamicTriggerBag[anchor, scene, heuristics, triggerBag];
Fill Scene Bag
FlushTriggerBag[sceneBag];
FillDynamicSceneBag[scene, sceneBag];
Fill Align Bag
FlushAlignBag[alignBag];
FillDynamicAlignBag[triggerBag, sceneBag, filters, hideAlignments, midpoints, action, alignBag];
CodeTimer.StopInt[$SetBagsForAction, $Gargoyle];
};
};
StaticToDynamicBags:
PUBLIC
PROC [ggData: GGData]
RETURNS [repaintForeground:
BOOL] = {
We are about to drag things around. Here is what that means for each of the bags:
triggerBag: AddHeuristics may add some things. RemoveMoving is going to remove some things.
sceneBag: RemoveMoving is going to remove some things.
alignBag: Those things that RemoveMoving now removes will no longer trigger midpoints, nor alignment lines.
repaintForeground is TRUE if alignBag is altered.
triggerBag: TriggerBag ← ggData.hitTest.triggerBag;
sceneBag: TriggerBag ← ggData.hitTest.sceneBag;
alignBag: AlignBag ← ggData.hitTest.alignBag;
heuristics: BOOL ← GGState.Heuristics[ggData];
filters: Filters ← ggData.hitTest;
hideAlignments: BOOL ← NOT GGState.ShowAlignments[ggData];
midpoints: BOOL ← GGState.Midpoints[ggData];
repaintForeground ← SomeSelectedIsHot[ggData.scene] OR GGState.Heuristics[ggData];
IF ggData.camera.hideAlignments
THEN {
No alignments. Just build sceneBag.
repaintForeground ← FALSE;
FlushAlignBag[alignBag];
ggData.hitTest.oldSceneBag ← sceneBag;
sceneBag ← StaticToDynamicSceneBag[ggData.scene, sceneBag];
ggData.hitTest.sceneBag ← sceneBag;
}
ELSE {
CodeTimer.StartInt[$UpdateBagsForAction, $Gargoyle];
Incrementally Update TriggerBag
ggData.hitTest.oldTriggerBag ← triggerBag;
triggerBag ← CopyTriggerBag[triggerBag];
AddHeuristics[ggData.scene, heuristics, $Drag, triggerBag];
RemoveMoving[ggData.scene, triggerBag];
ggData.hitTest.triggerBag ← triggerBag;
Incrementally Update SceneBag
ggData.hitTest.oldSceneBag ← sceneBag;
sceneBag ← StaticToDynamicSceneBag[ggData.scene, sceneBag];
ggData.hitTest.sceneBag ← sceneBag;
Remake AlignBag from Scratch
ggData.hitTest.oldAlignBag ← alignBag;
alignBag ← CreateAlignBag[];
BuiltInFilters[triggerBag, filters, hideAlignments, alignBag];
AddAllMidpoints[sceneBag, midpoints, alignBag];
ggData.hitTest.alignBag ← alignBag;
CodeTimer.StopInt[$UpdateBagsForAction, $Gargoyle];
};
};
StaticToDynamicSceneBag:
PROC [scene: Scene, sceneBag: TriggerBag]
RETURNS [newBag: TriggerBag] = {
newBag ← CopyTriggerBag[sceneBag];
RemoveMoving[scene, newBag];
CodeTimer.StartInt[$ComputeBoundBoxes, $Gargoyle];
ComputeAllBoundingBoxes[newBag];
CodeTimer.StopInt[$ComputeBoundBoxes, $Gargoyle];
};
DynamicToStaticBags:
PUBLIC
PROC [ggData: GGData]
RETURNS [repaintForeground:
BOOL] = {
triggerBag: TriggerBag;
sceneBag: TriggerBag;
alignBag: AlignBag ← ggData.hitTest.alignBag;
filters: Filters ← ggData.hitTest;
hideAlignments: BOOL ← NOT GGState.ShowAlignments[ggData];
midpoints: BOOL ← GGState.Midpoints[ggData];
Use the old TriggerBag and SceneBag.
triggerBag ← ggData.hitTest.triggerBag ← ggData.hitTest.oldTriggerBag;
sceneBag ← ggData.hitTest.sceneBag ← ggData.hitTest.oldSceneBag;
ComputeAllBoundingBoxes[sceneBag];
Remake AlignBag from Scratch
alignBag ← CreateAlignBag[];
BuiltInFilters[triggerBag, filters, hideAlignments, alignBag];
AddAllMidpoints[sceneBag, midpoints, alignBag];
ggData.hitTest.alignBag ← alignBag;
repaintForeground ← TRUE;
};
UpdateBagsForAdd:
PUBLIC
PROC [oldOutline: Outline, newOutline: Outline, trajEnd: TrajEnd, ggData: GGData]
RETURNS [repaintForeground:
BOOL] = {
newSeg is the number, in newOutline of the newly added segment.
If this operation is not a continuation then the bags should be updated as follows:
Trigger: Remove old outline descriptor. Add new hotness descriptor for newOutline.
Align: Remove mentions of old outline. Use filters to add new mentions.
Scene: Remove old outline descriptor. Add a complete descriptor for newOutline.
triggerBag: TriggerBag ← ggData.hitTest.triggerBag;
sceneBag: TriggerBag ← ggData.hitTest.sceneBag;
alignBag: AlignBag ← ggData.hitTest.alignBag;
scene: Scene ← ggData.scene;
newHot, oldHot, newWhole, oldWhole, hotMinusOldHot: OutlineDescriptor;
hideAlignments: BOOL ← NOT GGState.ShowAlignments[ggData];
midpoints: BOOL ← GGState.Midpoints[ggData];
alignObjects: LIST OF FeatureData;
Fixing the TriggerBag.
oldHot ← RemoveEntireHotOutline[oldOutline, triggerBag];
oldHot ← RemoveEntireHotSlice[oldOutline, triggerBag];
newHot ← GGSelect.FindSelectedSlice[newOutline, scene, hot];
We can ignore Heuristics because there is no motion.
We don't need to remove moving parts because there aren't any.
IF newHot # NIL THEN [] ← AddHotOutline[newHot, triggerBag];
IF newHot # NIL THEN [] ← AddHotSlice[newHot, triggerBag];
Fixing the SceneBag.
oldWhole ← RemoveEntireHotOutline[oldOutline, sceneBag];
oldWhole ← RemoveEntireHotSlice[oldOutline, sceneBag];
newWhole ← newOutline.class.newParts[newOutline, NIL, slice];
GGSlice.UpdateDescriptorBoundBoxes[newWhole];
[] ← AddHotOutline[newWhole, sceneBag];
[] ← AddHotSlice[newWhole, sceneBag];
Fixing the AlignBag.
We assume that the bags were set up static before this. Hence, there are two simple possibilities. Either the newly added segment is hot, or it isn't.
IF newHot = NIL THEN hotMinusOldHot ← NIL
ELSE {
mask: Sequence;
maskD: OutlineDescriptor;
newOutlineData: OutlineData ← NARROW[newOutline.data];
traj: Traj ← newOutlineData.children.first;
IF trajEnd = lo THEN mask ← GGSequence.Union[GGSequence.CreateFromSegment[traj, 0], GGSequence.CreateFromJoint[traj, 0]]
ELSE mask ← GGSequence.Union[GGSequence.CreateFromSegment[traj, GGTraj.HiSegment[traj]], GGSequence.CreateFromJoint[traj, GGTraj.HiJoint[traj]]];
maskD ← GGOutline.DescriptorFromSequence[newOutline, mask];
hotMinusOldHot ← newOutline.class.differenceParts[newHot, newOutline.class.differenceParts[newWhole, maskD]];
alignObjects ← IncrementalFilterOutline[hotMinusOldHot, ggData.hitTest, hideAlignments, alignBag];
alignObjects ← IncrementalFilterSlice[hotMinusOldHot, ggData.hitTest, hideAlignments, alignBag];
};
IncrementalMidpointsOutline[newWhole, midpoints, alignBag];
IncrementalMidpointsSlice[newWhole, midpoints, alignBag];
Fixing the Foreground Plane.
dc ← BufferedRefresh.GetLayerContext[ggData.refresh.sandwich, $Foreground];
GGGravity.DrawFeatureList[dc, alignObjects, ggData];
repaintForeground ← FALSE;
};
UpdateBagsForNewSlices:
PUBLIC
PROC [newSlices:
LIST
OF Slice, ggData: GGData] = {
Assuming that the new slices cannot be hot, the bags should be updated as follows:
Trigger: No change.
Scene: Add a complete descriptor for each new slice.
Align: Add midpoints for each new slice.
triggerBag: TriggerBag ← ggData.hitTest.triggerBag;
sceneBag: TriggerBag ← ggData.hitTest.sceneBag;
alignBag: AlignBag ← ggData.hitTest.alignBag;
scene: Scene ← ggData.scene;
hideAlignments: BOOL ← NOT GGState.ShowAlignments[ggData];
midpoints: BOOL ← GGState.Midpoints[ggData];
Fixing the SceneBag and AlignBag.
FOR list:
LIST
OF Slice ← newSlices, list.rest
UNTIL list =
NIL
DO
wholeD: SliceDescriptor ← list.first.class.newParts[list.first, NIL, slice];
GGSlice.UpdateDescriptorBoundBoxes[wholeD];
[] ← AddHotSlice[wholeD, sceneBag];
IncrementalMidpointsSlice[wholeD, midpoints, alignBag];
ENDLOOP;
};
UpdateBagsForDelete: PUBLIC PROC [oldOutline: Outline, ggData: GGData] RETURNS [repaintForeground: BOOL] = {
};
The Filter Routines shown in the figure as boxes and ovals
AddAnchorTrigger:
PROC [anchor: Caret, triggerBag: TriggerBag] = {
[] ← CreateAnchorTrigger[anchor, triggerBag];
};
AddAllHotOutlines: PROC [scene: Scene, triggerBag: TriggerBag] = {
feature: FeatureData;
outDGen: GGModelTypes.OutlineDescriptorGenerator;
outDGen ← GGSelect.SelectedOutlines[scene, hot];
FOR outlineD: OutlineDescriptor ← GGSelect.NextSliceDescriptor[outDGen], GGSelect.NextSliceDescriptor[outDGen] UNTIL outlineD = NIL DO
feature ← GGGravity.FeatureFromOutline[outlineD.slice, outlineD.parts];
AddFeature[feature, triggerBag];
ENDLOOP;
};
AddAllHotSlices:
PROC [scene: Scene, triggerBag: TriggerBag] = {
feature: FeatureData;
sliceDescGen: SliceDescriptorGenerator ← GGSelect.SelectedSlices[scene, hot];
FOR sliceD: SliceDescriptor ← GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen]
UNTIL sliceD =
NIL
DO
feature ← GGGravity.FeatureFromSlice[sliceD.slice, sliceD.parts];
AddFeature[feature, triggerBag];
ENDLOOP;
};
AddHeuristics:
PROC [scene: Scene, heuristics:
BOOL, atom:
ATOM, triggerBag: TriggerBag] = {
feature: FeatureData;
IF NOT heuristics THEN RETURN;
SELECT atom
FROM
$Drag => {
outDGen: GGModelTypes.OutlineDescriptorGenerator ← GGSelect.SelectedOutlines[scene, normal];
sliceDGen: GGModelTypes.SliceDescriptorGenerator ← GGSelect.SelectedSlices[scene, normal];
FOR outlineD: OutlineDescriptor ← GGSelect.NextSliceDescriptor[outDGen], GGSelect.NextSliceDescriptor[outDGen] UNTIL outlineD = NIL DO
feature ← GGGravity.FeatureFromOutline[outlineD.slice, NIL];
AddFeature[feature, triggerBag];
ENDLOOP;
FOR sliceD: SliceDescriptor ← GGSelect.NextSliceDescriptor[sliceDGen], GGSelect.NextSliceDescriptor[sliceDGen]
UNTIL sliceD =
NIL
DO
feature ← GGGravity.FeatureFromSlice[sliceD.slice, NIL];
AddFeature[feature, triggerBag];
ENDLOOP;
};
$CaretPos => NULL;
ENDCASE => ERROR UnexpectedType;
};
RemoveMovingOutline: PUBLIC PROC [outlineD: OutlineDescriptor, scene: Scene] RETURNS [stationary: OutlineDescriptor] = {
selSliceD: OutlineDescriptor ← GGSelect.FindSelectedSlice[outlineD.slice, scene, normal];
background, overlay, rubber, drag, move: OutlineDescriptor;
IF selSliceD = NIL THEN RETURN[outlineD]; -- clearly nothing is moving
[background, overlay, rubber, drag] ← outlineD.slice.class.movingParts[selSliceD.slice, selSliceD.parts];
move ← outlineD.slice.class.unionParts[rubber, drag];
stationary ← outlineD.slice.class.differenceParts[outlineD, move];
};
RemoveMovingSlice:
PUBLIC PROC [sliceD: SliceDescriptor, scene: Scene]
RETURNS [stationary: SliceDescriptor] = {
selSliceD: SliceDescriptor ← GGSelect.FindSelectedSlice[sliceD.slice, scene, normal];
background, overlay, rubber, drag, move: SliceDescriptor;
IF selSliceD = NIL THEN RETURN[sliceD]; -- clearly nothing is moving
[background, overlay, rubber, drag] ← sliceD.slice.class.movingParts[selSliceD.slice, selSliceD.parts];
move ← sliceD.slice.class.unionParts[rubber, drag];
stationary ← sliceD.slice.class.differenceParts[sliceD, move];
};
RemoveMoving:
PROC [scene: Scene, triggerBag: TriggerBag] = {
If we are about to drag all of the selected objects, then we must remove selected objects from the triggerBag. We must also remove segments which are adjacent to a moving joint (called "dangling" segments), and segments whose control points are selected.
DeleteTriggersFilter[triggerBag, scene, --RemoveMovingOutline,-- RemoveMovingSlice];
};
BuiltInFilters:
PUBLIC
PROC [triggerBag: TriggerBag, filters: Filters, hideAlignments:
BOOL, alignBag: AlignBag] = {
Add Alignment Objects to the alignBag, using the triggers in the triggerBag.
anchor: Caret;
anchorFeature: FeatureData;
sliceD: SliceDescriptor;
outlineD: OutlineDescriptor;
pointGen: PointGenerator;
pointPairGen: PointPairGenerator;
outPointGen: GGModelTypes.OutlinePointGenerator;
outPointPairGen: GGModelTypes.OutlinePointPairGenerator;
index: NAT ← 0;
CodeTimer.StartInt[$BuiltInFilters, $Gargoyle];
IF hideAlignments
THEN {
FlushAlignBag[alignBag];
RETURN;
};
The anchor as a trigger.
anchorFeature ← triggerBag.anchor;
IF anchorFeature #
NIL
AND GGCaret.Exists[(anchor ←
NARROW[anchorFeature.shape, Caret])]
THEN {
point: Point ← GGCaret.GetPoint[anchor];
AddAnchorObject[anchorFeature, alignBag];
[] ← PointFireRule[point, filters, alignBag];
};
Triggers from outlines.
FOR l: LIST OF FeatureData ← triggerBag.outlines, l.rest UNTIL l = NIL DO
outlineD ← NARROW[l.first.shape];
outPointGen ← outlineD.slice.class.pointsInDescriptor[outlineD];
FOR next: GGModelTypes.PointAndDone ← outlineD.slice.class.nextPoint[outPointGen], outlineD.slice.class.nextPoint[outPointGen] UNTIL next.done DO
[] ← PointFireRule[next.point, filters, alignBag];
ENDLOOP;
outPointPairGen ← outlineD.slice.class.pointPairsInDescriptor[outlineD];
index ← 0;
FOR next: GGModelTypes.PointPairAndDone ← outlineD.slice.class.nextPointPair[outPointPairGen], outlineD.slice.class.nextPointPair[outPointPairGen] UNTIL next.done DO
[] ← SegmentFireRule[index, next.lo, next.hi, filters, alignBag];
index ← index + 1;
ENDLOOP;
ENDLOOP;
Triggers from slices.
FOR l:
LIST
OF FeatureData ← triggerBag.slices, l.rest
UNTIL l =
NIL
DO
sliceD ← NARROW[l.first.shape];
pointGen ← sliceD.slice.class.pointsInDescriptor[sliceD];
FOR next: GGModelTypes.PointAndDone ← sliceD.slice.class.nextPoint[pointGen], sliceD.slice.class.nextPoint[pointGen]
UNTIL next.done
DO
[] ← PointFireRule[next.point, filters, alignBag];
ENDLOOP;
pointPairGen ← sliceD.slice.class.pointPairsInDescriptor[sliceD];
index ← 0;
FOR next: GGModelTypes.PointPairAndDone ← sliceD.slice.class.nextPointPair[pointPairGen], sliceD.slice.class.nextPointPair[pointPairGen]
UNTIL next.done
DO
[] ← SegmentFireRule[9999, next.lo, next.hi, filters, alignBag];
index ← index + 1;
ENDLOOP;
ENDLOOP;
CodeTimer.StopInt[$BuiltInFilters, $Gargoyle];
};
AddAllMidpoints:
PUBLIC
PROC [sceneBag: TriggerBag, midpoints:
BOOL, alignBag: AlignBag] = {
Add to the object bag the midpoints of all segments and slices in the trigger bag.
sliceD: SliceDescriptor;
outlineD: OutlineDescriptor;
pointPairGen: PointPairGenerator;
outPointPairGen: GGModelTypes.OutlinePointPairGenerator;
CodeTimer.StartInt[$AddAllMidpoints, $Gargoyle];
IF NOT GGState.PrecomputeMidpoints[] THEN RETURN;
alignBag.midpoints ← NIL;
IF
NOT midpoints
THEN
RETURN;
Segments as triggers.
FOR l: LIST OF FeatureData ← sceneBag.outlines, l.rest UNTIL l = NIL DO
pairCount: NAT ← 0;
outlineD ← NARROW[l.first.shape];
outPointPairGen ← outlineD.slice.class.pointPairsInDescriptor[outlineD];
FOR next: GGModelTypes.PointPairAndDone ← outlineD.slice.class.nextPointPair[outPointPairGen], outlineD.slice.class.nextPointPair[outPointPairGen] UNTIL next.done DO
[] ← GGGravity.SegmentAddMidpoint[pairCount, next.lo, next.hi, alignBag];
pairCount ← pairCount+1;
ENDLOOP;
ENDLOOP;
Slices as triggers.
FOR l:
LIST
OF FeatureData ← sceneBag.slices, l.rest
UNTIL l =
NIL
DO
pairCount: NAT ← 0;
sliceD ← NARROW[l.first.shape];
pointPairGen ← sliceD.slice.class.pointPairsInDescriptor[sliceD];
FOR next: GGModelTypes.PointPairAndDone ← sliceD.slice.class.nextPointPair[pointPairGen], sliceD.slice.class.nextPointPair[pointPairGen]
UNTIL next.done
DO
[] ← GGGravity.SegmentAddMidpoint[pairCount, next.lo, next.hi, alignBag];
pairCount ← pairCount+1;
ENDLOOP;
ENDLOOP;
CodeTimer.StopInt[$AddAllMidpoints, $Gargoyle];
};
Incremental Addition versions of the Filter Routines shown in the figure as boxes and ovals.
CreateAnchorTrigger:
PUBLIC
PROC [anchor: Caret, triggerBag: TriggerBag]
RETURNS [feature: FeatureData] = {
feature ← GGGravity.FeatureFromAnchor[anchor];
AddFeature[feature, triggerBag];
};
AddHotOutline: PUBLIC PROC [outlineD: OutlineDescriptor, triggerBag: TriggerBag] RETURNS [feature: FeatureData] = {
oldD, unionD: OutlineDescriptor;
oldD ← FindOldOutlineD[outlineD.slice, triggerBag.outlines];
IF oldD = NIL THEN {
feature ← GGGravity.FeatureFromOutline[outlineD.slice, outlineD.parts];
triggerBag.outlines ← AppendFeature[feature, triggerBag.outlines];
}
ELSE {
unionD ← outlineD.slice.class.unionParts[oldD, outlineD];
triggerBag.outlines ← DeleteOutlineD[oldD, triggerBag.outlines];
feature ← GGGravity.FeatureFromOutline[outlineD.slice, unionD.parts];
triggerBag.outlines ← AppendFeature[feature, triggerBag.outlines];
};
};
AddHotSlice:
PUBLIC
PROC [sliceD: SliceDescriptor, triggerBag: TriggerBag]
RETURNS [feature: FeatureData] = {
oldD, unionD: SliceDescriptor;
oldD ← FindOldSlice[sliceD.slice, triggerBag.slices];
IF oldD =
NIL
THEN {
feature ← GGGravity.FeatureFromSlice[sliceD.slice, sliceD.parts];
triggerBag.slices ← AppendFeature[feature, triggerBag.slices];
}
ELSE {
unionD ← sliceD.slice.class.unionParts[oldD, sliceD];
triggerBag.slices ← DeleteSlice[oldD.slice, triggerBag.slices];
feature ← GGGravity.FeatureFromSlice[sliceD.slice, unionD.parts];
triggerBag.slices ← AppendFeature[feature, triggerBag.slices];
};
};
IncrementalFilterSlice:
PUBLIC
PROC [sliceD: SliceDescriptor, filters: Filters, hideAlignments:
BOOL, alignBag: AlignBag]
RETURNS [alignObjects:
LIST
OF FeatureData]
= {
pointGen: PointGenerator;
pointPairGen: PointPairGenerator;
pointGen ← sliceD.slice.class.pointsInDescriptor[sliceD];
FOR next: GGModelTypes.PointAndDone ← sliceD.slice.class.nextPoint[pointGen], sliceD.slice.class.nextPoint[pointGen]
UNTIL next.done
DO
alignObjects ← NARROW[GList.Nconc[PointFireRule[next.point, filters, alignBag], alignObjects]];
ENDLOOP;
pointPairGen ← sliceD.slice.class.pointPairsInDescriptor[sliceD];
FOR next: GGModelTypes.PointPairAndDone ← sliceD.slice.class.nextPointPair[pointPairGen], sliceD.slice.class.nextPointPair[pointPairGen]
UNTIL next.done
DO
alignObjects ← NARROW[GList.Nconc[SegmentFireRule[9999, next.lo, next.hi, filters, alignBag], alignObjects]];
ENDLOOP;
};
IncrementalFilterOutline: PUBLIC PROC [outlineD: OutlineDescriptor, filters: Filters, hideAlignments: BOOL, alignBag: AlignBag] RETURNS [alignObjects: LIST OF FeatureData] = {
pointGen: GGModelTypes.OutlinePointGenerator;
pointPairGen: GGModelTypes.OutlinePointPairGenerator;
pointGen ← outlineD.slice.class.pointsInDescriptor[outlineD];
FOR next: GGModelTypes.PointAndDone ← outlineD.slice.class.nextPoint[pointGen], outlineD.slice.class.nextPoint[pointGen] UNTIL next.done DO
alignObjects ← NARROW[GList.Nconc[PointFireRule[next.point, filters, alignBag], alignObjects]];
ENDLOOP;
pointPairGen ← outlineD.slice.class.pointPairsInDescriptor[outlineD];
FOR next: GGModelTypes.PointPairAndDone ← outlineD.slice.class.nextPointPair[pointPairGen], outlineD.slice.class.nextPointPair[pointPairGen] UNTIL next.done DO
alignObjects ← NARROW[GList.Nconc[SegmentFireRule[9999, next.lo, next.hi, filters, alignBag], alignObjects]];
ENDLOOP;
};
IncrementalMidpointsOutline: PUBLIC PROC [outlineD: OutlineDescriptor, midpoints: BOOL, alignBag: AlignBag] = {
THIS CODE IS NO LONGER USED EXCEPT FOR PERFORMANCE BENCHMARKS; GGState.PrecomputeMidpoints[] is FALSE by default.
Add to the object bag the midpoints of all segments in outlineD.
outPointPairGen: GGModelTypes.OutlinePointPairGenerator;
pairCount: NAT ← 0;
IF NOT midpoints THEN RETURN;
IF NOT GGState.PrecomputeMidpoints[] THEN RETURN;
outPointPairGen ← outlineD.slice.class.pointPairsInDescriptor[outlineD];
FOR next: GGModelTypes.PointPairAndDone ← outlineD.slice.class.nextPointPair[outPointPairGen], outlineD.slice.class.nextPointPair[outPointPairGen] UNTIL next.done DO
[] ← GGGravity.SegmentAddMidpoint[pairCount, next.lo, next.hi, alignBag];
pairCount ← pairCount + 1;
ENDLOOP;
};
IncrementalMidpointsSlice:
PUBLIC
PROC [sliceD: SliceDescriptor, midpoints:
BOOL, alignBag: AlignBag] = {
THIS CODE IS NO LONGER USED EXCEPT FOR PERFORMANCE BENCHMARKS; GGState.PrecomputeMidpoints[] is FALSE by default.
Add to the object bag the midpoints of all segments in sliceD.
pointPairGen: GGModelTypes.PointPairGenerator;
pairCount: NAT ← 0;
IF NOT midpoints THEN RETURN;
IF NOT GGState.PrecomputeMidpoints[] THEN RETURN;
pointPairGen ← sliceD.slice.class.pointPairsInDescriptor[sliceD];
FOR next: GGModelTypes.PointPairAndDone ← sliceD.slice.class.nextPointPair[pointPairGen], sliceD.slice.class.nextPointPair[pointPairGen]
UNTIL next.done
DO
[] ← GGGravity.SegmentAddMidpoint[pairCount, next.lo, next.hi, alignBag];
pairCount ← pairCount + 1;
ENDLOOP;
};
IncrementalFilters:
PUBLIC
PROC [trigger: FeatureData, filters: Filters, hideAlignments:
BOOL, alignBag: AlignBag]
RETURNS [alignObjects:
LIST
OF FeatureData] = {
A single new trigger has been added to the triggerBag. Add to the alignBag, all alignment objects generated by that trigger. Returns a list of the new alignment objects that were generated.
alignObjects ← NIL;
IF hideAlignments
THEN {
FlushAlignBag[alignBag];
RETURN;
};
WITH trigger.shape
SELECT
FROM
sliceD: SliceDescriptor => {
alignObjects ← IncrementalFilterSlice[sliceD, filters, hideAlignments, alignBag];
};
outlineD: OutlineDescriptor => {
alignObjects ← IncrementalFilterOutline[outlineD, filters, hideAlignments, alignBag];
};
anchor: Caret => {
The anchor as a trigger.
IF GGCaret.Exists[anchor]
THEN {
point: Point ← GGCaret.GetPoint[anchor];
[] ← PointFireRule[point, filters, alignBag];
};
};
ENDCASE => ERROR;
};
Incremental Deletion versions of the Filter Routines shown in the figure as boxes and ovals.
RemoveAnchorTrigger:
PUBLIC
PROC [triggerBag: TriggerBag] = {
triggerBag.anchor ← NIL;
};
RemoveEntireHotOutline: PUBLIC PROC [outline: Outline, triggerBag: TriggerBag] RETURNS [oldD: OutlineDescriptor] = {
oldD ← FindOldOutlineD[outline, triggerBag.outlines];
IF oldD#NIL THEN {
triggerBag.outlines ← DeleteOutlineD[oldD, triggerBag.outlines];
};
};
RemoveHotOutline: PUBLIC PROC [outlineD: OutlineDescriptor, triggerBag: TriggerBag] = {
oldOut, diff: OutlineDescriptor;
feature: FeatureData;
oldOut ← FindOldOutlineD[outlineD.slice, triggerBag.outlines];
IF oldOut#NIL THEN {
diff ← outlineD.slice.class.differenceParts[oldOut, outlineD];
triggerBag.outlines ← DeleteOutlineD[oldOut, triggerBag.outlines];
IF NOT outlineD.slice.class.isEmptyParts[diff] THEN {
feature ← GGGravity.FeatureFromOutline[outlineD.slice, diff.parts];
triggerBag.outlines ← AppendFeature[feature, triggerBag.outlines];
};
};
};
RemoveEntireHotSlice:
PUBLIC
PROC [slice: Slice, triggerBag: TriggerBag]
RETURNS [oldSliceD: SliceDescriptor]= {
oldSliceD ← FindOldSlice[slice, triggerBag.slices];
IF oldSliceD#
NIL
THEN {
triggerBag.slices ← DeleteSlice[oldSliceD.slice, triggerBag.slices];
};
};
RemoveHotSlice:
PUBLIC
PROC [sliceD: SliceDescriptor, triggerBag: TriggerBag] = {
oldSliceD, diff: SliceDescriptor;
feature: FeatureData;
oldSliceD ← FindOldSlice[sliceD.slice, triggerBag.slices];
IF oldSliceD#
NIL
THEN {
diff ← sliceD.slice.class.differenceParts[oldSliceD, sliceD];
triggerBag.slices ← DeleteSliceD[oldSliceD, triggerBag.slices];
IF
NOT sliceD.slice.class.isEmptyParts[diff]
THEN {
feature ← GGGravity.FeatureFromSlice[sliceD.slice, diff];
triggerBag.slices ← AppendFeature[feature, triggerBag.slices];
};
};
};
RemoveMentionOutline: PUBLIC PROC [outline: Outline, alignBag: AlignBag] = {
Removes all alignment objects and midpoints that were triggered (only) by this outline. Removes mention of this outline from those alignment objects that are trigger by other triggers as well.
line: AlignmentLine;
FOR list: LIST OF FeatureData ← alignBag.slopeLines, list.rest UNTIL list = NIL DO
line ← NARROW[list.first.shape];
ENDLOOP;
};
RemoveMentionSlice: PUBLIC PROC [slice: Slice, alignBag: AlignBag] = {
Removes all alignment objects and midpoints that were triggered (only) by this slice. Removes mention of this slice from those alignment objects that are trigger by other triggers as well.
};
Other Routines
In support of building triggerBag and sceneBag.
AddFeature:
PROC [featureData: FeatureData, triggerBag: TriggerBag] = {
Process.CheckForAbort[];
SELECT featureData.type
FROM
outline => {
triggerBag.outlines ← CONS[featureData, triggerBag.outlines];
};
outline, slice => {
triggerBag.slices ← CONS[featureData, triggerBag.slices];
};
anchor => {
triggerBag.anchor ← featureData;
};
ENDCASE => ERROR;
};
DeleteTriggersFilter:
PROC [triggerBag: TriggerBag, scene: Scene, --filterOutlineProc: FilterOutlineProc,-- filterSliceProc: FilterSliceProc] = {
When this procedure is called, all sequences in triggerBag are passed to filterSeqProc. filterSeqProc should return those parts which should be included in the new bag.
outlineD, newD: OutlineDescriptor;
sliceD, newSliceD: SliceDescriptor;
outlineFeatures: LIST OF FeatureData ← triggerBag.outlines;
sliceFeatures: LIST OF FeatureData ← triggerBag.slices;
newFeature: FeatureData;
triggerBag.outlines ← NIL;
triggerBag.slices ← NIL;
FOR outList: LIST OF FeatureData ← outlineFeatures, outList.rest UNTIL outList = NIL DO
outlineD ← NARROW[outList.first.shape];
newD ← filterOutlineProc[outlineD, scene];
IF NOT outlineD.slice.class.isEmptyParts[newD] THEN {
newFeature ← GGGravity.FeatureFromOutline[newD.slice, newD.parts];
triggerBag.outlines ← CONS[newFeature, triggerBag.outlines];
};
ENDLOOP;
FOR sliceList:
LIST
OF FeatureData ← sliceFeatures, sliceList.rest
UNTIL sliceList =
NIL
DO
sliceD ← NARROW[sliceList.first.shape];
newSliceD ← filterSliceProc[sliceD, scene];
IF
NOT sliceD.slice.class.isEmptyParts[newSliceD]
THEN {
newFeature ← GGGravity.FeatureFromSlice[newSliceD.slice, newSliceD.parts];
triggerBag.slices ← CONS[newFeature, triggerBag.slices];
};
ENDLOOP;
};
In support of building sceneBag.
ComputeAllBoundingBoxes: PROC [sceneBag: TriggerBag] = {
featureData: FeatureData;
outlineD: OutlineDescriptor;
FOR list: LIST OF FeatureData ← sceneBag.outlines, list.rest UNTIL list = NIL DO
featureData ← list.first;
outlineD ← NARROW[featureData.shape];
GGSlice.UpdateDescriptorBoundBoxes[outlineD];
ENDLOOP;
};
ComputeAllBoundingBoxes:
PROC [sceneBag: TriggerBag] = {
featureData: FeatureData;
outlineD: OutlineDescriptor;
FOR list:
LIST
OF FeatureData ← sceneBag.slices, list.rest
UNTIL list =
NIL
DO
featureData ← list.first;
outlineD ← NARROW[featureData.shape];
GGSlice.UpdateDescriptorBoundBoxes[outlineD];
ENDLOOP;
};
AddAllTrajectories: PROC [scene: Scene, triggerBag: TriggerBag] = {
outlineGen: GGModelTypes.OutlineGenerator;
feature: FeatureData;
outlineGen ← GGScene.OutlinesInScene[scene];
FOR outline: Outline ← GGScene.NextOutline[outlineGen], GGScene.NextOutline[outlineGen] UNTIL outline = NIL DO
feature ← GGGravity.FeatureFromOutline[outline];
AddFeature[feature, triggerBag];
ENDLOOP;
};
AddAllSlices:
PROC [scene: Scene, triggerBag: TriggerBag] = {
feature: FeatureData;
sliceGen: SliceGenerator ← GGScene.SlicesInScene[scene];
FOR slice: Slice ← GGScene.NextSlice[sliceGen], GGScene.NextSlice[sliceGen]
UNTIL slice =
NIL
DO
feature ← GGGravity.FeatureFromSlice[slice];
AddFeature[feature, triggerBag];
ENDLOOP;
};
In support of building alignBag.
AddAnchorObject:
PROC [anchorFeature: FeatureData, alignBag: AlignBag] = {
alignBag.anchor ← anchorFeature;
};
PointFireRule:
PROC [point: Point, filters: Filters, alignBag: AlignBag]
RETURNS [alignObjects:
LIST
OF FeatureData] = {
firstButton: ScalarButtonClient;
feature: FeatureData;
alignObjects ← NIL;
SlopeLine
firstButton ← filters.slopeHeader.scalarButtons;
FOR thisButton: ScalarButtonClient ← firstButton, thisButton.next
UNTIL thisButton =
NIL
DO
IF thisButton.on
THEN {
feature ← GGGravity.JointAddSlopeLine[
degrees: thisButton.value,
direction: Vectors2d.VectorFromAngle[thisButton.value],
point: point,
objectBag: alignBag
];
IF feature # NIL THEN alignObjects ← CONS[feature, alignObjects];
};
ENDLOOP;
Circle
firstButton ← filters.radiusHeader.scalarButtons;
FOR thisButton: ScalarButtonClient ← firstButton, thisButton.next
UNTIL thisButton =
NIL
DO
IF thisButton.on
THEN {
feature ← GGGravity.JointAddCircle[
radius: thisButton.value*filters.scaleUnit,
point: point,
objectBag: alignBag
];
IF feature # NIL THEN alignObjects ← CONS[feature, alignObjects];
};
ENDLOOP;
};
SegmentFireRule:
PROC [segNum:
NAT, lo, hi: Point, filters: Filters, alignBag: AlignBag]
RETURNS [alignObjects:
LIST
OF FeatureData] = {
firstButton: ScalarButtonClient;
line1, line2: FeatureData;
alignObjects ← NIL;
Parallel Lines at Given Distance
firstButton ← filters.distanceHeader.scalarButtons;
FOR thisButton: ScalarButtonClient ← firstButton, thisButton.next
UNTIL thisButton =
NIL
DO
IF thisButton.on
THEN {
[line1, line2] ← GGGravity.SegmentAddDistanceLines[ distance: thisButton.value*filters.scaleUnit, segNum: segNum, lo: lo, hi: hi, objectBag: alignBag];
IF line1 # NIL THEN alignObjects ← CONS[line1, alignObjects];
IF line2 # NIL THEN alignObjects ← CONS[line2, alignObjects];
};
ENDLOOP;
AngleLines
firstButton ← filters.angleHeader.scalarButtons;
FOR thisButton: ScalarButtonClient ← firstButton, thisButton.next
UNTIL thisButton =
NIL
DO
IF thisButton.on
THEN {
[line1, line2] ← GGGravity.SegmentAddTwoAngleLines[degrees: thisButton.value, segNum: segNum, lo: lo, hi: hi, objectBag: alignBag];
IF line1 # NIL THEN alignObjects ← CONS[line1, alignObjects];
IF line2 # NIL THEN alignObjects ← CONS[line2, alignObjects];
};
ENDLOOP;
};
In support of building all three bags.
SomeSelectedIsHot:
PROC [scene: Scene]
RETURNS [
BOOL] = {
sliceDescGen: SliceDescriptorGenerator ← GGSelect.SelectedSlices[scene, normal];
FOR sliceD: SliceDescriptor ← GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen]
UNTIL sliceD=
NIL
DO
IF GGSelect.IsSelectedInPart[sliceD.slice, scene, hot] THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE];
};
Hooks for the future when users can write their own alignment line generators.
UserDefinedPointFilter: PUBLIC PROC [triggerBag: TriggerBag, objectBag: AlignBag, pointFilter: PointFilterProc] = {
jointGen: JointGenerator;
point: Point;
alignObjList: LIST OF AlignmentObject;
Filter the joints.
FOR l: LIST OF FeatureData ← triggerBag.seqs, l.rest UNTIL l = NIL DO
jointGen ← GGSequence.JointsInSequence[NARROW[l.first.shape, Sequence]];
FOR i: INT ← GGSequence.NextJoint[jointGen], GGSequence.NextJoint[jointGen] UNTIL i = -1 DO
point ← GGTraj.FetchJointPos[NARROW[l.first.shape, Sequence].traj, i];
alignObjList ← pointFilter[point];
FOR alignList: LIST OF AlignmentObject ← alignObjList, alignList.rest UNTIL alignList = NIL DO
WITH alignList.first SELECT FROM
line: Line => {
GGGravity.JointAddLine[line, point, i, NARROW[l.first.shape, Sequence].traj, alignBag];
};
circle: Circle => {
GGGravity.JointAddCircle[circle, point, i, NARROW[l.first.shape, Sequence].traj, alignBag];
};
ENDCASE => ERROR;
ENDLOOP;
ENDLOOP;
ENDLOOP;
};
UserDefinedSegmentFilter: PUBLIC PROC [triggerBag: TriggerBag, alignBag: AlignBag, segmentFilter: SegmentFilterProc] = {
segGen: SegmentGenerator;
alignObjList: LIST OF AlignmentObject;
Filter the segments.
FOR l: LIST OF FeatureData ← triggerBag.seqs, l.rest UNTIL l = NIL DO
segGen ← GGSequence.SegmentsInSequence[NARROW[l.first.shape, Sequence]];
FOR next: SegAndIndex ← GGSequence.NextSegmentAndIndex[segGen], GGSequence.NextSegmentAndIndex[segGen] UNTIL next.seg = NIL DO
alignObjList ← segmentFilter[next.seg.lo, next.seg.hi];
FOR alignList: LIST OF AlignmentObject ← alignObjList, alignList.rest UNTIL alignList = NIL DO
WITH alignList.first SELECT FROM
line: Line => {
GGGravity.SegmentAddLine[line, point, next.index, NARROW[feature.shape, Sequence].traj, alignBag];
};
circle: Circle => {
GGGravity.SegmentAddCircle[circle, point, next.index, NARROW[feature.shape, Sequence].traj, alignBag];
};
ENDCASE => ERROR;
ENDLOOP;
ENDLOOP;
ENDLOOP;
};
TAddTriggersFilter: PUBLIC PROC [triggerBag: TriggerBag, filterSeqProc: FilterSequenceProc, filterSliceProc: FilterSliceProc, ggData: GGData] = {
When this procedure is called, all sequences in the scene are passed to filterSeqProc to see if they should be added (in all or in part). filterSeqProc should return those parts which should be added. AddSequenceTrigger adds these parts to triggerBag. A similar things happens for slices using the filterSliceProc and calls to AddSliceTrigger
add: BOOL;
seq: Sequence;
sliceGen: SliceGenerator;
trajGen: TrajGenerator ← GGScene.TrajsInScene[ggData.scene];
FOR traj: Traj ← GGScene.NextTraj[trajGen], GGScene.NextTraj[trajGen] UNTIL traj = NIL DO
seq ← GGSequence.CreateComplete[traj];
filterSeqProc[seq]; -- keeps those parts of seq which should remain
AddSequenceTrigger[seq, triggerBag];
ENDLOOP;
sliceGen ← GGScene.SlicesInScene[ggData.scene];
FOR slice: Slice ← GGScene.NextSlice[sliceGen], GGScene.NextSlice[sliceGen] UNTIL slice = NIL DO
add ← filterSliceProc[slice];
IF add THEN AddSliceTrigger[slice, triggerBag];
ENDLOOP;
};
FeatureList Utilities
FindOldOutlineD: PROC [outline: Outline, list: LIST OF FeatureData] RETURNS [oldSliceD: OutlineDescriptor] = {
FOR l: LIST OF FeatureData ← list, l.rest UNTIL l = NIL DO
IF NARROW[l.first.shape, OutlineDescriptor].slice = outline THEN RETURN [NARROW[l.first.shape]];
ENDLOOP;
RETURN [NIL];
};
FindOldSlice:
PROC [slice: Slice, list:
LIST
OF FeatureData]
RETURNS [oldSliceD: SliceDescriptor] = {
FOR l:
LIST
OF FeatureData ← list, l.rest
UNTIL l =
NIL
DO
IF NARROW[l.first.shape, SliceDescriptor].slice = slice THEN RETURN [NARROW[l.first.shape]];
ENDLOOP;
RETURN [NIL];
};
FindSequenceAndNeighbors:
PROC [entity: Sequence, entityList:
LIST
OF FeatureData]
RETURNS [beforeEnt, ent, afterEnt:
LIST
OF FeatureData] = {
lastE: LIST OF FeatureData ← NIL;
eList: LIST OF FeatureData ← entityList;
IF eList = NIL THEN ERROR EntityNotFound;
UNTIL eList =
NIL
DO
IF NARROW[eList.first.shape, Sequence] = entity THEN {
beforeEnt ← lastE; ent ← eList; afterEnt ← eList.rest; RETURN};
lastE ← eList;
eList ← eList.rest;
ENDLOOP;
SIGNAL EntityNotFound;
};
FindOutlineAndNeighbors:
PROC [entity: Outline, entityList:
LIST
OF FeatureData]
RETURNS [beforeEnt, ent, afterEnt:
LIST
OF FeatureData] = {
lastE: LIST OF FeatureData ← NIL;
eList: LIST OF FeatureData ← entityList;
IF eList = NIL THEN ERROR EntityNotFound;
UNTIL eList =
NIL
DO
IF NARROW[eList.first.shape, OutlineDescriptor].slice = entity THEN {
beforeEnt ← lastE; ent ← eList; afterEnt ← eList.rest; RETURN};
lastE ← eList;
eList ← eList.rest;
ENDLOOP;
SIGNAL EntityNotFound;
};
FindOutlineDAndNeighbors: PROC [entity: Outline, entityList: LIST OF FeatureData] RETURNS [beforeEnt, ent, afterEnt: LIST OF FeatureData] = {
lastE: LIST OF FeatureData ← NIL;
eList: LIST OF FeatureData ← entityList;
IF eList = NIL THEN ERROR EntityNotFound;
UNTIL eList = NIL DO
IF NARROW[eList.first.shape, OutlineDescriptor].slice = entity THEN {
beforeEnt ← lastE; ent ← eList; afterEnt ← eList.rest; RETURN};
lastE ← eList;
eList ← eList.rest;
ENDLOOP;
SIGNAL EntityNotFound;
};
FindSliceAndNeighbors:
PROC [entity: Slice, entityList:
LIST
OF FeatureData]
RETURNS [beforeEnt, ent, afterEnt:
LIST
OF FeatureData] = {
lastE: LIST OF FeatureData ← NIL;
eList: LIST OF FeatureData ← entityList;
IF eList = NIL THEN ERROR EntityNotFound;
UNTIL eList =
NIL
DO
IF NARROW[eList.first.shape, SliceDescriptor].slice = entity THEN {
beforeEnt ← lastE; ent ← eList; afterEnt ← eList.rest; RETURN};
lastE ← eList;
eList ← eList.rest;
ENDLOOP;
SIGNAL EntityNotFound;
};
FindSliceDAndNeighbors: PROC [entity: Slice, entityList: LIST OF FeatureData] RETURNS [beforeEnt, ent, afterEnt: LIST OF FeatureData] = {
lastE: LIST OF FeatureData ← NIL;
eList: LIST OF FeatureData ← entityList;
IF eList = NIL THEN ERROR EntityNotFound;
UNTIL eList = NIL DO
IF NARROW[eList.first.shape, SliceDescriptor].slice = entity THEN {
beforeEnt ← lastE; ent ← eList; afterEnt ← eList.rest; RETURN};
lastE ← eList;
eList ← eList.rest;
ENDLOOP;
SIGNAL EntityNotFound;
};
DeleteOutline: PROC [outline: Outline, featureList: LIST OF FeatureData] RETURNS [smallerList: LIST OF FeatureData, notFound: BOOL ← FALSE] = {
before, at, after: LIST OF FeatureData;
IF featureList = NIL THEN RETURN[NIL, TRUE];
[before, at, after] ← FindOutlineAndNeighbors[outline, featureList ! EntityNotFound => {notFound ← TRUE; CONTINUE}];
IF notFound THEN RETURN[featureList, TRUE];
IF before = NIL THEN smallerList ← after
ELSE {
before.rest ← after;
smallerList ← featureList;
};
};
DeleteOutlineD: PROC [outlineD: OutlineDescriptor, featureList: LIST OF FeatureData] RETURNS [smallerList: LIST OF FeatureData] = {
before, at, after: LIST OF FeatureData;
notFound: BOOL ← FALSE;
IF featureList = NIL THEN RETURN[NIL];
[before, at, after] ← FindOutlineAndNeighbors[outlineD.slice, featureList ! EntityNotFound => {notFound ← TRUE; CONTINUE}];
IF notFound THEN RETURN[featureList];
IF before = NIL THEN smallerList ← after
ELSE {
before.rest ← after;
smallerList ← featureList;
};
};
DeleteSliceD:
PROC [sliceD: SliceDescriptor, featureList:
LIST
OF FeatureData]
RETURNS [smallerList:
LIST
OF FeatureData] = {
before, at, after: LIST OF FeatureData;
notFound: BOOL ← FALSE;
IF featureList = NIL THEN RETURN[NIL];
[before, at, after] ← FindSliceAndNeighbors[sliceD.slice, featureList ! EntityNotFound => {notFound ← TRUE; CONTINUE}];
IF notFound THEN RETURN[featureList];
IF before = NIL THEN smallerList ← after
ELSE {
before.rest ← after;
smallerList ← featureList;
};
};
DeleteSlice:
PROC [slice: Slice, featureList:
LIST
OF FeatureData]
RETURNS [smallerList:
LIST
OF FeatureData] = {
before, at, after: LIST OF FeatureData;
notFound: BOOL ← FALSE;
[before, at, after] ← FindSliceAndNeighbors[slice, featureList];
IF notFound THEN RETURN[featureList];
IF before = NIL THEN smallerList ← after
ELSE {
before.rest ← after;
smallerList ← featureList;
};
}; -- end of DeleteSlice
AppendFeature:
PUBLIC
PROC [feature: FeatureData, featureList:
LIST
OF FeatureData]
RETURNS [biggerList:
LIST
OF FeatureData] = {
Process.CheckForAbort[];
biggerList ← CONS[feature, featureList];
};
Obsolete Routines
ReplaceObsoleteOutlineTrigger: PUBLIC PROC [ggData: GGData, oldOutline: Outline, newOutline: Outline] = {
triggerBag: TriggerBag ← ggData.hitTest.triggerBag;
alignBag: AlignBag ← ggData.hitTest.alignBag;
sceneBag: TriggerBag ← ggData.hitTest.sceneBag;
feature: FeatureData;
hotD: OutlineDescriptor;
notFound: BOOL;
First, get rid of all references to the obsolete outline.
notFound ← FALSE;
UNTIL notFound DO
[sceneBag.outlines, notFound] ← DeleteOutline[oldOutline, sceneBag.outlines];
ENDLOOP;
notFound ← FALSE;
UNTIL notFound DO
[triggerBag.outlines, notFound] ← DeleteOutline[oldOutline, triggerBag.outlines];
ENDLOOP;
Next, add back any appropriate references to the new outline. Assume that the next operation will be a CaretPos.
If this traj is hot, add its hot parts to the triggerBag.
hotD ← GGSelect.FindSelectedOutline[newOutline, ggData.scene, hot];
IF hotD # NIL THEN {
feature ← GGGravity.FeatureFromOutline[hotD.slice, hotD.parts];
AddFeature[feature, triggerBag];
};
Add the whole object to the scene bag.
feature ← GGGravity.FeatureFromOutline[newOutline];
AddFeature[feature, sceneBag];
};
Initialization
InitStats:
PROC [] = {
boundBoxes, bags, interval: CodeTimer.Interval;
boundBoxes ← CodeTimer.CreateInterval[$ComputeBoundBoxes];
bags ← CodeTimer.CreateInterval[$UpdateBagsForAction, LIST[boundBoxes]];
CodeTimer.AddInt[bags, $Gargoyle];
bags ← CodeTimer.CreateInterval[$SetBagsForAction, NIL];
CodeTimer.AddInt[bags, $Gargoyle];
interval ← CodeTimer.CreateInterval[$BuiltInFilters, NIL];
CodeTimer.AddInt[interval, $Gargoyle];
interval ← CodeTimer.CreateInterval[$AddAllMidpoints, NIL];
CodeTimer.AddInt[interval, $Gargoyle];
};
Init:
PROC [] = {
emptyTriggerBag ← CreateTriggerBag[];
emptyAlignBag ← CreateAlignBag[];
};
InitStats[];
Init[];
END.