GGSelectImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Last edited by Bier on March 24, 1987 10:05:40 pm PST
Contents: Procedures dealing selecting and deselecting objects, including both selection actions and selection feedback.
Pier, April 24, 1987 2:26:19 pm PDT
Kurlander August 28, 1986 7:54:12 pm PDT
DIRECTORY
GGBasicTypes, GGBoundBox, GGInterfaceTypes, GGModelTypes, GGScene, GGOutline, GGSceneType, GGSegmentTypes, GGSelect, GGSequence, GGSlice, GGTraj, GGUtility, List;
GGSelectImpl:
CEDAR
PROGRAM
IMPORTS GGBoundBox, GGScene, GGOutline, GGSequence, GGSlice, GGTraj, GGUtility, List
EXPORTS GGModelTypes, GGSelect = BEGIN
BoundBox: TYPE = GGBasicTypes.BoundBox;
Caret: TYPE = GGInterfaceTypes.Caret;
EntityGenerator: TYPE = REF EntityGeneratorObj;
EntityGeneratorObj: TYPE = GGModelTypes.EntityGeneratorObj;
Joint: TYPE = GGModelTypes.Joint;
JointGenerator: TYPE = GGModelTypes.JointGenerator;
Outline: TYPE = GGModelTypes.Outline;
OutlineDescriptor: TYPE = REF OutlineDescriptorObj;
OutlineDescriptorObj: TYPE = GGModelTypes.OutlineDescriptorObj;
SceneRef: TYPE = REF SceneObj;
SceneObj: PUBLIC TYPE = GGSceneType.SceneObj;
SegAndIndex: TYPE = GGSequence.SegAndIndex;
Segment: TYPE = GGSegmentTypes.Segment;
SegmentGenerator: TYPE = GGModelTypes.SegmentGenerator;
SelectionClass: TYPE = GGInterfaceTypes.SelectionClass;
Sequence: TYPE = REF SequenceObj;
SequenceGenerator: TYPE = REF SequenceGeneratorObj;
SequenceGeneratorObj: TYPE = GGModelTypes.SequenceGeneratorObj;
SequenceObj: TYPE = GGModelTypes.SequenceObj;
Slice: TYPE = GGModelTypes.Slice;
SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor;
SliceDescriptorGenerator: TYPE = GGModelTypes.SliceDescriptorGenerator;
SliceDescriptorGeneratorObj: TYPE = GGModelTypes.SliceDescriptorGeneratorObj;
SliceDescriptorObj: TYPE = GGModelTypes.SliceDescriptorObj;
SliceGenerator: TYPE = REF SliceGeneratorObj;
SliceGeneratorObj: TYPE = GGModelTypes.SliceGeneratorObj;
SliceParts: TYPE = GGModelTypes.SliceParts;
Traj: TYPE = GGModelTypes.Traj;
TrajEnd: TYPE = GGModelTypes.TrajEnd;
TrajGenerator: TYPE = REF TrajGeneratorObj;
TrajGeneratorObj:
TYPE = GGModelTypes.TrajGeneratorObj;
NotYetImplemented:
PUBLIC
SIGNAL =
CODE;
OutlineSequenceGenerator: TYPE = REF OutlineSequenceGeneratorObj;
OutlineSequenceGeneratorObj: TYPE = GGSelect.OutlineSequenceGeneratorObj;
OutlineSequence: TYPE = REF OutlineSequenceObj;
OutlineSequenceObj:
TYPE = GGSelect.OutlineSequenceObj;
Implementation Notes (updated November 16, 1986, by Bier)
The Selection mechanism maintains two "structures" which must be kept consistent. The first is a list of selected entities: scene.selected. The second is the set of "selectedInFull" fields in each of the selected objects. The following types of entities can appear on scene.selected: SliceDescriptor, and OutlineDescriptor.
To efficiently answer IsSelectedInFull queries, "selectedInFull" fields are found in slices, segments, joints, and control points. IsSelectedInFull is expensive for trajectories, more expensive for outlines, and even more expensive for Gargoyle slices.
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 an OutlineDescriptor 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 non-gargoyle 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-Gargoyle 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.
SetComponentSelectedFields: PROC [entity: REF ANY, selected: BOOL, selectClass: SelectionClass] = {
WITH entity SELECT FROM
sliceD: SliceDescriptor => {
SetSliceField[sliceD.slice, selected, selectClass];
};
outlineD: OutlineDescriptor => {
outlineD.slice.class.setSelectedFields[outlineD, selected, selectClass];
};
traj: Traj => { -- this case needed for ReselectTraj
joint: Joint;
jointGen: JointGenerator;
segGen: SegmentGenerator;
jointGen ← GGSequence.JointsInTraj[traj];
Joint Fields.
FOR jointNum: INT ← GGSequence.NextJoint[jointGen], GGSequence.NextJoint[jointGen] UNTIL jointNum = -1 DO
joint ← NARROW[Rosary.Fetch[traj.joints, jointNum]];
SetJointField[joint, selected, selectClass];
ENDLOOP;
Segment Fields.
segGen ← GGSequence.SegmentsInTraj[traj];
FOR seg: Segment ← GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen] UNTIL seg = NIL DO
SetSegmentField[seg, selected, selectClass];
ENDLOOP;
};
ENDCASE => ERROR;
};
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;
ENDCASE;
};
ChangeDonkeyTailOutline:
PROC [sliceD: OutlineDescriptor, selectClass: SelectionClass] = {
SELECT selectClass
FROM
normal => sliceD.slice.normalSelectedParts ← sliceD;
hot => sliceD.slice.hotSelectedParts ← sliceD;
active => sliceD.slice.activeSelectedParts ← sliceD;
ENDCASE;
};
NoDonkeyTail:
PROC [slice: Slice, selectClass: SelectionClass] = {
SELECT selectClass
FROM
normal => slice.normalSelectedParts ← NIL;
hot => slice.hotSelectedParts ← NIL;
active => slice.activeSelectedParts ← NIL;
ENDCASE;
};
NoDonkeyTailOutline:
PROC [slice: Outline, selectClass: SelectionClass] = {
SELECT selectClass
FROM
normal => slice.normalSelectedParts ← NIL;
hot => slice.hotSelectedParts ← NIL;
active => slice.activeSelectedParts ← NIL;
ENDCASE;
};
SelectAll:
PUBLIC
PROC [scene: SceneRef, selectClass: SelectionClass] = {
entityGen: EntityGenerator;
entityGen ← GGScene.TopLevelEntitiesInScene[scene];
FOR entity:
REF
ANY ← GGScene.NextEntity[entityGen], GGScene.NextEntity[entityGen]
UNTIL entity =
NIL
DO
WITH entity
SELECT
FROM
slice: Slice => {
SelectEntireSlice[slice, scene, selectClass];
};
outline: Outline => {
SelectEntireOutline[outline, scene, selectClass];
};
ENDCASE => ERROR;
ENDLOOP;
};
SelectSlice:
PUBLIC
PROC [sliceD: SliceDescriptor, scene: SceneRef, selectClass: SelectionClass] = {
oldSliceD, union: SliceDescriptor;
slice: Slice ← sliceD.slice;
IF slice.class.emptyParts[sliceD] THEN RETURN;
oldSliceD ← FindSelectedSlice[slice, scene, selectClass];
IF oldSliceD#
NIL
THEN {
union ← slice.class.unionParts[oldSliceD, sliceD];
DeselectSlice[slice, oldSliceD.parts, scene, selectClass]; -- throw away old selection
}
ELSE union ← sliceD;
IF selectClass = normal THEN union ← slice.class.augmentParts[union, selectClass];
AppendSelection[scene, union, selectClass];
ChangeDonkeyTail[union, selectClass];
}; -- end SelectSlice
SelectOutline:
PUBLIC
PROC [sliceD: OutlineDescriptor, scene: SceneRef, selectClass: SelectionClass] = {
oldD, union: OutlineDescriptor;
slice: Outline ← sliceD.slice;
IF slice.class.emptyParts[sliceD] THEN RETURN;
oldD ← FindSelectedOutline[slice, scene, selectClass];
IF oldD #
NIL
THEN {
union ← slice.class.unionParts[oldD, sliceD];
DeselectOutline[slice, oldD.parts, scene, selectClass]; -- throw away old selection
}
ELSE union ← sliceD;
IF selectClass = normal THEN union ← slice.class.augmentParts[union, selectClass];
AppendSelection[scene, union, selectClass];
ChangeDonkeyTailOutline[union, selectClass];
}; -- end SelectOutline
SelectEntireOutline:
PUBLIC PROC [outline: Outline, scene: SceneRef, selectClass: SelectionClass] = {
allParts: OutlineDescriptor ← outline.class.newParts[outline, NIL, slice];
SelectOutline[allParts, scene, selectClass];
};
SelectEntireSlice:
PUBLIC PROC [slice: Slice, scene: SceneRef, selectClass: SelectionClass] = {
allParts: SliceDescriptor ← slice.class.newParts[slice, NIL, slice];
SelectSlice[allParts, scene, selectClass];
};
SelectSequence:
PUBLIC
PROC [seq: Sequence, scene: SceneRef, selectClass: SelectionClass] = {
outline: Outline ← GGOutline.OutlineOfTraj[seq.traj];
parts: SliceParts;
seqD: OutlineDescriptor;
IF GGSequence.IsEmpty[seq] THEN RETURN;
parts ← GGOutline.PartsFromSequence[outline, seq];
seqD ← GGOutline.DescriptorFromParts[outline, parts];
SelectOutline[seqD, scene, selectClass];
};
SelectTraj:
PUBLIC
PROC [traj: Traj, scene: SceneRef, selectClass: SelectionClass] = {
traj is now selected. This overrides any selections on traj's parts.
wholeSeq: Sequence;
wholeSeq ← GGSequence.CreateComplete[traj];
SelectSequence[wholeSeq, scene, selectClass];
};
DuplicateSelections:
PROC [scene: SceneRef, fromClass: SelectionClass, toClass: SelectionClass] = {
sliceDescGen: GGSelect.SliceDescriptorGenerator;
seqGen: SequenceGenerator;
sliceDescGen ← SelectedSlices[scene, fromClass];
FOR sliceD: SliceDescriptor ← NextSliceDescriptor[sliceDescGen], NextSliceDescriptor[sliceDescGen]
UNTIL sliceD =
NIL
DO
SelectSlice[sliceD, scene, toClass];
ENDLOOP;
seqGen ← SelectedSequences[scene, fromClass];
FOR seq: Sequence ← GGSequence.NextSequence[seqGen], GGSequence.NextSequence[seqGen]
UNTIL seq =
NIL
DO
SelectSequence[seq, scene, toClass];
ENDLOOP;
DeselectAll:
PUBLIC
PROC [scene: SceneRef, selectClass: SelectionClass] = {
Clear all flags that indicate the selection of each object.
FOR selected:
LIST
OF
REF
ANY ← ListSelected[scene, selectClass], selected.rest
UNTIL selected =
NIL
DO
SetComponentSelectedFields[selected.first, FALSE, selectClass];
WITH selected.first
SELECT
FROM
sliceD: SliceDescriptor => NoDonkeyTail[sliceD.slice, selectClass];
outlineD: OutlineDescriptor => NoDonkeyTailOutline[outlineD.slice, selectClass];
ENDCASE => ERROR;
ENDLOOP;
SetSelectedList[scene, NIL, selectClass];
};
DeselectEntity:
PUBLIC
PROC [entity:
REF
ANY, scene: SceneRef, selectClass: SelectionClass] = {
WITH entity
SELECT
FROM
seq: Sequence => DeselectSequence[seq, scene, selectClass];
traj: Traj => DeselectTraj[traj, scene, selectClass];
outline: Outline => DeselectEntireOutline[outline, scene, selectClass];
outlineD: OutlineDescriptor => DeselectOutline[outlineD.slice, outlineD.parts, scene, selectClass];
slice: Slice => DeselectEntireSlice[slice, scene, selectClass];
sliceD: SliceDescriptor => DeselectSlice[sliceD.slice, sliceD.parts, scene, selectClass];
ENDCASE => ERROR;
};
DeselectEntityAllClasses:
PUBLIC
PROC [entity:
REF
ANY, scene: SceneRef] = {
DeselectEntity[entity, scene, normal];
DeselectEntity[entity, scene, active];
DeselectEntity[entity, scene, hot];
};
DeselectAllAllClasses:
PUBLIC
PROC [scene: SceneRef] = {
DeselectAll[scene, normal];
DeselectAll[scene, active];
DeselectAll[scene, hot];
};
DeselectEntireSlice:
PUBLIC
PROC [slice: Slice, scene: SceneRef, selectClass: SelectionClass] = {
If this is a non-gargoyle slice, then make sure it is on the list, and take it off.
selSliceD: SliceDescriptor ← FindSelectedSlice[slice, scene, selectClass];
IF selSliceD#
NIL
THEN {
IF selSliceD.slice.class.type = $Outline THEN ERROR NotYetImplemented;
DeleteSelection[scene, selSliceD, selectClass];
NoDonkeyTail[selSliceD.slice, selectClass];
SetComponentSelectedFields[selSliceD, FALSE, selectClass];
};
};
DeselectSlice:
PUBLIC
PROC [slice: Slice, parts: SliceParts, scene: SceneRef, selectClass: SelectionClass] = {
oldD, newD, tempD: SliceDescriptor;
oldD ← FindSelectedSlice[slice, scene, selectClass];
IF oldD #
NIL
THEN {
tempD ← GGSlice.DescriptorFromParts[slice, parts];
newD ← slice.class.differenceParts[oldD, tempD];
DeleteSelection[scene, oldD, selectClass];
NoDonkeyTail[oldD.slice, selectClass];
IF
NOT slice.class.emptyParts[newD]
THEN {
IF selectClass = normal THEN newD ← slice.class.augmentParts[newD, selectClass];
AppendSelection[scene, newD, selectClass];
ChangeDonkeyTail[newD, selectClass];
};
};
};
DeselectOutline:
PUBLIC
PROC [outline: Outline, parts: SliceParts, scene: SceneRef, selectClass: SelectionClass] = {
Deselect each of the component trajectories;
oldD, newD, tempD: OutlineDescriptor;
oldD ← FindSelectedOutline[outline, scene, selectClass];
IF oldD #
NIL
THEN {
tempD ← GGOutline.DescriptorFromParts[outline, parts];
newD ← outline.class.differenceParts[oldD, tempD];
DeleteSelection[scene, oldD, selectClass];
NoDonkeyTailOutline[outline, selectClass];
IF
NOT outline.class.emptyParts[newD]
THEN {
IF selectClass = normal THEN newD ← outline.class.augmentParts[newD, selectClass];
AppendSelection[scene, newD, selectClass];
ChangeDonkeyTailOutline[newD, selectClass];
};
};
};
DeselectSequence:
PUBLIC
PROC [seq: Sequence, scene: SceneRef, selectClass: SelectionClass] = {
outline: Outline ← GGOutline.OutlineOfTraj[seq.traj];
seqParts: SliceParts ← GGOutline.PartsFromSequence[outline, seq];
DeselectOutline[outline, seqParts, scene, selectClass];
};
DeselectEntireOutline:
PUBLIC
PROC [outline: Outline, scene: SceneRef, selectClass: SelectionClass] = {
Remove this outline from the selected list, and reset all selected fields.
outlineD: OutlineDescriptor ← FindSelectedOutline[outline, scene, selectClass];
IF outlineD #
NIL
THEN {
DeleteSelection[scene, outlineD, selectClass];
NoDonkeyTailOutline[outlineD.slice, selectClass];
};
};
DeselectTraj:
PUBLIC
PROC [traj: Traj, scene: SceneRef, selectClass: SelectionClass] = {
Very much like DeselectOutline, but optimized for the single Traj case.
outline: Outline ← GGOutline.OutlineOfTraj[traj];
oldD, newD: OutlineDescriptor;
oldD ← FindSelectedOutline[outline, scene, selectClass];
IF oldD #
NIL
THEN {
newD ← GGOutline.RemoveTraj[oldD, traj];
DeleteSelection[scene, oldD, selectClass];
NoDonkeyTailOutline[outline, selectClass];
IF
NOT outline.class.emptyParts[newD]
THEN {
AppendSelection[scene, newD, selectClass];
ChangeDonkeyTailOutline[newD, selectClass];
};
};
};
Updating Selections When Sequences Become Obsolete
ReselectTraj:
PUBLIC
PROC [traj: Traj, trajEnd: TrajEnd, scene: SceneRef, extend:
BOOL]
RETURNS [newHot: Sequence] = {
selSeq, newSeq: Sequence;
selSeq ← FindSelectedSequence[traj, scene, normal];
normal selections
IF selSeq #
NIL
THEN {
newSeq ← GGSequence.Augment[selSeq, trajEnd, extend];
DeselectTraj[selSeq.traj, scene, normal];
SelectSequence[newSeq, scene, normal];
};
hot selections
selSeq ← FindSelectedSequence[traj, scene, hot];
IF selSeq #
NIL
THEN {
newHot ← newSeq ← GGSequence.Augment[selSeq, trajEnd, extend];
DeselectTraj[selSeq.traj, scene, hot];
SelectSequence[newSeq, scene, hot];
};
active selections
selSeq ← FindSelectedSequence[traj, scene, active];
IF selSeq #
NIL
THEN {
ERROR; -- I don't think ReselectTraj should be used for active
};
};
Utilities
DeleteSelection:
PROC [scene: SceneRef, entity:
REF
ANY, selectClass: SelectionClass] = {
Deletes entity from list. If it is not on the list, do nothing.
SetSelectedList[scene,
List.DRemove[entity, ListSelected[scene, selectClass]],
selectClass];
};
ListSelected:
PUBLIC
PROC [scene: SceneRef, selectClass: SelectionClass]
RETURNS [selectedList:
LIST
OF
REF
ANY] = {
SELECT selectClass
FROM
normal => selectedList ← scene.selected.normal;
hot => selectedList ← scene.selected.hot;
active => selectedList ← scene.selected.active;
ENDCASE => ERROR;
};
ListSelectedSequences:
PUBLIC
PROC [scene: SceneRef, selectClass: SelectionClass]
RETURNS [selectedList:
LIST
OF Sequence] = {
This can be sped up by keeping separate lists for the different types of selected object.
seq: Sequence;
selectedList ← NIL;
FOR entityList:
LIST
OF
REF
ANY ← ListSelected[scene, selectClass], entityList.rest
UNTIL entityList =
NIL
DO
IF
ISTYPE[entityList.first, Sequence]
THEN {
seq ← NARROW[entityList.first];
selectedList ← CONS[seq, selectedList];
};
ENDLOOP;
};
Operations which dispatch on the SelectionClass:
SetSliceField:
PROC [slice: Slice, selected:
BOOL, selectClass: SelectionClass] = {
SELECT selectClass
FROM
normal => slice.selectedInFull.normal ← selected;
hot => slice.selectedInFull.hot ← selected;
active => slice.selectedInFull.active ← selected;
ENDCASE;
};
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;
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;
ENDCASE;
};
AppendSelection:
PROC [scene: SceneRef, entity:
REF
ANY, selectClass: SelectionClass] = {
Build the selection list in the proper order and keep it that way.
SELECT selectClass
FROM
normal => scene.selected.normal ← List.Nconc[scene.selected.normal, LIST[entity]];
hot => scene.selected.hot ← List.Nconc[scene.selected.hot, LIST[entity]];
active => scene.selected.active ← List.Nconc[scene.selected.active, LIST[entity]];
ENDCASE => ERROR;
};
SetSelectedList:
PROC [scene: SceneRef, value:
LIST
OF
REF
ANY, selectClass: SelectionClass] = {
SELECT selectClass
FROM
normal => scene.selected.normal ← value;
hot => scene.selected.hot ← value;
active => scene.selected.active ← value;
ENDCASE => ERROR;
};
Enumerate Selected Objects
IsSelectedInFull:
PUBLIC
PROC [entity:
REF
ANY, scene: SceneRef, selectClass: SelectionClass]
RETURNS [
BOOL] = {
WITH entity SELECT FROM
slice: Slice => {
IF slice.class.type = $Outline THEN ERROR NotYetImplemented;
SELECT selectClass
FROM
normal => RETURN[slice.selectedInFull.normal];
hot => RETURN[slice.selectedInFull.hot];
active => RETURN[slice.selectedInFull.active];
ENDCASE => ERROR;
};
outline: Outline => {
trajGen: TrajGenerator;
trajGen ← GGOutline.TrajsInOutline[outline];
FOR traj: Traj ← GGScene.NextTraj[trajGen], GGScene.NextTraj[trajGen]
UNTIL traj =
NIL
DO
IF NOT IsSelectedInFull[traj, scene, selectClass] THEN RETURN[FALSE];
ENDLOOP;
RETURN[TRUE];
};
traj: Traj => {
seq: Sequence;
seq ← FindSelectedSequence[traj, scene, selectClass];
IF seq = NIL THEN RETURN[FALSE];
RETURN[GGSequence.IsComplete[seq]];
};
ENDCASE => ERROR;
};
IsSelectedInPart:
PUBLIC
PROC [entity:
REF
ANY, scene: SceneRef, 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;
sliceD ← FindSelectedSlice[slice, scene, selectClass];
RETURN[sliceD # NIL];
};
outline: Outline => {
outlineD: OutlineDescriptor;
outlineD ← FindSelectedOutline[outline, scene, selectClass];
RETURN[outlineD # NIL]
};
traj: Traj => {
seq: Sequence;
seq ← FindSelectedSequence[traj, scene, selectClass];
RETURN[seq # NIL];
};
seq: Sequence => {
selSeq: Sequence;
selSeq ← FindSelectedSequence[seq.traj, 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: SceneRef, 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];
ENDCASE => ERROR;
};
SelectedStuff:
PUBLIC
PROC [scene: SceneRef, selectClass: SelectionClass]
RETURNS [selectedGen: EntityGenerator] = {
Preserves order.
selectedGen ←
NEW[EntityGeneratorObj ← [
list: ListSelected[scene, selectClass],
countValid: FALSE,
count: 0
]];
};
FindSelectedSequence:
PUBLIC
PROC [traj: Traj, scene: SceneRef, selectClass: SelectionClass]
RETURNS [seq: Sequence] = {
FOR l:
LIST
OF
REF
ANY ← ListSelected[scene, selectClass], l.rest
UNTIL l =
NIL
DO
IF
ISTYPE[l.first, OutlineDescriptor]
THEN {
outlineD: OutlineDescriptor ← NARROW[l.first, OutlineDescriptor];
seq ← GGOutline.FindTrajInDescriptor[outlineD, traj];
IF seq # NIL THEN RETURN[seq];
};
ENDLOOP;
RETURN[NIL];
};
FindSequenceInList:
PUBLIC
PROC [traj: Traj, selectedList:
LIST
OF
REF
ANY]
RETURNS [seq: Sequence] = {
FOR l:
LIST
OF
REF
ANY ← selectedList, l.rest
UNTIL l =
NIL
DO
IF
ISTYPE[l.first, OutlineDescriptor]
THEN {
outlineD: OutlineDescriptor ← NARROW[l.first, OutlineDescriptor];
seq ← GGOutline.FindTrajInDescriptor[outlineD, traj];
IF seq # NIL THEN RETURN[seq];
};
ENDLOOP;
RETURN[NIL];
};
SelectedSequences:
PUBLIC
PROC [scene: SceneRef, selectClass: SelectionClass]
RETURNS [seqGen: SequenceGenerator] = {
seq: Sequence;
ptr, seqList: LIST OF Sequence;
seqGen ← NEW[SequenceGeneratorObj];
[seqGen.list, ptr] ← GGUtility.StartSequenceList[];
FOR entityList:
LIST
OF
REF
ANY ← ListSelected[scene, selectClass], entityList.rest
UNTIL entityList =
NIL
DO
IF
ISTYPE[entityList.first, OutlineDescriptor]
THEN {
seqList ← GGOutline.SequencesOfOutline[NARROW[entityList.first]];
FOR list:
LIST
OF Sequence ← seqList, list.rest
UNTIL list =
NIL
DO
seq ← NARROW[list.first];
[seqGen.list, ptr] ← GGUtility.AddSequence[seq, seqGen.list, ptr];
ENDLOOP;
};
ENDLOOP;
};
SelectedOutlines:
PUBLIC
PROC [scene: SceneRef, selectClass: SelectionClass]
RETURNS [outDGen: GGModelTypes.OutlineDescriptorGenerator] = {
sliceD: OutlineDescriptor ← NIL;
ptr: LIST OF OutlineDescriptor ← NIL;
outDGen ← NEW[GGModelTypes.OutlineDescriptorGeneratorObj];
FOR entityList:
LIST
OF
REF
ANY ← ListSelected[scene, selectClass], entityList.rest
UNTIL entityList =
NIL
DO
IF
ISTYPE[entityList.first, OutlineDescriptor]
THEN {
sliceD ← NARROW[entityList.first];
[outDGen.list, ptr] ← AddOutlineDescriptor[sliceD, outDGen.list, ptr];
};
ENDLOOP;
};
FindSelectedSlice: PUBLIC PROC [slice: Slice, scene: SceneRef, selectClass: SelectionClass] RETURNS [sliceD: SliceDescriptor] = {
FOR l: LIST OF REF ANY ← ListSelected[scene, selectClass], l.rest UNTIL l = NIL DO
IF ISTYPE[l.first, SliceDescriptor] THEN {
sliceD ← NARROW[l.first];
IF sliceD.slice = slice THEN RETURN;
};
ENDLOOP;
RETURN[NIL];
};
FindSelectedOutline: PUBLIC PROC [outline: Outline, scene: SceneRef, selectClass: SelectionClass] RETURNS [outlineD: OutlineDescriptor] = {
FOR l: LIST OF REF ANY ← ListSelected[scene, selectClass], l.rest UNTIL l = NIL DO
WITH l.first SELECT FROM
sliceD: SliceDescriptor => NULL;
outlineD: OutlineDescriptor => {
IF outlineD.slice = outline THEN RETURN[outlineD];
};
ENDCASE => ERROR;
ENDLOOP;
RETURN[NIL];
};
FindSelectedOutline:
PUBLIC PROC [outline: Outline, scene: SceneRef, selectClass: SelectionClass]
RETURNS [outlineD: OutlineDescriptor] = {
SELECT selectClass
FROM
normal => outlineD ← IF outline.normalSelectedParts = NIL THEN NIL ELSE outline.normalSelectedParts;
hot => outlineD ← IF outline.hotSelectedParts = NIL THEN NIL ELSE outline.hotSelectedParts;
active => outlineD ← IF outline.activeSelectedParts = NIL THEN NIL ELSE outline.activeSelectedParts;
ENDCASE => ERROR;
};
FindSelectedSlice:
PUBLIC
PROC [slice: Slice, scene: SceneRef, selectClass: SelectionClass]
RETURNS [sliceD: SliceDescriptor] = {
SELECT selectClass
FROM
normal => sliceD ← IF slice.normalSelectedParts = NIL THEN NIL ELSE slice.normalSelectedParts;
hot => sliceD ← IF slice.hotSelectedParts = NIL THEN NIL ELSE slice.hotSelectedParts;
active => sliceD ← IF slice.activeSelectedParts = NIL THEN NIL ELSE slice.activeSelectedParts;
ENDCASE => ERROR;
};
SelectedSlices:
PUBLIC
PROC [scene: SceneRef, selectClass: SelectionClass]
RETURNS [sliceDescGen: SliceDescriptorGenerator] = {
sliceD: SliceDescriptor ← NIL;
ptr: LIST OF SliceDescriptor ← NIL;
sliceDescGen ← NEW[SliceDescriptorGeneratorObj];
FOR entityList:
LIST
OF
REF
ANY ← ListSelected[scene, selectClass], entityList.rest
UNTIL entityList =
NIL
DO
IF
ISTYPE[entityList.first, SliceDescriptor]
THEN {
sliceD ← NARROW[entityList.first];
[sliceDescGen.list, ptr] ← AddSliceDescriptor[sliceD, sliceDescGen.list, ptr];
};
ENDLOOP;
};
NextSliceDescriptor:
PUBLIC PROC [g: SliceDescriptorGenerator]
RETURNS [next: SliceDescriptor] = {
IF g.list = NIL THEN RETURN[NIL]
ELSE {
next ← g.list.first;
g.list ← g.list.rest;
};
};
NextOutlineDescriptor:
PUBLIC PROC [g: GGModelTypes.OutlineDescriptorGenerator]
RETURNS [next: OutlineDescriptor] = {
IF g.list = NIL THEN RETURN[NIL]
ELSE {
next ← g.list.first;
g.list ← g.list.rest;
};
};
AddSliceDescriptor:
PROC [entity: SliceDescriptor, entityList, ptr:
LIST
OF SliceDescriptor]
RETURNS [newList, newPtr:
LIST
OF SliceDescriptor] = {
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;
};
};
AddOutlineDescriptor:
PROC [entity: OutlineDescriptor, entityList, ptr:
LIST
OF OutlineDescriptor]
RETURNS [newList, newPtr:
LIST
OF OutlineDescriptor] = {
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;
};
nilSeqGen: SequenceGenerator ← NEW[SequenceGeneratorObj ← [list: NIL]];
SelectedOutlineSequences:
PUBLIC
PROC [scene: SceneRef, selectClass: SelectionClass]
RETURNS [outSeqGen: OutlineSequenceGenerator] = {
Returns a generator for all selected sequences, grouped by outline.
holeSeqs, ptr: LIST OF Sequence;
outSeqPtr: LIST OF OutlineSequence;
outlineSeq: OutlineSequence;
fenceSeq, seq: Sequence;
entityGen: GGModelTypes.EntityGenerator;
seqList: LIST OF Sequence;
newSeqGen: SequenceGenerator;
outSeqGen ← NEW[OutlineSequenceGeneratorObj];
[outSeqGen.list, outSeqPtr] ← StartOutlineSequenceList[];
entityGen ← SelectedStuff[scene, selectClass];
FOR entity:
REF
ANY ← GGScene.NextEntity[entityGen], GGScene.NextEntity[entityGen]
UNTIL entity =
NIL
DO
WITH entity
SELECT
FROM
sliceD: SliceDescriptor => NULL;
outlineD: OutlineDescriptor => {
fenceSeq ← NIL;
[holeSeqs, ptr] ← GGUtility.StartSequenceList[];
seqList ← GGOutline.SequencesOfOutline[outlineD];
seq ← seqList.first;
IF seq.traj.role = fence
OR seq.traj.role = open
THEN {
fenceSeq ← seq;
seqList ← seqList.rest;
};
FOR list:
LIST
OF Sequence ← seqList, list.rest
UNTIL list =
NIL
DO
[holeSeqs, ptr] ← GGUtility.AddSequence[list.first, holeSeqs, ptr];
ENDLOOP;
newSeqGen ← IF holeSeqs=NIL THEN nilSeqGen ELSE NEW[SequenceGeneratorObj ← [list: holeSeqs]];
outlineSeq ← NEW[OutlineSequenceObj ← [outline: outlineD.slice, fenceSeq: fenceSeq, holeSeqs: newSeqGen]];
[outSeqGen.list, outSeqPtr] ← AddOutlineSequence[outlineSeq, outSeqGen.list, outSeqPtr];
};
ENDCASE => ERROR;
ENDLOOP;
};
NextOutlineSequences:
PUBLIC PROC [outSeqGen: OutlineSequenceGenerator]
RETURNS [outSeq: OutlineSequence] = {
IF outSeqGen.list = NIL THEN RETURN[NIL];
outSeq ← outSeqGen.list.first;
outSeqGen.list ← outSeqGen.list.rest;
};
AddOutlineSequence:
PUBLIC
PROC [entity: OutlineSequence, entityList, ptr:
LIST
OF OutlineSequence]
RETURNS [newList, newPtr:
LIST
OF OutlineSequence] = {
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;
};
StartOutlineSequenceList:
PUBLIC
PROC []
RETURNS [entityList, ptr:
LIST
OF OutlineSequence] = {
ptr ← entityList ← NIL;
};
Modifying All Selected Runs With A Call-Back Proc
ForEachOutlineRun:
PUBLIC
PROC [scene: SceneRef, selectClass: SelectionClass, runProc: RunProc, segmentsOnly:
BOOL ←
TRUE, selectNewRuns:
BOOL ←
FALSE]
RETURNS [bBox: BoundBox] = {
outSeqGen: OutlineSequenceGenerator;
fenceSeq: Sequence;
thisBox: BoundBox;
bBox ← GGBoundBox.BoundBoxOfSelected[scene, normal];
DuplicateSelections[scene, normal, active]; -- all normal selected objects become active
outSeqGen ← SelectedOutlineSequences[scene, active];
IF outSeqGen.list # NIL THEN {
GGCaret.NoAttractor[caret: caret];
GGCaret.SitOn[caret, NIL];
};
WHILE outSeqGen.list #
NIL
DO
-- while any active objects remain.
FOR outSeq: GGSelect.OutlineSequence ← NextOutlineSequences[outSeqGen], NextOutlineSequences[outSeqGen]
UNTIL outSeq =
NIL
DO
fenceSeq ← outSeq.fenceSeq;
IF fenceSeq #
NIL
THEN {
thisBox ← GetRunAndReplace[fenceSeq, scene, runProc, segmentsOnly, selectNewRuns];
GGBoundBox.EnlargeByBox[bBox: bBox, by: thisBox];
GOTO SelectionsHaveChanged;
};
FOR holeSeq: Sequence ← GGSequence.NextSequence[outSeq.holeSeqs], GGSequence.NextSequence[outSeq.holeSeqs]
UNTIL holeSeq =
NIL
DO
thisBox ← GetRunAndReplace[holeSeq, scene, runProc, segmentsOnly, selectNewRuns];
GGBoundBox.EnlargeByBox[bBox: bBox, by: thisBox];
GOTO SelectionsHaveChanged;
ENDLOOP;
REPEAT
SelectionsHaveChanged => {
outSeqGen ← SelectedOutlineSequences[scene, active];
};
ENDLOOP;
ENDLOOP;
};
RunProc: TYPE = GGSelect.RunProc;
GetRunAndReplace:
PROC [seq: Sequence, scene: SceneRef, runProc: RunProc, segmentsOnly:
BOOL, selectNewRuns:
BOOL]
RETURNS [bBox: BoundBox] = {
This procedure is a utility for ForEachOutlineRun. We get the first run of seq and replace it with the results of runProc.
IMPORTANT INVARIANT: When exiting from this loop, make sure that some part of seq has been deselected from the active list. Otherwise, infinite loops are possible.
oldOutline: Outline;
runGen: SequenceGenerator;
runCount: NAT;
newRun: Traj;
newOutlines: LIST OF Outline;
run: Sequence;
oldOutline ← GGOutline.OutlineOfTraj[seq.traj];
GGOutline.SaveSelectionsInOutline[oldOutline, scene];
IF GGSequence.IsComplete[seq] THEN run ← seq
ELSE {
[runGen, runCount] ← GGSequence.RunsInSequence[seq];
IF runCount = 0
THEN {
DeselectSequence[seq, scene, active]; -- maintain the important invariant
bBox ← GGBoundBox.emptyBoundBox;
RETURN; -- The sequence has only control points.
};
run ← GGSequence.NextSequence[runGen];
IF run = NIL THEN ERROR;
};
Only call the runProc if there is a segment in the run
IF run.segCount>0
OR NOT segmentsOnly
THEN {
newRun ← runProc[run];
IF newRun #
NIL
THEN {
SetSelectionBits[newRun, FALSE, active]; -- don't process run twice
IF selectNewRuns THEN SetSelectionBits[newRun, TRUE, normal];
};
[newOutlines: newOutlines] ← ReplaceRun[run, newRun, scene];
bBox ← BoundBoxOfOutlines[newOutlines];
DeselectEntityAllClasses[oldOutline, scene];
FOR list:
LIST
OF Outline ← newOutlines, list.rest
UNTIL list =
NIL
DO
GGOutline.RemakeSelectionsFromOutline[list.first, scene];
ENDLOOP;
}
ELSE {
DeselectSequence[seq, scene, active]; -- maintain the important invariant
bBox ← GGBoundBox.emptyBoundBox;
};
};
ReplaceRun:
PROC [run: Sequence, newRun: Traj, scene: SceneRef]
RETURNS [newOutlines:
LIST
OF Outline, newTraj: Traj ← NIL] = {
Replaces a run with a new one. Returns a list of new outlines created. If only one traj was modified, the new traj is also returned.
IF newRun = NIL THEN [----, newOutlines] ← GGScene.DeleteSequence[run, scene]
ELSE {
newTraj ← GGTraj.SpliceIn[run, newRun];
newOutlines ← LIST[newTraj.parent];
GGScene.DeleteOutline[scene, GGOutline.OutlineOfTraj[run.traj]];
GGScene.AddOutline[scene, newTraj.parent, -1];
};
};
SubstituteForSegment:
PUBLIC
PROC [traj: Traj, segNum:
NAT, newRun: Traj, scene: SceneRef]
RETURNS [bBox: BoundBox, newTraj: Traj] = {
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 Outline;
replaceSeq: Sequence ← GGSequence.CreateFromSegment[traj, segNum];
GGOutline.SaveSelectionsInOutline[traj.parent, scene];
DeselectTraj[traj, scene, normal];
DeselectTraj[traj, scene, hot];
[newOutlines, newTraj] ← ReplaceRun[replaceSeq, newRun, scene];
FOR list:
LIST
OF Outline ← newOutlines, list.rest
UNTIL list =
NIL
DO
GGOutline.RemakeSelectionsFromOutline[list.first, scene];
ENDLOOP;
bBox ← BoundBoxOfOutlines[newOutlines];
};
SetSelectionBits:
PUBLIC
PROC [traj: Traj, selected:
BOOL, selectClass: SelectionClass] = {
segGen: GGModelTypes.SegmentGenerator;
jointGen: GGModelTypes.JointGenerator;
joint: Joint;
segGen ← GGSequence.SegmentsInTraj[traj];
FOR seg: Segment ← GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen]
UNTIL seg =
NIL
DO
SetSegmentField[seg, selected, selectClass];
FOR i:
NAT
IN [0..seg.class.controlPointCount[seg]-1]
DO
seg.class.controlPointFieldSet[seg, i, selected, selectClass];
ENDLOOP;
ENDLOOP;
jointGen ← GGSequence.JointsInTraj[traj];
FOR jointNum:
INT ← GGSequence.NextJoint[jointGen], GGSequence.NextJoint[jointGen]
UNTIL jointNum = -1
DO
joint ← GGTraj.FetchJoint[traj, jointNum];
SetJointField[joint, selected, selectClass];
ENDLOOP;
};
BoundBoxOfOutlines:
PROC [outlines:
LIST
OF Outline]
RETURNS [bBox: BoundBox] = {
bBox ← GGBoundBox.NullBoundBox[];
FOR list:
LIST
OF Outline ← outlines, list.rest
UNTIL list =
NIL
DO
GGBoundBox.EnlargeByBox[bBox: bBox, by: list.first.boundBox];
ENDLOOP;
};
SliceProc: TYPE = GGSelect.SliceProc;
OutlineProc: TYPE = GGSelect.OutlineProc;
DoForEachSelectedSlice:
PUBLIC
PROC [scene: SceneRef, selectClass: SelectionClass, sliceProc: SliceProc] = {
sliceDescGen: SliceDescriptorGenerator ← SelectedSlices[scene, selectClass];
FOR sliceD: SliceDescriptor ← NextSliceDescriptor[sliceDescGen], NextSliceDescriptor[sliceDescGen]
UNTIL sliceD =
NIL
DO
sliceProc[sliceD];
ENDLOOP;
};
DoForEachSelectedOutline:
PUBLIC
PROC [scene: SceneRef, selectClass: SelectionClass, outlineProc: OutlineProc] = {
outDGen: GGModelTypes.OutlineDescriptorGenerator ← SelectedOutlines[scene, selectClass];
FOR outlineD: OutlineDescriptor ← NextOutlineDescriptor[outDGen], NextOutlineDescriptor[outDGen]
UNTIL outlineD =
NIL
DO
outlineProc[outlineD];
ENDLOOP;
};
END.