<> <> <> <> <> <> <> <<>> DIRECTORY GGBasicTypes, GGBoundBox, GGInterfaceTypes, GGModelTypes, GGOutline, GGParent, GGScene, GGSceneType, GGSegmentTypes, GGSelect, GGSlice, GGSliceOps, GGTraj, GGUtility, Imager, ImagerPath, Rope, TextNode; GGSceneImpl: CEDAR MONITOR IMPORTS GGBoundBox, GGParent, GGScene, GGOutline, GGSelect, GGSlice, GGSliceOps, GGTraj, GGUtility EXPORTS GGScene, GGModelTypes = BEGIN BoundBox: TYPE = GGModelTypes.BoundBox; BoundBoxGenerator: TYPE = GGModelTypes.BoundBoxGenerator; BoundBoxGeneratorObj: TYPE = GGModelTypes.BoundBoxGeneratorObj; Camera: TYPE = GGModelTypes.Camera; CameraObj: TYPE = GGModelTypes.CameraObj; 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] ]; }; 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; }; <> <<>> 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]]; 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 => { DoDeleteParts: PROC [childD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { affectedBox: BoundBox; IF GGSliceOps.IsCompleteParts[childD] THEN { smallerCluster: Slice; affectedBox _ GGSliceOps.GetBoundBox[childD.slice, NIL]; smallerCluster _ GGSlice.RemoveChild[sliceD.slice, childD.slice]; -- fast case } ELSE { newSlices: LIST OF Slice; priority: INT _ GGParent.GetChildPriority[sliceD.slice, childD.slice]; [newSlices, affectedBox] _ DeleteSliceParts[childD]; [] _ GGSlice.RemoveChild[sliceD.slice, childD.slice]; GGSlice.AddChildrenToCluster[sliceD.slice, newSlices, priority]; }; GGBoundBox.EnlargeByBox[bBox, affectedBox]; }; IF GGSliceOps.IsCompleteParts[sliceD] THEN RETURN[NIL, bBox]; <> [] _ GGParent.WalkIncludedChildren[sliceD.slice, sliceD.parts, first, DoDeleteParts]; newSlices _ LIST[sliceD.slice]; }; $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.