DIRECTORY Atom, Feedback, FeedbackTypes, GGBasicTypes, GGBoundBox, GGCoreOps, GGCoreTypes, GGHistoryTypes, GGInterfaceTypes, GGModelTypes, GGParseIn, GGParseOut, GGSegment, GGSegmentTypes, GGSequence, GGSlice, GGSliceOps, GGTraj, GGUtility, Imager, ImagerTransformation, IO, Rope, Vectors2d; GGSliceImplE: CEDAR PROGRAM IMPORTS Atom, Feedback, GGBoundBox, GGCoreOps, GGParseIn, GGParseOut, GGSegment, GGSequence, GGSlice, GGSliceOps, GGTraj, GGUtility, Imager, IO, Rope, Vectors2d EXPORTS GGSlice = BEGIN BitVector: TYPE = GGBasicTypes.BitVector; BoundBox: TYPE = GGCoreTypes.BoundBox; BoundBoxObj: TYPE = GGCoreTypes.BoundBoxObj; Camera: TYPE = GGModelTypes.Camera; Circle: TYPE = GGBasicTypes.Circle; Color: TYPE = Imager.Color; ControlPointGenerator: TYPE = GGModelTypes.ControlPointGenerator; Corner: TYPE = GGSlice.Corner; DefaultData: TYPE = GGModelTypes.DefaultData; Edge: TYPE = GGBasicTypes.Edge; EditConstraints: TYPE = GGModelTypes.EditConstraints; ExtendMode: TYPE = GGModelTypes.ExtendMode; FactoredTransformation: TYPE = ImagerTransformation.FactoredTransformation; FeatureData: TYPE = GGInterfaceTypes.FeatureData; MsgRouter: TYPE = FeedbackTypes.MsgRouter; FenceHoleOpen: TYPE = GGModelTypes.FenceHoleOpen; GGData: TYPE = GGInterfaceTypes.GGData; HistoryEvent: TYPE = GGHistoryTypes.HistoryEvent; HitType: TYPE = GGModelTypes.TrajPartType; Joint: TYPE = GGSegmentTypes.Joint; JointGenerator: TYPE = GGModelTypes.JointGenerator; JointObj: TYPE = GGSegmentTypes.JointObj; Line: TYPE = GGCoreTypes.Line; Object: TYPE = Imager.Object; Orientation: TYPE = GGModelTypes.Orientation; 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; PointWalkProc: TYPE = GGModelTypes.PointWalkProc; Scene: TYPE = GGModelTypes.Scene; SegAndIndex: TYPE = GGSequence.SegAndIndex; SegCPSequence: TYPE = GGTraj.SegCPSequence; SegCPSequenceObj: TYPE = GGTraj.SegCPSequenceObj; Segment: TYPE = GGSegmentTypes.Segment; SegmentClass: TYPE = GGSegmentTypes.SegmentClass; SegmentGenerator: TYPE = GGModelTypes.SegmentGenerator; SegmentGeneratorObj: TYPE = GGModelTypes.SegmentGeneratorObj; SelectedObjectData: TYPE = GGModelTypes.SelectedObjectData; SelectionClass: TYPE = GGSegmentTypes.SelectionClass; SelectMode: TYPE = GGModelTypes.SelectMode; Sequence: TYPE = GGModelTypes.Sequence; SequenceOfReal: TYPE = GGCoreTypes.SequenceOfReal; Slice: TYPE = GGModelTypes.Slice; SliceBoundBoxProc: TYPE = GGModelTypes.SliceBoundBoxProc; SliceClass: TYPE = GGModelTypes.SliceClass; SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor; SliceDescriptorObj: TYPE = GGModelTypes.SliceDescriptorObj; SliceGenerator: TYPE = GGModelTypes.SliceGenerator; SliceObj: TYPE = GGModelTypes.SliceObj; SliceParts: TYPE = GGModelTypes.SliceParts; StrokeEnd: TYPE = GGModelTypes.StrokeEnd; StrokeJoint: TYPE = GGModelTypes.StrokeJoint; Traj: TYPE = GGModelTypes.Traj; TrajData: TYPE = GGModelTypes.TrajData; TrajEnd: TYPE = GGModelTypes.TrajEnd; TrajHitData: TYPE = GGTraj.TrajHitData; TrajHitDataObj: TYPE = GGTraj.TrajHitDataObj; TrajParts: TYPE = GGModelTypes.TrajParts; TrajPartsObj: TYPE = GGModelTypes.TrajPartsObj; TrajPartType: TYPE = GGModelTypes.TrajPartType; Transformation: TYPE = GGModelTypes.Transformation; TriggerBag: TYPE = GGInterfaceTypes.TriggerBag; Vector: TYPE = GGBasicTypes.Vector; WalkProc: TYPE = GGModelTypes.WalkProc; SliceClassDef: TYPE = REF SliceClassDefObj; SliceClassDefObj: TYPE = RECORD[type: ATOM, class: SliceClass]; Problem: PUBLIC SIGNAL [msg: Rope.ROPE] = Feedback.Problem; BuildMoreTrajSliceClass: PUBLIC PROC [class: SliceClass] = { class.describe ¬ TrajDescribe; class.describeHit ¬ TrajDescribeHit; class.fileout ¬ TrajFileout; class.filein ¬ TrajFilein; class.pointsInDescriptor ¬ TrajPointsInDescriptor; class.walkPointsInDescriptor ¬ TrajWalkPointsInDescriptor; class.pointPairsInDescriptor ¬ TrajPointPairsInDescriptor; class.segmentsInDescriptor ¬ TrajSegmentsInDescriptor; class.walkSegments ¬ TrajWalkSegments; class.nextPoint ¬ TrajNextPoint; class.nextPointPair ¬ TrajNextPointPair; class.nextSegment ¬ TrajNextSegment; class.closestPoint ¬ TrajClosestPoint; class.closestJointToHitData ¬ TrajClosestJointToHitData; class.closestPointAndTangent ¬ TrajClosestPointAndTangent; class.closestSegment ¬ TrajClosestSegment; class.filledPathsUnderPoint ¬ TrajFilledPathsUnderPoint; class.lineIntersection ¬ TrajLineIntersection; class.circleIntersection ¬ TrajCircleIntersection; class.hitDataAsSimpleCurve ¬ TrajHitDataAsSimpleCurve; class.setDefaults ¬ TrajSetDefaults; class.setStrokeWidth ¬ TrajSetStrokeWidth; class.getStrokeWidth ¬ TrajGetStrokeWidth; class.setStrokeEnd ¬ TrajSetStrokeEnd; class.getStrokeEnd ¬ TrajGetStrokeEnd; class.setStrokeJoint ¬ TrajSetStrokeJoint; class.getStrokeJoint ¬ TrajGetStrokeJoint; class.setStrokeColor ¬ TrajSetStrokeColor; class.getStrokeColor ¬ TrajGetStrokeColor; class.setFillColor ¬ GGSlice.NoOpSetFillColor; class.getFillColor ¬ GGSlice.NoOpGetFillColor; class.setArrows ¬ TrajSetArrows; class.getArrows ¬ TrajGetArrows; class.setDashed ¬ TrajSetDashed; class.getDashed ¬ TrajGetDashed; class.setOrientation ¬ TrajSetOrientation; class.getOrientation ¬ TrajGetOrientation; }; AllStrokeEndsEqual: PROC [slice: Slice] RETURNS [allEqual: BOOL ¬ FALSE, end: StrokeEnd] = { seg: Segment; firstSeg: Segment ¬ GGTraj.FetchSegment[slice, 0]; hiSegment: NAT ¬ GGTraj.HiSegment[slice]; end ¬ firstSeg.strokeEnd; FOR i: INT IN [1..hiSegment] DO seg ¬ GGTraj.FetchSegment[slice, i]; IF seg.strokeEnd # end THEN RETURN[FALSE, round]; ENDLOOP; allEqual ¬ TRUE; }; AllStrokeWidthsEqual: PROC [slice: Slice] RETURNS [width: REAL] = { seg: Segment; firstSeg: Segment ¬ GGTraj.FetchSegment[slice, 0]; hiSegment: NAT ¬ GGTraj.HiSegment[slice]; width ¬ firstSeg.strokeWidth; FOR i: INT IN [1..hiSegment] DO seg ¬ GGTraj.FetchSegment[slice, i]; IF seg.strokeWidth # width THEN RETURN[-1.0]; ENDLOOP; }; AllStrokeColorsEqual: PROC [slice: Slice] RETURNS [allEqual: BOOL ¬ FALSE, color: Color] = { seg: Segment; firstSeg: Segment ¬ GGTraj.FetchSegment[slice, 0]; hiSegment: NAT ¬ GGTraj.HiSegment[slice]; color ¬ firstSeg.color; FOR i: INT IN [1..hiSegment] DO seg ¬ GGTraj.FetchSegment[slice, i]; IF NOT GGCoreOps.EquivalentColors[color, seg.color] THEN RETURN[FALSE, NIL]; ENDLOOP; allEqual ¬ TRUE; }; AllDashesEqual: PROC [slice: Slice] RETURNS [allEqual: BOOL ¬ FALSE, dashed: BOOL ¬ FALSE, pattern: SequenceOfReal ¬ NIL, offset, length: REAL ¬ 0.0] = { seg: Segment; firstSeg: Segment ¬ GGTraj.FetchSegment[slice, 0]; hiSegment: NAT ¬ GGTraj.HiSegment[slice]; dashed ¬ firstSeg.dashed; pattern ¬ firstSeg.pattern; offset ¬ firstSeg.offset; length ¬ firstSeg.length; FOR i: INT IN [1..hiSegment] DO seg ¬ GGTraj.FetchSegment[slice, i]; IF seg.dashed # dashed THEN RETURN[FALSE]; IF NOT dashed THEN LOOP; IF NOT GGUtility.EquivalentPatterns[seg.pattern, pattern] THEN RETURN[FALSE]; IF seg.offset # offset THEN RETURN[FALSE]; IF seg.length # length THEN RETURN[FALSE]; ENDLOOP; allEqual ¬ TRUE; }; TrajDescribe: PROC [sliceD: SliceDescriptor] RETURNS [rope: Rope.ROPE] = { trajData: TrajData ¬ NARROW[sliceD.slice.data]; trajParts: TrajParts ¬ NARROW[sliceD.parts]; IF trajParts=NIL THEN RETURN[NIL] ELSE { segNum, cpNum: NAT; segCount: NAT ¬ trajParts.segCount; jointCount: NAT ¬ trajParts.jointCount; cpCount: NAT ¬ trajParts.controlPointCount; SELECT TRUE FROM segCount=1 => { -- single segment selected. Describe it segGen: SegmentGenerator ¬ GGSequence.SegmentsInSequence[trajData, trajParts]; rope ¬ GGUtility.DescribeSegment[sliceD.slice, GGSequence.NextSegmentAndIndex[segGen].index]; }; segCount=0 AND cpCount=1 => { -- single CP selected. Describe it cpGen: ControlPointGenerator ¬ GGSequence.ControlPointsInSequence[trajData, trajParts]; [segNum, cpNum] ¬ GGSequence.NextSegNumAndCPNum[cpGen]; rope ¬ GGUtility.DescribeControlPoint[sliceD.slice, segNum, cpNum]; }; segCount=0 AND cpCount=0 AND jointCount=1 => { -- single joint selected. Describe it jointGen: JointGenerator ¬ GGSequence.JointsInSequence[trajParts]; rope ¬ GGUtility.DescribeJoint[sliceD.slice, GGSequence.NextJoint[jointGen]]; }; ENDCASE => rope ¬ GGUtility.DescribeSequence[sliceD]; }; }; TrajDescribeHit: PROC [slice: Slice, hitData: REF ANY] RETURNS [rope: Rope.ROPE] = { trajHitData: TrajHitData ¬ NARROW[hitData]; SELECT trajHitData.hitType FROM joint => rope ¬ GGUtility.DescribeJoint[slice, trajHitData.jointNum]; controlPoint => rope ¬ GGUtility.DescribeControlPoint[slice, trajHitData.segNum, trajHitData.cpNum]; segment => rope ¬ GGUtility.DescribeSegment[slice, trajHitData.segNum]; interior => rope ¬ GGUtility.DescribeInterior[slice]; ENDCASE => ERROR; }; TrajFileout: PROC [slice: Slice, f: IO.STREAM] = { trajData: TrajData ¬ NARROW[slice.data]; roleRope: Rope.ROPE ¬ RoleToRope[trajData.role]; strokeWidth, offset, length: REAL; strokeOK, endsOK, colorOK, dashOK, dashed: BOOL; pattern: SequenceOfReal; color: Color; point: Point; end: StrokeEnd; className: Rope.ROPE; seg: Segment; arrowCode: NAT; hiJoint: NAT ¬ GGTraj.HiJoint[slice]; arrowCode ¬ SELECT TRUE FROM trajData.hiArrow => SELECT TRUE FROM trajData.loArrow => 3, ENDCASE => 2, ENDCASE => SELECT TRUE FROM trajData.loArrow => 1, ENDCASE => 0; f.PutF[" (%g) [%g] arrows: %g ", [rope[roleRope]], [integer[hiJoint]], [integer[arrowCode]]]; f.PutRope["j: "]; -- traj Joint GGParseOut.WriteStrokeJoint[f, trajData.strokeJoint]; [endsOK, end] ¬ AllStrokeEndsEqual[slice]; f.PutRope[" e: "]; GGParseOut.WriteBool[f, endsOK]; f.PutChar[IO.SP]; GGParseOut.WriteStrokeEnd[f, end]; f.PutChar[IO.SP]; strokeWidth ¬ AllStrokeWidthsEqual[slice]; strokeOK ¬ strokeWidth >= 0.0; f.PutF1["w: %g ", [real[strokeWidth]]]; [colorOK, color] ¬ AllStrokeColorsEqual[slice]; f.PutRope["c: "]; GGParseOut.WriteBool[f, colorOK]; f.PutChar[IO.SP]; GGParseOut.WriteColor[f, color]; f.PutChar[IO.SP]; [dashOK, dashed, pattern, offset, length] ¬ AllDashesEqual[slice]; f.PutRope["d: "]; GGParseOut.WriteBool[f, dashOK]; IF dashOK THEN { f.PutChar[IO.SP]; GGParseOut.WriteBool[f, dashed]; f.PutChar[IO.SP]; IF dashed THEN { GGParseOut.WriteArrayOfReal[f, pattern]; f.PutF[" %g %g", [real[offset]], [real[length]]]; }; }; f.PutChar[IO.LF]; point ¬ GGTraj.FetchJointPos[slice, 0]; GGParseOut.WritePoint[f, point]; FOR index: NAT IN [1..hiJoint] DO seg ¬ GGTraj.FetchSegment[slice, index - 1]; className ¬ Atom.GetPName[seg.class.type]; IF strokeOK THEN f.PutF1[" (%g ", [rope[className]]] ELSE f.PutF[" (%g %g ", [rope[className]], [real[seg.strokeWidth]]]; IF NOT endsOK THEN { GGParseOut.WriteStrokeEnd[f, seg.strokeEnd]; f.PutChar[IO.SP]; }; IF NOT colorOK THEN { GGParseOut.WriteColor[f, seg.color]; f.PutChar[IO.SP]; }; IF NOT dashOK THEN { GGParseOut.WriteBool[f, seg.dashed]; f.PutChar[IO.SP]; IF seg.dashed THEN { GGParseOut.WriteArrayOfReal[f, seg.pattern]; f.PutChar[IO.SP]; f.PutF[" %g %g ", [real[seg.offset]], [real[seg.length]]]; }; }; seg.class.fileOut[seg, f]; GGParseOut.WriteProps[f, seg.props]; f.PutRope[") "]; point ¬ GGTraj.FetchJointPos[slice, index]; GGParseOut.WritePoint[f, point]; ENDLOOP; IF (trajData.role = fence OR trajData.role = hole) THEN { seg ¬ GGTraj.FetchSegment[slice, hiJoint]; className ¬ Atom.GetPName[seg.class.type]; IF strokeOK THEN f.PutF1[" (%g ", [rope[className]]] ELSE f.PutF[" (%g %g ", [rope[className]], [real[seg.strokeWidth]]]; IF NOT endsOK THEN { GGParseOut.WriteStrokeEnd[f, seg.strokeEnd]; f.PutChar[IO.SP]; }; IF NOT colorOK THEN { GGParseOut.WriteColor[f, seg.color]; f.PutChar[IO.SP]; }; IF NOT dashOK THEN { GGParseOut.WriteBool[f, seg.dashed]; f.PutChar[IO.SP]; IF seg.dashed THEN { GGParseOut.WriteArrayOfReal[f, seg.pattern]; f.PutChar[IO.SP]; f.PutF[" %g %g ", [real[seg.offset]], [real[seg.length]]]; }; }; seg.class.fileOut[seg, f]; GGParseOut.WriteProps[f, seg.props]; f.PutRope[")"]; }; f.PutRope[" fwd: "]; GGParseOut.WriteBool[f, trajData.forward]; }; TrajFilein: PROC [f: IO.STREAM, version: REAL, router: MsgRouter, camera: Camera] RETURNS [slice: Slice] = { hasCircle: BOOL ¬ FALSE; hiJoint, arrowCode: NAT; role: FenceHoleOpen; roleName, className: Rope.ROPE; pFirst, p0, p1: Point; trajData: TrajData; seg: Segment; class: SegmentClass; strokeWidth, offset, length: REAL; pattern: SequenceOfReal; strokeColor: Color; strokeEnd: StrokeEnd; strokeJoint: StrokeJoint; success, colorOK, widthOK, endsOK, dashOK, dashed, fwd: BOOL; IF version < 8802.04 THEN GGParseIn.ReadRope[f, "Traj"]; -- gone as of version 8802.04 GGParseIn.ReadChar[f, '(]; roleName ¬ GGParseIn.ReadWord[f]; role ¬ RoleFromRope[roleName]; GGParseIn.ReadChar[f, ')]; GGParseIn.ReadChar[f, '[]; hiJoint ¬ GGParseIn.ReadNAT[f]; GGParseIn.ReadChar[f, ']]; IF version >= 8607.22 THEN { GGParseIn.ReadRope[f, "arrows:"]; arrowCode ¬ GGParseIn.ReadNAT[f]; } ELSE arrowCode ¬ 0; IF version >= 8702.26 THEN { GGParseIn.ReadRope[f, "j:"]; strokeJoint ¬ GGParseIn.ReadStrokeJoint[f]; GGParseIn.ReadRope[f, "e:"]; endsOK ¬ GGParseIn.ReadBool[f]; strokeEnd ¬ GGParseIn.ReadStrokeEnd[f]; } ELSE { strokeJoint ¬ round; endsOK ¬ FALSE; }; IF version >= 8701.135 THEN { GGParseIn.ReadRope[f, "w:"]; strokeWidth ¬ GGParseIn.ReadReal[f]; widthOK ¬ strokeWidth>= 0.0; GGParseIn.ReadRope[f, "c:"]; colorOK ¬ GGParseIn.ReadBool[f]; strokeColor ¬ GGParseIn.ReadColor[f, version]; } ELSE { GGParseIn.ReadChar[f, ':]; widthOK ¬ FALSE; colorOK ¬ FALSE; }; IF version >= 8701.23 THEN { GGParseIn.ReadRope[f, "d:"]; dashOK ¬ GGParseIn.ReadBool[f]; IF dashOK THEN { dashed ¬ GGParseIn.ReadBool[f]; IF dashed THEN { pattern ¬ GGParseIn.ReadArrayOfReal[f]; offset ¬ GGParseIn.ReadReal[f]; length ¬ GGParseIn.ReadReal[f]; }; }; } ELSE { dashOK ¬ TRUE; dashed ¬ FALSE; }; pFirst ¬ p0 ¬ GGParseIn.ReadPoint[f]; slice ¬ GGTraj.CreateTraj[p0]; trajData ¬ NARROW[slice.data]; trajData.strokeJoint ¬ strokeJoint; FOR index: NAT IN [1..hiJoint] DO GGParseIn.ReadChar[f, '(]; className ¬ GGParseIn.ReadWord[f]; IF NOT widthOK THEN strokeWidth ¬ GGParseIn.ReadReal[f]; IF version >= 8702.26 THEN { IF NOT endsOK THEN strokeEnd ¬ GGParseIn.ReadStrokeEnd[f]; } ELSE strokeEnd ¬ round; IF version >= 8607.30 THEN { IF NOT colorOK THEN strokeColor ¬ GGParseIn.ReadColor[f, version]; } ELSE strokeColor ¬ Imager.black; IF NOT dashOK THEN { dashed ¬ GGParseIn.ReadBool[f]; IF dashed THEN { pattern ¬ GGParseIn.ReadArrayOfReal[f]; offset ¬ GGParseIn.ReadReal[f]; length ¬ GGParseIn.ReadReal[f]; }; }; class ¬ GGSegment.FetchSegmentClass[Atom.MakeAtom[className]]; seg ¬ class.fileIn[f, p0, [0.0, 0.0], version]; IF version >= 8610.29 THEN { lor: LIST OF Rope.ROPE; lor ¬ GGParseIn.ReadListOfRope[f]; -- ReadListOfRope cheats and terminates at ') as well as at IO.CR FOR next: LIST OF Rope.ROPE ¬ lor, next.rest UNTIL next=NIL DO seg.props ¬ CONS[next.first, seg.props]; ENDLOOP; }; GGParseIn.ReadChar[f, ')]; p1 ¬ GGParseIn.ReadPoint[f]; seg.hi ¬ p1; seg.class.endPointMoved[seg, FALSE, p1]; seg.strokeWidth ¬ strokeWidth; seg.strokeEnd ¬ strokeEnd; seg.color ¬ strokeColor; seg.dashed ¬ dashed; seg.pattern ¬ pattern; seg.offset ¬ offset; seg.length ¬ length; IF class.type=$Circle OR class.type=$Disc THEN { p: Point ¬ Vectors2d.Sub[seg.hi, seg.lo]; newJoint: Point ¬ [seg.lo.x-p.x, seg.lo.y-p.y]; newCp: Point ¬ [seg.lo.x+p.x, seg.lo.y+p.y]; success ¬ GGTraj.AddSegment[slice, hi, GGSegment.MakeArc[newJoint, newCp, newJoint, NIL], lo]; GGTraj.CloseByDistorting[slice, lo]; hasCircle ¬ TRUE; } ELSE success ¬ GGTraj.AddSegment[slice, hi, seg, lo]; IF NOT success THEN ERROR; p0 ¬ p1; ENDLOOP; IF (role = fence OR role = hole) THEN { GGParseIn.ReadChar[f, '(]; className ¬ GGParseIn.ReadWord[f]; IF NOT widthOK THEN strokeWidth ¬ GGParseIn.ReadReal[f]; IF version >= 8702.26 THEN { IF NOT endsOK THEN strokeEnd ¬ GGParseIn.ReadStrokeEnd[f]; } ELSE strokeEnd ¬ round; IF version >= 8607.30 THEN { IF NOT colorOK THEN strokeColor ¬ GGParseIn.ReadColor[f, version]; } ELSE strokeColor ¬ Imager.black; IF NOT dashOK THEN { dashed ¬ GGParseIn.ReadBool[f]; IF dashed THEN { pattern ¬ GGParseIn.ReadArrayOfReal[f]; offset ¬ GGParseIn.ReadReal[f]; length ¬ GGParseIn.ReadReal[f]; }; }; class ¬ GGSegment.FetchSegmentClass[Atom.MakeAtom[className]]; seg ¬ class.fileIn[f, p0, pFirst, version]; IF version >= 8610.29 THEN { lor: LIST OF Rope.ROPE; lor ¬ GGParseIn.ReadListOfRope[f]; -- ReadListOfRope cheats and terminates at ') as well as at IO.CR FOR next: LIST OF Rope.ROPE ¬ lor, next.rest UNTIL next=NIL DO seg.props ¬ CONS[next.first, seg.props]; ENDLOOP; }; GGParseIn.ReadChar[f, ')]; seg.strokeWidth ¬ strokeWidth; seg.color ¬ strokeColor; seg.strokeEnd ¬ strokeEnd; seg.dashed ¬ dashed; seg.pattern ¬ pattern; seg.offset ¬ offset; seg.length ¬ length; IF trajData.segCount=0 THEN { -- special case of closed single segment trajectory success ¬ GGTraj.AddSegment[slice, hi, seg, lo]; IF NOT success THEN ERROR; GGTraj.CloseByDistorting[slice, lo]; } ELSE GGTraj.CloseWithSegment[slice, seg, lo]; }; IF version>=8802.04 THEN { -- for trajectories, fwd=true means "think of me as clockwise" GGParseIn.ReadRope[f, "fwd:"]; fwd ¬ GGParseIn.ReadBool[f]; } ELSE fwd ¬ TRUE; trajData.forward ¬ fwd; trajData.role ¬ IF hasCircle THEN circle ELSE role; -- caller better fix this up !! }; RoleFromRope: PROC [roleName: Rope.ROPE] RETURNS [role: FenceHoleOpen] = { SELECT TRUE FROM Rope.Equal[roleName, "fence"] => role ¬ fence; Rope.Equal[roleName, "hole"] => role ¬ hole; Rope.Equal[roleName, "open"] => role ¬ open; ENDCASE => ERROR; }; RoleToRope: PROC [role: FenceHoleOpen] RETURNS [roleName: Rope.ROPE] = { SELECT role FROM fence => roleName ¬ "fence"; hole => roleName ¬ "hole"; open => roleName ¬ "open"; ENDCASE => ERROR; }; TrajPointGeneratorData: TYPE = REF TrajPointGeneratorDataObj; TrajPointGeneratorDataObj: TYPE = RECORD [ jointsDone: BOOL, jointGen: JointGenerator, cpGen: GGModelTypes.ControlPointGenerator ]; TrajPointPairGeneratorData: TYPE = REF TrajPointPairGeneratorDataObj; TrajPointPairGeneratorDataObj: TYPE = RECORD [ segGen: SegmentGenerator ]; TrajPointsInDescriptor: PROC [sliceD: SliceDescriptor] RETURNS [pointGen: PointGenerator] = { trajData: TrajData ¬ NARROW[sliceD.slice.data]; trajParts: TrajParts ¬ NARROW[sliceD.parts]; pgd: TrajPointGeneratorData; jointGen: JointGenerator; cpGen: ControlPointGenerator; jointGen ¬ GGSequence.JointsInSequence[trajParts]; cpGen ¬ GGSequence.ControlPointsInSequence[trajData, trajParts]; pgd ¬ NEW[TrajPointGeneratorDataObj ¬ [FALSE, jointGen, cpGen]]; pointGen ¬ NEW[PointGeneratorObj ¬ [sliceD, 0, 0, pgd]]; }; TrajWalkPointsInDescriptor: PROC [sliceD: SliceDescriptor, walkProc: PointWalkProc] = { trajParts: TrajParts ¬ NARROW[sliceD.parts]; trajData: TrajData ¬ NARROW[sliceD.slice.data]; GGSequence.WalkJointsInSequence[trajData, trajParts, walkProc]; GGSequence.WalkControlPointsInSequence[trajData, trajParts, walkProc]; }; TrajPointPairsInDescriptor: PROC [sliceD: SliceDescriptor] RETURNS [pointPairGen: PointPairGenerator] = { trajData: TrajData ¬ NARROW[sliceD.slice.data]; trajParts: TrajParts ¬ NARROW[sliceD.parts]; pgd: TrajPointPairGeneratorData; segGen: SegmentGenerator; segGen ¬ GGSequence.SegmentsInSequence[trajData, trajParts]; pgd ¬ NEW[TrajPointPairGeneratorDataObj ¬ [segGen]]; pointPairGen ¬ NEW[PointPairGeneratorObj ¬ [sliceD, 0, 0, pgd]]; }; TrajSegmentsInDescriptor: PROC [sliceD: SliceDescriptor] RETURNS [segGen: SegmentGenerator] = { trajData: TrajData ¬ NARROW[sliceD.slice.data]; trajParts: TrajParts ¬ NARROW[sliceD.parts]; segGen ¬ GGSequence.SegmentsInSequence[trajData, trajParts]; }; TrajWalkSegments: PROC [slice: Slice, walkProc: WalkProc] RETURNS [sliceD: SliceDescriptor] = { trajData: TrajData ¬ NARROW[slice.data]; trajParts: TrajParts ¬ GGSequence.CreateEmpty[trajData]; segGen: SegmentGenerator ¬ GGSequence.SegmentsInTraj[trajData]; FOR next: SegAndIndex ¬ GGSequence.NextSegmentAndIndex[segGen], GGSequence.NextSegmentAndIndex[segGen] UNTIL next.seg=NIL DO keep: BOOL ¬ walkProc[next.seg, NIL]; IF keep THEN { trajParts.segments[next.index] ¬ TRUE; trajParts.segCount ¬ trajParts.segCount+1; }; ENDLOOP; GGSequence.FillInJoints[trajParts]; GGSequence.FillInControlPoints[trajParts]; sliceD ¬ GGSlice.DescriptorFromParts[slice, trajParts]; }; TrajNextPoint: PROC [slice: Slice, pointGen: PointGenerator] RETURNS [pointAndDone: PointAndDone] = { pgd: TrajPointGeneratorData ¬ NARROW[pointGen.classSpecific]; IF NOT pgd.jointsDone THEN { nextJoint: INT ¬ GGSequence.NextJoint[pgd.jointGen]; IF nextJoint = -1 THEN { pgd.jointsDone ¬ TRUE; } ELSE { IF pointGen.sliceD.slice#slice THEN ERROR; pointAndDone.point ¬ GGTraj.FetchJointPos[slice, nextJoint]; pointAndDone.done ¬ FALSE; RETURN; }; }; pointAndDone ¬ GGSequence.NextControlPoint[pgd.cpGen]; }; TrajNextPointPair: PROC [slice: Slice, pointPairGen: PointPairGenerator] RETURNS [pointPairAndDone: PointPairAndDone] = { pgd: TrajPointPairGeneratorData ¬ NARROW[pointPairGen.classSpecific]; seg: Segment ¬ GGSliceOps.NextSegment[slice, pgd.segGen].seg; IF seg = NIL THEN RETURN[[[0,0], [0,0], TRUE]] ELSE RETURN[[seg.lo, seg.hi, FALSE]]; }; TrajNextSegment: PROC [slice: Slice, segGen: SegmentGenerator] RETURNS [seg: Segment, transform: Transformation] = { RETURN[GGSequence.NextSegment[segGen], NIL]; }; TrajClosestPoint: PROC [sliceD: SliceDescriptor, testPoint: Point, tolerance: REAL] RETURNS [bestPoint: Point ¬ [0,0], bestDist: REAL, bestNormal: Vector ¬ [0, -1], hitData: REF ANY, success: BOOL ¬ FALSE] = { trajParts: TrajParts ¬ NARROW[sliceD.parts]; thisDist: REAL; thisSegNum, bestSegNum, thisCP, bestCP, thisJointNum, bestJointNum: NAT; thisPoint: Point; thisNormal: Vector; thisSuccess: BOOL; bestType: TrajPartType ¬ none; trajHitData: TrajHitData; bestDist ¬ GGUtility.plusInfinity; IF NOT GGBoundBox.PointIsInGrownBox[testPoint, GGSliceOps.GetTightBox[sliceD.slice], tolerance] THEN RETURN; -- Bier, November 9, 1992 [thisDist, thisSegNum, thisCP, thisPoint, thisNormal, thisSuccess] ¬ GGTraj.NearestControlPoint[testPoint, sliceD, tolerance]; IF thisSuccess AND thisDist < bestDist THEN { bestType ¬ controlPoint; bestPoint ¬ thisPoint; bestNormal ¬ thisNormal; bestDist ¬ thisDist; bestSegNum ¬ thisSegNum; bestCP ¬ thisCP; success ¬ TRUE; }; [thisDist, thisJointNum, thisPoint, thisNormal, thisSuccess] ¬ GGTraj.NearestJoint[testPoint, sliceD, tolerance]; IF thisSuccess AND thisDist < bestDist THEN { bestType ¬ joint; bestPoint ¬ thisPoint; bestNormal ¬ thisNormal; bestDist ¬ thisDist; bestJointNum ¬ thisJointNum; success ¬ TRUE; }; IF success THEN { SELECT bestType FROM controlPoint => { hitData ¬ trajHitData ¬ NEW[TrajHitDataObj ¬ [ controlPoint, bestSegNum, bestCP, -1, bestPoint]]; }; joint => { hitData ¬ trajHitData ¬ NEW[TrajHitDataObj ¬ [ joint, -1, -1, bestJointNum, bestPoint]]; }; ENDCASE => ERROR; }; }; TrajClosestJointToHitData: PROC [sliceD: SliceDescriptor, mapPoint, testPoint: Point, hitData: REF ANY] RETURNS [jointD: SliceDescriptor, point: Point, normal: Vector ¬ [0,-1]] = { FindJointNormal: PROC [] RETURNS [normal: Vector] = { seg1, seg2: Segment; normal1, normal2, tangent1, tangent2, direction: Vector; IF GGTraj.HiSegment[traj] < jointNum THEN seg2 ¬ IF trajData.role = open THEN NIL ELSE GGTraj.FetchSegment[traj, 0] ELSE seg2 ¬ GGTraj.FetchSegment[traj, jointNum]; seg1 ¬ GGTraj.PreviousSegment[traj, jointNum]; IF seg1 # NIL AND seg2 # NIL THEN { [normal2, tangent2] ¬ seg2.class.jointNormal[seg2, point, testPoint, FALSE]; [normal1, tangent1] ¬ seg1.class.jointNormal[seg1, point, testPoint, TRUE]; direction ¬ Vectors2d.VectorFromPoints[point, testPoint]; normal ¬ IF ABS[Vectors2d.SmallestAngleBetweenVectors[tangent1, direction]] < ABS[Vectors2d.SmallestAngleBetweenVectors[tangent2, direction]] THEN normal1 ELSE normal2; } ELSE { IF seg1 # NIL THEN [normal, ----] ¬ seg1.class.jointNormal[seg1, point, testPoint, TRUE]; IF seg2 # NIL THEN [normal, ----] ¬ seg2.class.jointNormal[seg2, point, testPoint, FALSE]; }; }; hitType: HitType; segNum, cpNum, jointNum: INT; hitPoint: Point; jointParts: TrajParts; traj: Slice ¬ sliceD.slice; trajData: TrajData ¬ NARROW[traj.data]; [hitType, segNum, cpNum, jointNum, hitPoint] ¬ GGTraj.UnpackHitData[hitData]; SELECT hitType FROM interior => { jointDist, cpDist: REAL; bestControlPoint, bestSeg: NAT; bestPoint, bestCPPoint: Point; bestNormal: Vector; jointSuccess, cpSuccess: BOOL ¬ FALSE; [jointDist, jointNum, bestPoint, bestNormal, jointSuccess] ¬ GGTraj.NearestJoint[mapPoint, sliceD, 9999.0]; [cpDist, bestSeg, bestControlPoint, bestCPPoint, bestNormal, cpSuccess] ¬ GGTraj.NearestControlPoint[mapPoint, sliceD, 9999.0]; IF jointSuccess THEN { IF cpSuccess THEN { IF cpDist < jointDist THEN { seg: Segment; jointParts ¬ GGSequence.CreateFromControlPoint[trajData, bestSeg, bestControlPoint]; point ¬ bestCPPoint; seg ¬ GGTraj.FetchSegment[traj, bestSeg]; normal ¬ seg.class.cPNormal[seg, bestControlPoint, bestCPPoint, testPoint]; } ELSE { jointParts ¬ GGSequence.CreateFromJoint[trajData, jointNum]; point ¬ bestPoint; normal ¬ FindJointNormal[]; }; } ELSE { jointParts ¬ GGSequence.CreateFromJoint[trajData, jointNum]; point ¬ bestPoint; normal ¬ FindJointNormal[]; }; } ELSE ERROR; -- no joints and no control points }; joint => { jointParts ¬ GGSequence.CreateFromJoint[trajData, jointNum]; point ¬ mapPoint; normal ¬ FindJointNormal[]; }; controlPoint => { seg: Segment; jointParts ¬ GGSequence.CreateFromControlPoint[trajData, segNum, cpNum]; point ¬ mapPoint; seg ¬ GGTraj.FetchSegment[traj, segNum]; normal ¬ seg.class.cPNormal[seg, cpNum, point, testPoint]; }; segment => { NearestJointToHitData: PROC [] RETURNS [jointNum: NAT] = { nextNum: NAT; p1, p2: Point; d1, d2: REAL; trajHitData: TrajHitData ¬ NARROW[hitData]; SELECT trajHitData.hitType FROM joint => { jointNum ¬ trajHitData.jointNum; }; segment, controlPoint => { nextNum ¬ GGTraj.FollowingJoint[traj, trajHitData.segNum]; p1 ¬ GGTraj.FetchJointPos[traj, trajHitData.segNum]; p2 ¬ GGTraj.FetchJointPos[traj, nextNum]; d1 ¬ Vectors2d.DistanceSquared[p1, trajHitData.hitPoint]; d2 ¬ Vectors2d.DistanceSquared[p2, trajHitData.hitPoint]; IF d1 <= d2 THEN jointNum ¬ trajHitData.segNum ELSE jointNum ¬ nextNum; }; ENDCASE => ERROR; }; success: BOOL ¬ FALSE; jointPoint, cpPoint: Point; cpNormal: Vector; seg: Segment; seg ¬ GGTraj.FetchSegment[traj, segNum]; jointNum ¬ NearestJointToHitData[]; jointPoint ¬ GGTraj.FetchJointPos[traj, jointNum]; [cpPoint, cpNormal, cpNum, success] ¬ seg.class.closestControlPoint[seg, mapPoint, GGUtility.plusInfinity]; IF NOT success THEN { -- no control point found. Use a joint. jointParts ¬ GGSequence.CreateFromJoint[trajData, jointNum]; point ¬ jointPoint; normal ¬ FindJointNormal[]; } ELSE { -- could be a cp instead of a joint cpDist: REAL ¬ Vectors2d.DistanceSquared[cpPoint, mapPoint]; jointDist: REAL ¬ Vectors2d.DistanceSquared[jointPoint, mapPoint]; tisAJoint: BOOL ¬ jointDist <= cpDist; IF tisAJoint THEN { jointParts ¬ GGSequence.CreateFromJoint[trajData, jointNum]; point ¬ jointPoint; normal ¬ FindJointNormal[]; } ELSE { jointParts ¬ GGSequence.CreateFromControlPoint[trajData, segNum, cpNum]; point ¬ cpPoint; normal ¬ cpNormal; }; }; }; ENDCASE => ERROR; jointD ¬ GGSlice.DescriptorFromParts[traj, jointParts]; }; TrajClosestPointAndTangent: PROC [sliceD: SliceDescriptor, testPoint: Point, tolerance: REAL] RETURNS [bestPoint: Point ¬ [0.0, 0.0], bestDist: REAL ¬ 0.0, tangent: Vector ¬ [0,-1], hitData: REF ANY, success: BOOL ¬ FALSE] = { }; TrajClosestSegment: PROC [sliceD: SliceDescriptor, testPoint: Point, tolerance: REAL] RETURNS [bestPoint: Point, bestDist: REAL, bestNormal: Vector ¬ [0,-1], hitData: REF ANY, success: BOOL ¬ FALSE] = { bestSegNum: NAT; trajHitData: TrajHitData; [bestDist, bestSegNum, bestPoint, bestNormal, success] ¬ GGTraj.NearestSegment[testPoint, sliceD, tolerance]; IF success THEN { hitData ¬ trajHitData ¬ NEW[TrajHitDataObj ¬ [ segment, bestSegNum, -1, -1, bestPoint]]; }; }; InOutOn: TYPE = {in, out, on}; InsideContour: PROC [p: Point, pairs: GGSegmentTypes.PairSequence] RETURNS [BOOL] ~ { RETURN[HanrahanInsideContour[p, pairs] # out] }; HanrahanInsideContour: PROC [p: Point, pairs: GGSegmentTypes.PairSequence] RETURNS [InOutOn] ~ { zcross: REAL; odd: BOOL ¬ FALSE; d2: Point ¬ [pairs[pairs.length-1].x-p.x, pairs[pairs.length-1].y-p.y]; FOR n: NAT IN [0..pairs.length) DO d1: Point ¬ d2; d2 ¬ [pairs[n].x-p.x, pairs[n].y-p.y]; IF (d1.y > 0 AND d2.y > 0) OR (d1.y < 0 AND d2.y < 0) OR (d1.x < 0 AND d2.x < 0) THEN LOOP; -- no chance to cross IF (zcross ¬ d2.y*d1.x-d1.y*d2.x) = 0.0 THEN RETURN[on]; -- on the edge IF (d1.y > 0 OR d2.y > 0) AND (zcross < 0) # (d1.y-d2.y < 0) THEN odd ¬ NOT odd; ENDLOOP; RETURN[IF odd THEN in ELSE out]; }; PolylineOfTraj: PROC [trajData: TrajData, tolerance: REAL] RETURNS [wholePolyline: GGSegmentTypes.PairSequence, success: BOOL ¬ TRUE] = { polyList: LIST OF GGSegmentTypes.PairSequence; segCount: NAT ¬ 0; GetPolyline: PROC [traj: TrajData, seg: Segment, index: NAT] RETURNS [done: BOOL ¬ FALSE] = { thisPolyline: GGSegmentTypes.PairSequence ¬ seg.class.asPolyline[seg, tolerance]; segCount ¬ segCount+(thisPolyline.length-1); polyList ¬ CONS[thisPolyline, polyList]; }; [] ¬ GGSequence.WalkSegmentsInTraj[trajData, GetPolyline]; IF polyList = NIL THEN RETURN[NIL, FALSE] ELSE { index: NAT ¬ 0; success ¬ TRUE; wholePolyline ¬ NEW[GGSegmentTypes.PairSequenceRep[segCount]]; wholePolyline.length ¬ segCount; FOR list: LIST OF GGSegmentTypes.PairSequence ¬ polyList, list.rest UNTIL list = NIL DO FOR j: NAT DECREASING IN [1..list.first.length) DO wholePolyline[index] ¬ list.first[j]; index ¬ index + 1; ENDLOOP; ENDLOOP; }; }; TrajFilledPathsUnderPoint: PROC [slice: Slice, point: Point, tolerance: REAL] RETURNS [hitData: REF ANY ¬ NIL, moreHitDatas: LIST OF REF ANY ¬ NIL] = { trajData: TrajData ¬ NARROW[slice.data]; wholePolyline: GGSegmentTypes.PairSequence; success: BOOL ¬ TRUE; IF NOT GGBoundBox.PointIsInGrownBox[point, GGSliceOps.GetTightBox[slice], tolerance] THEN RETURN; IF trajData.role = open THEN RETURN; IF trajData.polyline # NIL AND trajData.polylineTolerance <= tolerance THEN wholePolyline ¬ trajData.polyline ELSE { [wholePolyline, success] ¬ PolylineOfTraj[trajData, tolerance]; IF NOT success THEN RETURN[NIL, NIL]; trajData.polyline ¬ wholePolyline; -- cache the polyline for later use trajData.polylineTolerance ¬ tolerance; }; IF InsideContour[point, wholePolyline] THEN { hitData ¬ NEW[TrajHitDataObj ¬ [interior, -1, -1, -1, point]]; }; }; TrajLineIntersection: PROC [sliceD: SliceDescriptor, line: Line] RETURNS [points: LIST OF Point, pointCount: NAT ¬ 0] = { segGen: SegmentGenerator; thesePoints: LIST OF Point; thisCount: NAT ¬ 0; trajParts: TrajParts ¬ NARROW[sliceD.parts]; trajData: TrajData ¬ NARROW[sliceD.slice.data]; segGen ¬ GGSequence.SegmentsInSequence[trajData, trajParts]; 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; }; TrajCircleIntersection: PROC [sliceD: SliceDescriptor, circle: Circle] RETURNS [points: LIST OF Point, pointCount: NAT ¬ 0] = { segGen: SegmentGenerator; thesePoints: LIST OF Point; thisCount: NAT ¬ 0; trajParts: TrajParts ¬ NARROW[sliceD.parts]; trajData: TrajData ¬ NARROW[sliceD.slice.data]; segGen ¬ GGSequence.SegmentsInSequence[trajData, trajParts]; 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; pointCount ¬ pointCount + thisCount; ENDLOOP; }; TrajHitDataAsSimpleCurve: PROC [slice: Slice, hitData: REF ANY] RETURNS [simpleCurve: REF ANY] = { trajHitData: TrajHitData ¬ NARROW[hitData]; SELECT trajHitData.hitType FROM joint, controlPoint, interior => RETURN[NIL]; segment => { segNum: INT ¬ trajHitData.segNum; seg: Segment ¬ GGTraj.FetchSegment[slice, segNum]; simpleCurve ¬ seg.class.asSimpleCurve[seg, trajHitData.hitPoint]; }; ENDCASE => ERROR; }; TrajSetDefaults: PROC [slice: Slice, parts: SliceParts, defaults: DefaultData, history: HistoryEvent] = { [] ¬ GGSliceOps.SetStrokeWidth[slice, parts, defaults.strokeWidth, NIL]; GGSliceOps.SetStrokeJoint[slice, parts, defaults.strokeJoint, NIL]; GGSliceOps.SetStrokeEnd[slice, parts, defaults.strokeEnd, NIL]; GGSliceOps.SetDashed[slice, parts, defaults.dashed, GGUtility.CopyPattern[defaults.pattern], defaults.offset, defaults.length, NIL]; GGSliceOps.SetStrokeColor[slice, parts, defaults.strokeColor, $Set, NIL]; }; TrajSetStrokeWidth: PROC [slice: Slice, parts: SliceParts, strokeWidth: REAL, history: HistoryEvent] RETURNS [box: BoundBox] = { bBoxes: LIST OF BoundBox; thisBox: BoundBox; trajParts: TrajParts ¬ NARROW[parts]; trajData: TrajData ¬ NARROW[slice.data]; segGen: SegmentGenerator ¬ IF parts#NIL THEN GGSequence.SegmentsInSequence[trajData, trajParts] ELSE GGSequence.SegmentsInTraj[trajData]; FOR seg: Segment ¬ GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen] UNTIL seg = NIL DO seg.class.setStrokeWidth[seg, strokeWidth]; thisBox ¬ seg.class.boundBox[seg]; IF thisBox#NIL THEN bBoxes ¬ CONS[thisBox, bBoxes]; ENDLOOP; box ¬ GGBoundBox.BoundBoxOfBoxes[bBoxes]; GGSlice.KillBoundBoxOnly[slice]; }; TrajGetStrokeWidth: PROC [slice: Slice, parts: SliceParts] RETURNS [strokeWidth: REAL ¬ -1.0, isUnique: BOOL ¬ TRUE] = { trajParts: TrajParts ¬ NARROW[parts]; trajData: TrajData ¬ NARROW[slice.data]; found, aborted: BOOL ¬ FALSE; IF parts = NIL OR GGSequence.CountSegmentsInSequence[trajData, trajParts] > 0 THEN { DoCheckWidth: PROC [traj: TrajData, seg: Segment, index: NAT] RETURNS [done: BOOL ¬ FALSE] = { IF found THEN { IF seg.strokeWidth # strokeWidth THEN done ¬ TRUE; } ELSE { found ¬ TRUE; strokeWidth ¬ seg.strokeWidth; }; }; IF parts#NIL THEN aborted ¬ GGSequence.WalkSegmentsInSequence[trajData, trajParts, DoCheckWidth] ELSE aborted ¬ GGSequence.WalkSegmentsInTraj[trajData, DoCheckWidth]; isUnique ¬ NOT aborted; } ELSE { isUnique ¬ FALSE; }; }; TrajSetStrokeEnd: PROC [slice: Slice, parts: SliceParts, strokeEnd: StrokeEnd, history: HistoryEvent] = { trajParts: TrajParts ¬ NARROW[parts]; trajData: TrajData ¬ NARROW[slice.data]; segGen: SegmentGenerator ¬ IF parts#NIL THEN GGSequence.SegmentsInSequence[trajData, trajParts] ELSE GGSequence.SegmentsInTraj[trajData]; FOR seg: Segment ¬ GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen] UNTIL seg = NIL DO seg.strokeEnd ¬ strokeEnd; ENDLOOP; }; TrajGetStrokeEnd: PROC [slice: Slice, parts: SliceParts] RETURNS [strokeEnd: StrokeEnd ¬ round, isUnique: BOOL ¬ TRUE] = { trajParts: TrajParts ¬ NARROW[parts]; trajData: TrajData ¬ NARROW[slice.data]; found, aborted: BOOL ¬ FALSE; IF parts = NIL OR GGSequence.CountSegmentsInSequence[trajData, trajParts] > 0 THEN { DoCheckEnd: PROC [traj: TrajData, seg: Segment, index: NAT] RETURNS [done: BOOL ¬ FALSE] = { IF found THEN { IF seg.strokeEnd # strokeEnd THEN done ¬ TRUE; } ELSE { found ¬ TRUE; strokeEnd ¬ seg.strokeEnd; }; }; IF parts#NIL THEN aborted ¬ GGSequence.WalkSegmentsInSequence[trajData, trajParts, DoCheckEnd] ELSE aborted ¬ GGSequence.WalkSegmentsInTraj[trajData, DoCheckEnd]; isUnique ¬ NOT aborted; } ELSE { isUnique ¬ FALSE; }; }; TrajSetStrokeJoint: PROC [slice: Slice, parts: SliceParts, strokeJoint: StrokeJoint, history: HistoryEvent] = { trajData: TrajData ¬ NARROW[slice.data]; trajData.strokeJoint ¬ strokeJoint; }; TrajGetStrokeJoint: PROC [slice: Slice, parts: SliceParts] RETURNS [strokeJoint: StrokeJoint, isUnique: BOOL ¬ TRUE] = { trajData: TrajData ¬ NARROW[slice.data]; RETURN[trajData.strokeJoint]; }; TrajSetStrokeColor: PROC [slice: Slice, parts: SliceParts, color: Color, setHow: ATOM, history: HistoryEvent] = { trajParts: TrajParts ¬ NARROW[parts]; trajData: TrajData ¬ NARROW[slice.data]; segGen: SegmentGenerator ¬ IF parts#NIL THEN GGSequence.SegmentsInSequence[trajData, trajParts] ELSE GGSequence.SegmentsInTraj[trajData]; FOR seg: Segment ¬ GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen] UNTIL seg = NIL DO SELECT setHow FROM $Set => seg.color ¬ color; $ChangeHue => { newColor: Color ¬ GGUtility.ChangeHue[seg.color, color]; seg.color ¬ newColor; }; ENDCASE => ERROR; ENDLOOP; }; TrajGetStrokeColor: PROC [slice: Slice, parts: SliceParts] RETURNS [color: Color, isUnique: BOOL ¬ TRUE] = { trajParts: TrajParts ¬ NARROW[parts]; trajData: TrajData ¬ NARROW[slice.data]; found, aborted: BOOL ¬ FALSE; IF parts = NIL OR GGSequence.CountSegmentsInSequence[trajData, trajParts] > 0 THEN { DoCheckColor: PROC [traj: TrajData, seg: Segment, index: NAT] RETURNS [done: BOOL ¬ FALSE] = { IF found THEN { IF NOT GGCoreOps.EquivalentColors[seg.color, color] THEN done ¬ TRUE; } ELSE { found ¬ TRUE; color ¬ seg.color; }; }; IF parts#NIL THEN aborted ¬ GGSequence.WalkSegmentsInSequence[trajData, trajParts, DoCheckColor] ELSE aborted ¬ GGSequence.WalkSegmentsInTraj[trajData, DoCheckColor]; isUnique ¬ NOT aborted; } ELSE { success: BOOL ¬ FALSE; partType: TrajPartType; jointNum: NAT; seg: Segment; IF trajParts.jointCount > 0 THEN { [success, partType, ----, ----, jointNum, ----, ----, seg, ----] ¬ GGSequence.UnpackSimpleSequence[slice, parts]; IF success THEN { SELECT partType FROM joint => { IF trajData.segCount > 0 THEN { IF jointNum <= GGTraj.HiSegmentTraj[trajData] THEN color ¬ GGTraj.FetchSegmentTraj[trajData, jointNum].color ELSE color ¬ GGTraj.FetchSegmentTraj[trajData, jointNum-1].color; }; }; controlPoint, segment => color ¬ seg.color; ENDCASE; }; }; }; }; TrajSetArrows: PROC [slice: Slice, parts: SliceParts, leftDown, rightUp: BOOL, history: HistoryEvent] = { trajData: TrajData ¬ NARROW[slice.data]; trajData.loArrow ¬ leftDown; trajData.hiArrow ¬ rightUp; }; TrajGetArrows: PROC [slice: Slice] RETURNS [leftDown, rightUp: BOOL ¬ FALSE] = { trajData: TrajData ¬ NARROW[slice.data]; RETURN[trajData.loArrow, trajData.hiArrow]; }; TrajSetDashed: PROC [slice: Slice, parts: SliceParts, dashed: BOOL, pattern: SequenceOfReal ¬ NIL, offset: REAL ¬ 0.0, length: REAL ¬ -1.0, history: HistoryEvent] = { trajParts: TrajParts ¬ NARROW[parts]; trajData: TrajData ¬ NARROW[slice.data]; segGen: SegmentGenerator ¬ IF parts#NIL THEN GGSequence.SegmentsInSequence[trajData, trajParts] ELSE GGSequence.SegmentsInTraj[trajData]; FOR seg: Segment ¬ GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen] UNTIL seg = NIL DO seg.dashed ¬ dashed; seg.pattern ¬ pattern; seg.offset ¬ offset; seg.length ¬ length; ENDLOOP; }; TrajGetDashed: PROC [slice: Slice, parts: SliceParts] RETURNS [dashed: BOOL ¬ FALSE, pattern: SequenceOfReal, offset, length: REAL ¬ 0.0, isUnique: BOOL ¬ TRUE] = { trajParts: TrajParts ¬ NARROW[parts]; trajData: TrajData ¬ NARROW[slice.data]; segCount: NAT ¬ GGSequence.CountSegmentsInSequence[trajData, trajParts]; found, aborted: BOOL ¬ FALSE; IF segCount > 0 THEN { DoCheckDashes: PROC [traj: TrajData, seg: Segment, index: NAT] RETURNS [done: BOOL ¬ FALSE] = { IF found THEN done ¬ seg.dashed # dashed OR (seg.dashed AND (seg.offset # offset OR seg.length # length OR NOT GGUtility.EquivalentPatterns[seg.pattern, pattern])) ELSE { found ¬ TRUE; dashed ¬ seg.dashed; offset ¬ seg.offset; length ¬ seg.length; pattern ¬ seg.pattern; }; }; IF parts#NIL THEN aborted ¬ GGSequence.WalkSegmentsInSequence[trajData, trajParts, DoCheckDashes] ELSE aborted ¬ GGSequence.WalkSegmentsInTraj[trajData, DoCheckDashes]; isUnique ¬ NOT aborted; } ELSE RETURN[FALSE, NIL, 0.0, 0.0, FALSE]; }; TrajSetOrientation: PROC [slice: Slice, parts: SliceParts, orientation: Orientation, history: HistoryEvent] RETURNS [success: BOOL ¬ TRUE] = { trajData: TrajData ¬ NARROW[slice.data]; SELECT orientation FROM cw => { IF NOT GGTraj.IsClockwiseTraj[slice] THEN GGTraj.ReverseTraj[slice]; trajData.forward ¬ TRUE; -- trajData.forward MEANS IsClockwiseTraj }; ccw => { IF GGTraj.IsClockwiseTraj[slice] THEN GGTraj.ReverseTraj[slice]; trajData.forward ¬ FALSE; -- trajData.forward MEANS IsClockwiseTraj }; reverse => { GGTraj.ReverseTraj[slice]; trajData.forward ¬ NOT trajData.forward; }; ENDCASE => ERROR; }; TrajGetOrientation: PROC [slice: Slice, parts: SliceParts] RETURNS [orientation: Orientation, isUnique: BOOL ¬ TRUE] = { trajData: TrajData ¬ NARROW[slice.data]; RETURN[IF trajData.forward THEN cw ELSE ccw]; }; FetchSliceClass: PUBLIC PROC [type: ATOM] RETURNS [class: SliceClass] = { FOR l: LIST OF SliceClassDef ¬ sliceClasses, l.rest UNTIL l=NIL DO IF l.first.type=type THEN RETURN[l.first.class]; ENDLOOP; SIGNAL Problem[msg: "Slice class not found."]; RETURN[NIL]; }; RegisterSliceClass: PUBLIC PROC [class: SliceClass] = { classDef: SliceClassDef ¬ NEW[SliceClassDefObj ¬ [type: class.type, class: class]]; sliceClasses ¬ CONS[classDef, sliceClasses]; }; DescriptorFromParts: PUBLIC PROC [slice: Slice, parts: SliceParts] RETURNS [sliceD: SliceDescriptor] = { sliceD ¬ NEW[SliceDescriptorObj ¬ [slice, parts]]; IF testing THEN masterSDlist ¬ CONS[sliceD, masterSDlist]; -- kludge for testing }; testing: BOOL ¬ FALSE; masterSDlist: LIST OF SliceDescriptor; -- kludge for testing UnlinkAllSliceDescriptors: PROC [list: LIST OF SliceDescriptor] = { FOR sdList: LIST OF SliceDescriptor ¬ masterSDlist, sdList.rest UNTIL sdList=NIL DO IF sdList.first.slice=NIL THEN LOOP; IF sdList.first.slice.data#NIL THEN GGSlice.UnlinkSlice[sdList.first.slice]; sdList.first.slice ¬ NIL; ENDLOOP; masterSDlist ¬ NIL; -- drop the list pointers. All SD should be collectible }; UnlinkSlice: PUBLIC PROC [slice: Slice] = { IF slice#NIL AND slice.data#NIL THEN { -- may have already been unlinked IF slice.normalSelectedParts#NIL THEN slice.normalSelectedParts.slice ¬ NIL; IF slice.hotSelectedParts#NIL THEN slice.hotSelectedParts.slice ¬ NIL; IF slice.activeSelectedParts#NIL THEN slice.activeSelectedParts.slice ¬ NIL; IF slice.matchSelectedParts#NIL THEN slice.matchSelectedParts.slice ¬ NIL; IF slice.nullDescriptor#NIL THEN slice.nullDescriptor.slice ¬ NIL; IF slice.fullDescriptor#NIL THEN slice.fullDescriptor.slice ¬ NIL; slice.normalSelectedParts ¬ slice.hotSelectedParts ¬ slice.activeSelectedParts ¬ slice.matchSelectedParts ¬ slice.nullDescriptor ¬ slice.fullDescriptor ¬ NIL; slice.class ¬ NIL; slice.data ¬ NIL; slice.parent ¬ NIL; slice.boundBox ¬ NIL; slice.tightBox ¬ NIL; }; }; EntitiesInSlice: PUBLIC PROC [slice: Slice] RETURNS [entityGenerator: SliceGenerator] = { SIGNAL Problem[msg: "GGSlice.EntitiesInSlice NYI"]; }; Init: PROC [] = { textDef: SliceClassDef ¬ NEW[SliceClassDefObj ¬ [type: $Text, class: GGSlice.BuildTextSliceClass[]]]; ipDef: SliceClassDef ¬ NEW[SliceClassDefObj ¬ [type: $IP, class: GGSlice.BuildIPSliceClass[]]]; boxDef: SliceClassDef ¬ NEW[SliceClassDefObj ¬ [type: $Box, class: GGSlice.BuildBoxSliceClass[]]]; circleDef: SliceClassDef ¬ NEW[SliceClassDefObj ¬ [type: $Circle, class: GGSlice.BuildCircleSliceClass[]]]; outlineDef: SliceClassDef ¬ NEW[SliceClassDefObj ¬ [type: $Outline, class: GGSlice.BuildOutlineSliceClass[]]]; clusterDef: SliceClassDef ¬ NEW[SliceClassDefObj ¬ [type: $Cluster, class: GGSlice.BuildClusterSliceClass[]]]; trajDef: SliceClassDef ¬ NEW[SliceClassDefObj ¬ [type: $Traj, class: GGSlice.BuildTrajSliceClass[]]]; sliceClasses ¬ LIST[trajDef, outlineDef, clusterDef, circleDef, boxDef, ipDef, textDef]; printPrefix ¬ Atom.MakeAtom["xerox/pressfonts/"]; screenPrefix ¬Atom.MakeAtom ["xerox/tiogafonts/"]; }; sliceClasses: LIST OF SliceClassDef; printPrefix: ATOM; screenPrefix: ATOM; Init[]; END. ς GGSliceImplE.mesa Contents: Implements some of the Traj slice class (see GGSliceImplD for more). Copyright Σ 1987, 1988, 1990, 1992 by Xerox Corporation. All rights reserved. Created By: Ken Pier, December 3, 1987 3:25:32 pm PST Bier, December 3, 1992 5:38 pm PST Last Edited By: Ken Pier, September 20, 1990 10:33 am PDT Doug Wyatt, April 14, 1992 2:39 pm PDT Textual Description Part Generators Hit Testing Style Textual Description GGModelTypes.SliceDescribeProc GGModelTypes.SliceFileoutProc Properties of the whole trajectory. f.PutF["Traj (%g) [%g] arrows: %g ", [rope[roleRope]], [integer[hiJoint]], [integer[arrowCode]]]; Version 8802.04 eliminated "Traj" from output individual segments the same ?? Now fileout the segment data. GGModelTypes.SliceFileinProc Traj (fence) [3] arrows: 0: Now read the Segment data. fake it with a closed arc segment Part Generators GGModelTypes.SliceWalkSegmentsProc pointAndDone.point _ FetchJointPosTraj[pgd.jointGen.seq.traj, nextJoint]; Time to look at the control points. Hit Testing GGModelTypes.SliceClosestPointProc GGModelTypes.SliceClosestJointToHitDataProc GGModelTypes.SliceClosestSegmentProc RETURN[BoysePointInPoly[p, pairs] # out]; Based on code from Pat Hanrahan: Since trajectories don't have a fill color (outlines do), we will answer the question as though trajectories were always filled. simpleCurve will be of type Edge, Arc, etc. There will be Conic and Bezier types as well. Style maybe should implement parts=NIL to mean all parts this is inefficient but easy to implement No need to kill tight box. It is still good. strokeWidth _ -1.0; strokeEnd _ round; Will there be an arrowhead on the lo end of traj? On the hi end? Class registration UpdateDescriptorBoundBoxes: PUBLIC PROC [sliceD: SliceDescriptor] = { Slice classes other than outlines don't use descriptor bound boxes !! IF GGSliceOps.GetType[sliceD.slice]=$Outline THEN { parts: OutlineParts _ NARROW[sliceD.parts]; FOR list: LIST OF Sequence _ parts.descriptors, list.rest UNTIL list = NIL DO IF list.first # NIL THEN GGSequence.UpdateBoundBox[list.first]; ENDLOOP; }; }; For use ONLY to destroy REFs in slices so that Cedar garbage collector can collect storage. Only for wizards. For use ONLY to destroy REFs in slices so that Cedar garbage collector can collect storage. Only for the initiated. break links from selected slice descriptors to slices break link from slices to selected slice descriptors NIL out other REFs for garbage collection efficiency Class-Specific Routines for Traj Κ-–(cedarcode) style•NewlineDelimiter ™codešœ™KšΟbœF™NKšœ ΟeœC™NK™5K™"K™9K™&K™—šΟk ˜ Kšœ™˜™K˜—šΟn œŸœŸ˜KšŸœ™˜ KšœŸœ Ÿ˜K˜Kšœ Ÿœ˜)Kšœ Ÿœ˜&Kšœ Ÿœ˜,KšœŸœ˜#KšœŸœ˜#KšœŸœ˜KšœŸœ&˜AKšœŸœ˜Kšœ Ÿœ˜-KšœŸœ˜KšœŸœ ˜5Kšœ Ÿœ˜+KšœŸœ/˜KKšœ Ÿœ ˜1Kšœ Ÿœ˜*IprocšœŸœ˜1KšœŸœ˜'KšœŸœ˜1Kšœ Ÿœ˜*KšœŸœ˜#KšœŸœ˜3Kšœ Ÿœ˜)KšœŸœ˜KšœŸœ˜Kšœ Ÿœ˜-KšœŸœ˜!KšœŸœ˜/KšœŸœ˜3KšœŸœ"˜9KšœŸœ!˜7KšœŸœ#˜;KšœŸœ&˜AKšœŸœ˜1KšœŸœ˜!Kšœ Ÿœ˜+KšœŸœ˜+KšœŸœ˜1Kšœ Ÿœ˜'KšœŸœ˜1KšœŸœ!˜7KšœŸœ$˜=KšœŸœ#˜;KšœŸœ!˜5Kšœ Ÿœ˜+Kšœ Ÿœ˜'KšœŸœ˜2KšœŸœ˜!KšœŸœ"˜9Kšœ Ÿœ˜+KšœŸœ ˜5KšœŸœ#˜;KšœŸœ˜3Kšœ Ÿœ˜'Kšœ Ÿœ˜+Kšœ Ÿœ˜)Kšœ Ÿœ˜-KšŸœ˜KšœŸœœ˜'KšœŸœœ˜%Kš Ÿœ˜'KšŸœ˜-Kš Ÿœ˜)KšŸœ˜/KšœŸœ˜/KšœŸœ˜3Kšœ Ÿœ˜/KšœŸœ˜#Kšœ Ÿœ˜'K˜KšœŸœŸœ˜+KšœŸœŸœŸœ˜?—K˜Kš œŸœŸœ Ÿœ˜;K˜š œŸœŸœ˜Lšœ:˜:L˜—L˜—Lšœ˜Lšœ$˜$L˜L˜+Lšœ ˜ LšŸœ˜—šŸœŸœŸœ˜9L˜*L˜*LšŸœ Ÿœ$˜4LšŸœ@˜DšŸœŸœŸœ˜Kšœ,˜,Kšœ ŸœŸœ˜Kšœ˜—šŸœŸœ Ÿœ˜Lšœ$˜$Lšœ ŸœŸœ˜L˜—šŸœŸœŸœ˜Lšœ/ŸœŸœ˜6šŸœ Ÿœ˜Lšœ7ŸœŸœ˜>Lšœ:˜:L˜—L˜—Lšœ˜Lšœ$˜$L˜L˜—K˜Kšœ*˜*K˜K™—š   œŸœŸœŸœ Ÿœ%Ÿœ˜lKšœ™Lšœ ŸœŸœ˜LšœŸœ˜L˜LšœŸœ˜L˜Lšœ˜L˜ Lšœ˜LšœŸœ˜"L˜L˜L˜L˜šœ8Ÿœ˜=Lšœ™—LšŸœŸœ ‘˜VL˜L˜!L˜L˜L˜L˜L˜šŸœŸœ˜L˜!L˜!L˜—LšŸœ˜šŸœŸœ˜L˜L˜+L˜L˜L˜'Lšœ˜—šŸœ˜L˜Lšœ Ÿœ˜Lšœ˜—šŸœŸœ˜L˜L˜$L˜L˜L˜ L˜.L˜—šŸœ˜L˜Lšœ Ÿœ˜Lšœ Ÿœ˜L˜—šŸœ˜šŸœ˜L˜L˜šŸœŸœ˜L˜šŸœŸœ˜L˜'L˜L˜L˜—L˜—L˜—šŸœ˜Lšœ Ÿœ˜Lšœ Ÿœ˜L˜L˜——Lšœ™L˜%L˜Lšœ Ÿœ ˜L˜#šŸœŸœŸœŸ˜!L˜L˜"LšŸœŸœ Ÿœ%˜8šŸœŸœ˜KšŸœŸœŸœ(˜:Kšœ˜—KšŸœ˜šŸœŸœ˜LšŸœŸœ Ÿœ/˜BL˜—LšŸœ˜ šŸœŸœŸœ˜L˜šŸœŸœ˜L˜'L˜L˜L˜—L˜—L˜>L˜/šŸœŸœ˜LšœŸœŸœŸœ˜Lšœ#‘A˜dš ŸœŸœŸœŸœŸœŸœŸ˜>Lšœ Ÿœ˜(LšŸœ˜—L˜—L˜L˜L˜ LšœŸœ˜(L˜L˜L˜L˜L˜L˜L˜šŸœŸœŸœ˜0K™!K˜)K˜/K˜,KšœTŸœ˜^Kšœ$˜$Kšœ Ÿœ˜K˜—LšŸœ1˜5LšŸœŸœ ŸœŸœ˜L˜LšŸœ˜—šŸœŸœŸœ˜'L˜L˜"LšŸœŸœ Ÿœ%˜8šŸœŸœ˜KšŸœŸœŸœ(˜:Kšœ˜—KšŸœ˜šŸœŸœ˜LšŸœŸœ Ÿœ/˜BL˜—LšŸœ˜ šŸœŸœŸœ˜L˜šŸœŸœ˜L˜'L˜L˜L˜—L˜—L˜>L˜+šŸœŸœ˜LšœŸœŸœŸœ˜Lšœ#‘A˜dš ŸœŸœŸœŸœŸœŸœŸ˜>Lšœ Ÿœ˜(LšŸœ˜—L˜—L˜L˜L˜L˜L˜L˜L˜L˜šŸœŸœ‘3˜QK˜0LšŸœŸœ ŸœŸœ˜Kšœ$˜$Kšœ˜—KšŸœ)˜-L˜—šŸœŸœ‘>˜YK˜L˜K˜—KšŸœŸœ˜K˜KšœŸœ ŸœŸœ‘˜SK˜K™—š  œŸœŸœŸœ˜JLšŸœŸœŸ˜L˜.L˜,L˜,LšŸœŸœ˜L˜L˜—š  œŸœŸœŸœ˜HLšŸœŸ˜L˜L˜L˜LšŸœŸœ˜L˜—K™šœ™K™—KšœŸœŸœ˜=šœŸœŸœ˜*Kšœ Ÿœ˜K˜Kšœ)˜)Kšœ˜K˜—KšœŸœŸœ˜EšœŸœŸœ˜.K˜Kšœ˜—K˜š œŸœŸœ˜]KšœŸœ˜/KšœŸœ˜,Kšœ˜K˜Kšœ˜K˜2K˜@KšœŸœŸœ˜@Kšœ Ÿœ*˜8K˜K˜—š œŸœ7˜WKšœŸœ˜,KšœŸœ˜/Kšœ?˜?KšœF˜FK˜K˜—š œŸœŸœ'˜iKšœŸœ˜/KšœŸœ˜,Kšœ ˜ K˜K˜K˜ š ŸœŸœŸœ3ŸœŸœŸ˜WšŸœŸœŸ œŸ˜2K˜%K˜KšŸœ˜—KšŸœ˜—K˜—K˜K˜—š œŸœ)ŸœŸœ ŸœŸœŸœŸœŸœŸœŸœŸœ˜—K™€KšœŸœ ˜(Kšœ+˜+Kšœ ŸœŸœ˜K˜šŸœŸœN˜TKšŸœŸœ˜ —šŸœŸœŸœ˜$K˜—šŸœŸœŸœ(˜FKšŸœ"˜&šŸœ˜K˜?Kš ŸœŸœ ŸœŸœŸœŸœ˜%Kšœ#‘#˜FK˜'K˜——šŸœ%Ÿœ˜-Kšœ Ÿœ1˜>K˜—K˜K˜—š  œŸœ'Ÿœ ŸœŸœŸœ ˜zKšœ˜Kšœ ŸœŸœ˜Kšœ Ÿœ˜KšœŸœ˜,KšœŸœ˜/K˜<šŸœOŸœŸœŸ˜dK˜Aš ŸœŸœŸœ ŸœŸœŸ˜DKšœ Ÿœ˜"KšŸœ˜—K˜$KšŸœ˜—K˜K˜—š  œŸœ+Ÿœ ŸœŸœŸœ ˜€Kšœ˜Kšœ ŸœŸœ˜Kšœ Ÿœ˜KšœŸœ˜,KšœŸœ˜/K˜<šŸœOŸœŸœŸ˜dK˜Eš ŸœŸœŸœ ŸœŸœŸ˜DKšœ Ÿœ˜"KšŸœ˜—K˜$KšŸœ˜—K˜K˜—š œŸœŸœŸœŸœŸœŸœ˜bKšœZ™ZKšœŸœ ˜+šŸœŸ˜Kšœ!ŸœŸœ˜-šœ ˜ KšœŸœ˜!K˜2K˜AK˜—KšŸœŸœ˜—K˜—K˜šœ™K™—š œŸœT˜iKšΠbk™2Kš)™)KšœCŸœ˜HKšœ>Ÿœ˜CKšœ:Ÿœ˜?KšœŸœ˜„KšœDŸœ˜IKšœ˜K˜—šΠbnœŸœ0ŸœŸœ˜€KšœŸœŸœ ˜Kšœ˜KšœŸœ˜%KšœŸœ ˜(Kš œŸœŸœŸœ4Ÿœ%˜‰šŸœOŸœŸœŸ˜dKšœ+˜+K˜"KšŸœ ŸœŸœ Ÿœ˜3KšŸœ˜—K˜)šœ ˜ K™-—K˜K˜—š  œŸœ#ŸœŸœŸœŸœ˜xKšœŸœ˜%KšœŸœ ˜(KšœŸœŸœ˜šŸœ ŸœŸœ=Ÿœ˜Tš   œŸœ'ŸœŸœŸœŸœ˜^šŸœŸœ˜KšŸœŸœŸœ˜2K˜—šŸœ˜KšœŸœ˜ K˜K˜—K˜—šŸœŸ˜ KšŸœO˜SKšŸœA˜E—Kšœ Ÿœ ˜K˜—šŸœ˜Kšœ™Kšœ Ÿœ˜K˜—K˜K˜—š£ œŸœS˜iKšœŸœ˜%KšœŸœ ˜(Kš œŸœŸœŸœ4Ÿœ%˜‰šŸœOŸœŸœŸ˜dK˜KšŸœ˜—K˜K˜—š £ œŸœ#Ÿœ*ŸœŸœ˜zKšœŸœ˜%KšœŸœ ˜(KšœŸœŸœ˜šŸœ ŸœŸœ=Ÿœ˜Tš   œŸœ'ŸœŸœŸœŸœ˜\šŸœŸœ˜KšŸœŸœŸœ˜.K˜—šŸœ˜KšœŸœ˜ K˜K˜—K˜—šŸœŸ˜ KšŸœB  œ˜QKšŸœ3  œ˜C—Kšœ Ÿœ ˜K˜—šŸœ˜Kšœ™Kšœ Ÿœ˜K˜—K˜K˜—š£œŸœW˜oKšœŸœ ˜(K˜#K˜K˜—š £œŸœ#Ÿœ&ŸœŸœ˜xKšœŸœ ˜(KšŸœ˜K˜K˜—š£œŸœ9Ÿœ˜qKšœŸœ˜%KšœŸœ ˜(Kš œŸœŸœŸœ4Ÿœ%˜‰šŸœOŸœŸœŸ˜dšŸœŸ˜K˜˜K˜8K˜K˜—KšŸœŸœ˜—KšŸœ˜—K˜K˜—š £œŸœ#ŸœŸœŸœ˜lKšœŸœ˜%KšœŸœ ˜(KšœŸœŸœ˜šŸœ ŸœŸœ=Ÿœ˜Tš   œŸœ'ŸœŸœŸœŸœ˜^šŸœŸœ˜KšŸœŸœ.ŸœŸœ˜EK˜—šŸœ˜KšœŸœ˜ K˜K˜—K˜—šŸœŸ˜ KšŸœO˜SKšŸœA˜E—Kšœ Ÿœ ˜K˜—šŸœŸ˜Kšœ ŸœŸœ˜Kšœ˜Kšœ Ÿœ˜K˜ šŸœŸœ˜"K˜qšŸœ Ÿœ˜šŸœ Ÿ˜šœ ˜ šŸœŸœ˜KšŸœ,Ÿœ:˜lKšŸœ=˜AK˜—Kšœ˜—K˜+KšŸœ˜—K˜—K˜—K˜—K˜K˜—š œŸœ6Ÿœ˜iK™AKšœŸœ ˜(K˜K˜K˜K˜—š  œŸœŸœŸœŸœ˜PKšœŸœ ˜(KšŸœ%˜+K˜K™—š £ œŸœ+ŸœŸœ ŸœŸœ#˜¦KšœŸœ˜%KšœŸœ ˜(Kš œŸœŸœŸœ4Ÿœ%˜‰šŸœOŸœŸœŸ˜dK˜K˜K˜K˜KšŸœ˜—K˜K˜—š£  œŸœ#Ÿœ ŸœŸœ+ŸœŸœŸœ˜€KšœŸœ˜%KšœŸœ ˜(Kšœ Ÿœ;˜HKšœŸœŸœ˜šŸœŸœ˜š   œŸœ'ŸœŸœŸœŸœ˜_šŸœŸ˜ ˜KšœŸœ Ÿ˜&KšœŸ˜KšœŸ˜KšŸœ5˜8——šŸœ˜KšœŸœ˜ K˜K˜K˜K˜K˜—K˜—šŸœŸ˜ KšŸœP˜TKšŸœB˜F—Kšœ Ÿœ ˜K˜—Kš ŸœŸœŸœŸœ Ÿœ˜)K˜K˜—š  œŸœTŸœ ŸœŸœ˜ŽKšœŸœ ˜(šŸœ Ÿ˜šœ˜šŸœŸœŸœ˜DKšœ’Πbc)˜B—Kšœ˜—šœ˜šŸœŸœ˜@Kšœ’€)˜C—Kšœ˜—šœ ˜ Kšœ˜Kšœ’˜(Kšœ˜—KšŸœŸœ˜—K˜K˜—š  œŸœ#Ÿœ&ŸœŸœ˜xKšœŸœ ˜(Kš ŸœŸœœŸœŸœ˜-K˜—K˜™K™—š  œŸœŸœŸœŸœ˜Iš ŸœŸœŸœ&ŸœŸœŸ˜BKšŸœŸœŸœ˜0KšŸœ˜—KšŸœ(˜.KšŸœŸœ˜ K˜K˜—š œŸœŸœ˜7KšœŸœ6˜SKšœŸœ˜,K˜K˜—š œŸœŸœ™EKšE™EšŸœ+Ÿœ™3KšœŸœ™+š ŸœŸœŸœ)ŸœŸœŸ™MJšŸœŸœŸœ'™?JšŸœ™—J™—K™K™—š œŸœŸœ#Ÿœ˜hKšœ Ÿœ&˜2Kš ’’œ’€˜PK˜—K˜Kšœ ŸœŸœ˜KšœŸœŸœ‘˜<š œŸœŸœŸœ˜CKš’b™nš Ÿœ ŸœŸœ œŸœŸœŸ˜SKšŸœŸœŸœŸœ˜$KšŸœŸœŸœ)˜LKšœŸœ˜KšŸœ˜—KšœŸœ‘8˜LK˜K˜—š  œŸœŸœ˜+KšœŸœh™tš ŸœŸœŸœ ŸœŸœ‘!˜HK™5KšŸœŸœŸœ#Ÿœ˜LKšŸœŸœŸœ Ÿœ˜FKšŸœŸœŸœ#Ÿœ˜LKšŸœŸœŸœ"Ÿœ˜JKšŸœŸœŸœŸœ˜BšŸœŸœŸœŸœ˜BK™4—šœšŸœ˜žKšŸœ1™4—Kš œŸœŸœŸœŸœŸœ˜lK˜—K˜K˜—š œŸœŸœŸœ&˜YKšŸœ-˜3K˜—K˜™ K™—š œŸœ˜KšœŸœI˜eKšœŸœE˜_KšœŸœG˜bKšœŸœM˜kKšœŸœO˜nKšœŸœO˜nKšœŸœI˜eKšœŸœE˜XK˜K˜1K˜2K˜—K˜KšœŸœŸœ˜$Kšœ Ÿœ˜KšœŸœ˜K˜K˜K˜KšŸœ˜K˜—…—ͺταζ