GGSceneImpl.mesa
Copyright © 1985 by Xerox Corporation.  All rights reserved.
Last edited by Bier on April 6, 1987 10:43:23 pm PDT
Contents:  The procedural interface to the Gargoyle object modeler.
Stone, August 5, 1985 4:13:18 pm PDT
Pier, May 14, 1987 3:30:44 pm PDT
Kurlander August 28, 1986 6:15:29 pm PDT
 
DIRECTORY
GGAlign, GGBasicTypes, GGBoundBox, GGInterfaceTypes, GGModelTypes, GGMultiGravity, GGScene, GGOutline, GGSceneType, GGSegment, GGSegmentTypes, GGSelect, GGSequence, GGTraj, GGUtility, GList, ImagerPath, Rope;
 
GGSceneImpl: 
CEDAR 
PROGRAM
IMPORTS GGAlign, GGBoundBox, GGMultiGravity, GGScene, GGOutline, GGSegment, GGSelect, GGSequence, GGTraj, GGUtility, GList
EXPORTS GGScene, GGModelTypes = BEGIN
 
BoundBox: TYPE = GGModelTypes.BoundBox;
BoundBoxGenerator: TYPE = REF BoundBoxGeneratorObj;
BoundBoxGeneratorObj: TYPE = GGModelTypes.BoundBoxGeneratorObj;
CurveType: TYPE = {line, bezier, conic};
Joint: TYPE = REF JointObj;
JointGenerator: TYPE = REF JointGeneratorObj;
JointGeneratorObj: TYPE = GGModelTypes.JointGeneratorObj;
JointObj: TYPE = GGSegmentTypes.JointObj;
Outline: TYPE = REF OutlineObj;
OutlineData: TYPE = GGOutline.OutlineData;
OutlineObj: TYPE = GGModelTypes.OutlineObj;
OutlineDescriptor: TYPE = GGModelTypes.OutlineDescriptor;
OutlineGenerator: TYPE = REF OutlineGeneratorObj;
OutlineGeneratorObj: TYPE = GGModelTypes.OutlineGeneratorObj;
Point: TYPE = GGBasicTypes.Point;
Scene: TYPE = GGModelTypes.Scene;
SceneObj: PUBLIC TYPE = GGSceneType.SceneObj; -- export of opaque type
SceneRef: TYPE = REF SceneObj;
Segment: TYPE = REF SegmentObj;
SegmentGenerator: TYPE = GGModelTypes.SegmentGenerator;
SegmentObj: TYPE = GGSegmentTypes.SegmentObj;
Sequence: TYPE = REF SequenceObj;
SequenceGenerator: TYPE = GGModelTypes.SequenceGenerator;
SequenceObj: TYPE = GGModelTypes.SequenceObj;
Slice: TYPE = GGModelTypes.Slice;
SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor;
SliceDescriptorGenerator: TYPE = GGModelTypes.SliceDescriptorGenerator;
SliceGenerator: TYPE = REF SliceGeneratorObj;
SliceGeneratorObj: TYPE = GGModelTypes.SliceGeneratorObj;
Traj: TYPE = REF TrajObj;
TrajEnd: TYPE = GGModelTypes.TrajEnd; -- {lo, hi};
TrajGenerator: TYPE = REF TrajGeneratorObj;
TrajGeneratorObj: TYPE = GGModelTypes.TrajGeneratorObj;
TrajObj: TYPE = GGModelTypes.TrajObj;
Vector: TYPE = GGBasicTypes.Vector;
GGData: TYPE = GGInterfaceTypes.GGData;
The scene object has a list of entities, a "finger" REF (ptr) to the end of the list for quick appends, and a BOOL which is TRUE when ptr in fact points to the end.  ptr MUST BE invalidated by setting ptrValid ← FALSE whenever a remove or delete is done from the entities.  Any use of ptr must check ptrValid and recalculate its value if it is invalid.  The only place that happens in here is in AppendEntity.
entities are kept in back to front order so that the list can be painted with a single traversal.
 
 
NotYetImplemented: PUBLIC SIGNAL = CODE;
Problem: PUBLIC SIGNAL [msg: Rope.ROPE] = CODE;
Building and Destroying Clusters
MakeCluster: PUBLIC PROC [entities: LIST OF REF ANY] RETURNS [newCluster: Slice];
BreakCluster: PUBLIC PROC [oldCluster: Slice] RETURNS [entities: LIST OF REF ANY];
Creating, Modifying Scenes
The list scene.entities is kept in back to front order.  Hence, newly added objects are usually added to the end of the list.  This list may be mutated freely.  Copies of it are made when needed.
CreateScene: 
PUBLIC 
PROC [] 
RETURNS [scene: SceneRef] = {
scene  ← NEW[SceneObj ← [prioritiesValid: FALSE] ];
};
 
