<> <> <> <> DIRECTORY GGAlign, GGBasicTypes, AtomButtons, GGCaret, GGError, GGGravity, GGInterfaceTypes, GGModelTypes, GGObjects, GGOutline, GGSegmentTypes, GGSelect, GGStatistics, GGVector, GList, Process, Rope; GGAlignImpl: CEDAR PROGRAM IMPORTS AtomButtons, GGCaret, GGError, GGGravity, GGObjects, GGOutline, GGSelect, GGStatistics, GGVector, GList, Process EXPORTS GGAlign = BEGIN AlignmentObject: TYPE = GGModelTypes.AlignmentObject; Angle: TYPE = GGBasicTypes.Angle; Caret: TYPE = GGInterfaceTypes.Caret; Circle: TYPE = GGBasicTypes.Circle; ControlPointGenerator: TYPE = GGModelTypes.ControlPointGenerator; Edge: TYPE = GGBasicTypes.Edge; EntityGenerator: TYPE = GGObjects.EntityGenerator; FeatureData: TYPE = REF FeatureDataObj; FeatureDataObj: TYPE = GGModelTypes.FeatureDataObj; FilterOutlineProc: TYPE = GGAlign.FilterOutlineProc; FilterSliceProc: TYPE = GGAlign.FilterSliceProc; Filters: TYPE = GGInterfaceTypes.Filters; GargoyleData: TYPE = GGInterfaceTypes.GargoyleData; Joint: TYPE = GGModelTypes.Joint; JointGenerator: TYPE = GGModelTypes.JointGenerator; Line: TYPE = GGBasicTypes.Line; ObjectBag: TYPE = GGInterfaceTypes.ObjectBag; Outline: TYPE = GGModelTypes.Outline; OutlineDescriptor: TYPE = REF OutlineDescriptorObj; OutlineDescriptorObj: TYPE = GGModelTypes.OutlineDescriptorObj; Point: TYPE = GGBasicTypes.Point; PointAndDone: TYPE = GGModelTypes.PointAndDone; PointFilterProc: TYPE = GGAlign.PointFilterProc; PointGenerator: TYPE = GGModelTypes.PointGenerator; PointPairGenerator: TYPE = GGModelTypes.PointPairGenerator; ScalarButtonClient: TYPE = AtomButtons.ScalarButtonClient; Scene: TYPE = GGModelTypes.Scene; Segment: TYPE = GGSegmentTypes.Segment; SegmentFilterProc: TYPE = GGAlign.SegmentFilterProc; SegmentGenerator: TYPE = GGModelTypes.SegmentGenerator; SelectionClass: TYPE = GGInterfaceTypes.SelectionClass; Sequence: TYPE = GGModelTypes.Sequence; SequenceGenerator: TYPE = GGModelTypes.SequenceGenerator; Slice: TYPE = GGModelTypes.Slice; SliceDescriptor: TYPE = REF SliceDescriptorObj; SliceDescriptorObj: TYPE = GGModelTypes.SliceDescriptorObj; SliceDescriptorGenerator: TYPE = GGModelTypes.SliceDescriptorGenerator; SliceGenerator: TYPE = GGModelTypes.SliceGenerator; SliceParts: TYPE = GGModelTypes.SliceParts; Traj: TYPE = GGModelTypes.Traj; TrajGenerator: TYPE = GGObjects.TrajGenerator; TriggerBag: TYPE = REF TriggerBagObj; TriggerBagObj: TYPE = GGInterfaceTypes.TriggerBagObj; Vector: TYPE = GGBasicTypes.Vector; Problem: SIGNAL [msg: Rope.ROPE] = GGError.Problem; <> <> <> <> <> <> <> <> <> <> <> <> <<>> EntityNotFound: PUBLIC SIGNAL = CODE; UnexpectedType: PUBLIC ERROR = CODE; BrokenInvariant: PUBLIC ERROR = CODE; << [Artwork node; type 'ArtworkInterpress on' to command tool] >> <<>> <> <<>> SetBagsForAction: PUBLIC PROC [gargoyleData: GargoyleData, atom: ATOM] = { <> <> <> <> currentTriggers: TriggerBag _ gargoyleData.hitTest.currentTriggerBag; currentObjects: ObjectBag _ gargoyleData.hitTest.currentObjectBag; sceneTriggers: TriggerBag _ gargoyleData.hitTest.sceneTriggerBag; IF gargoyleData.camera.hideAlignments THEN { GGGravity.FlushObjectBag[currentObjects]; SetSceneBagForAction[gargoyleData, atom]; } ELSE { GGStatistics.StartInterval[$SetBagsForAction, GGStatistics.GlobalTable[]]; <> FlushTriggerBag[currentTriggers]; AddAllHotSequences[gargoyleData, currentTriggers]; AddAllHotSlices[gargoyleData, currentTriggers]; AddAnchorTrigger[gargoyleData, currentTriggers]; AddHeuristics[gargoyleData, atom, currentTriggers]; <> RemoveMoving[gargoyleData.scene, atom, currentTriggers]; <> GGGravity.FlushObjectBag[currentObjects]; BuiltInFilters[currentTriggers, currentObjects, gargoyleData]; <> SetSceneBagForAction[gargoyleData, atom]; AddAllMidpoints[sceneTriggers, currentObjects, gargoyleData]; -- make all stationary segment midpoints into alignment objects GGStatistics.StopInterval[$SetBagsForAction, GGStatistics.GlobalTable[]]; }; }; << [Artwork node; type 'ArtworkInterpress on' to command tool] >> SetSceneBagForAction: PUBLIC PROC [gargoyleData: GargoyleData, atom: ATOM] = { sceneTriggers: TriggerBag _ gargoyleData.hitTest.sceneTriggerBag; <> FlushTriggerBag[sceneTriggers]; AddAllTrajectories[gargoyleData, sceneTriggers]; AddAllSlices[gargoyleData, sceneTriggers]; AddAnchor[gargoyleData, sceneTriggers]; SELECT atom FROM $CaretPos => NULL; -- nothing is about to move $Drag => RemoveMoving[gargoyleData.scene, $Drag, sceneTriggers]; ENDCASE => ERROR; ComputeAllBoundingBoxes[sceneTriggers]; }; SomeSelectedIsHot: PROC [scene: Scene] RETURNS [BOOL] = { entityGen: EntityGenerator; entityGen _ GGSelect.SelectedStuff[scene, normal]; FOR entity: REF ANY _ GGObjects.NextEntity[entityGen], GGObjects.NextEntity[entityGen] UNTIL entity = NIL DO WITH entity SELECT FROM sliceD: SliceDescriptor => IF GGSelect.IsSelectedInPart[sliceD.slice, scene, hot] THEN RETURN[TRUE]; outlineD: OutlineDescriptor => IF GGSelect.IsSelectedInPart[outlineD.slice, scene, hot] THEN RETURN[TRUE]; ENDCASE => ERROR; ENDLOOP; RETURN[FALSE]; }; UpdateBagsForAction: PUBLIC PROC [gargoyleData: GargoyleData, atom: ATOM] RETURNS [repaintNeeded: BOOL] = { <> <> currentTriggers: TriggerBag _ gargoyleData.hitTest.currentTriggerBag; currentObjects: ObjectBag _ gargoyleData.hitTest.currentObjectBag; sceneTriggers: TriggerBag _ gargoyleData.hitTest.sceneTriggerBag; IF atom # $Drag THEN ERROR; repaintNeeded _ SomeSelectedIsHot[gargoyleData.scene] OR AtomButtons.GetButtonState[gargoyleData.hitTest.heuristicsButton] = on; IF gargoyleData.camera.hideAlignments THEN { repaintNeeded _ FALSE; GGGravity.FlushObjectBag[currentObjects]; UpdateSceneBagForAction[gargoyleData.scene, sceneTriggers, atom]; } ELSE { GGStatistics.StartInterval[$UpdateBagsForAction, GGStatistics.GlobalTable[]]; <> AddHeuristics[gargoyleData, $Drag, currentTriggers]; RemoveMoving[gargoyleData.scene, $Drag, currentTriggers]; <> GGGravity.FlushObjectBag[currentObjects]; BuiltInFilters[currentTriggers, currentObjects, gargoyleData]; <> UpdateSceneBagForAction[gargoyleData.scene, sceneTriggers, $Drag]; AddAllMidpoints[sceneTriggers, currentObjects, gargoyleData]; -- make all stationary segment midpoints into alignment objects GGStatistics.StopInterval[$UpdateBagsForAction, GGStatistics.GlobalTable[]]; }; }; UpdateSceneBagForAction: PROC [scene: Scene, sceneTriggers: TriggerBag, atom: ATOM] = { SELECT atom FROM $CaretPos => NULL; -- nothing is about to move $Drag => RemoveMoving[scene, $Drag, sceneTriggers]; ENDCASE => ERROR; GGStatistics.StartInterval[$ComputeBoundBoxes, GGStatistics.GlobalTable[]]; ComputeAllBoundingBoxes[sceneTriggers]; GGStatistics.StopInterval[$ComputeBoundBoxes, GGStatistics.GlobalTable[]]; }; ReplaceObsoleteOutlineTrigger: PUBLIC PROC [gargoyleData: GargoyleData, oldOutline: Outline, newOutline: Outline] = { currentTriggers: TriggerBag _ gargoyleData.hitTest.currentTriggerBag; currentObjects: ObjectBag _ gargoyleData.hitTest.currentObjectBag; sceneTriggers: TriggerBag _ gargoyleData.hitTest.sceneTriggerBag; feature: FeatureData; hotD: OutlineDescriptor; notFound: BOOL; <> notFound _ FALSE; UNTIL notFound DO [sceneTriggers.outlines, notFound] _ DeleteOutline[oldOutline, sceneTriggers.outlines]; ENDLOOP; notFound _ FALSE; UNTIL notFound DO [currentTriggers.outlines, notFound] _ DeleteOutline[oldOutline, currentTriggers.outlines]; ENDLOOP; <> <> hotD _ GGSelect.FindSelectedOutline[newOutline, gargoyleData.scene, hot]; IF hotD # NIL THEN { feature _ GGGravity.FeatureFromOutline[hotD.slice, GGOutline.CopyParts[hotD.slice, hotD.parts]]; AddFeature[feature, currentTriggers]; }; <> feature _ GGGravity.FeatureFromOutline[newOutline]; AddFeature[feature, sceneTriggers]; }; BuiltInFilters: PUBLIC PROC [triggerBag: TriggerBag, objectBag: ObjectBag, gargoyleData: GargoyleData] = { <> anchorFeature: FeatureData; sliceD: SliceDescriptor; outlineD: OutlineDescriptor; pointGen: PointGenerator; pointPairGen: PointPairGenerator; outPointGen: GGModelTypes.OutlinePointGenerator; outPointPairGen: GGModelTypes.OutlinePointPairGenerator; index: NAT; filters: Filters _ gargoyleData.hitTest; GGStatistics.StartInterval[$BuiltInFilters, GGStatistics.GlobalTable[]]; IF gargoyleData.camera.hideAlignments THEN { GGGravity.FlushObjectBag[objectBag]; RETURN; }; <> anchorFeature _ triggerBag.anchor; IF anchorFeature # NIL AND GGCaret.Exists[NARROW[anchorFeature.shape, Caret]] THEN { point: Point; point _ GGCaret.GetPoint[NARROW[anchorFeature.shape, Caret]]; [] _ JointFireRule[point, filters, objectBag]; }; <> FOR l: LIST OF FeatureData _ triggerBag.outlines, l.rest UNTIL l = NIL DO outlineD _ NARROW[l.first.shape]; outPointGen _ outlineD.slice.class.pointsInDescriptor[outlineD]; FOR next: GGModelTypes.PointAndDone _ outlineD.slice.class.nextPoint[outPointGen], outlineD.slice.class.nextPoint[outPointGen] UNTIL next.done DO [] _ JointFireRule[next.point, filters, objectBag]; ENDLOOP; outPointPairGen _ outlineD.slice.class.pointPairsInDescriptor[outlineD]; index _ 0; FOR next: GGModelTypes.PointPairAndDone _ outlineD.slice.class.nextPointPair[outPointPairGen], outlineD.slice.class.nextPointPair[outPointPairGen] UNTIL next.done DO [] _ SegmentFireRule[index, next.lo, next.hi, filters, objectBag]; index _ index + 1; ENDLOOP; ENDLOOP; <> FOR l: LIST OF FeatureData _ triggerBag.slices, l.rest UNTIL l = NIL DO sliceD _ NARROW[l.first.shape]; pointGen _ sliceD.slice.class.pointsInDescriptor[sliceD]; FOR next: GGModelTypes.PointAndDone _ sliceD.slice.class.nextPoint[pointGen], sliceD.slice.class.nextPoint[pointGen] UNTIL next.done DO [] _ JointFireRule[next.point, filters, objectBag]; ENDLOOP; pointPairGen _ sliceD.slice.class.pointPairsInDescriptor[sliceD]; index _ 0; FOR next: GGModelTypes.PointPairAndDone _ sliceD.slice.class.nextPointPair[pointPairGen], sliceD.slice.class.nextPointPair[pointPairGen] UNTIL next.done DO [] _ SegmentFireRule[9999, next.lo, next.hi, filters, objectBag]; index _ index + 1; ENDLOOP; ENDLOOP; GGStatistics.StopInterval[$BuiltInFilters, GGStatistics.GlobalTable[]]; }; <<>> IncrementalFilters: PUBLIC PROC [trigger: FeatureData, objectBag: ObjectBag, gargoyleData: GargoyleData] RETURNS [alignObjects: LIST OF FeatureData] = { <> filters: Filters _ gargoyleData.hitTest; alignObjects _ NIL; IF gargoyleData.camera.hideAlignments THEN { GGGravity.FlushObjectBag[objectBag]; RETURN; }; WITH trigger.shape SELECT FROM anchor: Caret => { <> IF GGCaret.Exists[anchor] THEN { point: Point _ GGCaret.GetPoint[anchor]; [] _ JointFireRule[point, filters, objectBag]; }; }; outlineD: OutlineDescriptor => { pointGen: GGModelTypes.OutlinePointGenerator; pointPairGen: GGModelTypes.OutlinePointPairGenerator; <> pointGen _ outlineD.slice.class.pointsInDescriptor[outlineD]; FOR next: GGModelTypes.PointAndDone _ outlineD.slice.class.nextPoint[pointGen], outlineD.slice.class.nextPoint[pointGen] UNTIL next.done DO alignObjects _ NARROW[GList.Nconc[JointFireRule[next.point, filters, objectBag], alignObjects]]; ENDLOOP; pointPairGen _ outlineD.slice.class.pointPairsInDescriptor[outlineD]; FOR next: GGModelTypes.PointPairAndDone _ outlineD.slice.class.nextPointPair[pointPairGen], outlineD.slice.class.nextPointPair[pointPairGen] UNTIL next.done DO alignObjects _ NARROW[GList.Nconc[SegmentFireRule[9999, next.lo, next.hi, filters, objectBag], alignObjects]]; ENDLOOP; }; sliceD: SliceDescriptor => { pointGen: PointGenerator; pointPairGen: PointPairGenerator; <> pointGen _ sliceD.slice.class.pointsInDescriptor[sliceD]; FOR next: GGModelTypes.PointAndDone _ sliceD.slice.class.nextPoint[pointGen], sliceD.slice.class.nextPoint[pointGen] UNTIL next.done DO alignObjects _ NARROW[GList.Nconc[JointFireRule[next.point, filters, objectBag], alignObjects]]; ENDLOOP; pointPairGen _ sliceD.slice.class.pointPairsInDescriptor[sliceD]; FOR next: GGModelTypes.PointPairAndDone _ sliceD.slice.class.nextPointPair[pointPairGen], sliceD.slice.class.nextPointPair[pointPairGen] UNTIL next.done DO alignObjects _ NARROW[GList.Nconc[SegmentFireRule[9999, next.lo, next.hi, filters, objectBag], alignObjects]]; ENDLOOP; }; ENDCASE => ERROR; }; <<>> <<(1) Put Hot Objects in the Trigger Bag.>> AddAllHotSequences: PROC [gargoyleData: GargoyleData, triggerBag: TriggerBag] = { feature: FeatureData; copy: OutlineDescriptor; outDGen: GGModelTypes.OutlineDescriptorGenerator; outDGen _ GGSelect.SelectedOutlines[gargoyleData.scene, hot]; FOR outlineD: OutlineDescriptor _ GGSelect.NextOutlineDescriptor[outDGen], GGSelect.NextOutlineDescriptor[outDGen] UNTIL outlineD = NIL DO copy _ NEW[OutlineDescriptorObj _ [outlineD.slice, GGOutline.CopyParts[outlineD.slice, outlineD.parts]]]; feature _ GGGravity.FeatureFromOutline[copy.slice, copy.parts]; AddFeature[feature, triggerBag]; ENDLOOP; }; AddAllHotSlices: PROC [gargoyleData: GargoyleData, triggerBag: TriggerBag] = { feature: FeatureData; sliceDescGen: SliceDescriptorGenerator _ GGSelect.SelectedSlices[gargoyleData.scene, hot]; FOR sliceD: SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen] UNTIL sliceD = NIL DO feature _ GGGravity.FeatureFromSlice[sliceD.slice, sliceD.parts]; AddFeature[feature, triggerBag]; ENDLOOP; }; <<(2) Put More Objects in the Trigger Bag using Heuristics.>> AddHeuristics: PROC [gargoyleData: GargoyleData, atom: ATOM, triggerBag: TriggerBag] = { <> feature: FeatureData; IF AtomButtons.GetButtonState[gargoyleData.hitTest.heuristicsButton] = off THEN RETURN; SELECT atom FROM $Drag => { outDGen: GGModelTypes.OutlineDescriptorGenerator; outDGen _ GGSelect.SelectedOutlines[gargoyleData.scene, normal]; FOR outlineD: OutlineDescriptor _ GGSelect.NextOutlineDescriptor[outDGen], GGSelect.NextOutlineDescriptor[outDGen] UNTIL outlineD = NIL DO feature _ GGGravity.FeatureFromOutline[outlineD.slice, NIL]; AddFeature[feature, triggerBag]; ENDLOOP; }; $CaretPos => NULL; ENDCASE => ERROR UnexpectedType; }; <<(3) Removing any Triggers which are MOVING.>> RemoveMoving: PROC [scene: Scene, atom: ATOM, triggerBag: TriggerBag] = { <> <> <> NotSelectedOrDanglingSeq: FilterOutlineProc = { -- PROC [outlineD: OutlineDescriptor]; <> <> <> outlineD.parts _ outlineD.slice.class.fixedParts[outlineD.slice, outlineD.parts, GGObjects.GetSelections[scene, normal]]; }; NotSelectedSlice: FilterSliceProc = { -- PROC [sliceD: SliceDescriptor] <> selSliceD: SliceDescriptor _ GGSelect.FindSelectedSlice[sliceD.slice, scene, normal]; IF selSliceD=NIL THEN RETURN; -- nothing to trim sliceD.parts _ sliceD.slice.class.fixedParts[selSliceD.slice, selSliceD.parts, GGObjects.GetSelections[scene, normal]]; }; SELECT atom FROM $Drag => DeleteTriggersFilter[triggerBag, NotSelectedOrDanglingSeq, NotSelectedSlice]; $CaretPos, $DragStartUp => NULL; ENDCASE => ERROR; }; DeleteTriggersFilter: PUBLIC PROC [triggerBag: TriggerBag, filterOutlineProc: FilterOutlineProc, filterSliceProc: FilterSliceProc] = { <> FOR outList: LIST OF FeatureData _ triggerBag.outlines, outList.rest UNTIL outList = NIL DO outlineD: OutlineDescriptor _ NARROW[outList.first.shape]; filterOutlineProc[outlineD]; -- bashes those parts of outlineD which should not remain IF outlineD.slice.class.emptyParts[outlineD.slice, outlineD.parts] THEN { triggerBag.outlines _ DeleteOutlineD[outlineD, triggerBag.outlines]; }; ENDLOOP; FOR sliceList: LIST OF FeatureData _ triggerBag.slices, sliceList.rest UNTIL sliceList = NIL DO sliceD: SliceDescriptor _ NARROW[sliceList.first.shape]; filterSliceProc[sliceD]; -- bashes those parts of slice which should not remain IF sliceD.slice.class.emptyParts[sliceD.slice, sliceD.parts] THEN triggerBag.slices _ DeleteSlice[sliceD.slice, triggerBag.slices]; ENDLOOP; }; <<>> <<(4) For Each Trigger, Construct the Appropriate Alignment Objects>> JointFireRule: PROC [point: Point, filters: Filters, objectBag: ObjectBag] RETURNS [alignObjects: LIST OF FeatureData] = { firstButton: ScalarButtonClient; feature: FeatureData; alignObjects _ NIL; <> firstButton _ filters.slopeHeader.scalarButtons; FOR thisButton: ScalarButtonClient _ firstButton, thisButton.next UNTIL thisButton = NIL DO IF thisButton.on THEN { feature _ GGGravity.JointAddSlopeLine[ degrees: thisButton.value, direction: GGVector.VectorFromAngle[thisButton.value], point: point, objectBag: objectBag ]; IF feature # NIL THEN alignObjects _ CONS[feature, alignObjects]; }; ENDLOOP; <> firstButton _ filters.radiusHeader.scalarButtons; FOR thisButton: ScalarButtonClient _ firstButton, thisButton.next UNTIL thisButton = NIL DO IF thisButton.on THEN { feature _ GGGravity.JointAddCircle[ radius: thisButton.value*filters.scaleUnit, point: point, objectBag: objectBag ]; IF feature # NIL THEN alignObjects _ CONS[feature, alignObjects]; }; ENDLOOP; }; SegmentFireRule: PROC [segNum: NAT, lo, hi: Point, filters: Filters, objectBag: ObjectBag] RETURNS [alignObjects: LIST OF FeatureData] = { firstButton: ScalarButtonClient; line1, line2: FeatureData; alignObjects _ NIL; <> firstButton _ filters.distanceHeader.scalarButtons; FOR thisButton: ScalarButtonClient _ firstButton, thisButton.next UNTIL thisButton = NIL DO IF thisButton.on THEN { [line1, line2] _ GGGravity.SegmentAddDistanceLines[ distance: thisButton.value*filters.scaleUnit, segNum: segNum, lo: lo, hi: hi, objectBag: objectBag]; IF line1 # NIL THEN alignObjects _ CONS[line1, alignObjects]; IF line2 # NIL THEN alignObjects _ CONS[line2, alignObjects]; }; ENDLOOP; <> firstButton _ filters.angleHeader.scalarButtons; FOR thisButton: ScalarButtonClient _ firstButton, thisButton.next UNTIL thisButton = NIL DO IF thisButton.on THEN { [line1, line2] _ GGGravity.SegmentAddTwoAngleLines[degrees: thisButton.value, segNum: segNum, lo: lo, hi: hi, objectBag: objectBag]; IF line1 # NIL THEN alignObjects _ CONS[line1, alignObjects]; IF line2 # NIL THEN alignObjects _ CONS[line2, alignObjects]; }; ENDLOOP; }; <<(4') Hooks for the future when users can write their own alignment line generators.>> <> <> <> <> <> <> <> <> <> <> <> <> < {>> <> <<};>> < {>> <> <<};>> < ERROR;>> <> <> <> <<};>> <<>> <> <> <> <> <> <> <> <> <> <> < {>> <> <<};>> < {>> <> <<};>> < ERROR;>> <> <> <> <<};>> <<>> <> <<>> CreateTriggerBag: PUBLIC PROC [] RETURNS [triggerBag: TriggerBag] = { triggerBag _ NEW[TriggerBagObj _ [outlines: NIL, slices: NIL, intersectionPoints: NIL, anchor: NIL]]; }; FlushTriggerBag: PROC [triggerBag: TriggerBag] = { triggerBag.outlines _ NIL; triggerBag.slices _ NIL; triggerBag.intersectionPoints _ NIL; triggerBag.anchor _ NIL; }; CopyTriggerBag: PUBLIC PROC [triggerBag: TriggerBag] RETURNS [copy: TriggerBag] = { <> finger: LIST OF FeatureData; outlineD: OutlineDescriptor; copyD: OutlineDescriptor; copy _ NEW[TriggerBagObj _ [outlines: NIL]]; IF triggerBag.outlines = NIL THEN RETURN; outlineD _ NARROW[triggerBag.outlines.first.shape]; copyD _ NEW[OutlineDescriptorObj _ [outlineD.slice, GGOutline.CopyParts[outlineD.slice, outlineD.parts]]]; finger _ copy.outlines _ LIST[GGGravity.FeatureFromOutline[copyD.slice, copyD.parts]]; FOR oldOutList: LIST OF FeatureData _ triggerBag.outlines.rest, oldOutList.rest UNTIL oldOutList = NIL DO outlineD _ NARROW[oldOutList.first.shape]; copyD _ NEW[OutlineDescriptorObj _ [outlineD.slice, GGOutline.CopyParts[outlineD.slice, outlineD.parts]]]; finger.rest _ CONS[GGGravity.FeatureFromOutline[copyD.slice, copyD.parts], NIL]; finger _ finger.rest; ENDLOOP; }; PutBagInBag: PROC [from: TriggerBag, to: TriggerBag] = { <> <> outs, outStart: LIST OF FeatureData; slices, sliceStart: LIST OF FeatureData; outs _ outStart _ from.outlines; UNTIL outs.rest = NIL DO outs _ outs.rest ENDLOOP; outs.rest _ to.outlines; to.outlines _ from.outlines; slices _ sliceStart _ from.slices; UNTIL slices.rest = NIL DO slices _ slices.rest ENDLOOP; slices.rest _ to.slices; to.slices _ from.slices; }; AddOutlineTrigger: PROC [outlineD: OutlineDescriptor, triggerBag: TriggerBag] = { [] _ CreateOutlineTrigger[outlineD, triggerBag]; }; CreateOutlineTrigger: PUBLIC PROC [outlineD: OutlineDescriptor, triggerBag: TriggerBag] RETURNS [feature: FeatureData] = { oldD, copy: OutlineDescriptor; unionParts: SliceParts; oldD _ FindOldOutlineD[outlineD.slice, triggerBag.outlines]; IF oldD = NIL THEN { copy _ NEW[OutlineDescriptorObj _ [outlineD.slice, GGOutline.CopyParts[outlineD.slice, outlineD.parts]]]; feature _ GGGravity.FeatureFromOutline[copy.slice, copy.parts]; triggerBag.outlines _ AppendFeature[feature, triggerBag.outlines]; } ELSE { unionParts _ outlineD.slice.class.unionParts[outlineD.slice, oldD.parts, outlineD.parts]; triggerBag.outlines _ DeleteOutlineD[oldD, triggerBag.outlines]; feature _ GGGravity.FeatureFromOutline[outlineD.slice, unionParts]; triggerBag.outlines _ AppendFeature[feature, triggerBag.outlines]; }; }; AddSliceTrigger: PROC [slice: Slice, triggerBag: TriggerBag] = { allParts: SliceParts _ slice.class.newParts[slice, NIL, slice]; sliceD: SliceDescriptor _ NEW[SliceDescriptorObj _ [slice: slice, parts: allParts]]; [] _ CreateSliceTrigger[sliceD, triggerBag]; }; CreateSliceTrigger: PUBLIC PROC [sliceD: SliceDescriptor, triggerBag: TriggerBag] RETURNS [feature: FeatureData] = { oldD: SliceDescriptor; unionParts: SliceParts; oldD _ FindOldSlice[sliceD.slice, triggerBag.slices]; IF oldD = NIL THEN { <> feature _ GGGravity.FeatureFromSlice[sliceD.slice, sliceD.parts]; triggerBag.slices _ AppendFeature[feature, triggerBag.slices]; } ELSE { unionParts _ sliceD.slice.class.unionParts[sliceD.slice, oldD.parts, sliceD.parts]; triggerBag.slices _ DeleteSlice[oldD.slice, triggerBag.slices]; feature _ GGGravity.FeatureFromSlice[sliceD.slice, unionParts]; triggerBag.slices _ AppendFeature[feature, triggerBag.slices]; }; }; AddAnchorTrigger: PROC [gargoyleData: GargoyleData, triggerBag: TriggerBag] = { [] _ CreateAnchorTrigger[gargoyleData.anchor, triggerBag]; }; CreateAnchorTrigger: PUBLIC PROC [anchor: Caret, triggerBag: TriggerBag] RETURNS [feature: FeatureData] = { feature _ GGGravity.FeatureFromAnchor[anchor]; AddFeature[feature, triggerBag]; }; DeleteAnchorTrigger: PUBLIC PROC [triggerBag: TriggerBag] = { triggerBag.anchor _ NIL; }; DeleteOutlineTrigger: PROC [outlineD: OutlineDescriptor, triggerBag: TriggerBag] = { oldOut: OutlineDescriptor; diff: SliceParts; feature: FeatureData; oldOut _ FindOldOutlineD[outlineD.slice, triggerBag.outlines]; IF oldOut#NIL THEN { diff _ outlineD.slice.class.differenceParts[outlineD.slice, oldOut.parts, outlineD.parts]; triggerBag.outlines _ DeleteOutlineD[oldOut, triggerBag.outlines]; IF NOT outlineD.slice.class.emptyParts[outlineD.slice, diff] THEN { feature _ GGGravity.FeatureFromOutline[outlineD.slice, diff]; triggerBag.outlines _ AppendFeature[feature, triggerBag.outlines]; }; }; }; DeleteSliceTrigger: PUBLIC PROC [slice: Slice, triggerBag: TriggerBag] = { oldSliceD: SliceDescriptor; oldSliceD _ FindOldSlice[slice, triggerBag.slices]; IF oldSliceD#NIL THEN triggerBag.slices _ DeleteSlice[oldSliceD.slice, triggerBag.slices]; }; <> <<>> TAddTriggersFilter: PUBLIC PROC [triggerBag: TriggerBag, filterOutlineProc: FilterOutlineProc, filterSliceProc: FilterSliceProc, gargoyleData: GargoyleData] = {ERROR;}; <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <<};>> <<>> <> <<>> AddFeature: PROC [featureData: FeatureData, triggerBag: TriggerBag] = { Process.CheckForAbort[]; SELECT featureData.type FROM outline => { triggerBag.outlines _ CONS[featureData, triggerBag.outlines]; }; slice => { triggerBag.slices _ CONS[featureData, triggerBag.slices]; }; anchor => { triggerBag.anchor _ featureData; }; ENDCASE => ERROR; }; AddAllTrajectories: PROC [gargoyleData: GargoyleData, triggerBag: TriggerBag] = { outlineGen: GGModelTypes.OutlineGenerator; feature: FeatureData; outlineGen _ GGObjects.OutlinesInScene[gargoyleData.scene]; FOR outline: Outline _ GGObjects.NextOutline[outlineGen], GGObjects.NextOutline[outlineGen] UNTIL outline = NIL DO feature _ GGGravity.FeatureFromOutline[outline]; AddFeature[feature, triggerBag]; ENDLOOP; }; AddAllSlices: PROC [gargoyleData: GargoyleData, triggerBag: TriggerBag] = { feature: FeatureData; sliceGen: SliceGenerator _ GGObjects.SlicesInScene[gargoyleData.scene]; FOR slice: Slice _ GGObjects.NextSlice[sliceGen], GGObjects.NextSlice[sliceGen] UNTIL slice = NIL DO feature _ GGGravity.FeatureFromSlice[slice]; AddFeature[feature, triggerBag]; ENDLOOP; }; AddAnchor: PROC [gargoyleData: GargoyleData, triggerBag: TriggerBag] = { feature: FeatureData; IF GGCaret.Exists[gargoyleData.anchor] THEN { feature _ GGGravity.FeatureFromAnchor[gargoyleData.anchor]; AddFeature[feature, triggerBag]; }; }; AddAllMidpoints: PUBLIC PROC [sceneTriggers: TriggerBag, objectBag: ObjectBag, gargoyleData: GargoyleData] = { <> SegmentMidpointRule: PROC [segNum: NAT, lo, hi: Point] = { [] _ GGGravity.SegmentAddMidpoint[segNum: segNum, lo: lo, hi: hi, objectBag: objectBag]; }; sliceD: SliceDescriptor; outlineD: OutlineDescriptor; pointPairGen: PointPairGenerator; outPointPairGen: GGModelTypes.OutlinePointPairGenerator; objectBag.midpoints _ NIL; IF gargoyleData.hitTest.midpointButton.state = off THEN RETURN; <> FOR l: LIST OF FeatureData _ sceneTriggers.outlines, l.rest UNTIL l = NIL DO pairCount: NAT _ 0; outlineD _ NARROW[l.first.shape]; outPointPairGen _ outlineD.slice.class.pointPairsInDescriptor[outlineD]; FOR next: GGModelTypes.PointPairAndDone _ outlineD.slice.class.nextPointPair[outPointPairGen], outlineD.slice.class.nextPointPair[outPointPairGen] UNTIL next.done DO SegmentMidpointRule[pairCount, next.lo, next.hi]; pairCount _ pairCount+1; ENDLOOP; ENDLOOP; <> FOR l: LIST OF FeatureData _ sceneTriggers.slices, l.rest UNTIL l = NIL DO pairCount: NAT _ 0; sliceD _ NARROW[l.first.shape]; pointPairGen _ sliceD.slice.class.pointPairsInDescriptor[sliceD]; FOR next: GGModelTypes.PointPairAndDone _ sliceD.slice.class.nextPointPair[pointPairGen], sliceD.slice.class.nextPointPair[pointPairGen] UNTIL next.done DO SegmentMidpointRule[pairCount, next.lo, next.hi]; pairCount _ pairCount+1; ENDLOOP; ENDLOOP; }; <> <<>> FindOldOutlineD: PROC [outline: Outline, list: LIST OF FeatureData] RETURNS [oldSliceD: OutlineDescriptor] = { FOR l: LIST OF FeatureData _ list, l.rest UNTIL l = NIL DO IF NARROW[l.first.shape, OutlineDescriptor].slice = outline THEN RETURN [NARROW[l.first.shape]]; ENDLOOP; RETURN [NIL]; }; FindOldSlice: PROC [slice: Slice, list: LIST OF FeatureData] RETURNS [oldSliceD: SliceDescriptor] = { FOR l: LIST OF FeatureData _ list, l.rest UNTIL l = NIL DO IF NARROW[l.first.shape, SliceDescriptor].slice = slice THEN RETURN [NARROW[l.first.shape]]; ENDLOOP; RETURN [NIL]; }; FindSequenceAndNeighbors: PROC [entity: Sequence, entityList: LIST OF FeatureData] RETURNS [beforeEnt, ent, afterEnt: LIST OF FeatureData] = { lastE: LIST OF FeatureData _ NIL; eList: LIST OF FeatureData _ entityList; IF eList = NIL THEN ERROR EntityNotFound; UNTIL eList = NIL DO IF NARROW[eList.first.shape, Sequence] = entity THEN { beforeEnt _ lastE; ent _ eList; afterEnt _ eList.rest; RETURN}; lastE _ eList; eList _ eList.rest; ENDLOOP; SIGNAL EntityNotFound; }; FindOutlineAndNeighbors: PROC [entity: Outline, entityList: LIST OF FeatureData] RETURNS [beforeEnt, ent, afterEnt: LIST OF FeatureData] = { lastE: LIST OF FeatureData _ NIL; eList: LIST OF FeatureData _ entityList; IF eList = NIL THEN ERROR EntityNotFound; UNTIL eList = NIL DO IF NARROW[eList.first.shape, OutlineDescriptor].slice = entity THEN { beforeEnt _ lastE; ent _ eList; afterEnt _ eList.rest; RETURN}; lastE _ eList; eList _ eList.rest; ENDLOOP; SIGNAL EntityNotFound; }; FindOutlineDAndNeighbors: PROC [entity: Outline, entityList: LIST OF FeatureData] RETURNS [beforeEnt, ent, afterEnt: LIST OF FeatureData] = { lastE: LIST OF FeatureData _ NIL; eList: LIST OF FeatureData _ entityList; IF eList = NIL THEN ERROR EntityNotFound; UNTIL eList = NIL DO IF NARROW[eList.first.shape, OutlineDescriptor].slice = entity THEN { beforeEnt _ lastE; ent _ eList; afterEnt _ eList.rest; RETURN}; lastE _ eList; eList _ eList.rest; ENDLOOP; SIGNAL EntityNotFound; }; FindSliceAndNeighbors: PROC [entity: Slice, entityList: LIST OF FeatureData] RETURNS [beforeEnt, ent, afterEnt: LIST OF FeatureData] = { lastE: LIST OF FeatureData _ NIL; eList: LIST OF FeatureData _ entityList; IF eList = NIL THEN ERROR EntityNotFound; UNTIL eList = NIL DO IF NARROW[eList.first.shape, SliceDescriptor].slice = entity THEN { beforeEnt _ lastE; ent _ eList; afterEnt _ eList.rest; RETURN}; lastE _ eList; eList _ eList.rest; ENDLOOP; SIGNAL EntityNotFound; }; DeleteOutline: PROC [outline: Outline, featureList: LIST OF FeatureData] RETURNS [smallerList: LIST OF FeatureData, notFound: BOOL _ FALSE] = { before, at, after: LIST OF FeatureData; IF featureList = NIL THEN RETURN[NIL, TRUE]; [before, at, after] _ FindOutlineAndNeighbors[outline, featureList ! EntityNotFound => {notFound _ TRUE; CONTINUE}]; IF notFound THEN RETURN[featureList, TRUE]; IF before = NIL THEN smallerList _ after ELSE { before.rest _ after; smallerList _ featureList; }; }; DeleteOutlineD: PROC [outlineD: OutlineDescriptor, featureList: LIST OF FeatureData] RETURNS [smallerList: LIST OF FeatureData] = { before, at, after: LIST OF FeatureData; notFound: BOOL _ FALSE; IF featureList = NIL THEN RETURN[NIL]; [before, at, after] _ FindOutlineDAndNeighbors[outlineD.slice, featureList ! EntityNotFound => {notFound _ TRUE; CONTINUE}]; IF notFound THEN RETURN[featureList]; IF before = NIL THEN smallerList _ after ELSE { before.rest _ after; smallerList _ featureList; }; }; DeleteSlice: PROC [slice: Slice, featureList: LIST OF FeatureData] RETURNS [smallerList: LIST OF FeatureData] = { before, at, after: LIST OF FeatureData; notFound: BOOL _ FALSE; [before, at, after] _ FindSliceAndNeighbors[slice, featureList]; IF notFound THEN RETURN[featureList]; IF before = NIL THEN smallerList _ after ELSE { before.rest _ after; smallerList _ featureList; }; }; -- end of DeleteSlice AppendFeature: PUBLIC PROC [feature: FeatureData, featureList: LIST OF FeatureData] RETURNS [biggerList: LIST OF FeatureData] = { Process.CheckForAbort[]; biggerList _ CONS[feature, featureList]; }; <> <<>> ComputeAllBoundingBoxes: PROC [sceneTriggers: TriggerBag] = { featureData: FeatureData; outlineD: OutlineDescriptor; FOR list: LIST OF FeatureData _ sceneTriggers.outlines, list.rest UNTIL list = NIL DO featureData _ list.first; outlineD _ NARROW[featureData.shape]; GGOutline.UpdateDescriptorBoundBoxes[outlineD: outlineD]; ENDLOOP; }; InitStats: PROC [] = { boundBoxes, bags: GGStatistics.Interval; boundBoxes _ GGStatistics.CreateInterval[$ComputeBoundBoxes]; bags _ GGStatistics.CreateInterval[$UpdateBagsForAction, LIST[boundBoxes]]; GGStatistics.AddInterval[bags, GGStatistics.GlobalTable[]]; bags _ GGStatistics.CreateInterval[$SetBagsForAction, NIL]; GGStatistics.AddInterval[bags, GGStatistics.GlobalTable[]]; bags _ GGStatistics.CreateInterval[$BuiltInFilters, NIL]; GGStatistics.AddInterval[bags, GGStatistics.GlobalTable[]]; }; emptyTriggerBag: PUBLIC TriggerBag; Init: PROC [] = { emptyTriggerBag _ CreateTriggerBag[]; }; InitStats[]; Init[]; <<>> END.