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: BOOLNOT 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: BOOLNOT 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: BOOLNOT 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: BOOLNOT 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: BOOLNOT 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: BOOLNOT 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: BOOLFALSE] = {
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: BOOLFALSE;
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: BOOLFALSE;
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: BOOLFALSE;
[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.