AddOutline: 
PUBLIC 
PROC [scene: SceneRef, outline: Outline, priority: 
INT ← -1] = {
Add the given outline to the scene with the given priority (the lower the priority number, the closer the outline is to the front.  If priority is -1, the outline will be the rear-most entity.  If an entity with the given priority already exists, all priority numbers greater than or equal to it will be increased by one. 
IF priority = -1 THEN AppendSlice[scene, outline]
ELSE 
IF priority = 0 
THEN {
scene.ptrValid ← FALSE;
scene.entities ← CONS[outline, scene.entities];
}
 
ELSE SIGNAL Problem["Not yet implemented."];
scene.prioritiesValid ← FALSE;
};
 
AddSlice: 
PUBLIC 
PROC [scene: SceneRef, slice: Slice, priority: 
INT ← -1] = {
Add the given slice to the scene with the given priority (the higher the priority number, the closer the outline is to the front of the scene).  If priority is -1, the slice will be the front-most entity.  If the priority given is too high, we proceed as if priority = -1.
IF priority = -1 THEN AppendSlice[scene, slice]
ELSE 
IF priority = 0 
THEN {
scene.ptrValid ← FALSE;
scene.entities ← CONS[slice, scene.entities];
}
 
ELSE SIGNAL Problem["Not yet implemented."];
scene.prioritiesValid ← FALSE;
};
 
AddSlices: 
PUBLIC 
PROC [scene: SceneRef, slice: 
LIST 
OF Slice, priority: 
INT ← -1] = {
Adds the entities, assumed to be in back to front order, to the scene.  IF priority = -1, they become the frontmost entities.  Otherwise, the specified priority becomes the priority of the first entity in the list, if possible.  If the priority given is too high, we proceed as if priority = -1.
IF priority = -1 
THEN {
FOR list: 
LIST 
OF Slice ← slice, list.rest 
UNTIL list = 
NIL 
DO
AppendSlice[scene, list.first];
ENDLOOP;
 
}
 
ELSE SIGNAL Problem["Not yet implemented."];
scene.prioritiesValid ← FALSE;
};
 
AddEntity: PUBLIC PROC [scene: SceneRef, entity: REF ANY, priority: INT] = {
Adds the entity to the scene.  If priority = 0, it becomes the rearmost entity.  IF priority = -1, it becomes the frontmost entity.  If the priority given is too high, we proceed as if priority = -1.
IF priority = -1 THEN AppendEntity[scene, entity]
ELSE IF priority = 0 THEN {
scene.ptrValid ← FALSE;
scene.entities ← CONS[entity, scene.entities];
}
 
ELSE SIGNAL Problem["Not yet implemented."];
scene.prioritiesValid ← FALSE;
};
 
AppendSlice: 
PROC [scene: SceneRef, slice: Slice] = {
IF 
NOT scene.ptrValid 
THEN { 
-- restore ptr
IF scene.entities=NIL THEN scene.ptr ← NIL ELSE
FOR list: 
LIST 
OF Slice ← scene.entities, list.rest 
UNTIL list=
NIL 
DO
scene.ptr ← list;
ENDLOOP;
 
scene.ptrValid ← TRUE;
};
 
[scene.entities, scene.ptr] ← GGUtility.AddSlice[slice, scene.entities, scene.ptr];
};
 
SlicePriority: 
PUBLIC 
PROC [scene: SceneRef, slice: Slice] 
RETURNS [priority: 
INT] = {
IF NOT scene.prioritiesValid THEN UpdatePriorities[scene];
priority ← slice.priority;
};
 
UpdatePriorities: 
PROC [scene: SceneRef] = {
sliceGen: SliceGenerator ← GGScene.TopLevelSlicesInScene[scene];
index: NAT ← 0;
FOR slice: Slice ← GGScene.NextSlice[sliceGen], GGScene.NextSlice[sliceGen] 
UNTIL slice = 
NIL DO
slice.priority ← index;
index ← index + 1;
ENDLOOP;
 
scene.prioritiesValid ← TRUE;
};
 
DeleteSlice: 
PUBLIC 
PROC [scene: SceneRef, slice: Slice] =  {
scene.ptrValid ← FALSE;
scene.entities ← NARROW[GList.Remove[slice, scene.entities]];
scene.prioritiesValid ← FALSE;
};
 
DeleteEntity: PUBLIC PROC [scene: SceneRef, entity: REF ANY] = {
Removes the named entity from the scene.  If the entity is not on the list, no action is taken.
scene.ptrValid ← FALSE;
scene.entities ← List.DRemove[entity, scene.entities];
scene.prioritiesValid ← FALSE;
};
 
DeleteOutline: 
PUBLIC 
PROC [scene: SceneRef, outline: Outline] =  {
GGScene.DeleteSlice[scene, outline];
scene.prioritiesValid ← FALSE;
};
 
DeleteSequence: 
PUBLIC 
PROC [seq: Sequence, scene: SceneRef] 
RETURNS [oldOutline: Outline, newOutlines: 
LIST 
OF Outline] = {
Takes the trajectory of which seq is a part.  What is the role of this trajectory?
If role = hole, then create a new outline (and new trajectories), identical to seq.traj.outline, but without the hole.  The index corresponding to this outline will be 0.  Make separate outlines corresponding to those parts of the hole which were not deleted.  The index for each of these outlines will be the segment number, in the original hole traj, of segment 0 in the new outline. The old outline (and all of its parts) is now obsolete.
If role = fence, then create new outlines and new trajectories for all of the parts of seq.traj.outline which remain.  The index for each of these outlines will be the segment number, in the original fence traj, of segment 0 in the new outline.  The old outline (and all of its parts) is now obsolete.
If role = open, then create new outlines and new trajectories for all of the parts of seq.traj which remain.  The index for each of these outlines will be the segment number, in the original fence traj, of segment 0 in the new outline.  The old outline is obsolete.
oldOutline ← GGOutline.OutlineOfTraj[seq.traj];
SELECT seq.traj.role 
FROM
hole => {
smallerOutline: Outline;
The parts of the hole become top level parts of the scene.
newOutlines ← OutlinesOfTrajMinusSequence[seq];
The rest of the outline is replaced by a single new outline.
 
smallerOutline ← OutlineMinusHole[oldOutline, seq.traj];
AddOutline[scene, smallerOutline, -1];
AddOutlinesToScene[newOutlines, scene]; -- put the deflated holes on top.
newOutlines ← CONS[smallerOutline, newOutlines];
};
 
open => {
newOutlines ← OutlinesOfTrajMinusSequence[seq];
AddOutlinesToScene[newOutlines, scene];
};
 
fence => {
fenceParts, freedHoles: LIST OF Outline;
fenceParts ← OutlinesOfTrajMinusSequence[seq];
AddOutlinesToScene[fenceParts, scene];
freedHoles ← OutlinesFromOutlineExcept[oldOutline, seq.traj];
AddOutlinesToScene[freedHoles, scene];
newOutlines ← AppendOutlines[fenceParts, freedHoles];
};
 
ENDCASE => ERROR;
 
DeleteOutline[scene, oldOutline];
};
AddOutlinesToScene: 
PROC [outlines: 
LIST 
OF Outline, scene: SceneRef] = {
FOR list: 
LIST 
OF Outline ← outlines, list.rest 
UNTIL list = 
NIL 
DO
AddOutline[scene, list.first, -1];
ENDLOOP;
 
};
 
OutlinesOfTrajMinusSequence: 
PROC [seq: Sequence] 
RETURNS [newOutlines: 
LIST 
OF Outline] = {
diff, wholeTraj: Sequence;
IF GGSequence.IsComplete[seq] 
THEN 
RETURN[
NIL];
Parts of the trajectory are in the sequence.  Take the segments which remain, and all the joints that they touch.
 
wholeTraj ← GGSequence.CreateComplete[seq.traj];
IF NOT GGSequence.IsEmpty[seq] AND seq.traj.segCount=1 THEN GGSegment.OpenUpSegment[GGTraj.FetchSegment[seq.traj, 0]]; -- cyclic segments must become acyclic
diff ← GGSequence.Difference[wholeTraj, seq];
[newOutlines] ← GroupPieces[diff];
};
 
GroupPieces: 
PROC [seq: Sequence] 
RETURNS [newOutlines: 
LIST 
OF Outline] = {
seqGen: SequenceGenerator;
newTraj: Traj;
newOutline, oldOutline: Outline;
runCount: NAT;
joint: Joint;
newOutlines ← NIL;
oldOutline ← GGOutline.OutlineOfTraj[seq.traj];
[seqGen, runCount] ← GGSequence.RunsInSequence[seq];
FOR run: Sequence ← GGSequence.NextSequence[seqGen], GGSequence.NextSequence[seqGen] 
UNTIL run = 
NIL 
DO
IF run.segCount=0 THEN LOOP; -- special case needed by BS command.
[newTraj] ← GGTraj.CopyTrajFromRun[run];
Make sure the end joints of the run are not active selected.  They can be normal or hot selected.  This is important for the delete operation.
joint ← GGTraj.FetchJoint[newTraj, 0];
joint.TselectedInFull.active ← FALSE;
joint ← GGTraj.FetchJoint[newTraj, GGTraj.HiJoint[newTraj]];
joint.TselectedInFull.active ← FALSE;
 
newTraj.role ← open;
newOutline ← GGOutline.CreateOutline[newTraj, oldOutline.class.getFillColor[oldOutline] ];
newOutlines ← CONS[newOutline, newOutlines];
ENDLOOP;
 
};
 
OutlinesFromOutlineExcept: 
PROC [outline: Outline, except: Traj] 
RETURNS [newOutlines: 
LIST 
OF Outline] = {
trajGen: TrajGenerator;
newTraj: Traj;
newOutline: Outline;
ptr: LIST OF Outline;
trajGen ← GGOutline.TrajsInOutline[outline];
[newOutlines, ptr] ← StartOutlineList[];
FOR traj: Traj ← GGScene.NextTraj[trajGen], GGScene.NextTraj[trajGen] 
UNTIL traj = 
NIL 
DO
IF traj = except THEN LOOP;
newTraj ← GGTraj.CopyTraj[traj];
newOutline ← GGOutline.CreateOutline[newTraj, outline.class.getFillColor[outline] ];
[newOutlines, ptr] ← AddOutlineToList[newOutline, newOutlines, ptr];
ENDLOOP;
 
};
 
OutlineMinusHole: 
PROC [outline: Outline, except: Traj] 
RETURNS [newOutline: Outline] = {
holeGen: TrajGenerator;
newTraj, fence: Traj;
fence ← GGOutline.FenceOfOutline[outline];
newTraj ← GGTraj.CopyTraj[fence];
newOutline ← GGOutline.CreateOutline[newTraj, outline.class.getFillColor[outline] ];
holeGen ← GGOutline.HolesOfOutline[outline];
FOR hole: Traj ← GGScene.NextTraj[holeGen], GGScene.NextTraj[holeGen] 
UNTIL hole = 
NIL 
DO
IF hole = except THEN LOOP;
newTraj ← GGTraj.CopyTraj[hole];
newOutline ← GGOutline.AddHole[newOutline, newTraj];
ENDLOOP;
 
};
 
AppendOutlines: 
PROC [list1, list2: 
LIST 
OF Outline] 
RETURNS [result: 
LIST 
OF Outline] = {
Non-destructive (copies the first list).
pos: LIST OF Outline;
newCell: LIST OF Outline;
IF list1 = NIL THEN RETURN[list2];
result ← CONS[list1.first, NIL];
pos ← result;
FOR l: 
LIST 
OF Outline ← list1.rest, l.rest 
UNTIL l = 
NIL 
DO
newCell ← CONS[l.first, NIL];
pos.rest ← newCell;
pos ← newCell;
ENDLOOP;
 
pos.rest  ← list2;
};
 
StartOutlineList: 
PROC [] 
RETURNS [entityList, ptr: 
LIST 
OF Outline] = {
ptr ← entityList ← NIL;
};
 
AddOutlineToList: 
PROC [entity: Outline, entityList, ptr: 
LIST 
OF Outline] 
RETURNS [newList, newPtr: 
LIST 
OF Outline] = {
IF ptr = 
NIL 
THEN {
IF NOT entityList = NIL THEN ERROR;
newPtr ← newList ← CONS[entity, NIL];
RETURN;
}
 
ELSE {
newList ← entityList;
ptr.rest ← CONS[entity, NIL];
newPtr ← ptr.rest;
};
 
};
 
 
undeleteSize: INT ← 20; -- temporarily hardwired size of undelete stack
PushScene: 
PUBLIC 
PROC [scene: SceneRef] = {
Makes a copy of the current scene list, and pushes it on a stack.  For use in Undelete.
CopySliceList: 
PROC [list: 
LIST 
OF Slice] 
RETURNS [copy: 
LIST 
OF Slice] = {
entityList, ptr: LIST OF Slice;
[entityList, ptr] ← GGUtility.StartSliceList[];
FOR entity: 
LIST 
OF Slice ← list, entity.rest 
UNTIL entity=
NIL 
DO
[entityList, ptr] ← GGUtility.AddSlice[entity.first, entityList, ptr];
ENDLOOP;
 
copy ← entityList;
};
 
tList: LIST OF LIST OF Slice;
scene.oldEntities ← CONS[CopySliceList[scene.entities], scene.oldEntities];
tList ← scene.oldEntities;
FOR index: INT IN [0..undeleteSize) DO IF tList#NIL THEN tList ← tList.rest; ENDLOOP;
IF tList#NIL THEN tList.rest ← NIL; -- truncate the undelete stack
};
 
EmptySceneStack: 
PUBLIC 
PROC [scene: SceneRef] 
RETURNS [
BOOL] = {
RETURN[scene.oldEntities=NIL OR scene.oldEntities.first=NIL];
};
 
PopScene: 
PUBLIC 
PROC [scene: SceneRef] = {
Lets scene be the most recently pushed scene, that has not yet been popped.
IF EmptySceneStack[scene] THEN ERROR;
scene.entities ← scene.oldEntities.first;
scene.ptrValid ← FALSE;
scene.oldEntities ← scene.oldEntities.rest;
};
 
MergeScenes: 
PUBLIC 
PROC [back: SceneRef, front: SceneRef] 
RETURNS [combined: SceneRef] = {
combined ← NEW[SceneObj ← [prioritiesValid: FALSE]];
combined.entities ← NARROW[GList.Append[back.entities, front.entities]];
combined.selected.normal ← NARROW[GList.Append[back.selected.normal, front.selected.normal]];
combined.selected.hot ← NARROW[GList.Append[back.selected.hot, front.selected.hot]];
combined.selected.active ← NARROW[GList.Append[back.selected.active, front.selected.active]];
};
 
UpOne: 
PUBLIC 
PROC [scene: SceneRef, slice: Slice] = {
Moves the named slice one step closer to the front in the priority order.
ExchangeWithNextEntity: 
PROC [scene: SceneRef, slice: Slice] = {
previous, next: LIST OF Slice ← NIL;
FOR list: 
LIST OF Slice ← scene.entities, list.rest 
UNTIL list = 
NIL 
DO
IF list.first=slice 
THEN { 
-- exchange this entity with its next neighbor
next ← list.rest;
IF next=NIL THEN RETURN; -- entity already at end (i.e. top) of list
list.rest ← next.rest;  next.rest ← list;
IF previous#NIL THEN previous.rest ← next ELSE scene.entities ← next;
}
 
ELSE previous ← list;
ENDLOOP;
 
};
 
ExchangeWithNextEntity[scene, slice];
};
 
DownOne: 
PUBLIC 
PROC [scene: SceneRef, slice: Slice] = {
Moves the named slice one step closer to the back in the priority order.
ExchangeWithPreviousEntity: 
PROC [scene: SceneRef, slice: Slice] = {
preprev, previous: LIST OF Slice ← NIL;
FOR list: 
LIST 
OF Slice ← scene.entities, list.rest 
UNTIL list=
NIL 
DO
IF list.first=slice 
THEN { 
-- exchange this entity with its previous neighbor
SELECT 
TRUE 
FROM
preprev=NIL AND previous=NIL => RETURN; -- entity already on bottom 
preprev=
NIL 
AND previous#
NIL => { 
-- special case. Second from bottom
previous.rest ← list.rest;  list.rest ← previous; scene.entities ← list; RETURN;
};
 
preprev#NIL AND previous=NIL => ERROR; -- Assert: can't happen
preprev#
NIL 
AND previous#
NIL => { 
-- usual case. Exchange list elements
previous.rest ← list.rest;  list.rest ← previous;  preprev.rest ← list; RETURN;
};
 
ENDCASE => ERROR; -- Assert: can't happen
 
}
 
ELSE { preprev ← previous;  previous ← list; };
ENDLOOP;
 
};
 
ExchangeWithPreviousEntity[scene, slice];
};
 
Browsing the Scene Hierarchy -- Generators
TopLevelEntitiesInScene: 
PUBLIC 
PROC [scene: SceneRef] 
RETURNS [sliceGen: SliceGenerator] =  {
Generates all of the outlines and slices in the scene.
sliceGen ← 
NEW[SliceGeneratorObj ← [
list: scene.entities
]];
 
};
 
TopLevelSlicesInScene: 
PUBLIC 
PROC [scene: SceneRef] 
RETURNS [sliceGen: SliceGenerator] =  {
Generates all of the outlines and slices in the scene.
sliceGen ← 
NEW[SliceGeneratorObj ← [
list: scene.entities
]];
 
};
 
NextEntity: PUBLIC PROC [g: EntityGenerator] RETURNS [next: REF ANY] = {
IF g.list = NIL THEN RETURN[NIL]
ELSE {
next ← g.list.first;
g.list ← g.list.rest;
};
 
};
 
EntityCount: PUBLIC PROC [g: EntityGenerator] RETURNS [count: NAT] = {
IF g.countValid THEN RETURN[g.count];
count ← 0;
FOR list: LIST OF REF ANY ← g.list, list.rest UNTIL list = NIL DO
count ← count + 1;
ENDLOOP;
 
};
 
IsTopLevel: 
PUBLIC 
PROC [slice: Slice] 
RETURNS [
BOOL] = {
RETURN[slice.parent = NIL];
outline: Outline => RETURN[outline.parent = NIL OR outline.parent.parent = NIL];
traj: Traj => RETURN[traj.parent.parent = NIL OR traj.parent.parent.parent = NIL];
seq: Sequence => RETURN[IsTopLevel[seq.traj.parent]];
ENDCASE => ERROR;
};
 
TrajsInScene: 
PUBLIC 
PROC [scene: SceneRef] 
RETURNS [trajGen: TrajGenerator] =  {
Generates all of the trajectories in the scene, wherever they occur.
list: LIST OF Traj ← ListTrajsInScene[scene];
trajGen ← 
NEW[TrajGeneratorObj ← [
list: list
]];
 
};
 
OutlinesInScene: 
PUBLIC 
PROC [scene: SceneRef] 
RETURNS [outlineGen: OutlineGenerator] =  {
Generates all of the outlines in the scene, wherever they occur.
list: LIST OF Outline ← ListOutlinesInScene[scene];
outlineGen ← 
NEW[OutlineGeneratorObj ← [
list: list
]];
 
};
 
NextTraj: 
PUBLIC 
PROC [g: TrajGenerator] 
RETURNS [next: Traj] = {
IF g.list = NIL THEN RETURN[NIL]
ELSE {
next ← g.list.first;
g.list ← g.list.rest;
};
 
};
 
NextOutline: 
PUBLIC 
PROC [g: OutlineGenerator] 
RETURNS [next: Outline] = {
IF g.list = NIL THEN RETURN[NIL]
ELSE {
next ← g.list.first;
g.list ← g.list.rest;
};
 
};
 
SlicesInScene: 
PUBLIC 
PROC [scene: SceneRef] 
RETURNS [sliceGen: SliceGenerator] = {
list: LIST OF Slice ← ListSlicesInScene[scene];
sliceGen ← 
NEW[SliceGeneratorObj ← [
list: list
]];
 
};
 
NextSlice: 
PUBLIC 
PROC [g: SliceGenerator] 
RETURNS [next: Slice] = {
IF g.list = NIL THEN RETURN[NIL]
ELSE {
next ← g.list.first;
g.list ← g.list.rest;
};
 
};
 
Bounding Boxes
BoundBoxesInScene: 
PUBLIC 
PROC [scene: SceneRef] 
RETURNS [bBoxGen: BoundBoxGenerator] = {
Generates all of the boundBoxes in the scene, whereever they occur.
list: LIST OF BoundBox ← ListBoxesInScene[scene];
bBoxGen ← 
NEW[BoundBoxGeneratorObj ← [
list: list
]];
 
};
 
TightBoxesInScene: 
PUBLIC 
PROC [scene: SceneRef] 
RETURNS [bBoxGen: BoundBoxGenerator] = {
Generates all of the boundBoxes in the scene, whereever they occur.
list: LIST OF BoundBox ← ListTightBoxesInScene[scene];
bBoxGen ← 
NEW[BoundBoxGeneratorObj ← [
list: list
]];
 
};
 
NextBox: 
PUBLIC 
PROC [g: BoundBoxGenerator] 
RETURNS [next: BoundBox] = {
IF g.list = NIL THEN RETURN[NIL]
ELSE {
next ← g.list.first;
g.list ← g.list.rest;
};
 
};
 
ListBoxesInScene: 
PROC [scene: SceneRef] 
RETURNS [allBoxes: 
LIST 
OF BoundBox] = {
thisBox: BoundBox;
FOR entities: 
LIST 
OF Slice ← scene.entities, entities.rest 
UNTIL entities = 
NIL 
DO
thisBox ← entities.first.class.getBoundBox[entities.first, NIL];
allBoxes ← CONS[thisBox, allBoxes];
ENDLOOP;
 
};
 
ListTightBoxesInScene: 
PROC [scene: SceneRef] 
RETURNS [allBoxes: 
LIST 
OF BoundBox] = {
thisBox: BoundBox;
FOR entities: 
LIST 
OF Slice ← scene.entities, entities.rest 
UNTIL entities = 
NIL 
DO
thisBox ← entities.first.class.getTightBox[entities.first, NIL];
allBoxes ← CONS[thisBox, allBoxes];
ENDLOOP;
 
};
 
BoundBoxOfScene: 
PUBLIC PROC [scene: SceneRef] 
RETURNS [bBox: BoundBox] = {
box is in local IP coordinates
bbGen: GGModelTypes.BoundBoxGenerator ← BoundBoxesInScene[scene];
bBox ← GGBoundBox.BoundBoxOfBoxes[bbGen.list];  -- cheating to go inside generator
};
 
TightBoxOfScene: 
PUBLIC PROC [scene: SceneRef] 
RETURNS [bBox: BoundBox] = {
box is in local IP coordinates
bbGen: GGModelTypes.BoundBoxGenerator ← TightBoxesInScene[scene];
bBox ← GGBoundBox.BoundBoxOfBoxes[bbGen.list];  -- cheating to go inside generator
};
 
Utility Routines
AppendTrajs: 
PUBLIC 
PROC [l1, l2: 
LIST 
OF Traj] 
RETURNS [result: 
LIST 
OF Traj] = {
Destructive append.
z: LIST OF Traj ← l1;
IF z = NIL THEN RETURN[l2];
UNTIL z.rest = NIL DO z ← z.rest; ENDLOOP;
z.rest ← l2;
RETURN[l1];
};
 
AppendBoxes: 
PUBLIC 
PROC [l1, l2: 
LIST 
OF BoundBox] 
RETURNS [result: 
LIST 
OF BoundBox] = {
Destructive append.
z: LIST OF BoundBox ← l1;
IF z = NIL THEN RETURN[l2];
UNTIL z.rest = NIL DO z ← z.rest; ENDLOOP;
z.rest ← l2;
RETURN[l1];
};
 
ListTrajsInSceneOLDD: PROC [scene: SceneRef] RETURNS [allTrajs: LIST OF Traj] = {
allTrajs ← NIL;
FOR entities: LIST OF REF ANY ← scene.entities, entities.rest UNTIL entities = NIL DO
WITH entities.first SELECT FROM
outline: Outline => {
FOR trajs: LIST OF Traj ← outline.children, trajs.rest UNTIL trajs = NIL DO
allTrajs ← CONS[trajs.first, allTrajs];
 
ENDLOOP;
};
 
ENDCASE => ERROR;
 
ENDLOOP;
 
};
 
ListTrajsInScene: 
PROC [scene: SceneRef] 
RETURNS [allTrajs: 
LIST 
OF Traj] = {
FOR slices: 
LIST 
OF Slice ← scene.entities, slices.rest 
UNTIL slices = 
NIL 
DO
IF slices.first.class.type#$Outline THEN LOOP;
FOR trajs: 
LIST 
OF 
Traj ← GGOutline.
TrajectoriesOfOutline[slices.first], trajs.rest 
UNTIL trajs = 
NIL 
DO
allTrajs ← CONS[trajs.first, allTrajs];
ENDLOOP;
 
ENDLOOP;
 
};
 
ListOutlinesInSceneOLDD: PROC [scene: SceneRef] RETURNS [allOutlines: LIST OF Outline] = {
allOutlines ← NIL;
FOR entities: LIST OF REF ANY ← scene.entities, entities.rest UNTIL entities = NIL DO
WITH entities.first SELECT FROM
outline: Outline => {
allOutlines ← CONS[outline, allOutlines];
};
 
ENDCASE => ERROR;
 
ENDLOOP;
 
};
 
ListOutlinesInScene: 
PROC [scene: SceneRef] 
RETURNS [allOutlines: 
LIST 
OF Slice] = {
FOR slices: 
LIST 
OF Slice ← scene.entities, slices.rest 
UNTIL slices = 
NIL 
DO
IF slices.first.class.type=$Outline THEN allOutlines ← CONS[slices.first, allOutlines];
ENDLOOP;
 
};
 
ListSlicesInSceneOLDD: PROC [scene: SceneRef] RETURNS [allSlices: LIST OF Slice] = {
allSlices ← NIL;
FOR entities: LIST OF REF ANY ← scene.entities, entities.rest UNTIL entities = NIL DO
WITH entities.first SELECT FROM
slice: Slice => {
Doesn't work on Gargoyle slices.
allSlices ← CONS[slice, allSlices];
};
 
outline: Outline => {};
ENDCASE => ERROR;
 
ENDLOOP;
 
};
 
ListSlicesInScene: 
PROC [scene: SceneRef] 
RETURNS [allSlices: 
LIST 
OF Slice] = {
FOR slices: 
LIST 
OF Slice ← scene.entities, slices.rest 
UNTIL slices = 
NIL 
DO
allSlices ← CONS[slices.first, allSlices];
ENDLOOP;
 
};
 
ListTrajsInOutline: 
PROC [outline: Outline] 
RETURNS [trajList: 
LIST 
OF Traj] =  {
outlineData: OutlineData ← NARROW[outline.data];
trajList ← outlineData.children;
};
 
ListHolesOfOutline: 
PROC [outline: Outline] 
RETURNS [trajList: 
LIST 
OF Traj] =  {
outlineData: OutlineData ← NARROW[outline.data];
trajList ← outlineData.children.rest;
};
 
ListSegmentsInTraj: 
PROC [traj: Traj] 
RETURNS [segList: 
LIST 
OF Segment] = {
segList ← NIL;
FOR i: 
NAT 
DECREASING 
IN [0..GGTraj.HiSegment[traj]] 
DO
segList ← CONS[GGTraj.FetchSegment[traj, i], segList];
ENDLOOP;
 
};
 
Hit Testing -- Outline-Specific routines.  These should go away when Outlines become Slices.
NearestTrajectoryInScene: 
PUBLIC 
PROC [scene: SceneRef, worldPt: Point, ggData: GGData] 
RETURNS [traj: Traj] = {
feature: GGModelTypes.FeatureData;
resultPoint: Point;
sceneObjects: GGInterfaceTypes.TriggerBag;
sceneObjects ← NARROW[ggData.hitTest.sceneBag];
[resultPoint, feature] ← GGMultiGravity.StrictDistance[worldPt, ggData.hitTest.criticalR, GGAlign.emptyAlignBag, sceneObjects, ggData];
RETURN[IF feature=NIL OR feature.type=slice THEN NIL ELSE NARROW[feature.shape, Sequence].traj];
};
 
Selections
DeleteAllSelected: 
PUBLIC PROC [scene: SceneRef] 
RETURNS [bBox: BoundBox] = {
DeleteRun: GGSelect.RunProc = {
traj ← NIL;
};
 
sliceDescGen: GGModelTypes.SliceDescriptorGenerator;
thisBox: BoundBox;
bBox ← GGBoundBox.BoundBoxOfSelected[scene, normal];
SELECT ... sliceD
sliceDescGen ← GGSelect.SelectedSlices[scene, normal];
FOR sliceD: SliceDescriptor ← GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen] 
UNTIL sliceD = 
NIL 
DO
IF sliceD.slice.class.type=$Outline THEN LOOP; -- OUTLINES ARE TAKEN CARE OF BY ForEachOutlineRun CALL FOLLOWING !!
GGSelect.DeselectEntireSlice[sliceD.slice, scene, hot];
GGScene.DeleteSlice[scene, sliceD.slice];
ENDLOOP;
 
thisBox ← GGSelect.ForEachOutlineRun[scene, normal, DeleteRun, FALSE];
GGSelect.DeselectAll[scene, normal];
GGBoundBox.EnlargeByBox[bBox: bBox, by: thisBox];
};
 
