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; 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[]]; }; }; 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; }; 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; }; 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; }; 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; }; 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; }; 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. &ôGGAlignImpl.mesa Created by Eric Bier on June 5, 1985 3:41:39 pm PDT. Last edited by Bier on January 28, 1987 3:02:03 pm PST. Pier, October 21, 1986 6:15:54 pm PDT Computing the Triggers The set of alignment objects is computed in two steps. First, we determine which joints, control points, segments, and slice points in the scene are current triggers; that is, may trigger the display of one or more alignment objects. We put these in an object bag called the Current Trigger bag. Next we determine the set of alignment objects each object in the Current Trigger bag will trigger. This set is represented by the selected slopes, angles, radii, and distances in the user interface. We filter the Current Trigger bag through these filters to build up an object bag called the Current Object bag. We next build another trigger bag, called the Scene Trigger bag, which contains those scene objects which are gravity active; that is, they can attract the caret. This bag is generated from the set of all scene objects by removing all objects which are moving or partly moving. Starting today (June 25, 1986) two trigger bags will be maintained. The Current Trigger Bag contains objects which are hot, naturally or by heuristics, but are not moving, including the anchor. The Scene Trigger Bag contains those scene objects which are themselves gravity active, including the anchor. This bag is generated from the set of all scene objects by removing all objects which are moving. The two trigger bags are recomputed at these times: Current Triggers: Updated whenever an Add or Drag operation is performed, or whenever anything becomes hot or cold. Scene Triggers: Updated whenever an Add or Drag operation is performed. Computing the Objects In parallel with the above, we also maintain an ObjectBag. The Current Object Bag is the Current Trigger Bag after it has been filtered through all current filters. There is no need for a Scene object bag, since the Scene Trigger Bag itself can be used for this purpose. The object bag is recomputed at these times: Current Objects. Updated whenever an Add or Drag operation is performed, whenever anything becomes hot or cold, or whenever the set of current filters changes. Displaying the Objects Starting today (June 25, 1986) alignment objects are shown at all times. An option to turn them off will be provided later. Hence the alignment objects must be redrawn whenver the Current Object Bag changes. It is drawn on a special foreground plane, so it need not be redrawn if it hasn't changed. [Artwork node; type 'ArtworkInterpress on' to command tool] Compute the Alignment Objects as illustrated. We have two trigger bags and one object bag to get into shape. The Current 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 Current Object Bag is made by filtering the Current Trigger Bag through the currently selected filters. The Scene Triggers 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. Steps (1) and (2) Build the Current Trigger Bag Step (3) Eliminate MOVING Objects Step (4) Compute the Alignment Objects Steps (6) and (7) Compute the Gravity Active Scene Objects [Artwork node; type 'ArtworkInterpress on' to command tool] Compute the Gravity Active Scene Objects If we are about to do an Add, then we expect that the triggerBag is OK except that the moving trajectory's sequences are obsolete, the moving segment must be removed, and heuristic objects must be added. The sceneBag is OK except that moving objects must be removed. The objectBag should then be regenerated from the new triggerBag. repaintNeeded is TRUE if the currentObject bag is altered. Steps (1) and (2) Build the Current Trigger Bag Step (4) Compute the Alignment Objects Steps (6) and (7) Compute the Gravity Active Scene Objects First, get rid of all references to the obsolete outline. 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. Add the whole object to the scene bag. Add Alignment Objects to the objectBag, using the triggers in the triggerBag. The anchor as a trigger. Triggers from outlines. Triggers from slices. A single new trigger has been added to the triggerBag. Add to the objectBag, all alignment objects generated by that trigger. Returns a list of the new alignment objects that were generated. The anchor as a trigger. Triggers from a slice. Triggers from a slice. (1) Put Hot Objects in the Trigger Bag. (2) Put More Objects in the Trigger Bag using Heuristics. In the case of an Add operation, allow the caret chair to trigger even if it is not hot. In the case of a Drag operation, allow the chair being dragged to trigger even if not hot. A later call to RemoveMoving will keep only the stationary parts of the trajectories being dragged. (3) Removing any Triggers which are MOVING. 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. If we are about to add a new point to a trajectory, then we must remove any references to the new joint or its adjacent segment. Yipes! fixedParts has different meanings for Outlines and Slices. -- Bier, January 7, 1987 trim off the selected or dangling parts from the sliceD parts selSliceD: OutlineDescriptor _ GGSelect.FindSelectedOutline[outlineD.slice, gargoyleData.scene, normal]; IF selSliceD=NIL THEN RETURN; -- nothing to trim trim off the selected parts from the sliceD parts When this procedure is called, all sequences in triggerBag are passed to filterSeqProc. filterSeqProc should return those parts which should NOT be deleted, returning NIL or an empty sequence if all parts should remain on the list. Similarly for slices in the triggerBag (4) For Each Trigger, Construct the Appropriate Alignment Objects SlopeLine Circle Parallel Lines at Given Distance AngleLines (4') Hooks for the future when users can write their own alignment line generators. UserDefinedPointFilter: PUBLIC PROC [triggerBag: TriggerBag, objectBag: ObjectBag, 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, objectBag]; }; circle: Circle => { GGGravity.JointAddCircle[circle, point, i, NARROW[l.first.shape, Sequence].traj, objectBag]; }; ENDCASE => ERROR; ENDLOOP; ENDLOOP; ENDLOOP; }; UserDefinedSegmentFilter: PUBLIC PROC [triggerBag: TriggerBag, objectBag: ObjectBag, 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, objectBag]; }; circle: Circle => { GGGravity.SegmentAddCircle[circle, point, next.index, NARROW[feature.shape, Sequence].traj, objectBag]; }; ENDCASE => ERROR; ENDLOOP; ENDLOOP; ENDLOOP; }; Building a TriggerBag WHY DOES THIS PROC ONLY COPY SEQUENCES ?? WHY DOES THIS PROC ONLY MOVE SEQUENCES AND SLICES ?? Does not copy from. CON'es its elements right into to. copyD _ slice.class.copyDescriptor[oldD]; -- should be done some day For use in the glorious future. TAddTriggersFilter: PUBLIC PROC [triggerBag: TriggerBag, filterSeqProc: FilterSequenceProc, filterSliceProc: FilterSliceProc, gargoyleData: GargoyleData] = { 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 _ GGObjects.TrajsInScene[gargoyleData.scene]; FOR traj: Traj _ GGObjects.NextTraj[trajGen], GGObjects.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 _ GGObjects.SlicesInScene[gargoyleData.scene]; FOR slice: Slice _ GGObjects.NextSlice[sliceGen], GGObjects.NextSlice[sliceGen] UNTIL slice = NIL DO add _ filterSliceProc[slice]; IF add THEN AddSliceTrigger[slice, triggerBag]; ENDLOOP; }; Add and Remove scene parts to/from a TriggerBag Add to the object bag the midpoints of all segments and slices in the trigger bag. Segments as triggers. Slices as triggers. FeatureList Utilities Miscellaneous Ê2Û˜Icode™K™4™7K™%—K˜šÏk ˜ Kšœ¾˜¾—K˜šÏn œœ˜Kšœq˜xKšœ ˜—˜Kšœœ ˜5Kšœœ˜!Kšœœ˜%Kšœœ˜#Kšœœ&˜AKšœœ˜Kšœœ˜2Kšœ œœ˜'Kšœœ˜3Kšœœ˜4Kšœœ˜0Kšœ œ˜)Kšœœ!˜3Kšœœ˜!Kšœœ˜3Kšœœ˜Kšœ œ˜-Kšœ œ˜%Kšœœœ˜3Kšœœ%˜?Kšœœ˜!Kšœœ˜/Kšœœ˜0Kšœœ˜3Kšœœ#˜;Kšœœ"˜:Kšœœ˜!Kšœ œ˜'Kšœœ˜4Kšœœ!˜7Kšœœ#˜7Kšœ œ˜'Kšœœ"˜9Kšœœ˜!Kšœœœ˜/Kšœœ#˜;Kšœœ)˜GKšœœ˜3Kšœ œ˜+Kšœœ˜Kšœœ˜.Kšœ œœ˜%Kšœœ"˜5Kšœœ˜#K˜Kšžœœ œ˜3—K˜body™L™þLšœxÏiœ™™”L™3L™tL™G—™L™L™,L™ —™L™­—™Kšžœœœœ˜%Kšžœœœœ˜$Kšžœœœœ˜%I artworkFigure• Interpress©Interpress/Xerox/3.0  f j k j¡¥“ÄWB ¤ ¨  áh¡£Ö ¢ ¨ r j  º ¢ ¨¡¡¨ÄWB ¤ ¨ r j¡¥“ÄWB ¤ ¨¡¡¨ˆˆˆ£¡ˆ¡¡ÅXeroxÅResearchÅ RGBLinear£¡¡¦ ç • ” ç­“Ä! &™Ä`Ä$u&¡“]Ž]Ä`]Ä! &¡“Ž¡¡¡¡™¢¯“¡¡¨¢·“Ä! &™Ä`Ä$u&¡“]Ž]Ä`]Ä! &¡“Ž¡¸ˆˆˆ£¡ ” ç­“±ÄP!L™±ÄS‹L±ÄVõL¡“õŽõÄS‹LõÄP!L¡“±Ž¡¡¡¡™¢¯“¡¡¨¢·“±ÄP!L™±ÄS‹L±ÄVõL¡“õŽõÄS‹LõÄP!L¡“±Ž¡¸ˆˆˆ£¡ ” ç­“Ä2ñïÄ™UÄS‹LÄ2ñïÄ¡“Ä¿ŽšÄS‹LÄ¿Ä¡“Ä2ñ¡¡¡™¢¯“¡¡¨¢·“Ä2ñïÄ™UÄS‹LÄ2ñïÄ¡“Ä¿ŽšÄS‹LÄ¿Ä¡“Ä2ñ¸ˆˆˆ£¡ ” ç­“ÄÓÄ™ ÄS‹LÄÓÄ¡“\ŽÄ!ÃÄS‹L\Ä¡“ÄÓŽ¡¡¡¡™¢¯“¡¡¨¢·“ÄÓÄ™ ÄS‹LÄÓÄ¡“\ŽÄ!ÃÄS‹L\Ä¡“ÄÓŽ¡¸ˆˆˆ£¡ ” ç­“UÖ™UăUí¡“šŽšÄƒšÖ¡“UŽ¡¡¡¡™¢¯“¡¡¨¢·“UÖ™UăUí¡“šŽšÄƒšÖ¡“UŽ¡¸ˆˆˆ£¡ ” ç­“Äï‘ÁÄDR™§Ä`Äï‘ÁÄ`åb¡“ÄgV׎Ä`ÄgV×ÄDR¡“Äï‘ÁŽ¡¡¡¡™£¯“¡¡¨¢·“Äï‘ÁÄDR™§Ä`Äï‘ÁÄ`åb¡“ÄgV׎Ä`ÄgV×ÄDR¡“Äï‘ÁŽ¡¸¢¯“¡¡¨¢·“Ä¯Ö™Ę¢¯“¡¡¨¢·“sÄ»´™Ä¯í—|ÄŽqk—˜¢¯“¡¡¨¢·“Ä<Ç&Ä! &™f˜¢¯“¡¡¨¢·“ÄÄj‘™Ä<Ç&Ä! &—ĹÄj‘—˜ÅXeroxÅ PressFontsÅ Helvetica-mrr£¡ “ª ¤ ”¡•¡ —¡¡¨Ä%Ã2ļŠÁ Heuristics–¡¡¨¡ —¡¡¨Ä&Y2ÄÄöµŠÁTriggers–¡¡¨¢¯“¡¡¨¢·“Ä/O"ÄTáL™ ÄS‹L—Ä/O"ÄR5L—˜¢¯“¡¡¨¢·“šÄS‹L™±Ž˜¡ —¡¡¨Ä«1šÄo ‰ŠÁ Alignment–¡¡¨¡ —¡¡¨Ä®ÍšÄgðwŠÁObjects–¡¡¨¡ —¡¡¨Ä6×2ÄCŠÁ Not Moving–¡¡¨¢¯“¡¡¨¢·“õÄS‹L™ Ž˜¢¯“¡¡¨¢·“Ä#9"ÄTáL™±ÄS‹L—Ä#9"ÄR5L—˜¡ —¡¡¨Äí¶ŠÁActive Triggers–¡¡¨¢¯“¡¡¨¢·“įÄ5&™í˜¢¯“¡¡¨¢·“ÄÄ?¸C™Ä<Ç&Ä$u&—ĹÄ?¸C—˜¢¯“¡¡¨¢·“Ä`™Ž˜¢¯“¡¡¨¢·“Ä0 "Ä#k&™Ä`—Ä0 "Ä"&—˜¡ —¡¡¨ÄÿÄ\ãgŠÁ Firing Rules–¡¡¨¡ —¡¡¨Ä"Õ2Ä"0ŠÁHot Scene Objects–¡¡¨¢¯“¡¡¨¢·“įęf˜¢¯“¡¡¨¢·“sÄâŠÝ™Ä¯Ä—|ıV­—˜¡ —¡¡¨Äy2OÄ•ÉÏŠÁFilters–¡¡¨ r j¡ ¤ ¨¡¡¨£¯“HkHþ¡¹¡¡¨£¯“Hþpþ¡¹¡¡¨£¯“pþpk¡¹¡¡¨£¯“pkHk¡¹ k é¡ —¡¡¨Ä_sˆÄD-0ŠÁ Scene Objects–¡¡¨¢¯“¡¡¨¢·“Ä<Ç&Ä™Ä$u&˜¢¯“¡¡¨¢·“sÄ'Ñ"™Ä¯Ä—|Ä'Ñ"—˜¡ —¡¡¨]ŠÁ1–¡¡¨¡ —¡¡¨§ãŠÁ2–¡¡¨¡ —¡¡¨ÖÓŠÁ3–¡¡¨¡ —¡¡¨bžŠÁ4–¡¡¨¡ —¡¡¨™ƒŠÁ5–¡¡¨ r jÄ¿£Ø ¤ªç ¢ ¥ ¨¡¡¨Ä£Ø¿¯“¢°“Ÿ ™¡ Ÿ ¡“¡¸ k é r jÄ¿£Ø ¤`” ¢ ¥ ¨¡¡¨Ä£Ø¿¯“¢°“Ÿ ™¡ Ÿ ¡“¡¸ k é r jÄ¿£Ø ¤Ù× ¢ ¥ ¨¡¡¨Ä£Ø¿¯“¢°“Ÿ ™¡ Ÿ ¡“¡¸ k é r jÄ¿£Ø ¤d¢ ¢ ¥ ¨¡¡¨Ä£Ø¿¯“¢°“Ÿ ™¡ Ÿ ¡“¡¸ k é r jÄ¿£Ø ¤œ‡ ¢ ¥ ¨¡¡¨Ä£Ø¿¯“¢°“Ÿ ™¡ Ÿ ¡“¡¸ k é k é k é k g•Artwork Interpress•Bounds:0.0 mm xmin 0.0 mm ymin 113.2417 mm xmax 70.55555 mm ymax –G73.37777 mm topLeading 73.37777 mm topIndent 1.411111 mm bottomLeading šž=™=K™—K™-K™šžœœœ$œ˜JK™>KšœÏbœü™“Kšœ œU™kKšœ œ•™«K˜KšœE˜EKšœB˜BKšœA˜AK˜šœ$œ˜,Kšœ)˜)Kšœ)˜)K˜—šœ˜šœJ˜JKš /™/—Kšœ!˜!Kšœ2˜2Kšœ/˜/Kšœ0˜0šœ œ˜3Kš !™!—šœ! œ˜8Kš &™&—Kšœ)˜)šœ>˜>Kš :™:—Kšœ# œ˜)Kšœ>Ïc?˜}KšœI˜IK˜—K˜Icenter–G40.56945 mm topLeading 40.56945 mm topIndent 1.411111 mm bottomLeading –:0.0 mm xmin 0.0 mm ymin 75.49444 mm xmax 37.74722 mm ymax – Interpress–Ó Interpress/Xerox/3.0  f j k j¡¥“ÄWB ¤ ¨  v ¡£µ ¢ ¨ r j  º ¢ ¨¡¡¨ÄWB ¤ ¨ r j¡¥“ÄWB ¤ ¨¡¡¨ˆˆˆ£¡ˆ¡¡ÅXeroxÅResearchÅ RGBLinear£¡¡¦ ç • ” ç­“±ÄP!L™±ÄS‹L±ÄVõL¡“õŽõÄS‹LõÄP!L¡“±Ž¡¡¡¡™¢¯“¡¡¨¢°“±ÄP!L™±ÄS‹L±ÄVõL¡“˜¢¯“¡¡¨¢°“±ÄVõLõÄVõL¡¹¢¯“¡¡¨¢°“õÄVõL™õÄS‹LõÄP!L¡“˜¢¯“¡¡¨¢°“õÄP!L±ÄP!L¡¹¡¯“ˆˆˆ£¡ ” ç­“BęĻHïÄS‹LBÄ¡“Ä¿ŽšÄS‹LÄ¿Ä¡“BŽ¡¡¡¡™¢¯“¡¡¨¢°“BęĻHïÄS‹LBÄ¡“˜¢¯“¡¡¨¢°“BÄÄ¿Ä¡¹¢¯“¡¡¨¢°“Ä¿Ä™šÄS‹LÄ¿Ä¡“˜¢¯“¡¡¨¢°“Ä¿ÄBÄ¡¹¡¯“ˆˆˆ£¡ ” ç­“Äï‘ÁÄDR™§Ä`Äï‘ÁÄ`åb¡“ÄgV׎Ä`ÄgV×ÄDR¡“Äï‘ÁŽ¡¡¡¡™£¯“¡¡¨¢°“Äï‘ÁÄDR™§Ä`Äï‘ÁÄ`åb¡“˜£¯“¡¡¨¢°“Äï‘ÁÄ`åbÄgV×Ä`åb¡¹£¯“¡¡¨¢°“ÄgV×Ä`åb™Ä`ÄgV×ÄDR¡“˜£¯“¡¡¨¢°“ÄgV×ÄDRÄï‘ÁÄDR¡¹¡¯“¢¯“¡¡¨¢°“šÄS‹L±ÄS‹L¡¹¡¯“ÅXeroxÅ PressFontsÅ Helvetica-mrr£¡ “ª ¤ ”¡•¡ —Į͚ÄgðwŠÁObjects–¡ —Ä6×2ÄCŠÁ Not Moving–¢¯“¡¡¨¢°“Ä#9"ÄTáL±ÄS‹L¡¹¢¯“¡¡¨¢°“±ÄS‹LÄ#9"ÄR5L¡¹¡¯“¢¯“¡¡¨¢°“ÄYÄïëÑÄ`åb¡¹¢¯“¡¡¨¢°“ÑÄ`åbÄkÄï롹¡¯“¢¯“¡¡¨¢°“hÄhĸ¡¹¡¯“¢¯“¡¡¨¢°“ćÄâŠÝhÄ¡¹¢¯“¡¡¨¢°“hÄęıV­¡¹¡¯“¡¯“¡¡¨¢°“Ä|ÊEҙēÈEÒÄ|ÊEÒ¡“¡¸¡¯“¢¯“¡¡¨¢°“ÑÄP!LÑÄ`åb¡¹¡¯“¡ —LŠÁ All Objects–¡ —ÑΊÁ7–¡ —€ŠÁ6–¡¯“¡¡¨¢°“ÄõE ™Ä,óE ÄõE ¡“¡¸¡¯“¡ —ÄM¶ŠÁ Scene Objects–¡ —ÄÄÏ›àŠÁScene– k é k é k gšž=™=—šžœœœ$œ˜NšœA˜AKš (™(—Kšœ˜Kšœ0˜0Kšœ*˜*Kšœ'˜'šœ˜Kšœ œ¡˜.Kšœ@˜@Kšœœ˜—Kšœ'˜'K˜K˜—šžœœœœ˜9Kšœ˜Kšœ2˜2š œ œœDœ œ˜lšœœ˜Jš œœ5œœœ˜dJš œœ7œœœ˜jJšœœ˜—Jšœ˜—Jšœœ˜K˜K˜—š žœœœ$œœœ˜kK™ÎKšœ:™:KšœE˜EKšœB˜BKšœA˜AK˜Kšœœœ˜Kšœ6œH˜€šœ$œ˜,Kšœœ˜Kšœ)˜)KšœA˜AK˜—šœ˜šœM˜MKš /™/—Kšœ4˜4šœ9˜9Kš &™&—Kšœ)˜)šœ>˜>Kš :™:—KšœB˜BKšœ>¡?˜}KšœL˜LK˜—K˜K˜—šžœœ1œ˜Wšœ˜Kšœ œ¡˜.Kšœ3˜3Kšœœ˜—KšœK˜KKšœ'˜'KšœJ˜JK˜K˜—šžœœœK˜uKšœE˜EKšœB˜BKšœA˜AK˜Kšœ˜šœ œ˜Kš 9™9—Kšœ œ˜šœ ˜KšœW˜WKšœ˜—Kšœ œ˜šœ ˜Kšœ[˜[Kšœ˜Kš q™qKš 9™9—KšœI˜Išœœœ˜Kšœ`˜`Kšœ%˜%K˜Kš &™&—Kšœ3˜3Kšœ#˜#K˜K˜K˜—šžœœœO˜jK™MK˜Kšœ˜Kšœ˜K˜K˜!K˜0K˜8Kšœœ˜ Kšœ(˜(KšœH˜Hšœ$œ˜,Kšœ$˜$Kšœ˜K˜—Kš ™Kšœ"˜"š œœœœœ˜TKšœ ˜ Kšœœ˜=Kš œ˜.K˜—Kš ™š œœœ+œœ˜IJšœ œ˜!Jšœ@˜@šœ|œ ˜‘Jš œ!˜3Jšœ˜—JšœH˜HJ˜ šœœ ˜¥Jš œ.˜BJ˜Jšœ˜—Kšœ˜—Kš ™š œœœ)œœ˜GJšœ œ˜J˜9šœrœ ˜‡Jš œ!˜3Jšœ˜—J˜AJ˜ šœ†œ ˜›Jš œ-˜AJ˜Jšœ˜—Jšœ˜—KšœG˜GK˜K™—š žœœœJœœœ˜˜K™ÀKšœ(˜(Kšœœ˜šœ$œ˜,Kšœ$˜$Kšœ˜K˜—šœœ˜˜Kš ™šœœ˜ Kšœ(˜(Kšœ.˜.K˜—K˜—šœ ˜ K˜-˜5Kš ™—Jšœ=˜=šœvœ ˜‹Jšœœ   œ1˜`Jšœ˜—JšœE˜EšœŠœ ˜ŸJšœœ  œ=˜nJšœ˜—J˜—šœ˜K˜˜!Kš ™—J˜9šœrœ ˜‡Jšœœ   œ1˜`Jšœ˜—J˜Ašœ†œ ˜›Jšœœ  œ=˜nJšœ˜—J˜—Jšœœ˜—K˜K™—K˜Kšœ(™(šžœœ9˜QKšœ˜Kšœ˜K˜1Kšœ=˜=šœpœ œ˜ŠKšœœ_˜iKšœ?˜?Kšœ ˜ Kšœ˜—K˜K˜—šžœœ9˜NKšœ˜KšœZ˜Zšœrœ œ˜ŠKšœA˜AKšœ ˜ Kšœ˜—K˜K˜—Kšœ9™9šž œœ$œ˜XK™˜Kšœ˜KšœIœœ˜Wšœ˜˜ K˜1Kšœ@˜@šœpœ œ˜ŠKšœ7œ˜˜>K˜—šœ˜KšœS˜SKšœ?˜?Kšœ?˜?Kšœ>˜>K˜—K˜—K˜šžœœ9˜OKšœ:˜:K˜K˜—šžœœœ)œ˜kKšœ.˜.Kšœ ˜ K˜—šžœœœ˜=Kšœœ˜K˜—K˜šžœœ:˜TKšœ˜K˜K˜Kšœ>˜>šœœœ˜KšœZ˜ZKšœB˜Bšœœ7œ˜CKšœ=˜=KšœB˜BK˜—K˜—K˜K˜—šžœœœ+˜JKšœ˜Kšœ3˜3Kšœ œœE˜ZK˜—K˜K™K™Kšžœœœœ˜¨K˜šžœœœ~™KšœØ™ØKšœœ™ Kšœ™K™KšœD™DšœGœœ™]Kšœ&™&Kšœ¡/™CKšœ$™$Kšœ™—Kšœ7™7šœMœ œ™dKšœ™Kšœœ$™/Kšœ™—K™K™—Kšœ/™/K™šž œœ7˜GK˜Kšœ˜˜ Kšœœ#˜=K˜—˜ Kšœœ!˜9K˜—˜ Kšœ ˜ K˜—Kšœœ˜K˜K˜—šžœœ9˜QKšœ*˜*Kšœ˜Kšœ;˜;šœYœ œ˜rKšœ0˜0Kšœ ˜ Kšœ˜—K˜K˜—šž œœ9˜KKšœ˜KšœG˜GšœMœ œ˜dKšœ,˜,Kšœ ˜ Kšœ˜—K˜K˜—šž œœ9˜HKšœ˜šœ$˜-K•StartOfExpansion"[anchor: GGInterfaceTypes.Caret]šœ;˜;Kšœ ˜ K˜—K˜K˜—šžœ œR˜nK™RšÐbnœœ œ˜;KšœX˜XK˜—Kšœ˜K˜K˜!K˜8K˜Kšœœ˜šœ1œœ˜?Kš ™—š œœœ.œœ˜LJšœ œ˜Jšœ œ˜!JšœH˜Hšœœ ˜¥Kš œ˜1Jšœ˜Kšœ˜—Kšœ˜Kš ™—š œœœ,œœ˜JJšœ œ˜Jšœ œ˜J˜Ašœ†œ ˜›Jš œ˜1Jšœ˜Jšœ˜—Jšœ˜—K˜—K˜Kšœ™K™š žœœœœœ#˜nš œœœœœ˜:Kš œœ3œœœ˜`Kšœ˜—Kšœœ˜ K˜K˜—š ž œœœœœ!˜eš œœœœœ˜:Kš œœ/œœœ˜\Kšœ˜—Kšœœ˜ K˜K˜—šžœœ œœœœœ˜ŽKšœœœœ˜!Kšœœœ˜(Kšœ œœœ˜)šœ œ˜Kšœœ'œ˜6Kšœ7œ˜?Kšœ˜Kšœ˜Kšœ˜—Kšœ˜Kšœ˜K˜—šžœœœœœœœ˜ŒKšœœœœ˜!Kšœœœ˜(Kšœ œœœ˜)šœ œ˜Kšœœ6œ˜EKšœ7œ˜?Kšœ˜Kšœ˜Kšœ˜—Kšœ˜Kšœ˜K˜—šžœœœœœœœ˜Kšœœœœ˜!Kšœœœ˜(Kšœ œœœ˜)šœ œ˜Kšœœ6œ˜EKšœ7œ˜?Kšœ˜Kšœ˜Kšœ˜—Kšœ˜Kšœ˜K˜—šžœœœœœœœ˜ˆKšœœœœ˜!Kšœœœ˜(Kšœ œœœ˜)šœ œ˜Kšœœ4œ˜CKšœ7œ˜?Kšœ˜Kšœ˜Kšœ˜—Kšœ˜Kšœ˜K˜—šž œœ!œœœœœœœ˜Kšœœœ ˜'Kš œœœœœœ˜,Kšœcœœ˜tKšœ œœœ˜+Kšœ œœ˜(šœ˜Kšœ˜Kšœ˜K˜—Kšœ˜K˜—šžœœ,œœœœœ˜ƒKšœœœ ˜'Kšœ œœ˜Kš œœœœœ˜&Kšœkœœ˜|Kšœ œœ˜%Kšœ œœ˜(šœ˜Kšœ˜Kšœ˜K˜—Kšœ˜K˜K˜—šž œœœœœœœ˜qKšœœœ ˜'Kšœ œœ˜Kšœ@˜@Kšœ œœ˜%Kšœ œœ˜(šœ˜Kšœ˜Kšœ˜K˜—Kšœ¡˜K˜K˜—šž œœœ%œœœœœ˜K˜Kšœ œ˜(K˜—K˜K™ K™šžœœ ˜=Kšœ˜K˜š œœœ1œœ˜UKšœ˜Kšœ œ˜%Kšœ9˜9Kšœ˜—K˜K˜—šž œœ˜K˜(K˜=Kšœ9œ˜KK˜;Kšœ6œ˜;K˜;Kšœ4œ˜9K˜;Kšœ˜K˜—Kšœœ ˜#šžœœ˜Kšœ%˜%Kšœ˜—K˜Kšœ ˜ K˜K™Kšœ˜K˜—…—uÎë