DIRECTORY GGBasicTypes, GGBoundBox, GGDescribe, GGError, GGInterfaceTypes, GGModelTypes, GGOutline, GGObjects, GGSelect, GGSegmentTypes, GGSequence, GGShapes, GGTraj, GGUtility, GGVector, GList, Imager, ImagerColor, ImagerPath, ImagerTransformation, Rope, Rosary; GGOutlineImpl: CEDAR PROGRAM IMPORTS GGBoundBox, GGDescribe, GGError, GGObjects, GGSelect, GGSequence, GGShapes, GGTraj, GGUtility, GGVector, GList, Imager, ImagerColor, ImagerPath, ImagerTransformation, Rosary EXPORTS GGOutline = BEGIN BitVector: TYPE = GGBasicTypes.BitVector; BoundBox: TYPE = GGBasicTypes.BoundBox; CameraData: TYPE = GGModelTypes.CameraData; Circle: TYPE = GGBasicTypes.Circle; Color: TYPE = Imager.Color; ControlPointGenerator: TYPE = GGModelTypes.ControlPointGenerator; FeatureData: TYPE = GGInterfaceTypes.FeatureData; GargoyleData: TYPE = GGInterfaceTypes.GargoyleData; Joint: TYPE = GGSegmentTypes.Joint; JointGenerator: TYPE = GGModelTypes.JointGenerator; Line: TYPE = GGBasicTypes.Line; ObjectBag: TYPE = GGInterfaceTypes.ObjectBag; Outline: TYPE = REF OutlineObj; OutlineObj: TYPE = GGModelTypes.OutlineObj; OutlineClass: TYPE = REF OutlineClassObj; OutlineClassObj: TYPE = GGModelTypes.OutlineClassObj; OutlineDescriptor: TYPE = GGModelTypes.OutlineDescriptor; OutlineSequence: TYPE = GGSelect.OutlineSequence; OutlineSequenceGenerator: TYPE = GGSelect.OutlineSequenceGenerator; Point: TYPE = GGBasicTypes.Point; PointGenerator: TYPE = GGModelTypes.PointGenerator; PointPairGenerator: TYPE = GGModelTypes.PointPairGenerator; Scene: TYPE = GGModelTypes.Scene; Segment: TYPE = GGSegmentTypes.Segment; SegmentGenerator: TYPE = GGModelTypes.SegmentGenerator; SelectMode: TYPE = GGModelTypes.SelectMode; SelectionClass: TYPE = GGSegmentTypes.SelectionClass; Sequence: TYPE = GGModelTypes.Sequence; SliceClass: TYPE = REF SliceClassObj; SliceClassObj: TYPE = GGModelTypes.SliceClassObj; SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor; SliceParts: TYPE = GGModelTypes.SliceParts; Traj: TYPE = REF TrajObj; TrajEnd: TYPE = GGModelTypes.TrajEnd; TrajGenerator: TYPE = REF TrajGeneratorObj; TrajGeneratorObj: TYPE = GGModelTypes.TrajGeneratorObj; TrajObj: TYPE = GGModelTypes.TrajObj; TrajPartType: TYPE = GGModelTypes.TrajPartType; TriggerBag: TYPE = GGInterfaceTypes.TriggerBag; Vector: TYPE = GGBasicTypes.Vector; OutlineDescribeProc: TYPE = GGModelTypes.OutlineDescribeProc; OutlineFileoutProc: TYPE = GGModelTypes.OutlineFileoutProc; OutlineFileinProc: TYPE = GGModelTypes.OutlineFileinProc; OutlineUnionPartsProc: TYPE = GGModelTypes.OutlineUnionPartsProc; OutlineDifferencePartsProc: TYPE = GGModelTypes.OutlineDifferencePartsProc; OutlineFixedPartsProc: TYPE = GGModelTypes.OutlineFixedPartsProc; OutlineAugmentPartsProc: TYPE = GGModelTypes.OutlineAugmentPartsProc; OutlinePointsInDescriptorProc: TYPE = GGModelTypes.OutlinePointsInDescriptorProc; OutlinePointPairsInDescriptorProc: TYPE = GGModelTypes.OutlinePointPairsInDescriptorProc; OutlineNextPointProc: TYPE = GGModelTypes.OutlineNextPointProc; OutlineNextPointPairProc: TYPE = GGModelTypes.OutlineNextPointPairProc; OutlineClosestPointProc: TYPE = GGModelTypes.OutlineClosestPointProc; OutlineClosestPointAndTangentProc: TYPE = GGModelTypes.OutlineClosestPointAndTangentProc; OutlineClosestSegmentProc: TYPE = GGModelTypes.OutlineClosestSegmentProc; OutlineSetArrowsProc: TYPE = GGModelTypes.OutlineSetArrowsProc; OutlineGetArrowsProc: TYPE = GGModelTypes.OutlineGetArrowsProc; NotYetImplemented: PUBLIC ERROR = CODE; Problem: PUBLIC SIGNAL [msg: Rope.ROPE] = GGError.Problem; NoOpFileout: PUBLIC OutlineFileoutProc = { SIGNAL Problem [msg: "All slice classes must have this proc."]; }; NoOpFilein: PUBLIC OutlineFileinProc = { SIGNAL Problem [msg: "All slice classes must have this proc."]; }; NoOpFixedParts: PUBLIC OutlineFixedPartsProc = { SIGNAL Problem [msg: "All slice classes must have this proc."]; }; NoOpPointsInDescriptor: PUBLIC PROC [sliceD: OutlineDescriptor] RETURNS [pointGen: PointGenerator] = { pointGen _ NIL; }; NoOpPointPairsInDescriptor: PUBLIC PROC [sliceD: OutlineDescriptor] RETURNS [pointPairGen: PointPairGenerator] = { pointPairGen _ NIL; }; NoOpNextPoint: PUBLIC PROC [pointGen: PointGenerator] RETURNS [pointAndDone: GGModelTypes.PointAndDone] = { pointAndDone.done _ TRUE; }; NoOpNextPointPair: PUBLIC PROC [pointPairGen: PointPairGenerator] RETURNS [pointPairAndDone: GGModelTypes.PointPairAndDone] = { pointPairAndDone.done _ TRUE; }; NoOpClosestPoint: PUBLIC OutlineClosestPointProc = { SIGNAL Problem [msg: "All slice classes must have this proc."]; }; NoOpClosestPointAndTangent: PUBLIC OutlineClosestPointAndTangentProc = { SIGNAL Problem [msg: "All slice classes must have this proc."]; }; NoOpClosestSegment: PUBLIC OutlineClosestSegmentProc = { SIGNAL Problem [msg: "All slice classes must have this proc."]; }; NoOpSetArrows: PUBLIC OutlineSetArrowsProc = { SIGNAL Problem [msg: "All slice classes must have this proc."]; }; NoOpGetArrows: PUBLIC OutlineGetArrowsProc = { SIGNAL Problem [msg: "All slice classes must have this proc."]; }; MakeOutlineClass: PROC [] RETURNS [class: OutlineClass] = { class _ NEW[OutlineClassObj _ [ type: $Outline, getBoundBox: OutlineBoundBox, getTightBox: OutlineTightBox, copy: OutlineCopy, drawParts: OutlineDrawParts, drawTransform: OutlineDrawTransform, drawSelectionFeedback: OutlineDrawSelectionFeedback, drawAttractorFeedback: OutlineDrawAttractorFeedback, transform: OutlineTransform, describe: OutlineDescribe, fileout: NoOpFileout, filein: NoOpFilein, emptyParts: OutlineEmptyParts, newParts: OutlineNewParts, unionParts: OutlineUnionParts, differenceParts: OutlineDifferenceParts, movingParts: OutlineMovingParts, fixedParts: OutlineFixedParts, augmentParts: OutlineAugmentParts, setSelectedFields: OutlineSetSelectedFields, pointsInDescriptor: OutlinePointsInDescriptor, pointPairsInDescriptor: OutlinePointPairsInDescriptor, nextPoint: OutlineNextPoint, nextPointPair: OutlineNextPointPair, closestPoint: OutlineClosestPoint, closestPointAndTangent: NoOpClosestPointAndTangent, closestSegment: OutlineClosestSegment, lineIntersection: OutlineLineIntersection, circleIntersection: OutlineCircleIntersection, hitDataAsSimpleCurve: OutlineHitDataAsSimpleCurve, setStrokeWidth: OutlineSetStrokeWidth, getStrokeWidth: OutlineGetStrokeWidth, setStrokeColor: OutlineSetStrokeColor, getStrokeColor: OutlineGetStrokeColor, setFillColor: OutlineSetFillColor, getFillColor: OutlineGetFillColor, setArrows: NoOpSetArrows, getArrows: NoOpGetArrows ]]; }; OutlineParts: TYPE = REF OutlinePartsObj; OutlinePartsObj: TYPE = RECORD [ seqs: LIST OF Sequence ]; CreateOutline: PUBLIC PROC [traj: Traj, lineEnds: Imager.StrokeEnd _ square, fillColor: Imager.Color _ Imager.black] RETURNS [outline: Outline] = { boundBox: BoundBox _ GGBoundBox.CopyBoundBox[traj.boundBox]; outline _ NEW[OutlineObj _ [ class: FetchOutlineClass[], fillColor: fillColor, lineEnds: lineEnds, children: LIST[traj], parent: NIL, boundBox: boundBox]]; traj.parent _ outline; IF traj.role = hole THEN traj.role _ fence; }; OutlineBoundBox: PROC [slice: Outline, parts: SliceParts] RETURNS [box: BoundBox] = { IF parts = NIL THEN box _ slice.boundBox ELSE { outlineParts: OutlineParts _ NARROW[parts]; boxList: LIST OF BoundBox _ NIL; thisBox: BoundBox; FOR list: LIST OF Sequence _ outlineParts.seqs, list.rest UNTIL list = NIL DO IF list.first # NIL THEN { thisBox _ GGSequence.ComputeBoundBox[list.first]; boxList _ CONS[thisBox, boxList]; }; ENDLOOP; box _ GGBoundBox.BoundBoxOfBoxes[boxList]; }; }; OutlineTightBox: PROC [slice: Outline, parts: SliceParts] RETURNS [box: BoundBox] = { outlineParts: OutlineParts; boxList: LIST OF BoundBox _ NIL; thisBox: BoundBox; IF parts = NIL THEN parts _ slice.class.newParts[slice, NIL, slice]; outlineParts _ NARROW[parts]; FOR list: LIST OF Sequence _ outlineParts.seqs, list.rest UNTIL list = NIL DO IF list.first # NIL THEN { thisBox _ GGSequence.ComputeTightBox[list.first]; boxList _ CONS[thisBox, boxList]; }; ENDLOOP; box _ GGBoundBox.BoundBoxOfBoxes[boxList]; }; UpdateOutlineBoundBox: PUBLIC PROC [slice: Outline] = { outlineBoxes: LIST OF BoundBox _ NIL; trajGen: TrajGenerator; IF slice=NIL THEN RETURN; trajGen _ TrajsInOutline[slice]; FOR traj: Traj _ GGObjects.NextTraj[trajGen], GGObjects.NextTraj[trajGen] UNTIL traj = NIL DO outlineBoxes _ CONS[traj.boundBox, outlineBoxes]; ENDLOOP; UpdateBoundBoxFromList[slice.boundBox, outlineBoxes]; }; UpdateBoundBoxFromList: PROC [bBox: BoundBox, list: LIST OF BoundBox] = { IF list = NIL THEN ERROR; bBox.loX _ list.first.loX; bBox.hiX _ list.first.hiX; bBox.loY _ list.first.loY; bBox.hiY _ list.first.hiY; FOR bBoxList: LIST OF BoundBox _ list.rest, bBoxList.rest UNTIL bBoxList = NIL DO bBox.loX _ MIN[bBox.loX, bBoxList.first.loX]; bBox.hiX _ MAX[bBox.hiX, bBoxList.first.hiX]; bBox.loY _ MIN[bBox.loY, bBoxList.first.loY]; bBox.hiY _ MAX[bBox.hiY, bBoxList.first.hiY]; ENDLOOP; }; OutlineCopy: PROC [slice: Outline] RETURNS [copy: Outline] = { holeGen: TrajGenerator; newTraj, fence: Traj; fence _ FenceOfOutline[slice]; newTraj _ GGTraj.CopyTraj[fence]; copy _ CreateOutline[newTraj, slice.lineEnds, slice.fillColor]; holeGen _ HolesOfOutline[slice]; FOR hole: Traj _ GGObjects.NextTraj[holeGen], GGObjects.NextTraj[holeGen] UNTIL hole = NIL DO newTraj _ GGTraj.CopyTraj[hole]; copy.children _ AppendTrajList[copy.children, LIST[newTraj]]; newTraj.parent _ copy; ENDLOOP; }; OutlineDrawParts: PROC [slice: Outline, parts: SliceParts, dc: Imager.Context, camera: CameraData, quick: BOOL] = { IF parts # NIL THEN ERROR Problem[msg: "outline parts are not yet implemented"]; DrawOutline[dc, slice]; }; DrawOutline: PROC [dc: Imager.Context, outline: Outline] = { BuildOutline: Imager.PathProc = { BuildPath: Imager.PathProc = { BuildTrajPath[traj, moveTo, lineTo, curveTo, conicTo, arcTo]; }; imagerHole: ImagerPath.Trajectory; cc: BOOL; traj _ fence; cc _ GGTraj.IsClockwiseTraj[traj]; BuildTrajPath[traj, moveTo, lineTo, curveTo, conicTo, arcTo]; FOR holeList: LIST OF Traj _ outline.children.rest, holeList.rest UNTIL holeList = NIL DO traj _ holeList.first; imagerHole _ ImagerPath.TrajectoryListFromPath[BuildPath].first; IF cc=GGTraj.IsClockwiseTraj[traj] THEN ImagerPath.MapTrajectoryBackward[imagerHole, moveTo, lineTo, curveTo, conicTo, arcTo] ELSE ImagerPath.MapTrajectory[imagerHole, moveTo, lineTo, curveTo, conicTo, arcTo]; ENDLOOP; }; fence, traj: Traj; fence _ outline.children.first; IF fence.role = fence AND outline.fillColor#NIL THEN { Imager.SetColor[dc, outline.fillColor]; Imager.MaskFill[dc, BuildOutline]; }; GGTraj.DrawTraj[dc, fence]; FOR holeList: LIST OF Traj _ outline.children.rest, holeList.rest UNTIL holeList = NIL DO GGTraj.DrawTraj[dc, holeList.first]; ENDLOOP; }; OutlineDrawTransform: PROC [slice: Outline, parts: SliceParts, dc: Imager.Context, camera: CameraData, transform: ImagerTransformation.Transformation] = { sliceParts: OutlineParts _ NARROW[parts]; BuildOutline: Imager.PathProc = { BuildPath: Imager.PathProc = { seg: Segment; firstPoint: Point _ GGTraj.FetchJointPos[traj, 0]; moveTo[ [firstPoint.x, firstPoint.y] ]; FOR i: INT IN [0..GGTraj.HiSegment[traj]] DO seg _ GGTraj.FetchSegment[traj, i]; seg.class.buildPath[seg, lineTo, curveTo, conicTo, arcTo]; ENDLOOP; }; BuildPathTransformSeq: Imager.PathProc = { seg: Segment; firstPoint: Point _ GGTraj.FetchJointPos[traj, 0]; IF thisSeq.segments[0] OR thisSeq.joints[0] THEN firstPoint _ ImagerTransformation.Transform[transform, firstPoint]; moveTo[ [firstPoint.x, firstPoint.y] ]; FOR i: INT IN [0..GGTraj.HiSegment[traj]] DO seg _ GGTraj.FetchSegment[traj, i]; seg.class.buildPathTransform[seg, transform, thisSeq.segments[i], thisSeq.joints[i], thisSeq.joints[(i+1) MOD thisSeq.traj.segCount], thisSeq.controlPoints[i], lineTo, curveTo, conicTo, arcTo]; ENDLOOP; }; imagerHole: ImagerPath.Trajectory; cc, holeCC: BOOL; traj _ fence; thisSeq _ FindSequenceInList[traj, sliceParts.seqs]; IF thisSeq # NIL THEN { BuildPathTransformSeq[moveTo, lineTo, curveTo, conicTo, arcTo]; cc _ GGTraj.IsClockwiseTrajTransformSeq[thisSeq, transform]; } ELSE { BuildPath[moveTo, lineTo, curveTo, conicTo, arcTo]; cc _ GGTraj.IsClockwiseTraj[traj]; }; FOR holeList: LIST OF Traj _ slice.children.rest, holeList.rest UNTIL holeList = NIL DO traj _ holeList.first; thisSeq _ FindSequenceInList[traj, sliceParts.seqs]; IF thisSeq # NIL THEN { imagerHole _ ImagerPath.TrajectoryListFromPath[BuildPathTransformSeq].first; holeCC _ GGTraj.IsClockwiseTrajTransformSeq[thisSeq, transform]; } ELSE { imagerHole _ ImagerPath.TrajectoryListFromPath[BuildPath].first; holeCC _ GGTraj.IsClockwiseTraj[traj]; }; IF cc=holeCC THEN ImagerPath.MapTrajectoryBackward[imagerHole, moveTo, lineTo, curveTo, conicTo, arcTo] ELSE ImagerPath.MapTrajectory[imagerHole, moveTo, lineTo, curveTo, conicTo, arcTo]; ENDLOOP; }; traj: Traj; thisSeq: Sequence; fence: Traj _ slice.children.first; IF fence.role = fence AND slice.fillColor#NIL THEN { transformedColor: Color; WITH slice.fillColor SELECT FROM cc: ImagerColor.ConstantColor => { Imager.SetColor[dc, cc]; }; sc: ImagerColor.SampledColor => { IF OutlineCompleteParts[slice, parts] THEN { transformedColor _ ImagerColor.MakeSampledColor[pa: sc.pa, um: ImagerTransformation.Concat[sc.um, transform], colorOperator: sc.colorOperator]; Imager.SetColor[dc, transformedColor]; }; }; ENDCASE => ERROR; Imager.MaskFill[dc, BuildOutline]; }; thisSeq _ FindSequenceInList[fence, sliceParts.seqs]; IF thisSeq # NIL THEN GGTraj.DrawTrajTransformSeq[dc, thisSeq, transform] ELSE GGTraj.DrawTraj[dc, fence]; FOR holeList: LIST OF Traj _ slice.children.rest, holeList.rest UNTIL holeList = NIL DO thisSeq _ FindSequenceInList[holeList.first, sliceParts.seqs]; IF thisSeq # NIL THEN GGTraj.DrawTrajTransformSeq[dc, thisSeq, transform] ELSE GGTraj.DrawTraj[dc, holeList.first]; ENDLOOP; }; FindSequenceInList: PROC [traj: Traj, seqList: LIST OF Sequence] RETURNS [seq: Sequence] = { FOR list: LIST OF Sequence _ seqList, list.rest UNTIL list = NIL DO IF list.first # NIL AND list.first.traj = traj THEN RETURN[list.first]; ENDLOOP; RETURN[NIL]; }; BuildTrajPath: PROC [traj: Traj, moveTo: ImagerPath.MoveToProc, lineTo: ImagerPath.LineToProc, curveTo: ImagerPath.CurveToProc, conicTo: ImagerPath.ConicToProc, arcTo: ImagerPath.ArcToProc] = { DoBuildTrajPath: Imager.PathProc = { seg: Segment; firstPoint: Point _ GGTraj.FetchJointPos[traj, 0]; moveTo[ [firstPoint.x, firstPoint.y] ]; FOR i: INT IN [0..GGTraj.HiSegment[traj]] DO seg _ GGTraj.FetchSegment[traj, i]; seg.class.buildPath[seg, lineTo, curveTo, conicTo, arcTo]; ENDLOOP; }; DoBuildTrajPath[moveTo, lineTo, curveTo, conicTo, arcTo]; }; OutlineDrawSelectionFeedback: PROC [slice: Outline, selectedParts: SliceParts, hotParts: SliceParts, dc: Imager.Context, camera: CameraData, dragInProgress, caretIsMoving, hideHot, quick: BOOL] = { normalOutlineParts, hotOutlineParts: OutlineParts; normalList, hotList: LIST OF Sequence; normalOutlineParts _ NARROW[selectedParts]; hotOutlineParts _ NARROW[hotParts]; IF caretIsMoving OR dragInProgress THEN RETURN; IF selectedParts = NIL AND hotParts = NIL THEN RETURN; IF selectedParts # NIL AND hotParts # NIL THEN { hotList _ hotOutlineParts.seqs; FOR normalList _ normalOutlineParts.seqs, normalList.rest UNTIL normalList = NIL DO IF normalList.first # NIL THEN GGTraj.DrawSelectionFeedback[normalList.first.traj, normalList.first, hotList.first, dc, camera, dragInProgress, caretIsMoving, hideHot, quick] ELSE IF hotList.first # NIL THEN GGTraj.DrawSelectionFeedback[hotList.first.traj, normalList.first, hotList.first, dc, camera, dragInProgress, caretIsMoving, hideHot, quick]; hotList _ hotList.rest; ENDLOOP; } ELSE IF selectedParts # NIL THEN { FOR normalList _ normalOutlineParts.seqs, normalList.rest UNTIL normalList = NIL DO IF normalList.first # NIL THEN GGTraj.DrawSelectionFeedback[normalList.first.traj, normalList.first, NIL, dc, camera, dragInProgress, caretIsMoving, hideHot, quick]; ENDLOOP; } ELSE { FOR hotList _ hotOutlineParts.seqs, hotList.rest UNTIL hotList = NIL DO IF hotList.first # NIL THEN GGTraj.DrawSelectionFeedback[hotList.first.traj, NIL, hotList.first, dc, camera, dragInProgress, caretIsMoving, hideHot, quick]; ENDLOOP; }; }; DrawCpsAndJoints: PROC [dc: Imager.Context, seg: Segment] = { point: Point; GGShapes.DrawJoint[dc, seg.lo]; GGShapes.DrawJoint[dc, seg.hi]; FOR j:INT IN [0..seg.class.controlPointCount[seg]) DO point _ seg.class.controlPointGet[seg, j]; GGShapes.DrawCP[dc, point]; ENDLOOP; }; OutlineDrawAttractorFeedback: PUBLIC PROC [sliceD: OutlineDescriptor, selectedParts: SliceParts, dragInProgress: BOOL, dc: Imager.Context, camera: CameraData] = { success: BOOL; partType: TrajPartType; traj: Traj; seg: Segment; jointNum: NAT; [success, partType, traj, ----, jointNum, ----, ----, seg] _ UnpackSimpleDescriptorOld[sliceD]; SELECT partType FROM joint => { IF jointNum > 0 THEN DrawCpsAndJoints[dc, GGTraj.FetchSegment[traj, jointNum-1]]; IF jointNum < traj.segCount THEN DrawCpsAndJoints[dc, GGTraj.FetchSegment[traj, jointNum]]; }; segment, controlPoint => DrawCpsAndJoints[dc, seg]; ENDCASE; -- none }; OutlineTransform: PUBLIC PROC [slice: Outline, parts: SliceParts, transform: ImagerTransformation.Transformation] = { outlineParts: OutlineParts _ NARROW[parts]; transformedColor: Color; FOR list: LIST OF Sequence _ outlineParts.seqs, list.rest UNTIL list = NIL DO IF list.first # NIL THEN GGTraj.TransformSequence[list.first, transform]; ENDLOOP; UpdateOutlineBoundBox[slice]; IF slice.fillColor # NIL THEN { WITH slice.fillColor SELECT FROM cc: ImagerColor.ConstantColor => {}; sc: ImagerColor.SampledColor => { IF OutlineCompleteParts[slice, parts] THEN { transformedColor _ ImagerColor.MakeSampledColor[pa: sc.pa, um: ImagerTransformation.Concat[sc.um, transform], colorOperator: sc.colorOperator]; slice.fillColor _ transformedColor; }; }; ENDCASE => ERROR; }; }; OneSequenceOnly: PROC [realParts: OutlineParts] RETURNS [theSeq: Sequence] = { theSeq _ NIL; FOR list: LIST OF Sequence _ realParts.seqs, list.rest UNTIL list = NIL DO IF list.first # NIL THEN { IF theSeq # NIL THEN RETURN[NIL] ELSE theSeq _ list.first; }; ENDLOOP; }; OutlineDescribe: PROC [slice: Outline, parts: SliceParts] RETURNS [rope: Rope.ROPE] = { realParts: OutlineParts _ NARROW[parts]; theSeq: Sequence; IF (theSeq _ OneSequenceOnly[realParts]) # NIL THEN { rope _ DescribeSequence[theSeq]; } ELSE { FOR list: LIST OF Sequence _ realParts.seqs, list.rest UNTIL list = NIL DO IF list.first # NIL THEN RETURN["several parts of a multi-trajectory outline"]; ENDLOOP; rope _ "No outline parts to speak of."; }; }; DescribeSequence: PROC [seq: Sequence] RETURNS [rope: Rope.ROPE] = { segNum, cpNum: NAT; segCount: NAT _ seq.segCount; jointCount: NAT _ seq.jointCount; cpCount: NAT _ seq.controlPointCount; SELECT TRUE FROM segCount=1 => { -- single segment selected. Describe it segGen: SegmentGenerator _ GGSequence.SegmentsInSequence[seq]; rope _ GGDescribe.DescribeSegment[seq.traj, GGSequence.NextSegmentAndIndex[segGen].index]; }; segCount=0 AND cpCount=1 => { -- single CP selected. Describe it cpGen: ControlPointGenerator _ GGSequence.ControlPointsInSequence[seq]; [segNum, cpNum] _ GGSequence.NextSegNumAndCPNum[cpGen]; rope _ GGDescribe.DescribeControlPoint[seq.traj, segNum, cpNum]; }; segCount=0 AND cpCount=0 AND jointCount=1 => { -- single joint selected. Describe it jointGen: JointGenerator _ GGSequence.JointsInSequence[seq]; rope _ GGDescribe.DescribeJoint[seq.traj, GGSequence.NextJoint[jointGen]]; }; ENDCASE => rope _ GGDescribe.DescribeSequence[seq]; }; OutlineMovingParts: PROC [slice: Outline, parts: SliceParts] RETURNS [moving: SliceParts] = { outlineParts: OutlineParts _ NARROW[parts]; moveParts: OutlineParts; ptr: LIST OF Sequence; movingSeq: Sequence; moving _ moveParts _ NEW[OutlinePartsObj]; [moveParts.seqs, ptr] _ GGUtility.StartSequenceList[]; FOR list: LIST OF Sequence _ outlineParts.seqs, list.rest UNTIL list = NIL DO IF list.first = NIL THEN { [moveParts.seqs, ptr] _ GGUtility.AddSequence[NIL, moveParts.seqs, ptr] } ELSE { movingSeq _ MovingSequence[list.first]; [moveParts.seqs, ptr] _ GGUtility.AddSequence[movingSeq, moveParts.seqs, ptr]; }; ENDLOOP; }; OutlineFixedParts: PROC [slice: Outline, parts: SliceParts, selectedList: LIST OF REF ANY] RETURNS [fixed: SliceParts] = { realParts: OutlineParts; seq: Sequence; fixed _ realParts _ NARROW[parts]; FOR list: LIST OF Sequence _ realParts.seqs, list.rest UNTIL list = NIL DO seq _ list.first; IF seq # NIL THEN { GGSequence.TrimSelectedControlPointSegments[seq, selectedList]; GGSequence.TrimSelectedJointSegments[seq, selectedList]; GGSequence.TrimSelectedParts[seq, selectedList]; }; ENDLOOP; }; MovingSequence: PROC [seq: Sequence] RETURNS [movingSeq: Sequence] = { completeSeq: Sequence _ GGSequence.CreateComplete[seq.traj]; stationarySeq: Sequence _ GGSequence.Copy[completeSeq]; GGSequence.DDifference[stationarySeq, seq]; -- remove everything that is directly moving GGSequence.TrimControlPointSegments[stationarySeq, FALSE]; -- remove all segments with a directly moving control point. GGSequence.TrimJointSegments[stationarySeq, FALSE]; -- remove all segments with a directly moving joint movingSeq _ GGSequence.Difference[completeSeq, stationarySeq]; -- Difference calculates the moving parts }; OutlineEmptyParts: PROC [slice: Outline, parts: SliceParts] RETURNS [BOOL] = { realParts: OutlineParts _ NARROW[parts]; FOR list: LIST OF Sequence _ realParts.seqs, list.rest UNTIL list = NIL DO IF list.first # NIL AND NOT GGSequence.IsEmpty[list.first] THEN RETURN[FALSE]; ENDLOOP; RETURN[TRUE]; }; OutlineCompleteParts: PROC [slice: Outline, parts: SliceParts] RETURNS [BOOL] = { realParts: OutlineParts _ NARROW[parts]; FOR list: LIST OF Sequence _ realParts.seqs, list.rest UNTIL list = NIL DO IF list.first = NIL THEN RETURN[FALSE]; IF NOT GGSequence.IsComplete[list.first] THEN RETURN[FALSE]; ENDLOOP; RETURN[TRUE]; }; NearestJointToHitSpot: PUBLIC PROC [caretPt: Point, traj: Traj, hitJointNum: INT, hitSegNum: INT, hitType: HitType] RETURNS [jointNum: NAT] = { nextNum: NAT; p1, p2: Point; d1, d2: REAL; SELECT hitType FROM joint => { jointNum _ hitJointNum; }; segment, controlPoint => { nextNum _ GGTraj.FollowingJoint[traj, hitSegNum]; p1 _ GGTraj.FetchJointPos[traj, hitSegNum]; p2 _ GGTraj.FetchJointPos[traj, nextNum]; d1 _ GGVector.DistanceSquared[p1, caretPt]; d2 _ GGVector.DistanceSquared[p2, caretPt]; IF d1 <= d2 THEN jointNum _ hitSegNum ELSE jointNum _ nextNum; }; ENDCASE => ERROR Problem[msg: "not yet implemented"]; }; -- end of NearestJointToHitSpot OutlineNewParts: PROC [slice: Outline, hitData: REF ANY, mode: SelectMode] RETURNS [parts: SliceParts] = { realParts: OutlineParts; ptr: LIST OF Sequence; trajGen: GGModelTypes.TrajGenerator; parts _ realParts _ NEW[OutlinePartsObj]; [realParts.seqs, ptr] _ GGUtility.StartSequenceList[]; SELECT mode FROM none => { outlineHitData: OutlineHitData _ NARROW[hitData]; SELECT outlineHitData.hitType FROM joint, controlPoint => parts _ OutlineNewParts[slice, hitData, joint]; segment => parts _ OutlineNewParts[slice, hitData, segment]; ENDCASE => ERROR; }; slice => { completeSeq: Sequence; trajGen _ TrajsInOutline[slice]; FOR traj: Traj _ GGObjects.NextTraj[trajGen], GGObjects.NextTraj[trajGen] UNTIL traj = NIL DO completeSeq _ GGSequence.CreateComplete[traj]; [realParts.seqs, ptr] _ GGUtility.AddSequence[completeSeq, realParts.seqs, ptr]; ENDLOOP; }; joint => { outlineHitData: OutlineHitData _ NARROW[hitData]; SELECT outlineHitData.hitType FROM joint => { -- hit a joint traj: Traj _ outlineHitData.traj; jointNum: NAT _ outlineHitData.jointNum; seq: Sequence _ GGSequence.CreateJointToJoint[traj, jointNum, jointNum]; parts _ PartsFromSequence[slice, seq]; }; segment => { -- we are in the middle of a segment. Select nearest joint or cp success: BOOL; segNum: NAT; cpNum, jointNum: NAT; jointPoint, cpPoint, caretPt: Point; traj: Traj; seg: Segment; seq: Sequence; traj _ outlineHitData.traj; segNum _ outlineHitData.segNum; seg _ GGTraj.FetchSegment[traj, segNum]; caretPt _ outlineHitData.hitPoint; jointNum _ NearestJointToHitSpot[caretPt, traj, -1, segNum, segment]; jointPoint _ GGTraj.FetchJointPos[traj, jointNum]; [cpPoint, cpNum, success] _ seg.class.closestControlPoint[seg, caretPt, GGUtility.plusInfinity]; IF NOT success THEN seq _ GGSequence.CreateJointToJoint[traj, jointNum, jointNum] ELSE { cpDist: REAL _ GGVector.DistanceSquared[cpPoint, caretPt]; jointDist: REAL _ GGVector.DistanceSquared[jointPoint, caretPt]; seq _ IF cpDist < jointDist THEN GGSequence.CreateFromControlPoint[traj, segNum, cpNum] ELSE GGSequence.CreateJointToJoint[traj, jointNum, jointNum]; }; parts _ PartsFromSequence[slice, seq]; }; controlPoint => { traj: Traj; segNum, cpNum: NAT; controlPointSeq: Sequence; traj _ outlineHitData.traj; segNum _ outlineHitData.segNum; cpNum _ outlineHitData.cpNum; controlPointSeq _ GGSequence.CreateFromControlPoint[traj, segNum, cpNum]; parts _ PartsFromSequence[slice, controlPointSeq]; }; ENDCASE => ERROR; }; segment => { outlineHitData: OutlineHitData _ NARROW[hitData]; SELECT outlineHitData.hitType FROM joint => { parts _ CreateEmptyParts[slice]; }; segment, controlPoint => { seq: Sequence; seq _ GGSequence.CreateFromSegment[outlineHitData.traj, outlineHitData.segNum]; parts _ PartsFromSequence[slice, seq]; }; ENDCASE => ERROR; }; traj => { outlineHitData: OutlineHitData _ NARROW[hitData]; traj: Traj _ outlineHitData.traj; seq: Sequence _ GGSequence.CreateComplete[traj]; parts _ PartsFromSequence[slice, seq]; }; topLevel => { parts _ slice.class.newParts[slice, NIL, slice]; }; ENDCASE => ERROR; }; CreateEmptyParts: PROC [slice: Outline] RETURNS [empty: SliceParts] = { realParts: OutlineParts; empty _ realParts _ NEW[OutlinePartsObj]; realParts.seqs _ NIL; FOR list: LIST OF Traj _ slice.children, list.rest UNTIL list = NIL DO realParts.seqs _ CONS[NIL, realParts.seqs]; ENDLOOP; }; OutlineUnionParts: PROC [slice: Outline, partsA: SliceParts, partsB: SliceParts] RETURNS [aPlusB: SliceParts] = { realPartsA: OutlineParts _ NARROW[partsA]; realPartsB: OutlineParts _ NARROW[partsB]; union: OutlineParts; listA: LIST OF Sequence _ realPartsA.seqs; listB: LIST OF Sequence _ realPartsB.seqs; ptr: LIST OF Sequence; aPlusB _ union _ NEW[OutlinePartsObj]; [union.seqs, ptr] _ GGUtility.StartSequenceList[]; UNTIL listA = NIL DO IF listA.first = NIL THEN [union.seqs, ptr] _ GGUtility.AddSequence[listB.first, union.seqs, ptr] ELSE IF listB.first = NIL THEN [union.seqs, ptr] _ GGUtility.AddSequence[listA.first, union.seqs, ptr] ELSE { newSeq: Sequence _ GGSequence.Union[listA.first, listB.first]; [union.seqs, ptr] _ GGUtility.AddSequence[newSeq, union.seqs, ptr]; }; listA _ listA.rest; listB _ listB.rest; ENDLOOP; }; OutlineDifferenceParts: PROC [slice: Outline, partsA: SliceParts, partsB: SliceParts] RETURNS [aMinusB: SliceParts] = { realPartsA: OutlineParts _ NARROW[partsA]; realPartsB: OutlineParts _ NARROW[partsB]; diff: OutlineParts; listA: LIST OF Sequence _ realPartsA.seqs; listB: LIST OF Sequence _ realPartsB.seqs; ptr: LIST OF Sequence; aMinusB _ diff _ NEW[OutlinePartsObj]; [diff.seqs, ptr] _ GGUtility.StartSequenceList[]; UNTIL listA = NIL DO IF listA.first = NIL THEN [diff.seqs, ptr] _ GGUtility.AddSequence[NIL, diff.seqs, ptr] ELSE IF listB.first = NIL THEN [diff.seqs, ptr] _ GGUtility.AddSequence[listA.first, diff.seqs, ptr] ELSE { newSeq: Sequence _ GGSequence.Difference[listA.first, listB.first]; [diff.seqs, ptr] _ GGUtility.AddSequence[newSeq, diff.seqs, ptr]; }; listA _ listA.rest; listB _ listB.rest; ENDLOOP; }; OutlineAugmentParts: PROC [slice: Outline, parts: SliceParts, selectClass: SelectionClass] RETURNS [more: SliceParts] = { sliceParts: OutlineParts _ NARROW[parts]; FOR list: LIST OF Sequence _ sliceParts.seqs, list.rest UNTIL list = NIL DO IF list.first # NIL THEN SequenceAugmentParts[list.first, selectClass]; ENDLOOP; more _ parts; }; SequenceAugmentParts: PROC [seq: Sequence, selectClass: SelectionClass] = { IF selectClass = normal THEN { GGSequence.FillInJoints[seq]; GGSequence.FillInControlPoints[seq]; } ELSE { GGSequence.FillInControlPoints[seq]; }; }; OutlineSetSelectedFields: PROC [sliceD: OutlineDescriptor, selected: BOOL, selectClass: SelectionClass] = { sliceParts: OutlineParts _ NARROW[sliceD.parts]; FOR list: LIST OF Sequence _ sliceParts.seqs, list.rest UNTIL list = NIL DO IF list.first # NIL THEN SequenceSetSelectedFields[list.first, selected, selectClass]; ENDLOOP; }; SequenceSetSelectedFields: PROC [seq: Sequence, selected: BOOL, selectClass: SelectionClass] = { joint: Joint; jointGen: JointGenerator; segGen: SegmentGenerator; SetTrajPartField[seq.traj, selected, selectClass]; jointGen _ GGSequence.JointsInSequence[seq]; FOR jointNum: INT _ GGSequence.NextJoint[jointGen], GGSequence.NextJoint[jointGen] UNTIL jointNum = -1 DO joint _ NARROW[Rosary.Fetch[seq.traj.joints, jointNum]]; SetJointField[joint, selected, selectClass]; ENDLOOP; segGen _ GGSequence.SegmentsInSequence[seq]; FOR seg: Segment _ GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen] UNTIL seg = NIL DO SetSegmentField[seg, selected, selectClass]; ENDLOOP; }; SetTrajPartField: PROC [traj: Traj, selected: BOOL, selectClass: SelectionClass] = { SELECT selectClass FROM normal => traj.selectedInPart.normal _ selected; hot => traj.selectedInPart.hot _ selected; active => traj.selectedInPart.active _ selected; ENDCASE; }; SetJointField: PROC [joint: Joint, selected: BOOL, selectClass: SelectionClass] = { SELECT selectClass FROM normal => joint.TselectedInFull.normal _ selected; hot => joint.TselectedInFull.hot _ selected; active => joint.TselectedInFull.active _ selected; ENDCASE; }; SetSegmentField: PROC [seg: Segment, selected: BOOL, selectClass: SelectionClass] = { SELECT selectClass FROM normal => seg.TselectedInFull.normal _ selected; hot => seg.TselectedInFull.hot _ selected; active => seg.TselectedInFull.active _ selected; ENDCASE; }; SetControlPointField: PROC [seg: Segment, cpNum: NAT, selected: BOOL, selectClass: SelectionClass] = { seg.class.controlPointFieldSet[seg, cpNum, selected, selectClass]; }; GetControlPointField: PROC [seg: Segment, cpNum: NAT, selectClass: SelectionClass] RETURNS [selected: BOOL] = { selected _ seg.class.controlPointFieldGet[seg, cpNum, selectClass]; }; GetJointField: PROC [joint: Joint, selectClass: SelectionClass] RETURNS [selected: BOOL] = { SELECT selectClass FROM normal => selected _ joint.TselectedInFull.normal; hot => selected _ joint.TselectedInFull.hot; active => selected _ joint.TselectedInFull.active; ENDCASE; }; GetSegmentField: PROC [seg: Segment, selectClass: SelectionClass] RETURNS [selected: BOOL] = { SELECT selectClass FROM normal => selected _ seg.TselectedInFull.normal; hot => selected _ seg.TselectedInFull.hot; active => selected _ seg.TselectedInFull.active; ENDCASE; }; HitType: TYPE = GGModelTypes.TrajPartType; OutlineHitData: TYPE = REF OutlineHitDataObj; OutlineHitDataObj: TYPE = RECORD [ traj: Traj, hitType: HitType, segNum: INT, cpNum: INT, jointNum: INT, hitPoint: Point ]; PointGeneratorData: TYPE = REF PointGeneratorDataObj; PointGeneratorDataObj: TYPE = RECORD [ seqPtr: LIST OF Sequence, pointGen: GGModelTypes.PointGenerator -- the point generator for the first sequence of seqPtr ]; PointPairGeneratorData: TYPE = REF PointPairGeneratorDataObj; PointPairGeneratorDataObj: TYPE = RECORD [ seqPtr: LIST OF Sequence, pointGen: GGModelTypes.PointPairGenerator -- the pair generator for first sequence of seqPtr ]; OutlinePointsInDescriptor: PUBLIC PROC [sliceD: OutlineDescriptor] RETURNS [pointGen: GGModelTypes.OutlinePointGenerator] = { realParts: OutlineParts _ NARROW[sliceD.parts]; seqPtr: LIST OF Sequence _ realParts.seqs; trajPointGen: GGModelTypes.PointGenerator; pointGenData: PointGeneratorData; trajPointGen _ IF seqPtr.first = NIL THEN NIL ELSE GGTraj.PointsInDescriptor[seqPtr.first]; pointGenData _ NEW[PointGeneratorDataObj _ [seqPtr: seqPtr, pointGen: trajPointGen]]; pointGen _ NEW[GGModelTypes.OutlinePointGeneratorObj _ [sliceD, 0, 0, pointGenData]]; }; OutlinePointPairsInDescriptor: PUBLIC PROC [sliceD: OutlineDescriptor] RETURNS [pointPairGen: GGModelTypes.OutlinePointPairGenerator] = { realParts: OutlineParts _ NARROW[sliceD.parts]; seqPtr: LIST OF Sequence _ realParts.seqs; trajPointGen: GGModelTypes.PointPairGenerator; pointGenData: PointPairGeneratorData; trajPointGen _ IF seqPtr.first = NIL THEN NIL ELSE GGTraj.PointPairsInDescriptor[seqPtr.first]; pointGenData _ NEW[PointPairGeneratorDataObj _ [seqPtr: seqPtr, pointGen: trajPointGen]]; pointPairGen _ NEW[GGModelTypes.OutlinePointPairGeneratorObj _ [sliceD, 0, 0, pointGenData]]; }; OutlineNextPoint: PUBLIC PROC [pointGen: GGModelTypes.OutlinePointGenerator] RETURNS [pointAndDone: GGModelTypes.PointAndDone] = { pgd: PointGeneratorData _ NARROW[pointGen.classSpecific]; seqPtr: LIST OF Sequence _ pgd.seqPtr; IF seqPtr = NIL THEN RETURN[[[0,0], TRUE]]; WHILE TRUE DO IF seqPtr.first # NIL THEN { pointAndDone _ GGTraj.NextPoint[pgd.pointGen]; IF NOT pointAndDone.done THEN { pgd.seqPtr _ seqPtr; RETURN; }; seqPtr _ seqPtr.rest; -- on to the next sequence }; UNTIL seqPtr = NIL OR seqPtr.first # NIL DO seqPtr _ seqPtr.rest ENDLOOP; IF seqPtr = NIL THEN RETURN[[[0,0], TRUE]]; pgd.pointGen _ GGTraj.PointsInDescriptor[seqPtr.first]; ENDLOOP; }; OutlineNextPointPair: PUBLIC PROC [pointPairGen: GGModelTypes.OutlinePointPairGenerator] RETURNS [pointPairAndDone: GGModelTypes.PointPairAndDone] = { pgd: PointPairGeneratorData _ NARROW[pointPairGen.classSpecific]; seqPtr: LIST OF Sequence _ pgd.seqPtr; IF seqPtr = NIL THEN RETURN[[[0,0], [0,0], TRUE]]; WHILE TRUE DO IF seqPtr.first # NIL THEN { pointPairAndDone _ GGTraj.NextPointPair[pgd.pointGen]; IF NOT pointPairAndDone.done THEN { pgd.seqPtr _ seqPtr; RETURN; }; seqPtr _ seqPtr.rest; -- on to the next sequence }; UNTIL seqPtr = NIL OR seqPtr.first # NIL DO seqPtr _ seqPtr.rest ENDLOOP; IF seqPtr = NIL THEN RETURN[[[0,0], [0,0], TRUE]]; pgd.pointGen _ GGTraj.PointPairsInDescriptor[seqPtr.first]; ENDLOOP; }; GoodPointType: TYPE = {joint, intersectionPoint, midpoint, controlPoint, slice, none}; OutlineClosestPoint: PROC [sliceD: OutlineDescriptor, testPoint: Point, tolerance: REAL] RETURNS [bestPoint: Point, bestDist: REAL, hitData: REF ANY, success: BOOL] = { parts: OutlineParts _ NARROW[sliceD.parts]; thisDist: REAL; thisSegNum, bestSegNum, thisCP, bestCP, thisJointNum, bestJointNum: NAT; thisPoint: Point; thisSuccess: BOOL; bestTraj: Traj; bestType: GoodPointType _ none; outlineHitData: OutlineHitData; success _ FALSE; bestDist _ GGUtility.plusInfinity; FOR list: LIST OF Sequence _ parts.seqs, list.rest UNTIL list = NIL DO [thisDist, thisSegNum, thisCP, thisPoint, thisSuccess] _ GGTraj.NearestControlPoint[testPoint, list.first, tolerance]; IF thisSuccess AND thisDist < bestDist THEN { bestType _ controlPoint; bestPoint _ thisPoint; bestDist _ thisDist; bestTraj _ list.first.traj; bestSegNum _ thisSegNum; bestCP _ thisCP; success _ TRUE; }; ENDLOOP; FOR list: LIST OF Sequence _ parts.seqs, list.rest UNTIL list = NIL DO [thisDist, thisJointNum, thisPoint, thisSuccess] _ GGTraj.NearestJoint[testPoint, list.first, tolerance]; IF thisSuccess AND thisDist < bestDist THEN { bestType _ joint; bestPoint _ thisPoint; bestDist _ thisDist; bestTraj _ list.first.traj; bestJointNum _ thisJointNum; success _ TRUE; }; ENDLOOP; IF success THEN { SELECT bestType FROM controlPoint => { hitData _ outlineHitData _ NEW[OutlineHitDataObj _ [ bestTraj, controlPoint, bestSegNum, bestCP, -1, bestPoint]]; }; joint => { hitData _ outlineHitData _ NEW[OutlineHitDataObj _ [ bestTraj, joint, -1, -1, bestJointNum, bestPoint]]; }; ENDCASE => ERROR; }; }; OutlineClosestSegment: PUBLIC PROC [sliceD: OutlineDescriptor, testPoint: Point, tolerance: REAL] RETURNS [bestPoint: Point, bestDist: REAL, hitData: REF ANY, success: BOOL] = { parts: OutlineParts _ NARROW[sliceD.parts]; thisDist: REAL; thisSegNum, bestSegNum: NAT; thisPoint: Point; thisSuccess: BOOL; bestTraj: Traj; outlineHitData: OutlineHitData; success _ FALSE; bestDist _ GGUtility.plusInfinity; FOR list: LIST OF Sequence _ parts.seqs, list.rest UNTIL list = NIL DO [thisDist, thisSegNum, thisPoint, thisSuccess] _ GGTraj.NearestSegment[testPoint, list.first, tolerance]; IF thisSuccess AND thisDist < bestDist THEN { bestPoint _ thisPoint; bestDist _ thisDist; bestTraj _ list.first.traj; bestSegNum _ thisSegNum; success _ TRUE; }; ENDLOOP; IF success THEN { hitData _ outlineHitData _ NEW[OutlineHitDataObj _ [ bestTraj, segment, bestSegNum, -1, -1, bestPoint]]; }; }; OutlineLineIntersection: PUBLIC PROC [sliceD: OutlineDescriptor, line: Line] RETURNS [points: LIST OF Point, pointCount: NAT] = { segGen: GGModelTypes.SegmentGenerator; thesePoints: LIST OF Point; thisCount: NAT; seq: Sequence; parts: OutlineParts _ NARROW[sliceD.parts]; points _ NIL; pointCount _ 0; FOR list: LIST OF Sequence _ parts.seqs, list.rest UNTIL list = NIL DO seq _ list.first; IF seq = NIL THEN LOOP; segGen _ GGSequence.SegmentsInSequence[seq]; FOR seg: Segment _ GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen] 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; }; OutlineCircleIntersection: PROC [sliceD: OutlineDescriptor, circle: Circle] RETURNS [points: LIST OF Point, pointCount: NAT] = { segGen: GGModelTypes.SegmentGenerator; thesePoints: LIST OF Point; thisCount: NAT; seq: Sequence; parts: OutlineParts _ NARROW[sliceD.parts]; points _ NIL; pointCount _ 0; FOR list: LIST OF Sequence _ parts.seqs, list.rest UNTIL list = NIL DO seq _ list.first; IF seq = NIL THEN LOOP; segGen _ GGSequence.SegmentsInSequence[seq]; FOR seg: Segment _ GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen] 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; ENDLOOP; pointCount _ pointCount + thisCount; ENDLOOP; }; OutlineHitDataAsSimpleCurve: PROC [slice: Outline, hitData: REF ANY] RETURNS [simpleCurve: REF ANY] = { outlineHitData: OutlineHitData _ NARROW[hitData]; traj: Traj _ outlineHitData.traj; SELECT outlineHitData.hitType FROM joint, controlPoint => RETURN[NIL]; segment => { segNum: INT _ outlineHitData.segNum; seg: Segment _ GGTraj.FetchSegment[traj, segNum]; simpleCurve _ seg.class.asSimpleCurve[seg, outlineHitData.hitPoint]; }; ENDCASE => ERROR; }; OutlineSetStrokeWidth: PROC [slice: Outline, parts: SliceParts, strokeWidth: REAL] = { realParts: OutlineParts _ NARROW[parts]; FOR list: LIST OF Sequence _ realParts.seqs, list.rest UNTIL list = NIL DO IF list.first # NIL THEN GGSequence.SetStrokeWidth[list.first, strokeWidth]; ENDLOOP; }; OutlineGetStrokeWidth: PROC [slice: Outline, parts: SliceParts] RETURNS [strokeWidth: REAL] = { realParts: OutlineParts _ NARROW[parts]; FOR list: LIST OF Sequence _ realParts.seqs, list.rest UNTIL list = NIL DO IF list.first # NIL THEN RETURN[GGSequence.GetStrokeWidth[list.first]]; ENDLOOP; ERROR Problem[msg: "No segments to get a stroke width from"]; }; OutlineSetStrokeColor: PROC [slice: Outline, parts: SliceParts, color: Imager.Color] = { realParts: OutlineParts _ NARROW[parts]; FOR list: LIST OF Sequence _ realParts.seqs, list.rest UNTIL list = NIL DO IF list.first # NIL THEN SequenceSetStrokeColor[list.first, color]; ENDLOOP; }; SequenceSetStrokeColor: PROC [seq: Sequence, color: Imager.Color] = { segGen: SegmentGenerator; segGen _ GGSequence.SegmentsInSequence[seq]; FOR seg: Segment _ GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen] UNTIL seg = NIL DO seg.color _ color; ENDLOOP; }; OutlineGetStrokeColor: PROC [slice: Outline, parts: SliceParts] RETURNS [color: Imager.Color] = { realParts: OutlineParts _ NARROW[parts]; FOR list: LIST OF Sequence _ realParts.seqs, list.rest UNTIL list = NIL DO IF list.first # NIL THEN RETURN[SequenceGetStrokeColor[list.first]]; ENDLOOP; ERROR Problem[msg: "No segments to get a stroke width from"]; }; SequenceGetStrokeColor: PROC [seq: Sequence] RETURNS [color: Imager.Color] = { segGen: SegmentGenerator _ GGSequence.SegmentsInSequence[seq]; seg: Segment _ GGSequence.NextSegment[segGen]; IF seg#NIL THEN color _ seg.color; }; OutlineSetFillColor: PROC [slice: Outline, color: Imager.Color] = { slice.fillColor _ color }; OutlineGetFillColor: PROC [slice: Outline] RETURNS [color: Imager.Color] = { color _ slice.fillColor; }; UnpackOnePointDescriptorOld: PUBLIC PROC [outlineD: OutlineDescriptor] RETURNS [traj: Traj, isACP: BOOL, segNum, cpNum, jointNum: NAT] = { parts: OutlineParts; theSeq: Sequence; IF outlineD = NIL THEN RETURN[NIL, FALSE, 999, 999, 999]; parts _ NARROW[outlineD.parts]; traj _ NIL; FOR list: LIST OF Sequence _ parts.seqs, list.rest UNTIL list = NIL DO IF list.first # NIL THEN { IF traj # NIL THEN RETURN[NIL, FALSE, 999, 999, 999] ELSE { theSeq _ list.first; traj _ theSeq.traj; [isACP, segNum, cpNum, jointNum] _ GGSequence.UnpackOnePointSequence[theSeq]; }; }; ENDLOOP; }; UnpackSimpleDescriptorOld: PUBLIC PROC [outlineD: OutlineDescriptor] RETURNS [success: BOOL, partType: TrajPartType, traj: Traj, joint: Joint _ NIL, jointNum: NAT _ 999, cp: Point, cpNum: NAT _ 999, seg: Segment _ NIL, segNum: NAT _ 999] = { parts: OutlineParts; theSeq: Sequence; IF outlineD = NIL THEN RETURN[FALSE, joint, NIL, NIL, 999, [0,0]]; parts _ NARROW[outlineD.parts]; traj _ NIL; FOR list: LIST OF Sequence _ parts.seqs, list.rest UNTIL list = NIL DO IF list.first # NIL THEN { IF traj # NIL THEN RETURN[FALSE, joint, NIL, NIL, 999, [0,0]] ELSE { theSeq _ list.first; traj _ theSeq.traj; [success, partType, traj, joint, jointNum, cp, cpNum, seg, segNum] _ GGSequence.UnpackSimpleSequence[theSeq]; }; }; ENDLOOP; }; UnpackSimpleDescriptor: PUBLIC PROC [sliceD: SliceDescriptor] RETURNS [success: BOOL, partType: TrajPartType, traj: Traj, joint: Joint _ NIL, jointNum: NAT _ 999, cp: Point, cpNum: NAT _ 999, seg: Segment _ NIL, segNum: NAT _ 999] = { ERROR Problem[msg: "Outlines are not slices yet"]; }; UnpackOnePointDescriptor: PUBLIC PROC [sliceD: SliceDescriptor] RETURNS [traj: Traj, isACP: BOOL, segNum, cpNum, jointNum: NAT] = { ERROR Problem[msg: "Outlines are not slices yet"]; }; UnpackOneSegmentDescriptorOld: PUBLIC PROC [outlineD: OutlineDescriptor] RETURNS [traj: Traj, segNum: NAT] = { parts: OutlineParts; theSeq: Sequence; IF outlineD = NIL THEN RETURN[NIL, 999]; parts _ NARROW[outlineD.parts]; traj _ NIL; FOR list: LIST OF Sequence _ parts.seqs, list.rest UNTIL list = NIL DO IF list.first # NIL THEN { IF traj # NIL THEN RETURN[NIL, 999] ELSE { theSeq _ list.first; traj _ theSeq.traj; [segNum] _ GGSequence.UnpackOneSegmentSequence[theSeq]; }; }; ENDLOOP; }; UnpackOneSegmentDescriptor: PUBLIC PROC [sliceD: SliceDescriptor] RETURNS [traj: Traj, segNum: NAT] = { ERROR Problem[msg: "Outlines are not slices yet"]; }; FindTrajInDescriptor: PUBLIC PROC [outlineD: OutlineDescriptor, traj: Traj] RETURNS [seq: Sequence] = { outlineParts: OutlineParts; outlineParts _ NARROW[outlineD.parts]; FOR list: LIST OF Sequence _ outlineParts.seqs, list.rest UNTIL list = NIL DO IF list.first # NIL AND list.first.traj = traj THEN RETURN[list.first]; ENDLOOP; RETURN[NIL]; }; PartsFromSequence: PUBLIC PROC [outline: Outline, seq: Sequence] RETURNS [sliceParts: SliceParts] = { realParts: OutlineParts; ptr: LIST OF Sequence; trajGen: GGModelTypes.TrajGenerator; sliceParts _ realParts _ NEW[OutlinePartsObj]; [realParts.seqs, ptr] _ GGUtility.StartSequenceList[]; trajGen _ TrajsInOutline[outline]; FOR traj: Traj _ GGObjects.NextTraj[trajGen], GGObjects.NextTraj[trajGen] UNTIL traj = NIL DO IF traj = seq.traj THEN [realParts.seqs, ptr] _ GGUtility.AddSequence[seq, realParts.seqs, ptr] ELSE [realParts.seqs, ptr] _ GGUtility.AddSequence[NIL, realParts.seqs, ptr]; ENDLOOP; }; CopyParts: PUBLIC PROC [outline: Outline, parts: SliceParts] RETURNS [copy: SliceParts] = { realParts, realCopyParts: OutlineParts; copySeq: Sequence; ptr: LIST OF Sequence; realParts _ NARROW[parts]; copy _ realCopyParts _ NEW[OutlinePartsObj]; [realCopyParts.seqs, ptr] _ GGUtility.StartSequenceList[]; FOR list: LIST OF Sequence _ realParts.seqs, list.rest UNTIL list = NIL DO IF list.first = NIL THEN [realCopyParts.seqs, ptr] _ GGUtility.AddSequence[NIL, realCopyParts.seqs, ptr] ELSE { copySeq _ GGSequence.Copy[list.first]; [realCopyParts.seqs, ptr] _ GGUtility.AddSequence[copySeq, realCopyParts.seqs, ptr] }; ENDLOOP; }; AddHole: PUBLIC PROC [outline: Outline, hole: Traj] RETURNS [holier: Outline] = { holier _ outline.class.copy[outline]; holier.children _ AppendTrajList[holier.children, LIST[hole]]; hole.parent _ holier; hole.role _ hole; UpdateOutlineBoundBox[holier]; }; ReplaceFence: PUBLIC PROC [outline: Outline, newFence: Traj] RETURNS [newOutline: Outline] = { holeGen: TrajGenerator; newTraj: Traj; newOutline _ CreateOutline[newFence, outline.lineEnds, outline.fillColor]; holeGen _ HolesOfOutline[outline]; FOR hole: Traj _ GGObjects.NextTraj[holeGen], GGObjects.NextTraj[holeGen] UNTIL hole = NIL DO newTraj _ GGTraj.CopyTraj[hole]; newOutline.children _ AppendTrajList[newOutline.children, LIST[newTraj]]; newTraj.parent _ newOutline; ENDLOOP; UpdateOutlineBoundBox[newOutline]; }; ReplaceHole: PUBLIC PROC [outline: Outline, oldHole, newHole: Traj] RETURNS [newOutline: Outline] = { holeGen: TrajGenerator; newTraj, fence: Traj; fence _ FenceOfOutline[outline]; newTraj _ GGTraj.CopyTraj[fence]; newOutline _ CreateOutline[newTraj, outline.lineEnds, outline.fillColor]; holeGen _ HolesOfOutline[outline]; FOR hole: Traj _ GGObjects.NextTraj[holeGen], GGObjects.NextTraj[holeGen] UNTIL hole = NIL DO IF hole = oldHole THEN { newOutline.children _ AppendTrajList[newOutline.children, LIST[newHole]]; newHole.parent _ newOutline; } ELSE { newTraj _ GGTraj.CopyTraj[hole]; newOutline.children _ AppendTrajList[newOutline.children, LIST[newTraj]]; newTraj.parent _ newOutline; }; ENDLOOP; UpdateOutlineBoundBox[newOutline]; }; OutlineOfTraj: PUBLIC PROC [traj: Traj] RETURNS [outline: Outline] = { outline _ traj.parent; }; SaveSelectionsInOutline: PUBLIC PROC [outline: Outline, scene: Scene] = { outlineD: OutlineDescriptor; outlineParts: OutlineParts; outlineD _ GGSelect.FindSelectedOutline[outline, scene, hot]; ClearSelectionInOutline[outline, hot]; IF outlineD # NIL THEN { outlineParts _ NARROW[outlineD.parts]; FOR seqList: LIST OF Sequence _ outlineParts.seqs, seqList.rest UNTIL seqList = NIL DO IF seqList.first#NIL THEN SaveSelectionInSequence[seqList.first, hot]; ENDLOOP; }; outlineD _ GGSelect.FindSelectedOutline[outline, scene, normal]; ClearSelectionInOutline[outline, normal]; IF outlineD # NIL THEN { outlineParts _ NARROW[outlineD.parts]; FOR seqList: LIST OF Sequence _ outlineParts.seqs, seqList.rest UNTIL seqList = NIL DO IF seqList.first#NIL THEN SaveSelectionInSequence[seqList.first, normal]; ENDLOOP; }; outlineD _ GGSelect.FindSelectedOutline[outline, scene, active]; ClearSelectionInOutline[outline, active]; IF outlineD # NIL THEN { outlineParts _ NARROW[outlineD.parts]; FOR seqList: LIST OF Sequence _ outlineParts.seqs, seqList.rest UNTIL seqList = NIL DO IF seqList.first#NIL THEN SaveSelectionInSequence[seqList.first, active]; ENDLOOP; }; }; SaveSelectionInTraj: PUBLIC PROC [traj: Traj, selectClass: SelectionClass, scene: Scene] = { seq: Sequence _ GGSelect.FindSelectedSequence[traj, scene, selectClass]; IF seq=NIL THEN ClearSelectionInTraj[traj, selectClass] ELSE SaveSelectionInSequence[seq, selectClass]; }; ClearSelectionInTraj: PROC [traj: Traj, selectClass: SelectionClass] = { seg: Segment; joint: Joint; FOR i: NAT IN [0..GGTraj.HiSegment[traj]] DO seg _ GGTraj.FetchSegment[traj, i]; SetSegmentField[seg, FALSE, selectClass]; FOR j: NAT IN [0..seg.class.controlPointCount[seg]-1] DO SetControlPointField[seg, j, FALSE, selectClass]; ENDLOOP; ENDLOOP; FOR i: NAT IN [0..GGTraj.HiJoint[traj]] DO joint _ GGTraj.FetchJoint[traj, i]; SetJointField[joint, FALSE, selectClass]; ENDLOOP; }; RemakeSelectionFromTraj: PUBLIC PROC [traj: Traj, scene: Scene, selectClass: SelectionClass] = { seq: Sequence _ GGSequence.CreateEmpty[traj]; seg: Segment; joint: Joint; FOR i: NAT IN [0..GGTraj.HiSegment[traj]] DO seg _ GGTraj.FetchSegment[traj, i]; seq.segments[i] _ GetSegmentField[seg, selectClass]; IF seq.segments[i] THEN seq.segCount _ seq.segCount + 1; FOR j: NAT IN [0..seg.class.controlPointCount[seg]-1] DO seq.controlPoints[i][j] _ GetControlPointField[seg, j, selectClass]; IF seq.controlPoints[i][j] THEN seq.controlPointCount _ seq.controlPointCount + 1; ENDLOOP; ENDLOOP; FOR i: NAT IN [0..GGTraj.HiJoint[traj]] DO joint _ GGTraj.FetchJoint[traj, i]; seq.joints[i] _ GetJointField[joint, selectClass]; IF seq.joints[i] THEN seq.jointCount _ seq.jointCount + 1; ENDLOOP; GGSelect.SelectSequence[seq, scene, selectClass]; }; SaveSelectionInSequence: PROC [seq: Sequence, selectClass: SelectionClass] = { seg: Segment; joint: Joint; FOR i: NAT IN [0..GGTraj.HiSegment[seq.traj]] DO seg _ GGTraj.FetchSegment[seq.traj, i]; SetSegmentField[seg, seq.segments[i], selectClass]; FOR j: NAT IN [0..seg.class.controlPointCount[seg]-1] DO SetControlPointField[seg, j, seq.controlPoints[i][j], selectClass]; ENDLOOP; ENDLOOP; FOR i: NAT IN [0..GGTraj.HiJoint[seq.traj]] DO joint _ GGTraj.FetchJoint[seq.traj, i]; SetJointField[joint, seq.joints[i], selectClass]; ENDLOOP; }; ClearSelectionInOutline: PROC [outline: Outline, selectClass: SelectionClass] = { trajGen: GGModelTypes.TrajGenerator; trajGen _ TrajsInOutline[outline]; FOR traj: Traj _ GGObjects.NextTraj[trajGen], GGObjects.NextTraj[trajGen] UNTIL traj = NIL DO ClearSelectionInTraj[traj, selectClass]; ENDLOOP; }; RemakeSelectionsFromOutline: PUBLIC PROC [outline: Outline, scene: Scene] = { trajGen: GGModelTypes.TrajGenerator; trajGen _ TrajsInOutline[outline]; FOR traj: Traj _ GGObjects.NextTraj[trajGen], GGObjects.NextTraj[trajGen] UNTIL traj = NIL DO RemakeSelectionFromTraj[traj, scene, hot]; RemakeSelectionFromTraj[traj, scene, normal]; RemakeSelectionFromTraj[traj, scene, active]; ENDLOOP; }; SequencesOfOutline: PUBLIC PROC [outlineD: OutlineDescriptor] RETURNS [seqList: LIST OF Sequence] = { outlineParts: OutlineParts _ NARROW[outlineD.parts]; ptr: LIST OF Sequence; [seqList, ptr] _ GGUtility.StartSequenceList[]; FOR list: LIST OF Sequence _ outlineParts.seqs, list.rest UNTIL list = NIL DO IF list.first # NIL THEN [seqList, ptr] _ GGUtility.AddSequence[list.first, seqList, ptr]; ENDLOOP; }; RemoveTraj: PUBLIC PROC [outlineD: OutlineDescriptor, traj: Traj] = { outlineParts: OutlineParts _ NARROW[outlineD.parts]; seq: Sequence; FOR list: LIST OF Sequence _ outlineParts.seqs, list.rest UNTIL list = NIL DO IF list.first#NIL AND list.first.traj = traj THEN { seq _ list.first; GOTO Done; }; REPEAT Done => { outlineParts.seqs _ NARROW[GList.DSubst[NIL, seq, outlineParts.seqs]]; }; FINISHED => NULL; ENDLOOP; }; NearestJointToHitData: PUBLIC PROC [hitData: REF ANY] RETURNS [jointNum: NAT, traj: Traj] = { outlineHitData: OutlineHitData _ NARROW[hitData]; nextNum: NAT; p1, p2: Point; d1, d2: REAL; traj _ outlineHitData.traj; SELECT outlineHitData.hitType FROM joint => { jointNum _ outlineHitData.jointNum; }; segment, controlPoint => { nextNum _ GGTraj.FollowingJoint[traj, outlineHitData.segNum]; p1 _ GGTraj.FetchJointPos[traj, outlineHitData.segNum]; p2 _ GGTraj.FetchJointPos[traj, nextNum]; d1 _ GGVector.DistanceSquared[p1, outlineHitData.hitPoint]; d2 _ GGVector.DistanceSquared[p2, outlineHitData.hitPoint]; IF d1 <= d2 THEN jointNum _ outlineHitData.segNum ELSE jointNum _ nextNum; }; ENDCASE => ERROR; }; UnpackHitData: PUBLIC PROC [hitData: REF ANY] RETURNS [traj: Traj, hitType: TrajPartType, segNum, cpNum, jointNum: INT, hitPoint: Point] = { outlineHitData: OutlineHitData _ NARROW[hitData]; traj _ outlineHitData.traj; hitType _ outlineHitData.hitType; segNum _ outlineHitData.segNum; cpNum _ outlineHitData.cpNum; jointNum _ outlineHitData.jointNum; hitPoint _ outlineHitData.hitPoint; }; UpdateDescriptorBoundBoxes: PUBLIC PROC [outlineD: OutlineDescriptor] = { parts: OutlineParts _ NARROW[outlineD.parts]; FOR list: LIST OF Sequence _ parts.seqs, list.rest UNTIL list = NIL DO IF list.first # NIL THEN GGSequence.UpdateBoundBox[list.first]; ENDLOOP; }; HasHoles: PUBLIC PROC [outline: Outline] RETURNS [BOOL] = { RETURN[outline.children.rest # NIL]; }; FenceOfOutline: PUBLIC PROC [outline: Outline] RETURNS [fence: Traj] = { fence _ outline.children.first; }; HolesOfOutline: PUBLIC PROC [outline: Outline] RETURNS [trajGen: TrajGenerator] = { list: LIST OF Traj _ outline.children.rest; trajGen _ NEW[TrajGeneratorObj _ [ list: list ]]; }; TrajsInOutline: PUBLIC PROC [outline: Outline] RETURNS [trajGen: TrajGenerator] = { list: LIST OF Traj _ outline.children; trajGen _ NEW[TrajGeneratorObj _ [ list: list ]]; }; FetchOutlineClass: PROC [] RETURNS [class: OutlineClass] = { class _ globalOutlineClass; }; globalOutlineClass: OutlineClass; Init: PROC [] = { globalOutlineClass _ MakeOutlineClass[]; }; AppendTrajList: PROC [list1, list2: LIST OF Traj] RETURNS [result: LIST OF Traj] = { pos: LIST OF Traj; newCell: LIST OF Traj; IF list1 = NIL THEN RETURN[list2]; result _ CONS[list1.first, NIL]; pos _ result; FOR l: LIST OF Traj _ list1.rest, l.rest UNTIL l = NIL DO newCell _ CONS[l.first, NIL]; pos.rest _ newCell; pos _ newCell; ENDLOOP; pos.rest _ list2; }; Init[]; END. ÖGGOutlineImpl.mesa Copyright c 1986 by Xerox Corporation. All rights reserved. Last edited by Bier on June 2, 1986 5:10:01 pm PDT Contents: Procedures to implement the Outline Slice Class in Gargoyle. Outlines consist of Trajectories which in turn consist of Segments. Outline is the most important slice class. NoOp Class Routines Textual Description Style The Outline Class Drawing Transforming Textual Description Parts Hit Testing Style simple: BOOL _ FALSE, -- TRUE if seqs contains only a single control point or a single joint Fundamentals Computes it from traj boxes. Drawing GGModelTypes.OutlineDrawPartsProc Drawing Utilities Someday this will be a call to path.class.buildPath[traj, moveTo, lineTo, curveTo, conicTo, arcTo]; path.class.buildPath[traj, moveTo, lineTo, curveTo, conicTo, arcTo]; Will be path.class.drawBorder[fence, dc, gargoyleData]; Will be path.class.drawBorder[holeList.first, dc, gargoyleData]; PathProc: TYPE ~ PROC[moveTo: MoveToProc, lineTo: LineToProc, curveTo: CurveToProc, conicTo: ConicToProc, arcTo: ArcToProc]; Transforming Textual Description Parts Trim away the moving parts. exclude segments with moving cps exclude segments with moving joints exclude all directly moving parts Segments with selected control points are not stationary If selectClass = normal, then for each segment mentioned in parts, place it, its adjacent joints, and its control points in more. If selectClass # normal then just place each segment and its control points in more. Selected sequences must satisfy the requirement that if a segment is selected its joints and control points are as well. Set the selected fields of all of the joints and segments mentioned in sliceD. Joint Fields. Segment Fields. Hit Testing Finds the intersection of the line with those slice parts mentioned in sliceD. simpleCurve will be of type Edge, Arc, etc. There will be Conic and Bezier types as well. Style Sets the stroke width of the named parts of slice to be strokeWidth. Get the stroke width of the first segment you come to. Sets the stroke color of the named parts of slice to be color. Get the stroke color of the named parts of slice. Sets the fill color of the slice to be color. Get the fill color of the slice. Outline-Only operations. outlineD describes either a single control point or a single joint. Return the [segNum, cpNum] pair or the jointNum as appropriate. outlineD describes either a single control point or a single joint. Return the [segNum, cpNum] pair or the jointNum as appropriate. Holes can be removed using DeleteSequence. Should check for duplicates. -- Bier, February 20 Like CopyOutline except in one place. Like CopyOutline except in one place. Finds the unique outline to which traj belongs. hotness. normal selection. active selection. This outline has just been created by copying parts from an existing outline. The selection information was copied as well. Make sequences from the selection bits, and select these sequences. hotness. normal selection. active selection. Used by GGSelect to do a DeselectTraj. Replace the old sequence with a NIL. No trajectory has actually been deleted. It is just no longer mentioned in outlineD. This will go away when the Caret machinery is revised. Update the bound boxes of all contained sequences. Queries about Outlines. Returns the unique fence trajectory of outline. Generates all of the holes of outline. Generates the fence and all of the holes of outline. Utility Routines Non-destructive (copies the first list). Ê;“˜J˜Icodešœ™Kšœ Ïmœ1™K˜Kšœ˜Kšœ˜Kšœ!˜!Kšœ?˜?Kšœ ˜ šžœGžœžœž˜]Kšœ ˜ Kšœ.žœ ˜=Kšœ˜Kšžœ˜—K˜K˜—K™Kšœ™K˜šŸœžœTžœ˜sKšœ!™!Kšžœ žœžœžœ7˜PKšœ˜K˜K˜—K™K™K˜šŸ œžœ+˜<šŸ œ˜!šŸ œ˜K™KšœD™DKšœ=˜=K˜—Kšœ"˜"Kšœžœ˜ Kšœ ˜ Kšœ#˜#KšœD™DKšœ=˜=š žœ žœžœ-žœ žœž˜YKšœ˜Kšœ@˜@Kšžœ!žœV˜}KšžœO˜S—Kšžœ˜K˜—Kšœ˜Kšœ˜šžœžœžœžœ˜6Kšœ'˜'K˜"K˜—Kšœ˜Kšœ7™7š žœ žœžœ-žœ žœž˜YKšœ$˜$Kšœ@™@—Kšžœ˜K˜K˜—šŸœžœ€˜šKšœžœ˜)šÐbn œ˜!Kšœ žœžœh™}š¢ œ˜K˜ Kšœ2˜2Kšœ'˜'šžœžœžœž˜,Kšœ#˜#Kšœ:˜:—Kšžœ˜K˜—š¢œ˜*K˜ Kšœ2˜2KšžœžœžœD˜tKšœ'˜'šžœžœžœž˜,Kšœ#˜#KšœjžœT˜ÁKšžœ˜—K˜—K˜Kšœ"˜"Kšœ žœ˜Kšœ ˜ K˜Kšœ4˜4šžœ žœžœ˜Kšœ?˜?Kšœ=˜=K˜—šžœ˜Kšœ3˜3Kšœ#˜#K˜—š žœ žœžœ+žœ žœž˜WKšœ˜Kšœ4˜4šžœ žœžœ˜KšœL˜LKšœ@˜@K˜—šžœ˜Kšœ@˜@Kšœ&˜&K˜—Kšžœ žœV˜gKšžœO˜SKšžœ˜—K˜—K˜Kšœ ˜ K˜Kšœ#˜#šžœžœžœžœ˜4Kšœ˜šžœžœž˜ šœ"˜"Kšœ˜Kšœ˜—šœ!˜!šžœ$žœ˜,Kšœ˜Kšœ&˜&K˜—K˜—Kšžœžœ˜—K˜"K˜—Kšœ5˜5Kšžœ žœžœ5žœ˜jš žœ žœžœ+žœ žœž˜WKšœ>˜>Kšžœ žœžœ5žœ%˜sKšžœ˜—K˜K˜—š Ÿœžœžœžœ žœ˜\š žœžœžœžœžœž˜CKš žœžœžœžœžœ ˜GKšžœ˜—Kšžœžœ˜ K˜K˜—šŸ œžœ¯˜ÂšŸœ˜$K˜ Kšœ2˜2Kšœ'˜'šžœžœžœž˜,Kšœ#˜#Kšœ:˜:—Kšžœ˜K˜—Kšœ9˜9Kšœ˜—˜K˜—šŸœžœšžœ˜ÅK˜2Kšœžœžœ ˜&Kšœžœ˜+Kšœžœ ˜#Kšžœžœžœžœ˜/Kš žœžœžœ žœžœžœ˜6š žœžœžœ žœžœ˜0Kšœ˜šžœ7žœžœž˜SKšžœžœžœ˜®KšžœžœžœžœŽ˜®Kšœ˜Kšžœ˜—K˜—šžœžœžœžœ˜"šžœ7žœžœž˜SKšžœžœžœGžœ=˜¥Kšžœ˜—K˜—šžœ˜šžœ.žœ žœž˜GKšžœžœžœ2žœL˜œKšžœ˜—K˜—K˜K˜—K™šŸœžœ'˜=Kšœ ˜ Kšœ˜Kšœ˜šžœžœžœ'ž˜5Kšœ*˜*Kšœ˜Kšžœ˜—K˜K˜—š¢œžœžœHžœ-˜¢Jšœ žœ˜Jšœ˜Jšœ ˜ Jšœ ˜ Jšœ žœ˜Jšœ¡œA˜_šžœ ž˜šœ ˜ Jšžœžœ=˜QJšžœžœ;˜[J˜—Jšœ4˜4Jšžœ¡˜—K˜—K™Kšœ ™ K˜šŸœžœžœX˜uKšœžœ˜+K˜š žœžœžœ)žœžœž˜MKšžœžœžœ1˜IKšžœ˜—Kšœ˜šžœž ˜šžœžœž˜ Kšœ$˜$šœ!˜!šžœ$žœ˜,Kšœ˜Kšœ#˜#K˜—K˜—Kšžœžœ˜—K˜—K˜K˜—K™Kšœ™K˜šŸœžœžœ˜NKšœ žœ˜ š žœžœžœ&žœžœž˜Jšžœžœžœ˜Kš žœ žœžœžœžœ˜ Kšžœ˜K˜—Kšžœ˜—K˜K˜—šŸœžœ%žœ žœ˜WKšœžœ˜(Kšœ˜šžœ)ž ˜5Kšœ ˜ K˜—šžœ˜š žœžœžœ&žœžœž˜JJšžœžœžœžœ0˜OJšžœ˜—Jšœ'˜'J˜—K˜K˜—šŸœžœžœ žœ˜DKšœžœ˜Kšœ žœ˜Kšœ žœ˜!Kšœ žœ˜%šžœžœž˜šœ¡'˜7Kšœ>˜>šœ œ ˜+Kšœ.˜.—K˜—šœ žœ¡"˜@KšœG˜GKšœ7˜7Kšœ œ˜@K˜—šœ žœ žœ¡%˜TKšœ<˜˜Qšžœ˜Kšœžœ.˜:Kšœ žœ1˜@Kšœžœžœ7˜WKšžœ9˜=K˜—Kšœ&˜&K˜—˜Kšœ ˜ Kšœžœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜KšœI˜IKšœ2˜2K˜—Kšžœžœ˜—J˜—˜ Jšœ!žœ ˜1šžœž˜"šœ ˜ Kšœ ˜ K˜—šœ˜Kšœ˜KšœO˜OKšœ&˜&K˜—Kšžœžœ˜—J˜—˜ Jšœ!žœ ˜1Jšœ!˜!Jšœ0˜0Jšœ&˜&J˜—˜ Jšœ$žœ ˜0J˜—Jšžœžœ˜—K˜K˜—šŸœžœžœ˜GK˜Kšœžœ˜)Kšœžœ˜š žœžœžœ"žœžœž˜FKšœžœžœ˜+Kšžœ˜—Kšœ˜K˜—šŸœžœ:žœ˜qKšœžœ ˜*Kšœžœ ˜*Kšœ˜Kšœžœžœ˜*Kšœžœžœ˜*Kšœžœžœ ˜Kšœžœ˜&Kšœ2˜2šžœ žœž˜KšžœžœžœH˜aKšžœžœžœžœH˜fšžœ˜Kšœ>˜>KšœC˜CK˜—Kšœ˜K˜Kšžœ˜—K˜K˜—šŸœžœ:žœ˜wKšœžœ ˜*Kšœžœ ˜*Kšœ˜Kšœžœžœ˜*Kšœžœžœ˜*Kšœžœžœ ˜Kšœžœ˜&Kšœ1˜1šžœ žœž˜Kšžœžœžœ*žœ˜WKšžœžœžœžœF˜dšžœ˜KšœC˜CKšœA˜AK˜—Kšœ˜K˜Kšžœ˜—K˜K˜—šŸœžœBžœ˜yKšœ×™×Kšœžœ˜)š žœžœžœ'žœžœž˜KKšžœžœžœ/˜GKšžœ˜—Kšœ ˜ K˜K˜—šŸœžœ1˜KK™xšžœžœ˜Kšœ˜Kšœ$˜$K˜—šžœ˜Kšœ$˜$K˜—K˜K™—šŸœžœ'žœ"˜kKšœN™NKšœžœ˜0š žœžœžœ'žœžœž˜KKšžœžœžœ>˜VKšžœ˜—K˜K˜—šŸœžœžœ"˜`K˜ K˜K˜Kšœ2˜2šœ,˜,Kšœ ™ —šžœ žœBžœž˜iKšœžœ*˜8Kšœ,˜,Kšžœ˜Kšœ™—Kšœ,˜,šžœOžœžœž˜dKšœ,˜,Kšžœ˜—K˜K˜—šŸœžœžœ#˜Ušžœ ž˜Kšœ0˜0Kšœ*˜*Kšœ0˜0Kšžœ˜—K˜K˜—šŸ œžœžœ#˜Tšžœ ž˜Kšœ2˜2Kšœ,˜,Kšœ2˜2Kšžœ˜—K˜K˜K˜—šŸœžœžœ#˜Všžœ ž˜Kšœ0˜0Kšœ*˜*Kšœ0˜0Kšžœ˜—K˜K˜—šŸœžœžœ žœ#˜gKšœB˜BK˜K˜—š Ÿœžœžœžœ žœ˜pKšœC˜CK˜K˜—šŸ œžœ-žœ žœ˜\šžœ ž˜Kšœ2˜2Kšœ,˜,Kšœ2˜2Kšžœ˜—K˜K˜—šŸœžœ-žœ žœ˜^šžœ ž˜Kšœ0˜0Kšœ*˜*Kšœ0˜0Kšžœ˜—K˜K˜—K˜K™ K™Kšœ žœ˜*Kšœžœžœ˜-šœžœžœ˜"K˜ K˜Kšœžœ˜ Kšœžœ˜ Kšœ žœ˜K˜Kšœ˜K˜—Kšœžœžœ˜5šœžœžœ˜&Kšœžœžœ ˜Kšœ]˜]Kšœ˜K˜—Kšœžœžœ˜=šœžœžœ˜*Kšœžœžœ ˜Kšœ\˜\Kšœ˜K˜—š¢œžœžœžœ3˜}Kšœžœ˜/Kšœžœžœ˜*Kšœ*˜*Kšœ!˜!Kš œžœžœžœžœžœ)˜[KšœžœC˜UKšœ žœG˜UK˜K˜—š¢œžœžœžœ;˜‰Kšœžœ˜/Kšœžœžœ˜*Kšœ.˜.Kšœ%˜%Kš œžœžœžœžœžœ-˜_KšœžœG˜YKšœžœK˜]K˜K˜—š¢œžœžœ0žœ.˜‚Kšœžœ˜9Kšœžœžœ˜&Kš žœ žœžœžœ žœ˜+šžœžœž˜ šžœžœžœ˜Kšœ.˜.šžœžœžœž˜Kšœ˜Kšžœ˜K˜—Kšœ¡˜0K˜—Kš žœ žœžœžœžœžœ˜IKš žœ žœžœžœ žœ˜+Kšœ7˜7Kšžœ˜—K˜K˜—š¢œžœžœ8žœ6˜–Kšœžœ˜AKšœžœžœ˜&Kš žœ žœžœžœžœ˜2šžœžœž˜ šžœžœžœ˜Kšœ6˜6šžœžœžœž˜#Kšœ˜Kšžœ˜K˜—Kšœ¡˜0K˜—Kš žœ žœžœžœžœžœ˜IKš žœ žœžœžœžœ˜2Kšœ;˜;Kšžœ˜—K˜—K˜KšœžœC˜Vš œžœ:žœžœžœ žœžœ žœ˜¨Kšœžœ˜+Kšœ žœ˜KšœDžœ˜HKšœ˜Kšœ žœ˜K˜Kšœ˜Kšœ˜K˜Kšœ žœ˜Kšœ"˜"š žœžœžœ"žœžœž˜FJšœv˜všžœ žœžœ˜-Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ žœ˜J˜—Jšžœ˜—š žœžœžœ"žœžœž˜FJšœi˜išžœ žœžœ˜-Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ žœ˜J˜—Jšžœ˜—šžœ žœ˜Jšžœ ž˜šœ˜šœžœ˜4Jšœ<˜<—J˜—˜ šœžœ˜4Jšœ3˜3—J˜—Jšžœžœ˜J˜—K˜K˜K˜—šŸœžœžœ:žœžœžœ žœžœ žœ˜±Kšœžœ˜+Kšœ žœ˜Kšœžœ˜Kšœ˜Kšœ žœ˜K˜Kšœ˜K˜Kšœ žœ˜Kšœ"˜"š žœžœžœ"žœžœž˜FJšœi˜išžœ žœžœ˜-Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ žœ˜J˜—Jšžœ˜—šžœ žœ˜šœžœ˜4Jšœ3˜3—J˜—K˜—K™šŸœžœžœ)žœ žœžœžœ˜KšœN™NJšœ&˜&Kšœ žœžœ˜Kšœ žœ˜Kšœ˜Kšœžœ˜+K˜Jšœ žœ˜ Jšœ˜š žœžœžœ"žœžœž˜FJšœ˜Jšžœžœžœžœ˜Jšœ,˜,šžœOžœžœž˜dJšœA˜Aš žœžœžœ žœžœž˜DJšœ žœ˜"Jšžœ˜—Jšœ$˜$Jšžœ˜—J˜Jšžœ˜—K˜K˜—š ¢œžœ-žœ žœžœžœ˜€Jšœ&˜&Kšœ žœžœ˜Kšœ žœ˜Kšœ˜Kšœžœ˜+K˜Jšœ žœ˜ Jšœ˜š žœžœžœ"žœžœž˜FJšœ˜Jšžœžœžœžœ˜Jšœ,˜,šžœOžœžœž˜dJšœE˜Eš žœžœžœ žœžœž˜DJšœ žœ˜"Jšžœ˜—Jšžœ˜—Jšœ$˜$Jšžœ˜—K˜K˜—š¢œžœžœžœžœžœžœ˜gKšœZ™ZKšœ!žœ ˜1Kšœ"˜"šžœž˜"Kšœžœžœ˜#šœ ˜ Kšœžœ˜$Kšœ1˜1KšœD˜DK˜—Kšžœžœ˜—K˜K˜—K™K˜š¢œžœ2žœ˜VKšœD™DKšœžœ˜(š žœžœžœ&žœžœž˜JJšžœžœžœ4˜LJšžœ˜—K˜K˜—š¢œžœ%žœžœ˜_Kšœ6™6Kšœžœ˜(š žœžœžœ&žœžœž˜JJšžœžœžœžœ(˜GJšžœ˜—Jšžœ8˜=K˜K˜—š¢œžœ=˜XKšœ>™>Kšœžœ˜(š žœžœžœ&žœžœž˜JJšžœžœžœ+˜CJšžœ˜—K˜K˜—šŸœžœ*˜FK˜Kšœ,˜,šžœOžœžœž˜dKšœ˜—Kšžœ˜K˜K˜—š¢œžœ%žœ˜aKšœ1™1Kšœžœ˜(š žœžœžœ&žœžœž˜JJšžœžœžœžœ%˜DJšžœ˜—Jšžœ8˜=K˜K˜—šŸœžœžœ˜OKšœ>˜>Kšœ.˜.Kšžœžœžœ˜"K˜K˜—šŸ¢ œžœ*˜CKšœ-™-Kšœ˜K˜—š¢œžœžœ˜LKšœ ™ Kšœ˜K˜—K™K™K™š Ÿœžœžœžœžœžœ˜ŠKšœ„™„Kšœ˜K˜Kš žœ žœžœžœžœžœ˜9Kšœžœ˜Kšœžœ˜ š žœžœžœ"žœžœž˜Fšžœžœžœ˜Jš žœžœžœžœžœžœ˜4šžœ˜Jšœ˜Jšœ˜JšœM˜MJ˜—J˜—Jšžœ˜—K˜K˜—šŸœžœžœžœ žœ5žœ žœžœžœ žœ ˜ñKšœ˜K˜Kšžœ žœžœžœžœ žœžœ˜BKšœžœ˜Kšœžœ˜ š žœžœžœ"žœžœž˜Fšžœžœžœ˜Jšžœžœžœžœžœ žœžœ ˜=šžœ˜Jšœ˜Jšœ˜Jšœm˜mJ˜—J˜—Jšžœ˜—K˜K˜—šŸœžœžœžœ žœ5žœ žœžœžœ žœ ˜êKšžœ-˜2K˜K˜—š Ÿœžœžœžœžœžœ˜ƒKšžœ-˜2K˜K˜—š Ÿœžœžœžœžœ˜nKšœ„™„Kšœ˜Kšœ˜Kš žœ žœžœžœžœ˜(Kšœžœ˜Kšœžœ˜ š žœžœžœ"žœžœž˜Fšžœžœžœ˜Jš žœžœžœžœžœ˜#šžœ˜Jšœ˜Jšœ˜Jšœ7˜7J˜—J˜—Jšžœ˜—K˜K˜K˜—š Ÿœžœžœžœžœ˜gKšžœ-˜2K˜K˜—šŸœžœžœ+žœ˜gKšœ˜Kšœžœ˜&š žœžœžœ)žœžœž˜MKš žœžœžœžœžœ ˜GKšžœ˜—Kšžœžœ˜ K˜K˜—šŸœž œ#žœ˜eK˜Kšœžœžœ ˜Jšœ$˜$Kšœ.˜.Kšœ6˜6Jšœ"˜"šžœGžœžœž˜]Jšœ_˜_Jšžœ/žœ˜MJšžœ˜—J˜J˜—šŸ œžœžœ'žœ˜[K˜'Kšœ˜Kšœžœžœ ˜Kšœ˜Kšœ,˜,Kšœ:˜:š žœžœžœ&žœžœž˜JJšžœžœžœ3žœ˜hšžœ˜Jšœ&˜&JšœS˜SJ˜—Jšžœ˜—K˜K˜—šŸœžœžœ žœ˜QK™*K™1Kšœ%˜%Kšœ2žœ˜>Kšœ˜Kšœ˜Kšœ˜Kšœ˜K˜—šŸ œž œ$žœ˜^K™%K˜Kšœ˜KšœJ˜JKšœ"˜"šžœGžœžœž˜]Kšœ ˜ Kšœ:žœ ˜IKšœ˜Kšžœ˜—Kšœ"˜"K˜K˜K˜—šŸ œž œ,žœ˜eK™%K˜Kšœ˜Kšœ ˜ Kšœ!˜!KšœI˜IKšœ"˜"šžœGžœžœž˜]šžœžœ˜Kšœ:žœ ˜IKšœ˜K˜—šžœ˜Kšœ ˜ Kšœ:žœ ˜IKšœ˜K˜—Kšžœ˜—Kšœ"˜"K˜K˜—šŸ œžœžœžœ˜FKšœ/™/Kšœ˜K˜K˜—šŸœžœžœ%˜IKšœ˜˜Jš ™—Kšœ8 œ˜=Jšœ! œ˜&šžœ žœžœ˜Kšœžœ˜&šžœ=žœ žœž˜VJšžœžœžœ( œ˜FJšžœ˜—J˜Jš ™—Kšœ8 œ˜@Jšœ! œ˜)šžœ žœžœ˜Kšœžœ˜&šžœ=žœ žœž˜VJšžœžœžœ0˜IJšžœ˜—J˜Jš ™—Kšœ8 œ˜@Jšœ! œ˜)šžœ žœžœ˜Kšœžœ˜&š žœ žœžœ,žœ žœž˜VJšžœžœžœ( œ˜IJšžœ˜—J˜—J˜K˜—šŸœžœžœ<˜\JšœH˜HJšžœžœžœ(˜7Jšžœ+˜/J˜J˜—šŸœžœ.˜HJšœ ˜ Jšœ ˜ šžœžœžœž˜,Jšœ#˜#Jšœžœ˜)šžœžœžœ)ž˜8Jšœžœ˜1Jšžœ˜—Jšžœ˜—šžœžœžœž˜*Jšœ#˜#Jšœžœ˜)Jšžœ˜—šœ˜J˜——šŸœžœžœ<˜`Jšœ-˜-J˜ J˜ šžœžœžœž˜,Jšœ#˜#Jšœ4˜4Jšžœžœ!˜8šžœžœžœ)ž˜8JšœD˜DJšžœžœ3˜RJšžœ˜—Jšžœ˜—šžœžœžœž˜*Jšœ#˜#Jšœ2˜2Jšžœžœ%˜:Jšžœ˜—Jšœ1˜1K˜K˜—šŸœžœ1˜NJ˜ J˜ šžœžœžœ!ž˜0Jšœ'˜'Jšœ3˜3šžœžœžœ)ž˜8JšœC˜CJšžœ˜—Jšžœ˜—šžœžœžœž˜.Jšœ'˜'Jšœ1˜1Jšžœ˜—J˜J˜—šŸœžœ4˜QJšœ$˜$Jšœ"˜"šžœGžœžœž˜]Jšœ(˜(Jšžœ˜—Jšœ˜J˜—šŸœžœžœ%˜MK™ÁJšœ$˜$Jšœ"˜"šžœGžœžœž˜]Jš ™Jšœ% œ˜*Jš ™Jšœ% œ˜-Jš ™Jšœ% œ˜-Jšžœ˜—K˜K˜—š Ÿœžœžœžœ žœžœ˜eKšœžœ˜4Kšœžœžœ ˜Kšœ/˜/š žœžœžœ)žœžœž˜MJšžœžœžœB˜ZJšžœ˜—K˜K˜—šŸ œžœžœ.˜EK™&Kšœžœ˜4K˜š žœžœžœ)žœžœž˜Mšžœ žœžœžœ˜3J˜Jšžœ˜ J˜—šž˜˜ šœžœžœ˜FJšœ{™{—J˜—Jšžœžœ˜—Jšžœ˜—K˜—K™š Ÿœž œ žœžœžœ žœ˜]Kšœ!žœ ˜1Kšœ žœ˜ Kšœ˜Kšœžœ˜ Kšœ˜šžœž˜"˜ Kšœ#˜#K˜—šœ˜Kšœ=˜=Kšœ7˜7Kšœ)˜)Kšœ;˜;Kšœ;˜;Kšžœ žœ!˜1Kšžœ˜K˜—Kšžœžœ˜—K˜K˜—šŸ œžœžœ žœžœžœ>žœ˜ŒK™6Kšœ!žœ ˜1Kšœ˜Kšœ!˜!Kšœ˜Kšœ˜Kšœ#˜#Kšœ#˜#K˜K˜—šŸœžœžœ"˜IK™2Kšœžœ˜-š žœžœžœ"žœžœž˜FJšžœžœžœ'˜?Jšžœ˜—K˜—K™Kšœ œ™š Ÿœžœžœžœžœ˜