GGSelectImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Last edited by Bier on November 7, 1985 11:03:52 pm PST
Contents: Procedures dealing selecting and deselecting objects, including both selection actions and selection feedback.
DIRECTORY
GGError,
GGModelTypes,
GGObjects,
GGInterfaceTypes,
GGSelect,
GGUtility,
Rosary;
GGSelectImpl: CEDAR PROGRAM
IMPORTS GGError, GGObjects, GGUtility, Rosary
EXPORTS GGSelect =
BEGIN
Cluster: TYPE = GGModelTypes.Cluster;
EntityGenerator: TYPE = REF EntityGeneratorObj;
EntityGeneratorObj: TYPE = GGModelTypes.EntityGeneratorObj;
Joint: TYPE = GGModelTypes.Joint;
JointPair: TYPE = GGModelTypes.JointPair;
JointGenerator: TYPE = GGModelTypes.JointGenerator;
GargoyleData: TYPE = GGInterfaceTypes.GargoyleData;
Outline: TYPE = GGModelTypes.Outline;
Segment: TYPE = GGModelTypes.Segment;
SegmentGenerator: TYPE = GGModelTypes.SegmentGenerator;
SelectionClass: TYPE = GGInterfaceTypes.SelectionClass;
Sequence: TYPE = REF SequenceObj;
SequenceObj: TYPE = GGModelTypes.SequenceObj;
SequenceGenerator: TYPE = REF SequenceGeneratorObj;
SequenceGeneratorObj: TYPE = GGModelTypes.SequenceGeneratorObj;
Traj: TYPE = GGModelTypes.Traj;
TrajGenerator: TYPE = REF TrajGeneratorObj;
TrajGeneratorObj: TYPE = GGModelTypes.TrajGeneratorObj;
Implementation Notes: The Selection mechanism maintains two "structures" which must be kept consistent. The first is a list of selected entities: gargoyleData.selectedEntities. The second is the set of "selected" fields in each of the selected objects. The following types of entities can appear on gargoyleData.selectedEntities: clusters, outlines, trajectories, and sequences. A sequence can represent a single selected joint or a sequence of selected joints. No support for selected control points is provided yet. "selected" fields are found in clusters, outlines, trajectories, sequences, and joints.
The following invariants should be maintained:
1) If an entity is on the selected list, its selected field, and all of the selected fields of its components should be TRUE.
2) If an entity is on the selected list, none of its components should be on the selected list.
3) If a sequence is on the selected list, all of the selected fields of its joints should be TRUE.
4) If an entity is not on the selected list, and none of its ancestors are, its selected field should be FALSE.
5) No entity may be on the selection list twice.
6) If several sequences for the same trajectory are selected, they must not overlap.
These invariants will be maintained with the help of these procedures: IsOnList, AncestorOnList, ListComponentsOnList, SetComponentSelectedFields, ListOtherSequencesOnList
IsOnList: PROC [entity: REF ANY, gargoyleData: GargoyleData, selectClass: SelectionClass] RETURNS [BOOL] = {
RETURN[GGUtility.IsMember[entity, ListSelected[gargoyleData, selectClass]]];
};
AncestorOnList: PROC [entity: REF ANY, gargoyleData: GargoyleData, selectClass: SelectionClass] RETURNS [BOOL] = {
IF entity = NIL THEN RETURN[FALSE];
IF IsOnList[entity, gargoyleData, selectClass] THEN RETURN[TRUE];
WITH entity SELECT FROM
cluster: Cluster => RETURN[AncestorOnList[cluster.parent, gargoyleData, selectClass]];
outline: Outline => RETURN[AncestorOnList[outline.parent, gargoyleData, selectClass]];
traj: Traj => RETURN[AncestorOnList[traj.outline, gargoyleData, selectClass]];
seq: Sequence => RETURN[AncestorOnList[seq.traj, gargoyleData, selectClass]];
ENDCASE => ERROR;
};
FindTheSequence: PROC [traj: Traj, gargoyleData: GargoyleData, selectClass: SelectionClass] RETURNS [seq: Sequence] = {
FOR l: LIST OF REF ANY ← ListSelected[gargoyleData, selectClass], l.rest UNTIL l = NIL DO
IF ISTYPE[l.first, Sequence] THEN {
thisSeq: Sequence ← NARROW[l.first];
IF thisSeq.traj = traj THEN RETURN[thisSeq];
};
ENDLOOP;
RETURN[NIL];
};
GetAnyAncestor: PROC [entity: REF ANY, gargoyleData: GargoyleData, selectClass: SelectionClass] RETURNS [ancestor: REF ANY] = {
IF entity = NIL THEN ERROR;
IF IsOnList[entity, gargoyleData, selectClass] THEN RETURN[entity];
WITH entity SELECT FROM
cluster: Cluster => RETURN[GetAnyAncestor[cluster.parent, gargoyleData, selectClass]];
outline: Outline => RETURN[GetAnyAncestor[outline.parent, gargoyleData, selectClass]];
traj: Traj => RETURN[GetAnyAncestor[traj.outline, gargoyleData, selectClass]];
seq: Sequence => RETURN[GetAnyAncestor[seq.traj, gargoyleData, selectClass]];
ENDCASE => ERROR;
};
ListComponentsOnList: PROC [entity: REF ANY, gargoyleData: GargoyleData, selectClass: SelectionClass] RETURNS [components: LIST OF REF ANY] = {
components ← NIL;
WITH entity SELECT FROM
cluster: Cluster => {
entityGen: EntityGenerator;
entityGen ← GGObjects.EntitiesInCluster[cluster];
FOR child: REF ANY ← GGObjects.NextEntity[entityGen], GGObjects.NextEntity[entityGen] UNTIL child = NIL DO
IF IsOnList[child, gargoyleData, selectClass] THEN components ← CONS[child, components]
ELSE components ← GGUtility.AppendList[ListComponentsOnList[child, gargoyleData, selectClass], components];
ENDLOOP;
};
outline: Outline => {
trajGen: TrajGenerator;
thisSeq: Sequence;
trajGen ← GGObjects.TrajsInOutline[outline];
FOR child: Traj ← GGObjects.NextTraj[trajGen], GGObjects.NextTraj[trajGen] UNTIL child = NIL DO
thisSeq ← FindTheSequence[child, gargoyleData, selectClass];
IF thisSeq # NIL THEN components ← CONS[thisSeq, components];
ENDLOOP;
};
seq: Sequence => ERROR; -- sequences cannot be divided further
ENDCASE => ERROR;
};
SetComponentSelectedFields: PROC [entity: REF ANY, selected: BOOL, gargoyleData: GargoyleData, selectClass: SelectionClass] = {
WITH entity SELECT FROM
cluster: Cluster => {
entityGen: EntityGenerator;
SetClusterField[cluster, selected, selectClass];
entityGen ← GGObjects.EntitiesInCluster[cluster];
FOR child: REF ANY ← GGObjects.NextEntity[entityGen], GGObjects.NextEntity[entityGen] UNTIL child = NIL DO
SetComponentSelectedFields[child, selected, gargoyleData, selectClass];
ENDLOOP;
};
outline: Outline => {
trajGen: TrajGenerator;
SetOutlineField[outline, selected, selectClass];
trajGen ← GGObjects.TrajsInOutline[outline];
FOR child: Traj ← GGObjects.NextTraj[trajGen], GGObjects.NextTraj[trajGen] UNTIL child = NIL DO
joint: Joint;
seg: Segment;
FOR i: NAT IN [0..GGObjects.HiJoint[child]] DO
joint ← NARROW[Rosary.Fetch[child.joints, i]];
SetJointField[joint, selected, selectClass];
ENDLOOP;
FOR i: NAT IN [0..GGObjects.HiSegment[child]] DO
seg ← NARROW[Rosary.Fetch[child.segments, i]];
SetSegmentField[seg, selected, selectClass];
ENDLOOP;
ENDLOOP;
};
seq: Sequence => {
joint: Joint;
jointGen: JointGenerator;
segGen: SegmentGenerator;
SetSequenceField[seq, selected, selectClass]; -- this field is pretty useless.
jointGen ← GGObjects.JointsInSequence[seq];
FOR i: INT ← GGObjects.NextJoint[jointGen], GGObjects.NextJoint[jointGen] UNTIL i = -1 DO
joint ← NARROW[Rosary.Fetch[seq.traj.joints, i]];
SetJointField[joint, selected, selectClass];
ENDLOOP;
segGen ← GGObjects.SegmentsInSequence[seq];
FOR seg: Segment ← GGObjects.NextSegment[segGen], GGObjects.NextSegment[segGen] UNTIL seg = NIL DO
SetSegmentField[seg, selected, selectClass];
ENDLOOP;
};
ENDCASE => ERROR;
};
NotYetImplemented: PUBLIC SIGNAL = CODE;
PUBLIC Procedures
SelectCluster: PUBLIC PROC [cluster: Cluster, gargoyleData: GargoyleData, selectClass: SelectionClass] = {
ERROR NotYetImplemented;
};
SelectOutline: PUBLIC PROC [outline: Outline, gargoyleData: GargoyleData, selectClass: SelectionClass] = {
outline is now selected. This overrides any selections on outline's parts.
components: LIST OF REF ANY;
IF AncestorOnList[outline, gargoyleData, selectClass] THEN {
GGError.Append["Outline already selected. Ignored.",oneLiner];
GGError.Blink[];
RETURN; -- Do nothing
};
Check to see if any of the outline's parts are selected.
components ← ListComponentsOnList[outline, gargoyleData, selectClass];
IF components # NIL THEN {
FOR seqList: LIST OF REF ANY ← components, seqList.rest UNTIL seqList = NIL DO
DeselectEntity[seqList.first, gargoyleData, selectClass];
ENDLOOP;
};
AppendSelection[gargoyleData, outline, selectClass];
SetComponentSelectedFields[outline, TRUE, gargoyleData, selectClass];
};
SelectTraj: PUBLIC PROC [traj: Traj, gargoyleData: GargoyleData, selectClass: SelectionClass] = {
traj is now selected. This overrides any selections on traj's parts.
oldSeq, wholeSeq: Sequence;
oldSeq ← FindTheSequence[traj, gargoyleData, selectClass];
Check to see if any of the trajectory's parts are selected.
IF oldSeq # NIL THEN {
DeselectSequence[oldSeq, gargoyleData, selectClass];
};
IF AncestorOnList[traj, gargoyleData, selectClass] THEN {
GGError.Append["Trajectory already selected. Ignored.", oneLiner];
RETURN; -- Do nothing
};
wholeSeq ← GGObjects.CreateCompleteSequence[traj];
AppendSelection[gargoyleData, wholeSeq, selectClass];
SetComponentSelectedFields[wholeSeq, TRUE, gargoyleData, selectClass];
};
EntireTrajectory: PROC [seq: Sequence] RETURNS [BOOL] = {
segGen: SegmentGenerator ← GGObjects.SegmentsInSequence[seq];
RETURN[segGen.toGo = seq.traj.segCount];
};
SelectSequence: PUBLIC PROC [seq: Sequence, gargoyleData: GargoyleData, selectClass: SelectionClass] RETURNS [union, newMinusOld: Sequence] = {
jointGen: JointGenerator;
segGen: SegmentGenerator;
joint: Joint;
oldSeq: Sequence;
oldSeq ← FindTheSequence[seq.traj, gargoyleData, selectClass];
IF oldSeq # NIL THEN {
[union, newMinusOld] ← GGObjects.CombineSequences[seq, oldSeq];
IF EntireTrajectory[union] THEN union.all ← TRUE;
DeselectSequence[oldSeq, gargoyleData, selectClass];
AppendSelection[gargoyleData, union, selectClass];
jointGen ← GGObjects.JointsInSequence[union];
segGen ← GGObjects.SegmentsInSequence[union];
}
ELSE {
IF EntireTrajectory[seq] THEN seq.all ← TRUE;
AppendSelection[gargoyleData, seq, selectClass];
union ← seq;
newMinusOld ← seq;
jointGen ← GGObjects.JointsInSequence[seq];
segGen ← GGObjects.SegmentsInSequence[seq];
};
FOR jointNum: INT ← GGObjects.NextJoint[jointGen], GGObjects.NextJoint[jointGen] UNTIL jointNum = -1 DO
joint ← NARROW[Rosary.Fetch[seq.traj.joints, jointNum]];
SetJointField[joint, TRUE, selectClass];
ENDLOOP;
FOR seg: Segment ← GGObjects.NextSegment[segGen], GGObjects.NextSegment[segGen] UNTIL seg = NIL DO
SetSegmentField[seg, TRUE, selectClass];
ENDLOOP;
};
SelectEntity: PUBLIC PROC [entity: REF ANY, gargoyleData: GargoyleData, selectClass: SelectionClass] = {
WITH entity SELECT FROM
cluster: Cluster => SelectCluster[cluster, gargoyleData, selectClass];
outline: Outline => SelectOutline[outline, gargoyleData, selectClass];
traj: Traj => SelectTraj[traj, gargoyleData, selectClass];
seq: Sequence => [----,----] ← SelectSequence[seq, gargoyleData, selectClass];
ENDCASE => ERROR;
};
SelectAll: PUBLIC PROC [gargoyleData: GargoyleData, selectClass: SelectionClass] = {
entityGen: EntityGenerator;
entityGen ← GGObjects.TopLevelEntities[gargoyleData.scene];
FOR entity: REF ANY ← GGObjects.NextEntity[entityGen], GGObjects.NextEntity[entityGen] UNTIL entity = NIL DO
SelectEntity[entity, gargoyleData, selectClass];
ENDLOOP;
};
DeleteSelection: PRIVATE PROC [gargoyleData: GargoyleData, entity: REF ANY, selectClass: SelectionClass] = {
SetSelectedList[gargoyleData,
GGUtility.DeleteEntityFromList[entity, ListSelected[gargoyleData, selectClass]],
selectClass];
};
DeselectCluster: PUBLIC PROC [cluster: Cluster, gargoyleData: GargoyleData, selectClass: SelectionClass] = {
IF NOT IsSelected[cluster, gargoyleData, selectClass] THEN RETURN;
ERROR NotYetImplemented;
};
DeselectOutline: PUBLIC PROC [outline: Outline, gargoyleData: GargoyleData, selectClass: SelectionClass] = {
Outline is now deselected. If any ancestors of outline are selected, they are not (selected in entirety) anymore. For now, we just check that this isn't true. This procedure should also be updated to work when outline is not selected in entirety.
components: LIST OF REF ANY;
IF AncestorOnList[outline.parent, gargoyleData, selectClass] THEN {
GGError.Append["Deselecting Parts of clusters not yet implemented.", oneLiner];
GGError.Blink[];
ERROR;
};
IF IsSelected[outline, gargoyleData, selectClass] THEN {
DeleteSelection[gargoyleData, outline, selectClass];
SetComponentSelectedFields[outline, FALSE, gargoyleData, selectClass];
}
ELSE { -- make sure that all descendants of outline are removed from the selected list and have their component fields cleared
components ← ListComponentsOnList[outline, gargoyleData, selectClass];
IF components # NIL THEN {
FOR seqList: LIST OF REF ANY ← components, seqList.rest UNTIL seqList = NIL DO
DeselectEntity[seqList.first, gargoyleData, selectClass];
ENDLOOP;
};
};
};
DeselectTraj: PUBLIC PROC [traj: Traj, gargoyleData: GargoyleData, selectClass: SelectionClass] = {
traj is now deselected. If any ancestors of traj are selected, they are not (selected in entirety) anymore. For now, we just check that this isn't true. This procedure should also be updated to work when traj is not selected in entirety.
IF NOT IsSelected[traj, selectClass] THEN RETURN;
IF AncestorOnList[traj.outline, gargoyleData, selectClass] THEN {
GGError.Append["Deselecting Parts of outlines not yet implemented.", oneLiner];
GGError.Blink[];
ERROR;
};
DeleteSelection[gargoyleData, traj, selectClass];
SetComponentSelectedFields[traj, FALSE, gargoyleData, selectClass];
};
DeselectSequence: PUBLIC PROC [seq: Sequence, gargoyleData: GargoyleData, selectClass: SelectionClass] = {
jointGen: JointGenerator;
joint: Joint;
oldSeq, oldMinusNew: Sequence;
oldSeq ← FindTheSequence[seq.traj, gargoyleData, selectClass];
IF oldSeq # NIL THEN {
[----, oldMinusNew] ← GGObjects.CombineSequences[oldSeq, seq];
DeleteSelection[gargoyleData, oldSeq, selectClass];
IF oldMinusNew.parts # NIL THEN
AppendSelection[gargoyleData, oldMinusNew, selectClass];
}
ELSE RETURN; -- it wasn't selected in the first place
FOR jointNum: NAT IN [0..GGObjects.HiJoint[seq.traj]] DO
joint ← NARROW[Rosary.Fetch[seq.traj.joints, jointNum]];
SetJointField[joint, FALSE, selectClass];
ENDLOOP;
jointGen ← GGObjects.JointsInSequence[oldMinusNew];
FOR jointNum: INT ← GGObjects.NextJoint[jointGen], GGObjects.NextJoint[jointGen] UNTIL jointNum = -1 DO
joint ← NARROW[Rosary.Fetch[seq.traj.joints, jointNum]];
SetJointField[joint, TRUE, selectClass];
ENDLOOP;
};
DeselectEntity: PUBLIC PROC [entity: REF ANY, gargoyleData: GargoyleData, selectClass: SelectionClass] = {
WITH entity SELECT FROM
cluster: Cluster => DeselectCluster[cluster, gargoyleData, selectClass];
outline: Outline => DeselectOutline[outline, gargoyleData, selectClass];
traj: Traj => DeselectTraj[traj, gargoyleData, selectClass];
seq: Sequence => DeselectSequence[seq, gargoyleData, selectClass];
ENDCASE => ERROR;
};
DeselectAll: PUBLIC PROC [gargoyleData: GargoyleData, selectClass: SelectionClass] = {
Clear all flags that indicate the selection of each object.
FOR selected: LIST OF REF ANY ← ListSelected[gargoyleData, selectClass], selected.rest UNTIL selected = NIL DO
SetComponentSelectedFields[selected.first, FALSE, gargoyleData, selectClass];
ENDLOOP;
SetSelectedList[gargoyleData, NIL, selectClass];
};
ListSelected: PUBLIC PROC [gargoyleData: GargoyleData, selectClass: SelectionClass] RETURNS [selectedList: LIST OF REF ANY] = {
SELECT selectClass FROM
normal => selectedList ← gargoyleData.selected.normal;
copy => selectedList ← gargoyleData.selected.copy;
hot => selectedList ← gargoyleData.selected.hot;
active => selectedList ← gargoyleData.selected.active;
ENDCASE => ERROR;
};
SelectedTrajs: PUBLIC PROC [gargoyleData: GargoyleData, selectClass: SelectionClass] RETURNS [trajGen: TrajGenerator] = {
This is rather inefficient for large scenes.
allGen: TrajGenerator ← GGObjects.TrajsInScene[gargoyleData.scene];
trajGen ← NEW[TrajGeneratorObj];
trajGen.list ← NIL;
FOR traj: Traj ← GGObjects.NextTraj[allGen], GGObjects.NextTraj[allGen] UNTIL traj = NIL DO
IF IsSelected[traj, selectClass] THEN trajGen.list ← CONS[traj, trajGen.list];
ENDLOOP;
};
ListSelectedSequences: PUBLIC PROC [gargoyleData: GargoyleData, 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[gargoyleData, 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:
SetClusterField: PROC [cluster: Cluster, selected: BOOL, selectClass: SelectionClass] = {
SELECT selectClass FROM
normal => cluster.selected.normal ← selected;
copy => cluster.selected.copy ← selected;
hot => cluster.selected.hot ← selected;
active => cluster.selected.active ← selected;
ENDCASE;
};
SetOutlineField: PROC [outline: Outline, selected: BOOL, selectClass: SelectionClass] = {
SELECT selectClass FROM
normal => outline.selected.normal ← selected;
copy => outline.selected.copy ← selected;
hot => outline.selected.hot ← selected;
active => outline.selected.active ← selected;
ENDCASE;
};
SetTrajField: PROC [traj: Traj, selected: BOOL, selectClass: SelectionClass] = {
SELECT selectClass FROM
normal => traj.selected.normal ← selected;
copy => traj.selected.copy ← selected;
hot => traj.selected.hot ← selected;
active => traj.selected.active ← selected;
ENDCASE;
};
SetSequenceField: PROC [seq: Sequence, selected: BOOL, selectClass: SelectionClass] = {
SELECT selectClass FROM
normal => seq.selected.normal ← selected;
copy => seq.selected.copy ← selected;
hot => seq.selected.hot ← selected;
active => seq.selected.active ← selected;
ENDCASE;
};
SetJointField: PROC [joint: Joint, selected: BOOL, selectClass: SelectionClass] = {
SELECT selectClass FROM
normal => joint.selected.normal ← selected;
copy => joint.selected.copy ← selected;
hot => joint.selected.hot ← selected;
active => joint.selected.active ← selected;
ENDCASE;
};
SetSegmentField: PROC [seg: Segment, selected: BOOL, selectClass: SelectionClass] = {
SELECT selectClass FROM
normal => seg.selected.normal ← selected;
copy => seg.selected.copy ← selected;
hot => seg.selected.hot ← selected;
active => seg.selected.active ← selected;
ENDCASE;
};
AppendSelection: PROC [gargoyleData: GargoyleData, entity: REF ANY, selectClass: SelectionClass] = {
SELECT selectClass FROM
normal => gargoyleData.selected.normal ← CONS[entity, gargoyleData.selected.normal];
copy => gargoyleData.selected.copy ← CONS[entity, gargoyleData.selected.copy];
hot => gargoyleData.selected.hot ← CONS[entity, gargoyleData.selected.hot];
active => gargoyleData.selected.active ← CONS[entity, gargoyleData.selected.active];
ENDCASE => ERROR;
};
SetSelectedList: PROC [gargoyleData: GargoyleData, value: LIST OF REF ANY, selectClass: SelectionClass] = {
SELECT selectClass FROM
normal => gargoyleData.selected.normal ← value;
copy => gargoyleData.selected.copy ← value;
hot => gargoyleData.selected.hot ← value;
active => gargoyleData.selected.active ← value;
ENDCASE => ERROR;
};
IsSelected: PUBLIC PROC [entity: REF ANY, gargoyleData: GargoyleData, selectClass: SelectionClass] RETURNS [BOOL] = {
WITH entity SELECT FROM
cluster: Cluster => {
SELECT selectClass FROM
normal => RETURN[cluster.selected.normal];
copy => RETURN[cluster.selected.copy];
hot => RETURN[cluster.selected.hot];
active => RETURN[cluster.selected.active];
ENDCASE => ERROR;
};
outline: Outline => {
SELECT selectClass FROM
normal => RETURN[outline.selected.normal];
copy => RETURN[outline.selected.copy];
hot => RETURN[outline.selected.hot];
active => RETURN[outline.selected.active];
ENDCASE => ERROR;
};
traj: Traj => {
A trajectory is selected if either: 1) A sequence representing it appears on the selected list, or 2) Its parent outline is selected. Selected bits for trajectories have been abandoned for now.
seq: Sequence ← FindSelectedSequence[traj, gargoyleData, selectClass];
IF seq = NIL THEN {
parent: Outline ← GGObjects.OutlineOfTraj[traj];
RETURN[IsSelected[parent, gargoyleData, selectClass]];
};
RETURN[seq.all];
};
seq: Sequence => {
SELECT selectClass FROM
normal => RETURN[seq.selected.normal];
copy => RETURN[seq.selected.copy];
hot => RETURN[seq.selected.hot];
active => RETURN[seq.selected.active];
ENDCASE => ERROR;
};
joint: Joint => {
SELECT selectClass FROM
normal => RETURN[joint.selected.normal];
copy => RETURN[joint.selected.copy];
hot => RETURN[joint.selected.hot];
active => RETURN[joint.selected.active];
ENDCASE => ERROR;
};
seg: Segment => {
SELECT selectClass FROM
normal => RETURN[seg.selected.normal];
copy => RETURN[seg.selected.copy];
hot => RETURN[seg.selected.hot];
active => RETURN[seg.selected.active];
ENDCASE => ERROR;
};
ENDCASE => ERROR;
};
IsSelectedInPart: PUBLIC PROC [entity: REF ANY, gargoyleData: GargoyleData, selectClass: SelectionClass] RETURNS [BOOL] = {
Returns TRUE if all or part of entity is selected. These are the cases:
1) entity is a cluster. 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 (not yet implemented).
WITH entity SELECT FROM
cluster: Cluster => {
IF IsSelected[cluster, gargoyleData, selectClass] THEN RETURN[TRUE];
Otherwise, search its children.
ERROR NotYetImplemented;
};
outline: Outline => {
trajGen: TrajGenerator;
IF IsSelected[outline, gargoyleData, selectClass] THEN RETURN[TRUE];
trajGen ← GGObjects.TrajsInOutline[outline];
FOR traj: Traj ← GGObjects.NextTraj[trajGen], GGObjects.NextTraj[trajGen] UNTIL traj = NIL DO
IF IsSelectedInPart[traj, gargoyleData, selectClass] THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE];
};
traj: Traj => {
seq: Sequence;
seq ← FindSelectedSequence[traj, gargoyleData, selectClass];
RETURN[seq # NIL];
};
seq: Sequence => {
selSeq: Sequence;
selSeq ← FindSelectedSequence[seq.traj, gargoyleData, selectClass];
IF selSeq = seq THEN RETURN[TRUE];
IF selSeq = NIL THEN RETURN[FALSE];
RETURN[GGObjects.SequencesOverlap[seq, selSeq]];
};
joint: Joint => {
RETURN[IsSelected[joint, gargoyleData, selectClass]];
};
seg: Segment => {
RETURN[IsSelected[seg, gargoyleData, selectClass]];
};
ENDCASE => ERROR;
};
NoSelections: PUBLIC PROC [gargoyleData: GargoyleData, selectClass: SelectionClass] RETURNS [BOOL] = {
SELECT selectClass FROM
normal => RETURN[gargoyleData.selected.normal = NIL];
copy => RETURN[gargoyleData.selected.copy = NIL];
hot => RETURN[gargoyleData.selected.hot = NIL];
active => RETURN[gargoyleData.selected.active = NIL];
ENDCASE => ERROR;
};
ListSelectedDescendants: PUBLIC PROC [entity: REF ANY, gargoyleData: GargoyleData, selectClass: SelectionClass] RETURNS [components: LIST OF REF ANY] = {
components ← NIL;
WITH entity SELECT FROM
cluster: Cluster => {
entityGen: EntityGenerator;
entityGen ← GGObjects.EntitiesInCluster[cluster];
FOR child: REF ANY ← GGObjects.NextEntity[entityGen], GGObjects.NextEntity[entityGen] UNTIL child = NIL DO
IF IsSelected[child, gargoyleData, selectClass] THEN components ← CONS[child, components]
ELSE components ← GGUtility.AppendList[ListComponentsOnList[child, gargoyleData, selectClass], components];
ENDLOOP;
};
outline: Outline => {
trajGen: TrajGenerator;
trajGen ← GGObjects.TrajsInOutline[outline];
FOR child: Traj ← GGObjects.NextTraj[trajGen], GGObjects.NextTraj[trajGen] UNTIL child = NIL DO
IF IsSelected[child, gargoyleData, selectClass] THEN components ← CONS[child, components]
ELSE components ← GGUtility.AppendList[ListComponentsOnList[child, gargoyleData, selectClass], components];
ENDLOOP;
};
traj: Traj => {
FOR guess: LIST OF REF ANY ← ListSelected[gargoyleData, selectClass], guess.rest UNTIL guess = NIL DO
IF ISTYPE[guess.first, Sequence] THEN {
seq: Sequence ← NARROW[guess.first];
IF seq.traj = traj THEN components ← CONS[seq, components];
};
ENDLOOP;
};
seq: Sequence => ERROR NotYetImplemented;
ENDCASE => ERROR;
};
FindSelectedSequence: PUBLIC PROC [traj: Traj, gargoyleData: GargoyleData, selectClass: SelectionClass] RETURNS [seq: Sequence] = {
seq ← NIL;
FOR guess: LIST OF REF ANY ← ListSelected[gargoyleData, selectClass], guess.rest UNTIL guess = NIL DO
IF ISTYPE[guess.first, Sequence] THEN {
seq ← NARROW[guess.first];
IF seq.traj = traj THEN RETURN[seq];
};
ENDLOOP;
RETURN[NIL];
};
SelectedEntities: PUBLIC PROC [gargoyleData: GargoyleData, selectClass: SelectionClass] RETURNS [selectedGen: EntityGenerator] = {
selectedGen ← NEW[EntityGeneratorObj];
selectedGen.list ← ListSelected[gargoyleData, selectClass];
};
SelectedSequences: PUBLIC PROC [gargoyleData: GargoyleData, selectClass: SelectionClass] RETURNS [seqGen: SequenceGenerator] = {
This only finds partially selected trajectories.
seq: Sequence;
seqGen ← NEW[SequenceGeneratorObj];
seqGen.list ← NIL;
FOR entityList: LIST OF REF ANY ← ListSelected[gargoyleData, selectClass], entityList.rest UNTIL entityList = NIL DO
IF ISTYPE[entityList.first, Sequence] THEN
seq ← NARROW[entityList.first];
seqGen.list ← CONS[seq, seqGen.list];
ENDLOOP;
};
END.