DIRECTORY AtomButtons, CodeTimer, Feedback, GGAlign, GGBasicTypes, GGCaret, GGGravity, GGInterfaceTypes, GGModelTypes, GGScene, GGOutline, GGSegmentTypes, GGSelect, GGSequence, GGSlice, GGState, GGTraj, GList, Imager, Process, Rope, Vectors2d; GGAlignImpl: CEDAR PROGRAM IMPORTS CodeTimer, Feedback, GGCaret, GGGravity, GGScene, GGOutline, GGSelect, GGSequence, GGSlice, GGState, GGTraj, GList, Process, Vectors2d EXPORTS GGAlign = BEGIN AlignmentLine: TYPE = GGInterfaceTypes.AlignmentLine; 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 = GGScene.EntityGenerator; FeatureData: TYPE = REF FeatureDataObj; FeatureDataObj: TYPE = GGModelTypes.FeatureDataObj; FilterOutlineProc: TYPE = GGAlign.FilterOutlineProc; FilterSliceProc: TYPE = GGAlign.FilterSliceProc; Filters: TYPE = GGInterfaceTypes.Filters; GGData: TYPE = GGInterfaceTypes.GGData; Joint: TYPE = GGModelTypes.Joint; JointGenerator: TYPE = GGModelTypes.JointGenerator; Line: TYPE = GGBasicTypes.Line; AlignBag: TYPE = GGInterfaceTypes.AlignBag; AlignBagObj: TYPE = GGInterfaceTypes.AlignBagObj; Outline: TYPE = GGModelTypes.Outline; OutlineData: TYPE = GGOutline.OutlineData; OutlineDescriptor: TYPE = REF OutlineDescriptorObj; OutlineDescriptorObj: TYPE = GGModelTypes.OutlineDescriptorObj; Point: TYPE = GGBasicTypes.Point; PointAndDone: TYPE = GGModelTypes.PointAndDone; PointGenerator: TYPE = GGModelTypes.PointGenerator; PointPairGenerator: TYPE = GGModelTypes.PointPairGenerator; ScalarButtonClient: TYPE = AtomButtons.ScalarButtonClient; Scene: TYPE = GGModelTypes.Scene; Segment: TYPE = GGSegmentTypes.Segment; 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; TrajEnd: TYPE = GGModelTypes.TrajEnd; TrajGenerator: TYPE = GGScene.TrajGenerator; TriggerBag: TYPE = REF TriggerBagObj; TriggerBagObj: TYPE = GGInterfaceTypes.TriggerBagObj; Vector: TYPE = GGBasicTypes.Vector; Problem: SIGNAL [msg: Rope.ROPE] = Feedback.Problem; EntityNotFound: PUBLIC SIGNAL = CODE; UnexpectedType: PUBLIC ERROR = CODE; BrokenInvariant: PUBLIC ERROR = CODE; emptyTriggerBag: PUBLIC TriggerBag; CreateTriggerBag: PUBLIC PROC [] RETURNS [triggerBag: TriggerBag] = { triggerBag _ NEW[TriggerBagObj _ [slices: NIL, intersectionPoints: NIL, anchor: NIL]]; }; FlushTriggerBag: PUBLIC PROC [triggerBag: TriggerBag] = { triggerBag.slices _ NIL; triggerBag.intersectionPoints _ NIL; triggerBag.anchor _ NIL; }; CopyTriggerBag: PUBLIC PROC [triggerBag: TriggerBag] RETURNS [copy: TriggerBag] = { copy _ CreateTriggerBag[]; copy.slices _ NARROW[GList.Copy[triggerBag.slices]]; copy.intersectionPoints _ NIL; copy.anchor _ triggerBag.anchor; }; emptyAlignBag: PUBLIC AlignBag; CreateAlignBag: PUBLIC PROC [] RETURNS [alignBag: AlignBag] = { alignBag _ NEW[AlignBagObj _ [ slopeLines: NIL, angleLines: NIL, radiiCircles: NIL, distanceLines: NIL, midpoints: NIL, intersectionPoints: NIL, anchor: NIL ]]; }; FlushAlignBag: PUBLIC PROC [alignBag: AlignBag] = { alignBag.slopeLines _ NIL; alignBag.angleLines _ NIL; alignBag.radiiCircles _ NIL; alignBag.distanceLines _ NIL; alignBag.midpoints _ NIL; alignBag.intersectionPoints _ NIL; alignBag.anchor _ NIL; }; FillStaticTriggerBag: PUBLIC PROC [anchor: Caret, scene: Scene, heuristics: BOOL, triggerBag: TriggerBag] = { AddAnchorTrigger[anchor, triggerBag]; AddAllHotSlices[scene, triggerBag]; }; FillDynamicTriggerBag: PUBLIC PROC [anchor: Caret, scene: Scene, heuristics: BOOL, triggerBag: TriggerBag] = { AddAnchorTrigger[anchor, triggerBag]; AddAllHotSlices[scene, triggerBag]; AddHeuristics[scene, heuristics, $Drag, triggerBag]; -- nothing is being dragged RemoveMoving[scene, triggerBag]; -- nothing is moving }; FillStaticSceneBag: PUBLIC PROC [scene: Scene, sceneBag: TriggerBag] = { AddAllSlices[scene, sceneBag]; ComputeAllBoundingBoxes[sceneBag]; }; FillDynamicSceneBag: PUBLIC PROC [scene: Scene, sceneBag: TriggerBag] = { AddAllSlices[scene, sceneBag]; RemoveMoving[scene, sceneBag]; ComputeAllBoundingBoxes[sceneBag]; }; FillStaticAlignBag: PUBLIC PROC [triggerBag: TriggerBag, sceneBag: TriggerBag, filters: Filters, hideAlignments: BOOL, midpoints: BOOL, alignBag: AlignBag] = { BuiltInFilters[triggerBag, filters, hideAlignments, alignBag]; AddAllMidpoints[sceneBag, midpoints, alignBag]; }; FillDynamicAlignBag: PUBLIC PROC [triggerBag: TriggerBag, sceneBag: TriggerBag, filters: Filters, hideAlignments: BOOL, midpoints: BOOL, action: ATOM, alignBag: AlignBag] = { BuiltInFilters[triggerBag, filters, hideAlignments, alignBag]; AddAllMidpoints[sceneBag, midpoints, alignBag]; }; SetStaticBags: PUBLIC PROC [ggData: GGData] = { triggerBag: TriggerBag _ ggData.hitTest.triggerBag; alignBag: AlignBag _ ggData.hitTest.alignBag; sceneBag: TriggerBag _ ggData.hitTest.sceneBag; scene: Scene _ ggData.scene; anchor: Caret _ ggData.anchor; heuristics: BOOL _ GGState.Heuristics[ggData]; filters: Filters _ ggData.hitTest; hideAlignments: BOOL _ NOT GGState.ShowAlignments[ggData]; midpoints: BOOL _ GGState.Midpoints[ggData]; IF hideAlignments THEN { FlushAlignBag[alignBag]; FlushTriggerBag[sceneBag]; FillStaticSceneBag[scene, sceneBag]; } ELSE { CodeTimer.StartInt[$SetBagsForAction, $Gargoyle]; FlushTriggerBag[triggerBag]; FillStaticTriggerBag[anchor, scene, heuristics, triggerBag]; FlushTriggerBag[sceneBag]; FillStaticSceneBag[scene, sceneBag]; FlushAlignBag[alignBag]; FillStaticAlignBag[triggerBag, sceneBag, filters, hideAlignments, midpoints, alignBag]; CodeTimer.StopInt[$SetBagsForAction, $Gargoyle]; }; }; SetDynamicBags: PUBLIC PROC [ggData: GGData, action: ATOM] = { triggerBag: TriggerBag _ ggData.hitTest.triggerBag; alignBag: AlignBag _ ggData.hitTest.alignBag; sceneBag: TriggerBag _ ggData.hitTest.sceneBag; scene: Scene _ ggData.scene; anchor: Caret _ ggData.anchor; heuristics: BOOL _ GGState.Heuristics[ggData]; filters: Filters _ ggData.hitTest; hideAlignments: BOOL _ NOT GGState.ShowAlignments[ggData]; midpoints: BOOL _ GGState.Midpoints[ggData]; IF hideAlignments THEN { FlushAlignBag[alignBag]; FlushTriggerBag[sceneBag]; FlushTriggerBag[triggerBag]; -- added March 30, 1987. KAP FillDynamicSceneBag[scene, sceneBag]; } ELSE { CodeTimer.StartInt[$SetBagsForAction, $Gargoyle]; FlushTriggerBag[triggerBag]; FillDynamicTriggerBag[anchor, scene, heuristics, triggerBag]; FlushTriggerBag[sceneBag]; FillDynamicSceneBag[scene, sceneBag]; FlushAlignBag[alignBag]; FillDynamicAlignBag[triggerBag, sceneBag, filters, hideAlignments, midpoints, action, alignBag]; CodeTimer.StopInt[$SetBagsForAction, $Gargoyle]; }; }; StaticToDynamicBags: PUBLIC PROC [ggData: GGData] RETURNS [repaintForeground: BOOL] = { triggerBag: TriggerBag _ ggData.hitTest.triggerBag; sceneBag: TriggerBag _ ggData.hitTest.sceneBag; alignBag: AlignBag _ ggData.hitTest.alignBag; heuristics: BOOL _ GGState.Heuristics[ggData]; filters: Filters _ ggData.hitTest; hideAlignments: BOOL _ NOT GGState.ShowAlignments[ggData]; midpoints: BOOL _ GGState.Midpoints[ggData]; repaintForeground _ SomeSelectedIsHot[ggData.scene] OR GGState.Heuristics[ggData]; IF ggData.camera.hideAlignments THEN { repaintForeground _ FALSE; FlushAlignBag[alignBag]; ggData.hitTest.oldSceneBag _ sceneBag; sceneBag _ StaticToDynamicSceneBag[ggData.scene, sceneBag]; ggData.hitTest.sceneBag _ sceneBag; } ELSE { CodeTimer.StartInt[$UpdateBagsForAction, $Gargoyle]; ggData.hitTest.oldTriggerBag _ triggerBag; triggerBag _ CopyTriggerBag[triggerBag]; AddHeuristics[ggData.scene, heuristics, $Drag, triggerBag]; RemoveMoving[ggData.scene, triggerBag]; ggData.hitTest.triggerBag _ triggerBag; ggData.hitTest.oldSceneBag _ sceneBag; sceneBag _ StaticToDynamicSceneBag[ggData.scene, sceneBag]; ggData.hitTest.sceneBag _ sceneBag; ggData.hitTest.oldAlignBag _ alignBag; alignBag _ CreateAlignBag[]; BuiltInFilters[triggerBag, filters, hideAlignments, alignBag]; AddAllMidpoints[sceneBag, midpoints, alignBag]; ggData.hitTest.alignBag _ alignBag; CodeTimer.StopInt[$UpdateBagsForAction, $Gargoyle]; }; }; StaticToDynamicSceneBag: PROC [scene: Scene, sceneBag: TriggerBag] RETURNS [newBag: TriggerBag] = { newBag _ CopyTriggerBag[sceneBag]; RemoveMoving[scene, newBag]; CodeTimer.StartInt[$ComputeBoundBoxes, $Gargoyle]; ComputeAllBoundingBoxes[newBag]; CodeTimer.StopInt[$ComputeBoundBoxes, $Gargoyle]; }; DynamicToStaticBags: PUBLIC PROC [ggData: GGData] RETURNS [repaintForeground: BOOL] = { triggerBag: TriggerBag; sceneBag: TriggerBag; alignBag: AlignBag _ ggData.hitTest.alignBag; filters: Filters _ ggData.hitTest; hideAlignments: BOOL _ NOT GGState.ShowAlignments[ggData]; midpoints: BOOL _ GGState.Midpoints[ggData]; triggerBag _ ggData.hitTest.triggerBag _ ggData.hitTest.oldTriggerBag; sceneBag _ ggData.hitTest.sceneBag _ ggData.hitTest.oldSceneBag; ComputeAllBoundingBoxes[sceneBag]; alignBag _ CreateAlignBag[]; BuiltInFilters[triggerBag, filters, hideAlignments, alignBag]; AddAllMidpoints[sceneBag, midpoints, alignBag]; ggData.hitTest.alignBag _ alignBag; repaintForeground _ TRUE; }; UpdateBagsForAdd: PUBLIC PROC [oldOutline: Outline, newOutline: Outline, trajEnd: TrajEnd, ggData: GGData] RETURNS [repaintForeground: BOOL] = { triggerBag: TriggerBag _ ggData.hitTest.triggerBag; sceneBag: TriggerBag _ ggData.hitTest.sceneBag; alignBag: AlignBag _ ggData.hitTest.alignBag; scene: Scene _ ggData.scene; newHot, oldHot, newWhole, oldWhole, hotMinusOldHot: OutlineDescriptor; hideAlignments: BOOL _ NOT GGState.ShowAlignments[ggData]; midpoints: BOOL _ GGState.Midpoints[ggData]; alignObjects: LIST OF FeatureData; oldHot _ RemoveEntireHotSlice[oldOutline, triggerBag]; newHot _ GGSelect.FindSelectedSlice[newOutline, scene, hot]; IF newHot # NIL THEN [] _ AddHotSlice[newHot, triggerBag]; oldWhole _ RemoveEntireHotSlice[oldOutline, sceneBag]; newWhole _ newOutline.class.newParts[newOutline, NIL, slice]; GGSlice.UpdateDescriptorBoundBoxes[newWhole]; [] _ AddHotSlice[newWhole, sceneBag]; IF newHot = NIL THEN hotMinusOldHot _ NIL ELSE { mask: Sequence; maskD: OutlineDescriptor; newOutlineData: OutlineData _ NARROW[newOutline.data]; traj: Traj _ newOutlineData.children.first; IF trajEnd = lo THEN mask _ GGSequence.Union[GGSequence.CreateFromSegment[traj, 0], GGSequence.CreateFromJoint[traj, 0]] ELSE mask _ GGSequence.Union[GGSequence.CreateFromSegment[traj, GGTraj.HiSegment[traj]], GGSequence.CreateFromJoint[traj, GGTraj.HiJoint[traj]]]; maskD _ GGOutline.DescriptorFromSequence[newOutline, mask]; hotMinusOldHot _ newOutline.class.differenceParts[newHot, newOutline.class.differenceParts[newWhole, maskD]]; alignObjects _ IncrementalFilterSlice[hotMinusOldHot, ggData.hitTest, hideAlignments, alignBag]; }; IncrementalMidpointsSlice[newWhole, midpoints, alignBag]; }; UpdateBagsForNewSlices: PUBLIC PROC [newSlices: LIST OF Slice, ggData: GGData] = { triggerBag: TriggerBag _ ggData.hitTest.triggerBag; sceneBag: TriggerBag _ ggData.hitTest.sceneBag; alignBag: AlignBag _ ggData.hitTest.alignBag; scene: Scene _ ggData.scene; hideAlignments: BOOL _ NOT GGState.ShowAlignments[ggData]; midpoints: BOOL _ GGState.Midpoints[ggData]; FOR list: LIST OF Slice _ newSlices, list.rest UNTIL list = NIL DO wholeD: SliceDescriptor _ list.first.class.newParts[list.first, NIL, slice]; GGSlice.UpdateDescriptorBoundBoxes[wholeD]; [] _ AddHotSlice[wholeD, sceneBag]; IncrementalMidpointsSlice[wholeD, midpoints, alignBag]; ENDLOOP; }; AddAnchorTrigger: PROC [anchor: Caret, triggerBag: TriggerBag] = { [] _ CreateAnchorTrigger[anchor, triggerBag]; }; AddAllHotSlices: PROC [scene: Scene, triggerBag: TriggerBag] = { feature: FeatureData; sliceDescGen: SliceDescriptorGenerator _ GGSelect.SelectedSlices[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; }; AddHeuristics: PROC [scene: Scene, heuristics: BOOL, atom: ATOM, triggerBag: TriggerBag] = { feature: FeatureData; IF NOT heuristics THEN RETURN; SELECT atom FROM $Drag => { sliceDGen: GGModelTypes.SliceDescriptorGenerator _ GGSelect.SelectedSlices[scene, normal]; FOR sliceD: SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDGen], GGSelect.NextSliceDescriptor[sliceDGen] UNTIL sliceD = NIL DO feature _ GGGravity.FeatureFromSlice[sliceD.slice, NIL]; AddFeature[feature, triggerBag]; ENDLOOP; }; $CaretPos => NULL; ENDCASE => ERROR UnexpectedType; }; RemoveMovingSlice: PUBLIC PROC [sliceD: SliceDescriptor, scene: Scene] RETURNS [stationary: SliceDescriptor] = { selSliceD: SliceDescriptor _ GGSelect.FindSelectedSlice[sliceD.slice, scene, normal]; background, overlay, rubber, drag, move: SliceDescriptor; IF selSliceD = NIL THEN RETURN[sliceD]; -- clearly nothing is moving [background, overlay, rubber, drag] _ sliceD.slice.class.movingParts[selSliceD.slice, selSliceD.parts]; move _ sliceD.slice.class.unionParts[rubber, drag]; stationary _ sliceD.slice.class.differenceParts[sliceD, move]; }; RemoveMoving: PROC [scene: Scene, triggerBag: TriggerBag] = { DeleteTriggersFilter[triggerBag, scene, --RemoveMovingOutline,-- RemoveMovingSlice]; }; BuiltInFilters: PUBLIC PROC [triggerBag: TriggerBag, filters: Filters, hideAlignments: BOOL, alignBag: AlignBag] = { anchor: Caret; anchorFeature: FeatureData; sliceD: SliceDescriptor; pointGen: PointGenerator; pointPairGen: PointPairGenerator; index: NAT _ 0; CodeTimer.StartInt[$BuiltInFilters, $Gargoyle]; IF hideAlignments THEN { FlushAlignBag[alignBag]; RETURN; }; anchorFeature _ triggerBag.anchor; IF anchorFeature # NIL AND GGCaret.Exists[(anchor _ NARROW[anchorFeature.shape, Caret])] THEN { point: Point _ GGCaret.GetPoint[anchor]; AddAnchorObject[anchorFeature, alignBag]; [] _ PointFireRule[point, filters, alignBag]; }; 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 [] _ PointFireRule[next.point, filters, alignBag]; 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, alignBag]; index _ index + 1; ENDLOOP; ENDLOOP; CodeTimer.StopInt[$BuiltInFilters, $Gargoyle]; }; AddAllMidpoints: PUBLIC PROC [sceneBag: TriggerBag, midpoints: BOOL, alignBag: AlignBag] = { sliceD: SliceDescriptor; pointPairGen: PointPairGenerator; CodeTimer.StartInt[$AddAllMidpoints, $Gargoyle]; IF NOT GGState.PrecomputeMidpoints[] THEN RETURN; alignBag.midpoints _ NIL; IF NOT midpoints THEN RETURN; FOR l: LIST OF FeatureData _ sceneBag.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 [] _ GGGravity.SegmentAddMidpoint[pairCount, next.lo, next.hi, alignBag]; pairCount _ pairCount+1; ENDLOOP; ENDLOOP; CodeTimer.StopInt[$AddAllMidpoints, $Gargoyle]; }; CreateAnchorTrigger: PUBLIC PROC [anchor: Caret, triggerBag: TriggerBag] RETURNS [feature: FeatureData] = { feature _ GGGravity.FeatureFromAnchor[anchor]; AddFeature[feature, triggerBag]; }; AddHotSlice: PUBLIC PROC [sliceD: SliceDescriptor, triggerBag: TriggerBag] RETURNS [feature: FeatureData] = { oldD, unionD: SliceDescriptor; oldD _ FindOldSlice[sliceD.slice, triggerBag.slices]; IF oldD = NIL THEN { feature _ GGGravity.FeatureFromSlice[sliceD.slice, sliceD.parts]; triggerBag.slices _ AppendFeature[feature, triggerBag.slices]; } ELSE { unionD _ sliceD.slice.class.unionParts[oldD, sliceD]; triggerBag.slices _ DeleteSlice[oldD.slice, triggerBag.slices]; feature _ GGGravity.FeatureFromSlice[sliceD.slice, unionD.parts]; triggerBag.slices _ AppendFeature[feature, triggerBag.slices]; }; }; IncrementalFilterSlice: PUBLIC PROC [sliceD: SliceDescriptor, filters: Filters, hideAlignments: BOOL, alignBag: AlignBag] RETURNS [alignObjects: LIST OF FeatureData] = { 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[PointFireRule[next.point, filters, alignBag], 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, alignBag], alignObjects]]; ENDLOOP; }; IncrementalMidpointsSlice: PUBLIC PROC [sliceD: SliceDescriptor, midpoints: BOOL, alignBag: AlignBag] = { pointPairGen: GGModelTypes.PointPairGenerator; pairCount: NAT _ 0; IF NOT midpoints THEN RETURN; IF NOT GGState.PrecomputeMidpoints[] THEN RETURN; pointPairGen _ sliceD.slice.class.pointPairsInDescriptor[sliceD]; FOR next: GGModelTypes.PointPairAndDone _ sliceD.slice.class.nextPointPair[pointPairGen], sliceD.slice.class.nextPointPair[pointPairGen] UNTIL next.done DO [] _ GGGravity.SegmentAddMidpoint[pairCount, next.lo, next.hi, alignBag]; pairCount _ pairCount + 1; ENDLOOP; }; IncrementalFilters: PUBLIC PROC [trigger: FeatureData, filters: Filters, hideAlignments: BOOL, alignBag: AlignBag] RETURNS [alignObjects: LIST OF FeatureData] = { alignObjects _ NIL; IF hideAlignments THEN { FlushAlignBag[alignBag]; RETURN; }; WITH trigger.shape SELECT FROM sliceD: SliceDescriptor => { alignObjects _ IncrementalFilterSlice[sliceD, filters, hideAlignments, alignBag]; }; anchor: Caret => { IF GGCaret.Exists[anchor] THEN { point: Point _ GGCaret.GetPoint[anchor]; [] _ PointFireRule[point, filters, alignBag]; }; }; ENDCASE => ERROR; }; RemoveAnchorTrigger: PUBLIC PROC [triggerBag: TriggerBag] = { triggerBag.anchor _ NIL; }; RemoveEntireHotSlice: PUBLIC PROC [slice: Slice, triggerBag: TriggerBag] RETURNS [oldSliceD: SliceDescriptor]= { oldSliceD _ FindOldSlice[slice, triggerBag.slices]; IF oldSliceD#NIL THEN { triggerBag.slices _ DeleteSlice[oldSliceD.slice, triggerBag.slices]; }; }; RemoveHotSlice: PUBLIC PROC [sliceD: SliceDescriptor, triggerBag: TriggerBag] = { oldSliceD, diff: SliceDescriptor; feature: FeatureData; oldSliceD _ FindOldSlice[sliceD.slice, triggerBag.slices]; IF oldSliceD#NIL THEN { diff _ sliceD.slice.class.differenceParts[oldSliceD, sliceD]; triggerBag.slices _ DeleteSliceD[oldSliceD, triggerBag.slices]; IF NOT sliceD.slice.class.isEmptyParts[diff] THEN { feature _ GGGravity.FeatureFromSlice[sliceD.slice, diff]; triggerBag.slices _ AppendFeature[feature, triggerBag.slices]; }; }; }; AddFeature: PROC [featureData: FeatureData, triggerBag: TriggerBag] = { Process.CheckForAbort[]; SELECT featureData.type FROM outline, slice => { triggerBag.slices _ CONS[featureData, triggerBag.slices]; }; anchor => { triggerBag.anchor _ featureData; }; ENDCASE => ERROR; }; DeleteTriggersFilter: PROC [triggerBag: TriggerBag, scene: Scene, --filterOutlineProc: FilterOutlineProc,-- filterSliceProc: FilterSliceProc] = { sliceD, newSliceD: SliceDescriptor; sliceFeatures: LIST OF FeatureData _ triggerBag.slices; newFeature: FeatureData; triggerBag.slices _ NIL; FOR sliceList: LIST OF FeatureData _ sliceFeatures, sliceList.rest UNTIL sliceList = NIL DO sliceD _ NARROW[sliceList.first.shape]; newSliceD _ filterSliceProc[sliceD, scene]; IF NOT sliceD.slice.class.isEmptyParts[newSliceD] THEN { newFeature _ GGGravity.FeatureFromSlice[newSliceD.slice, newSliceD.parts]; triggerBag.slices _ CONS[newFeature, triggerBag.slices]; }; ENDLOOP; }; ComputeAllBoundingBoxes: PROC [sceneBag: TriggerBag] = { featureData: FeatureData; outlineD: OutlineDescriptor; FOR list: LIST OF FeatureData _ sceneBag.slices, list.rest UNTIL list = NIL DO featureData _ list.first; outlineD _ NARROW[featureData.shape]; GGSlice.UpdateDescriptorBoundBoxes[outlineD]; ENDLOOP; }; AddAllSlices: PROC [scene: Scene, triggerBag: TriggerBag] = { feature: FeatureData; sliceGen: SliceGenerator _ GGScene.SlicesInScene[scene]; FOR slice: Slice _ GGScene.NextSlice[sliceGen], GGScene.NextSlice[sliceGen] UNTIL slice = NIL DO feature _ GGGravity.FeatureFromSlice[slice]; AddFeature[feature, triggerBag]; ENDLOOP; }; AddAnchorObject: PROC [anchorFeature: FeatureData, alignBag: AlignBag] = { alignBag.anchor _ anchorFeature; }; PointFireRule: PROC [point: Point, filters: Filters, alignBag: AlignBag] 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: Vectors2d.VectorFromAngle[thisButton.value], point: point, objectBag: alignBag ]; 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: alignBag ]; IF feature # NIL THEN alignObjects _ CONS[feature, alignObjects]; }; ENDLOOP; }; SegmentFireRule: PROC [segNum: NAT, lo, hi: Point, filters: Filters, alignBag: AlignBag] 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: alignBag]; 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: alignBag]; IF line1 # NIL THEN alignObjects _ CONS[line1, alignObjects]; IF line2 # NIL THEN alignObjects _ CONS[line2, alignObjects]; }; ENDLOOP; }; SomeSelectedIsHot: PROC [scene: Scene] RETURNS [BOOL] = { sliceDescGen: SliceDescriptorGenerator _ GGSelect.SelectedSlices[scene, normal]; FOR sliceD: SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDescGen], GGSelect.NextSliceDescriptor[sliceDescGen] UNTIL sliceD=NIL DO IF GGSelect.IsSelectedInPart[sliceD.slice, scene, hot] THEN RETURN[TRUE]; ENDLOOP; RETURN[FALSE]; }; 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; }; 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; }; DeleteSliceD: PROC [sliceD: SliceDescriptor, 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] _ FindSliceAndNeighbors[sliceD.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]; }; InitStats: PROC [] = { boundBoxes, bags, interval: CodeTimer.Interval; boundBoxes _ CodeTimer.CreateInterval[$ComputeBoundBoxes]; bags _ CodeTimer.CreateInterval[$UpdateBagsForAction, LIST[boundBoxes]]; CodeTimer.AddInt[bags, $Gargoyle]; bags _ CodeTimer.CreateInterval[$SetBagsForAction, NIL]; CodeTimer.AddInt[bags, $Gargoyle]; interval _ CodeTimer.CreateInterval[$BuiltInFilters, NIL]; CodeTimer.AddInt[interval, $Gargoyle]; interval _ CodeTimer.CreateInterval[$AddAllMidpoints, NIL]; CodeTimer.AddInt[interval, $Gargoyle]; }; Init: PROC [] = { emptyTriggerBag _ CreateTriggerBag[]; emptyAlignBag _ CreateAlignBag[]; }; InitStats[]; Init[]; END. SôGGAlignImpl.mesa Created by Eric Bier on June 5, 1985 3:41:39 pm PDT. Last edited by Bier on April 20, 1987 6:46:44 pm PDT. Pier, May 15, 1987 6:15:56 pm PDT The TriggerBag, the SceneBag and the AlignBag The set of alignment objects is computed in three steps (see Figure 1). First, we determine which joints, control points, and segments in the scene are triggers. We put these in triggerBag. Next we determine which scene objects are gravity active. We put these in sceneBag. Finally, we determine the set of alignment objects from the triggerBag using the current filters and from the sceneBag if midpoints are activated. This set is represented by the selected slopes, angles, radii, and distances in the user interface. The bags are recomputed at these times: triggerBag: Updated whenever an Add or Drag operation is performed, or whenever anything becomes hot or cold. sceneBag: Updated whenever an Add or Drag operation is performed. objectBag. Updated whenever an Add or Drag operation is performed, whenever anything becomes hot or cold, or whenever the set of current filters changes. The bags are represented as follows: triggerBag and sceneBag: several LIST OF FeatureData. The FeatureData will point to a SliceDescriptor, --an OutlineDescriptor,-- or the anchor. objectBag: several LIST OF FeatureData. The FeatureData will point to an AlignmentLine, an AlignmentCircle, an AlignmentPoint, or a Caret (e.g. the anchor). [Artwork node; type 'ArtworkInterpress on' to command tool] Creating a TriggerBag triggerBag.outlines _ NIL; copy.outlines _ NARROW[GList.Copy[triggerBag.outlines]]; Creating an AlignBag Filling the Trigger Bag AddAllHotOutlines[scene, triggerBag]; AddAllHotOutlines[scene, triggerBag]; Filling the SceneBag AddAllTrajectories[scene, sceneBag]; AddAllTrajectories[scene, sceneBag]; Filling the Align Bag Filling all three Bags at Once We have two trigger bags and one object bag to get into shape. The Trigger Bag should already contain the anchor, and all hot objects. For now, however, we will put these into the bag here. Next, we remove all objects which are going to be moving during the upcoming operation. For selection operations, we remove all triggers. The Scene Bag will be empty. We fill it with all of the scene objects. Then, we remove those objects which are going to be moving during the upcoming operation. The Align Bag is made by filtering the Trigger Bag through the currently selected filters. Fill TriggerBag Fill Scene Bag Fill Align Bag We have two trigger bags and one object bag to get into shape. The Trigger Bag should already contain the anchor, and all hot objects. For now, however, we will put these into the bag here. Next, we remove all objects which are going to be moving during the upcoming operation. For selection operations, we remove all triggers. The Scene Bag will be empty. We fill it with all of the scene objects. Then, we remove those objects which are going to be moving during the upcoming operation. The Align Bag is made by filtering the Trigger Bag through the currently selected filters. Fill TriggerBag Fill Scene Bag Fill Align Bag We are about to drag things around. Here is what that means for each of the bags: triggerBag: AddHeuristics may add some things. RemoveMoving is going to remove some things. sceneBag: RemoveMoving is going to remove some things. alignBag: Those things that RemoveMoving now removes will no longer trigger midpoints, nor alignment lines. repaintForeground is TRUE if alignBag is altered. No alignments. Just build sceneBag. Incrementally Update TriggerBag Incrementally Update SceneBag Remake AlignBag from Scratch Use the old TriggerBag and SceneBag. Remake AlignBag from Scratch newSeg is the number, in newOutline of the newly added segment. If this operation is not a continuation then the bags should be updated as follows: Trigger: Remove old outline descriptor. Add new hotness descriptor for newOutline. Align: Remove mentions of old outline. Use filters to add new mentions. Scene: Remove old outline descriptor. Add a complete descriptor for newOutline. Fixing the TriggerBag. oldHot _ RemoveEntireHotOutline[oldOutline, triggerBag]; We can ignore Heuristics because there is no motion. We don't need to remove moving parts because there aren't any. IF newHot # NIL THEN [] _ AddHotOutline[newHot, triggerBag]; Fixing the SceneBag. oldWhole _ RemoveEntireHotOutline[oldOutline, sceneBag]; [] _ AddHotOutline[newWhole, sceneBag]; Fixing the AlignBag. We assume that the bags were set up static before this. Hence, there are two simple possibilities. Either the newly added segment is hot, or it isn't. alignObjects _ IncrementalFilterOutline[hotMinusOldHot, ggData.hitTest, hideAlignments, alignBag]; IncrementalMidpointsOutline[newWhole, midpoints, alignBag]; Fixing the Foreground Plane. dc _ BufferedRefresh.GetLayerContext[ggData.refresh.sandwich, $Foreground]; GGGravity.DrawFeatureList[dc, alignObjects, ggData]; repaintForeground _ FALSE; Assuming that the new slices cannot be hot, the bags should be updated as follows: Trigger: No change. Scene: Add a complete descriptor for each new slice. Align: Add midpoints for each new slice. Fixing the SceneBag and AlignBag. UpdateBagsForDelete: PUBLIC PROC [oldOutline: Outline, ggData: GGData] RETURNS [repaintForeground: BOOL] = { }; The Filter Routines shown in the figure as boxes and ovals AddAllHotOutlines: PROC [scene: Scene, triggerBag: TriggerBag] = { feature: FeatureData; outDGen: GGModelTypes.OutlineDescriptorGenerator; outDGen _ GGSelect.SelectedOutlines[scene, hot]; FOR outlineD: OutlineDescriptor _ GGSelect.NextSliceDescriptor[outDGen], GGSelect.NextSliceDescriptor[outDGen] UNTIL outlineD = NIL DO feature _ GGGravity.FeatureFromOutline[outlineD.slice, outlineD.parts]; AddFeature[feature, triggerBag]; ENDLOOP; }; outDGen: GGModelTypes.OutlineDescriptorGenerator _ GGSelect.SelectedOutlines[scene, normal]; FOR outlineD: OutlineDescriptor _ GGSelect.NextSliceDescriptor[outDGen], GGSelect.NextSliceDescriptor[outDGen] UNTIL outlineD = NIL DO feature _ GGGravity.FeatureFromOutline[outlineD.slice, NIL]; AddFeature[feature, triggerBag]; ENDLOOP; RemoveMovingOutline: PUBLIC PROC [outlineD: OutlineDescriptor, scene: Scene] RETURNS [stationary: OutlineDescriptor] = { selSliceD: OutlineDescriptor _ GGSelect.FindSelectedSlice[outlineD.slice, scene, normal]; background, overlay, rubber, drag, move: OutlineDescriptor; IF selSliceD = NIL THEN RETURN[outlineD]; -- clearly nothing is moving [background, overlay, rubber, drag] _ outlineD.slice.class.movingParts[selSliceD.slice, selSliceD.parts]; move _ outlineD.slice.class.unionParts[rubber, drag]; stationary _ outlineD.slice.class.differenceParts[outlineD, move]; }; If we are about to drag all of the selected objects, then we must remove selected objects from the triggerBag. We must also remove segments which are adjacent to a moving joint (called "dangling" segments), and segments whose control points are selected. Add Alignment Objects to the alignBag, using the triggers in the triggerBag. outlineD: OutlineDescriptor; outPointGen: GGModelTypes.OutlinePointGenerator; outPointPairGen: GGModelTypes.OutlinePointPairGenerator; The anchor as a trigger. Triggers from outlines. 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 [] _ PointFireRule[next.point, filters, alignBag]; 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, alignBag]; index _ index + 1; ENDLOOP; ENDLOOP; Triggers from slices. Add to the object bag the midpoints of all segments and slices in the trigger bag. outlineD: OutlineDescriptor; outPointPairGen: GGModelTypes.OutlinePointPairGenerator; Segments as triggers. FOR l: LIST OF FeatureData _ sceneBag.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 [] _ GGGravity.SegmentAddMidpoint[pairCount, next.lo, next.hi, alignBag]; pairCount _ pairCount+1; ENDLOOP; ENDLOOP; Slices as triggers. Incremental Addition versions of the Filter Routines shown in the figure as boxes and ovals. AddHotOutline: PUBLIC PROC [outlineD: OutlineDescriptor, triggerBag: TriggerBag] RETURNS [feature: FeatureData] = { oldD, unionD: OutlineDescriptor; oldD _ FindOldOutlineD[outlineD.slice, triggerBag.outlines]; IF oldD = NIL THEN { feature _ GGGravity.FeatureFromOutline[outlineD.slice, outlineD.parts]; triggerBag.outlines _ AppendFeature[feature, triggerBag.outlines]; } ELSE { unionD _ outlineD.slice.class.unionParts[oldD, outlineD]; triggerBag.outlines _ DeleteOutlineD[oldD, triggerBag.outlines]; feature _ GGGravity.FeatureFromOutline[outlineD.slice, unionD.parts]; triggerBag.outlines _ AppendFeature[feature, triggerBag.outlines]; }; }; IncrementalFilterOutline: PUBLIC PROC [outlineD: OutlineDescriptor, filters: Filters, hideAlignments: BOOL, alignBag: AlignBag] RETURNS [alignObjects: LIST OF FeatureData] = { 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[PointFireRule[next.point, filters, alignBag], 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, alignBag], alignObjects]]; ENDLOOP; }; IncrementalMidpointsOutline: PUBLIC PROC [outlineD: OutlineDescriptor, midpoints: BOOL, alignBag: AlignBag] = { THIS CODE IS NO LONGER USED EXCEPT FOR PERFORMANCE BENCHMARKS; GGState.PrecomputeMidpoints[] is FALSE by default. Add to the object bag the midpoints of all segments in outlineD. outPointPairGen: GGModelTypes.OutlinePointPairGenerator; pairCount: NAT _ 0; IF NOT midpoints THEN RETURN; IF NOT GGState.PrecomputeMidpoints[] THEN RETURN; outPointPairGen _ outlineD.slice.class.pointPairsInDescriptor[outlineD]; FOR next: GGModelTypes.PointPairAndDone _ outlineD.slice.class.nextPointPair[outPointPairGen], outlineD.slice.class.nextPointPair[outPointPairGen] UNTIL next.done DO [] _ GGGravity.SegmentAddMidpoint[pairCount, next.lo, next.hi, alignBag]; pairCount _ pairCount + 1; ENDLOOP; }; THIS CODE IS NO LONGER USED EXCEPT FOR PERFORMANCE BENCHMARKS; GGState.PrecomputeMidpoints[] is FALSE by default. Add to the object bag the midpoints of all segments in sliceD. A single new trigger has been added to the triggerBag. Add to the alignBag, all alignment objects generated by that trigger. Returns a list of the new alignment objects that were generated. outlineD: OutlineDescriptor => { alignObjects _ IncrementalFilterOutline[outlineD, filters, hideAlignments, alignBag]; }; The anchor as a trigger. Incremental Deletion versions of the Filter Routines shown in the figure as boxes and ovals. RemoveEntireHotOutline: PUBLIC PROC [outline: Outline, triggerBag: TriggerBag] RETURNS [oldD: OutlineDescriptor] = { oldD _ FindOldOutlineD[outline, triggerBag.outlines]; IF oldD#NIL THEN { triggerBag.outlines _ DeleteOutlineD[oldD, triggerBag.outlines]; }; }; RemoveHotOutline: PUBLIC PROC [outlineD: OutlineDescriptor, triggerBag: TriggerBag] = { oldOut, diff: OutlineDescriptor; feature: FeatureData; oldOut _ FindOldOutlineD[outlineD.slice, triggerBag.outlines]; IF oldOut#NIL THEN { diff _ outlineD.slice.class.differenceParts[oldOut, outlineD]; triggerBag.outlines _ DeleteOutlineD[oldOut, triggerBag.outlines]; IF NOT outlineD.slice.class.isEmptyParts[diff] THEN { feature _ GGGravity.FeatureFromOutline[outlineD.slice, diff.parts]; triggerBag.outlines _ AppendFeature[feature, triggerBag.outlines]; }; }; }; RemoveMentionOutline: PUBLIC PROC [outline: Outline, alignBag: AlignBag] = { Removes all alignment objects and midpoints that were triggered (only) by this outline. Removes mention of this outline from those alignment objects that are trigger by other triggers as well. line: AlignmentLine; FOR list: LIST OF FeatureData _ alignBag.slopeLines, list.rest UNTIL list = NIL DO line _ NARROW[list.first.shape]; ENDLOOP; }; RemoveMentionSlice: PUBLIC PROC [slice: Slice, alignBag: AlignBag] = { Removes all alignment objects and midpoints that were triggered (only) by this slice. Removes mention of this slice from those alignment objects that are trigger by other triggers as well. }; Other Routines In support of building triggerBag and sceneBag. outline => { triggerBag.outlines _ CONS[featureData, triggerBag.outlines]; }; When this procedure is called, all sequences in triggerBag are passed to filterSeqProc. filterSeqProc should return those parts which should be included in the new bag. outlineD, newD: OutlineDescriptor; outlineFeatures: LIST OF FeatureData _ triggerBag.outlines; triggerBag.outlines _ NIL; FOR outList: LIST OF FeatureData _ outlineFeatures, outList.rest UNTIL outList = NIL DO outlineD _ NARROW[outList.first.shape]; newD _ filterOutlineProc[outlineD, scene]; IF NOT outlineD.slice.class.isEmptyParts[newD] THEN { newFeature _ GGGravity.FeatureFromOutline[newD.slice, newD.parts]; triggerBag.outlines _ CONS[newFeature, triggerBag.outlines]; }; ENDLOOP; In support of building sceneBag. ComputeAllBoundingBoxes: PROC [sceneBag: TriggerBag] = { featureData: FeatureData; outlineD: OutlineDescriptor; FOR list: LIST OF FeatureData _ sceneBag.outlines, list.rest UNTIL list = NIL DO featureData _ list.first; outlineD _ NARROW[featureData.shape]; GGSlice.UpdateDescriptorBoundBoxes[outlineD]; ENDLOOP; }; AddAllTrajectories: PROC [scene: Scene, triggerBag: TriggerBag] = { outlineGen: GGModelTypes.OutlineGenerator; feature: FeatureData; outlineGen _ GGScene.OutlinesInScene[scene]; FOR outline: Outline _ GGScene.NextOutline[outlineGen], GGScene.NextOutline[outlineGen] UNTIL outline = NIL DO feature _ GGGravity.FeatureFromOutline[outline]; AddFeature[feature, triggerBag]; ENDLOOP; }; In support of building alignBag. SlopeLine Circle Parallel Lines at Given Distance AngleLines In support of building all three bags. Hooks for the future when users can write their own alignment line generators. UserDefinedPointFilter: PUBLIC PROC [triggerBag: TriggerBag, objectBag: AlignBag, pointFilter: PointFilterProc] = { jointGen: JointGenerator; point: Point; alignObjList: LIST OF AlignmentObject; Filter the joints. FOR l: LIST OF FeatureData _ triggerBag.seqs, l.rest UNTIL l = NIL DO jointGen _ GGSequence.JointsInSequence[NARROW[l.first.shape, Sequence]]; FOR i: INT _ GGSequence.NextJoint[jointGen], GGSequence.NextJoint[jointGen] UNTIL i = -1 DO point _ GGTraj.FetchJointPos[NARROW[l.first.shape, Sequence].traj, i]; alignObjList _ pointFilter[point]; FOR alignList: LIST OF AlignmentObject _ alignObjList, alignList.rest UNTIL alignList = NIL DO WITH alignList.first SELECT FROM line: Line => { GGGravity.JointAddLine[line, point, i, NARROW[l.first.shape, Sequence].traj, alignBag]; }; circle: Circle => { GGGravity.JointAddCircle[circle, point, i, NARROW[l.first.shape, Sequence].traj, alignBag]; }; ENDCASE => ERROR; ENDLOOP; ENDLOOP; ENDLOOP; }; UserDefinedSegmentFilter: PUBLIC PROC [triggerBag: TriggerBag, alignBag: AlignBag, segmentFilter: SegmentFilterProc] = { segGen: SegmentGenerator; alignObjList: LIST OF AlignmentObject; Filter the segments. FOR l: LIST OF FeatureData _ triggerBag.seqs, l.rest UNTIL l = NIL DO segGen _ GGSequence.SegmentsInSequence[NARROW[l.first.shape, Sequence]]; FOR next: SegAndIndex _ GGSequence.NextSegmentAndIndex[segGen], GGSequence.NextSegmentAndIndex[segGen] UNTIL next.seg = NIL DO alignObjList _ segmentFilter[next.seg.lo, next.seg.hi]; FOR alignList: LIST OF AlignmentObject _ alignObjList, alignList.rest UNTIL alignList = NIL DO WITH alignList.first SELECT FROM line: Line => { GGGravity.SegmentAddLine[line, point, next.index, NARROW[feature.shape, Sequence].traj, alignBag]; }; circle: Circle => { GGGravity.SegmentAddCircle[circle, point, next.index, NARROW[feature.shape, Sequence].traj, alignBag]; }; ENDCASE => ERROR; ENDLOOP; ENDLOOP; ENDLOOP; }; TAddTriggersFilter: PUBLIC PROC [triggerBag: TriggerBag, filterSeqProc: FilterSequenceProc, filterSliceProc: FilterSliceProc, ggData: GGData] = { When this procedure is called, all sequences in the scene are passed to filterSeqProc to see if they should be added (in all or in part). filterSeqProc should return those parts which should be added. AddSequenceTrigger adds these parts to triggerBag. A similar things happens for slices using the filterSliceProc and calls to AddSliceTrigger add: BOOL; seq: Sequence; sliceGen: SliceGenerator; trajGen: TrajGenerator _ GGScene.TrajsInScene[ggData.scene]; FOR traj: Traj _ GGScene.NextTraj[trajGen], GGScene.NextTraj[trajGen] UNTIL traj = NIL DO seq _ GGSequence.CreateComplete[traj]; filterSeqProc[seq]; -- keeps those parts of seq which should remain AddSequenceTrigger[seq, triggerBag]; ENDLOOP; sliceGen _ GGScene.SlicesInScene[ggData.scene]; FOR slice: Slice _ GGScene.NextSlice[sliceGen], GGScene.NextSlice[sliceGen] UNTIL slice = NIL DO add _ filterSliceProc[slice]; IF add THEN AddSliceTrigger[slice, triggerBag]; ENDLOOP; }; FeatureList Utilities 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]; }; 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; }; FindSliceDAndNeighbors: 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] _ FindOutlineAndNeighbors[outlineD.slice, featureList ! EntityNotFound => {notFound _ TRUE; CONTINUE}]; IF notFound THEN RETURN[featureList]; IF before = NIL THEN smallerList _ after ELSE { before.rest _ after; smallerList _ featureList; }; }; Obsolete Routines ReplaceObsoleteOutlineTrigger: PUBLIC PROC [ggData: GGData, oldOutline: Outline, newOutline: Outline] = { triggerBag: TriggerBag _ ggData.hitTest.triggerBag; alignBag: AlignBag _ ggData.hitTest.alignBag; sceneBag: TriggerBag _ ggData.hitTest.sceneBag; feature: FeatureData; hotD: OutlineDescriptor; notFound: BOOL; First, get rid of all references to the obsolete outline. notFound _ FALSE; UNTIL notFound DO [sceneBag.outlines, notFound] _ DeleteOutline[oldOutline, sceneBag.outlines]; ENDLOOP; notFound _ FALSE; UNTIL notFound DO [triggerBag.outlines, notFound] _ DeleteOutline[oldOutline, triggerBag.outlines]; ENDLOOP; Next, add back any appropriate references to the new outline. Assume that the next operation will be a CaretPos. If this traj is hot, add its hot parts to the triggerBag. hotD _ GGSelect.FindSelectedOutline[newOutline, ggData.scene, hot]; IF hotD # NIL THEN { feature _ GGGravity.FeatureFromOutline[hotD.slice, hotD.parts]; AddFeature[feature, triggerBag]; }; Add the whole object to the scene bag. feature _ GGGravity.FeatureFromOutline[newOutline]; AddFeature[feature, sceneBag]; }; Initialization Ê@~˜Icode™K™4™5K™!—K˜šÏk ˜ Jšœé˜é—K˜šÏn œœ˜Jšœ‡˜ŽKšœ ˜—˜Kšœœ"˜5Kšœœ ˜5Kšœœ˜!Kšœœ˜%Kšœœ˜#Kšœœ&˜AKšœœ˜Kšœœ˜0Kšœ œœ˜'Kšœœ˜3Kšœœ˜4Kšœœ˜0Kšœ œ˜)Kšœœ˜'Kšœœ˜!Kšœœ˜3Kšœœ˜Kšœ œ˜+Kšœ œ ˜1Kšœ œ˜%Kšœ œ˜*Kšœœœ˜3Kšœœ%˜?Kšœœ˜!Kšœœ˜/Kšœœ˜3Kšœœ#˜;Kšœœ"˜:Kšœœ˜!Kšœ œ˜'Kšœœ!˜7Kšœœ#˜7Kšœ œ˜'Kšœœ"˜9Kšœœ˜!Kšœœœ˜/Kšœœ#˜;Kšœœ)˜GKšœœ˜3Kšœ œ˜+Kšœœ˜Kšœ œ˜%Kšœœ˜,Kšœ œœ˜%Kšœœ"˜5Kšœœ˜#K˜Kšžœœ œ˜4—K˜body™.L™ŽL™'L™nL™AL™šL™$L™Lšœž™ž—™Kšžœœœœ˜%Kšžœœœœ˜$Kšžœœœœ˜%—I artworkFigure• Interpressá+Interpress/Xerox/3.0  f j k j¡¥“ļ= ¤ ¨  ™ÄApC¡£ r jbÙ ¢ ¨¡¡¨ r jª ¤èI ¢ ¥ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“ÁHot Scene Objects– k é r jª ¤u  ¢ ¥ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“Á Heuristics– k é r j¬ ¤ÄOò ¢ ¥ ¨ÅxeroxÅ tiogafontsÅMath12£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“ÁU– k é r j¢¯“¢°“¡¡¨eBeX¡¹¢¯“¢°“¡¡¨eXÆX¡¹¢¯“¢°“¡¡¨ÆXÆB¡¹¢¯“¢°“¡¡¨ÆBeB¡¹ k é r jª ¤jÄS ¢ ¥ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“Á Scene Objects– k é r j¢¯“¢°“¡¡¨âBâX¡¹¢¯“¢°“¡¡¨âX_X¡¹¢¯“¢°“¡¡¨_X_B¡¹¢¯“¢°“¡¡¨_BâB¡¹ k é¢¯“¢°“¢·“¡¡¨ÄˆµÄ¹i¨™Ä![;Ä_D—Ä@ŽoĨ Äq•¾Ä‚Ý|Ä#‡;Ĉ#ƒ¡’ÄxwÅÄFSFÄ>ÝoÄL3OÄ&±EŒ¡’Ä[!ÄccpÄL=„Ä[kÄZÀ•ÄPña¡’Ä#í8ÄR‰eÄÃ*Ä£KÇÄ*–;Ä1;¡’ÄμÄGXSÄYøqÄÀQÖÄIU]Äe½m¡’Ä@¸SÄOËRÄ. AÄOŽOÄQ qÄ&Ÿ%¡’Ĉù¿Ä€ãjÄ@¡XÄ ÆùĈµÄ¹i¨¡’¡¸ r jª ¤Ä)Œ ¢ ¥ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“ÁTrigger– k é r jª ¤Ä?~ ¢ ¥ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“ÁBag– k é¢¯“¢°“¢·“¡¡¨p™e#p.¡“±Ž¼#±¡“pŽ¡¸ r j¡¡¨¢¯“Ä;÷™Ä;èÄYèÄÙ J¡”ÄwèÄw÷ÄÙ J¡”ÄwÄYÄÙ J¡”Ä;Ä;÷ÄÙ J¡”¡¸ k é¢¯“¢°“¢·“¡¡¨ÄB™ÄY—˜¢¯“¢°“¢·“¡¡¨ÄëB™.˜¢¯“¢°“¢·“¡¡¨Äá™ÄY—˜¢¯“¢°“¢·“¡¡¨Äe7ÄtyC™Äam;ĸ——ĹoăJMÄ?é¾ÄÌyÄc™;Ä÷Þ-¡’ĉ~QÄ9a#ÄZà7Ä1x¿ÄqŸEÄ¡’Ä61!Ä¥êmÄÛ•„ĶÆ{Äi>ÄV¯;¡’Ä`½8ÄLô5ÄJ_*Ä!yÄj¨;Ä­év¡’Ä,_Ä.SÄÔ®qÄe5BÄ®S]Äcøã¡’ÄšÚSÄAì)Äu6AÄƨyÄÔÅvÄ|7J¡’ÄXc¿Ä[±6Äž_WÄo·Äe7ÄtyC¡’¡¸ r jª ¤ÄUÄ ¢ ¥ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“ÁTrigger– k é r jª ¤ÄkÄ ¢ ¥ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“ÁBag– k é¢¯“¢°“¢·“¡¡¨Mó™BþM ¡“uŽ€þuó¡“MŽ¡¸ r jª ¤Pĵ ¢ ¥ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“ÁFilters– k é¢¯“¢°“¢·“¡¡¨Ä Ç™ÄõÒÄ Ý¡“Ä£ŽÄ¹Òģǡ“Ä Ž¡¸ r jª ¤ÄÏ ¢ ¥ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“Á Not Moving– k é¢¯“¢°“¢·“¡¡¨ÄYè™ÄWÝ—˜¢¯“¢°“¢·“¡¡¨ÄWǙĻW«˜¢¯“¢°“¢·“¡¡¨œÄ¹i¨™ÄtßAÄ_D—Äe™8Ĩ ĆlíÄ‚Ý|ćÇJĈ#ƒ¡’ÄŠPKÄFSFÄÊ ÄL3OÄ>Å#Œ¡’ÄT/ÄccpÄÊ¿pÄ[kļ“ÄPña¡’Ä®M]ÄR‰eÄxÍ?Ä%¿.Ä‚óCÄ1;¡’ĉ±EÄGXSĘ*KÄÀQÖÄw@;Äe½m¡’Ä’èIÄOËRÄF3$ÄOŽOÄà<sÄ&Ÿ%¡’Ä&ÿÄ€ãjÄN¯(Ä ÆùœÄ¹i¨¡’¡¸ r jª ¤ÄA #Ä4@; ¢ ¥ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“ÁBag– k é r jª ¤wÄá ¢ ¥ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“ÁAlign– k é r j¢¯“¢°“¡¡¨ØBØX¡¹¢¯“¢°“¡¡¨ØX9X¡¹¢¯“¢°“¡¡¨9X9B¡¹¢¯“¢°“¡¡¨9BØB¡¹ k é r jª ¤ÝÄS ¢ ¥ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“Á Scene Objects– k é¢¯“¢°“¢·“¡¡¨âÇ™×ÒâÝ¡“.Ž9Ò.Ç¡“⎡¸ r jª ¤çÏ ¢ ¥ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“Á Not Moving– k é¢¯“¢°“¢·“¡¡¨!Ĺi¨™ÄU¤AÄ_D—ÄJ±8Ĩ Ä™íÄ‚Ý|Äm´QĈ#ƒ¡’ÄfGKÄFSFÄ ÄL3OÄ-ô#Œ¡’Ä=/ÄccpÄ”ïpÄ[kÄÈ“ÄPña¡’Äž]ÄR‰eÄZˆ?Ä%¿.ÄbÂCÄ1;¡’ÄhŠEÄGXSÄt!KÄÀQÖÄZç;Äe½m¡’ÄoÕIÄOËRÄ4ç$ÄOŽOĨûsÄ&Ÿ%¡’ÄcÄ€ãjÄ;w(Ä Æù!Ĺi¨¡’¡¸ r jª ¤ÄV¤?~ ¢ ¥ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“ÁBag– k é¢¯“¢°“¢·“¡¡¨ÄÑB™Ý—˜¢¯“¢°“¢·“¡¡¨Ç™Ä$!˜ r jª ¤ûÄÙ ¢ ¥ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“ÁScene– k é r j¢¯“¢°“¡¡¨ää*¡¹¢¯“¢°“¡¡¨ä**¡¹¢¯“¢°“¡¡¨*¡¹¢¯“¢°“¡¡¨ä¡¹ k é r jª ¤éÄ÷ ¢ ¥ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“ÁAnchor– k é¢¯“¢°“¢·“¡¡¨™ÄY—˜¢¯“¢°“¢·“¡¡¨ÃÄtyC™Ä~ÆAĸ——Än!8ăJMÄчíÄÌyÄ öQÄ÷Þ-¡’Ä•½KÄ9a#Äw Ä1x¿ÄD#Ä¡’Ä[=/Ä¥êmÄÛÏpĶÆ{Ä%!“ÄV¯;¡’ļx]ÄLô5Ä‚f?Ä!yÄ(CÄ­év¡’Ä…+>Ä.SÄ£—KÄe5BÄ€=;Äcøã¡’ÄžIÄAì)ÄK¯$ÄƨyÄñÁsÄ|7J¡’Ä* Ä[±6ÄTÇ(Äo·ÃÄtyC¡’¡¸ r jª ¤Ä~‚?Ä ¢ ¥ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“ÁBag– k é r jª ¤/ ¢ ¥ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“ÁScene– k é¢¯“¢°“¢·“¡¡¨˜ó™þ˜ ¡“ÈŽÓþÈ󡓘Ž¡¸ r jª ¤”û ¢ ¥ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“Á Midpoints– k é r j¬ ¤ÄÃÔ ¢ ¥ ¨ÅxeroxÅ tiogafontsÅMath12£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“ÁU– k é r j¡¡¨¢¯“įٙįÊÄÍÊÄÙ J¡”ÄëÊÄëÙÄÙ J¡”ÄëèÄÍèÄÙ J¡”įèįÙÄÙ J¡”¡¸ k é¢¯“¢°“¢·“¡¡¨ÄO-Äs=O™ ˜¢¯“¢°“¢·“¡¡¨ÄdÜ1Ä^ÕA™ ˜¢¯“¢°“¢·“¡¡¨aó™ÄÍè—˜¢¯“¢°“¢·“¡¡¨ÄÍÊ™Ä# ˜¢¯“¢°“¢·“¡¡¨°ó™ÄÍè—˜ r jª ¤Ä#ÄÅ ¢ ¥ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“ÁGravity– k é r jª ¤Ä'Ä© ¢ ¥ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “Ä  ¤ ” •  —¡¡¨  Š¡²“ÁActive– k é¢¯“¢°“¢·“¡¡¨áw™k²Žw˜ k é k g•Artwork Interpress•Bounds:0.0 mm xmin 0.0 mm ymin 178.1528 mm xmax 88.20494 mm ymax –G91.02716 mm topLeading 91.02716 mm topIndent 1.411111 mm bottomLeading šž=™=Kšœ™Kšœœ ˜#šžœœœœ˜EKš œ œœœ œ˜VK˜K˜—šžœ œ˜9Kšœœ™Kšœœ˜Kšœ œ˜$Kšœœ˜K˜K˜—šžœœœœ˜SKšœ˜Kšœœ"™8Kšœœ ˜4Kšœœ˜Kšœ ˜ K˜K˜—K˜K™Kšœœ ˜šžœœœœ˜?šœ œ˜Kšœ œ˜Kšœ œ˜Kšœœ˜Kšœœ˜Kšœ œ˜Kšœ˜Kšœ˜ K˜—K˜K˜—šž œœœ˜3Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜"Kšœœ˜Kšœ˜K˜—K˜K™šžœ œ+œ˜mKšœ%˜%Kšœ%™%Kšœ#˜#K˜K˜—šžœ œ+œ˜nKšœ%˜%Kšœ%™%Kšœ#˜#KšœP˜PKšœ5˜5K˜K˜K˜—K˜K™šžœœœ)˜HKšœ$™$Kšœ˜Kšœ"˜"K˜K˜—šžœœœ)˜IKšœ$™$Kšœ˜Kšœ˜Kšœ"˜"K˜—K˜K™š žœœœRœ œ˜ŸKšœ>˜>Kšœ/˜/K˜K˜—š žœœœRœ œ œ˜®Kšœ>˜>Kšœ/˜/K˜K˜K˜—K™K™šž œœœ˜/K™>KšœÏb œü™‹KšœŸ œ•™¢KšœŸ œM™ZK˜Kšœ3˜3Kšœ-˜-Kšœ/˜/Kšœ˜Kšœ˜Kšœ œ˜.Kšœ"˜"Kšœœœ ˜:Kšœ œ˜,K˜šœœ˜Kšœ˜Kšœ˜Kšœ$˜$K˜—šœ˜Kšœ1˜1KšŸ™Kšœ˜Kšœ<˜K™>KšœŸ œü™‹KšœŸ œ•™¢KšœŸ œM™ZK˜Kšœ3˜3Kšœ-˜-Kšœ/˜/Kšœ˜Kšœ˜Kšœ œ˜.Kšœ"˜"Kšœœœ ˜:Kšœ œ˜,K˜šœœ˜Kšœ˜Kšœ˜KšœÏc˜9Kšœ%˜%K˜—šœ˜Kšœ1˜1KšŸ™Kšœ˜Kšœ=˜=KšŸ™Kšœ˜Kšœ%˜%KšŸ™Kšœ˜Kšœ`˜`Kšœ0˜0K˜—K˜K˜—K˜š žœœœœœ˜WK™RK™]K™7Kšœl™lKšœ1™1Kšœ3˜3Kšœ/˜/Kšœ-˜-Kšœ œ˜.Kšœ"˜"Kšœœœ ˜:Kšœ œ˜,K˜Kšœ4œ˜Ršœœ˜&KšŸ$™$Kšœœ˜Kšœ˜Kšœ&˜&Kšœ;˜;Kšœ#˜#K˜—šœ˜Kšœ4˜4K˜KšŸ™Kšœ*˜*Kšœ(˜(Kšœ;˜;Kšœ'˜'Kšœ'˜'KšŸ™Kšœ&˜&Kšœ;˜;Kšœ#˜#KšŸ™Kšœ&˜&Kšœ˜Kšœ>˜>Kšœ/˜/Kšœ#˜#K˜Kšœ3˜3K˜—K˜K˜—šžœœ&œ˜cKšœ"˜"Kšœ˜Kšœ2˜2Kšœ ˜ Kšœ1˜1K˜K˜—š žœœœœœ˜WKšœ˜Kšœ˜Kšœ-˜-Kšœ"˜"Kšœœœ ˜:Kšœ œ˜,KšŸ$™$KšœF˜FKšœ@˜@Kšœ"˜"KšŸ™Kšœ˜Kšœ>˜>Kšœ/˜/Kšœ#˜#Kšœœ˜K˜—K˜š žœœœNœœ˜Kšœ?™?™SKšœT™TKšœI™IKšœQ™Q—Kšœ3˜3Kšœ/˜/Kšœ-˜-Kšœ˜KšœF˜FKšœœœ ˜:Kšœ œ˜,Kšœœœ ˜"K˜KšŸ™Kšœ8™8Kšœ6˜6Kšœ<˜Kšœ œœ(™˜>Kšœ˜K˜—šž œœ+˜=Kšœÿ™ÿKšœT˜TK˜K˜—šžœœœ<œ˜tK™LK˜K˜Kšœ˜Kšœ™K˜K˜!K™0K™8Kšœœ˜Kšœ/˜/šœœ˜Kšœ˜Kšœ˜K˜—KšŸ™Kšœ"˜"š œœœœœ˜_Kšœ(˜(Kšœ)˜)KšŸœ˜-K˜—KšŸ™š œœœ+œœ™IJšœ œ™!Jšœ@™@šœ|œ ™‘JšŸœ ™2Jšœ™—JšœH™HJ™ šœœ ™¥JšŸœ-™AJ™Jšœ™—Kšœ™—KšŸ™š œœœ)œœ˜GJšœ œ˜J˜9šœrœ ˜‡JšŸœ ˜2Jšœ˜—J˜AJ˜ šœ†œ ˜›JšŸœ,˜@J˜Jšœ˜—Jšœ˜—Kšœ.˜.K˜K™—šžœœœ#œ˜\K™RKšœ˜K™K˜!K™8K˜Kšœ0˜0Kšœœœœ˜1Kšœœ˜šœœ œœ˜KšŸ™—š œœœ)œœ™GJšœ œ™Jšœ œ™!JšœH™Hšœœ ™¥KšœI™IJšœ™Kšœ™—Kšœ™KšŸ™—š œœœ'œœ˜EJšœ œ˜Jšœ œ˜J˜Ašœ†œ ˜›KšœI˜IJšœ˜Jšœ˜—Jšœ˜—Kšœ/˜/K˜—K˜K™\K˜šžœœœ)œ˜kKšœ.˜.Kšœ ˜ K˜K˜—šž œœœ7œ™sKšœ ™ Kšœ<™<šœœœ™KšœG™GKšœB™BK™—šœ™Kšœ9™9Kšœ@™@KšœE™EKšœB™BK™—K™—K˜šž œœœ3œ˜mKšœ˜Kšœ5˜5šœœœ˜KšœA˜AKšœ>˜>K˜—šœ˜Kšœ5˜5Kšœ?˜?KšœA˜AKšœ>˜>K˜—K˜—K˜šžœœœ=œœœœ ž˜©K˜K˜!J˜9šœrœ ˜‡Jšœœ Ÿ œ0˜_Jšœ˜—J˜Ašœ†œ ˜›Jšœœ Ÿœ<˜mJšœ˜—J˜J˜—šžœœœAœœœœ™¯K™-K™5Jšœ=™=šœvœ ™‹Jšœœ Ÿ œ0™_Jšœ™—JšœE™EšœŠœ ™ŸJšœœ Ÿœ<™mJšœ™—K™K™—šžœœœ*œ™oKšœr™rKšœ@™@K™8Kšœ œ™K™Kšœœ œœ™Kšœœœœ™1JšœH™Hšœœ ™¥KšœI™IKšœ™Kšœ™—K™K™—šžœœœ&œ˜iKšœr™rKšœ>™>K˜.Kšœ œ˜K˜Kšœœ œœ˜Kšœœœœ˜1JšœA˜Ašœ†œ ˜›KšœI˜IKšœ˜Kšœ˜—K˜K˜—šžœœœ:œœœœ˜¢K™¿Kšœœ˜šœœ˜Kšœ˜Kšœ˜K˜—šœœ˜šœ˜JšœQ˜QJ˜—šœ ™ JšœU™UJ™—˜KšŸ™šœœ˜ Kšœ(˜(Kšœ-˜-K˜—K˜—Jšœœ˜—K˜K™—K™K™\K˜šžœœœ˜=Kšœœ˜K˜K˜—šžœœœ,œ™tKšœ5™5šœœœ™Kšœ@™@K™—K™K™—šžœ œ:™WKšœ ™ K™Kšœ>™>šœœœ™Kšœ>™>KšœB™Bšœœ)œ™5KšœC™CKšœB™BK™—K™—K™K™—šžœœœ(œ ˜pKšœ3˜3šœ œœ˜KšœD˜DK˜—K˜K˜—šžœœœ6˜QKšœ!˜!K˜Kšœ:˜:šœ œœ˜Kšœ=˜=Kšœ?˜?šœœ'œ˜3Kšœ9˜9Kšœ>˜>K˜—K˜—K˜K˜—šžœœœ+™LKšœÁ™ÁKšœ™š œœœ.œœ™RJšœœ™ Jšœ™—K™K™—šžœœœ'™FKšœ½™½K™K™—K™K™K™K™/šž œœ7˜GK˜šœ˜™ Kšœœ#™=K™—˜Kšœœ!˜9K˜—˜ Kšœ ˜ K˜—Kšœœ˜—K˜K˜—šžœœw˜‘Kšœ©™©Kšœ"™"Kšœ#˜#Kšœœœ#™;Kšœœœ!˜7Kšœ˜Kšœœ™Kšœœ˜š œ œœ-œ œ™WKšœ œ™'Kšœ*™*šœœ)œ™5KšœB™BKšœœ"™