DIRECTORY Basics, Feedback, GGBasicTypes, GGBoundBox, GGCoreOps, GGCoreTypes, GGHistoryTypes, GGInterfaceTypes, GGModelTypes, GGOutline, GGParent, GGScene, GGSegmentTypes, GGSelect, GGSequence, GGSlice, GGSliceOps, GGTraj, 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; 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 _ 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; 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 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 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]; }; 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; [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]; holesList _ data.children.rest; }; HasHoles: PUBLIC PROC [outline: Slice] RETURNS [BOOL _ FALSE] = { data: OutlineData _ NARROW[outline.data]; RETURN[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 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. 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 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). 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. Κ?‘˜Icodešœ™šΟnœK™SKšœH™HKšœ%™%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šœžœ!˜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šœB˜BKšœ/˜/Kšœ;˜;Kšœ3˜3Kšœ7˜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šœB˜BKšœ/˜/Kšœ;˜;Kšœ3˜3Kšœ7˜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™”šžœ žœžœ˜-Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ žœ˜J˜—Jšžœ˜—šžœ žœ˜šœžœ˜4Jšœ˜—J˜—K˜—K˜K˜—š œž œ@žœžœžœE˜·Kšœ!žœ ˜1Kšœ$˜$Kšœžœžœ˜4KšœP˜PKšœ˜Kšœ(Ÿœ,˜iKšœI˜IK˜K˜—šœž œ8žœžœ+žœ.žœžœ žœžœ˜ΰš žœžœžœžœžœ˜ Kšœžœ˜+Kšœ˜Kšœ˜Kšœ žœ˜Kšœ˜Kšœžœžœ˜"Kšœ žœžœ˜Kšœ˜K˜KšžœžœY˜_KšžœžœΟc1˜>K˜Kšœ"˜"š žœžœžœ0žœžœž˜TKšžœ žœžœžœ˜JšœUŸœ#˜†K™”šžœ žœžœ˜-Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ žœ˜J˜—Jšžœ˜—šžœ žœ˜šœžœ˜4Jšœ˜—J˜—K˜—K˜K˜—šœžœ)žœžœ žœžœžœžœžœžœžœžœ˜šK™œLšœžœ ˜.Lšœžœ˜žœžœž˜pJšœ,˜,Jšžœ žœžœžœ˜JšœŸœ ˜1šžœsžœžœž˜ˆJšœ%Ÿœ ˜Aš žœžœžœ žœžœž˜DJšœ žœ˜"Jšžœ˜—Jšœ$˜$Jšžœ˜—Jšžœ˜—K˜K˜—š  œž œ+žœ žœžœžœ ˜‚Kšœx™xJšœ&˜&Kšœžœ˜2Kšœ žœžœ˜Kšœ žœ˜š žœžœžœ>žœžœž˜pJšœ,˜,Jšžœ žœžœžœ˜JšœŸœ ˜1šžœsžœžœž˜ˆJšœ%Ÿœ˜Eš žœžœžœ žœžœž˜DJšœ žœ˜"Jšžœ˜—Jšœ$˜$Jšžœ˜—Jšžœ˜—K˜K˜—š œž œžœžœžœžœžœ˜eKšœZ™ZKšœ!žœ ˜1KšœŸœ4˜aK˜K˜—š œžœžœžœžœžœžœžœ˜kKšœ ˜ Kšœ˜šžœžœž˜$Kšœ!žœ˜5Kšœ˜Kšœ*˜*Kšžœ˜—K˜K˜—™K˜—š œžœžœT˜lšœžœ&˜’œ˜CJšœ’œ˜„JšœD’œ‘Πbc˜iJšœ@’œ‘£˜eK˜K˜—Kšœžœ˜+Jšœžœ ˜.šœ+˜+K™ —šžœ žœžœžœ žœžœ.žœ žœž˜pJšœ"žœ˜'Jšž˜—š žœžœ žœžœ<žœ žœž˜oJšžœžœžœ@˜[Jšžœ˜—Kšœ˜K˜—š  œžœžœ0žœžœ˜ƒKšœD™DK™ Kšœžœ˜+Jšœžœ ˜.Kšœ•™•šžœ žœžœžœ žœžœ.žœ žœž˜pKšœ0žœ’œ˜GJšž˜—š žœžœžœžœ7žœžœž˜`šžœ žœžœ˜JšœP’œ˜UJ˜—Jšžœ˜—Kšœ+˜+K˜K˜—š œžœžœ#žœžœžœžœ˜{Kšœžœžœ˜š   œžœ(žœžœžœ˜`Jšœžœžœ˜Kšœ žœ˜JšœI˜Išžœžœžœ˜Jšœ˜Jšžœžœ˜ J˜—šžœ˜šžœžœ˜Jšžœžœžœžœ˜-J˜—šžœ˜Jšœ˜Jšœžœ˜ J˜—J˜—J˜—Kšœ žœ<žœ˜TK˜K˜—š  œžœžœS˜lKšœ@™@K™ Kšœžœ˜+Jšœžœ ˜.Kšœ”™”šžœ žœžœžœ žœžœ.žœ žœž˜pKšœ.žœ ’œ˜CJšž˜—š žœžœžœžœ7žœžœž˜`Jšžœ žœžœM’œ˜hJšžœ˜—K˜K˜—š   œžœžœ#žœ*žœžœ˜}Kšœžœžœ˜š œžœ(žœžœžœ˜^Jšœžœžœ˜Kšœ˜JšœE˜Ešžœžœžœ˜Jšœ˜Jšžœžœ˜ J˜—šžœ˜šžœžœ˜Jšžœžœžœžœ˜)J˜—šžœ˜Jšœ˜Jšœžœ˜ J˜—J˜—J˜—Kšœ žœ:žœ˜RK˜K˜—š œžœžœW˜rKšœD™DK™ Kšœžœ˜+Jšœžœ ˜.Kšœ–™–šžœ žœžœžœ žœžœ.žœ žœž˜pKšœ0žœžœ˜GJšž˜—š žœžœžœžœ7žœžœž˜`Jš žœ žœžœŸœ2’œ˜lJšžœ˜—Kšœ˜K˜—š  œžœžœ#žœ.žœžœ˜ƒKšœžœžœ˜š œžœ(žœžœžœ˜`Jšœžœžœ˜Kšœ˜JšœI˜Išžœžœžœ˜Jšœ˜Jšžœžœ˜ J˜—šžœ˜šžœžœ˜Jšžœžœžœžœ˜-J˜—šžœ˜Jšœ˜Jšœžœ˜ J˜—J˜—J˜—Kšœ žœ<žœ˜TK˜K˜—š œžœžœ9žœ˜tKšœ>™>K™ Kšœžœ˜+Jšœžœ ˜.Kšœ–™–šžœ žœžœžœ žœžœ.žœ žœž˜pKšœ0žœ’œ˜IJšž˜—š žœžœžœžœ7žœžœž˜`šžœ žœžœ˜JšœŸœ4’œ˜WJ˜—Jšžœ˜—K˜K˜—š  œžœžœ#žœžœžœ˜oKšœžœžœ˜š œžœ(žœžœžœ˜`Jšœžœžœ˜Kšœ˜JšœI˜Išžœžœžœ˜Jšœ˜Jšžœžœ˜ J˜—šžœ˜šžœžœ˜Jš žœžœ.žœžœžœ˜FJ˜—šžœ˜Jšœ˜Jšœžœ˜ J˜—J˜—J˜—Kšœ žœ<žœ˜TK˜K˜—š  œžœžœ9žœ˜rKšœ<™žœžœž˜pšžœžœžœ˜Jšœ$˜$Kšžœ1žœ.žœ˜„Jšžœ˜J˜—Jšžœ˜—K˜K˜—šœžœžœžœ žœžœCžœžœ žœ"žœžœ žœ ˜žKšœ˜Kš žœ žœžœ-žœžœ˜KKšœžœ˜&š žœžœžœ>žœžœž˜pšžœžœžœ˜Jšœ žœ‘&˜6Jšœ$˜$Kšžœ1žœ˜8Jšœ’˜’Jšžœ˜J˜—Jšžœ˜—K˜K™—K˜šœŸ™K™—š œžœžœžœ žœžœ ˜gKšœΜ™ΜKšœžœ˜0Kšœžœžœ˜š  œžœžœžœžœ˜AKšžœ"žœ@˜hK˜—Kšœ=˜=K˜K˜—šœžœžœžœ˜SKšœ2™2Kšœžœ˜0Kšœ žœžœ(˜:Kšœ žœ"˜/K˜K˜—šœžœžœžœ žœžœ žœ˜\Kšœ;™;Kšœžœ˜0Kšœžœžœ˜Kšœ*˜*š žœžœžœ)žœžœž˜MJšžœ&žœ;˜gJšžœ˜—K˜K˜—š  œžœžœžœ žœžœ ˜NKšœžœ˜)Kšœ˜K˜K˜—š œžœžœžœž œ˜BKšœžœ˜)Kšžœžœ˜K˜—M™Kšœ%™%K™šœžœžœžœ)˜_Kšœ2˜2K˜K™—š  œžœžœžœ žœžœ˜aKšœžœ˜2Kšœžœžœ˜Kšœ8˜8š žœžœžœ7žœžœž˜[JšžœžœžœM˜eJšžœ˜—K˜K˜—šœžœžœ%žœ)˜hKšœ žœ$˜2Kš žœžœžœžœ‘˜'šžœ˜Kšœžœ˜+Kšœžœžœ˜(Kšœ8˜8š žœžœžœ7žœžœž˜[JšžœžœžœM˜eJšžœ˜—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šœK˜KKšžœ žœžœ˜K˜—Jšžœ˜—J˜—K˜K˜—šœžœžœdžœžœžœ žœžœ˜΄Kšœ–žœN™ηKšœžœ˜(šžœžœ˜%Kšœžœ˜+Kšžœžœžœžœ˜"š žœ žœžœ=žœžœž˜mKšœ/˜/š žœžœžœžœ%žœ˜FKšœ žœ(˜6K™*š žœžœ6žœžœžœ3žœ˜™Kšœ ˜ Kšžœ žœžœ˜K˜—K™š žœžœžœžœžœ%žœ˜}Kšœ ŸœA˜_Kšžœ žœžœ˜K˜—K˜—Jšžœ˜—J˜—K˜K˜—šœžœ_žœžœžœ žœžœ˜₯Kšœžœ˜(šžœžœ˜%šžœ žœžœ˜Kšœžœ˜/š žœ žœžœ.žœ žœž˜\Kšœ#˜#Kšœ žœ!˜/K™*š žœžœ/žœžœžœ,žœ˜‹Kšœžœ˜$Kšžœ žœžœ˜K˜—K™š žœžœžœžœžœžœ˜vKšœ Ÿœ žœ˜HKšžœ žœžœ˜K˜—Jšžœ˜—K˜—šžœ˜Kšœžœ˜+Kšžœžœžœžœ˜"š žœ žœžœ=žœžœž˜mKšœ/˜/š žœžœžœžœ%žœ˜FKšœ žœ(˜6K™*š žœžœ6žœžœžœ3žœ˜™Kšœ8˜8Kšžœ žœžœ˜K˜—K™š žœžœžœžœžœ%žœ˜}Kšœ œA˜\Kšžœ žœžœ˜K˜—K˜—Jšžœ˜—J˜—J˜—K˜K˜—š œžœžœ.žœžœžœ žœžœ ˜yKšœžœžœ˜š  œžœžœžœžœ˜CKšœ=˜=K˜—Kšœ.˜.Kšœ;˜;K˜K˜—šœžœžœAžœžœžœžœžœ˜‘Kšœžœžœ˜š  œžœžœžœžœ˜NKšœN˜NK˜—Kšœ;˜;KšœJ˜JK˜K˜—š  œžœžœ.žœžœžœ˜pš œžœžœžœžœ˜FKšœ˜Kšžœžœ˜ K˜—Kšœ>˜>K˜K˜—š œžœžœAžœžœžœ˜‘š œžœžœžœžœ˜QKšœ˜Kšžœžœ˜ K˜—KšœM˜MK˜K˜—š  œžœžœ,žœ$žœžœ˜‚KšœΑ™ΑKšœ&žœžœ˜3Kšœ0˜0šžœžœžœ˜5Kšœžœ˜/Kšœžœžœ˜Kšœžœ˜.KšœC˜Cš žœžœžœ)žœžœž˜MJšœ˜šžœžœ žœ˜Jšœ8˜8Jšžœ žœžœ žœ˜%J˜—Jšžœ žœ˜Jšœ]˜]Jšžœ˜—Jšžœ žœ7˜HJšžœ žœ˜K˜—šžœ˜Kšžœžœ&žœ˜=Kšžœ žœ˜Kšœ˜K˜—K˜—š œžœžœIžœ$žœžœ˜§KšœΑ™ΑKšœ žœžœ˜-Kšœ˜KšœF˜FKšœ2˜2šžœžœžœ˜5Kšœžœ˜+Kšœžœžœ˜Kšœžœ˜.KšœC˜Cš žœžœžœ7žœžœž˜[Jšœ˜šžœžœ žœ˜šžœžœžœ˜JšœX˜XJšžœ žœžœ žœ˜%J˜—J˜—Jšžœ žœ˜Jšœ]˜]Jšžœ˜—Jšžœ žœ7˜HJšžœ žœ˜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˜Kšžœ žœžœžœ˜Kšœ˜Kšžœžœ'žœ˜Yšžœžœžœ˜Kšœžœ˜Kšœžœ˜#Kšœžœ˜7K˜—KšžœFžœ ˜WKšœ˜Kšœ˜K˜—š œžœžœžœžœžœ˜]Kšœžœ˜.K˜Kšžœ žœžœžœ˜š žœžœžœžœžœž˜AKšœ˜Kšžœ˜—šžœžœžœ˜7š žœžœžœžœžœž˜AKšœ ˜ Kšžœ˜—K˜—šžœžœžœ˜Kšœžœ˜Kšœžœ˜#KšœO˜OK˜—šžœ˜Kšœ2™2Kšœžœžœ+˜@KšœS˜SK˜—Kšœ˜K˜—K˜š œžœžœžœ˜UK™fKšœžœ˜.Kšœžœ˜Kšœžœ˜#KšœP˜PKšœ˜Kš œ žœžœžœžœžœ˜>K˜K˜—šœžœžœ#˜AKšœžœ˜0šžœžœžœ˜$Kšœ#˜#šžœ žœžœ˜Kšœ$˜$K˜—šžœ˜Kšœ4˜4Kšžœžœžœ"˜=K˜—K˜—K˜K˜—š œžœžœžœ˜9Kšœžœ˜/Kšœžœ˜;Kšœ˜K˜K˜—š  œžœžœžœ žœ˜BKšœžœ˜/Kšœžœ˜;Kšœ˜K˜—K˜š œžœ"˜3Kšœžœ˜.K˜Kšœžœ˜#Kšžœžœžœ˜6Kšœg˜gK˜K˜—K™K™š  œžœžœžœ žœ˜DKšœ<™Jšœ˜Jšœ˜J˜—Jšœžœ˜Kšœ(€œ€˜4Jšœžœ˜"Kšœ˜K˜—š œžœ˜-Kš žœžœžœžœž˜9š žœžœžœ(žœžœž˜JKšœ˜Kšžœ˜—Kšœžœ˜Kšœ˜K˜—K˜Kšžœ˜—…—Υ†.U