DIRECTORY GGBasicTypes, GGBoundBox, GGInterfaceTypes, GGModelTypes, GGOutline, GGScene, GGSceneType, GGSegmentTypes, GGSelect, GGSequence, GGSlice, GGTraj, GGUtility; GGSelectImpl: CEDAR PROGRAM IMPORTS GGBoundBox, GGOutline, GGScene, GGSelect, GGSequence, GGSlice, GGTraj, GGUtility 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; OutlineDescriptorGenerator: TYPE = GGModelTypes.OutlineDescriptorGenerator; 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; 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; }; NoDonkeyTail: PROC [slice: Slice, 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] = { sliceGen: SliceGenerator _ GGScene.TopLevelSlicesInScene[scene]; FOR slice: Slice _ GGScene.NextSlice[sliceGen], GGScene.NextSlice[sliceGen] UNTIL slice = NIL DO SelectEntireSlice[slice, scene, selectClass]; ENDLOOP; }; SelectSlice: PUBLIC PROC [sliceD: SliceDescriptor, scene: SceneRef, selectClass: SelectionClass] = { oldSliceD, union: SliceDescriptor; slice: Slice _ sliceD.slice; IF slice.class.isEmptyParts[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 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 _ GGSlice.DescriptorFromParts[outline, parts]; SelectSlice[seqD, scene, selectClass]; }; SelectTraj: PUBLIC PROC [traj: Traj, scene: SceneRef, selectClass: SelectionClass] = { 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] = { FOR sliceD: LIST OF SliceDescriptor _ ListSelected[scene, selectClass], sliceD.rest UNTIL sliceD = NIL DO NoDonkeyTail[sliceD.first.slice, selectClass]; 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]; 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] = { sliceD: SliceDescriptor _ FindSelectedSlice[slice, scene, selectClass]; IF sliceD # NIL THEN { DeleteSelection[scene, sliceD, selectClass]; NoDonkeyTail[sliceD.slice, 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.isEmptyParts[newD] THEN { IF selectClass = normal THEN newD _ slice.class.augmentParts[newD, selectClass]; AppendSelection[scene, newD, selectClass]; ChangeDonkeyTail[newD, selectClass]; }; }; }; DeselectSequence: PUBLIC PROC [seq: Sequence, scene: SceneRef, selectClass: SelectionClass] = { outline: Outline _ GGOutline.OutlineOfTraj[seq.traj]; seqParts: SliceParts _ GGOutline.PartsFromSequence[outline, seq]; DeselectSlice[outline, seqParts, scene, selectClass]; }; DeselectTraj: PUBLIC PROC [traj: Traj, scene: SceneRef, selectClass: SelectionClass] = { outline: Outline _ GGOutline.OutlineOfTraj[traj]; oldD, newD: OutlineDescriptor; oldD _ FindSelectedSlice[outline, scene, selectClass]; IF oldD # NIL THEN { newD _ GGOutline.RemoveTraj[oldD, traj]; DeleteSelection[scene, oldD, selectClass]; NoDonkeyTail[outline, selectClass]; IF NOT outline.class.isEmptyParts[newD] THEN { AppendSelection[scene, newD, selectClass]; ChangeDonkeyTail[newD, selectClass]; }; }; }; ReselectTraj: PUBLIC PROC [traj: Traj, trajEnd: TrajEnd, scene: SceneRef, extend: BOOL] RETURNS [newHot: Sequence] = { selSeq, newSeq: Sequence; selSeq _ FindSelectedSequence[traj, scene, normal]; IF selSeq # NIL THEN { newSeq _ GGSequence.Augment[selSeq, trajEnd, extend]; DeselectTraj[selSeq.traj, scene, normal]; SelectSequence[newSeq, scene, normal]; }; 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]; }; selSeq _ FindSelectedSequence[traj, scene, active]; IF selSeq # NIL THEN { ERROR; -- I don't think ReselectTraj should be used for active }; }; DeleteSelection: PROC [scene: SceneRef, entity: SliceDescriptor, selectClass: SelectionClass] = { SetSelectedList[scene, DRemove[entity, ListSelected[scene, selectClass]], selectClass]; }; ListSelected: PUBLIC PROC [scene: SceneRef, 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; ENDCASE => ERROR; }; 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: SliceDescriptor, selectClass: SelectionClass] = { SELECT selectClass FROM normal => scene.selected.normal _ Nconc[scene.selected.normal, LIST[entity]]; hot => scene.selected.hot _ Nconc[scene.selected.hot, LIST[entity]]; active => scene.selected.active _ Nconc[scene.selected.active, LIST[entity]]; ENDCASE => ERROR; }; SetSelectedList: PROC [scene: SceneRef, value: LIST OF SliceDescriptor, selectClass: SelectionClass] = { SELECT selectClass FROM normal => scene.selected.normal _ value; hot => scene.selected.hot _ value; active => scene.selected.active _ value; ENDCASE => ERROR; }; IsSelectedInFull: PUBLIC PROC [slice: Slice, scene: SceneRef, selectClass: SelectionClass] RETURNS [BOOL] = { donkeyTail: SliceDescriptor _ SELECT selectClass FROM active => slice.activeSelectedParts, hot => slice.hotSelectedParts, normal => slice.normalSelectedParts, ENDCASE => ERROR; IF donkeyTail = NIL THEN RETURN[FALSE]; RETURN[slice.class.isCompleteParts[donkeyTail]]; }; IsSelectedInPart: PUBLIC PROC [entity: REF ANY, scene: SceneRef, selectClass: SelectionClass] RETURNS [BOOL] = { WITH entity SELECT FROM slice: Slice => { sliceD: SliceDescriptor; sliceD _ FindSelectedSlice[slice, scene, selectClass]; RETURN[sliceD # 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; }; FindSelectedSequence: PUBLIC PROC [traj: Traj, scene: SceneRef, selectClass: SelectionClass] RETURNS [seq: Sequence] = { FOR sliceDList: LIST OF SliceDescriptor _ ListSelected[scene, selectClass], sliceDList.rest UNTIL sliceDList = NIL DO sliceD: SliceDescriptor _ sliceDList.first; seq _ IF sliceD.slice.class.type=$Outline THEN GGOutline.FindTrajInDescriptor[sliceD, traj] ELSE NIL; IF seq # NIL THEN RETURN[seq]; ENDLOOP; RETURN[NIL]; }; FindSequenceInList: PUBLIC PROC [traj: Traj, selectedList: LIST OF SliceDescriptor] RETURNS [seq: Sequence] = { FOR sliceDList: LIST OF SliceDescriptor _ selectedList, sliceDList.rest UNTIL sliceDList = NIL DO sliceD: SliceDescriptor _ sliceDList.first; seq _ IF sliceD.slice.class.type=$Outline THEN GGOutline.FindTrajInDescriptor[sliceD, traj] ELSE NIL; IF seq # NIL THEN RETURN[seq]; ENDLOOP; RETURN[NIL]; }; SelectedSequences: PUBLIC PROC [scene: SceneRef, selectClass: SelectionClass] RETURNS [seqGen: SequenceGenerator] = { ptr, seqList: LIST OF Sequence; seqGen _ NEW[SequenceGeneratorObj]; [seqGen.list, ptr] _ GGUtility.StartSequenceList[]; FOR sliceDList: LIST OF SliceDescriptor _ ListSelected[scene, selectClass], sliceDList.rest UNTIL sliceDList = NIL DO IF sliceDList.first.slice.class.type=$Outline THEN { seqList _ GGOutline.SequencesOfOutline[sliceDList.first]; FOR list: LIST OF Sequence _ seqList, list.rest UNTIL list = NIL DO [seqGen.list, ptr] _ GGUtility.AddSequence[list.first, seqGen.list, ptr]; ENDLOOP; }; ENDLOOP; }; SelectedOutlines: PUBLIC PROC [scene: SceneRef, selectClass: SelectionClass] RETURNS [outDGen: OutlineDescriptorGenerator] = { sliceD: OutlineDescriptor _ NIL; ptr: LIST OF OutlineDescriptor _ NIL; outDGen _ NEW[GGModelTypes.OutlineDescriptorGeneratorObj]; FOR sliceDList: LIST OF SliceDescriptor _ ListSelected[scene, selectClass], sliceDList.rest UNTIL sliceDList = NIL DO IF sliceDList.first.slice.class.type=$Outline THEN { [outDGen.list, ptr] _ AddSliceDescriptor[sliceDList.first, outDGen.list, ptr]; }; ENDLOOP; }; FindSelectedSlice: PUBLIC PROC [slice: Slice, scene: SceneRef, selectClass: SelectionClass] RETURNS [sliceD: SliceDescriptor] = { SELECT selectClass FROM normal => sliceD _ slice.normalSelectedParts; hot => sliceD _ slice.hotSelectedParts; active => sliceD _ slice.activeSelectedParts; ENDCASE => ERROR; }; SelectedSlices: PUBLIC PROC [scene: SceneRef, selectClass: SelectionClass] RETURNS [sliceDescGen: SliceDescriptorGenerator] = { ptr: LIST OF SliceDescriptor _ NIL; sliceDescGen _ NEW[SliceDescriptorGeneratorObj]; FOR sliceDList: LIST OF SliceDescriptor _ ListSelected[scene, selectClass], sliceDList.rest UNTIL sliceDList = NIL DO [sliceDescGen.list, ptr] _ AddSliceDescriptor[sliceDList.first, 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; }; }; 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; }; }; nilSeqGen: SequenceGenerator _ NEW[SequenceGeneratorObj _ [list: NIL]]; SelectedOutlineSequences: PUBLIC PROC [scene: SceneRef, selectClass: SelectionClass] RETURNS [outSeqGen: OutlineSequenceGenerator] = { holeSeqs, ptr: LIST OF Sequence; outSeqPtr: LIST OF OutlineSequence; outlineSeq: OutlineSequence; fenceSeq, seq: Sequence; sliceDescGen: SliceDescriptorGenerator; seqList: LIST OF Sequence; newSeqGen: SequenceGenerator; outSeqGen _ NEW[OutlineSequenceGeneratorObj]; [outSeqGen.list, outSeqPtr] _ StartOutlineSequenceList[]; sliceDescGen _ SelectedSlices[scene, selectClass]; FOR sliceD: SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen] UNTIL sliceD = NIL DO IF sliceD.slice.class.type=$Outline THEN { fenceSeq _ NIL; [holeSeqs, ptr] _ GGUtility.StartSequenceList[]; seqList _ GGOutline.SequencesOfOutline[sliceD]; 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: sliceD.slice, fenceSeq: fenceSeq, holeSeqs: newSeqGen]]; [outSeqGen.list, outSeqPtr] _ AddOutlineSequence[outlineSeq, outSeqGen.list, outSeqPtr]; }; 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; }; 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]; 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] = { 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; }; 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] = { 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] = { 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; }; 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]; }; Nconc: PROC [l1, l2: LIST OF SliceDescriptor] RETURNS[LIST OF SliceDescriptor] = { z: LIST OF SliceDescriptor _ l1; IF z = NIL THEN RETURN[l2]; UNTIL z.rest = NIL DO z _ z.rest; ENDLOOP; z.rest _ l2; RETURN[l1]; }; END. 6GGSelectImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Last edited by Bier on May 13, 1987 5:36:56 pm PDT Contents: Procedures dealing selecting and deselecting objects, including both selection actions and selection feedback. Pier, May 14, 1987 4:04:00 pm PDT Kurlander August 28, 1986 7:54:12 pm PDT 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 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; }; NoDonkeyTailOutline: PROC [slice: Outline, selectClass: SelectionClass] = { SELECT selectClass FROM normal => slice.normalSelectedParts _ NIL; hot => slice.hotSelectedParts _ NIL; active => slice.activeSelectedParts _ NIL; ENDCASE; }; 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[outline, scene, selectClass]; }; traj is now selected. This overrides any selections on traj's parts. Clear all flags that indicate the selection of each object. SetComponentSelectedFields[selected.first, FALSE, selectClass]; outline: Outline => DeselectEntireOutline[outline, scene, selectClass]; outlineD: OutlineDescriptor => DeselectOutline[outlineD.slice, outlineD.parts, scene, selectClass]; DeselectEntireSliceOLDD: 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]; }; }; Remove this slice from the selected list, and reset all selected fields. 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]; }; }; }; 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]; }; }; Very much like DeselectOutline, but optimized for the single Traj case. Updating Selections When Sequences Become Obsolete normal selections hot selections active selections Utilities Deletes entity from list. If it is not on the list, do nothing. ListSelectedSequences: 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; }; Build the selection list in the proper order and keep it that way. Enumerate Selected Objects IsSelectedInFullOLDD: 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; }; 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. outline: Outline => { outlineD: OutlineDescriptor; outlineD _ FindSelectedOutline[outline, scene, selectClass]; RETURN[outlineD # 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; }; SelectedStuff: PUBLIC PROC [scene: SceneRef, selectClass: SelectionClass] RETURNS [sliceDescGen: SliceDescriptorGenerator] = { ptr: LIST OF SliceDescriptor _ NIL; sliceDescGen _ NEW[SliceDescriptorGeneratorObj]; FOR sliceDList: LIST OF SliceDescriptor _ ListSelected[scene, selectClass], sliceDList.rest UNTIL sliceDList = NIL DO [sliceDescGen.list, ptr] _ AddSliceDescriptor[sliceDList.first, sliceDescGen.list, ptr]; ENDLOOP; }; 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; }; NextOutlineDescriptor: PUBLIC PROC [g: OutlineDescriptorGenerator] RETURNS [next: OutlineDescriptor] = { IF g.list = NIL THEN RETURN[NIL] ELSE { next _ g.list.first; g.list _ g.list.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; }; }; SelectedOutlineSequencesOLDD: 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; }; Returns a generator for all selected sequences, grouped by outline. Modifying All Selected Runs With A Call-Back Proc IF outSeqGen.list # NIL THEN { GGCaret.NoAttractor[caret: caret]; GGCaret.SitOn[caret, NIL]; }; 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. Only call the runProc if there is a segment in the run 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. 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. DoForEachSelectedOutline: PUBLIC PROC [scene: SceneRef, selectClass: SelectionClass, outlineProc: OutlineProc] = { outDGen: OutlineDescriptorGenerator _ SelectedOutlines[scene, selectClass]; FOR outlineD: OutlineDescriptor _ NextOutlineDescriptor[outDGen], NextOutlineDescriptor[outDGen] UNTIL outlineD = NIL DO outlineProc[outlineD]; ENDLOOP; }; Κ$!˜codešœ™Kšœ Οmœ1™žœ ™JKšœ,™,Kšœ/™/K™K™—š œž œA˜_Kšœ8žœ ˜DKšœ*˜*K˜K™—š œžœžœB˜]Kšœ5˜5Kšœ˜Kšœ˜Kšžœžœžœ˜'Kšœ2˜2Kšœ3˜3Kšœ&˜&K˜K˜—š  œžœžœ?˜VK™EKšœ˜Kšœ+˜+Kšœ-˜-K˜K™—K˜š œžœJ˜cJšœ0˜0Jšœ˜Jšœ0˜0šžœ`žœ žœž˜xJšœ$˜$Jšžœ˜—Jšœ-˜-šžœRžœžœž˜gJšœ$˜$Jšžœ˜—˜J˜——š  œžœžœ3˜KK™;š žœ žœžœAžœ žœž˜iKšœ+žœ™?Kšœ.˜.Kšžœ˜—Kšœžœ˜)K˜K˜—š  œžœžœ žœžœ3˜_šžœžœž˜Kšœ;˜;Kšœ5˜5Kšœ?˜?KšœY˜YKšœG™GKšœc™cKšžœžœ˜—K˜K˜—š  œžœžœ žœžœ˜LKšœ&˜&Kšœ&˜&Kšœ#˜#K˜K˜—š œžœžœ˜8Kšœ˜Kšœ˜Kšœ˜K˜K˜—š œžœžœA™eKšŸS™SKšœJ™Jšžœ žœžœ™Kšžœ'žœžœ™FKšœ/™/Kšœ+™+Kšœ&žœ™:K™—K™K™—š œžœžœA˜aKšœH™HKšœG˜Gšžœ žœžœ˜Kšœ,˜,Kšœ(˜(K˜—K˜K˜—š  œžœžœT˜nKšœ#˜#Kšœ4˜4šžœžœžœ˜Kšœ2˜2Kšœ0˜0Kšœ*˜*Kšœ&˜&šžœžœ žœ˜,Kšžœžœ4˜PKšœ*˜*Kšœ$˜$K˜—K˜—K˜K˜—š œžœžœX™tK™,Kšœ%™%Kšœ8™8šžœžœžœ™Kšœ6™6Kšœ2™2Kšœ*™*Kšœ*™*šžœžœ žœ™,Kšžœžœ6™RKšœ*™*Kšœ+™+K™—K™—K™K™—š œžœžœB˜_K˜5KšœA˜AKš  œ(˜5K˜K˜—š œžœžœE™gK™JKšœO™Ošžœ žœžœ™Kšœ.™.Kšœ1™1K™—K™K™—š  œžœžœ?˜XKšœG™GKšœ1˜1Kšœ˜Kšœ6˜6šžœžœžœ˜Kšœ(˜(Kšœ*˜*Kšœ#˜#šžœžœ"žœ˜.Kšœ*˜*Kšœ$˜$K˜—K˜—K˜K˜—K™K™2K™š   œžœžœ9žœžœ˜vKšœ˜šœ3˜3KšŸ™—šžœ žœžœ˜Kšœ5˜5Kšœ)˜)Kšœ&˜&K˜KšŸ™—Kšœ0˜0šžœ žœžœ˜Kšœ>˜>Kšœ&˜&Kšœ#˜#K˜KšŸ™—Kšœ3˜3šžœ žœžœ˜Kšžœ‘7˜>K˜—K˜K˜—K™ K™š œžœL˜aK™@šœ˜Kšœ2˜2Kšœ ˜ —K˜K˜—š   œžœžœ0žœžœžœ˜|šžœ ž˜Kšœ/˜/Kšœ)˜)Kšœ/˜/Kšžœžœ˜—K˜K˜—š  œžœ0žœžœžœ™wK™YKšœ™Kšœžœ™šžœ žœžœžœžœ5žœžœž™mšžœžœžœ™,Kšœžœ™Kšœžœ™'K™—Kšžœ™—K™—K˜Kšœ0™0K™š  œžœžœ#™Tšžœ ž™Kšœ1™1Kšœ+™+Kšœ1™1Kšžœ™—K™K™—š  œžœžœ#˜Tšžœ ž˜Kšœ2˜2Kšœ,˜,Kšœ2˜2Kšžœ˜—K˜K˜K˜—š œžœžœ#˜Všžœ ž˜Kšœ0˜0Kšœ*˜*Kšœ0˜0Kšžœ˜—K˜K˜—š œžœL˜aK™Bšžœ ž˜Kšœ?žœ ˜MKšœ6žœ ˜DKšœ?žœ ˜MKšžœžœ˜—K˜K˜—š œžœžœžœ2˜hšžœ ž˜Kšœ(˜(Kšœ"˜"Kšœ(˜(Kšžœžœ˜—K˜—K˜K™K˜š œžœžœ žœžœ0žœžœ™tKšžœžœž™šœ™Kšžœžœžœ™<šžœ ž™Kšœ žœ™.Kšœžœ™(Kšœ žœ™.Kšžœžœ™—K™—šœ™K™Kšœ,™,šžœCžœžœž™YKš žœžœ,žœžœžœ™EKšžœ™—Kšžœžœ™ K™—šœ™K™Kšœ5™5Kš žœžœžœžœžœ™ Kšžœ™#K™—Kšžœžœ™K™K™—š  œžœžœ>žœžœ˜mšœžœ ž˜5Kšœ$˜$Kšœ˜Kšœ$˜$Kšžœžœ˜—Kš žœžœžœžœžœ˜'Kšžœ*˜0K˜K˜—š œžœžœ žœžœ0žœžœ˜pKšœH™HKšœ@™@KšœF™FKšœ?™?Kšœ=™=Kšžœžœž˜šœ˜K˜Kšœ6˜6Kšžœ žœ˜K˜—šœ™K™Kšœ<™žœ˜šžœ ž˜Kšœ-˜-Kšœ'˜'Kšœ-˜-Kšžœžœ˜—Kšœ˜K˜—š  œžœžœ0žœ-™~Kšœžœžœžœ™#Kšœžœ™0š žœ žœžœEžœžœž™uKšœX™XKšžœ™—K™K™—š œžœžœ0žœ-˜Kšœžœžœžœ˜#Kšœžœ˜0š žœ žœžœEžœžœž˜uKšœX˜XKšžœ˜—K˜K˜—š œžœžœ0žœ-™Kšœžœ™Kšœžœžœžœ™#Kšœžœ™0šžœ žœžœžœžœ5žœžœž™mšžœžœ$žœ™3Kšœ žœ™"KšœN™NK™—Kšžœ™—K™K™—š œž œžœ˜bKš žœ žœžœžœžœ˜ šžœ˜Kšœ˜K˜K˜—Kšœ˜K˜—š œž œ!žœ™hKš žœ žœžœžœžœ™ šžœ™Kšœ™K™K™—Kšœ™—K˜š œžœ,žœžœžœžœžœ˜“šžœžœžœ˜Kš žœžœžœžœžœ˜#Kšœžœ žœ˜%Kšžœ˜K˜—šžœ˜Kšœ˜Kšœ žœ žœ˜Kšœ˜K˜—K˜K˜—š œžœ.žœžœžœžœžœ™›šžœžœžœ™Kš žœžœžœžœžœ™#Kšœžœ žœ™%Kšžœ™K™—šžœ™Kšœ™Kšœ žœ žœ™Kšœ™K™—K™—K˜Kšœžœžœ˜Gš œžœžœ0žœ*™ŠKšŸC™CKšœžœžœ ™ Kšœ žœžœ™#Kšœ™Kšœ™Jšœ(™(Jšœ žœžœ ™K™J™Kšœ žœ™-Kšœ9™9Jšœ.™.š žœ žœžœ@žœ žœž™hšžœžœž™Jšœžœ™ šœ ™ Kšœ žœ™Kšœ0™0Jšœ1™1Kšœ™šžœžœžœ™7Kšœ™Kšœ™K™—š žœžœžœžœžœž™CKšœC™CKšžœ™—Kš œ žœ žœžœ žœžœ*™]Kšœ žœZ™jKšœX™XJšœ™—Jšžœžœ™—Jšžœ™—J™K™—š œžœžœ0žœ*˜†KšŸC™CKšœžœžœ ˜ Kšœ žœžœ˜#Kšœ˜Kšœ˜Jšœ'˜'Jšœ žœžœ ˜K˜J˜Kšœ žœ˜-Kšœ9˜9Jšœ2˜2šžœržœ žœž˜Ššžœ!žœ˜+Kšœ žœ˜Kšœ0˜0Jšœ œŸœ˜/Kšœ˜šžœžœžœ˜7Kšœ˜Kšœ˜K˜—š žœžœžœžœžœž˜CKšœC˜CKšžœ˜—Kš œ žœ žœžœ žœžœ*˜]Kšœ žœX˜hKšœX˜XJšœ˜—Jšžœ˜—J˜K˜—š œž œ'žœ˜mKš žœžœžœžœžœ˜)Kšœ˜Kšœ%˜%K˜—K˜š œžœžœ,žœžœžœžœžœ˜ššžœžœžœ˜Kš žœžœžœžœžœ˜#Kšœžœ žœ˜%Kšžœ˜K˜—šžœ˜Kšœ˜Kšœ žœ žœ˜Kšœ˜K˜—˜K˜——š  œžœžœžœžœžœ˜_Kšœžœ˜K˜—K˜K™1K˜š œžœžœPžœžœžœžœžœ˜΄Kšœ$˜$Kšœ˜K˜Kšœ,Ÿœ˜4KšœŸœŸœ/˜XJšœ,Ÿœ˜4•StartOfExpansionΒ[gargoyleData: GGInterfaceTypes.GargoyleData, point: GGBasicTypes.Point, attractor: GGModelTypes.Traj, onJoint: BOOL _ TRUE, attractorJointNum: NAT _ 999, attractorSegNum: NAT _ 999]šžœžœžœ™Jšœ"™"Jšœžœ™J™—šžœžœžœ‘#˜Ašžœežœ žœž˜}Jšœ˜šžœ žœžœ˜KšœŸœŸœ/˜RKšœ1˜1Kšžœ˜K˜—šžœhžœ žœž˜KšœŸœŸœ/˜QKšœ1˜1Kšžœ˜Jšžœ˜—šž˜˜Jšœ,Ÿœ˜4J˜——Jšžœ˜—Jšžœ˜—K˜K˜—K˜Kšœ žœ˜!š  œžœBžœžœžœ˜Kšœ  œJ™{K™₯Kšœ˜Kšœ˜Kšœ žœ˜K˜ Kšœ žœžœ ˜K˜Kšœ/˜/Kšœ5˜5Kšžœžœ ˜,šžœ˜Kšœ4˜4šžœžœ˜KšœŸœ‘#˜IKšœ ˜ Kšžœ‘(˜0K˜—Kšœ&˜&Kšžœžœžœžœ˜K˜—K™6šžœžœžœ˜,Kšœ Ÿœ˜šžœ žœžœ˜KšœžœŸœ‘˜CKšžœžœžœŸœ˜=K˜—KšœŸ œ˜˜>Jšžœ˜—Jšžœ˜—Jšœ)˜)šžœ žœBžœž˜iJšœ*˜*Jšœ,˜,Jšžœ˜—J˜K˜K˜—š  œžœ žœžœ žœ˜QKšœ!˜!š žœžœžœžœžœž˜CK–<[bBox: GGBasicTypes.BoundBox, by: GGBasicTypes.BoundBox]šœ=˜=Kšžœ˜—K˜—K˜Kšœ žœ˜%Kšœ žœ˜)K˜š œžœžœI˜lKšœL˜Lšžœ`žœ žœž˜xKšœ˜Jšžœ˜—K˜K˜—š œžœžœM™rKšœK™Kšžœ^žœ žœž™xKšœ™Kšžœ™—K™K™—š œžœžœžœžœžœžœ˜iKšœžœžœžœ˜%K˜ šžœžœž˜šžœ žœ˜Kš žœžœžœžœ ‘˜@K˜Kšžœ˜ Kšœ˜—K˜K˜ Kšžœ˜—Kšžœ˜Kšœ˜K˜—š œžœ žœžœžœžœžœ˜SKšœžœžœ˜ Kšžœžœžœžœ˜šžœ žœž˜Kšœ ˜ Kšžœ˜—K˜ Kšžœ˜ Kšœ˜—K˜Kšžœ˜K™K™K™—…—WΜ²}