SaveSelections: 
PUBLIC 
PROC [scene: SceneRef] = {
scene.savedSelected.normal ← ListCopy[scene.selected.normal];
Make a copy of the list quickly. The list will be modified in place later on.
 
};
 
RestoreSelections: 
PUBLIC 
PROC [scene: SceneRef] = {
GGSelect.DeselectAll[scene, normal];  -- get rid of any transient selections
FOR list: 
LIST 
OF SliceDescriptor ← scene.savedSelected.normal, list.rest 
UNTIL list = 
NIL 
DO
GGSelect.SelectSlice[list.first, scene, normal];
ENDLOOP;
 
};
 
GetSelections: 
PUBLIC 
PROC [scene: SceneRef, selectClass: GGSegmentTypes.SelectionClass] 
RETURNS [selected: 
LIST 
OF SliceDescriptor] = {
SELECT selectClass 
FROM
normal => selected ← scene.selected.normal;
hot => selected ← scene.selected.hot;
active => selected ← scene.selected.active;
ENDCASE => ERROR;
 
};
 
ListCopy: 
PROC [l1: 
LIST OF SliceDescriptor] 
RETURNS [val: 
LIST OF SliceDescriptor] = {
z: LIST OF SliceDescriptor ← NIL;
val ← NIL;
IF l1 = NIL THEN RETURN[val];
val ← CONS[l1.first, val];
z ← val;
UNTIL (l1 ← l1.rest) = 
NIL 
DO
z.rest ← CONS[l1.first, z.rest];
z ← z.rest;
ENDLOOP;
 
RETURN[val];
}; -- of Append
 
ListDRemove: PROC [ref: SliceDescriptor, list: LIST OF SliceDescriptor] RETURNS[LIST OF SliceDescriptor] =  {
l, l1: LIST OF SliceDescriptor ← NIL;
l ← list;
UNTIL l = NIL DO
IF l.first=ref THEN {
IF l1 = NIL THEN RETURN[l.rest]; -- ref was first object on list
l1.rest ← l.rest;
RETURN[list];
};
 
l1 ← l;
l ← l.rest;
ENDLOOP;
 
RETURN [list];
};
 
END.