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:
BOOL ←
FALSE] = {
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:
BOOL ←
FALSE] = {
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:
BOOL ←
FALSE] = {
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
};
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:
BOOL ←
FALSE] = {
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.