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.