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.PutF["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.CR]; 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.PutF[" (%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.PutF[" (%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, good: BOOL; IF version < 8802.04 THEN GGParseIn.ReadWRope[f, "Traj"]; -- gone as of version 8802.04 GGParseIn.ReadWRope[f, "("]; roleName _ GGParseIn.ReadWWord[f]; role _ RoleFromRope[roleName]; GGParseIn.ReadWRope[f, ")"]; GGParseIn.ReadWRope[f, "["]; hiJoint _ GGParseIn.ReadWNAT[f]; GGParseIn.ReadWRope[f, "]"]; IF version >= 8607.22 THEN { GGParseIn.ReadWRope[f, "arrows:"]; arrowCode _ GGParseIn.ReadWNAT[f]; } ELSE arrowCode _ 0; IF version >= 8702.26 THEN { GGParseIn.ReadWRope[f, "j:"]; strokeJoint _ GGParseIn.ReadStrokeJoint[f]; GGParseIn.ReadWRope[f, "e:"]; [endsOK, good] _ GGParseIn.ReadBool[f, version]; IF NOT good THEN ERROR; strokeEnd _ GGParseIn.ReadStrokeEnd[f]; } ELSE { strokeJoint _ round; endsOK _ FALSE; }; IF version >= 8701.135 THEN { GGParseIn.ReadWRope[f, "w:"]; strokeWidth _ GGParseIn.ReadWReal[f]; widthOK _ strokeWidth>= 0.0; GGParseIn.ReadWRope[f, "c:"]; [colorOK, good] _ GGParseIn.ReadBool[f, version]; IF NOT good THEN ERROR; strokeColor _ GGParseIn.ReadColor[f, version]; } ELSE { GGParseIn.ReadWRope[f, ":"]; widthOK _ FALSE; colorOK _ FALSE; }; IF version >= 8701.23 THEN { GGParseIn.ReadWRope[f, "d:"]; [dashOK, good] _ GGParseIn.ReadBool[f, version]; IF NOT good THEN ERROR; IF dashOK THEN { [dashed, good] _ GGParseIn.ReadBool[f, version]; IF NOT good THEN ERROR; IF dashed THEN { pattern _ GGParseIn.ReadArrayOfReal[f]; offset _ GGParseIn.ReadWReal[f]; length _ GGParseIn.ReadWReal[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.ReadWRope[f, "("]; className _ GGParseIn.ReadWWord[f]; IF NOT widthOK THEN strokeWidth _ GGParseIn.ReadWReal[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, good] _ GGParseIn.ReadBool[f, version]; IF NOT good THEN ERROR; IF dashed THEN { pattern _ GGParseIn.ReadArrayOfReal[f]; offset _ GGParseIn.ReadWReal[f]; length _ GGParseIn.ReadWReal[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.ReadWRope[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.ReadWRope[f, "("]; className _ GGParseIn.ReadWWord[f]; IF NOT widthOK THEN strokeWidth _ GGParseIn.ReadWReal[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, good] _ GGParseIn.ReadBool[f, version]; IF NOT good THEN ERROR; IF dashed THEN { pattern _ GGParseIn.ReadArrayOfReal[f]; offset _ GGParseIn.ReadWReal[f]; length _ GGParseIn.ReadWReal[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.ReadWRope[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.ReadWRope[f, "fwd:"]; [fwd, good] _ GGParseIn.ReadBool[f, version]; IF NOT good THEN ERROR; } 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, 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; [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 by Xerox Corporation. All rights reserved. Created By: Ken Pier, December 3, 1987 3:25:32 pm PST Last Edited By: Ken Pier, September 20, 1990 10:33 am PDT Bier, August 31, 1989 10:08:47 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 Κ0‘˜Icodešœ™šΟbœF™NK™BK™5K™9K™%—K™šΟk ˜ Jšœ™˜™—K˜šΟn œžœž˜Jšžœ™˜ Kšœžœ ž˜K˜Kšœ žœ˜)Kšœ žœ˜&Kšœ žœ˜,Kšœžœ˜#Kšœžœ˜#Kšœžœ˜Kšœžœ&˜AKšœžœ˜Kšœ žœ˜-Kšœžœ˜Kšœžœ ˜5Kšœ žœ˜+Jšœžœ/˜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šžœ žœ#˜3Lšžœ@˜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˜šœ>žœ˜CLšœ™—Lšžœžœ! ˜WLšœ˜Lšœ"˜"Lšœ˜Lšœ˜Lšœ˜Lšœ ˜ Lšœ˜šžœžœ˜Lšœ"˜"Lšœ"˜"L˜—Lšžœ˜šžœžœ˜Lšœ˜Lšœ+˜+Lšœ˜Lšœ0˜0Lšžœžœžœžœ˜Lšœ'˜'Lšœ˜—šžœ˜Lšœ˜Lšœ žœ˜Lšœ˜—šžœžœ˜Lšœ˜Lšœ%˜%Lšœ˜Lšœ˜Lšœ1˜1Lšžœžœžœžœ˜Lšœ.˜.L˜—šžœ˜Lšœ˜Lšœ žœ˜Lšœ žœ˜L˜—šžœ˜šžœ˜Lšœ˜Lšœ0˜0Lšžœžœžœžœ˜šžœžœ˜Lšœ0˜0Lšžœžœžœžœ˜šžœžœ˜Lšœ'˜'Lšœ ˜ Lšœ ˜ L˜—L˜—L˜—šžœ˜Lšœ žœ˜Lšœ žœ˜L˜L˜——Lšœ™Lšœ%˜%Lšœ˜Lšœ žœ ˜Lšœ#˜#šžœžœžœž˜!Lšœ˜Lšœ#˜#Lšžœžœ žœ&˜9šžœžœ˜Kšžœžœžœ(˜:Kšœ˜—Kšžœ˜šžœžœ˜Lšžœžœ žœ/˜BL˜—Lšžœ˜ šžœžœžœ˜Lšœ0˜0Lšžœžœžœžœ˜šžœžœ˜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šžœžœ žœ&˜9šžœžœ˜Kšžœžœžœ(˜:Kšœ˜—Kšžœ˜šžœžœ˜Lšžœžœ žœ/˜BL˜—Lšžœ˜ šžœžœžœ˜Lšœ0˜0Lšžœžœžœžœ˜šžœžœ˜Lšœ'˜'Lšœ ˜ Lšœ ˜ L˜—L˜—Lšœ>˜>Lšœ+˜+šžœžœ˜Lšœžœžœžœ˜Lšœ# A˜dš žœžœžœžœžœžœž˜>Lšœ žœ˜(Lšžœ˜—L˜—Lšœ˜Lšœ˜Lšœ˜Lšœ˜L˜L˜L˜L˜šžœžœ 3˜QKšœ0˜0Lšžœžœ žœžœ˜Kšœ$˜$Kšœ˜—Kšžœ)˜-L˜—šžœžœ >˜YKšœ˜Lšœ-˜-Kšžœžœžœžœ˜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šœ2˜2Kšœ@˜@Kšœžœžœ˜@Kšœ žœ*˜8K˜K˜—šŸœžœ7˜WKšœžœ˜,Kšœžœ˜/Kšœ?˜?KšœF˜FK˜K˜—šŸœžœžœ'˜iKšœžœ˜/Kšœžœ˜,Kšœ ˜ K˜Kšœ<˜Kšœ<˜Kšœ ˜ š žœžœžœ3žœžœž˜Wšžœžœž œž˜2Jšœ%˜%J˜Jšžœ˜—Jšžœ˜—J˜—K˜K˜—šŸœžœ)žœžœ žœžœžœžœžœžœžœžœ˜—K™€Kšœžœ ˜(Kšœ+˜+Kšœ žœžœ˜K˜šžœžœN˜TKšžœžœ˜ —šžœžœžœ˜$K˜—šžœžœžœ(˜FKšžœ"˜&šžœ˜Kšœ?˜?Kš žœžœ žœžœžœžœ˜%Kšœ# #˜FKšœ'˜'K˜——šžœ%žœ˜-Jšœ žœ1˜>K˜—K˜K˜—š Ÿœžœ'žœ žœžœžœ ˜zJšœ˜Kšœ žœžœ˜Kšœ žœ˜Kšœžœ˜,Kšœžœ˜/Jšœ<˜<šžœOžœžœž˜dJšœA˜Aš žœžœžœ žœžœž˜DJšœ žœ˜"Jšžœ˜—Jšœ$˜$Jšžœ˜—K˜K˜—š Ÿœžœ+žœ žœžœžœ ˜€Jšœ˜Kšœ žœžœ˜Kšœ žœ˜Kšœžœ˜,Kšœžœ˜/Jšœ<˜<šžœOžœžœž˜dJšœE˜Eš žœžœžœ žœžœž˜DJšœ žœ˜"Jšžœ˜—Jšœ$˜$Jšžœ˜—K˜K˜—šŸœžœžœžœžœžœžœ˜bKšœZ™ZKšœžœ ˜+šžœž˜Kšœ!žœžœ˜-šœ ˜ Kšœžœ˜!Kšœ2˜2KšœA˜AK˜—Kšžœžœ˜—K˜—K˜šœ™K™—šŸœžœT˜iKšΠbk™2Kš)™)JšœCžœ˜HJšœ>žœ˜CJšœ:žœ˜?Jšœžœ˜„JšœDžœ˜IKšœ˜K˜—šΠbnœžœ0žœžœ˜€Kšœžœžœ ˜Kšœ˜Kšœžœ˜%Kšœžœ ˜(Kš œžœžœžœ4žœ%˜‰šžœOžœžœž˜dKšœ+˜+Kšœ"˜"Kšžœ žœžœ žœ˜3Kšžœ˜—Kšœ)˜)šœ ˜ K™-—K˜K˜—š Ÿœžœ#žœžœžœžœ˜xKšœžœ˜%Kšœžœ ˜(Kšœžœžœ˜šžœ žœžœ=žœ˜Tš Ÿ œžœ'žœžœžœžœ˜^šžœžœ˜Jšžœžœžœ˜2J˜—šžœ˜Jšœžœ˜ Jšœ˜J˜—J˜—šžœž˜ 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š Ÿ œžœ'žœžœžœžœ˜\šžœžœ˜Jšžœžœžœ˜.J˜—šžœ˜Jšœžœ˜ Jšœ˜J˜—J˜—šžœž˜ KšžœBŸ œ˜QKšžœ3Ÿ œ˜C—Kšœ žœ ˜K˜—šžœ˜Kšœ™Kšœ žœ˜K˜—K˜K˜—š’œžœW˜oJšœžœ ˜(Kšœ#˜#K˜K˜—š ’œžœ#žœ&žœžœ˜xJšœžœ ˜(Kšžœ˜K˜K˜—š’œžœ9žœ˜qKšœžœ˜%Kšœžœ ˜(Kš œžœžœžœ4žœ%˜‰šžœOžœžœž˜dšžœž˜Kšœ˜˜Kšœ8˜8Kšœ˜K˜—Kšžœžœ˜—Kšžœ˜—K˜K˜—š ’œžœ#žœžœžœ˜lKšœžœ˜%Kšœžœ ˜(Kšœžœžœ˜šžœ žœžœ=žœ˜Tš Ÿ œžœ'žœžœžœžœ˜^šžœžœ˜Jšžœžœ.žœžœ˜EJ˜—šžœ˜Jšœžœ˜ Jšœ˜J˜—J˜—šžœž˜ KšžœO˜SKšžœA˜E—Kšœ žœ ˜K˜—šžœž˜Kšœ žœžœ˜Kšœ˜Kšœ žœ˜K˜ šžœžœ˜"Kšœq˜qšžœ žœ˜šžœ ž˜šœ ˜ šžœžœ˜Kšžœ,žœ:˜lKšžœ=˜AK˜—Kšœ˜—Kšœ+˜+Kšžœ˜—K˜—K˜—K˜—K˜K˜—š œžœ6žœ˜iK™AJšœžœ ˜(Kšœ˜Kšœ˜K˜K˜—š  œžœžœžœžœ˜PJšœžœ ˜(Kšžœ%˜+K˜K™—š ’ œžœ+žœžœ žœžœ#˜¦Kšœžœ˜%Kšœžœ ˜(Kš œžœžœžœ4žœ%˜‰šžœOžœžœž˜dKšœ˜Kšœ˜Kšœ˜Kšœ˜Kšžœ˜—K˜K˜—š’Ÿ œžœ#žœ žœžœ+žœžœžœ˜€Kšœžœ˜%Kšœžœ ˜(Kšœ žœ;˜HKšœžœžœ˜šžœžœ˜š Ÿ œžœ'žœžœžœžœ˜_šžœž˜ šœ˜Jšœžœ ž˜&Jšœž˜Jšœž˜Jšžœ5˜8——šžœ˜Jšœžœ˜ Kšœ˜Kšœ˜Kšœ˜Kšœ˜J˜—J˜—šžœž˜ 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˜—J˜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™šŸœžœ˜KšœžœI˜eKšœžœE˜_KšœžœG˜bKšœžœM˜kKšœžœO˜nKšœžœO˜nKšœžœI˜eKšœžœE˜XK˜Kšœ1˜1Kšœ2˜2K˜—K˜Kšœžœžœ˜$Kšœ žœ˜Kšœžœ˜K˜K˜K˜Kšžœ˜K˜—…—«°ζ