GGSelectImpl.mesa
Contents: Procedures dealing selecting and deselecting objects, including both selection actions and selection feedback.
Copyright Ó 1988 by Xerox Corporation. All rights reserved.
Pier, November 10, 1988 4:41:22 pm PST
Kurlander August 28, 1986 7:54:12 pm PDT
Bier, June 22, 1989 9:22:07 pm PDT
DIRECTORY
GGBasicTypes, GGBoundBox, GGCoreTypes, GGModelTypes, GGOutline, GGParent, GGScene, GGSceneType, GGSegmentTypes, GGSelect, GGSequence, GGSlice, GGSliceOps, GGTraj, TextNode;
GGSelectImpl: CEDAR PROGRAM
IMPORTS GGBoundBox, GGOutline, GGParent, GGScene, GGSequence, GGSlice, GGSliceOps, GGTraj
EXPORTS GGModelTypes, GGSelect = BEGIN
BoundBox: TYPE = GGCoreTypes.BoundBox;
Joint: TYPE = GGModelTypes.Joint;
JointGenerator: TYPE = GGModelTypes.JointGenerator;
OutlineParts: TYPE = GGOutline.OutlineParts;
Scene: TYPE = GGModelTypes.Scene;
SceneObj: PUBLIC TYPE = GGSceneType.SceneObj;
SegAndIndex: TYPE = GGSequence.SegAndIndex;
Segment: TYPE = GGSegmentTypes.Segment;
SegmentGenerator: TYPE = GGModelTypes.SegmentGenerator;
SelectionClass: TYPE = GGSegmentTypes.SelectionClass;
Sequence: TYPE = GGModelTypes.Sequence;
SequenceGenerator: TYPE = GGSequence.SequenceGenerator;
SequenceGeneratorObj: TYPE = GGSequence.SequenceGeneratorObj;
Slice: TYPE = GGModelTypes.Slice;
SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor;
SliceDescriptorGenerator: TYPE = GGModelTypes.SliceDescriptorGenerator;
SliceDescriptorGeneratorObj: TYPE = GGModelTypes.SliceDescriptorGeneratorObj;
SliceDescriptorObj: TYPE = GGModelTypes.SliceDescriptorObj;
SliceDescriptorWalkProc: TYPE = GGModelTypes.SliceDescriptorWalkProc;
SliceGenerator: TYPE = GGModelTypes.SliceGenerator;
SliceGeneratorObj: TYPE = GGModelTypes.SliceGeneratorObj;
SliceParts: TYPE = GGModelTypes.SliceParts;
SliceWalkProc: TYPE = GGModelTypes.SliceWalkProc;
SlicePartsWalkProc: TYPE = GGModelTypes.SlicePartsWalkProc;
TrajData: TYPE = GGModelTypes.TrajData;
TrajEnd: TYPE = GGModelTypes.TrajEnd;
TrajParts: TYPE = GGModelTypes.TrajParts;
OutlineSequenceGenerator: TYPE = REF OutlineSequenceGeneratorObj;
OutlineSequenceGeneratorObj: TYPE = RECORD [
list: LIST OF OutlineSequence
];
OutlineSequence: TYPE = REF OutlineSequenceObj;
OutlineSequenceObj: TYPE = RECORD [
outline: Slice,  -- the outline represented by this record
fenceParts: TrajParts,  -- included parts of the fence trajectory
holeParts: LIST OF TrajParts -- included parts of holes
];
NotYetImplemented: PUBLIC SIGNAL = CODE;
Implementation Notes (updated July 6, 1987, by Bier)
The Selection mechanism maintains two "structures" which must be kept consistent. The first is a list of selected SliceDescriptors: scene.selected. The second is the set of "selectedInFull" fields in each of the selected slices.
To efficiently answer IsSelectedInFull queries, "selectedInFull" fields are found in slices, segments, joints, and control points. IsSelectedInFull is expensive for trajectories, and more expensive for outlines.
To efficiently answer IsSelectedInPart queries, the selectedInFull field does double duty in segments, joints, control points, and slices. IsSelectedInPart is somewhat expensive for outlines.
The following invariants should be maintained:
1) If a SliceDescriptor is on the selected list, all of the selectedInFull fields of its joints, segments, and control points should be TRUE. The selected fields are maintained for efficiency only. The information they represent is always obtainable from the selected list as well.
Converse of 1: If no sequence representing a given joint, segment, or control point is on the selected list, then the selectedInFull field of that entity should be FALSE.
2) Only one sequence per trajectory may appear on the selected list.
Corollary of 1 and 2: Any given selected flag is caused by a single element of the selected list. GetSelectedSequence will find that element (for joint, segments, and control points). A selected slice is its own cause.
3) A slice may only appear once on the selected list.
4) Iff a sequence representing a trajectory is on the selected list, the "selectedInPart" field of the trajectory will TRUE.
5) Iff a non-Outline slice is on the selected list, the "selectedInFull" field of that slice will be true.
These invariants will be maintained with the help of SetComponentSelectedFields.
Basic Selecting and Deselecting
ChangeDonkeyTail: PROC [sliceD: SliceDescriptor, selectClass: SelectionClass] = {
SELECT selectClass FROM
normal => sliceD.slice.normalSelectedParts ← sliceD;
hot => sliceD.slice.hotSelectedParts ← sliceD;
active => sliceD.slice.activeSelectedParts ← sliceD;
match => sliceD.slice.matchSelectedParts ← sliceD;
ENDCASE;
};
NoDonkeyTail: PROC [slice: Slice, selectClass: SelectionClass] = {
SELECT selectClass FROM
normal => slice.normalSelectedParts ← NIL;
hot => slice.hotSelectedParts ← NIL;
active => slice.activeSelectedParts ← NIL;
match => slice.matchSelectedParts ← NIL;
ENDCASE;
};
SelectAll: PUBLIC PROC [scene: Scene, selectClass: SelectionClass] = {
DoSelectSlice: PROC [slice: Slice] RETURNS [done: BOOLFALSE] = {
SelectEntireSlice[slice, scene, selectClass];
};
[] ← GGScene.WalkSlices[scene, first, DoSelectSlice];
IF selectClass = normal THEN {
IF scene.selected.normal = NIL OR scene.selected.normal.rest # NIL
THEN scene.selected.normalLast ← NIL
ELSE scene.selected.normalLast ← scene.selected.normal.first;
};
};
SelectSliceFromParts: PUBLIC PROC [slice: Slice, parts: SliceParts, scene: Scene, selectClass: SelectionClass] = {
sliceD: SliceDescriptor ← GGSlice.DescriptorFromParts[slice, parts];
SelectSlice[sliceD, scene, selectClass];
IF selectClass = normal THEN scene.selected.normalLast ← GGParent.TopLevelDescriptorFromChildDescriptor[sliceD];
};
SelectSlice: PUBLIC PROC [sliceD: SliceDescriptor, scene: Scene, selectClass: SelectionClass] = {
oldSliceD, union: SliceDescriptor;
IF sliceD=NIL OR sliceD.parts = NIL OR GGSliceOps.IsEmptyParts[sliceD] THEN RETURN;
sliceD ← GGParent.TopLevelDescriptorFromChildDescriptor[sliceD];
oldSliceD ← FindSelectedSlice[sliceD.slice, selectClass];
IF oldSliceD#NIL THEN {
union ← GGSliceOps.UnionParts[oldSliceD, sliceD];
DeselectSlice[sliceD.slice, oldSliceD.parts, scene, selectClass]; -- throw away old selection
}
ELSE union ← sliceD;
IF selectClass = normal THEN union ← GGSliceOps.AugmentParts[union, selectClass];
AppendSelection[scene, union, selectClass];
ChangeDonkeyTail[union, selectClass];
IF selectClass = normal THEN scene.selected.normalLast ← GGParent.TopLevelDescriptorFromChildDescriptor[sliceD];
}; -- end SelectSlice
SelectEntireSlice: PUBLIC PROC [slice: Slice, scene: Scene, selectClass: SelectionClass] = {
allParts: SliceDescriptor ← GGSliceOps.NewParts[slice, NIL, slice];
SelectSlice[allParts, scene, selectClass];
};
DuplicateSelections: PUBLIC PROC [scene: Scene, fromClass: SelectionClass, toClass: SelectionClass] = {
DoSelectSlice: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOLFALSE] = {
SelectSlice[sliceD, scene, toClass];
};
IF fromClass=toClass THEN RETURN;
[] ← GGScene.WalkSelectedSlices[scene, first, DoSelectSlice, fromClass];
};
DeselectEntity: PUBLIC PROC [entity: REF ANY, scene: Scene, selectClass: SelectionClass] = {
WITH entity SELECT FROM
slice: Slice => DeselectEntireSlice[slice, scene, selectClass];
sliceD: SliceDescriptor => DeselectSlice[sliceD.slice, sliceD.parts, scene, selectClass];
ENDCASE => ERROR;
};
DeselectEntityAllClasses: PUBLIC PROC [entity: REF ANY, scene: Scene] = {
DeselectEntity[entity, scene, normal];
DeselectEntity[entity, scene, active];
DeselectEntity[entity, scene, hot];
DeselectEntity[entity, scene, match];
};
DeselectAll: PUBLIC PROC [scene: Scene, selectClass: SelectionClass] = {
Clear all flags that indicate the selection of each object.
FOR sliceD: LIST OF SliceDescriptor ← ListSelected[scene, selectClass], sliceD.rest UNTIL sliceD = NIL DO
NoDonkeyTail[sliceD.first.slice, selectClass];
ENDLOOP;
SetSelectedList[scene, NIL, selectClass];
};
DeselectAllAllClasses: PUBLIC PROC [scene: Scene] = {
DeselectAll[scene, normal];
DeselectAll[scene, active];
DeselectAll[scene, hot];
DeselectAll[scene, match];
};
DeselectEntireSlice: PUBLIC PROC [slice: Slice, scene: Scene, selectClass: SelectionClass] = {
Remove this slice from the selected list, and reset all selected fields.
IF GGScene.IsTopLevel[slice] THEN {
sliceD: SliceDescriptor ← FindSelectedSlice[slice, selectClass];
IF sliceD # NIL THEN {
DeleteSelection[scene, sliceD, selectClass];
NoDonkeyTail[sliceD.slice, selectClass];
};
IF selectClass = normal THEN
IF scene.selected.normalLast # NIL AND scene.selected.normalLast.slice = slice
THEN scene.selected.normalLast ← NIL;
}
ELSE {
entireD: SliceDescriptor ← GGSliceOps.NewParts[slice, NIL, slice];
DeselectSlice[entireD.slice, entireD.parts, scene, selectClass];
};
};
DeselectSlice: PUBLIC PROC [slice: Slice, parts: SliceParts, scene: Scene, selectClass: SelectionClass] = {
oldD, newD, tempD: SliceDescriptor;
tempD ← GGSlice.DescriptorFromParts[slice, parts];
tempD ← GGParent.TopLevelDescriptorFromChildDescriptor[tempD];
oldD ← FindSelectedSlice[tempD.slice, selectClass];
IF oldD#NIL THEN {
newD ← GGSliceOps.DifferenceParts[oldD, tempD];
DeleteSelection[scene, oldD, selectClass];
NoDonkeyTail[oldD.slice, selectClass];
IF NOT GGSliceOps.IsEmptyParts[newD] THEN {
IF selectClass = normal THEN newD ← GGSliceOps.AugmentParts[newD, selectClass];
AppendSelection[scene, newD, selectClass];
ChangeDonkeyTail[newD, selectClass];
};
IF selectClass = normal THEN {
IF scene.selected.normalLast # NIL AND scene.selected.normalLast.slice = slice
THEN scene.selected.normalLast ← GGSliceOps.DifferenceParts[scene.selected.normalLast, tempD];
IF scene.selected.normalLast = NIL OR GGSliceOps.IsEmptyParts[scene.selected.normalLast]
THEN scene.selected.normalLast ← NIL;
};
};
};
DeselectTraj: PROC [traj: Slice, scene: Scene, selectClass: SelectionClass] = {
Very much like DeselectSlice, but optimized for the single Traj case.
ancestor: Slice ← GGParent.GetTopLevelAncestor[traj];
oldD: SliceDescriptor ← FindSelectedSlice[ancestor, selectClass];
IF oldD#NIL THEN {
newD: SliceDescriptor ← GGParent.RemoveTraj[oldD, traj];
DeleteSelection[scene, oldD, selectClass];
NoDonkeyTail[ancestor, selectClass];
IF NOT GGSliceOps.IsEmptyParts[newD] THEN {
AppendSelection[scene, newD, selectClass];
ChangeDonkeyTail[newD, selectClass];
};
IF selectClass = normal THEN
IF scene.selected.normalLast # NIL AND scene.selected.normalLast.slice = ancestor
THEN {
scene.selected.normalLast ← GGParent.RemoveTraj[scene.selected.normalLast, traj];
IF scene.selected.normalLast = NIL OR GGSliceOps.IsEmptyParts[scene.selected.normalLast]
THEN scene.selected.normalLast ← NIL;
};
};
};
Updating Selections When Sequences Become Obsolete
SaveSelectionsInSliceAllClasses: PUBLIC PROC [slice: Slice, scene: Scene] = {
sliceD: SliceDescriptor;
sliceD ← FindSelectedSlice[slice, normal];
GGSliceOps.SaveSelections[slice, IF sliceD = NIL THEN NIL ELSE sliceD.parts, normal];
sliceD ← FindSelectedSlice[slice, hot];
GGSliceOps.SaveSelections[slice, IF sliceD = NIL THEN NIL ELSE sliceD.parts, hot];
sliceD ← FindSelectedSlice[slice, active];
GGSliceOps.SaveSelections[slice, IF sliceD = NIL THEN NIL ELSE sliceD.parts, active];
sliceD ← FindSelectedSlice[slice, match];
GGSliceOps.SaveSelections[slice, IF sliceD = NIL THEN NIL ELSE sliceD.parts, match];
};
RemakeSelections: PROC [slice: Slice, scene: Scene, selectClass: SelectionClass] RETURNS [isTopLevel: BOOLFALSE] = {
parts: SliceParts ← GGSliceOps.RemakeSelections[slice, selectClass];
IF GGScene.IsTopLevel[slice] THEN {
DeselectEntireSlice[slice, scene, selectClass]; -- get rid of old selection
SelectSliceFromParts[slice, parts, scene, selectClass];
isTopLevel ← TRUE;
};
};
ReselectSliceAllClasses: PUBLIC PROC [slice: Slice, scene: Scene] = {
[] ← RemakeSelections[slice, scene, normal];
[] ← RemakeSelections[slice, scene, hot];
[] ← RemakeSelections[slice, scene, active];
[] ← RemakeSelections[slice, scene, match];
};
ReselectTraj: PUBLIC PROC [traj: Slice, trajEnd: TrajEnd, scene: Scene, extend: BOOL] RETURNS [newHot: Sequence] = {
This routine propogates the former selection (say, hotness) to a newly extended traj and extends the selection to its end if extend = TRUE.
newOutlineD: SliceDescriptor;
newTrajD: SliceDescriptor;
selSeq: Sequence;
newSeqParts, newHotParts: TrajParts;
selSeq ← FindSelectedSequence[traj, scene, normal];
normal selections
IF selSeq#NIL THEN {
newSeqParts ← GGSequence.Augment[selSeq, trajEnd, extend];
DeselectTraj[selSeq.slice, scene, normal];
newTrajD ← GGSlice.DescriptorFromParts[traj, newSeqParts];
SelectSlice[newTrajD, scene, normal];
};
hot selections
selSeq ← FindSelectedSequence[traj, scene, hot];
IF selSeq#NIL THEN {
newHotParts ← newSeqParts ← GGSequence.Augment[selSeq, trajEnd, extend];
DeselectTraj[selSeq.slice, scene, hot]; -- assumes second level Traj
newTrajD ← newHot ← GGSlice.DescriptorFromParts[traj, newSeqParts];
SelectSlice[newTrajD, scene, hot];
};
Ignore active and match selections
};
Utilities
DeleteSelection: PROC [scene: Scene, entity: SliceDescriptor, selectClass: SelectionClass] = {
Deletes entity from list. If it is not on the list, do nothing.
SetSelectedList[scene,
DRemove[entity, ListSelected[scene, selectClass]],
selectClass];
};
Operations which dispatch on the SelectionClass:
ListSelected: PUBLIC PROC [scene: Scene, selectClass: SelectionClass] RETURNS [selectedList: LIST OF SliceDescriptor] = {
SELECT selectClass FROM
normal => selectedList ← scene.selected.normal;
hot => selectedList ← scene.selected.hot;
active => selectedList ← scene.selected.active;
match => selectedList ← scene.selected.match;
ENDCASE => ERROR;
};
SetSelected: PUBLIC PROC [scene: Scene, selectClass: SelectionClass, selected: LIST OF SliceDescriptor] = {
For each SliceDescriptor in selected that is not obsolete, re-establish that selection. Remember to call DeselectAll[scene, selectClass] if you wish to replace any existing selections.
FOR list: LIST OF SliceDescriptor ← selected, list.rest UNTIL list = NIL DO
SelectSlice[list.first, scene, selectClass];
ENDLOOP;
};
SetJointField: PROC [joint: Joint, selected: BOOL, selectClass: SelectionClass] = {
SELECT selectClass FROM
normal => joint.TselectedInFull.normal ← selected;
hot => joint.TselectedInFull.hot ← selected;
active => joint.TselectedInFull.active ← selected;
match => joint.TselectedInFull.match ← selected;
ENDCASE;
};
SetSegmentField: PROC [seg: Segment, selected: BOOL, selectClass: SelectionClass] = {
SELECT selectClass FROM
normal => seg.TselectedInFull.normal ← selected;
hot => seg.TselectedInFull.hot ← selected;
active => seg.TselectedInFull.active ← selected;
match => seg.TselectedInFull.match ← selected;
ENDCASE;
};
OPERATIONS THAT ALTER THE SELECTED LISTS
AppendSelection: PROC [scene: Scene, entity: SliceDescriptor, selectClass: SelectionClass] = {
Build the selection list in the proper order and keep it that way.
SELECT selectClass FROM
normal => {
IF scene.selected.normal = NIL THEN {
scene.selected.normal ← LIST[entity];
scene.selected.normalPtr ← scene.selected.normal;
scene.selected.normalPtrValid ← TRUE;
}
ELSE IF scene.selected.normalPtrValid THEN {
scene.selected.normalPtr.rest ← LIST[entity];
scene.selected.normalPtr ← scene.selected.normalPtr.rest;
}
ELSE {
scene.selected.normalPtr ← LastSliceDescriptor[scene.selected.normal];
scene.selected.normalPtr.rest ← LIST[entity];
scene.selected.normalPtr ← scene.selected.normalPtr.rest;
scene.selected.normalPtrValid ← TRUE;
};
};
hot => {
IF scene.selected.hot = NIL THEN {
scene.selected.hot ← LIST[entity];
scene.selected.hotPtr ← scene.selected.hot;
scene.selected.hotPtrValid ← TRUE;
}
ELSE IF scene.selected.hotPtrValid THEN {
scene.selected.hotPtr.rest ← LIST[entity];
scene.selected.hotPtr ← scene.selected.hotPtr.rest;
}
ELSE {
scene.selected.hotPtr ← LastSliceDescriptor[scene.selected.hot];
scene.selected.hotPtr.rest ← LIST[entity];
scene.selected.hotPtr ← scene.selected.hotPtr.rest;
scene.selected.hotPtrValid ← TRUE;
};
};
active => {
IF scene.selected.active = NIL THEN {
scene.selected.active ← LIST[entity];
scene.selected.activePtr ← scene.selected.active;
scene.selected.activePtrValid ← TRUE;
}
ELSE IF scene.selected.activePtrValid THEN {
scene.selected.activePtr.rest ← LIST[entity];
scene.selected.activePtr ← scene.selected.activePtr.rest;
}
ELSE {
scene.selected.activePtr ← LastSliceDescriptor[scene.selected.active];
scene.selected.activePtr.rest ← LIST[entity];
scene.selected.activePtr ← scene.selected.activePtr.rest;
scene.selected.activePtrValid ← TRUE;
};
};
match => {
IF scene.selected.match = NIL THEN {
scene.selected.match ← LIST[entity];
scene.selected.matchPtr ← scene.selected.match;
scene.selected.matchPtrValid ← TRUE;
}
ELSE IF scene.selected.matchPtrValid THEN {
scene.selected.matchPtr.rest ← LIST[entity];
scene.selected.matchPtr ← scene.selected.matchPtr.rest;
}
ELSE {
scene.selected.matchPtr ← LastSliceDescriptor[scene.selected.match];
scene.selected.matchPtr.rest ← LIST[entity];
scene.selected.matchPtr ← scene.selected.matchPtr.rest;
scene.selected.matchPtrValid ← TRUE;
};
};
ENDCASE => ERROR;
};
LastSliceDescriptor: PROC [sliceDList: LIST OF SliceDescriptor] RETURNS [last: LIST OF SliceDescriptor] = {
FOR last ← sliceDList, last.rest UNTIL last.rest = NIL DO ENDLOOP;
};
SetSelectedList: PROC [scene: Scene, value: LIST OF SliceDescriptor, selectClass: SelectionClass] = {
SELECT selectClass FROM
normal => {
scene.selected.normal ← value;
scene.selected.normalPtrValid ← FALSE;
};
hot => {
scene.selected.hot ← value;
scene.selected.hotPtrValid ← FALSE;
};
active => {
scene.selected.active ← value;
scene.selected.activePtrValid ← FALSE;
};
match => {
scene.selected.match ← value;
scene.selected.matchPtrValid ← FALSE;
};
ENDCASE => ERROR;
};
Enumerate Selected Objects
IsSelectedInFull: PUBLIC PROC [slice: Slice, scene: Scene, selectClass: SelectionClass] RETURNS [BOOL] = {
donkeyTail: SliceDescriptor ← SELECT selectClass FROM
match => slice.matchSelectedParts,
active => slice.activeSelectedParts,
hot => slice.hotSelectedParts,
normal => slice.normalSelectedParts,
ENDCASE => ERROR;
IF donkeyTail = NIL THEN RETURN[FALSE];
RETURN[GGSliceOps.IsCompleteParts[donkeyTail]];
};
IsSelectedInPart: PUBLIC PROC [entity: REF ANY, scene: Scene, selectClass: SelectionClass] RETURNS [BOOL] = {
Returns TRUE if all or part of entity is selected. These are the cases:
1) entity is a slice. Some outline is selected all or in part.
2) entity is an outline. Some trajectory is selected all or in part.
3) entity is a trajectory. Some joint or segment is selected.
4) entity is a sequence. Some joint or segment is selected.
WITH entity SELECT FROM
slice: Slice => {
sliceD: SliceDescriptor ← FindSelectedSlice[slice, selectClass];
RETURN[sliceD # NIL];
};
traj: Slice => {
seq: Sequence;
seq ← FindSelectedSequence[traj, scene, selectClass];
RETURN[seq # NIL];
};
seq: Sequence => {
selSeq: Sequence;
selSeq ← FindSelectedSequence[seq.slice, scene, selectClass];
IF selSeq = seq THEN RETURN[TRUE];
IF selSeq = NIL THEN RETURN[FALSE];
RETURN[GGSequence.Overlap[seq, selSeq]];
};
ENDCASE => ERROR;
};
NoSelections: PUBLIC PROC [scene: Scene, selectClass: SelectionClass] RETURNS [BOOL] = {
SELECT selectClass FROM
normal => RETURN[scene.selected.normal = NIL];
hot => RETURN[scene.selected.hot = NIL];
active => RETURN[scene.selected.active = NIL];
match => RETURN[scene.selected.match = NIL];
ENDCASE => ERROR;
};
FindSelectedSequence: PROC [traj: Slice, scene: Scene, selectClass: SelectionClass] RETURNS [seq: Sequence] = { -- returns SliceDescriptor for a trajectory if selected
LookForTraj: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOLFALSE] = {
IF sliceD.slice=traj THEN {
seq ← sliceD;
done ← TRUE;
};
};
[] ← GGScene.WalkSelectedSlices[scene, leaf, LookForTraj, selectClass, $Traj];
};
FindSelectedSlice: PUBLIC PROC [slice: Slice, selectClass: SelectionClass] RETURNS [sliceD: SliceDescriptor] = {
SELECT selectClass FROM
normal => sliceD ← slice.normalSelectedParts;
hot => sliceD ← slice.hotSelectedParts;
active => sliceD ← slice.activeSelectedParts;
match => sliceD ← slice.matchSelectedParts;
ENDCASE => ERROR;
};
GetLastSelection: PUBLIC PROC [scene: Scene] RETURNS [sliceD: SliceDescriptor] = {
sliceD ← scene.selected.normalLast;
};
SubstituteForSegment: PUBLIC PROC [traj: Slice, segNum: NAT, newRun: Slice, scene: Scene] RETURNS [bBox: BoundBox, newTraj: Slice] = {
Replaces a segment with a new run. If newRun is not NIL, then only one trajectory is modified, and the new trajectory is returned. Also returned is the bbox of modified Outlines.
newOutlines: LIST OF Slice;
trajData: TrajData ← NARROW[traj.data];
runParts: TrajParts ← GGSequence.CreateFromSegment[trajData, segNum];
run: SliceDescriptor ← GGSlice.DescriptorFromParts[traj, runParts];
oldOutline: Slice ← GGParent.GetParent[traj];
newOutline: Slice;
priority: INT ← GGScene.GetPriority[scene, oldOutline];
GGOutline.SaveSelectionsInOutlineAllClasses[oldOutline];
DeselectTraj[traj, scene, normal];
DeselectTraj[traj, scene, hot];
newTraj ← GGTraj.SpliceIn[run, newRun];
newOutline ← GGParent.GetParent[newTraj];
GGScene.DeleteSlice[scene, oldOutline];
IF newOutline#NIL THEN {
GGScene.AddSlice[scene, newOutline, priority];
ReselectSliceAllClasses[newOutline, scene];
};
bBox ← BoundBoxOfOutlines[newOutlines];
};
BoundBoxOfOutlines: PROC [outlines: LIST OF Slice] RETURNS [bBox: BoundBox] = {
bBox ← GGBoundBox.NullBoundBox[];
FOR list: LIST OF Slice ← outlines, list.rest UNTIL list = NIL DO
GGBoundBox.EnlargeByBox[bBox: bBox, by: GGSliceOps.GetBoundBox[list.first] ];
ENDLOOP;
};
DRemove: 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.