DIRECTORY Basics, Feedback, GGBasicTypes, GGBoundBox, GGCoreOps, GGCoreTypes, GGHistoryTypes, GGInterfaceTypes, GGModelTypes, GGOutline, GGParent, GGScene, GGSegmentTypes, GGSelect, GGSequence, GGSlice, GGSliceOps, GGTraj, GGTrajTypes, GGUtility, Imager, List, Rope, TextNode, TiogaImager; GGOutlineImplB: CEDAR PROGRAM IMPORTS Basics, Feedback, GGBoundBox, GGCoreOps, GGOutline, GGParent, GGScene, GGSelect, GGSequence, GGSlice, GGSliceOps, GGTraj, GGUtility, List, TextNode EXPORTS GGSlice, GGOutline, GGParent = BEGIN BoundBox: TYPE = GGCoreTypes.BoundBox; Camera: TYPE = GGModelTypes.Camera; Circle: TYPE = GGBasicTypes.Circle; Color: TYPE = Imager.Color; ControlPointGenerator: TYPE = GGModelTypes.ControlPointGenerator; DefaultData: TYPE = GGModelTypes.DefaultData; EditConstraints: TYPE = GGModelTypes.EditConstraints; FeatureData: TYPE = GGInterfaceTypes.FeatureData; GGData: TYPE = GGInterfaceTypes.GGData; HistoryEvent: TYPE = GGHistoryTypes.HistoryEvent; HitType: TYPE = GGModelTypes.TrajPartType; Joint: TYPE = GGSegmentTypes.Joint; JointGenerator: TYPE = GGModelTypes.JointGenerator; Line: TYPE = GGCoreTypes.Line; Orientation: TYPE = GGModelTypes.Orientation; OutlineData: TYPE = GGOutline.OutlineData; OutlineDataObj: TYPE = GGOutline.OutlineDataObj; OutlineHitData: TYPE = GGOutline.OutlineHitData; OutlineHitDataObj: TYPE = GGOutline.OutlineHitDataObj; OutlineParts: TYPE = GGOutline.OutlineParts; OutlinePartsObj: TYPE = GGOutline.OutlinePartsObj; Point: TYPE = GGBasicTypes.Point; PointAndDone: TYPE = GGModelTypes.PointAndDone; PointGenerator: TYPE = GGModelTypes.PointGenerator; PointGeneratorObj: TYPE = GGModelTypes.PointGeneratorObj; PointPairAndDone: TYPE = GGModelTypes.PointPairAndDone; PointPairGenerator: TYPE = GGModelTypes.PointPairGenerator; PointPairGeneratorObj: TYPE = GGModelTypes.PointPairGeneratorObj; RunProc: TYPE = GGTrajTypes.RunProc; Scene: TYPE = GGModelTypes.Scene; Segment: TYPE = GGSegmentTypes.Segment; SegmentGenerator: TYPE = GGModelTypes.SegmentGenerator; SegmentGeneratorObj: TYPE = GGModelTypes.SegmentGeneratorObj; SelectionClass: TYPE = GGSegmentTypes.SelectionClass; SelectMode: TYPE = GGModelTypes.SelectMode; SequenceOfReal: TYPE = GGCoreTypes.SequenceOfReal; Slice: TYPE = GGModelTypes.Slice; SliceClass: TYPE = GGModelTypes.SliceClass; SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor; SliceDescriptorGenerator: TYPE = GGModelTypes.SliceDescriptorGenerator; SliceDescriptorGeneratorObj: TYPE = GGModelTypes.SliceDescriptorGeneratorObj; SliceDescriptorTallyProc: TYPE = GGModelTypes.SliceDescriptorTallyProc; SliceDescriptorWalkProc: TYPE = GGModelTypes.SliceDescriptorWalkProc; SliceGenerator: TYPE = GGModelTypes.SliceGenerator; SliceGeneratorObj: TYPE = GGModelTypes.SliceGeneratorObj; SliceObj: TYPE = GGModelTypes.SliceObj; SliceParts: TYPE = GGModelTypes.SliceParts; SlicePartsWalkProc: TYPE = GGModelTypes.SlicePartsWalkProc; SliceTallyProc: TYPE = GGModelTypes.SliceTallyProc; SliceWalkProc: TYPE = GGModelTypes.SliceWalkProc; StrokeEnd: TYPE = Imager.StrokeEnd; StrokeJoint: TYPE = Imager.StrokeJoint; Traj: TYPE = GGModelTypes.Traj; TrajData: TYPE = GGModelTypes.TrajData; TrajEnd: TYPE = GGModelTypes.TrajEnd; TrajParts: TYPE = GGModelTypes.TrajParts; TrajPartType: TYPE = GGModelTypes.TrajPartType; TriggerBag: TYPE = GGInterfaceTypes.TriggerBag; Vector: TYPE = GGBasicTypes.Vector; WalkLevel: TYPE = GGModelTypes.WalkLevel; Problem: PUBLIC SIGNAL [msg: Rope.ROPE] = Feedback.Problem; ClusterData: TYPE = REF ClusterDataObj; ClusterDataObj: TYPE = GGSlice.ClusterDataObj; BuildMoreOutlineSliceClass: PUBLIC PROC [class: SliceClass] = { class.closestPoint ¬ GGParent.ClosestPoint; class.closestJointToHitData ¬ GGParent.ClosestJointToHitData; class.closestPointAndTangent ¬ GGSlice.NoOpClosestPointAndTangent; class.closestSegment ¬ GGParent.ClosestSegment; class.filledPathsUnderPoint ¬ OutlineFilledPathsUnderPoint; class.lineIntersection ¬ GGParent.LineIntersection; class.circleIntersection ¬ GGParent.CircleIntersection; class.hitDataAsSimpleCurve ¬ GGParent.HitDataAsSimpleCurve; class.setDefaults ¬ GGParent.SetDefaults; class.setStrokeWidth ¬ GGParent.SetStrokeWidth; class.getStrokeWidth ¬ GGParent.GetStrokeWidth; class.setStrokeEnd ¬ GGParent.SetStrokeEnd; class.getStrokeEnd ¬ GGParent.GetStrokeEnd; class.setStrokeJoint ¬ GGParent.SetStrokeJoint; class.getStrokeJoint ¬ GGParent.GetStrokeJoint; class.setStrokeColor ¬ GGParent.SetStrokeColor; class.getStrokeColor ¬ GGParent.GetStrokeColor; class.setFillColor ¬ GGParent.SetFillColor; class.getFillColor ¬ OutlineGetFillColor; class.setArrows ¬ GGSlice.NoOpSetArrows; class.getArrows ¬ GGSlice.NoOpGetArrows; class.setDashed ¬ GGParent.SetDashed; class.getDashed ¬ GGParent.GetDashed; class.setOrientation ¬ GGParent.SetOrientation; class.getOrientation ¬ GGParent.GetOrientation; }; BuildMoreClusterSliceClass: PUBLIC PROC [class: SliceClass] = { class.closestPoint ¬ GGParent.ClosestPoint; class.closestJointToHitData ¬ GGParent.ClosestJointToHitData; class.closestPointAndTangent ¬ GGSlice.NoOpClosestPointAndTangent; class.closestSegment ¬ GGParent.ClosestSegment; class.filledPathsUnderPoint ¬ ClusterFilledPathsUnderPoint; class.lineIntersection ¬ GGParent.LineIntersection; class.circleIntersection ¬ GGParent.CircleIntersection; class.hitDataAsSimpleCurve ¬ GGParent.HitDataAsSimpleCurve; class.setDefaults ¬ GGParent.SetDefaults; class.setStrokeWidth ¬ GGParent.SetStrokeWidth; class.getStrokeWidth ¬ GGParent.GetStrokeWidth; class.setStrokeEnd ¬ GGParent.SetStrokeEnd; class.getStrokeEnd ¬ GGParent.GetStrokeEnd; class.setStrokeJoint ¬ GGParent.SetStrokeJoint; class.getStrokeJoint ¬ GGParent.GetStrokeJoint; class.setStrokeColor ¬ GGParent.SetStrokeColor; class.getStrokeColor ¬ GGParent.GetStrokeColor; class.setFillColor ¬ GGParent.SetFillColor; class.getFillColor ¬ GGParent.GetFillColor; class.setArrows ¬ GGSlice.NoOpSetArrows; class.getArrows ¬ GGSlice.NoOpGetArrows; class.setDashed ¬ GGParent.SetDashed; class.getDashed ¬ GGParent.GetDashed; class.setOrientation ¬ GGParent.SetOrientation; class.getOrientation ¬ GGParent.GetOrientation; }; ClosestPoint: PUBLIC PROC [sliceD: SliceDescriptor, testPoint: Point, tolerance: REAL] RETURNS [bestPoint: Point ¬ [0.0, 0.0], bestDist: REAL ¬ 0.0, bestNormal: Vector ¬ [0,-1], hitData: REF ANY, success: BOOL ¬ FALSE] = { IF sliceD=NIL THEN RETURN ELSE { parts: OutlineParts ¬ NARROW[sliceD.parts]; outlineHitData: OutlineHitData; thisPoint: Point; thisDist: REAL; thisNormal: Vector; thisHitData, bestHitData: REF ANY; thisSuccess: BOOL ¬ FALSE; bestChild: Slice; IF NOT GGBoundBox.PointIsInGrownBox[testPoint, GGSliceOps.GetTightBox[sliceD.slice], tolerance] THEN RETURN; bestDist ¬ GGUtility.plusInfinity; FOR list: LIST OF SliceDescriptor ¬ parts.descriptors, list.rest UNTIL list = NIL DO IF list.first=NIL THEN LOOP; [thisPoint, thisDist, thisNormal, thisHitData, thisSuccess] ¬ list.first.slice.class.closestPoint[list.first, testPoint, tolerance]; IF thisSuccess AND thisDist < bestDist THEN { bestChild ¬ list.first.slice; bestPoint ¬ thisPoint; bestDist ¬ thisDist; bestNormal ¬ thisNormal; bestHitData ¬ thisHitData; success ¬ TRUE; }; ENDLOOP; IF success THEN { hitData ¬ outlineHitData ¬ NEW[OutlineHitDataObj ¬ [ bestChild, bestHitData]]; }; }; }; ClosestJointToHitData: PUBLIC PROC [sliceD: SliceDescriptor, mapPoint, testPoint: Point, hitData: REF ANY] RETURNS [jointD: SliceDescriptor, point: Point, normal: Vector ¬ [0,-1]] = { outlineHitData: OutlineHitData ¬ NARROW[hitData]; child: Slice ¬ outlineHitData.child; childHitData: REF ANY ¬ outlineHitData.childHitData; childD: SliceDescriptor ¬ GGParent.ChildDescriptorFromDescriptor[sliceD, child]; newChildD: SliceDescriptor; [newChildD, point, normal] ¬ GGSliceOps.ClosestJointToHitData[childD, mapPoint, testPoint, childHitData]; jointD ¬ GGParent.DescriptorFromChildDescriptor[sliceD.slice, newChildD]; }; ClosestSegment: PUBLIC PROC [sliceD: SliceDescriptor, testPoint: Point, tolerance: REAL] RETURNS [bestPoint: Point ¬ [0.0, 0.0], bestDist: REAL ¬ 0.0, bestNormal: Vector ¬ [0,-1], hitData: REF ANY, success: BOOL ¬ FALSE] = { IF sliceD=NIL THEN RETURN ELSE { parts: OutlineParts ¬ NARROW[sliceD.parts]; outlineHitData: OutlineHitData; thisPoint: Point; thisDist: REAL; thisNormal: Vector; thisHitData, bestHitData: REF ANY; thisSuccess: BOOL ¬ FALSE; bestChild: Slice; IF NOT GGBoundBox.PointIsInGrownBox[testPoint, GGSliceOps.GetTightBox[sliceD.slice], tolerance] THEN RETURN; -- success=FALSE, added February 24, 1988 by Bier bestDist ¬ GGUtility.plusInfinity; FOR list: LIST OF SliceDescriptor ¬ parts.descriptors, list.rest UNTIL list = NIL DO IF list.first=NIL THEN LOOP; [thisPoint, thisDist, thisNormal, thisHitData, thisSuccess] ¬ list.first.slice.class.closestSegment[list.first, testPoint, tolerance]; IF thisSuccess AND thisDist < bestDist THEN { bestChild ¬ list.first.slice; bestPoint ¬ thisPoint; bestDist ¬ thisDist; bestNormal ¬ thisNormal; bestHitData ¬ thisHitData; success ¬ TRUE; }; ENDLOOP; IF success THEN { hitData ¬ outlineHitData ¬ NEW[OutlineHitDataObj ¬ [ bestChild, bestHitData]]; }; }; }; ClusterFilledPathsUnderPoint: PROC [slice: Slice, point: Point, tolerance: REAL] RETURNS [hitData: REF ANY ¬ NIL, moreHitDatas: LIST OF REF ANY ¬ NIL] = { outlineData: OutlineData ¬ NARROW[slice.data]; clusterData: ClusterData ¬ NARROW[outlineData.subclassData]; thisChild: Slice; thisHitData: REF ANY; childHitDatas: LIST OF REF ANY; thisSuccess: BOOL ¬ FALSE; success: BOOL ¬ FALSE; FOR childList: LIST OF Slice ¬ outlineData.children, childList.rest UNTIL childList=NIL DO thisChild ¬ childList.first; [thisHitData, childHitDatas] ¬ GGSliceOps.FilledPathsUnderPoint[thisChild, point, tolerance]; IF thisHitData # NIL THEN { FOR list: LIST OF REF ANY ¬ List.DReverse[childHitDatas], list.rest UNTIL list = NIL DO IF NOT success THEN { success ¬ TRUE; hitData ¬ NEW[OutlineHitDataObj ¬ [thisChild, list.first]]; } ELSE { moreHitDatas ¬ CONS[hitData, moreHitDatas]; hitData ¬ NEW[OutlineHitDataObj ¬ [thisChild, list.first]]; }; ENDLOOP; IF NOT success THEN { success ¬ TRUE; hitData ¬ NEW[OutlineHitDataObj ¬ [thisChild, thisHitData]]; } ELSE { moreHitDatas ¬ CONS[hitData, moreHitDatas]; hitData ¬ NEW[OutlineHitDataObj ¬ [thisChild, thisHitData]]; }; }; ENDLOOP; }; OutlineFilledPathsUnderPoint: PROC [slice: Slice, point: Point, tolerance: REAL] RETURNS [hitData: REF ANY ¬ NIL, moreHitDatas: LIST OF REF ANY ¬ NIL] = { outlineData: OutlineData ¬ NARROW[slice.data]; thisHole: Slice; thisHitData, holeHitData: REF ANY; childHitDatas: LIST OF REF ANY; thisSuccess: BOOL ¬ FALSE; success: BOOL ¬ FALSE; fence: Slice ¬ outlineData.children.first; pointInSomeHole: BOOL ¬ FALSE; IF outlineData.fillColor = NIL THEN RETURN; IF NOT GGBoundBox.PointIsInGrownBox[point, GGSliceOps.GetTightBox[slice], tolerance] THEN RETURN; FOR childList: LIST OF Slice ¬ outlineData.children.rest, childList.rest UNTIL childList=NIL DO thisHole ¬ childList.first; [holeHitData, childHitDatas] ¬ GGSliceOps.FilledPathsUnderPoint[thisHole, point, tolerance]; IF holeHitData # NIL THEN { pointInSomeHole ¬ TRUE; EXIT; }; ENDLOOP; [thisHitData, ] ¬ GGSliceOps.FilledPathsUnderPoint[fence, point, tolerance]; IF thisHitData # NIL THEN { -- is in the fence IF pointInSomeHole THEN RETURN[NIL, NIL] ELSE hitData ¬ NEW[OutlineHitDataObj ¬ [fence, thisHitData]]; } ELSE IF pointInSomeHole THEN hitData ¬ NEW[OutlineHitDataObj ¬ [thisHole, holeHitData]] ELSE RETURN[NIL, NIL]; }; LineIntersection: PUBLIC PROC [sliceD: SliceDescriptor, line: Line] RETURNS [points: LIST OF Point, pointCount: NAT ¬ 0] = { segGen: GGModelTypes.SegmentGenerator; outlineParts: OutlineParts ¬ NARROW[sliceD.parts]; thesePoints: LIST OF Point; thisCount: NAT; FOR descriptors: LIST OF SliceDescriptor ¬ outlineParts.descriptors, descriptors.rest UNTIL descriptors = NIL DO childD: SliceDescriptor ¬ descriptors.first; IF childD = NIL THEN LOOP; segGen ¬ GGSliceOps.SegmentsInDescriptor[childD]; FOR seg: Segment ¬ GGSliceOps.NextSegment[childD.slice, segGen].seg, GGSliceOps.NextSegment[childD.slice, segGen].seg UNTIL seg = NIL DO [thesePoints, thisCount] ¬ seg.class.lineIntersection[seg, line]; FOR list: LIST OF Point ¬ thesePoints, list.rest UNTIL list = NIL DO points ¬ CONS[list.first, points]; ENDLOOP; pointCount ¬ pointCount + thisCount; ENDLOOP; ENDLOOP; }; CircleIntersection: PUBLIC PROC [sliceD: SliceDescriptor, circle: Circle] RETURNS [points: LIST OF Point, pointCount: NAT ¬ 0] = { segGen: GGModelTypes.SegmentGenerator; outlineParts: OutlineParts ¬ NARROW[sliceD.parts]; thesePoints: LIST OF Point; thisCount: NAT; FOR descriptors: LIST OF SliceDescriptor ¬ outlineParts.descriptors, descriptors.rest UNTIL descriptors = NIL DO childD: SliceDescriptor ¬ descriptors.first; IF childD = NIL THEN LOOP; segGen ¬ GGSliceOps.SegmentsInDescriptor[childD]; FOR seg: Segment ¬ GGSliceOps.NextSegment[childD.slice, segGen].seg, GGSliceOps.NextSegment[childD.slice, segGen].seg UNTIL seg = NIL DO [thesePoints, thisCount] ¬ seg.class.circleIntersection[seg, circle]; FOR list: LIST OF Point ¬ thesePoints, list.rest UNTIL list = NIL DO points ¬ CONS[list.first, points]; ENDLOOP; pointCount ¬ pointCount + thisCount; ENDLOOP; ENDLOOP; }; HitDataAsSimpleCurve: PUBLIC PROC [slice: Slice, hitData: REF ANY] RETURNS [simpleCurve: REF ANY] = { outlineHitData: OutlineHitData ¬ NARROW[hitData]; simpleCurve ¬ GGSliceOps.HitDataAsSimpleCurve[outlineHitData.child, outlineHitData.childHitData]; }; PathOfHitData: PUBLIC PROC [slice: Slice, hitData: REF ANY] RETURNS [path: Slice, pathHitData: REF ANY] = { path ¬ slice; pathHitData ¬ hitData; UNTIL NOT GGParent.IsParent[path] DO outlineHitData: OutlineHitData ¬ NARROW[pathHitData]; path ¬ outlineHitData.child; pathHitData ¬ outlineHitData.childHitData; ENDLOOP; }; SetDefaults: PUBLIC PROC [slice: Slice, parts: SliceParts, defaults: DefaultData, history: HistoryEvent] = { ChildSetDefaults: PROC [child: Slice, parts: SliceParts] = { [] ¬ GGSliceOps.SetStrokeWidth[child, parts, defaults.strokeWidth, NIL]; GGSliceOps.SetStrokeEnd[child, parts, defaults.strokeEnd, NIL]; GGSliceOps.SetStrokeJoint[child, parts, defaults.strokeJoint, NIL]; GGSliceOps.SetDashed[child, parts, defaults.dashed, GGUtility.CopyPattern[defaults.pattern], defaults.offset, defaults.length, NIL]; GGSliceOps.SetStrokeColor[child, parts, defaults.strokeColor, $Set, NIL]; -- should this copy the Color?? GGSliceOps.SetFillColor[child, parts, defaults.fillColor, $Set, NIL]; -- should this copy the Color?? }; outlineParts: OutlineParts ¬ NARROW[parts]; outlineData: OutlineData ¬ NARROW[slice.data]; outlineData.fillColor ¬ defaults.fillColor; IF parts = NIL THEN FOR childList: LIST OF Slice ¬ outlineData.children, childList.rest UNTIL childList = NIL DO ChildSetDefaults[childList.first, NIL]; ENDLOOP ELSE FOR partsList: LIST OF SliceDescriptor ¬ outlineParts.descriptors, partsList.rest UNTIL partsList = NIL DO IF partsList.first#NIL THEN ChildSetDefaults[partsList.first.slice, partsList.first.parts]; ENDLOOP; }; SetStrokeWidth: PUBLIC PROC [slice: Slice, parts: SliceParts, strokeWidth: REAL, history: HistoryEvent] RETURNS [box: BoundBox] = { outlineParts: OutlineParts ¬ NARROW[parts]; outlineData: OutlineData ¬ NARROW[slice.data]; IF parts = NIL THEN FOR childList: LIST OF Slice ¬ outlineData.children, childList.rest UNTIL childList = NIL DO [] ¬ GGSliceOps.SetStrokeWidth[childList.first, NIL, strokeWidth, NIL]; ENDLOOP ELSE FOR list: LIST OF SliceDescriptor ¬ outlineParts.descriptors, list.rest UNTIL list = NIL DO IF list.first#NIL THEN { [] ¬ GGSliceOps.SetStrokeWidth[list.first.slice, list.first.parts, strokeWidth, NIL]; }; ENDLOOP; box ¬ GGBoundBox.CopyBoundBox[GGSliceOps.GetBoundBox[slice, parts]]; }; GetStrokeWidth: PUBLIC PROC [slice: Slice, parts: SliceParts] RETURNS [strokeWidth: REAL ¬ -1.0, isUnique: BOOL ¬ TRUE] = { found: BOOL ¬ FALSE; DoCheckStrokeWidth: PROC [child: Slice, childParts: SliceParts] RETURNS [done: BOOL ¬ FALSE] = { thisIsUnique: BOOL ¬ TRUE; thisWidth: REAL; [thisWidth, thisIsUnique] ¬ GGSliceOps.GetStrokeWidth[child, childParts]; IF NOT thisIsUnique THEN { strokeWidth ¬ thisWidth; RETURN[TRUE]; } ELSE { IF found THEN { IF thisWidth # strokeWidth THEN RETURN[TRUE]; } ELSE { strokeWidth ¬ thisWidth; found ¬ TRUE; }; }; }; isUnique ¬ NOT WalkIncludedParts[slice, parts, first, DoCheckStrokeWidth] AND found; }; SetStrokeEnd: PUBLIC PROC [slice: Slice, parts: SliceParts, strokeEnd: StrokeEnd, history: HistoryEvent] = { outlineParts: OutlineParts ¬ NARROW[parts]; outlineData: OutlineData ¬ NARROW[slice.data]; IF parts = NIL THEN FOR childList: LIST OF Slice ¬ outlineData.children, childList.rest UNTIL childList = NIL DO [] ¬ GGSliceOps.SetStrokeEnd[childList.first, NIL, strokeEnd, NIL]; ENDLOOP ELSE FOR list: LIST OF SliceDescriptor ¬ outlineParts.descriptors, list.rest UNTIL list = NIL DO IF list.first#NIL THEN [] ¬ GGSliceOps.SetStrokeEnd[list.first.slice, list.first.parts, strokeEnd, NIL]; ENDLOOP; }; GetStrokeEnd: PUBLIC PROC [slice: Slice, parts: SliceParts] RETURNS [strokeEnd: StrokeEnd ¬ round, isUnique: BOOL ¬ TRUE] = { found: BOOL ¬ FALSE; DoCheckStrokeEnd: PROC [child: Slice, childParts: SliceParts] RETURNS [done: BOOL ¬ FALSE] = { thisIsUnique: BOOL ¬ TRUE; thisEnd: StrokeEnd; [thisEnd, thisIsUnique] ¬ GGSliceOps.GetStrokeEnd[child, childParts]; IF NOT thisIsUnique THEN { strokeEnd ¬ thisEnd; RETURN[TRUE]; } ELSE { IF found THEN { IF thisEnd # strokeEnd THEN RETURN[TRUE]; } ELSE { strokeEnd ¬ thisEnd; found ¬ TRUE; }; }; }; isUnique ¬ NOT WalkIncludedParts[slice, parts, first, DoCheckStrokeEnd] AND found; }; SetStrokeJoint: PUBLIC PROC [slice: Slice, parts: SliceParts, strokeJoint: StrokeJoint, history: HistoryEvent] = { outlineParts: OutlineParts ¬ NARROW[parts]; outlineData: OutlineData ¬ NARROW[slice.data]; IF parts = NIL THEN FOR childList: LIST OF Slice ¬ outlineData.children, childList.rest UNTIL childList = NIL DO [] ¬ GGSliceOps.SetStrokeJoint[childList.first, NIL, strokeJoint, NIL]; ENDLOOP ELSE FOR list: LIST OF SliceDescriptor ¬ outlineParts.descriptors, list.rest UNTIL list = NIL DO IF list.first#NIL THEN [] ¬ GGSliceOps.SetStrokeJoint[list.first.slice, list.first.parts, strokeJoint, NIL]; ENDLOOP; }; GetStrokeJoint: PUBLIC PROC [slice: Slice, parts: SliceParts] RETURNS [strokeJoint: StrokeJoint ¬ round, isUnique: BOOL ¬ TRUE] = { found: BOOL ¬ FALSE; DoCheckStrokeJoint: PROC [child: Slice, childParts: SliceParts] RETURNS [done: BOOL ¬ FALSE] = { thisIsUnique: BOOL ¬ TRUE; thisJoint: StrokeJoint; [thisJoint, thisIsUnique] ¬ GGSliceOps.GetStrokeJoint[child, childParts]; IF NOT thisIsUnique THEN { strokeJoint ¬ thisJoint; RETURN[TRUE]; } ELSE { IF found THEN { IF thisJoint # strokeJoint THEN RETURN[TRUE]; } ELSE { strokeJoint ¬ thisJoint; found ¬ TRUE; }; }; }; isUnique ¬ NOT WalkIncludedParts[slice, parts, first, DoCheckStrokeJoint] AND found; }; SetStrokeColor: PUBLIC PROC [slice: Slice, parts: SliceParts, color: Color, setHow: ATOM, history: HistoryEvent] = { outlineParts: OutlineParts ¬ NARROW[parts]; outlineData: OutlineData ¬ NARROW[slice.data]; IF parts = NIL THEN FOR childList: LIST OF Slice ¬ outlineData.children, childList.rest UNTIL childList = NIL DO [] ¬ GGSliceOps.SetStrokeColor[childList.first, NIL, color, setHow, NIL]; ENDLOOP ELSE FOR list: LIST OF SliceDescriptor ¬ outlineParts.descriptors, list.rest UNTIL list = NIL DO IF list.first#NIL THEN { [] ¬ GGSliceOps.SetStrokeColor[list.first.slice, list.first.parts, color, setHow, NIL]; }; ENDLOOP; }; GetStrokeColor: PUBLIC PROC [slice: Slice, parts: SliceParts] RETURNS [color: Color, isUnique: BOOL ¬ TRUE] = { found: BOOL ¬ FALSE; DoCheckStrokeColor: PROC [child: Slice, childParts: SliceParts] RETURNS [done: BOOL ¬ FALSE] = { thisIsUnique: BOOL ¬ TRUE; thisColor: Color; [thisColor, thisIsUnique] ¬ GGSliceOps.GetStrokeColor[child, childParts]; IF NOT thisIsUnique THEN { color ¬ thisColor; RETURN[TRUE]; } ELSE { IF found THEN { IF NOT GGCoreOps.EquivalentColors[thisColor, color] THEN RETURN[TRUE]; } ELSE { color ¬ thisColor; found ¬ TRUE; }; }; }; isUnique ¬ NOT WalkIncludedParts[slice, parts, first, DoCheckStrokeColor] AND found; }; SetFillColor: PUBLIC PROC [slice: Slice, parts: SliceParts, color: Color, setHow: ATOM, history: HistoryEvent] = { outlineData: OutlineData ¬ NARROW[slice.data]; outlineParts: OutlineParts ¬ NARROW[parts]; SELECT setHow FROM $Set => outlineData.fillColor ¬ color; $ChangeHue => { newColor: Color ¬ GGUtility.ChangeHue[outlineData.fillColor, color]; outlineData.fillColor ¬ newColor; }; ENDCASE => ERROR; IF parts = NIL THEN { FOR childList: LIST OF Slice ¬ outlineData.children, childList.rest UNTIL childList = NIL DO [] ¬ GGSliceOps.SetFillColor[childList.first, NIL, color, setHow, NIL]; ENDLOOP; } ELSE { FOR list: LIST OF SliceDescriptor ¬ outlineParts.descriptors, list.rest UNTIL list = NIL DO IF list.first#NIL THEN { [] ¬ GGSliceOps.SetFillColor[list.first.slice, list.first.parts, color, setHow, NIL]; }; ENDLOOP; }; }; OutlineGetFillColor: PUBLIC PROC [slice: Slice, parts: SliceParts] RETURNS [color: Color, isUnique: BOOL ¬ TRUE] = { outlineData: OutlineData ¬ NARROW[slice.data]; color ¬ outlineData.fillColor; }; GetFillColor: PUBLIC PROC [slice: Slice, parts: SliceParts] RETURNS [color: Color, isUnique: BOOL ¬ TRUE] = { found: BOOL ¬ FALSE; DoCheckFillColor: PROC [child: Slice, childParts: SliceParts] RETURNS [done: BOOL ¬ FALSE] = { thisIsUnique: BOOL ¬ TRUE; thisColor: Color; [thisColor, thisIsUnique] ¬ GGSliceOps.GetFillColor[child, childParts]; IF NOT thisIsUnique THEN { color ¬ thisColor; RETURN[TRUE]; } ELSE { IF found THEN { IF NOT GGCoreOps.EquivalentColors[thisColor, color] THEN RETURN[TRUE]; } ELSE { color ¬ thisColor; found ¬ TRUE; }; }; }; isUnique ¬ NOT WalkIncludedParts[slice, parts, first, DoCheckFillColor] AND found; }; SetDashed: PUBLIC PROC [slice: Slice, parts: SliceParts, dashed: BOOL, pattern: SequenceOfReal ¬ NIL, offset: REAL ¬ 0.0, length: REAL ¬ -1.0, history: HistoryEvent] = { outlineParts: OutlineParts ¬ NARROW[parts]; outlineData: OutlineData ¬ NARROW[slice.data]; IF parts = NIL THEN FOR childList: LIST OF Slice ¬ outlineData.children, childList.rest UNTIL childList = NIL DO [] ¬ GGSliceOps.SetDashed[childList.first, NIL, dashed, pattern, offset, length, NIL]; ENDLOOP ELSE FOR list: LIST OF SliceDescriptor ¬ outlineParts.descriptors, list.rest UNTIL list = NIL DO IF list.first#NIL THEN { [] ¬ GGSliceOps.SetDashed[list.first.slice, list.first.parts, dashed, pattern, offset, length, NIL]; }; ENDLOOP; }; GetDashed: PUBLIC PROC [slice: Slice, parts: SliceParts] RETURNS [dashed: BOOL ¬ FALSE, pattern: SequenceOfReal, offset, length: REAL ¬ 0.0, isUnique: BOOL ¬ TRUE] = { found: BOOL ¬ FALSE; DoCheckDashes: PROC [child: Slice, childParts: SliceParts] RETURNS [done: BOOL ¬ FALSE] = { thisIsUnique: BOOL ¬ TRUE; thisDashed: BOOL ¬ FALSE; thisPattern: SequenceOfReal; thisOffset, thisLength: REAL; [thisDashed, thisPattern, thisOffset, thisLength, thisIsUnique] ¬ GGSliceOps.GetDashed[child, childParts]; IF NOT thisIsUnique THEN { dashed ¬ thisDashed; pattern ¬ thisPattern; offset ¬ thisOffset; length ¬ thisLength; RETURN[TRUE]; } ELSE { IF found THEN { IF thisDashed # dashed OR (thisDashed AND (thisOffset # offset OR thisLength # length OR NOT GGUtility.EquivalentPatterns[thisPattern, pattern])) THEN RETURN[TRUE]; } ELSE { found ¬ TRUE; dashed ¬ thisDashed; pattern ¬ thisPattern; offset ¬ thisOffset; length ¬ thisLength; }; }; }; isUnique ¬ NOT WalkIncludedParts[slice, parts, first, DoCheckDashes] AND found; }; SetOrientation: PUBLIC PROC [slice: Slice, parts: SliceParts, orientation: Orientation, history: HistoryEvent] RETURNS [success: BOOL ¬ FALSE] = { outlineData: OutlineData ¬ NARROW[slice.data]; outlineParts: OutlineParts ¬ NARROW[parts]; IF parts = NIL THEN FOR childList: LIST OF Slice ¬ outlineData.children, childList.rest UNTIL childList = NIL DO [] ¬ GGSliceOps.SetOrientation[childList.first, NIL, orientation, NIL]; ENDLOOP ELSE FOR list: LIST OF SliceDescriptor ¬ outlineParts.descriptors, list.rest UNTIL list = NIL DO IF list.first#NIL THEN { [] ¬ GGSliceOps.SetOrientation[list.first.slice, list.first.parts, orientation, NIL]; }; ENDLOOP; }; GetOrientation: PUBLIC PROC [slice: Slice, parts: SliceParts] RETURNS [orientation: Orientation ¬ ccw, isUnique: BOOL ¬ TRUE] = { outlineData: OutlineData ¬ NARROW[slice.data]; found: BOOL ¬ FALSE; DoCheckOrientation: PROC [child: Slice, childParts: SliceParts] RETURNS [done: BOOL ¬ FALSE] = { thisIsUnique: BOOL ¬ TRUE; thisOrient: Orientation; [thisOrient, thisIsUnique] ¬ GGSliceOps.GetOrientation[child, childParts]; IF NOT thisIsUnique THEN { orientation ¬ thisOrient; RETURN[TRUE]; } ELSE { IF found THEN { IF thisOrient # orientation THEN RETURN[TRUE]; } ELSE { orientation ¬ thisOrient; found ¬ TRUE; }; }; }; isUnique ¬ NOT WalkIncludedParts[slice, parts, first, DoCheckOrientation] AND found; }; CreateOutline: PUBLIC PROC [child: Slice, fillColor: Imager.Color] RETURNS [parent: Slice] = { data: OutlineData ¬ NEW[OutlineDataObj ¬ [oddWrap: TRUE, fillColor: fillColor, children: LIST[child]] ]; parent ¬ NEW[SliceObj ¬ [ class: GGSlice.FetchSliceClass[$Outline], data: data, selectedInFull: [FALSE, FALSE, FALSE], tightBox: GGBoundBox.NullBoundBox[], boundBox: GGBoundBox.NullBoundBox[], boxValid: FALSE]]; parent.nullDescriptor ¬ GGSlice.DescriptorFromParts[parent, NIL]; child.parent ¬ parent; IF GGSliceOps.GetType[child]=$Traj THEN { trajData: TrajData ¬ NARROW[child.data]; IF trajData.role=hole THEN trajData.role ¬ fence; }; }; AddChild: PUBLIC PROC [outline: Slice, child: Slice] RETURNS [holier: Slice] = { holyData: OutlineData; IF GGSliceOps.GetType[outline]#$Outline THEN ERROR; holier ¬ GGSliceOps.Copy[outline].first; -- copy the outline holyData ¬ NARROW[holier.data]; -- get the copy's children list holyData.children ¬ GGUtility.AppendSliceList[holyData.children, LIST[child]]; -- and append the new hole child.parent ¬ holier; -- make the new hole's parent the new copy IF GGSliceOps.GetType[child]=$Traj THEN GGTraj.SetTrajRole[child, hole]; GGSlice.KillBoundBox[holier]; }; ReplaceFirstChild: PUBLIC PROC [outline: Slice, newChild: Slice] RETURNS [newOutline: Slice] = { isClosed: BOOL ¬ FALSE; holeList: LIST OF Slice ¬ GGOutline.ListHoles[outline]; -- all but the first child newData: OutlineData; newFirstChild: Slice; IF GGSliceOps.GetType[outline]#$Outline THEN ERROR; IF newChild#NIL THEN newFirstChild ¬ newChild ELSE { IF holeList = NIL THEN newFirstChild ¬ NIL ELSE { newFirstChild ¬ holeList.first; holeList ¬ holeList.rest; }; }; IF newFirstChild=NIL THEN RETURN[NIL]; isClosed ¬ GGSliceOps.GetType[newFirstChild]#$Traj OR NARROW[newFirstChild.data, TrajData].role#open; IF NOT isClosed THEN ERROR; newOutline ¬ GGOutline.CreateOutline[newFirstChild, GGSliceOps.GetFillColor[outline, NIL].color]; newData ¬ NARROW[newOutline.data]; FOR list: LIST OF Slice ¬ holeList, list.rest UNTIL list = NIL DO hole: Slice ¬ list.first; newSlice: Slice ¬ GGSliceOps.Copy[hole].first; -- Copy should keep role=hole if Traj newData.children ¬ GGUtility.AppendSliceList[newData.children, LIST[newSlice]]; newSlice.parent ¬ newOutline; ENDLOOP; GGSlice.KillBoundBox[newOutline]; }; ReplaceChild: PUBLIC PROC [outline: Slice, oldChild, newChild: Slice] RETURNS [newOutline: Slice] = { newSlice: Slice; newData: OutlineData; holeList: LIST OF Slice; oldData: OutlineData ¬ NARROW[outline.data]; IF GGSliceOps.GetType[outline]#$Outline THEN ERROR; IF oldChild=NIL THEN RETURN [outline]; IF oldChild=oldData.children.first THEN RETURN [ReplaceFirstChild[outline, newChild]]; newSlice ¬ GGSliceOps.Copy[oldData.children.first].first; newOutline ¬ GGOutline.CreateOutline[newSlice, oldData.fillColor]; newData ¬ NARROW[newOutline.data]; holeList ¬ GGOutline.ListHoles[outline]; -- all but the first child FOR list: LIST OF Slice ¬ holeList, list.rest UNTIL list = NIL DO hole: Slice ¬ list.first; IF hole=oldChild AND newChild#NIL THEN { -- replace a matching child unless newChild=NIL newSlice ¬ GGSliceOps.Copy[newChild].first; newData.children ¬ GGUtility.AppendSliceList[newData.children, LIST[newSlice]]; newSlice.parent ¬ newOutline; } ELSE IF hole#oldChild THEN { -- copy any hole that does not match newSlice ¬ GGSliceOps.Copy[hole].first; newData.children ¬ GGUtility.AppendSliceList[newData.children, LIST[newSlice]]; newSlice.parent ¬ newOutline; }; ENDLOOP; GGSlice.KillBoundBox[newOutline]; }; ReplaceRunsInOutline: PUBLIC PROC [outlineD: SliceDescriptor, runProc: RunProc, segmentsOnly: BOOL ¬ TRUE, selectNewRuns: BOOL ¬ FALSE] RETURNS [newOutline: Slice ¬ NIL] = { WHILE outlineD # NIL AND NOT GGSliceOps.IsEmptyParts[outlineD] DO thisChildD: SliceDescriptor ¬ GGParent.FirstIncludedChild[outlineD.slice, outlineD.parts, first]; SELECT GGSliceOps.GetType[thisChildD.slice] FROM $Traj => { newOutline ¬ GGTraj.ReplaceFirstRun[thisChildD, runProc, segmentsOnly, selectNewRuns]; IF newOutline = NIL THEN outlineD ¬ NIL ELSE { parts: SliceParts ¬ GGSliceOps.RemakeSelections[newOutline, active]; -- yuk outlineD ¬ IF parts = NIL THEN NIL ELSE GGSlice.DescriptorFromParts[newOutline, parts]; }; }; ENDCASE => { -- $Box or $Circle. Clear its active bits and ignore it. parts: SliceParts; -- yuk GGSliceOps.SaveSelections[thisChildD.slice, NIL, active]; parts ¬ GGSliceOps.RemakeSelections[thisChildD.slice, active]; outlineD ¬ IF parts = NIL THEN NIL ELSE GGSlice.DescriptorFromParts[thisChildD.slice, parts]; }; ENDLOOP; }; SetWrapRule: PUBLIC PROC [outline: Slice, oddWrap: BOOL] = { NARROW[outline.data, OutlineData].oddWrap ¬ oddWrap; }; GetWrapRule: PUBLIC PROC [outline: Slice] RETURNS [oddWrap: BOOL] = { RETURN[NARROW[outline.data, OutlineData].oddWrap]; }; SetFillText: PUBLIC PROC [slice: Slice, node: TextNode.Ref, screenStyle: BOOL ¬ FALSE, history: HistoryEvent] = { IF GGSliceOps.GetType[slice]#$Outline THEN ERROR -- debug check ELSE { PutTextInBox: PROC [thisChild: Slice] RETURNS [done: BOOL ¬ FALSE] = { IF previousChild#NIL THEN { siblingNodes ¬ GGSlice.GetBoxNodes[previousChild]; resumeLoc ¬ siblingNodes.resume; }; GGSlice.SetBoxText[thisChild, resumeLoc, screenStyle, history]; previousChild ¬ thisChild; }; siblingNodes: TiogaImager.FormattedNodes; resumeLoc: TextNode.Location ¬ [NIL, 0]; previousChild: Slice ¬ NIL; IF GGParent.FirstChild[slice, first, $Box] # NIL THEN { outlineData: OutlineData ¬ NARROW[slice.data]; outlineData.fillText ¬ node; -- complete document to outline (parent) outlineData.screenStyle ¬ screenStyle; resumeLoc.node ¬ TextNode.FirstChild[node]; -- Remove root node for first child [] ¬ GGParent.WalkChildren[slice, first, PutTextInBox, $Box]; }; }; }; DescriptorFromChildDescriptor: PUBLIC PROC [slice: Slice, childD: SliceDescriptor] RETURNS [sliceD: SliceDescriptor] = { outlineData: OutlineData ¬ NARROW[slice.data]; outlineChildren: LIST OF Slice ¬ outlineData.children; ptr: LIST OF SliceDescriptor; newParts: OutlineParts ¬ NEW[OutlinePartsObj]; [newParts.descriptors, ptr] ¬ GGUtility.StartSliceDescriptorList[]; FOR children: LIST OF Slice ¬ outlineChildren, children.rest UNTIL children=NIL DO [newParts.descriptors, ptr] ¬ GGUtility.AddSliceDescriptor[IF children.first=childD.slice THEN childD ELSE NIL, newParts.descriptors, ptr]; ENDLOOP; sliceD ¬ GGSlice.DescriptorFromParts[slice, newParts]; }; DescriptorFromChild: PUBLIC PROC [slice: Slice, child: Slice] RETURNS [sliceD: SliceDescriptor] = { childD: SliceDescriptor ¬ GGSliceOps.NewParts[child, NIL, slice]; sliceD ¬ DescriptorFromChildDescriptor[slice, childD]; }; TopLevelDescriptorFromChildDescriptor: PUBLIC PROC [childD: SliceDescriptor] RETURNS [ancestorD: SliceDescriptor] = { IF GGScene.IsTopLevel[childD.slice] THEN RETURN[childD] ELSE RETURN [TopLevelDescriptorFromChildDescriptor[GGParent.DescriptorFromChildDescriptor[GGParent.GetParent[childD.slice], childD]]]; }; ChildDescriptorFromDescriptor: PUBLIC PROC [sliceD: SliceDescriptor, child: Slice] RETURNS [childD: SliceDescriptor] = { outlineParts: OutlineParts ¬ NARROW[sliceD.parts]; IF child=NIL THEN RETURN[NIL]; IF outlineParts=NIL THEN RETURN[GGSlice.DescriptorFromParts[child, NIL]]; FOR list: LIST OF SliceDescriptor ¬ outlineParts.descriptors, list.rest UNTIL list=NIL DO IF list.first#NIL AND list.first.slice=child THEN RETURN[list.first]; ENDLOOP; RETURN[NIL]; }; RemoveTraj: PUBLIC PROC [sliceD: SliceDescriptor, traj: Slice] RETURNS [newD: SliceDescriptor] = { SELECT GGSliceOps.GetType[sliceD.slice] FROM $Cluster => { outlineParts: OutlineParts ¬ NARROW[sliceD.parts]; newChildD: SliceDescriptor; newParts: OutlineParts; newChildDList, ptr: LIST OF SliceDescriptor; [newChildDList, ptr] ¬ GGUtility.StartSliceDescriptorList[]; FOR list: LIST OF SliceDescriptor ¬ outlineParts.descriptors, list.rest UNTIL list=NIL DO newChildD ¬ IF list.first = NIL THEN NIL ELSE RemoveTraj[list.first, traj]; [newChildDList, ptr] ¬ GGUtility.AddSliceDescriptor[newChildD, newChildDList, ptr]; ENDLOOP; newParts ¬ NEW[OutlinePartsObj ¬ [newChildDList]]; newD ¬ GGSlice.DescriptorFromParts[sliceD.slice, newParts]; }; $Outline => { newD ¬ OutlineRemoveTraj[sliceD, traj]; }; ENDCASE => { newD ¬ sliceD; }; }; OutlineRemoveTraj: PROC [outlineD: SliceDescriptor, traj: Traj] RETURNS [newD: SliceDescriptor] = { outlineParts: OutlineParts ¬ NARROW[outlineD.parts]; seq: SliceDescriptor; FOR list: LIST OF SliceDescriptor ¬ outlineParts.descriptors, list.rest UNTIL list = NIL DO IF list.first#NIL AND list.first.slice = traj THEN { seq ¬ list.first; GOTO Done; }; REPEAT Done => { newSeqs: LIST OF SliceDescriptor ¬ GGUtility.SequenceSubst[new: NIL, old: seq, expr: outlineParts.descriptors]; newParts: SliceParts ¬ NEW[OutlinePartsObj ¬ [newSeqs]]; newD ¬ GGSlice.DescriptorFromParts[outlineD.slice, newParts]; }; FINISHED => newD ¬ outlineD; ENDLOOP; }; DeleteControlPoints: PUBLIC PROC [outlineD: SliceDescriptor, scene: Scene] RETURNS [bBox: BoundBox] = { DoDelete: PROC [nextPartsD: SliceDescriptor] RETURNS [done: BOOL ¬ FALSE] = { GGBoundBox.EnlargeByBox[bBox, GGTraj.DeleteControlPoints[nextPartsD, scene]]; }; bBox ¬ GGBoundBox.NullBoundBox[]; -- start with empty refresh box, and increase size [] ¬ GGParent.WalkIncludedChildren[outlineD.slice, outlineD.parts, leaf, DoDelete, $Traj]; }; SaveSelectionsInOutlineAllClasses: PUBLIC PROC [outline: Slice] = { SaveSelectionsAux: PROC [selectClass: SelectionClass] = { outlineD: SliceDescriptor ¬ GGSelect.FindSelectedSlice[outline, selectClass]; IF outlineD#NIL THEN { DoSaveSelections: PROC [nextPart: SliceDescriptor] RETURNS [done: BOOL ¬ FALSE] = { GGSliceOps.SaveSelections[nextPart.slice, nextPart.parts, selectClass]; }; [] ¬ GGParent.WalkIncludedChildren[outlineD.slice, outlineD.parts, first, DoSaveSelections]; }; }; outlineData: OutlineData ¬ NARROW[outline.data]; children: LIST OF Slice ¬ outlineData.children; FOR nextChildren: LIST OF Slice ¬ children, nextChildren.rest UNTIL nextChildren=NIL DO nextChild: Slice ¬ nextChildren.first; -- clear every selection bit in every child GGSliceOps.SaveSelections[nextChild, NIL, normal]; -- clear GGSliceOps.SaveSelections[nextChild, NIL, hot]; -- clear GGSliceOps.SaveSelections[nextChild, NIL, active]; -- clear GGSliceOps.SaveSelections[nextChild, NIL, match]; -- clear ENDLOOP; SaveSelectionsAux[normal]; SaveSelectionsAux[hot]; SaveSelectionsAux[active]; SaveSelectionsAux[match]; }; UnpackHitData: PUBLIC PROC [hitData: REF ANY] RETURNS [child: Slice, hitType: TrajPartType ¬ none, segNum, cpNum, jointNum: INT ¬ -999, hitPoint: Point] = { outlineHitData: OutlineHitData ¬ NARROW[hitData]; child ¬ outlineHitData.child; IF GGSliceOps.GetType[child]=$Traj THEN [hitType, segNum, cpNum, jointNum, hitPoint] ¬ GGTraj.UnpackHitData[outlineHitData.childHitData]; }; UnpackOneSegmentDescriptor: PUBLIC PROC [outlineD: SliceDescriptor] RETURNS [childDescriptor: SliceDescriptor, segNum: NAT ¬ 999] = { outlineParts: OutlineParts; IF outlineD=NIL OR GGSliceOps.GetType[outlineD.slice]#$Outline THEN RETURN[NIL, 999]; outlineParts ¬ NARROW[outlineD.parts]; FOR descriptors: LIST OF SliceDescriptor ¬ outlineParts.descriptors, descriptors.rest UNTIL descriptors = NIL DO IF descriptors.first#NIL THEN { childDescriptor ¬ descriptors.first; IF GGSliceOps.GetType[childDescriptor.slice]=$Traj THEN segNum ¬ GGSequence.UnpackOneSegmentSequence[NARROW[childDescriptor.parts]]; RETURN; }; ENDLOOP; }; UnpackSimpleDescriptor: PUBLIC PROC [outlineD: SliceDescriptor] RETURNS [success: BOOL ¬ FALSE, hitType: TrajPartType ¬ none, childDescriptor: SliceDescriptor ¬ NIL, joint: Joint ¬ NIL, jointNum: NAT ¬ 999, cp: Point ¬ [0,0], cpNum: NAT ¬ 999, seg: Segment ¬ NIL, segNum: NAT ¬ 999] = { outlineParts: OutlineParts; IF outlineD=NIL OR GGSliceOps.GetType[outlineD.slice]#$Outline THEN RETURN; outlineParts ¬ NARROW[outlineD.parts]; FOR descriptors: LIST OF SliceDescriptor ¬ outlineParts.descriptors, descriptors.rest UNTIL descriptors = NIL DO IF descriptors.first#NIL THEN { success ¬ TRUE; -- questionable if child is not a Traj childDescriptor ¬ descriptors.first; IF GGSliceOps.GetType[childDescriptor.slice]=$Traj THEN [success, hitType, ----, joint, jointNum, cp, cpNum, seg, segNum] ¬ GGSequence.UnpackSimpleSequence[childDescriptor.slice, childDescriptor.parts]; RETURN; }; ENDLOOP; }; FindTrajShapeInOutline: PUBLIC PROC [traj: Slice, outline: Slice] RETURNS [oldTrajs: LIST OF Slice] = { outlineData: OutlineData ¬ NARROW[outline.data]; tail: LIST OF Slice; MatchTraj: PROC [oldTraj: Slice] RETURNS [done: BOOL ¬ FALSE] = { IF GGTraj.MatchShape[traj, oldTraj] THEN [oldTrajs, tail] ¬ GGUtility.AddSlice[oldTraj, oldTrajs, tail]; }; [] ¬ GGParent.WalkChildren[outline, first, MatchTraj, $Traj]; }; TrajsInOutline: PUBLIC PROC [outline: Slice] RETURNS [trajGen: SliceGenerator] = { outlineData: OutlineData ¬ NARROW[outline.data]; partsList: LIST OF Slice ¬ TrajectoriesOfOutline[outline]; trajGen ¬ NEW[SliceGeneratorObj ¬ [partsList]]; }; TrajectoriesOfOutline: PUBLIC PROC [outline: Slice] RETURNS [trajs: LIST OF Slice ¬ NIL] = { outlineData: OutlineData ¬ NARROW[outline.data]; ptr: LIST OF Slice; IF NOT GGSliceOps.GetType[outline]=$Outline THEN RETURN; [trajs, ptr] ¬ GGUtility.StartSliceList[]; FOR list: LIST OF Slice ¬ outlineData.children, list.rest UNTIL list = NIL DO IF GGSliceOps.GetType[list.first]=$Traj THEN [trajs, ptr] ¬ GGUtility.AddSlice[list.first, trajs, ptr]; ENDLOOP; }; ListHoles: PUBLIC PROC [outline: Slice] RETURNS [holesList: LIST OF Slice] = { data: OutlineData ¬ NARROW[outline.data]; IF GGSliceOps.GetType[outline]=$Outline THEN holesList ¬ data.children.rest; }; HasHoles: PUBLIC PROC [outline: Slice] RETURNS [BOOL ¬ FALSE] = { data: OutlineData ¬ NARROW[outline.data]; RETURN[GGSliceOps.GetType[outline]=$Outline AND data.children.rest#NIL]; }; Parts: PUBLIC PROC [outlineD: SliceDescriptor] RETURNS [partsGen: SliceDescriptorGenerator] = { partsGen ¬ Parts2[outlineD.slice, outlineD.parts]; }; PartsList: PUBLIC PROC [sliceD: SliceDescriptor] RETURNS [partsList: LIST OF SliceDescriptor] = { outlineParts: OutlineParts ¬ NARROW[sliceD.parts]; ptr: LIST OF SliceDescriptor; [partsList, ptr] ¬ GGUtility.StartSliceDescriptorList[]; FOR list: LIST OF SliceDescriptor ¬ outlineParts.descriptors, list.rest UNTIL list = NIL DO IF list.first # NIL THEN [partsList, ptr] ¬ GGUtility.AddSliceDescriptor[list.first, partsList, ptr]; ENDLOOP; }; Parts2: PUBLIC PROC [outline: Slice, parts: SliceParts] RETURNS [partsGen: SliceDescriptorGenerator] = { partsGen ¬ NEW[SliceDescriptorGeneratorObj ¬ [] ]; IF parts=NIL THEN RETURN -- empty parts ELSE { outlineParts: OutlineParts ¬ NARROW[parts]; partsList, ptr: LIST OF SliceDescriptor; [partsList, ptr] ¬ GGUtility.StartSliceDescriptorList[]; FOR list: LIST OF SliceDescriptor ¬ outlineParts.descriptors, list.rest UNTIL list = NIL DO IF list.first # NIL THEN [partsList, ptr] ¬ GGUtility.AddSliceDescriptor[list.first, partsList, ptr]; ENDLOOP; partsGen.list ¬ partsList; }; }; Children: PUBLIC PROC [outline: Slice] RETURNS [children: SliceGenerator] = { data: OutlineData ¬ NARROW[outline.data]; children ¬ NEW[SliceGeneratorObj ¬ [ list: data.children ]]; }; ChildList: PUBLIC PROC [outline: Slice] RETURNS [childList: LIST OF Slice] = { data: OutlineData ¬ NARROW[outline.data]; childList ¬ data.children; }; WalkChildren: PUBLIC PROC [parent: Slice, level: WalkLevel, walkProc: SliceWalkProc ¬ NIL, classType: ATOM ¬ NIL] RETURNS [aborted: BOOL ¬ FALSE] = { IF GGParent.IsParent[parent] THEN { outlineData: OutlineData ¬ NARROW[parent.data]; FOR slices: LIST OF Slice ¬ outlineData.children, slices.rest UNTIL slices = NIL DO thisType: ATOM ¬ GGSliceOps.GetType[slices.first]; IF (level # leaf OR GGParent.IsLeafOfClass[slices.first, classType]) AND (classType = NIL OR thisType = classType) THEN { aborted ¬ walkProc[slices.first]; IF aborted THEN RETURN; }; IF (level = all OR level = leaf OR (level = highest AND thisType # classType)) AND GGParent.IsParent[slices.first] THEN { aborted ¬ GGParent.WalkChildren[slices.first, level, walkProc, classType]; IF aborted THEN RETURN; }; ENDLOOP; }; }; WalkIncludedChildren: PUBLIC PROC [parent: Slice, parts: SliceParts, level: WalkLevel, walkProc: SliceDescriptorWalkProc, classType: ATOM ¬ NIL] RETURNS [aborted: BOOL ¬ FALSE] = { type: ATOM ¬ GGSliceOps.GetType[parent]; IF GGParent.IsParentType[type] THEN { outlineParts: OutlineParts ¬ NARROW[parts]; IF outlineParts = NIL THEN RETURN; FOR childDList: LIST OF SliceDescriptor ¬ outlineParts.descriptors, childDList.rest UNTIL childDList = NIL DO thisChildD: SliceDescriptor ¬ childDList.first; IF thisChildD # NIL AND NOT GGSliceOps.IsEmptyParts[thisChildD] THEN { thisType: ATOM ¬ GGSliceOps.GetType[thisChildD.slice]; IF (level # leaf OR GGParent.IsLeafOfClass[thisChildD.slice, classType]) AND (classType = NIL OR GGSliceOps.GetType[thisChildD.slice] = classType) THEN { aborted ¬ walkProc[thisChildD]; IF aborted THEN RETURN; }; IF (level = all OR level = leaf OR (level = highest AND thisType # classType)) AND GGParent.IsParent[thisChildD.slice] THEN { aborted ¬ WalkIncludedChildren[thisChildD.slice, thisChildD.parts, level, walkProc, classType]; IF aborted THEN RETURN; }; }; ENDLOOP; }; }; WalkIncludedParts: PROC [parent: Slice, parts: SliceParts, level: WalkLevel, walkProc: SlicePartsWalkProc, classType: ATOM ¬ NIL] RETURNS [aborted: BOOL ¬ FALSE] = { type: ATOM ¬ GGSliceOps.GetType[parent]; IF GGParent.IsParentType[type] THEN { IF parts = NIL THEN { outlineData: OutlineData ¬ NARROW[parent.data]; FOR childList: LIST OF Slice ¬ outlineData.children, childList.rest UNTIL childList = NIL DO thisChild: Slice ¬ childList.first; thisType: ATOM ¬ GGSliceOps.GetType[thisChild]; IF (level # leaf OR GGParent.IsLeafOfClass[thisChild, classType]) AND (classType = NIL OR GGSliceOps.GetType[thisChild] = classType) THEN { aborted ¬ walkProc[thisChild, NIL]; IF aborted THEN RETURN; }; IF (level = all OR level = leaf OR (level = highest AND thisType # classType)) AND GGParent.IsParent[thisChild] THEN { aborted ¬ WalkIncludedParts[thisChild, NIL, level, walkProc, classType]; IF aborted THEN RETURN; }; ENDLOOP; } ELSE { outlineParts: OutlineParts ¬ NARROW[parts]; IF outlineParts = NIL THEN RETURN; FOR childDList: LIST OF SliceDescriptor ¬ outlineParts.descriptors, childDList.rest UNTIL childDList = NIL DO thisChildD: SliceDescriptor ¬ childDList.first; IF thisChildD # NIL AND NOT GGSliceOps.IsEmptyParts[thisChildD] THEN { thisType: ATOM ¬ GGSliceOps.GetType[thisChildD.slice]; IF (level # leaf OR GGParent.IsLeafOfClass[thisChildD.slice, classType]) AND (classType = NIL OR GGSliceOps.GetType[thisChildD.slice] = classType) THEN { aborted ¬ walkProc[thisChildD.slice, thisChildD.parts]; IF aborted THEN RETURN; }; IF (level = all OR level = leaf OR (level = highest AND thisType # classType)) AND GGParent.IsParent[thisChildD.slice] THEN { aborted ¬ WalkIncludedParts[thisChildD.slice, thisChildD.parts, level, walkProc, classType]; IF aborted THEN RETURN; }; }; ENDLOOP; }; }; }; ListChildren: PUBLIC PROC [parent: Slice, level: WalkLevel, classType: ATOM ¬ NIL] RETURNS [sliceList: LIST OF Slice] = { ptr: LIST OF Slice; DoAppendSlice: PROC [slice: Slice] RETURNS [done: BOOL ¬ FALSE] = { [sliceList, ptr] ¬ GGUtility.AddSlice[slice, sliceList, ptr]; }; [sliceList, ptr] ¬ GGUtility.StartSliceList[]; [] ¬ WalkChildren[parent, level, DoAppendSlice, classType]; }; ListIncludedChildren: PUBLIC PROC [parent: Slice, parts: SliceParts, level: WalkLevel, classType: ATOM ¬ NIL] RETURNS [selectedList: LIST OF SliceDescriptor] = { ptr: LIST OF SliceDescriptor; DoAppendSlice: PROC [childD: SliceDescriptor] RETURNS [done: BOOL ¬ FALSE] = { [selectedList, ptr] ¬ GGUtility.AddSliceDescriptor[childD, selectedList, ptr]; }; [selectedList, ptr] ¬ GGUtility.StartSliceDescriptorList[]; [] ¬ WalkIncludedChildren[parent, parts, level, DoAppendSlice, classType]; }; FirstChild: PUBLIC PROC [parent: Slice, level: WalkLevel, classType: ATOM ¬ NIL] RETURNS [firstChild: Slice] = { StopAtFirstSlice: PROC [slice: Slice] RETURNS [done: BOOL ¬ FALSE] = { firstChild ¬ slice; RETURN[TRUE]; }; [] ¬ WalkChildren[parent, level, StopAtFirstSlice, classType]; }; FirstIncludedChild: PUBLIC PROC [parent: Slice, parts: SliceParts, level: WalkLevel, classType: ATOM ¬ NIL] RETURNS [childD: SliceDescriptor] = { StopAtFirstSlice: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL ¬ FALSE] = { childD ¬ sliceD; RETURN[TRUE]; }; [] ¬ WalkIncludedChildren[parent, parts, level, StopAtFirstSlice, classType]; }; TallyChildren: PUBLIC PROC [parent: Slice, tallyProc: SliceTallyProc] RETURNS [tallyD: SliceDescriptor, aborted: BOOL ¬ FALSE] = { visitChildren, keep, done, keepFound: BOOL ¬ FALSE; [visitChildren, keep, done] ¬ tallyProc[parent]; IF GGParent.IsParent[parent] AND visitChildren THEN { outlineData: OutlineData ¬ NARROW[parent.data]; ptr: LIST OF SliceDescriptor; newParts: OutlineParts ¬ NEW[OutlinePartsObj]; [newParts.descriptors, ptr] ¬ GGUtility.StartSliceDescriptorList[]; FOR list: LIST OF Slice ¬ outlineData.children, list.rest UNTIL list = NIL DO thisD: SliceDescriptor; IF NOT aborted THEN { [thisD, aborted] ¬ TallyChildren[list.first, tallyProc]; IF thisD # NIL THEN keepFound ¬ TRUE; } ELSE thisD ¬ NIL; [newParts.descriptors, ptr] ¬ GGUtility.AddSliceDescriptor[thisD, newParts.descriptors, ptr]; ENDLOOP; IF keepFound THEN tallyD ¬ GGSlice.DescriptorFromParts[parent, newParts] ELSE tallyD ¬ NIL; } ELSE { IF keep THEN tallyD ¬ GGSliceOps.NewParts[parent, NIL, slice] ELSE tallyD ¬ NIL; aborted ¬ done; }; }; TallyIncludedChildren: PUBLIC PROC [parent: Slice, parts: SliceParts, tallyProc: SliceDescriptorTallyProc] RETURNS [tallyD: SliceDescriptor, aborted: BOOL ¬ FALSE] = { visitChildren, done, keepFound: BOOL ¬ FALSE; keepD: SliceDescriptor; parentD: SliceDescriptor ¬ GGSlice.DescriptorFromParts[parent, parts]; [visitChildren, keepD, done] ¬ tallyProc[parentD]; IF GGParent.IsParent[parent] AND visitChildren THEN { outlineParts: OutlineParts ¬ NARROW[parts]; ptr: LIST OF SliceDescriptor; newParts: OutlineParts ¬ NEW[OutlinePartsObj]; [newParts.descriptors, ptr] ¬ GGUtility.StartSliceDescriptorList[]; FOR list: LIST OF SliceDescriptor ¬ outlineParts.descriptors, list.rest UNTIL list = NIL DO thisD: SliceDescriptor; IF NOT aborted THEN { IF list.first # NIL THEN { [thisD, aborted] ¬ TallyIncludedChildren[list.first.slice, list.first.parts, tallyProc]; IF thisD # NIL THEN keepFound ¬ TRUE; }; } ELSE thisD ¬ NIL; [newParts.descriptors, ptr] ¬ GGUtility.AddSliceDescriptor[thisD, newParts.descriptors, ptr]; ENDLOOP; IF keepFound THEN tallyD ¬ GGSlice.DescriptorFromParts[parent, newParts] ELSE tallyD ¬ NIL; } ELSE { tallyD ¬ keepD; aborted ¬ done; }; }; IsParent: PUBLIC PROC [slice: Slice] RETURNS [BOOL] = { RETURN[slice.class.type = $Outline OR slice.class.type = $Cluster]; }; IsParentType: PUBLIC PROC [classType: ATOM] RETURNS [BOOL] = { RETURN[classType = $Outline OR classType = $Cluster]; }; GetParent: PUBLIC PROC [child: Slice] RETURNS [parent: Slice] = { RETURN[child.parent]; }; GetTopLevelAncestor: PUBLIC PROC [slice: Slice] RETURNS [ancestor: Slice] = { ancestor ¬ slice; UNTIL GGScene.IsTopLevel[ancestor] DO ancestor ¬ GGParent.GetParent[ancestor]; ENDLOOP; }; IsLeafOfClass: PUBLIC PROC [slice: Slice, classType: ATOM ¬ NIL] RETURNS [BOOL] = { NoteSameClassChild: PROC [slice: Slice] RETURNS [done: BOOL ¬ FALSE] = { done ¬ TRUE; }; IF classType=NIL THEN RETURN [NOT IsParent[slice]]; IF classType = $Cluster THEN RETURN[NOT GGParent.WalkChildren[slice, first, NoteSameClassChild, $Cluster]]; IF classType = $Outline THEN RETURN[NOT GGParent.WalkChildren[slice, first, NoteSameClassChild, $Outline]]; RETURN[GGSliceOps.GetType[slice] = classType]; }; CreateCluster: PUBLIC PROC [frozen: BOOL ¬ TRUE] RETURNS [parent: Slice] = { data: OutlineData ¬ NEW[OutlineDataObj ¬ [oddWrap: TRUE, fillColor: NIL, children: NIL, subclassData: NEW[ClusterDataObj ¬ [frozen: frozen]] ] ]; parent ¬ NEW[SliceObj ¬ [ class: GGSlice.FetchSliceClass[$Cluster], data: data, selectedInFull: [FALSE, FALSE, FALSE], tightBox: GGBoundBox.NullBoundBox[], boundBox: GGBoundBox.NullBoundBox[], boxValid: FALSE]]; parent.nullDescriptor ¬ GGSlice.DescriptorFromParts[parent, NIL]; }; AddChildToCluster: PUBLIC PROC [parent: Slice, child: Slice, priority: INT] = { parentData: OutlineData ¬ NARROW[parent.data]; IF GGSliceOps.GetType[parent]#$Cluster THEN ERROR; IF child = NIL THEN RETURN; child.parent ¬ parent; IF priority = -1 OR GGParent.TopPriority[parent] do for all children Sets the stroke width of the named parts of slice to be strokeWidth. parts=NIL => do for all children Call GGSliceOps.SetStrokeWidth on each child, with history=NIL so the children will not make history list entries. The parent will make one instead. CopyBoundBox in case the caller or descendants messes with the conference of the slice boundBox Sets the stroke end of the named parts of slice to be strokeEnd. parts=NIL => do for all children call GGSliceOps.SetStrokeEnd on each child, with history=NIL so the children will not make history list entries. The outline will make one instead. Sets the stroke Joint of the named parts of slice to be strokeJoint. parts=NIL => do for all children call GGSliceOps.SetStrokeJoint on each child, with history=NIL so the children will not make history list entries. The outline will make one instead. Sets the stroke color of the named parts of slice to be color. parts=NIL => do for all children Call GGSliceOps.SetStrokeColor on each child, with history=NIL so the children will not make history list entries. The outline will make one instead. Sets the fill color of the named parts of slice to be color. parts=NIL => do for all children Call GGSliceOps.SetFillColor on each child, with history=NIL so the children will not make history list entries. The parent will make one instead. Get the fill color of the slice. Sets the dash pattern of the named parts of slice. parts=NIL => do for all children call GGSliceOps.SetDashed on each child, with history=NIL so the children will not make history list entries. The outline will make one instead. Define the orientation of an outline to be the orientation of its first child. Outline Utility operations. Should check for duplicates. -- Bier, February 20 IF newChild = NIL, this routine removes the first child. oldChild=NIL => RETURN[outline] newChild=NIL => delete oldChild outlineD is a summary of the active bits of this outline that are set. We proceed as follows: 1) Find the first active run of outlineD. 2) Replace it with the desired new run to create a new trajectory and a new outline. 3) Extract a new outlineD from the active bits of the result. 4) If outlineD is empty, we are done. Otherwise return to step 1. If newOutline is NIL at the end, no replaces were done. First call: previousChild=NIL, resumeLoc=FirstChild[node] Rest calls: previousChild#NIL initialize resumeLoc for first call of PutTextInBox sliceD.slice is a cluster. For each child, if its descriptor contains traj, remove traj from the descriptor. Otherwise leave the descriptor alone. Build a new cluster descriptor from the results. Used by GGSelect to do a DeselectTraj. This routine could simply call GGSliceOps.SaveSelections for each selection class. It is slightly more optimized for the common case. Now save the appropriate bits in the selected children. This will go away when the Caret machinery is revised. hitType, segNum, cpNum, jointNum, hitPoint only valid if GetType[child] is Traj outlineD describes a single segment. Return the segNum. Queries about Outlines Looks through the children of outline to see if any of the shapes match, control point for control point and coordinate for the coordinate, the traj entry. If so, return all such trajectories that match. Generates only the trajectory parts of the outline Like TrajsInOutline but returns a list (used by MatchTool). N.B.: the type check is to prevent erroneous reporting that Cluster types have holes, because if this proc is called with a Slice of class=$Cluster the NARROW will succeed. N.B.: the type check is to prevent erroneous reporting that Cluster types have holes, because if this proc is called with a Slice of class=$Cluster the NARROW will succeed. The Parent Meta-Class Parent Meta-Class Utility operations. Built-in classes include: $Cluster, $Outline, $Traj, $Box, $Circle, $Text, and $IP. This routine finds all such slices, even within clusters. class=NIL => all classes. Use level=all to walk every slice of a given class. Top level classes Depth first walk Built-in classes include: $Cluster, $Outline, $Traj, $Box, $Circle, $Text, and $IP. This routine finds all such slices, even within clusters. class=NIL => all classes. Use level=all to walk every selected slice of a given class. Depth first walk: First walk yourself ... ... then walk your children. Depth first walk: First walk yourself ... ... then walk your children. Depth first walk: First walk yourself ... ... then walk your children. Walk through the children (and, if requested recursively through the children's children, etc.) building up a descriptor of all parts of parent that meet the criterion defined by the tallyProc. Walk through the children (and, if requested recursively through the children's children, etc.) building up a descriptor of all parts of parent that meet the criterion defined by the tallyProc. A slice is a leaf if it is not a Parent slice and class doesn't matter. Return TRUE if a cluster has no cluster child => it is a leaf cluster Return TRUE if a outline has no outline child => it is a leaf outline Return TRUE if any other type matches the class Should check for duplicates. Copy incoming list to avoid mutations by PutBehind Disconnects the named child from parent (to which it presumably belongs). Does not affect selections. Priority order Returns the priority of the frontmost position in the scene. Destructively splices slices into scene Destructively splices slices into scene or off the scale If returned priority is 0, the child is the back-most entity. THIS CODE SHOULD POST AN ERROR IF slice is not in scene !! slice1 and slice2 must be descendents of the same parent slice. However, they can be at different levels within the parent slice's tree. Κ>¨–(cedarcode) style•NewlineDelimiter ™codešœ™KšΟnœK™SKšœ ΟeœC™NK™K™"K™*K™—šΟk ˜ J˜—K˜—šœŸœŸ˜JšŸœœ“˜›KšŸœ!Ÿ˜-K˜Kšœ Ÿœ˜&KšœŸœ˜#KšœŸœ˜#KšœŸœ˜KšœŸœ&˜AKšœ Ÿœ˜-KšœŸœ ˜5Kšœ Ÿœ ˜1KšœŸœ˜'KšœŸœ˜1Kšœ Ÿœ˜*KšœŸœ˜#KšœŸœ˜3KšœŸœ˜Kšœ Ÿœ˜-Kšœ Ÿœ˜*KšœŸœ˜0KšœŸœ˜0KšœŸœ˜6KšœŸœ˜,KšœŸœ˜2KšœŸœ˜!KšœŸœ˜/KšœŸœ˜3KšœŸœ"˜9KšœŸœ!˜7KšœŸœ#˜;KšœŸœ&˜AKšœ Ÿœœœœ˜$KšœŸœ˜!Kšœ Ÿœ˜'KšœŸœ!˜7KšœŸœ$˜=KšœŸœ!˜5Kšœ Ÿœ˜+KšœŸœ˜2KšœŸœ˜!Kšœ Ÿœ˜+KšœŸœ ˜5KšœŸœ)˜GKšœŸœ,˜MKšœŸœ)˜GKšœŸœ(˜EKšœŸœ˜3KšœŸœ"˜9Kšœ Ÿœ˜'Kšœ Ÿœ˜+KšœŸœ#˜;KšœŸœ˜3KšœŸœ˜1Iprocšœ Ÿœ˜#Kšœ Ÿœ˜'KšœŸœ˜Kšœ Ÿœ˜'Kšœ Ÿœ˜%Kšœ Ÿœ˜)KšœŸœ˜/Kšœ Ÿœ˜/KšœŸœ˜#Kšœ Ÿœ˜)K˜KšœŸœŸœ Ÿœ˜;K˜Lšœ ŸœŸœ˜'LšœŸœ˜.—Ihead1™šœŸœŸœ˜@K˜+K˜=K˜BK˜/K˜;K˜3K˜7K˜;KšΟb™K˜)K˜/K˜/K˜+K˜+K˜/K˜/K˜/K˜/K˜+K˜)K˜(K˜(K˜%K˜%K˜/K˜/K˜K˜—šœŸœŸœ˜@Kš  ™ K˜+K˜=K˜BK˜/K˜;K˜3K˜7K˜;Kš ™K˜)K˜/K˜/K˜+K˜+K˜/K˜/K˜/K˜/K˜+K˜+K˜(K˜(K˜%K˜%K˜/K˜/K˜—K˜K™ šΠbn œŸœŸœ8ŸœŸœ+Ÿœ.ŸœŸœ ŸœŸœ˜ήš ŸœŸœŸœŸœŸœ˜ KšœŸœ˜+Kšœ˜Kšœ˜Kšœ Ÿœ˜Kšœ˜KšœŸœŸœ˜"Kšœ ŸœŸœ˜Kšœ˜K˜šŸœŸœY˜_KšŸœŸœ˜ —K˜K˜"š ŸœŸœŸœ0ŸœŸœŸ˜TKšŸœ ŸœŸœŸœ˜KšœU  œ#˜„K™”šŸœ ŸœŸœ˜-K˜K˜K˜K˜K˜Kšœ Ÿœ˜K˜—KšŸœ˜—šŸœ Ÿœ˜šœŸœ˜4Kšœ˜—K˜—K˜—K˜K˜—š œŸ œ@ŸœŸœŸœE˜·Kšœ!Ÿœ ˜1K˜$KšœŸœŸœ˜4K˜PKšœ˜Kšœ( œ,˜iK˜IK˜K˜—šœŸ œ8ŸœŸœ+Ÿœ.ŸœŸœ ŸœŸœ˜ΰš ŸœŸœŸœŸœŸœ˜ KšœŸœ˜+Kšœ˜Kšœ˜Kšœ Ÿœ˜Kšœ˜KšœŸœŸœ˜"Kšœ ŸœŸœ˜Kšœ˜K˜KšŸœŸœY˜_KšŸœŸœΟc1˜>K˜K˜"š ŸœŸœŸœ0ŸœŸœŸ˜TKšŸœ ŸœŸœŸœ˜KšœU œ#˜†K™”šŸœ ŸœŸœ˜-K˜K˜K˜K˜K˜Kšœ Ÿœ˜K˜—KšŸœ˜—šŸœ Ÿœ˜šœŸœ˜4Kšœ˜—K˜—K˜—K˜K˜—šœŸœ)ŸœŸœ ŸœŸœŸœŸœŸœŸœŸœŸœ˜šK™œLšœŸœ ˜.LšœŸœ˜ŸœŸœŸ˜pK˜,KšŸœ ŸœŸœŸœ˜Kšœ œ ˜1šŸœsŸœŸœŸ˜ˆKšœ% œ ˜Aš ŸœŸœŸœ ŸœŸœŸ˜DKšœ Ÿœ˜"KšŸœ˜—K˜$KšŸœ˜—KšŸœ˜—K˜K˜—š ‘œŸ œ+Ÿœ ŸœŸœŸœ ˜‚Kšœx™xKšœ&˜&KšœŸœ˜2Kšœ ŸœŸœ˜Kšœ Ÿœ˜š ŸœŸœŸœ>ŸœŸœŸ˜pK˜,KšŸœ ŸœŸœŸœ˜Kšœ œ ˜1šŸœsŸœŸœŸ˜ˆKšœ% œ˜Eš ŸœŸœŸœ ŸœŸœŸ˜DKšœ Ÿœ˜"KšŸœ˜—K˜$KšŸœ˜—KšŸœ˜—K˜K˜—š‘œŸ œŸœŸœŸœŸœŸœ˜eKšœZ™ZKšœ!Ÿœ ˜1Kšœ œ4˜aK˜K˜—š œŸœŸœŸœŸœŸœŸœŸœ˜kK˜ K˜šŸœŸœŸ˜$Kšœ!Ÿœ˜5K˜K˜*KšŸœ˜—K˜K˜—™K˜—š œŸœŸœT˜lšœŸœ&˜£œ˜CKšœ£œ˜„KšœD£œ’Πbc˜iKšœ@£œ’€˜eK˜K˜—KšœŸœ˜+KšœŸœ ˜.˜+K™ —šŸœ ŸœŸœŸœ ŸœŸœ.Ÿœ ŸœŸ˜pKšœ"Ÿœ˜'KšŸ˜—š ŸœŸœ ŸœŸœ<Ÿœ ŸœŸ˜oKšŸœŸœŸœ@˜[KšŸœ˜—Kšœ˜K˜—š ‘œŸœŸœ0ŸœŸœ˜ƒKšœD™DK™ KšœŸœ˜+KšœŸœ ˜.Kšœ•™•šŸœ ŸœŸœŸœ ŸœŸœ.Ÿœ ŸœŸ˜pKšœ0Ÿœ£œ˜GKšŸ˜—š ŸœŸœŸœŸœ7ŸœŸœŸ˜`šŸœ ŸœŸœ˜KšœP£œ˜UK˜—KšŸœ˜—˜DK™_—K˜K˜—š‘œŸœŸœ#ŸœŸœŸœŸœ˜{KšœŸœŸœ˜š  ‘œŸœ(ŸœŸœŸœ˜`KšœŸœŸœ˜Kšœ Ÿœ˜K˜IšŸœŸœŸœ˜K˜KšŸœŸœ˜ K˜—šŸœ˜šŸœŸœ˜KšŸœŸœŸœŸœ˜-K˜—šŸœ˜K˜KšœŸœ˜ K˜—K˜—K˜—Kšœ Ÿœ<Ÿœ˜TK˜K˜—š‘ œŸœŸœS˜lKšœ@™@K™ KšœŸœ˜+KšœŸœ ˜.Kšœ”™”šŸœ ŸœŸœŸœ ŸœŸœ.Ÿœ ŸœŸ˜pKšœ.Ÿœ £œ˜CKšŸ˜—š ŸœŸœŸœŸœ7ŸœŸœŸ˜`KšŸœ ŸœŸœM£œ˜hKšŸœ˜—K˜K˜—š ‘ œŸœŸœ#Ÿœ*ŸœŸœ˜}KšœŸœŸœ˜š œŸœ(ŸœŸœŸœ˜^KšœŸœŸœ˜Kšœ˜K˜EšŸœŸœŸœ˜K˜KšŸœŸœ˜ K˜—šŸœ˜šŸœŸœ˜KšŸœŸœŸœŸœ˜)K˜—šŸœ˜K˜KšœŸœ˜ K˜—K˜—K˜—Kšœ Ÿœ:Ÿœ˜RK˜K˜—š‘œŸœŸœW˜rKšœD™DK™ KšœŸœ˜+KšœŸœ ˜.Kšœ–™–šŸœ ŸœŸœŸœ ŸœŸœ.Ÿœ ŸœŸ˜pKšœ0ŸœŸœ˜GKšŸ˜—š ŸœŸœŸœŸœ7ŸœŸœŸ˜`Kš Ÿœ ŸœŸœ œ2£œ˜lKšŸœ˜—Kšœ˜K˜—š ‘œŸœŸœ#Ÿœ.ŸœŸœ˜ƒKšœŸœŸœ˜š œŸœ(ŸœŸœŸœ˜`KšœŸœŸœ˜Kšœ˜K˜IšŸœŸœŸœ˜K˜KšŸœŸœ˜ K˜—šŸœ˜šŸœŸœ˜KšŸœŸœŸœŸœ˜-K˜—šŸœ˜K˜KšœŸœ˜ K˜—K˜—K˜—Kšœ Ÿœ<Ÿœ˜TK˜K˜—š‘œŸœŸœ9Ÿœ˜tKšœ>™>K™ KšœŸœ˜+KšœŸœ ˜.Kšœ–™–šŸœ ŸœŸœŸœ ŸœŸœ.Ÿœ ŸœŸ˜pKšœ0Ÿœ£œ˜IKšŸ˜—š ŸœŸœŸœŸœ7ŸœŸœŸ˜`šŸœ ŸœŸœ˜Kšœ œ4£œ˜WK˜—KšŸœ˜—K˜K˜—š ‘œŸœŸœ#ŸœŸœŸœ˜oKšœŸœŸœ˜š œŸœ(ŸœŸœŸœ˜`KšœŸœŸœ˜Kšœ˜K˜IšŸœŸœŸœ˜K˜KšŸœŸœ˜ K˜—šŸœ˜šŸœŸœ˜Kš ŸœŸœ.ŸœŸœŸœ˜FK˜—šŸœ˜K˜KšœŸœ˜ K˜—K˜—K˜—Kšœ Ÿœ<Ÿœ˜TK˜K˜—š‘ œŸœŸœ9Ÿœ˜rKšœ<™Kš œ Ÿœ ŸœŸœŸœŸœ6˜]K˜——KšŸœ˜—K˜K˜—š œŸœŸœŸœ˜ŸœŸœŸ˜pšŸœŸœŸœ˜K˜$KšŸœ1Ÿœ.Ÿœ˜„KšŸœ˜K˜—KšŸœ˜—K˜K˜—šœŸœŸœŸœ ŸœŸœCŸœŸœ Ÿœ"ŸœŸœ Ÿœ ˜žKšœ˜Kš Ÿœ ŸœŸœ-ŸœŸœ˜KKšœŸœ˜&š ŸœŸœŸœ>ŸœŸœŸ˜pšŸœŸœŸœ˜Kšœ Ÿœ’&˜6K˜$KšŸœ1Ÿœ˜8K˜’KšŸœ˜K˜—KšŸœ˜—K˜K™—K˜šœ ™K™—š œŸœŸœŸœ ŸœŸœ ˜gKšœΜ™ΜKšœŸœ˜0KšœŸœŸœ˜š  œŸœŸœŸœŸœ˜AKšŸœ"Ÿœ@˜hK˜—K˜=K˜K˜—šœŸœŸœŸœ˜SKšœ2™2KšœŸœ˜0Kšœ ŸœŸœ(˜:Kšœ Ÿœ"˜/K˜K˜—šœŸœŸœŸœ ŸœŸœ Ÿœ˜\Kšœ;™;KšœŸœ˜0KšœŸœŸœ˜KšŸœŸœ&ŸœŸœ˜8K˜*š ŸœŸœŸœ)ŸœŸœŸ˜MKšŸœ&Ÿœ;˜gKšŸœ˜—K˜K˜—š  œŸœŸœŸœ ŸœŸœ ˜NKšœŸœ˜)K™¬KšŸœ&Ÿœ ˜LK˜K˜—š œŸœŸœŸœŸœŸœ˜BKšœŸœ˜)K™¬KšŸœ&ŸœŸœ˜HK˜—M™Kšœ%™%K™šœŸœŸœŸœ)˜_K˜2K˜K™—š  œŸœŸœŸœ ŸœŸœ˜aKšœŸœ˜2KšœŸœŸœ˜K˜8š ŸœŸœŸœ7ŸœŸœŸ˜[KšŸœŸœŸœM˜eKšŸœ˜—K˜K˜—šœŸœŸœ%Ÿœ)˜hKšœ Ÿœ$˜2Kš ŸœŸœŸœŸœ’˜'šŸœ˜KšœŸœ˜+KšœŸœŸœ˜(K˜8š ŸœŸœŸœ7ŸœŸœŸ˜[KšŸœŸœŸœM˜eKšŸœ˜—K˜K˜—K˜K˜—šœŸœŸœŸœ ˜NKšœŸœ˜)šœ Ÿœ˜$Kšœ˜K˜—K˜K˜—š  œŸœŸœŸœ ŸœŸœ ˜OKšœŸœ˜)K˜K˜K˜K˜—K˜š œŸœŸœ=Ÿœ ŸœŸœŸœ ŸœŸœ˜•Kšœ–ŸœE™ήšŸœŸœ˜#KšœŸœ˜/š Ÿœ ŸœŸœ+Ÿœ ŸœŸ˜SKšœ Ÿœ$˜2Kšœ™š ŸœŸœ2ŸœŸœŸœŸœ˜yK˜"KšŸœ ŸœŸœ˜K˜—K™š ŸœŸœŸœŸœŸœ!Ÿœ˜yK˜KKšŸœ ŸœŸœ˜K˜—KšŸœ˜—K˜—K˜K˜—šœŸœŸœdŸœŸœŸœ ŸœŸœ˜΄Kšœ–ŸœN™ηKšœŸœ˜(šŸœŸœ˜%KšœŸœ˜+KšŸœŸœŸœŸœ˜"š Ÿœ ŸœŸœ=ŸœŸœŸ˜mK˜/š ŸœŸœŸœŸœ%Ÿœ˜FKšœ Ÿœ(˜6K™*š ŸœŸœ6ŸœŸœŸœ3Ÿœ˜™K˜ KšŸœ ŸœŸœ˜K˜—K™š ŸœŸœŸœŸœŸœ%Ÿœ˜}Kšœ  œA˜_KšŸœ ŸœŸœ˜K˜—K˜—KšŸœ˜—K˜—K˜K˜—šœŸœ_ŸœŸœŸœ ŸœŸœ˜₯KšœŸœ˜(šŸœŸœ˜%šŸœ ŸœŸœ˜KšœŸœ˜/š Ÿœ ŸœŸœ.Ÿœ ŸœŸ˜\K˜#Kšœ Ÿœ!˜/K™*š ŸœŸœ/ŸœŸœŸœ,Ÿœ˜‹KšœŸœ˜$KšŸœ ŸœŸœ˜K˜—K™š ŸœŸœŸœŸœŸœŸœ˜vKšœ  œ Ÿœ˜HKšŸœ ŸœŸœ˜K˜—KšŸœ˜—K˜—šŸœ˜KšœŸœ˜+KšŸœŸœŸœŸœ˜"š Ÿœ ŸœŸœ=ŸœŸœŸ˜mK˜/š ŸœŸœŸœŸœ%Ÿœ˜FKšœ Ÿœ(˜6K™*š ŸœŸœ6ŸœŸœŸœ3Ÿœ˜™K˜8KšŸœ ŸœŸœ˜K˜—K™š ŸœŸœŸœŸœŸœ%Ÿœ˜}Kšœ œA˜\KšŸœ ŸœŸœ˜K˜—K˜—KšŸœ˜—K˜—K˜—K˜K˜—š œŸœŸœ.ŸœŸœŸœ ŸœŸœ ˜yKšœŸœŸœ˜š  œŸœŸœŸœŸœ˜CK˜=K˜—K˜.K˜;K˜K˜—šœŸœŸœAŸœŸœŸœŸœŸœ˜‘KšœŸœŸœ˜š  œŸœŸœŸœŸœ˜NK˜NK˜—K˜;K˜JK˜K˜—š  œŸœŸœ.ŸœŸœŸœ˜pš œŸœŸœŸœŸœ˜FK˜KšŸœŸœ˜ K˜—K˜>K˜K˜—š œŸœŸœAŸœŸœŸœ˜‘š œŸœŸœŸœŸœ˜QK˜KšŸœŸœ˜ K˜—K˜MK˜K˜—š  œŸœŸœ,Ÿœ$ŸœŸœ˜‚KšœΑ™ΑKšœ&ŸœŸœ˜3K˜0šŸœŸœŸœ˜5KšœŸœ˜/KšœŸœŸœ˜KšœŸœ˜.K˜Cš ŸœŸœŸœ)ŸœŸœŸ˜MKšœ˜šŸœŸœ Ÿœ˜K˜8KšŸœ ŸœŸœ Ÿœ˜%K˜—KšŸœ Ÿœ˜K˜]KšŸœ˜—KšŸœ Ÿœ7˜HKšŸœ Ÿœ˜K˜—šŸœ˜KšŸœŸœ&Ÿœ˜=KšŸœ Ÿœ˜K˜K˜—K˜—š œŸœŸœIŸœ$ŸœŸœ˜§KšœΑ™ΑKšœ ŸœŸœ˜-Kšœ˜K˜FK˜2šŸœŸœŸœ˜5KšœŸœ˜+KšœŸœŸœ˜KšœŸœ˜.K˜Cš ŸœŸœŸœ7ŸœŸœŸ˜[Kšœ˜šŸœŸœ Ÿœ˜šŸœŸœŸœ˜K˜XKšŸœ ŸœŸœ Ÿœ˜%K˜—K˜—KšŸœ Ÿœ˜K˜]KšŸœ˜—KšŸœ Ÿœ7˜HKšŸœ Ÿœ˜K˜—šŸœ˜K˜K˜K˜—K˜K˜—š œŸœŸœŸœŸœ˜7KšŸœŸœ˜CK˜K˜—š  œŸœŸœ ŸœŸœŸœ˜>KšŸœŸœ˜5K˜K˜—š œŸœŸœŸœ˜AKšŸœ˜K˜K˜—šœŸœŸœŸœ˜MK˜KšŸœŸœ*Ÿœ˜WK˜K˜—š œŸœŸœŸœŸœŸœŸœ˜Sš œŸœŸœŸœŸœ˜HKšœŸœ˜ K˜—K™GKš Ÿœ ŸœŸœŸœŸœ˜3KšœŸœ:™EšŸœŸ˜KšŸœŸœD˜N—KšœŸœ:™EšŸœŸ˜KšŸœŸœD˜N—KšœŸœ$™/KšŸœ(˜.K˜K˜—š  œŸœŸœ ŸœŸœŸœ˜MKš œŸœŸœ Ÿœ ŸœŸœ(˜‘šœ Ÿœ ˜Kšœ)˜)K˜ KšœŸœŸœŸœ˜&Kšœ$˜$Kšœ$˜$Kšœ Ÿœ˜—Kšœ<Ÿœ˜AKšœ˜K˜—šœŸœŸœ)Ÿœ˜OKšœ™KšœŸœ˜.KšŸœ%ŸœŸœ˜2KšŸœ ŸœŸœŸœ˜K˜KšŸœŸœ'Ÿœ˜YšŸœŸœŸœ˜KšœŸœ˜KšœŸœ˜#KšœŸœ˜7K˜—KšŸœFŸœ ˜WKšœ˜Kšœ˜K˜—š œŸœŸœŸœŸœŸœ˜]KšœŸœ˜.KšŸœ%ŸœŸœ˜2KšŸœ ŸœŸœŸœ˜š ŸœŸœŸœŸœŸœŸ˜AK˜KšŸœ˜—šŸœŸœŸœ˜7š ŸœŸœŸœŸœŸœŸ˜AKšœ ˜ KšŸœ˜—K˜—šŸœŸœŸœ˜KšœŸœ˜KšœŸœ˜#K˜OK˜—šŸœ˜Kšœ2™2KšœŸœŸœ+˜@KšœS˜SK˜—Kšœ˜K˜—K˜š œŸœŸœŸœ˜UK™fKšœŸœ˜.KšœŸœ˜KšœŸœ˜#K˜PKšœ˜Kš œ ŸœŸœŸœŸœŸœ˜>K˜K˜—šœŸœŸœ#˜AKšœŸœ˜0KšŸœ&ŸœŸœ˜3šŸœŸœŸœ˜$K˜#šŸœ ŸœŸœ˜Kšœ$˜$K˜—šŸœ˜K˜4KšŸœŸœŸœ"˜=K˜—K˜—K˜K˜—š œŸœŸœŸœ˜9KšœŸœ˜/KšœŸœ˜;K˜K˜K˜—š  œŸœŸœŸœ Ÿœ˜BKšœŸœ˜/KšœŸœ˜;K˜K˜—K˜š œŸœ"˜3KšœŸœ˜.K˜KšœŸœ˜#KšŸœŸœŸœ˜6K˜gK˜K˜—K™K™š  œŸœŸœŸœ Ÿœ˜DKšœ<™K˜K˜K˜—KšœŸœ˜Kšœ(₯œ₯˜4KšœŸœ˜"Kšœ˜K˜—š œŸœ˜-Kš ŸœŸœŸœŸœŸ˜9š ŸœŸœŸœ(ŸœŸœŸ˜JK˜KšŸœ˜—KšœŸœ˜Kšœ˜K˜—K˜KšŸœ˜—…—Ϋ˜6ΐ