<<>> <> <> <> <> <> <> <> <<>> DIRECTORY BasicTime, GGBasicTypes, GGBoundBox, GGInterfaceTypes, GGModelTypes, GGOutline, GGParent, GGScene, GGSceneType, GGSegmentTypes, GGSelect, GGSlice, GGSliceOps, GGTraj, GGUtility, Imager, ImagerPath, Rope, TextNode; GGSceneImpl: CEDAR MONITOR IMPORTS BasicTime, GGBoundBox, GGOutline, GGParent, GGScene, GGSelect, GGSlice, GGSliceOps, GGTraj, GGUtility, Imager EXPORTS GGScene, GGModelTypes = BEGIN BoundBox: TYPE = GGModelTypes.BoundBox; BoundBoxGenerator: TYPE = GGModelTypes.BoundBoxGenerator; BoundBoxGeneratorObj: TYPE = GGModelTypes.BoundBoxGeneratorObj; Camera: TYPE = GGModelTypes.Camera; CameraObj: TYPE = GGModelTypes.CameraObj; Color: TYPE = Imager.Color; CurveType: TYPE = {line, bezier, conic}; FeatureCycler: TYPE = GGInterfaceTypes.FeatureCycler; GGData: TYPE = GGInterfaceTypes.GGData; Joint: TYPE = GGSegmentTypes.Joint; JointGenerator: TYPE = GGModelTypes.JointGenerator; JointGeneratorObj: TYPE = GGModelTypes.JointGeneratorObj; JointObj: TYPE = GGSegmentTypes.JointObj; OutlineData: TYPE = GGOutline.OutlineData; Point: TYPE = GGBasicTypes.Point; Scene: TYPE = GGModelTypes.Scene; SceneObj: PUBLIC TYPE = GGSceneType.SceneObj; -- export of opaque type SelectedData: PUBLIC TYPE = GGSceneType.SelectedData; -- export of opaque type SelectionClass: TYPE = GGSegmentTypes.SelectionClass; Segment: TYPE = GGModelTypes.Segment; SegmentGenerator: TYPE = GGModelTypes.SegmentGenerator; SegmentObj: TYPE = GGSegmentTypes.SegmentObj; Slice: TYPE = GGModelTypes.Slice; SliceParts: TYPE = GGModelTypes.SliceParts; SlicePartsWalkProc: TYPE = GGModelTypes.SlicePartsWalkProc; SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor; SliceDescriptorGenerator: TYPE = GGModelTypes.SliceDescriptorGenerator; SliceDescriptorWalkProc: TYPE = GGModelTypes.SliceDescriptorWalkProc; SliceGenerator: TYPE = GGModelTypes.SliceGenerator; SliceGeneratorObj: TYPE = GGModelTypes.SliceGeneratorObj; SliceWalkProc: TYPE = GGScene.SliceWalkProc; Traj: TYPE = GGModelTypes.Traj; TrajData: TYPE = GGModelTypes.TrajData; TrajParts: TYPE = GGModelTypes.TrajParts; TrajEnd: TYPE = GGModelTypes.TrajEnd; -- {lo, hi}; Vector: TYPE = GGBasicTypes.Vector; WalkLevel: TYPE = GGModelTypes.WalkLevel; <> <> Problem: PUBLIC SIGNAL [msg: Rope.ROPE] = CODE; <<>> <> <> <<>> CreateScene: PUBLIC PROC [entities: LIST OF Slice ¬ NIL] RETURNS [scene: Scene] = { scene ¬ NEW[SceneObj ¬ [entities: entities, prioritiesValid: FALSE, backgroundColor: Imager.white] ]; scene.lastEditTime ¬ BasicTime.ExtendedNow[]; }; SetScene: PUBLIC PROC [from: Scene, to: Scene] = { to2: REF SceneObj ¬ NARROW[to]; from1: REF SceneObj ¬ NARROW[from]; to2­ ¬ from1­; }; CreateDefaultCamera: PUBLIC PROC [] RETURNS [camera: Camera] = { camera ¬ NEW[CameraObj]; }; AddSlice: PUBLIC PROC [scene: Scene, slice: Slice, priority: INT ¬ -1] = { <> IF slice=NIL THEN RETURN; slice.parent ¬ NIL; IF priority = -1 OR TopPriority[scene]> IF slices=NIL THEN RETURN; FOR list: LIST OF Slice ¬ slices, list.rest UNTIL list = NIL DO list.first.parent ¬ NIL; ENDLOOP; IF priority = -1 OR TopPriority[scene]> copiedSlices: LIST OF Slice ¬ GGUtility.CopySliceList[slices]; PutBehind[scene, GetAtPriority[scene, priority], copiedSlices]; }; }; AddHole: PUBLIC PROC [outline: Slice, hole: Slice, scene: Scene] RETURNS [holier: Slice] = { holeOutline: Slice ¬ GGParent.GetParent[hole]; priority: INT ¬ GetPriority[scene: scene, slice: outline]; IF holeOutline = outline THEN ERROR; holier ¬ GGOutline.AddChild[outline, hole]; DeleteSlice[scene, IF holeOutline=NIL THEN hole ELSE holeOutline]; DeleteSlice[scene, outline]; AddSlice[scene, holier, priority]; }; AppendHoles: PUBLIC PROC [outline: Slice, holes: LIST OF Slice] = { <> outlineData: OutlineData ¬ NARROW[outline.data]; FOR holeList: LIST OF Slice ¬ holes, holeList.rest UNTIL holeList=NIL DO outlineData.children ¬ GGUtility.AppendSliceList[outlineData.children, LIST[holeList.first]]; holeList.first.parent ¬ outline; IF GGSliceOps.GetType[holeList.first]=$Traj THEN GGTraj.SetTrajRole[holeList.first, hole]; ENDLOOP; GGSlice.KillBoundBox[outline]; }; AppendSlice: PROC [scene: Scene, slice: Slice] = { scene.prioritiesValid ¬ FALSE; IF NOT scene.ptrValid THEN UpdatePtr[scene]; [scene.entities, scene.ptr] ¬ GGUtility.AddSlice[slice, scene.entities, scene.ptr]; }; UpdatePtr: PROC [scene: Scene] = { IF scene.entities=NIL THEN scene.ptr ¬ NIL ELSE FOR list: LIST OF Slice ¬ scene.entities, list.rest UNTIL list=NIL DO scene.ptr ¬ list; ENDLOOP; scene.ptrValid ¬ TRUE; }; SetBackgroundColor: PUBLIC PROC [scene: Scene, color: Color] = { scene.backgroundColor ¬ color; }; GetBackgroundColor: PUBLIC PROC [scene: Scene] RETURNS [color: Color] = { color ¬ scene.backgroundColor; }; SetLastEditedTime: PUBLIC PROC [scene: Scene, time: BasicTime.ExtendedGMT] = { scene.lastEditTime ¬ time; }; GetLastEditedTime: PUBLIC PROC [scene: Scene] RETURNS [time: BasicTime.ExtendedGMT] = { time ¬ scene.lastEditTime; }; <> <<>> CopySelectedParts: PUBLIC PROC [fromScene: Scene, toScene: Scene] RETURNS [newSlices: LIST OF Slice ¬ NIL] = { <> MakeNewSlices: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL ¬ FALSE] = { copies: LIST OF Slice ¬ GGSliceOps.Copy[sliceD.slice, sliceD.parts]; FOR list: LIST OF Slice ¬ copies, list.rest UNTIL list = NIL DO list.first.priority ¬ GGScene.GetPriority[fromScene, sliceD.slice]; [newSlices, ptr] ¬ GGUtility.AddSlice[list.first, newSlices, ptr]; ENDLOOP; }; ptr: LIST OF Slice ¬ NIL; [] ¬ GGScene.WalkSelectedSlices[fromScene, first, MakeNewSlices, normal]; newSlices ¬ GGUtility.SortSliceListByPriority[newSlices]; -- preserves order of equal elements GGSelect.DeselectAll[toScene, normal]; <> FOR sliceList: LIST OF Slice ¬ newSlices, sliceList.rest UNTIL sliceList = NIL DO GGScene.AddSlice[toScene, sliceList.first, -1]; GGSelect.SelectEntireSlice[sliceList.first, toScene, normal]; ENDLOOP; }; <> <<>> UpdatePriorities: PROC [scene: Scene] = { DoUpdate: PROC [slice: Slice] RETURNS [done: BOOL ¬ FALSE] = { slice.priority ¬ index; index ¬ index + 1; }; index: NAT ¬ 0; [] ¬ GGScene.WalkSlices[scene, first, DoUpdate]; scene.prioritiesValid ¬ TRUE; }; GetPriority: PUBLIC PROC [scene: Scene, slice: Slice] RETURNS [priority: INT] = { <> IF NOT scene.prioritiesValid THEN UpdatePriorities[scene]; priority ¬ slice.priority; }; TopPriority: PUBLIC PROC [scene: Scene] RETURNS [priority: INT] = { <> IF NOT scene.prioritiesValid THEN UpdatePriorities[scene]; IF NOT scene.ptrValid THEN UpdatePtr[scene]; RETURN[IF scene.ptr=NIL THEN 0 ELSE scene.ptr.first.priority]; }; UpOne: PUBLIC PROC [scene: Scene, slice: Slice] = { <> ExchangeWithNextEntity: PROC [scene: Scene, slice: Slice] = { previous, next: LIST OF Slice ¬ NIL; FOR list: LIST OF Slice ¬ scene.entities, list.rest UNTIL list = NIL DO IF list.first=slice THEN { -- exchange this entity with its next neighbor next ¬ list.rest; IF next=NIL THEN RETURN; -- entity already at end (i.e. top) of list list.rest ¬ next.rest; next.rest ¬ list; IF previous#NIL THEN previous.rest ¬ next ELSE scene.entities ¬ next; } ELSE previous ¬ list; ENDLOOP; }; scene.ptrValid ¬ scene.prioritiesValid ¬ FALSE; ExchangeWithNextEntity[scene, slice]; }; PutInFront: PUBLIC PROC [scene: Scene, slice: Slice, slices: LIST OF Slice] = { <> IF slice#NIL AND slices#NIL THEN { beforeEnt, ent, afterEnt, slicesRest: LIST OF Slice; found: BOOL ¬ FALSE; [beforeEnt, ent, afterEnt, found] ¬ GGUtility.FindSliceAndNeighbors[slice, scene.entities]; IF found THEN { IF ent.first#slice THEN ERROR; -- debugging test scene.ptrValid ¬ FALSE; scene.prioritiesValid ¬ FALSE; ent.rest ¬ slices; -- ent is really part of scene.entities. Skip over slice and then Splice in new slices. FOR sList: LIST OF Slice ¬ slices, sList.rest UNTIL sList=NIL DO slicesRest ¬ sList; ENDLOOP; slicesRest.rest ¬ afterEnt; }; }; }; DownOne: PUBLIC PROC [scene: Scene, slice: Slice] = { <> ExchangeWithPreviousEntity: PROC [scene: Scene, slice: Slice] = { preprev, previous: LIST OF Slice ¬ NIL; FOR list: LIST OF Slice ¬ scene.entities, list.rest UNTIL list=NIL DO IF list.first=slice THEN { -- exchange this entity with its previous neighbor SELECT TRUE FROM preprev=NIL AND previous=NIL => RETURN; -- entity already on bottom preprev=NIL AND previous#NIL => { -- special case. Second from bottom previous.rest ¬ list.rest; list.rest ¬ previous; scene.entities ¬ list; RETURN; }; preprev#NIL AND previous=NIL => ERROR; -- Assert: can't happen preprev#NIL AND previous#NIL => { -- usual case. Exchange list elements previous.rest ¬ list.rest; list.rest ¬ previous; preprev.rest ¬ list; RETURN; }; ENDCASE => ERROR; -- Assert: can't happen } ELSE { preprev ¬ previous; previous ¬ list; }; ENDLOOP; }; scene.ptrValid ¬ scene.prioritiesValid ¬ FALSE; ExchangeWithPreviousEntity[scene, slice]; }; PutBehind: PUBLIC PROC [scene: Scene, slice: Slice, slices: LIST OF Slice] = { <> IF slice#NIL AND slices#NIL THEN { beforeEnt, ent, afterEnt, slicesRest: LIST OF Slice; found: BOOL ¬ FALSE; [beforeEnt, ent, afterEnt, found] ¬ GGUtility.FindSliceAndNeighbors[slice, scene.entities]; IF found THEN { IF ent.first#slice THEN ERROR; -- debugging test scene.ptrValid ¬ FALSE; scene.prioritiesValid ¬ FALSE; IF beforeEnt#NIL THEN beforeEnt.rest ¬ slices ELSE scene.entities ¬ slices; -- beforeEnt is really part of scene.entities. Splice in new slices before the existing slice FOR sList: LIST OF Slice ¬ slices, sList.rest UNTIL sList=NIL DO slicesRest ¬ sList; ENDLOOP; slicesRest.rest ¬ ent; -- add on the original slice and the remaining entities }; }; }; ReplaceSlice: PUBLIC PROC [scene: Scene, slice: Slice, slices: LIST OF Slice] = { <> <> beforeEnt, ent, afterEnt, slicesRest: LIST OF Slice; found: BOOL ¬ FALSE; IF slice#NIL AND slices#NIL THEN { [beforeEnt, ent, afterEnt, found] ¬ GGUtility.FindSliceAndNeighbors[slice, scene.entities]; IF found THEN { IF ent.first#slice THEN ERROR; -- debugging test scene.ptrValid ¬ FALSE; scene.prioritiesValid ¬ FALSE; IF beforeEnt#NIL THEN beforeEnt.rest ¬ slices ELSE scene.entities ¬ slices; -- beforeEnt is really part of scene.entities. Splice in new slices before the existing slice FOR sList: LIST OF Slice ¬ slices, sList.rest UNTIL sList=NIL DO slicesRest ¬ sList; ENDLOOP; slicesRest.rest ¬ afterEnt; }; }; }; PutAtPriority: PUBLIC PROC [scene: Scene, slice: Slice, priority: INT ¬ -1] = { <> RemoveSlice[scene, slice]; AddSlice[scene, slice, priority]; }; GetAtPriority: PUBLIC PROC [scene: Scene, priority: INT] RETURNS [slice: Slice] = { IF NOT scene.prioritiesValid THEN UpdatePriorities[scene]; IF priority = -1 THEN { -- special case: frontmost IF NOT scene.ptrValid THEN UpdatePtr[scene]; RETURN[scene.ptr.first]; } ELSE IF priority = 0 THEN RETURN[scene.entities.first] -- special case: rear-most ELSE { -- somewhere in the middle count: INT ¬ 1; FOR sliceList: LIST OF Slice ¬ scene.entities.rest, sliceList.rest UNTIL sliceList=NIL DO IF count=priority THEN RETURN[sliceList.first]; count ¬ count+1; ENDLOOP; <> IF NOT scene.ptrValid THEN UpdatePtr[scene]; RETURN[scene.ptr.first]; }; }; DeleteAtPriority: PUBLIC PROC [scene: Scene, priority: INT] RETURNS [deleted: Slice] = { deleted ¬ GetAtPriority[scene, priority]; DeleteSlice[scene, deleted]; }; RemoveSlice: PUBLIC PROC [scene: Scene, slice: Slice] = { scene.ptrValid ¬ FALSE; scene.prioritiesValid ¬ FALSE; scene.entities ¬ GGUtility.DeleteSliceFromList[slice, scene.entities]; }; DeleteSlice: PUBLIC PROC [scene: Scene, slice: Slice] = { scene.ptrValid ¬ FALSE; scene.prioritiesValid ¬ FALSE; GGSelect.DeselectEntityAllClasses[slice, scene]; scene.entities ¬ GGUtility.DeleteSliceFromList[slice, scene.entities]; }; DeleteSequence: PUBLIC PROC [seq: SliceDescriptor, scene: Scene] RETURNS [oldOutline: Slice, newOutlines: LIST OF Slice] = { priority: INT; traj: Slice ¬ seq.slice; smallerOutline: Slice; oldOutline ¬ GGParent.GetParent[traj]; GGOutline.SaveSelectionsInOutlineAllClasses[oldOutline]; priority ¬ GGScene.GetPriority[scene, oldOutline]; [smallerOutline, newOutlines] ¬ GGTraj.DeleteSequence[seq]; IF smallerOutline # NIL THEN newOutlines ¬ CONS[smallerOutline, newOutlines]; IF newOutlines#NIL THEN AddSlices[scene, newOutlines, priority]; DeleteSlice[scene, oldOutline]; FOR list: LIST OF Slice ¬ newOutlines, list.rest UNTIL list = NIL DO GGSelect.ReselectSliceAllClasses[list.first, scene]; ENDLOOP; }; OutlinesFromOutlineExcept: PROC [outline: Slice, except: Traj] RETURNS [newOutlines: LIST OF Slice] = { ptr: LIST OF Slice; newSlice, newOutline: Slice; children: LIST OF Slice ¬ NARROW[outline.data, OutlineData].children; [newOutlines, ptr] ¬ GGUtility.StartSliceList[]; FOR childList: LIST OF Slice ¬ children, childList.rest UNTIL childList = NIL DO IF childList.first = except THEN LOOP; newSlice ¬ GGSliceOps.Copy[childList.first].first; newOutline ¬ GGOutline.CreateOutline[newSlice, GGSliceOps.GetFillColor[outline, NIL].color]; [newOutlines, ptr] ¬ GGUtility.AddSlice[newOutline, newOutlines, ptr]; ENDLOOP; }; noGarbage: BOOL ¬ FALSE; MakeSceneGarbage: PUBLIC PROC [scene: Scene] = { <> oldEntities: LIST OF LIST OF Slice; entities: LIST OF Slice; IF noGarbage THEN RETURN; oldEntities ¬ scene.oldEntities; scene.oldEntities ¬ NIL; entities ¬ scene.entities; scene.entities ¬ NIL; -- in case a screen refresh is attempted while we are doing this. FOR sllist: LIST OF LIST OF Slice ¬ oldEntities, sllist.rest UNTIL sllist=NIL DO FOR list: LIST OF Slice ¬ sllist.first, list.rest UNTIL list=NIL DO IF list.first.class#NIL THEN GGSliceOps.Unlink[list.first]; -- if class=NIL, already unlinked ENDLOOP; ENDLOOP; FOR list: LIST OF Slice ¬ entities, list.rest UNTIL list=NIL DO IF list.first.class#NIL THEN GGSliceOps.Unlink[list.first]; -- if class=NIL, already unlinked ENDLOOP; }; MergeScenes: PUBLIC PROC [back: Scene, front: Scene] RETURNS [combined: Scene] = { combined ¬ NEW[SceneObj ¬ [prioritiesValid: FALSE, backgroundColor: back.backgroundColor]]; combined.entities ¬ GGUtility.AppendSliceList[back.entities, front.entities]; combined.selected.normal ¬ GGUtility.AppendSliceDescriptorList[back.selected.normal, front.selected.normal]; combined.selected.hot ¬ GGUtility.AppendSliceDescriptorList[back.selected.hot, front.selected.hot]; combined.selected.active ¬ GGUtility.AppendSliceDescriptorList[back.selected.active, front.selected.active]; combined.selected.match ¬ GGUtility.AppendSliceDescriptorList[back.selected.match, front.selected.match]; }; <<>> <> WalkSlices: PUBLIC PROC [scene: Scene, level: WalkLevel, walkProc: SliceWalkProc, classType: ATOM ¬ NIL] RETURNS [aborted: BOOL ¬ FALSE] = { < all classes. Use level=all to walk every slice of a given class.>> FOR slices: LIST OF Slice ¬ scene.entities, slices.rest UNTIL slices = NIL DO thisType: ATOM ¬ GGSliceOps.GetType[slices.first]; <> IF (level # leaf OR GGParent.IsLeafOfClass[slices.first, classType]) AND (classType = NIL OR thisType = classType) THEN { aborted ¬ walkProc[slices.first]; IF aborted THEN RETURN; }; <> IF (level = all OR level = leaf OR (level = highest AND thisType # classType)) AND GGParent.IsParent[slices.first] THEN { aborted ¬ GGParent.WalkChildren[slices.first, level, walkProc, classType]; IF aborted THEN RETURN; }; ENDLOOP; }; WalkSelectedSlices: PUBLIC PROC [scene: Scene, level: WalkLevel, walkProc: SliceDescriptorWalkProc, selectClass: SelectionClass, classType: ATOM ¬ NIL] RETURNS [aborted: BOOL ¬ FALSE] = { < all classes. Use level=all to walk every selected slice of a given class.>> FOR list: LIST OF SliceDescriptor ¬ GGSelect.ListSelected[scene, selectClass], list.rest UNTIL list = NIL DO thisSlice: Slice ¬ list.first.slice; thisParts: SliceParts ¬ list.first.parts; thisType: ATOM ¬ GGSliceOps.GetType[thisSlice]; <> IF (classType = NIL OR thisType = classType) AND (level # leaf OR GGParent.IsLeafOfClass[thisSlice, classType]) THEN { aborted ¬ walkProc[list.first]; IF aborted THEN RETURN; }; <> IF (level = all OR level = leaf OR (level = highest AND thisType # classType)) AND GGParent.IsParent[thisSlice] THEN { aborted ¬ GGParent.WalkIncludedChildren[thisSlice, thisParts, level, walkProc, classType]; IF aborted THEN RETURN; }; ENDLOOP; }; ListSlices: PUBLIC PROC [scene: Scene, level: WalkLevel, classType: ATOM ¬ NIL] RETURNS [sliceList: LIST OF Slice] = { ptr: LIST OF Slice; DoAppendSlice: PROC [slice: Slice] RETURNS [done: BOOL ¬ FALSE] = { [sliceList, ptr] ¬ GGUtility.AddSlice[slice, sliceList, ptr]; }; [sliceList, ptr] ¬ GGUtility.StartSliceList[]; [] ¬ WalkSlices[scene, level, DoAppendSlice, classType]; }; ListSelectedSlices: PUBLIC PROC [scene: Scene, level: WalkLevel, selectClass: SelectionClass, classType: ATOM ¬ NIL] RETURNS [selectedList: LIST OF SliceDescriptor] = { ptr: LIST OF SliceDescriptor; DoAppendSlice: PROC [childD: SliceDescriptor] RETURNS [done: BOOL ¬ FALSE] = { [selectedList, ptr] ¬ GGUtility.AddSliceDescriptor[childD, selectedList, ptr]; }; [selectedList, ptr] ¬ GGUtility.StartSliceDescriptorList[]; [] ¬ WalkSelectedSlices[scene, level, DoAppendSlice, selectClass, classType]; }; <<>> CountSlices: PUBLIC PROC [scene: Scene, level: WalkLevel, classType: ATOM ¬ NIL] RETURNS [count: INT ¬ 0] = { AddEmUp: PROC [child: Slice] RETURNS [done: BOOL ¬ FALSE] = { count ¬ count + 1; }; [] ¬ WalkSlices[scene, level, AddEmUp, classType]; }; CountSelectedSlices: PUBLIC PROC [scene: Scene, level: WalkLevel, selectClass: SelectionClass, classType: ATOM ¬ NIL] RETURNS [count: INT ¬ 0] = { AddEmUp: PROC [childD: SliceDescriptor] RETURNS [done: BOOL ¬ FALSE] = { count ¬ count + 1; }; [] ¬ WalkSelectedSlices[scene, level, AddEmUp, selectClass, classType]; }; FirstSelectedSlice: PUBLIC PROC [scene: Scene, level: WalkLevel, selectClass: SelectionClass, classType: ATOM ¬ NIL] RETURNS [sliceD: SliceDescriptor] = { StopAtFirstSlice: PROC [childD: SliceDescriptor] RETURNS [done: BOOL ¬ FALSE] = { sliceD ¬ childD; RETURN[TRUE]; }; [] ¬ WalkSelectedSlices[scene, level, StopAtFirstSlice, selectClass, classType]; }; LastSelectedSlice: PUBLIC PROC [scene: Scene, level: WalkLevel, selectClass: SelectionClass, classType: ATOM ¬ NIL] RETURNS [sliceD: SliceDescriptor] = { StopAtLastSlice: PROC [childD: SliceDescriptor] RETURNS [done: BOOL ¬ FALSE] = { sliceD ¬ childD; }; [] ¬ WalkSelectedSlices[scene, level, StopAtLastSlice, selectClass, classType]; }; IsTopLevel: PUBLIC PROC [slice: Slice] RETURNS [BOOL] = { RETURN[slice.parent = NIL]; }; <<>> <> <<>> BoundBoxesInScene: PUBLIC PROC [scene: Scene] RETURNS [bBoxGen: BoundBoxGenerator] = { <> list: LIST OF BoundBox; thisBox: BoundBox; FOR entities: LIST OF Slice ¬ scene.entities, entities.rest UNTIL entities = NIL DO IF GGSliceOps.GetType[entities.first]=$Outline THEN { -- multiple bound boxes wanted outlineData: OutlineData ¬ NARROW[entities.first.data]; FOR children: LIST OF Slice ¬ outlineData.children, children.rest UNTIL children=NIL DO thisBox ¬ GGSliceOps.GetBoundBox[children.first, NIL]; list ¬ CONS[thisBox, list]; ENDLOOP; } ELSE { thisBox ¬ GGSliceOps.GetBoundBox[entities.first, NIL]; list ¬ CONS[thisBox, list]; }; ENDLOOP; bBoxGen ¬ NEW[BoundBoxGeneratorObj ¬ [ list: list ]]; }; TightBoxesInScene: PUBLIC PROC [scene: Scene] RETURNS [bBoxGen: BoundBoxGenerator] = { <> <> list: LIST OF BoundBox; thisBox: BoundBox; FOR entities: LIST OF Slice ¬ scene.entities, entities.rest UNTIL entities = NIL DO thisBox ¬ GGSliceOps.GetTightBox[entities.first, NIL]; list ¬ CONS[thisBox, list]; ENDLOOP; bBoxGen ¬ NEW[BoundBoxGeneratorObj ¬ [ list: list ]]; }; NextBox: PUBLIC PROC [g: BoundBoxGenerator] RETURNS [next: BoundBox] = { IF g.list = NIL THEN RETURN[NIL] ELSE { next ¬ g.list.first; g.list ¬ g.list.rest; }; }; <<>> BoundBoxOfScene: PUBLIC PROC [scene: Scene] RETURNS [bBox: BoundBox] = { <> bbGen: GGModelTypes.BoundBoxGenerator ¬ BoundBoxesInScene[scene]; bBox ¬ GGBoundBox.BoundBoxOfBoxes[bbGen.list]; -- cheating to go inside generator }; TightBoxOfScene: PUBLIC PROC [scene: Scene] RETURNS [bBox: BoundBox] = { <> bbGen: GGModelTypes.BoundBoxGenerator ¬ TightBoxesInScene[scene]; bBox ¬ GGBoundBox.BoundBoxOfBoxes[bbGen.list]; -- cheating to go inside generator }; <<>> SelectionBoxOfSelected: PUBLIC PROC [scene: Scene, selectClass: SelectionClass ¬ normal, full: BOOL ¬ FALSE] RETURNS [bigBox: BoundBox] = { nextBox: BoundBox; DoEnlargeBox: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL ¬ FALSE] = { nextBox ¬ IF GGSliceOps.GetType[sliceD.slice]=$Text THEN GGSliceOps.GetTightBox[sliceD.slice, IF full THEN NIL ELSE sliceD.parts] ELSE GGSliceOps.GetBoundBox[sliceD.slice, IF full THEN NIL ELSE sliceD.parts]; GGBoundBox.EnlargeByBox[bBox: bigBox, by: nextBox]; }; bigBox ¬ GGBoundBox.NullBoundBox[]; [] ¬ GGScene.WalkSelectedSlices[scene, first, DoEnlargeBox, selectClass]; }; BoundBoxOfSelected: PUBLIC PROC [scene: Scene, selectClass: SelectionClass ¬ normal, sliceLevel: BOOL ¬ FALSE] RETURNS [bigBox: BoundBox] = { nextBox: BoundBox; DoEnlargeBox: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL ¬ FALSE] = { nextBox ¬ GGSliceOps.GetBoundBox[sliceD.slice, IF sliceLevel THEN NIL ELSE sliceD.parts]; GGBoundBox.EnlargeByBox[bBox: bigBox, by: nextBox]; }; bigBox ¬ GGBoundBox.NullBoundBox[]; [] ¬ GGScene.WalkSelectedSlices[scene, first, DoEnlargeBox, selectClass]; }; TightBoxOfSelected: PUBLIC PROC [scene: Scene, selectClass: SelectionClass ¬ normal, sliceLevel: BOOL ¬ FALSE] RETURNS [bigBox: BoundBox] = { <> nextBox: BoundBox; DoEnlargeBox: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL ¬ FALSE] = { nextBox ¬ GGSliceOps.GetTightBox[sliceD.slice, IF sliceLevel THEN NIL ELSE sliceD.parts]; GGBoundBox.EnlargeByBox[bBox: bigBox, by: nextBox]; }; bigBox ¬ GGBoundBox.NullBoundBox[]; [] ¬ GGScene.WalkSelectedSlices[scene, first, DoEnlargeBox, selectClass]; }; useSelected: BOOL ¬ FALSE; BoundBoxOfMoving: PUBLIC PROC [scene: Scene, editConstraints: GGModelTypes.EditConstraints, bezierDrag: GGInterfaceTypes.BezierDragRecord, selectClass: SelectionClass ¬ normal] RETURNS [bigBox: BoundBox] = { <> nextBox: BoundBox; DoEnlargeBox: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL ¬ FALSE] = { <> overlay, rubber, drag, movingParts: SliceDescriptor; [----, overlay, rubber, drag] ¬ GGSliceOps.MovingParts[sliceD.slice, sliceD.parts, editConstraints, bezierDrag]; movingParts ¬ GGSliceOps.UnionParts[overlay, rubber]; movingParts ¬ GGSliceOps.UnionParts[movingParts, drag]; nextBox ¬ GGSliceOps.GetBoundBox[sliceD.slice, movingParts.parts]; GGBoundBox.EnlargeByBox[bBox: bigBox, by: nextBox]; }; IF useSelected THEN RETURN[BoundBoxOfSelected[scene, selectClass]]; bigBox ¬ GGBoundBox.NullBoundBox[]; [] ¬ GGScene.WalkSelectedSlices[scene, first, DoEnlargeBox, selectClass]; }; <> <<>> DeleteAllSelected: PUBLIC PROC [scene: Scene] RETURNS [bBox: BoundBox] = { DeleteTopLevel: PROC [topLevelD: SliceDescriptor] RETURNS [done: BOOL ¬ FALSE] = { IF GGSliceOps.IsCompleteParts[topLevelD] THEN { affectedBox ¬ GGSliceOps.GetBoundBox[topLevelD.slice, NIL]; DeleteSlice[scene, topLevelD.slice]; -- fast case } ELSE { newSlices: LIST OF Slice; priority: INT ¬ GetPriority[scene, topLevelD.slice]; GGSelect.SaveSelectionsInSliceAllClasses[topLevelD.slice, scene]; [newSlices, affectedBox] ¬ DeleteSliceParts[topLevelD]; DeleteSlice[scene, topLevelD.slice]; AddSlices[scene, newSlices, priority]; FOR list: LIST OF Slice ¬ newSlices, list.rest UNTIL list = NIL DO GGSelect.ReselectSliceAllClasses[list.first, scene]; GGSelect.DeselectEntireSlice[list.first, scene, active]; -- do this in case some active selections remain for parts which were not deleted. This fixes a problem when a control point is selected (not a joint) and DEL is invoked but the CP is not deleted. ENDLOOP; }; GGBoundBox.EnlargeByBox[bBox, affectedBox]; }; affectedBox: BoundBox; bBox ¬ GGScene.BoundBoxOfSelected[scene, normal]; GGSelect.DuplicateSelections[scene, normal, active]; [] ¬ GGScene.WalkSelectedSlices[scene, first, DeleteTopLevel, active]; }; DeleteSliceParts: PROC [sliceD: SliceDescriptor] RETURNS [newSlices: LIST OF Slice, bBox: BoundBox] = { <> bBox ¬ GGBoundBox.NullBoundBox[]; SELECT GGSliceOps.GetType[sliceD.slice] FROM $Traj => ERROR; $Cluster => { newChildren: LIST OF Slice; IF GGSliceOps.IsCompleteParts[sliceD] THEN RETURN[NIL, bBox]; <> <> <> FOR children: LIST OF Slice _ GGParent.ListChildren[sliceD.slice, first], children.rest UNTIL children=NIL DO thisChild: Slice _ children.first; -- the next child, also possibly a cluster thisChildD: SliceDescriptor ¬ GGParent.ChildDescriptorFromDescriptor[sliceD, thisChild]; <> IF thisChildD=NIL OR GGSliceOps.IsEmptyParts[thisChildD] THEN <> newChildren ¬ GGUtility.AppendSliceList[newChildren, LIST[thisChild]] ELSE { <> newBox: BoundBox; newChildSlices: LIST OF Slice; [newChildSlices, newBox] ¬ DeleteSliceParts[thisChildD]; -- RECURSIVE CALL IF newChildSlices#NIL THEN { newChildren ¬ GGUtility.AppendSliceList[newChildren, newChildSlices]; GGBoundBox.EnlargeByBox[bBox: bBox, by: newBox]; }; }; ENDLOOP; <> IF newChildren=NIL THEN RETURN[NIL, bBox] ELSE { newCluster: Slice ¬ GGSlice.CreateCluster[frozen: GGSlice.GetFrozen[sliceD.slice]]; GGSlice.AddChildrenToCluster[newCluster, newChildren, -1]; RETURN[LIST[newCluster], bBox]; }; }; $Outline => { thisBox: BoundBox; childD: SliceDescriptor; outlineParts: SliceParts; original, outline: Slice; openTrajOutlines: LIST OF Slice; original ¬ sliceD.slice; outlineParts ¬ sliceD.parts; outline ¬ sliceD.slice; -- gets smaller with each iteration childD ¬ GGParent.FirstIncludedChild[outline, outlineParts, first]; UNTIL childD = NIL DO IF GGSliceOps.GetType[childD.slice] = $Traj THEN { IF GGSliceOps.IsCompleteParts[childD] THEN { outline ¬ GGOutline.ReplaceChild[outline, childD.slice, NIL]; } ELSE { [outline, openTrajOutlines] ¬ GGTraj.DeleteSequence[childD]; newSlices ¬ GGUtility.AppendSliceList[openTrajOutlines, newSlices]; }; } ELSE { -- $Box or $Circle screenStyle: BOOL ¬ TRUE; fillText: TextNode.Location; GGBoundBox.EnlargeByBox[bBox: bBox, by: GGSliceOps.GetBoundBox[outline]]; [fillText, screenStyle] ¬ GGSlice.GetBoxText[outline]; outline ¬ GGOutline.ReplaceChild[outline, childD.slice, NIL]; IF outline # NIL THEN GGOutline.SetFillText[outline, fillText.node, screenStyle, NIL]; }; IF outline = NIL THEN childD ¬ NIL ELSE { outlineParts ¬ GGSliceOps.RemakeSelections[outline, active]; childD ¬ GGParent.FirstIncludedChild[outline, outlineParts, first]; }; ENDLOOP; IF outline # NIL THEN newSlices ¬ CONS[outline, newSlices]; -- filled outline backmost thisBox ¬ BoundBoxOfSlices[newSlices]; GGBoundBox.EnlargeByBox[bBox: bBox, by: thisBox]; }; ENDCASE => { newSlices ¬ NIL; }; }; BoundBoxOfSlices: PROC [sliceList: LIST OF Slice] RETURNS [bBox: BoundBox] = { bBox ¬ GGBoundBox.NullBoundBox[]; FOR list: LIST OF Slice ¬ sliceList, list.rest UNTIL list = NIL DO GGBoundBox.EnlargeByBox[bBox: bBox, by: GGSliceOps.GetBoundBox[list.first, NIL]]; ENDLOOP; }; SaveSelections: PUBLIC PROC [scene: Scene] = { scene.savedSelected.normal ¬ GGUtility.CopySliceDescriptorList[scene.selected.normal]; scene.savedSelected.featureCycler ¬ scene.selected.featureCycler; }; RestoreSelections: PUBLIC PROC [scene: Scene] = { GGSelect.DeselectAll[scene, normal]; -- get rid of any transient selections FOR list: LIST OF SliceDescriptor ¬ scene.savedSelected.normal, list.rest UNTIL list = NIL DO GGSelect.SelectSlice[list.first, scene, normal]; ENDLOOP; scene.selected.featureCycler ¬ scene.savedSelected.featureCycler; }; SelectInBox: PUBLIC PROC [scene: Scene, box: BoundBox, selectClass: SelectionClass ¬ normal] = { <> inParts: SliceDescriptor; SelectSliceParts: PROC [slice: Slice] RETURNS [done: BOOL ¬ FALSE] = { inParts ¬ GGSliceOps.PartsInBoundBox[slice, box]; IF inParts # NIL THEN GGSelect.SelectSlice[inParts, scene, selectClass]; }; [] ¬ GGScene.WalkSlices[scene, first, SelectSliceParts]; }; <> <<>> <> IF scene.lock = $NotLocked THEN { scene.lock ¬ name; success ¬ TRUE; } ELSE { IF NOT wait THEN RETURN; UNTIL scene.lock = $NotLocked DO WAIT lockFree ENDLOOP; scene.lock ¬ name; success ¬ TRUE; }; }; UnLockScene: PUBLIC ENTRY PROC [scene: Scene] = { scene.lock ¬ $NotLocked; NOTIFY lockFree; }; >> END.