DIRECTORY AtomButtons, AtomButtonsTypes, CodeTimer, Feedback, FunctionCache, Imager, ImagerBackdoor, Matrix3d, GList, Lines2d, Process, Rope, SV2d, SV3d, SVAlign, SVAssembly, SVCaret, SVGraphics, SVInterfaceTypes, SVLines3d, SVModelTypes, SVScene, SVSceneTypes, SVSelect, SVState, SVVector3d; SVAlignImpl: CEDAR PROGRAM IMPORTS AtomButtons, CodeTimer, Feedback, GList, Imager, ImagerBackdoor, Matrix3d, Process, SVAssembly, SVCaret, SVGraphics, SVLines3d, SVScene, SVSelect, SVState, SVVector3d EXPORTS SVAlign = BEGIN AlignBag: TYPE = REF AlignBagObj; AlignBagObj: TYPE = SVInterfaceTypes.AlignBagObj; AlignmentLine: TYPE = REF AlignmentLineObj; AlignmentLineObj: TYPE = SVInterfaceTypes.AlignmentLineObj; AssemblyGenerator: TYPE = SVScene.AssemblyGenerator; Camera: TYPE = SVModelTypes.Camera; FeatureData: TYPE = SVInterfaceTypes.FeatureData; FeatureDataObj: TYPE = SVInterfaceTypes.FeatureDataObj; Filters: TYPE = SVInterfaceTypes.Filters; FilterSliceProc: TYPE = SVAlign.FilterSliceProc; Line: TYPE = Lines2d.Line; Line3d: TYPE = SV3d.Line3d; Point3d: TYPE = SV3d.Point3d; PointGenerator: TYPE = SVSceneTypes.PointGenerator; PointPairGenerator: TYPE = SVSceneTypes.PointPairGenerator; ScalarButtonClient: TYPE = AtomButtonsTypes.ScalarButtonClient; Scene: TYPE = SVSceneTypes.Scene; Skitter: TYPE = SVSceneTypes.Skitter; Slice: TYPE = SVSceneTypes.Slice; SliceDescriptor: TYPE = SVSceneTypes.SliceDescriptor; SliceDescriptorGenerator: TYPE = SVSceneTypes.SliceDescriptorGenerator; SliceParts: TYPE = SVSceneTypes.SliceParts; TriggerBag: TYPE = REF TriggerBagObj; TriggerBagObj: TYPE = SVInterfaceTypes.TriggerBagObj; SVData: TYPE = SVInterfaceTypes.SVData; Vector2d: TYPE = SV2d.Vector2d; Vector3d: TYPE = SV3d.Vector3d; Problem: SIGNAL [msg: Rope.ROPE] = Feedback.Problem; alignmentColor: Imager.Color _ ImagerBackdoor.MakeStipple[145065B]; checkerColor: Imager.Color _ ImagerBackdoor.MakeStipple[122645B]; emptyTriggerBag: PUBLIC TriggerBag; CreateTriggerBag: PUBLIC PROC [scene: Scene] RETURNS [triggerBag: TriggerBag] = { triggerBag _ NEW[TriggerBagObj _ [tree: scene.tree, ignoreMoving: FALSE, scene: scene, slices: NIL, anchor: NIL]]; }; FlushTriggerBag: PUBLIC PROC [triggerBag: TriggerBag] = { triggerBag.slices _ NIL; triggerBag.anchor _ NIL; }; CopyTriggerBag: PUBLIC PROC [triggerBag: TriggerBag] RETURNS [copy: TriggerBag] = { copy _ CreateTriggerBag[triggerBag.scene]; copy.ignoreMoving _ triggerBag.ignoreMoving; copy.slices _ NARROW[GList.Copy[triggerBag.slices]]; copy.anchor _ triggerBag.anchor; }; FeatureFromSlice: PUBLIC PROC [slice: Slice, parts: SliceParts _ NIL] RETURNS [feature: FeatureData] = { sliceParts: SliceParts _ IF parts=NIL THEN SVAssembly.NewParts[slice, NIL, [0,0,0], slice].parts ELSE parts; sliceD: SliceDescriptor _ SVAssembly.DescriptorFromParts[slice, sliceParts]; feature _ NEW[FeatureDataObj]; feature.type _ slice; feature.shape _ sliceD; }; FeatureFromAnchor: PUBLIC PROC [anchor: Skitter] RETURNS [feature: FeatureData] = { feature _ NEW[FeatureDataObj]; feature.type _ anchor; -- that is, anchor => the member of the enumerated type feature.shape _ anchor; -- that is, anchor => the parameter }; FillStaticTriggerBag: PUBLIC PROC [anchor: Skitter, scene: Scene, heuristics: BOOL, triggerBag: TriggerBag] = { AddAnchorTrigger[anchor, triggerBag]; AddAllHotSlices[scene, triggerBag]; }; FillDynamicTriggerBag: PUBLIC PROC [anchor: Skitter, scene: Scene, heuristics: BOOL, triggerBag: TriggerBag] = { AddAnchorTrigger[anchor, triggerBag]; AddAllHotSlices[scene, triggerBag]; AddHeuristics[scene, heuristics, $Drag, triggerBag]; RemoveMoving[scene, triggerBag]; }; FillStaticSceneBag: PUBLIC PROC [scene: Scene, sceneBag: TriggerBag] = { sceneBag.tree _ scene.tree; sceneBag.ignoreMoving _ FALSE; IF sceneBag.tree # NIL THEN sceneBag.tree.outOfDate _ TRUE; sceneBag.scene _ scene; AddAllSlices[scene, sceneBag]; }; FillDynamicSceneBag: PUBLIC PROC [scene: Scene, sceneBag: TriggerBag] = { sceneBag.ignoreMoving _ TRUE; IF sceneBag.tree # NIL THEN sceneBag.tree.outOfDate _ TRUE; AddAllSlices[scene, sceneBag]; RemoveMoving[scene, sceneBag]; }; emptyAlignBag: PUBLIC AlignBag; CreateAlignBag: PUBLIC PROC [] RETURNS [alignBag: AlignBag] = { alignBag _ NEW[AlignBagObj _ [ slopeLines: NIL, slopePlanes: NIL, anchor: NIL ]]; }; FlushAlignBag: PUBLIC PROC [alignBag: AlignBag] = { alignBag.slopeLines _ NIL; alignBag.slopePlanes _ NIL; alignBag.anchor _ NIL; }; JointAddSlopeLine: PUBLIC PROC [azimuth, slope: REAL, point: Point3d, alignBag: AlignBag] RETURNS [feature: FeatureData] = { line: Line3d; direction: Vector3d; oldFeature: FeatureData _ FindLine[point, azimuth, slope, alignBag.slopeLines]; IF oldFeature # NIL THEN { alignmentLine: AlignmentLine _ NARROW[oldFeature.shape]; alignmentLine.triggerPoints _ CONS[point, alignmentLine.triggerPoints]; feature _ NIL; } ELSE { alignmentLine: AlignmentLine; direction _ SVVector3d.VectorFromAngles[azimuth, slope]; line _ SVLines3d.LineFromPointAndVector[point, direction]; alignmentLine _ NEW[AlignmentLineObj _ [line: line, azimuth: azimuth, slope: slope, triggerPoints: CONS[point, NIL]]]; feature _ NEW[FeatureDataObj]; feature.type _ slopeLine; feature.shape _ alignmentLine; AddAlignment[feature, alignBag]; }; }; FindLine: PRIVATE PROC [point: Point3d, azimuth, slope: REAL, list: LIST OF FeatureData] RETURNS [coincident: FeatureData] = { epsilon: REAL = 1.0e-5; line3d: Line3d; alignmentLine: AlignmentLine; FOR l: LIST OF FeatureData _ list, l.rest UNTIL l = NIL DO alignmentLine _ NARROW[l.first.shape]; line3d _ alignmentLine.line; IF alignmentLine.azimuth = azimuth AND alignmentLine.slope = slope AND SVLines3d.DistancePointToLine[point, line3d] < epsilon THEN RETURN[l.first]; ENDLOOP; RETURN[NIL]; }; FillStaticAlignBag: PUBLIC PROC [triggerBag: TriggerBag, sceneBag: TriggerBag, filters: Filters, hideAlignments: BOOL, midpoints: BOOL, alignBag: AlignBag] = { BuiltInFilters[triggerBag, filters, hideAlignments, alignBag]; }; FillDynamicAlignBag: PUBLIC PROC [triggerBag: TriggerBag, sceneBag: TriggerBag, filters: Filters, hideAlignments: BOOL, midpoints: BOOL, action: ATOM, alignBag: AlignBag] = { BuiltInFilters[triggerBag, filters, hideAlignments, alignBag]; }; SetStaticBags: PUBLIC PROC [svData: SVData] = { triggerBag: TriggerBag _ svData.hitTest.triggerBag; alignBag: AlignBag _ svData.hitTest.alignBag; sceneBag: TriggerBag _ svData.hitTest.sceneBag; scene: Scene _ svData.scene; anchor: Skitter _ scene.anchor; heuristics: BOOL _ SVState.GetHeuristics[svData]; filters: Filters _ svData.hitTest; hideAlignments: BOOL _ NOT SVState.GetShowAlignments[svData]; midpoints: BOOL _ SVState.GetMidpoints[svData]; IF hideAlignments THEN { FlushAlignBag[alignBag]; FlushTriggerBag[sceneBag]; FillStaticSceneBag[scene, sceneBag]; } ELSE { CodeTimer.StartInt[$SetBagsForAction, $Solidviews]; FlushTriggerBag[triggerBag]; FillStaticTriggerBag[anchor, scene, heuristics, triggerBag]; FlushTriggerBag[sceneBag]; FillStaticSceneBag[scene, sceneBag]; FlushAlignBag[alignBag]; FillStaticAlignBag[triggerBag, sceneBag, filters, hideAlignments, midpoints, alignBag]; CodeTimer.StopInt[$SetBagsForAction, $Solidviews]; }; }; SetDynamicBags: PUBLIC PROC [svData: SVData, action: ATOM] = { triggerBag: TriggerBag _ svData.hitTest.triggerBag; alignBag: AlignBag _ svData.hitTest.alignBag; sceneBag: TriggerBag _ svData.hitTest.sceneBag; scene: Scene _ svData.scene; anchor: Skitter _ scene.anchor; heuristics: BOOL _ SVState.GetHeuristics[svData]; filters: Filters _ svData.hitTest; hideAlignments: BOOL _ NOT SVState.GetShowAlignments[svData]; midpoints: BOOL _ SVState.GetMidpoints[svData]; IF hideAlignments THEN { FlushAlignBag[alignBag]; FlushTriggerBag[sceneBag]; FlushTriggerBag[triggerBag]; FillDynamicSceneBag[scene, sceneBag]; } ELSE { CodeTimer.StartInt[$SetBagsForAction, $Solidviews]; 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, $Solidviews]; }; }; StaticToDynamicBags: PUBLIC PROC [svData: SVData] RETURNS [repaintForeground: BOOL] = { triggerBag: TriggerBag _ svData.hitTest.triggerBag; sceneBag: TriggerBag _ svData.hitTest.sceneBag; alignBag: AlignBag _ svData.hitTest.alignBag; heuristics: BOOL _ SVState.GetHeuristics[svData]; filters: Filters _ svData.hitTest; hideAlignments: BOOL _ NOT SVState.GetShowAlignments[svData]; midpoints: BOOL _ SVState.GetMidpoints[svData]; repaintForeground _ SomeSelectedIsHot[svData.scene] OR SVState.GetHeuristics[svData]; IF NOT SVState.GetShowAlignments[svData] THEN { repaintForeground _ FALSE; FlushAlignBag[alignBag]; svData.hitTest.oldSceneBag _ sceneBag; sceneBag _ StaticToDynamicSceneBag[svData.scene, sceneBag]; svData.hitTest.sceneBag _ sceneBag; } ELSE { CodeTimer.StartInt[$UpdateBagsForAction, $Solidviews]; svData.hitTest.oldTriggerBag _ triggerBag; triggerBag _ CopyTriggerBag[triggerBag]; AddHeuristics[svData.scene, heuristics, $Drag, triggerBag]; RemoveMoving[svData.scene, triggerBag]; svData.hitTest.triggerBag _ triggerBag; svData.hitTest.oldSceneBag _ sceneBag; sceneBag _ StaticToDynamicSceneBag[svData.scene, sceneBag]; svData.hitTest.sceneBag _ sceneBag; svData.hitTest.oldAlignBag _ alignBag; alignBag _ CreateAlignBag[]; BuiltInFilters[triggerBag, filters, hideAlignments, alignBag]; svData.hitTest.alignBag _ alignBag; CodeTimer.StopInt[$UpdateBagsForAction, $Solidviews]; }; }; StaticToDynamicSceneBag: PROC [scene: Scene, sceneBag: TriggerBag] RETURNS [newBag: TriggerBag] = { newBag _ CopyTriggerBag[sceneBag]; RemoveMoving[scene, newBag]; newBag.ignoreMoving _ TRUE; IF newBag.tree # NIL THEN newBag.tree.outOfDate _ TRUE; }; DynamicToStaticBags: PUBLIC PROC [svData: SVData] RETURNS [repaintForeground: BOOL] = { triggerBag: TriggerBag; sceneBag: TriggerBag; alignBag: AlignBag _ svData.hitTest.alignBag; filters: Filters _ svData.hitTest; hideAlignments: BOOL _ NOT SVState.GetShowAlignments[svData]; midpoints: BOOL _ SVState.GetMidpoints[svData]; triggerBag _ svData.hitTest.triggerBag _ svData.hitTest.oldTriggerBag; sceneBag _ svData.hitTest.sceneBag _ svData.hitTest.oldSceneBag; alignBag _ CreateAlignBag[]; BuiltInFilters[triggerBag, filters, hideAlignments, alignBag]; svData.hitTest.alignBag _ alignBag; repaintForeground _ TRUE; }; UpdateBagsForNewSlices: PUBLIC PROC [newSlices: LIST OF Slice, svData: SVData] = { SetStaticBags[svData]; }; AddAllSlices: PROC [scene: Scene, triggerBag: TriggerBag] = { sliceGen: AssemblyGenerator _ SVScene.PrimAssembliesInScene[scene]; feature: FeatureData; FOR slice: Slice _ SVScene.NextAssembly[sliceGen], SVScene.NextAssembly[sliceGen] UNTIL slice = NIL DO feature _ FeatureFromSlice[slice]; AddFeature[feature, triggerBag]; ENDLOOP; }; AddFeature: PROC [featureData: FeatureData, triggerBag: TriggerBag] = { Process.CheckForAbort[]; SELECT featureData.type FROM slice => { triggerBag.slices _ CONS[featureData, triggerBag.slices]; }; anchor => { triggerBag.anchor _ featureData; }; ENDCASE => ERROR; }; AddAlignment: PRIVATE PROC [featureData: FeatureData, alignBag: AlignBag] = { Process.CheckForAbort[]; SELECT featureData.type FROM slopeLine => alignBag.slopeLines _ CONS[featureData, alignBag.slopeLines]; anchor => alignBag.anchor _ featureData; ENDCASE => SIGNAL Problem[msg: "Unimplemented feature type"]; }; AddAnchorTrigger: PROC [anchor: Skitter, triggerBag: TriggerBag] = { [] _ CreateAnchorTrigger[anchor, triggerBag]; }; AddAllHotSlices: PROC [scene: Scene, triggerBag: TriggerBag] = { feature: FeatureData; sliceDescGen: SliceDescriptorGenerator _ SVSelect.SelectedSlices[scene, hot]; FOR sliceD: SliceDescriptor _ SVSelect.NextSliceDescriptor[sliceDescGen], SVSelect.NextSliceDescriptor[sliceDescGen] UNTIL sliceD = NIL DO feature _ 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: SVSceneTypes.SliceDescriptorGenerator _ SVSelect.SelectedSlices[scene, normal]; FOR sliceD: SliceDescriptor _ SVSelect.NextSliceDescriptor[sliceDGen], SVSelect.NextSliceDescriptor[sliceDGen] UNTIL sliceD = NIL DO feature _ FeatureFromSlice[sliceD.slice, NIL]; AddFeature[feature, triggerBag]; ENDLOOP; }; $CaretPos => NULL; ENDCASE => ERROR Problem[msg: "unexpected type"]; }; RemoveMoving: PROC [scene: Scene, triggerBag: TriggerBag] = { DeleteTriggersFilter[triggerBag, scene, RemoveMovingSlice]; }; RemoveMovingSlice: PROC [sliceD: SliceDescriptor, scene: Scene] RETURNS [stationary: SliceDescriptor] = { selSliceD: SliceDescriptor _ SVSelect.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] _ SVAssembly.MovingParts[selSliceD.slice, selSliceD.parts]; move _ SVAssembly.UnionParts[rubber, drag]; stationary _ SVAssembly.DifferenceParts[sliceD, move]; }; BuiltInFilters: PUBLIC PROC [triggerBag: TriggerBag, filters: Filters, hideAlignments: BOOL, alignBag: AlignBag] = { anchor: Skitter; anchorFeature: FeatureData; sliceD: SliceDescriptor; pointGen: PointGenerator; pointPairGen: PointPairGenerator; index: NAT _ 0; CodeTimer.StartInt[$BuiltInFilters, $Solidviews]; IF hideAlignments THEN { FlushAlignBag[alignBag]; RETURN; }; anchorFeature _ triggerBag.anchor; IF anchorFeature # NIL AND SVCaret.Exists[(anchor _ NARROW[anchorFeature.shape, Skitter])] THEN { point: Point3d _ Matrix3d.OriginOfMatrix[SVCaret.GetPosition[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 _ SVAssembly.PointsInDescriptor[sliceD]; FOR next: SVSceneTypes.PointAndDone _ SVAssembly.NextPoint[pointGen], SVAssembly.NextPoint[pointGen] UNTIL next.done DO [] _ PointFireRule[next.point, filters, alignBag]; ENDLOOP; pointPairGen _ SVAssembly.PointPairsInDescriptor[sliceD]; index _ 0; FOR next: SVSceneTypes.PointPairAndDone _ SVAssembly.NextPointPair[pointPairGen], SVAssembly.NextPointPair[pointPairGen] UNTIL next.done DO [] _ SegmentFireRule[9999, next.lo, next.hi, filters, alignBag]; index _ index + 1; ENDLOOP; ENDLOOP; CodeTimer.StopInt[$BuiltInFilters, $Solidviews]; }; CreateAnchorTrigger: PUBLIC PROC [anchor: Skitter, triggerBag: TriggerBag] RETURNS [feature: FeatureData] = { feature _ 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 _ FeatureFromSlice[sliceD.slice, sliceD.parts]; triggerBag.slices _ AppendFeature[feature, triggerBag.slices]; } ELSE { unionD _ SVAssembly.UnionParts[oldD, sliceD]; triggerBag.slices _ DeleteSlice[oldD.slice, triggerBag.slices]; feature _ FeatureFromSlice[sliceD.slice, unionD.parts]; triggerBag.slices _ AppendFeature[feature, triggerBag.slices]; }; }; 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: Skitter => { IF SVCaret.Exists[anchor] THEN { point: Point3d _ SVCaret.GetPoint[anchor]; [] _ PointFireRule[point, filters, alignBag]; }; }; ENDCASE => ERROR; }; IncrementalFilterSlice: PUBLIC PROC [sliceD: SliceDescriptor, filters: Filters, hideAlignments: BOOL, alignBag: AlignBag] RETURNS [alignObjects: LIST OF FeatureData] = { pointGen: PointGenerator; pointPairGen: PointPairGenerator; pointGen _ SVAssembly.PointsInDescriptor[sliceD]; FOR next: SVSceneTypes.PointAndDone _ SVAssembly.NextPoint[pointGen], SVAssembly.NextPoint[pointGen] UNTIL next.done DO alignObjects _ NARROW[GList.Nconc[PointFireRule[next.point, filters, alignBag], alignObjects]]; ENDLOOP; pointPairGen _ SVAssembly.PointPairsInDescriptor[sliceD]; FOR next: SVSceneTypes.PointPairAndDone _ SVAssembly.NextPointPair[pointPairGen], SVAssembly.NextPointPair[pointPairGen] UNTIL next.done DO alignObjects _ NARROW[GList.Nconc[SegmentFireRule[9999, next.lo, next.hi, filters, alignBag], alignObjects]]; ENDLOOP; }; CreateAnchorAlignment: PUBLIC PROC [anchor: Skitter, alignBag: AlignBag] RETURNS [feature: FeatureData] = { feature _ FeatureFromAnchor[anchor]; AddAnchorObject[feature, alignBag]; }; DeleteTriggersFilter: PROC [triggerBag: TriggerBag, scene: Scene, 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 SVAssembly.EmptyParts[sliceD] THEN { newFeature _ FeatureFromSlice[newSliceD.slice, newSliceD.parts]; triggerBag.slices _ CONS[newFeature, triggerBag.slices]; }; ENDLOOP; }; AddAnchorObject: PROC [anchorFeature: FeatureData, alignBag: AlignBag] = { alignBag.anchor _ anchorFeature; }; PointFireRule: PROC [point: Point3d, filters: Filters, alignBag: AlignBag] RETURNS [alignObjects: LIST OF FeatureData] = { CreateSlopeLines: PROC [state: BOOL, name: Rope.ROPE, value: REF ANY, clientData: REF ANY] RETURNS [done: BOOL _ FALSE] = { IF state THEN { vector: Vector2d _ NARROW[value, REF Vector2d]^; feature _ JointAddSlopeLine[ azimuth: vector[1], slope: vector[2], point: point, alignBag: alignBag ]; IF feature # NIL THEN alignObjects _ CONS[feature, alignObjects]; }; }; feature: FeatureData; alignObjects _ NIL; AtomButtons.ReadSortedButtons[filters.slopeLineHandle, CreateSlopeLines]; }; SegmentFireRule: PROC [segNum: NAT, lo, hi: Point3d, filters: Filters, alignBag: AlignBag] RETURNS [alignObjects: LIST OF FeatureData] = { }; SomeSelectedIsHot: PROC [scene: Scene] RETURNS [BOOL] = { sliceDescGen: SliceDescriptorGenerator _ SVSelect.SelectedSlices[scene, normal]; FOR sliceD: SliceDescriptor _ SVSelect.NextSliceDescriptor[sliceDescGen], SVSelect.NextSliceDescriptor[sliceDescGen] UNTIL sliceD=NIL DO IF SVSelect.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]; }; 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 Problem[msg: "entity not found"]; 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 Problem[msg: "entity not found"]; }; 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]; }; DrawAlignBagRegardless: PUBLIC PROC [dc: Imager.Context, alignBag: AlignBag, svData: SVData] = { IF alignBag = NIL THEN RETURN; DrawFeatureList[dc, alignBag.slopeLines, svData]; DrawFeatureList[dc, alignBag.slopePlanes, svData]; }; DrawFeatureList: PUBLIC PROC [dc: Imager.Context, alignObjects: LIST OF FeatureData, svData: SVData] = { DrawLineAux: PROC [line: Line3d] = { -- uses FunctionCache to avoid duplication LineCompare: PROC [argument: FunctionCache.Domain] RETURNS [good: BOOL] = { thisLine: Line3d _ NARROW[argument]; RETURN[SVLines3d.EqualLine[thisLine, line]]; }; SVGraphics.DrawInfiniteLine[dc, line.base, SVVector3d.Add[line.base, line.direction], camera, scene.coordSysRoot, rect]; }; rect: Imager.Rectangle _ SVState.GetViewport[svData]; scene: Scene _ svData.scene; camera: Camera _ svData.camera; Imager.SetStrokeWidth[dc, 1.0]; Imager.SetColor[dc, alignmentColor]; FOR list: LIST OF FeatureData _ alignObjects, list.rest UNTIL list = NIL DO WITH list.first.shape SELECT FROM slopeLine: AlignmentLine => DrawLineAux[slopeLine.line]; ENDCASE => ERROR; ENDLOOP; }; InitStats: PROC [] = { boundBoxes, bags, interval: CodeTimer.Interval; boundBoxes _ CodeTimer.CreateInterval[$ComputeBoundBoxes]; bags _ CodeTimer.CreateInterval[$UpdateBagsForAction, LIST[boundBoxes]]; CodeTimer.AddInt[bags, $Solidviews]; bags _ CodeTimer.CreateInterval[$SetBagsForAction, NIL]; CodeTimer.AddInt[bags, $Solidviews]; interval _ CodeTimer.CreateInterval[$BuiltInFilters, NIL]; CodeTimer.AddInt[interval, $Solidviews]; interval _ CodeTimer.CreateInterval[$AddAllMidpoints, NIL]; CodeTimer.AddInt[interval, $Solidviews]; }; Init: PROC [] = { emptyTriggerBag _ NIL; emptyAlignBag _ CreateAlignBag[]; }; InitStats[]; Init[]; END.  SVAlignImpl.mesa Copyright c 1986 by Xerox Corporation. All rights reserved. Last edited by Bier on March 11, 1987 4:59:39 pm PST Contents: Maintains the three snap-dragging gravity bags: triggerBag, sceneBag, and alignBag for Solidviews. 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. alignBag. 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 SliceDescriptor,-- or the anchor. alignBag: several LIST OF FeatureData. The FeatureData will point to an AlignmentLine, an AlignmentCircle, an AlignmentPoint, or a Skitter (e.g. the anchor). [Artwork node; type 'ArtworkInterpress on' to command tool] Creating and filling a TriggerBag Filling the SceneBag ComputeAllBoundingBoxes[sceneBag]; -- if we wish to be like Solidviews ComputeAllBoundingBoxes[sceneBag]; -- if we wish to be like Solidviews AlignBag Use this as an (immutable) empty TriggerBag. For now, each slope line only remembers one of the joints which it passes thru. Filling all three Bags at Once Fill TriggerBag Fill Scene Bag Fill Align Bag Fill TriggerBag Fill Scene Bag Fill Align Bag No alignments. Just build sceneBag. Incrementally Update TriggerBag Incrementally Update SceneBag Remake AlignBag from Scratch AddAllMidpoints[sceneBag, midpoints, alignBag]; ComputeAllBoundingBoxes[newBag]; Use the old TriggerBag and SceneBag. ComputeAllBoundingBoxes[sceneBag]; -- not understood yet in Solidviews Remake AlignBag from Scratch AddAllMidpoints[sceneBag, midpoints, alignBag]; In support of building sceneBag. The Filter Routines shown in the figure as boxes and ovals 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. The anchor as a trigger. Triggers from slices. Incremental Addition versions of the Filter Routines shown in the figure as boxes and ovals. 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. The anchor as a trigger. Special Operations Adds the new feature to the AlignBag and returns it. In support of building triggerBag and sceneBag. 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. In support of building alignBag. Circle firstButton _ filters.radiusHeader.scalarButtons; FOR thisButton: ScalarButtonClient _ firstButton, thisButton.next UNTIL thisButton = NIL DO IF thisButton.on THEN { feature _ SVGravity.JointAddCircle[ radius: thisButton.value*filters.scaleUnit, point: point, objectBag: alignBag ]; IF feature # NIL THEN alignObjects _ CONS[feature, alignObjects]; }; ENDLOOP; firstButton: ScalarButtonClient; line1, line2: FeatureData; alignObjects _ NIL; Parallel Lines at Given Distance firstButton _ filters.distanceHeader.scalarButtons; FOR thisButton: ScalarButtonClient _ firstButton, thisButton.next UNTIL thisButton = NIL DO IF thisButton.on THEN { [line1, line2] _ SVGravity.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; AngleLines firstButton _ filters.angleHeader.scalarButtons; FOR thisButton: ScalarButtonClient _ firstButton, thisButton.next UNTIL thisButton = NIL DO IF thisButton.on THEN { [line1, line2] _ SVGravity.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; In support of building all three bags. FeatureList Utilities Drawing Draws all objects in the object bag regardless of how they have been marked. ok: BOOL; [----, ok] _ FunctionCache.Lookup[svData.refresh.lineCache, LineCompare]; IF ok THEN RETURN FunctionCache.Insert[svData.refresh.lineCache, line, NIL, 2]; IF ABS[line.theta] < 1.0 OR ABS[line.theta - 90.0] < 1.0 THEN { Imager.SetColor[dc, checkerColor]; Lines2d.DrawLine[dc, line, rect, 0.0]; -- 0 width lines go fast Imager.SetColor[dc, alignmentColor]; } ELSE Lines2d.DrawLine[dc, line, rect, 0.0]; -- 0 width lines go fast Initializing Ê/#˜J˜Icodešœ™Kšœ Ïmœ1™Ý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šœ=™=—Kšœ!™!Kšœžœ ˜#šŸœžœžœžœ˜QKš œ žœ2žœžœ žœ˜rK˜K˜—šŸœž œ˜9Kšœžœ˜Kšœžœ˜K˜K˜—šŸœžœžœžœ˜SKšœ*˜*Kšœ,˜,Kšœžœ ˜4Kšœ ˜ K˜—K˜š Ÿœžœžœ$žœžœ˜hKš œžœžœžœžœžœ˜lKšœL˜LKšœ žœ˜Kšœ˜Kšœ˜K˜K˜—šŸœžœžœžœ˜SKšœ žœ˜Kšœ 7˜NKšœ #˜;K˜K˜—šŸœžœžœ-žœ˜oKšœ%˜%Kšœ#˜#K˜K˜—šŸœžœžœ-žœ˜pKšœ%˜%Kšœ#˜#Kšœ4˜4Kšœ ˜ K˜—K™K™šŸœžœžœ)˜HKšœ˜Kšœžœ˜Kšžœžœžœžœ˜;Kšœ˜Kšœ˜KšœF™FK˜—šŸœžœžœ)˜IKšœžœ˜Kšžœžœžœžœ˜;Kšœ˜Kšœ˜KšœF™FK˜—K™K™šœžœ ˜Kšœ,™,—K˜šŸœžœžœžœ˜?šœ žœ˜Kšœ žœ˜Kšœ žœ˜Kšœž˜ K˜—K˜K˜—šŸ œžœžœ˜3Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšœ˜K˜—K˜š Ÿœžœžœžœ&žœ˜|K™OKšœ ˜ Kšœ˜KšœO˜Ošžœžœžœ˜Kšœžœ˜8Kšœžœ%˜GKšœ žœ˜K˜—šžœ˜K˜Kšœ8˜8Kšœ:˜:KšœžœPžœžœ˜vKšœ žœ˜Kšœ˜Kšœ˜Kšœ ˜ K˜—K˜K˜—šŸœžœžœ"žœžœžœžœ˜~Kšœ žœ ˜K˜Kšœ˜š žœžœžœžœžœž˜:Kšœžœ˜&Kšœ˜šžœ!žœž˜FKšœ7žœžœ ˜L—Kšžœ˜—Kšžœžœ˜ K˜K˜—K˜š ŸœžœžœRžœ žœ˜ŸKšœ>˜>K˜K˜—š ŸœžœžœRžœ žœ žœ˜®Kšœ>˜>K˜K˜—K™K™šŸ œžœžœ˜/Kšœ3˜3Kšœ-˜-Kšœ/˜/Kšœ˜Kšœ˜Kšœ žœ!˜1Kšœ"˜"Kšœžœžœ#˜=Kšœ žœ ˜/K˜šžœžœ˜Kšœ˜Kšœ˜Kšœ$˜$K˜—šžœ˜Kšœ3˜3KšÏb™Kšœ˜Kšœ<˜Kšœ3˜3Kšœ-˜-Kšœ/˜/Kšœ˜Kšœ˜Kšœ žœ!˜1Kšœ"˜"Kšœžœžœ#˜=Kšœ žœ ˜/K˜šžœžœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ%˜%K˜—šžœ˜Kšœ3˜3Kš¡™Kšœ˜Kšœ=˜=Kš¡™Kšœ˜Kšœ%˜%Kš¡™Kšœ˜Kšœ`˜`Kšœ2˜2K˜—K˜—K˜š Ÿœžœžœžœžœ˜WKšœ3˜3Kšœ/˜/Kšœ-˜-Kšœ žœ!˜1Kšœ"˜"Kšœžœžœ#˜=Kšœ žœ ˜/K˜Kšœ4žœ˜Ušžœžœ#žœ˜/Kš¡$™$Kšœžœ˜Kšœ˜Kšœ&˜&Kšœ;˜;Kšœ#˜#K˜—šžœ˜Kšœ6˜6K˜Kš¡™Kšœ*˜*Kšœ(˜(Kšœ;˜;Kšœ'˜'Kšœ'˜'Kš¡™Kšœ&˜&Kšœ;˜;Kšœ#˜#Kš¡™Kšœ&˜&Kšœ˜Kšœ>˜>Kšœ/™/Kšœ#˜#K˜Kšœ5˜5K˜—K˜K˜—šŸœžœ&žœ˜cKšœ"˜"Kšœ˜Kšœžœ˜Kšžœžœžœžœ˜7Kšœ ™ K˜K˜—š Ÿœžœžœžœžœ˜WKšœ˜Kšœ˜Kšœ-˜-Kšœ"˜"Kšœžœžœ#˜=Kšœ žœ ˜/K˜Kš¡$™$KšœF˜FKšœ@˜@KšœF™FKš¡™Kšœ˜Kšœ>˜>Kšœ/™/Kšœ#˜#Kšœžœ˜K˜K˜—š Ÿœžœžœ žœžœ˜RKšœ˜K˜K˜—K™K™ šŸ œžœ+˜=KšœC˜CKšœ˜šžœOžœ žœž˜fKšœ"˜"Kšœ ˜ Kšžœ˜—K˜K˜—šŸ œžœ7˜GK˜šžœž˜˜ Kšœžœ!˜9K˜—˜ Kšœ ˜ K˜—Kšžœžœ˜—K˜K˜—šŸ œžœžœ3˜MK˜šžœž˜Kšœ#žœ#˜JKšœ(˜(Kšžœžœ,˜=—K˜—K™K™:šŸœžœ.˜DKšœ-˜-K˜K˜—šŸœžœ+˜@Kšœ˜KšœM˜Mšžœržœ žœž˜ŠKšœ7˜7Kšœ ˜ Kšžœ˜—K˜K˜—šŸ œžœžœžœ˜\Kšœ˜Kšžœžœ žœžœ˜šžœž˜˜ KšœZ˜Zšžœlžœ žœž˜„Kšœ)žœ˜.Kšœ ˜ Kšžœ˜—K˜—Kšœ žœ˜Kšžœžœ!˜1—K˜K˜—šŸ œžœ+˜=Kšœÿ™ÿKšœ;˜;K˜K˜—šŸœžœ)žœ"˜iKšœU˜UKšœ9˜9Kš žœ žœžœžœ  ˜DKšœ_˜_Kšœ+˜+Kšœ6˜6Kšœ˜K˜—šŸœžœžœ<žœ˜tK™LK˜K˜Kšœ˜K˜K˜!Kšœžœ˜Kšœ1˜1šžœžœ˜Kšœ˜Kšžœ˜K˜—Kš¡™Kšœ"˜"š žœžœžœžœ!žœ˜aKšœF˜FKšœ)˜)Kš¡œ˜-K˜—Kš¡™š žœžœžœ)žœžœž˜GJšœ žœ˜Jšœ1˜1šžœbžœ ž˜wJš¡œ ˜2Jšžœ˜—Jšœ9˜9J˜ šžœvžœ ž˜‹Jš¡œ,˜@J˜Jšžœ˜—Jšžœ˜—Kšœ0˜0K˜K™—K™K™\šŸœžœžœ+žœ˜mKšœ$˜$Kšœ ˜ K˜K˜—šŸ œžœžœ3žœ˜mKšœ˜Kšœ5˜5šžœžœžœ˜Kšœ7˜7Kšœ>˜>K˜—šžœ˜Kšœ-˜-Kšœ?˜?Kšœ7˜7Kšœ>˜>K˜—K˜K˜—šŸœžœžœ:žœžœžœžœ˜¢K™¿Kšœžœ˜šžœžœ˜Kšœ˜Kšžœ˜K˜—šžœžœž˜šœ˜JšœQ˜QJ˜—˜Kš¡™šžœžœ˜ Kšœ*˜*Kšœ-˜-K˜—K˜—Jšžœžœ˜—K˜K˜—šŸœžœžœ=žœžœžœžœ˜©K˜K˜!J˜1šžœbžœ ž˜wJšœžœ ¡ œ0˜_Jšžœ˜—J˜9šžœvžœ ž˜‹Jšœžœ ¡œ<˜mJšžœ˜—J˜J˜—K™K™šŸœžœžœ'žœ˜kKšœ4™4Kšœ$˜$Kšœ#˜#K˜—K™K™/šŸœžœM˜gKšœ©™©Kšœ#˜#Kšœžœžœ!˜7Kšœ˜Kšœžœ˜š žœ žœžœ-žœ žœž˜[Kšœ žœ˜'Kšœ+˜+šžœžœžœ˜+Kšœ@˜@Kšœžœ ˜8K˜—Kšžœ˜—K˜K˜K™—K™ šŸœžœ5˜JKšœ ˜ K˜K˜—š Ÿ œžœ8žœžœžœ˜zšŸœžœ žœ žœ žœžœžœžœžœžœžœ˜{šžœžœ˜Kšœžœžœ ˜0šœ˜Kšœ˜Kšœ˜Kšœ ˜ Kšœ˜Kšœ˜—Kšžœ žœžœžœ˜AK˜—K˜—K˜Kšœžœ˜KšœI˜Išœ™Kšœ1™1šžœ?žœžœž™[šžœžœ™šœ#™#Kšœ+™+Kšœ ™ Kšœ™Kšœ™—Kšžœ žœžœžœ™AK™—Kšžœ™——K˜K˜—š Ÿœžœ žœ9žœžœžœ˜ŠKšœ ™ Kšœ™Kšœžœ™š¡ ™ Kšœ3™3šžœ?žœžœž™[šžœžœ™Kšœ—™—Kšžœ žœžœžœ™=Kšžœ žœžœžœ™=K™—Kšžœ™——šœ ™ Kšœ0™0šžœ?žœžœž™[šžœžœ™Kšœƒ™ƒKšžœ žœžœžœ™=Kšžœ žœžœžœ™=K™—Kšžœ™——K˜K˜—K˜K™&šŸœžœžœžœ˜9KšœP˜Pšžœržœžœž˜ˆJšžœ5žœžœžœ˜IJšžœ˜—Jšžœžœ˜K˜K˜—K™Kšœ™š Ÿ œžœžœžœžœ!˜eš žœžœžœžœžœž˜:Kš žœžœ/žœžœžœ˜\Kšžœ˜—Kšžœžœ˜ K˜K˜—šŸœžœžœžœžœžœžœ˜ˆKšœžœžœžœ˜!Kšœžœžœ˜(Kšžœ žœžœžœ"˜;šžœ žœž˜Kšžœžœ4žœ˜CKšœ7žœ˜?Kšœ˜Kšœ˜Kšžœ˜—Kšžœ"˜(Kšœ˜K˜—šŸ œžœžœžœžœžœžœ˜qKšœžœžœ ˜'Kšœ žœžœ˜Kšœ@˜@Kšžœ žœžœ˜%Kšžœ žœžœ˜(šžœ˜Kšœ˜Kšœ˜K˜—Kšœ ˜K˜K˜—šŸ œžœžœ%žœžœžœžœžœ˜K˜Kšœ žœ˜(K˜K˜—K™šŸœž œ=˜`KšœL™LKšžœ žœžœžœ˜Kšœ1˜1Kšœ2˜2K˜—K˜š Ÿœžœžœ$žœžœ!˜hšŸ œžœ *˜OšŸ œžœ"žœžœ˜KKšœžœ ˜$Kšžœ&˜,K˜—Kšœžœ™ Kšœ œD™IKšžœžœž™Kšœ5žœ™=š žœžœžœžœžœ™?Kšœ"™"Kšœ' ™?Kšœ$™$K™—Kšžœ( ™DKšœx˜xK˜—Kšœ5˜5Kšœ˜Kšœ˜K˜Kšœ$˜$š žœžœžœ'žœžœž˜Kšžœžœž˜!Kšœ8˜8Kšžœžœ˜—Kšžœ˜—K˜K˜—K™ šŸ œžœ˜Kšœ/˜/Kšœ:˜:Kšœ6žœ˜HKšœ$˜$Kšœ3žœ˜8Kšœ$˜$Kšœ5žœ˜:Kšœ(˜(Kšœ6žœ˜;Kšœ(˜(Kšœ˜K˜—šŸœžœ˜Kšœžœ˜Kšœ!˜!Kšœ˜K˜—Kšœ ˜ K˜Kšžœ˜K˜J˜—…—Utš7