<> <> <> <> <<>> DIRECTORY Atom, Feedback, GGBasicTypes, GGBoundBox, GGDescribe, GGInterfaceTypes, GGModelTypes, GGOutline, GGParseIn, GGParseOut, GGSegment, GGSegmentTypes, GGSelect, GGSequence, GGShapes, GGTraj, GGTransform, GGUtility, Imager, ImagerColorPrivate, ImagerTransformation, IO, RealFns, Rope, Rosary, Vectors2d; GGTrajImpl: CEDAR PROGRAM IMPORTS Atom, Feedback, GGBoundBox, GGDescribe, GGOutline, GGParseIn, GGParseOut, GGSegment, GGSelect, GGSequence, GGShapes, GGTransform, GGUtility, Imager, ImagerColorPrivate, ImagerTransformation, IO, RealFns, Rope, Rosary, Vectors2d EXPORTS GGTraj = BEGIN BitVector: TYPE = GGBasicTypes.BitVector; BoundBox: TYPE = GGBasicTypes.BoundBox; CameraData: TYPE = GGModelTypes.CameraData; Color: TYPE = Imager.Color; HitType: TYPE = GGModelTypes.TrajPartType; StrokeEnd: TYPE = Imager.StrokeEnd; StrokeJoint: TYPE = Imager.StrokeJoint; FeatureData: TYPE = GGInterfaceTypes.FeatureData; FenceHoleOpen: TYPE = GGModelTypes.FenceHoleOpen; GGData: TYPE = GGInterfaceTypes.GGData; Joint: TYPE = REF JointObj; JointObj: TYPE = GGSegmentTypes.JointObj; JointGenerator: TYPE = GGModelTypes.JointGenerator; Outline: TYPE = GGModelTypes.Outline; OutlineData: TYPE = GGOutline.OutlineData; Point: TYPE = GGBasicTypes.Point; PointGenerator: TYPE = GGModelTypes.PointGenerator; PointPairGenerator: TYPE = GGModelTypes.PointPairGenerator; Scene: TYPE = GGModelTypes.Scene; SegAndIndex: TYPE = GGSequence.SegAndIndex; SegmentClass: TYPE = GGSegmentTypes.SegmentClass; SelectionClass: TYPE = GGInterfaceTypes.SelectionClass; Segment: TYPE = GGSegmentTypes.Segment; SegmentGenerator: TYPE = GGModelTypes.SegmentGenerator; Sequence: TYPE = GGModelTypes.Sequence; SequenceGenerator: TYPE = GGModelTypes.SequenceGenerator; SequenceOfReal: TYPE = GGBasicTypes.SequenceOfReal; TriggerBag: TYPE = GGInterfaceTypes.TriggerBag; Traj: TYPE = REF TrajObj; TrajObj: TYPE = GGModelTypes.TrajObj; TrajEnd: TYPE = GGModelTypes.TrajEnd; Vector: TYPE = GGBasicTypes.Vector; Problem: ERROR [msg: Rope.ROPE] = Feedback.Problem; <> CreateTraj: PUBLIC PROC [point: Point] RETURNS [traj: Traj] = { firstJoint: Joint _ NEW[JointObj _ [point: point]]; bBox: BoundBox _ GGBoundBox.CreateBoundBox[point.x, point.y, point.x, point.y]; traj _ NEW[TrajObj _ [open, 0, NIL, Rosary.FromItem[firstJoint], NIL, NIL, bBox, FALSE]]; }; AddSegment: PUBLIC PROC [traj: Traj, trajEnd: TrajEnd, seg: Segment, segEnd: TrajEnd] RETURNS [success: BOOL _ TRUE] = { diff: Vector; loJoint, hiJoint: Joint; outline: Outline; <> IF traj.role = fence OR traj.role = hole THEN {success _ FALSE; RETURN}; <> IF trajEnd = segEnd THEN GGSegment.ReverseSegment[seg]; diff _ IF trajEnd = lo THEN Vectors2d.Sub[FetchJointPos[traj, 0], seg.hi] ELSE Vectors2d.Sub[FetchJointPos[traj, traj.segCount], seg.lo]; GGSegment.TranslateSegment[seg, diff]; <> IF traj.segCount = 0 THEN { traj.segments _ Rosary.FromItem[seg]; loJoint _ NEW[JointObj _ [point: seg.lo]]; hiJoint _ NEW[JointObj _ [point: seg.hi]]; traj.joints _ Rosary.FromList[LIST[loJoint, hiJoint]]; traj.segCount _ 1; traj.boundBox _ GGBoundBox.CopyBoundBox[seg.class.boundBox[seg]]; } <> ELSE { IF trajEnd = lo THEN { loJoint _ NEW[JointObj _ [point: seg.lo]]; traj.joints _ Rosary.Concat[Rosary.FromItem[loJoint], traj.joints]; traj.segments _ Rosary.Concat[Rosary.FromItem[seg], traj.segments]; } ELSE { hiJoint _ NEW[JointObj _ [point: seg.hi]]; traj.joints _ Rosary.Concat[traj.joints, Rosary.FromItem[hiJoint]]; traj.segments _ Rosary.Concat[traj.segments, Rosary.FromItem[seg]]; }; traj.segCount _ traj.segCount + 1; GGBoundBox.EnlargeByBox[traj.boundBox, seg.class.boundBox[seg]]; outline _ GGOutline.OutlineOfTraj[traj]; IF outline#NIL THEN GGOutline.UpdateBoundBox[outline]; }; }; CloseWithSegment: PUBLIC PROC [traj: Traj, seg: Segment, segEnd: TrajEnd] = { <> diff: Vector; outline: Outline; IF traj.segCount = 0 THEN ERROR Problem[msg: "single closed segment not implemented"]; <> IF segEnd = hi THEN GGSegment.ReverseSegment[seg]; diff _ Vectors2d.Sub[LastJointPos[traj], seg.lo]; GGSegment.TranslateSegment[seg, diff]; traj.segments _ Rosary.Concat[traj.segments, Rosary.FromItem[seg]]; traj.segCount _ traj.segCount + 1; traj.role _ fence; GGBoundBox.EnlargeByBox[traj.boundBox, seg.class.boundBox[seg]]; outline _ GGOutline.OutlineOfTraj[traj]; IF outline#NIL THEN GGOutline.UpdateBoundBox[outline]; }; <<>> CloseByDistorting: PUBLIC PROC [traj: Traj, distortEnd: TrajEnd] = { <> seg: Segment; loJoint, hiJoint: Joint; outline: Outline; loJoint _ FetchJoint[traj, 0]; hiJoint _ FetchJoint[traj, HiJoint[traj]]; SELECT distortEnd FROM lo => { seg _ FetchSegment[traj, 0]; loJoint.point _ hiJoint.point; seg.lo _ hiJoint.point; seg.class.endPointMoved[seg, TRUE, hiJoint.point]; }; hi => { seg _ FetchSegment[traj, HiSegment[traj]]; hiJoint.point _ loJoint.point; seg.hi _ loJoint.point; seg.class.endPointMoved[seg, FALSE, loJoint.point]; }; ENDCASE => ERROR; traj.joints _ Rosary.Substr[traj.joints, 0, HiJoint[traj]]; traj.role _ fence; GGBoundBox.EnlargeByBox[traj.boundBox, seg.class.boundBox[seg]]; outline _ GGOutline.OutlineOfTraj[traj]; IF outline#NIL THEN GGOutline.UpdateBoundBox[outline]; }; OnlyChild: PUBLIC PROC [traj: Traj] RETURNS [BOOL] = { outline: Outline _ traj.parent; outlineData: OutlineData _ NARROW[outline.data]; RETURN[outlineData.children.rest = NIL]; }; <> CopyTrajFromRun: PUBLIC PROC [seq: Sequence] RETURNS [copy: Traj] = { <> originalSegments: Rosary.ROSARY _ seq.traj.segments; desiredSegments: Rosary.Segment; loSegments, hiSegments, extractedSegments: Rosary.ROSARY; originalJoints: Rosary.ROSARY _ seq.traj.joints; desiredJoints: Rosary.Segment; loJoints, hiJoints, extractedJoints: Rosary.ROSARY; segGen: SegmentGenerator; next: GGSequence.SegAndIndex; s1, len1, s2, len2: INT; jointCount: NAT; newRole: GGModelTypes.FenceHoleOpen; CopyEachSegment: PROC[q: PROC[item: Rosary.Item, repeat: INT _ 1]] = { CopySegmentAndBuild: PROC [item: Rosary.Item] RETURNS [quit: BOOLEAN _ FALSE] = { copy, oldSeg: Segment; oldSeg _ NARROW[item]; copy _ GGSegment.CopySegment[oldSeg]; q[copy, 1]; }; [] _ Rosary.Map[desiredSegments, CopySegmentAndBuild]; }; CopyEachJoint: PROC[q: PROC[item: Rosary.Item, repeat: INT _ 1]] = { CopyJointAndBuild: PROC [item: Rosary.Item] RETURNS [quit: BOOLEAN _ FALSE] = { copy, oldJoint: Joint; oldJoint _ NARROW[item]; copy _ CopyJoint[oldJoint]; q[copy, 1]; }; [] _ Rosary.Map[desiredJoints, CopyJointAndBuild]; }; segGen _ GGSequence.OrderedSegmentsInSequence[seq]; next _ GGSequence.NextSegmentAndIndex[segGen]; [s1, len1, s2, len2] _ GGUtility.BreakIntervalMODLen[next.index, seq.segCount, seq.traj.segCount]; IF s2 # -1 THEN { desiredSegments _ [originalSegments, s1, len1]; loSegments _ Rosary.FromProcProc[CopyEachSegment]; desiredSegments _ [originalSegments, s2, len2]; hiSegments _ Rosary.FromProcProc[CopyEachSegment]; extractedSegments _ Rosary.Concat[hiSegments, loSegments]; } ELSE { desiredSegments _ [originalSegments, next.index, seq.segCount]; extractedSegments _ Rosary.FromProcProc[CopyEachSegment]; }; jointCount _ seq.segCount + 1; -- ignore the joint information in the sequence. We just want all of the segments to have a joint on each side. This distinction is particularly important when a single joint of a closed trajectory is being deleted. [s1, len1, s2, len2] _ GGUtility.BreakIntervalMODLen[next.index, jointCount, HiJoint[seq.traj]+1]; IF s2 # -1 THEN { desiredJoints _ [originalJoints, s1, len1]; loJoints _ Rosary.FromProcProc[CopyEachJoint]; desiredJoints _ [originalJoints, s2, len2]; hiJoints _ Rosary.FromProcProc[CopyEachJoint]; extractedJoints _ Rosary.Concat[hiJoints, loJoints]; } ELSE { desiredJoints _ [originalJoints, next.index, jointCount]; extractedJoints _ Rosary.FromProcProc[CopyEachJoint]; }; newRole _ IF GGSequence.IsComplete[seq] AND seq.traj.role # open THEN fence ELSE open; copy _ NEW[TrajObj _ [newRole, seq.segCount, extractedSegments, extractedJoints, NIL, NIL, NIL, seq.traj.visibleJoints, seq.traj.strokeJoint ]]; copy.boundBox _ GGBoundBox.NullBoundBox[]; UpdateBoundBox[copy]; }; CopyTrajFromRange: PUBLIC PROC [original: Traj, start: INT, len: INT] RETURNS [piece: Traj] = { <> originalSegments: Rosary.ROSARY _ original.segments; desiredSegments: Rosary.Segment _ [originalSegments, start, len]; extractedSegments: Rosary.ROSARY; originalJoints: Rosary.ROSARY _ original.joints; desiredJoints: Rosary.Segment _ [originalJoints, start, len]; extractedJoints: Rosary.ROSARY; CopyEachSegment: PROC[q: PROC[item: Rosary.Item, repeat: INT _ 1]] = { CopySegmentAndBuild: PROC [item: Rosary.Item] RETURNS [quit: BOOLEAN _ FALSE] = { copy, oldSeg: Segment; oldSeg _ NARROW[item]; copy _ GGSegment.CopySegment[oldSeg]; q[copy, 1]; }; [] _ Rosary.Map[desiredSegments, CopySegmentAndBuild]; }; CopyEachJoint: PROC[q: PROC[item: Rosary.Item, repeat: INT _ 1]] = { CopyJointAndBuild: PROC [item: Rosary.Item] RETURNS [quit: BOOLEAN _ FALSE] = { copy, oldJoint: Joint; oldJoint _ NARROW[item]; copy _ CopyJoint[oldJoint]; q[copy, 1]; }; [] _ Rosary.Map[desiredJoints, CopyJointAndBuild]; }; extractedSegments _ Rosary.FromProcProc[CopyEachSegment]; extractedJoints _ Rosary.FromProcProc[CopyEachJoint]; piece _ NEW[TrajObj _ [fence, len, extractedSegments, extractedJoints, NIL, NIL, NIL, original.visibleJoints, original.strokeJoint ]]; <> piece.boundBox _ GGBoundBox.NullBoundBox[]; UpdateBoundBox[piece]; }; Concat: PUBLIC PROC [fixed: Traj, fixedEnd: TrajEnd, moving: Traj, movingEnd: TrajEnd] RETURNS [longer: Traj] = { <> diff: Vector; fixed _ CopyTraj[fixed]; moving _ CopyTraj[moving]; IF fixedEnd = movingEnd THEN ReverseTraj[moving]; IF fixedEnd = hi THEN { diff _ Vectors2d.Sub[FetchJointPos[fixed, HiJoint[fixed]], FetchJointPos[moving, 0]]; TranslateTraj[moving, diff]; longer _ NEW[TrajObj _ [ role: open, segCount: fixed.segCount + moving.segCount, segments: Rosary.Concat[fixed.segments, moving.segments], joints: Rosary.Concat[fixed.joints, Rosary.Substr[moving.joints, 1]], extraPoints: NIL, parent: NIL, boundBox: NIL, visibleJoints: fixed.visibleJoints, strokeJoint: fixed.strokeJoint, selectedInPart: [FALSE, FALSE, FALSE] ]]; longer.boundBox _ GGBoundBox.NullBoundBox[]; UpdateBoundBox[longer]; } ELSE { -- fixedEnd = lo diff _ Vectors2d.Sub[FetchJointPos[fixed, 0], FetchJointPos[moving, HiJoint[moving]]]; TranslateTraj[moving, diff]; longer _ NEW[TrajObj _ [ role: open, segCount: fixed.segCount + moving.segCount, segments: Rosary.Concat[moving.segments, fixed.segments], joints: Rosary.Concat[moving.joints, Rosary.Substr[fixed.joints, 1]], extraPoints: NIL, parent: NIL, boundBox: NIL, visibleJoints: fixed.visibleJoints, strokeJoint: fixed.strokeJoint, selectedInPart: [FALSE, FALSE, FALSE] ]]; longer.boundBox _ GGBoundBox.NullBoundBox[]; UpdateBoundBox[longer]; }; }; SpliceIn: PUBLIC PROC [run: Sequence, traj: Traj] RETURNS [newTraj: Traj] = { oldOutline: Outline _ GGOutline.OutlineOfTraj[run.traj]; oldOutlineData: OutlineData _ NARROW[oldOutline.data]; SELECT run.traj.role FROM hole => { newTraj _ SpliceInClosed[run, traj]; newTraj.role _ hole; [] _ GGOutline.ReplaceHole[oldOutline, run.traj, newTraj]; }; open => { newTraj _ SpliceInOpen[run, traj]; newTraj.role _ open; [] _ GGOutline.CreateOutline[newTraj, oldOutlineData.fillColor]; }; fence => { newTraj _ SpliceInClosed[run, traj]; newTraj.role _ fence; [] _ GGOutline.ReplaceFence[oldOutline, newTraj]; }; ENDCASE => ERROR; <> newTraj.loArrow _ run.traj.loArrow; newTraj.hiArrow _ run.traj.hiArrow; }; IncludesLoEnd: PROC [run: Sequence] RETURNS [BOOL] = { RETURN[run.segments[0]]; }; ResetEndSelectionBits: PROC [traj: Traj, lo, hi: BOOL _ TRUE] = { joint: Joint; IF lo THEN { joint _ FetchJoint[traj, 0]; joint.TselectedInFull.active _ FALSE; }; IF hi THEN { joint _ FetchJoint[traj, HiJoint[traj]]; joint.TselectedInFull.active _ FALSE; }; }; SpliceInOpen: PROC [run: Sequence, traj: Traj] RETURNS [newTraj: Traj] = { runGen: SequenceGenerator; wholeSeq, remainder, run1, run2, run3: Sequence; traj1, traj2: Traj; <> <> wholeSeq _ GGSequence.CreateComplete[run.traj]; remainder _ GGSequence.Difference[wholeSeq, run]; GGSequence.FillInJoints[remainder]; [runGen,----] _ GGSequence.RunsInSequence[remainder]; run1 _ GGSequence.NextSequence[runGen]; run2 _ GGSequence.NextSequence[runGen]; run3 _ GGSequence.NextSequence[runGen]; IF run3 # NIL THEN ERROR; SELECT TRUE FROM run1 = NIL => { newTraj _ traj; }; run2 = NIL => { traj1 _ CopyTrajFromRun[run1]; IF IncludesLoEnd[run] THEN { ResetEndSelectionBits[traj: traj1, lo: TRUE, hi: FALSE]; newTraj _ Concat[traj, hi, traj1, lo]; -- order is important. traj1's lo joint is kept } ELSE { ResetEndSelectionBits[traj: traj1, lo: FALSE, hi: TRUE]; newTraj _ Concat[traj, lo, traj1, hi]; -- order is important. traj1's hi joint is kept }; }; ENDCASE => { << [Artwork node; type 'ArtworkInterpress on' to command tool] >> traj1 _ CopyTrajFromRun[run1]; ResetEndSelectionBits[traj: traj1, lo: FALSE, hi: TRUE]; traj2 _ CopyTrajFromRun[run2]; ResetEndSelectionBits[traj: traj2, lo: TRUE, hi: FALSE]; newTraj _ Concat[traj, lo, traj1, hi]; newTraj _ Concat[newTraj, hi, traj2, lo]; }; <> }; SpliceInClosed: PROC [run: Sequence, traj: Traj] RETURNS [newTraj: Traj] = { wholeSeq, remainder, run1, run2: Sequence; runGen: SequenceGenerator; traj1: Traj; <> <> IF GGSequence.IsComplete[run] THEN RETURN[traj]; -- no need to splice. wholeSeq _ GGSequence.CreateComplete[run.traj]; remainder _ GGSequence.Difference[wholeSeq, run]; GGSequence.FillInJoints[remainder]; [runGen, ----] _ GGSequence.RunsInSequence[remainder]; run1 _ GGSequence.NextSequence[runGen]; run2 _ GGSequence.NextSequence[runGen]; IF run1 = NIL OR run2 # NIL THEN ERROR; traj1 _ CopyTrajFromRun[run1]; ResetEndSelectionBits[traj1]; newTraj _ Concat[traj, lo, traj1, hi]; -- order is important because traj1's joints are kept. CloseByDistorting[newTraj, lo]; << [Artwork node; type 'ArtworkInterpress on' to command tool] >> <> }; <<>> <> ReverseTraj: PUBLIC PROC [traj: Traj] = { newSegments: Rosary.ROSARY _ NIL; newJoints: Rosary.ROSARY _ NIL; seg: Segment; joint: Joint; <> FOR i: NAT IN [0..traj.segCount) DO seg _ FetchSegment[traj, i]; GGSegment.ReverseSegment[seg]; newSegments _ Rosary.Concat[Rosary.FromItem[seg], newSegments]; ENDLOOP; FOR i: NAT IN [0..traj.segCount] DO joint _ IF traj.role=open THEN FetchJoint[traj, i] ELSE FetchJoint[traj, (i MOD traj.segCount)]; newJoints _ Rosary.Concat[Rosary.FromItem[joint], newJoints]; ENDLOOP; traj.segments _ newSegments; traj.joints _ newJoints; }; IsClockwiseTraj: PUBLIC PROC [traj: Traj] RETURNS [BOOL] = { RETURN [SignedArea[traj]>0]; }; IsClockwiseTrajTransformSeq: PUBLIC PROC [seq: Sequence, transform: ImagerTransformation.Transformation] RETURNS [BOOL] = { RETURN [SignedAreaTransformSeq[seq, transform]>0]; }; <<>> <> <<>> <> <<>> GetBoundBox: PUBLIC PROC [traj: Traj] RETURNS [bBox: BoundBox] = { UpdateBoundBox[traj]; RETURN[traj.boundBox]; }; UpdateBoundBox: PUBLIC PROC [traj: Traj] = { <> segGen: SegmentGenerator; segGen _ GGSequence.SegmentsInTraj[traj]; traj.boundBox^ _ GGBoundBox.emptyBoundBox^; FOR seg: Segment _ GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen] UNTIL seg = NIL DO GGBoundBox.EnlargeByBox[bBox: traj.boundBox, by: seg.class.boundBox[seg]]; ENDLOOP; IF traj.parent#NIL THEN GGOutline.UpdateBoundBox[traj.parent]; }; CopyTraj: PUBLIC PROC [original: Traj] RETURNS [copy: Traj] = { <> originalSegments: Rosary.ROSARY _ original.segments; desiredSegments: Rosary.Segment _ [originalSegments, 0, original.segCount]; extractedSegments: Rosary.ROSARY; originalJoints: Rosary.ROSARY _ original.joints; desiredJoints: Rosary.Segment _ [originalJoints, 0, HiJoint[original] + 1]; extractedJoints: Rosary.ROSARY; CopyEachSegment: PROC[q: PROC[item: Rosary.Item, repeat: INT _ 1]] = { CopySegmentAndBuild: PROC [item: Rosary.Item] RETURNS [quit: BOOLEAN _ FALSE] = { copy, oldSeg: Segment; oldSeg _ NARROW[item]; copy _ GGSegment.CopySegment[oldSeg]; q[copy, 1]; }; [] _ Rosary.Map[desiredSegments, CopySegmentAndBuild]; }; CopyEachJoint: PROC[q: PROC[item: Rosary.Item, repeat: INT _ 1]] = { CopyJointAndBuild: PROC [item: Rosary.Item] RETURNS [quit: BOOLEAN _ FALSE] = { copy, oldJoint: Joint; oldJoint _ NARROW[item]; copy _ CopyJoint[oldJoint]; q[copy, 1]; }; [] _ Rosary.Map[desiredJoints, CopyJointAndBuild]; }; extractedSegments _ Rosary.FromProcProc[CopyEachSegment]; extractedJoints _ Rosary.FromProcProc[CopyEachJoint]; copy _ NEW[TrajObj _ [original.role, original.segCount, extractedSegments, extractedJoints, NIL, NIL, NIL, original.visibleJoints, original.strokeJoint, original.loArrow, original.hiArrow]]; copy.boundBox _ GGBoundBox.NullBoundBox[]; UpdateBoundBox[copy]; }; <> DrawTraj: PUBLIC PROC [dc: Imager.Context, traj: Traj] = { <.>> <.>> <.>> <> <> DrawTrajAux: PROC [dc: Imager.Context, traj: Traj] = { Imager.SetStrokeJoint[dc, traj.strokeJoint]; FOR i: INT IN [0..HiSegment[traj]] DO seg _ FetchSegment[traj, i]; IF seg.strokeWidth#0 AND seg.color#NIL THEN { Imager.SetStrokeWidth[dc, seg.strokeWidth]; Imager.SetStrokeEnd[dc, seg.strokeEnd]; Imager.SetColor[dc, seg.color]; MaskStroke[dc, seg]; }; ENDLOOP; }; seg: Segment; IF AllStrokePropsAndColorsEqual[traj] THEN DrawSingleStrokeTraj[dc, traj] ELSE DrawTrajAux[dc, traj]; -- why no DoSaveAll here ?? }; DrawSingleStrokeTraj: PROC [dc: Imager.Context, traj: Traj] = { BuildPath: Imager.PathProc = { seg: Segment; firstPoint: Point _ FetchJointPos[traj, 0]; moveTo[firstPoint]; FOR i: INT IN [0..HiSegment[traj]] DO seg _ FetchSegment[traj, i]; seg.class.buildPath[seg, lineTo, curveTo, conicTo, arcTo]; ENDLOOP; }; PatternProc: PROC [i: NAT] RETURNS [REAL] = { RETURN[pattern[i]]; }; firstSeg: Segment _ FetchSegment[traj, 0]; strokeWidth: REAL _ firstSeg.strokeWidth; pattern: SequenceOfReal _ firstSeg.pattern; IF strokeWidth = 0.0 OR firstSeg.color=NIL THEN RETURN; Imager.SetStrokeWidth[dc, strokeWidth]; Imager.SetStrokeEnd[dc, firstSeg.strokeEnd]; Imager.SetStrokeJoint[dc, traj.strokeJoint]; Imager.SetColor[dc, firstSeg.color]; IF firstSeg.dashed THEN Imager.MaskDashedStroke[dc, BuildPath, pattern.len, PatternProc, firstSeg.offset, firstSeg.length] ELSE Imager.MaskStroke[dc, BuildPath, traj.role = fence OR traj.role = hole]; }; -- end DrawSingleStrokeTraj DrawTrajSeq: PUBLIC PROC [dc: Imager.Context, seq: Sequence] = { cpCount: NAT; seg: Segment; IF AllStrokePropsAndColorsEqual[seq.traj] THEN DrawSingleStrokeTrajSeq[dc, seq] ELSE { Imager.SetStrokeJoint[dc, seq.traj.strokeJoint]; FOR i: INT IN [0..HiSegment[seq.traj]] DO seg _ FetchSegment[seq.traj, i]; IF seg.strokeWidth=0 OR seg.color=NIL THEN LOOP; IF seq.segments[i] THEN { Imager.SetStrokeWidth[dc, seg.strokeWidth]; Imager.SetStrokeEnd[dc, seg.strokeEnd]; Imager.SetColor[dc, seg.color]; SegMaskStroke[dc, seg]; cpCount _ seg.class.controlPointCount[seg]; FOR j: NAT IN [0..cpCount) DO IF seq.controlPoints[i][j] THEN GGShapes.DrawCP[dc, seg.class.controlPointGet[seg, j]]; ENDLOOP; }; ENDLOOP; }; }; DrawSingleStrokeTrajSeq: PROC [dc: Imager.Context, seq: Sequence] = { BuildPath: Imager.PathProc = { FOR i: INT IN [0..HiSegment[seq.traj]] DO IF seq.segments[i] THEN { seg: Segment _ FetchSegment[seq.traj, i]; moveTo[seg.lo]; seg.class.buildPath[seg, lineTo, curveTo, conicTo, arcTo]; }; ENDLOOP; }; PatternProc: PROC [i: NAT] RETURNS [REAL] = { RETURN[pattern[i]]; }; firstSeg: Segment _ FetchSegment[seq.traj, 0]; pattern: SequenceOfReal _ firstSeg.pattern; IF firstSeg.strokeWidth = 0.0 OR firstSeg.color=NIL THEN RETURN; Imager.SetStrokeWidth[dc, firstSeg.strokeWidth]; Imager.SetStrokeEnd[dc, firstSeg.strokeEnd]; Imager.SetStrokeJoint[dc, seq.traj.strokeJoint]; Imager.SetColor[dc, firstSeg.color]; IF firstSeg.dashed THEN Imager.MaskDashedStroke[dc, BuildPath, pattern.len, PatternProc, firstSeg.offset, firstSeg.length] ELSE Imager.MaskStroke[dc, BuildPath, seq.traj.role = fence OR seq.traj.role = hole]; }; -- end DrawSingleStrokeTrajSeq DrawTrajTransformSeq: PUBLIC PROC [dc: Imager.Context, selSeq: Sequence, transform: ImagerTransformation.Transformation] = { cpCount: NAT; seg: Segment; IF AllStrokePropsAndColorsEqual[selSeq.traj] THEN DrawSingleStrokeTrajTransformSeq[dc, selSeq, transform] ELSE { point: Point; Imager.SetStrokeJoint[dc, selSeq.traj.strokeJoint]; FOR i: INT IN [0..HiSegment[selSeq.traj]] DO seg _ FetchSegment[selSeq.traj, i]; Imager.SetStrokeWidth[dc, (IF seg.strokeWidth#0 THEN seg.strokeWidth ELSE 1)]; Imager.SetStrokeEnd[dc, seg.strokeEnd]; Imager.SetColor[dc, IF seg.color#NIL THEN seg.color ELSE Imager.black]; MaskStrokeTransform[dc, seg, transform, selSeq.segments[i], selSeq.joints[i], selSeq.joints[IF selSeq.traj.role=open THEN (i+1) ELSE (i+1) MOD selSeq.traj.segCount], selSeq.controlPoints[i]]; cpCount _ seg.class.controlPointCount[seg]; FOR j: NAT IN [0..cpCount) DO IF selSeq.controlPoints[i][j] THEN { point _ ImagerTransformation.Transform[transform, seg.class.controlPointGet[seg, j]]; GGShapes.DrawCP[dc, point]; } ELSE { point _ seg.class.controlPointGet[seg, j]; GGShapes.DrawCP[dc, point]; }; ENDLOOP; ENDLOOP; }; }; DrawSingleStrokeTrajTransformSeq: PROC [dc: Imager.Context, selSeq: Sequence, transform: ImagerTransformation.Transformation] = { BuildPath: Imager.PathProc = { seg: Segment; entire, lo, hi: BOOL; controlPoints: BitVector; firstPoint: Point; entire _ selSeq.segments[0]; lo _ selSeq.joints[0]; firstPoint _ FetchJointPos[selSeq.traj, 0]; IF entire OR lo THEN firstPoint _ ImagerTransformation.Transform[transform, firstPoint]; moveTo[firstPoint]; FOR i: INT IN [0..HiSegment[selSeq.traj]] DO seg _ FetchSegment[selSeq.traj, i]; entire _ selSeq.segments[i]; lo _ selSeq.joints[i]; hi _ selSeq.joints[IF selSeq.traj.role=open THEN (i+1) ELSE (i+1) MOD selSeq.traj.segCount]; controlPoints _ selSeq.controlPoints[i]; seg.class.buildPathTransform[seg, transform, entire, lo, hi, controlPoints, lineTo, curveTo, conicTo, arcTo]; ENDLOOP; }; PatternProc: PROC [i: NAT] RETURNS [REAL] = { RETURN[pattern[i]]; }; firstSeg: Segment _ FetchSegment[selSeq.traj, 0]; strokeWidth: REAL _ firstSeg.strokeWidth; pattern: SequenceOfReal _ firstSeg.pattern; IF strokeWidth = 0.0 OR firstSeg.color=NIL THEN RETURN; Imager.SetStrokeWidth[dc, strokeWidth]; Imager.SetStrokeEnd[dc, firstSeg.strokeEnd]; Imager.SetStrokeJoint[dc, selSeq.traj.strokeJoint]; Imager.SetColor[dc, firstSeg.color]; IF firstSeg.dashed THEN Imager.MaskDashedStroke[dc, BuildPath, pattern.len, PatternProc, firstSeg.offset, firstSeg.length] ELSE Imager.MaskStroke[dc, BuildPath, selSeq.traj.role = fence OR selSeq.traj.role = hole]; }; -- end DrawSingleStrokeTrajTransformSeq DrawSelectionFeedback: PUBLIC PROC [traj: Traj, selectedParts, hotParts: Sequence, dc: Imager.Context, camera: CameraData, dragInProgress, caretIsMoving, hideHot, quick: BOOL] = { DoDrawFeedback: PROC = { segGen: GGModelTypes.SegmentGenerator; jointGen: GGModelTypes.JointGenerator; seg: Segment; someNormal, someHot, thisCPisHot, thisCPisSelected: BOOL; point: Point; <> jointGen _ GGSequence.JointsInTraj[traj]; FOR i: INT _ GGSequence.NextJoint[jointGen], GGSequence.NextJoint[jointGen] UNTIL i = -1 DO thisCPisHot _ hotParts#NIL AND hotParts.joints[i]; thisCPisSelected _ selectedParts#NIL AND selectedParts.joints[i]; point _ FetchJointPos[traj, i]; IF thisCPisHot THEN GGShapes.DrawSelectedJoint[dc, point, hot]; IF thisCPisSelected THEN GGShapes.DrawSelectedJoint[dc, point, normal]; IF NOT thisCPisHot AND NOT thisCPisSelected THEN GGShapes.DrawJoint[dc, point]; ENDLOOP; <> <> <> someNormal _ selectedParts # NIL; someHot _ hotParts # NIL; segGen _ GGSequence.SegmentsInTraj[traj]; FOR next: GGSequence.SegAndIndex _ GGSequence.NextSegmentAndIndex[segGen], GGSequence.NextSegmentAndIndex[segGen] UNTIL next.seg = NIL DO i: NAT _ next.index; seg _ next.seg; IF someNormal OR someHot THEN { FOR j: INT IN [0..seg.class.controlPointCount[seg]) DO thisCPisHot _ hotParts#NIL AND hotParts.controlPoints[i][j]; thisCPisSelected _ selectedParts#NIL AND selectedParts.controlPoints[i][j]; point _ seg.class.controlPointGet[seg, j]; IF thisCPisHot THEN GGShapes.DrawSelectedJoint[dc, point, hot]; IF thisCPisSelected THEN GGShapes.DrawSelectedJoint[dc, point, normal]; IF NOT thisCPisHot AND NOT thisCPisSelected THEN GGShapes.DrawCP[dc, point]; ENDLOOP; }; ENDLOOP; }; IF caretIsMoving OR dragInProgress THEN RETURN; IF selectedParts = NIL AND hotParts = NIL THEN RETURN; IF camera.quality # quality THEN Imager.DoSaveAll[dc, DoDrawFeedback]; }; <> MaskStroke: PROC [dc: Imager.Context, seg: Segment] = { MaskPath: Imager.PathProc = { moveTo[seg.lo]; seg.class.buildPath[seg, lineTo, curveTo, conicTo, arcTo]; }; PatternProc: PROC [i: NAT] RETURNS [REAL] = { RETURN[pattern[i]]; }; pattern: SequenceOfReal _ seg.pattern; IF seg.dashed THEN Imager.MaskDashedStroke[dc, MaskPath, pattern.len, PatternProc, seg.offset, seg.length] ELSE Imager.MaskStroke[dc, MaskPath, FALSE]; }; SegMaskStroke: PROC [dc: Imager.Context, seg: Segment] = { MaskPath: Imager.PathProc = { <<[moveTo: ImagerPath.MoveToProc, lineTo: ImagerPath.LineToProc, curveTo: ImagerPath.CurveToProc, conicTo: ImagerPath.ConicToProc, arcTo: ImagerPath.ArcToProc]>> moveTo[seg.lo]; seg.class.buildPath[seg, lineTo, curveTo, conicTo, arcTo]; }; PatternProc: PROC [i: NAT] RETURNS [REAL] = { RETURN[pattern[i]]; }; pattern: SequenceOfReal _ seg.pattern; IF seg.dashed THEN Imager.MaskDashedStroke[dc, MaskPath, pattern.len, PatternProc, seg.offset, seg.length] ELSE Imager.MaskStroke[dc, MaskPath, FALSE]; }; MaskStrokeTransform: PROC [dc: Imager.Context, seg: Segment, transform: ImagerTransformation.Transformation, entire, lo, hi: BOOL, controlPoints: BitVector] = { MaskPath: Imager.PathProc = { <<[moveTo: ImagerPath.MoveToProc, lineTo: ImagerPath.LineToProc, curveTo: ImagerPath.CurveToProc, conicTo: ImagerPath.ConicToProc, arcTo: ImagerPath.ArcToProc]>> loPoint: Point; loPoint _ IF entire OR lo THEN ImagerTransformation.Transform[transform, seg.lo] ELSE seg.lo; moveTo[loPoint]; seg.class.buildPathTransform[seg, transform, entire, lo, hi, controlPoints, lineTo, curveTo, conicTo, arcTo]; }; PatternProc: PROC [i: NAT] RETURNS [REAL] = { RETURN[pattern[i]]; }; pattern: SequenceOfReal _ seg.pattern; IF seg.dashed THEN Imager.MaskDashedStroke[dc, MaskPath, pattern.len, PatternProc, seg.offset, seg.length] ELSE Imager.MaskStroke[dc, MaskPath, FALSE]; }; EqualSequence: PROC [s1, s2: SequenceOfReal] RETURNS [BOOL] = { IF s1.len # s2.len THEN RETURN[FALSE]; FOR i: NAT IN [0..s1.len) DO IF s1[i] # s2[i] THEN RETURN[FALSE]; ENDLOOP; RETURN[TRUE]; }; AllStrokePropsAndColorsEqual: PUBLIC PROC [traj: Traj] RETURNS [BOOL] = { firstSeg: Segment _ FetchSegment[traj, 0]; seg: Segment; width: REAL _ firstSeg.strokeWidth; end: StrokeEnd _ firstSeg.strokeEnd; color: Color _ firstSeg.color; dashed: BOOL _ firstSeg.dashed; pattern: SequenceOfReal _ firstSeg.pattern; offset: REAL _ firstSeg.offset; length: REAL _ firstSeg.length; FOR i: INT IN [1..HiSegment[traj]] DO seg _ FetchSegment[traj, i]; IF seg.dashed # dashed THEN RETURN[FALSE]; IF seg.strokeEnd # end THEN RETURN[FALSE]; IF seg.strokeWidth # width THEN RETURN[FALSE]; IF NOT GGUtility.EquivalentColors[color, seg.color] THEN RETURN[FALSE]; IF NOT dashed THEN LOOP; IF seg.offset # offset THEN RETURN[FALSE]; IF seg.length # length THEN RETURN[FALSE]; IF NOT EqualSequence[seg.pattern, pattern] THEN RETURN[FALSE]; REPEAT FINISHED => RETURN[TRUE]; ENDLOOP; }; AllStrokeWidthsEqual: PROC [traj: Traj] RETURNS [width: REAL] = { firstSeg: Segment _ FetchSegment[traj, 0]; seg: Segment; width _ firstSeg.strokeWidth; FOR i: INT IN [1..HiSegment[traj]] DO seg _ FetchSegment[traj, i]; IF seg.strokeWidth # width THEN RETURN[-1.0]; ENDLOOP; }; AllStrokeEndsEqual: PROC [traj: Traj] RETURNS [allEqual: BOOL, end: StrokeEnd] = { firstSeg: Segment _ FetchSegment[traj, 0]; seg: Segment; end _ firstSeg.strokeEnd; FOR i: INT IN [1..HiSegment[traj]] DO seg _ FetchSegment[traj, i]; IF seg.strokeEnd # end THEN RETURN[FALSE, round]; ENDLOOP; allEqual _ TRUE; }; AllStrokeColorsEqual: PROC [traj: Traj] RETURNS [allEqual: BOOL, color: Color] = { firstSeg: Segment _ FetchSegment[traj, 0]; seg: Segment; color _ firstSeg.color; FOR i: INT IN [1..HiSegment[traj]] DO seg _ FetchSegment[traj, i]; <> IF NOT GGUtility.EquivalentColors[color, seg.color] THEN RETURN[FALSE, NIL]; ENDLOOP; allEqual _ TRUE; }; AllDashesEqual: PROC [traj: Traj] RETURNS [allEqual: BOOL, dashed: BOOL _ FALSE, pattern: SequenceOfReal _ NIL, offset, length: REAL _ 0.0] = { firstSeg: Segment _ FetchSegment[traj, 0]; seg: Segment; dashed _ firstSeg.dashed; pattern _ firstSeg.pattern; offset _ firstSeg.offset; length _ firstSeg.length; FOR i: INT IN [1..HiSegment[traj]] DO seg _ FetchSegment[traj, i]; IF seg.dashed # dashed THEN RETURN[FALSE]; IF NOT dashed THEN LOOP; IF NOT EqualSequence[seg.pattern, pattern] THEN RETURN[FALSE]; IF seg.offset # offset THEN RETURN[FALSE]; IF seg.length # length THEN RETURN[FALSE]; ENDLOOP; allEqual _ TRUE; }; SegmentSelected: PROC [segNum: NAT, selectedSeq: Sequence] RETURNS [BOOL] = { RETURN[selectedSeq.segments[segNum]]; }; DrawSequenceFeedback: PUBLIC PROC [dc: Imager.Context, seq: Sequence, normalSeq: Sequence, camera: CameraData, quick: BOOL _ FALSE, selectClass: SelectionClass _ normal] = { segGen: SegmentGenerator; color: Imager.Color; IF camera.quality = quality THEN RETURN; segGen _ GGSequence.SegmentsInSequence[seq]; Imager.SetColor[dc, Imager.black]; IF NOT quick THEN { -- draw full selection feedback Imager.SetStrokeJoint[dc, seq.traj.strokeJoint]; FOR next: SegAndIndex _ GGSequence.NextSegmentAndIndex[segGen], GGSequence.NextSegmentAndIndex[segGen] UNTIL next.seg = NIL DO IF SegmentSelected[next.index, normalSeq] THEN { Imager.SetStrokeWidth[dc, 2.0*(IF next.seg.strokeWidth#0 THEN next.seg.strokeWidth ELSE 1.0)]; Imager.SetStrokeEnd[dc, next.seg.strokeEnd]; color _ IF (next.seg.color=NIL OR ImagerColorPrivate.GrayFromColor[NARROW[next.seg.color]] < 0.2) THEN Imager.black ELSE next.seg.color; Imager.SetColor[dc, color]; -- want to always see feedback, even if stroke is white or NIL colored MaskStroke[dc, next.seg]; }; ENDLOOP; }; }; DrawThemJoints: PROC [dc: Imager.Context, traj: Traj, segNum: NAT, normalSeq: Sequence, hotSeq: Sequence] = { drewLo, drewHi: BOOL _ FALSE; seg: Segment _ FetchSegment[traj, segNum]; nextJoint: NAT _ FollowingJoint[traj, segNum]; IF hotSeq # NIL THEN { IF hotSeq.joints[segNum] THEN { GGShapes.DrawSelectedJoint[dc, seg.lo, hot]; drewLo _ TRUE; }; IF hotSeq.joints[nextJoint] THEN { GGShapes.DrawSelectedJoint[dc, seg.hi, hot]; drewHi _ TRUE; }; }; IF normalSeq # NIL THEN { IF normalSeq.joints[segNum] THEN { GGShapes.DrawSelectedJoint[dc, seg.lo, normal]; drewLo _ TRUE; }; IF normalSeq.joints[nextJoint] THEN { GGShapes.DrawSelectedJoint[dc, seg.hi, normal]; drewHi _ TRUE; }; }; IF seg.lo#seg.hi THEN { -- joints are not coincident IF NOT drewLo THEN GGShapes.DrawJoint[dc, seg.lo]; IF NOT drewHi THEN GGShapes.DrawJoint[dc, seg.hi]; } ELSE IF NOT (drewLo OR drewHi) THEN GGShapes.DrawJoint[dc, seg.hi]; }; <<>> <> TranslateTraj: PUBLIC PROC [traj: Traj, vector: Vector] = { <> transform: ImagerTransformation.Transformation; transform _ ImagerTransformation.Translate[[vector.x, vector.y]]; TransformTraj[traj, transform]; }; TransformTraj: PUBLIC PROC [traj: Traj, transform: ImagerTransformation.Transformation] = { <> seg: Segment; joint: Joint; FOR i: NAT IN [0..HiSegment[traj]] DO seg _ FetchSegment[traj, i]; GGSegment.TransformSegment[seg, transform]; ENDLOOP; FOR i: NAT IN [0..HiJoint[traj]] DO joint _ NARROW[Rosary.Fetch[traj.joints, i]]; joint.point _ GGTransform.Transform[transform, joint.point]; ENDLOOP; UpdateBoundBox[traj]; GGOutline.UpdateBoundBox[traj.parent]; }; TransformSequence: PUBLIC PROC [seq: Sequence, transform: ImagerTransformation.Transformation] = { << A tricky operation. All selected segments are transformed. Then, all segments which are not selected but which touch a selected joint are told that the joint has moved.>> joint: Joint; seg: Segment; SELECT seq.traj.role FROM open => TransformSequenceOpen[seq, transform]; fence, hole => TransformSequenceClosed[seq, transform]; ENDCASE => ERROR; FOR i: NAT IN [0..HiJoint[seq.traj]] DO IF seq.joints[i] THEN { joint _ NARROW[Rosary.Fetch[seq.traj.joints, i]]; joint.point _ GGTransform.Transform[transform, joint.point]; }; ENDLOOP; FOR i: NAT IN [0..HiSegment[seq.traj]] DO cpCount: NAT _ seq.controlPoints[i].len; FOR j: NAT IN [0..cpCount) DO IF seq.controlPoints[i][j] AND NOT seq.segments[i] THEN { seg _ NARROW[Rosary.Fetch[seq.traj.segments, i]]; seg.class.controlPointMoved[seg, transform, j]; }; ENDLOOP; ENDLOOP; UpdateBoundBox[seq.traj]; GGOutline.UpdateBoundBox[seq.traj.parent]; }; TransformSequenceOpen: PUBLIC PROC [seq: Sequence, transform: ImagerTransformation.Transformation] = { seg: Segment; FOR i: NAT IN [0..HiSegment[seq.traj]] DO seg _ FetchSegment[seq.traj, i]; IF seq.segments[i] THEN { GGSegment.TransformSegment[seg, transform]; } ELSE { IF seq.joints[i] THEN GGSegment.MoveEndPointSegment[seg, TRUE, GGTransform.Transform[transform, seg.lo]]; IF seq.joints[i+1] THEN GGSegment.MoveEndPointSegment[seg, FALSE, GGTransform.Transform[transform, seg.hi]]; }; ENDLOOP; }; TransformSequenceClosed: PUBLIC PROC [seq: Sequence, transform: ImagerTransformation.Transformation] = { << A tricky operation. All selected segments are transformed. Then, all segments which are not selected but which touch a selected joint are told that the joint has moved.>> seg: Segment; hiSeg: NAT _ HiSegment[seq.traj]; FOR i: NAT IN [0..hiSeg-1] DO seg _ FetchSegment[seq.traj, i]; IF seq.segments[i] THEN { GGSegment.TransformSegment[seg, transform]; } ELSE { IF seq.joints[i] THEN GGSegment.MoveEndPointSegment[seg, TRUE, GGTransform.Transform[transform, seg.lo]]; IF seq.joints[i+1] THEN GGSegment.MoveEndPointSegment[seg, FALSE, GGTransform.Transform[transform, seg.hi]]; }; ENDLOOP; seg _ FetchSegment[seq.traj, hiSeg]; IF seq.segments[hiSeg] THEN { GGSegment.TransformSegment[seg, transform]; } ELSE { IF seq.joints[hiSeg] THEN GGSegment.MoveEndPointSegment[seg, TRUE, GGTransform.Transform[transform, seg.lo]]; IF seq.joints[0] THEN GGSegment.MoveEndPointSegment[seg, FALSE, GGTransform.Transform[transform, seg.hi]]; }; }; <<>> <> <<>> DescribeHit: PUBLIC PROC [traj: Traj, hitType: HitType, segNum, cpNum, jointNum: INT] RETURNS [rope: Rope.ROPE] = { SELECT hitType FROM joint => rope _ GGDescribe.DescribeJoint[traj, jointNum]; controlPoint => rope _ GGDescribe.DescribeControlPoint[traj, segNum, cpNum]; segment => rope _ GGDescribe.DescribeSegment[traj, segNum]; ENDCASE => ERROR; }; GetArrowCode: PROC [traj: Traj] RETURNS [code: NAT] = { code _ IF NOT traj.hiArrow THEN IF NOT traj.loArrow THEN 0 ELSE 1 ELSE IF NOT traj.loArrow THEN 2 ELSE 3; }; Fileout: PUBLIC PROC [f: IO.STREAM, traj: Traj] = { roleRope: Rope.ROPE _ RoleToRope[traj.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 _ HiJoint[traj]; <> arrowCode _ GetArrowCode[traj]; f.PutF["Traj (%g) [%g] arrows: %g ", [rope[roleRope]], [integer[hiJoint]], [integer[arrowCode]]]; f.PutRope["j: "]; -- traj Joint GGParseOut.WriteStrokeJoint[f, traj.strokeJoint]; <> [endsOK, end] _ AllStrokeEndsEqual[traj]; f.PutRope[" e: "]; GGParseOut.WriteBOOL[f, endsOK]; f.PutChar[IO.SP]; GGParseOut.WriteStrokeEnd[f, end]; f.PutChar[IO.SP]; strokeWidth _ AllStrokeWidthsEqual[traj]; strokeOK _ strokeWidth >= 0.0; f.PutF["w: %g ", [real[strokeWidth]]]; [colorOK, color] _ AllStrokeColorsEqual[traj]; 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[traj]; 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 _ FetchJointPos[traj, 0]; GGParseOut.WritePoint[f, point]; FOR index: NAT IN [1..hiJoint] DO seg _ FetchSegment[traj, 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 _ FetchJointPos[traj, index]; GGParseOut.WritePoint[f, point]; ENDLOOP; IF (traj.role = fence OR traj.role = hole) THEN { seg _ FetchSegment[traj, 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[")"]; }; }; Filein: PUBLIC PROC [f: IO.STREAM, version: REAL] RETURNS [traj: Traj, hasCircle: BOOL _ FALSE] = { hiJoint, arrowCode: NAT; role: FenceHoleOpen; roleName, className: Rope.ROPE; pFirst, p0, p1: Point; seg: Segment; class: SegmentClass; strokeWidth, offset, length: REAL; pattern: SequenceOfReal; strokeColor: Color; strokeEnd: StrokeEnd; strokeJoint: StrokeJoint; success, colorOK, widthOK, endsOK, dashOK, dashed, good: BOOL; <> GGParseIn.ReadBlankAndRope[f, "Traj"]; GGParseIn.ReadBlankAndRope[f, "("]; roleName _ GGParseIn.ReadBlankAndWord[f]; role _ RoleFromRope[roleName]; GGParseIn.ReadBlankAndRope[f, ")"]; GGParseIn.ReadBlankAndRope[f, "["]; hiJoint _ GGParseIn.ReadBlankAndNAT[f]; GGParseIn.ReadBlankAndRope[f, "]"]; IF version >= 8607.22 THEN { GGParseIn.ReadBlankAndRope[f, "arrows:"]; arrowCode _ GGParseIn.ReadBlankAndNAT[f]; } ELSE arrowCode _ 0; IF version >= 8702.26 THEN { GGParseIn.ReadBlankAndRope[f, "j:"]; strokeJoint _ GGParseIn.ReadStrokeJoint[f]; GGParseIn.ReadBlankAndRope[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 { good: BOOL; GGParseIn.ReadBlankAndRope[f, "w:"]; strokeWidth _ GGParseIn.ReadBlankAndReal[f]; widthOK _ strokeWidth>= 0.0; GGParseIn.ReadBlankAndRope[f, "c:"]; [colorOK, good] _ GGParseIn.ReadBOOL[f, version]; IF NOT good THEN ERROR; strokeColor _ GGParseIn.ReadColor[f, version]; } ELSE { GGParseIn.ReadBlankAndRope[f, ":"]; widthOK _ FALSE; colorOK _ FALSE; }; IF version >= 8701.23 THEN { GGParseIn.ReadBlankAndRope[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.ReadBlankAndReal[f]; length _ GGParseIn.ReadBlankAndReal[f]; }; }; } ELSE { dashOK _ TRUE; dashed _ FALSE; }; <> pFirst _ p0 _ GGParseIn.ReadPoint[f]; traj _ CreateTraj[p0]; traj.strokeJoint _ strokeJoint; FOR index: NAT IN [1..hiJoint] DO GGParseIn.ReadBlankAndRope[f, "("]; className _ GGParseIn.ReadBlankAndWord[f]; IF NOT widthOK THEN strokeWidth _ GGParseIn.ReadBlankAndReal[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.ReadBlankAndReal[f]; length _ GGParseIn.ReadBlankAndReal[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.ReadBlankAndRope[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 _ AddSegment[traj, hi, GGSegment.MakeArc[newJoint, newCp, newJoint, NIL], lo]; CloseByDistorting[traj, lo]; hasCircle _ TRUE; } ELSE success _ AddSegment[traj, hi, seg, lo]; IF NOT success THEN ERROR; p0 _ p1; ENDLOOP; IF (role = fence OR role = hole) THEN { GGParseIn.ReadBlankAndRope[f, "("]; className _ GGParseIn.ReadBlankAndWord[f]; IF NOT widthOK THEN strokeWidth _ GGParseIn.ReadBlankAndReal[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.ReadBlankAndReal[f]; length _ GGParseIn.ReadBlankAndReal[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.ReadBlankAndRope[f, ")"]; seg.strokeWidth _ strokeWidth; seg.color _ strokeColor; seg.strokeEnd _ strokeEnd; seg.dashed _ dashed; seg.pattern _ pattern; seg.offset _ offset; seg.length _ length; IF traj.segCount=0 THEN { -- special case of closed single segment trajectory success _ AddSegment[traj, hi, seg, lo]; IF NOT success THEN ERROR; CloseByDistorting[traj, lo]; } ELSE CloseWithSegment[traj, seg, lo]; }; }; 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; }; <<>> <> FetchSegment: PUBLIC PROC [traj: Traj, index: NAT] RETURNS [seg: Segment] = { seg _ NARROW[Rosary.Fetch[traj.segments, index]]; }; FetchJoint: PUBLIC PROC [traj: Traj, index: NAT] RETURNS [joint: Joint] = { joint _ NARROW[Rosary.Fetch[traj.joints, index]]; }; FetchJointPos: PUBLIC PROC [traj: Traj, index: NAT] RETURNS [point: Point] = { joint: Joint; IF index < 0 OR index > HiJoint[traj] THEN ERROR; joint _ NARROW[Rosary.Fetch[traj.joints, index]]; point _ joint.point }; LastJointPos: PUBLIC PROC [traj: Traj] RETURNS [point: Point] = { joint: Joint _ NARROW[Rosary.Fetch[traj.joints, traj.segCount]]; point _ joint.point }; SetJointPos: PUBLIC PROC [traj: Traj, index: NAT, newPos: Point] = { <> joint: Joint _ NARROW[Rosary.Fetch[traj.joints, index]]; segLeft, segRight: Segment; joint.point _ newPos; IF index > 0 THEN { segLeft _ FetchSegment[traj, index-1]; segLeft.hi _ newPos; }; IF index < traj.segCount THEN { segRight _ FetchSegment[traj, index]; segRight.lo _ newPos; }; }; HiSegment: PUBLIC PROC [traj: Traj] RETURNS [highestIndex: NAT] = { highestIndex _ traj.segCount - 1; }; HiJoint: PUBLIC PROC [traj: Traj] RETURNS [highestIndex: NAT] = { SELECT traj.role FROM open => highestIndex _ traj.segCount; fence, hole => highestIndex _ traj.segCount -1; ENDCASE => ERROR; }; PreviousSegment: PUBLIC PROC [traj: Traj, segNum: NAT] RETURNS [prev: Segment] = { IF traj.role = open THEN { IF segNum = 0 THEN RETURN[NIL] ELSE prev _ FetchSegment[traj, segNum - 1]; } ELSE { prev _ FetchSegment[traj, (segNum-1+traj.segCount) MOD traj.segCount]; }; }; PreviousSegmentNum: PUBLIC PROC [traj: Traj, segNum: NAT] RETURNS [prevNum: INT] = { prevNum _ IF traj.role = open THEN segNum - 1 ELSE (segNum-1+traj.segCount) MOD traj.segCount; }; FollowingSegmentNum: PUBLIC PROC [traj: Traj, segNum: NAT] RETURNS [followNum: INT] = { IF traj.role = open THEN followNum _ IF segNum = HiSegment[traj] THEN -1 ELSE segNum + 1 ELSE followNum _ (segNum+1) MOD traj.segCount; }; FollowingJoint: PUBLIC PROC [traj: Traj, index: NAT] RETURNS [nextIndex: INT] = { SELECT traj.role FROM open => nextIndex _ IF index = traj.segCount THEN -1 ELSE index + 1; fence, hole => nextIndex _ (index + 1) MOD traj.segCount; ENDCASE => ERROR; }; IsEndJoint: PUBLIC PROC [traj: Traj, index: NAT] RETURNS [BOOL] = { SELECT traj.role FROM open => RETURN[index = 0 OR index = traj.segCount]; fence, hole => RETURN[FALSE]; ENDCASE => ERROR; }; <<>> <> <> SetTrajPartField: PROC [traj: Traj, selected: BOOL, selectClass: SelectionClass] = { SELECT selectClass FROM normal => traj.selectedInPart.normal _ selected; hot => traj.selectedInPart.hot _ selected; active => traj.selectedInPart.active _ selected; ENDCASE; }; SetSelectedFields: PUBLIC PROC [seq: Sequence, selected: BOOL, selectClass: SelectionClass] = { joint: Joint; jointGen: JointGenerator; segGen: SegmentGenerator; SetTrajPartField[seq.traj, selected, selectClass]; jointGen _ GGSequence.JointsInSequence[seq]; <> FOR jointNum: INT _ GGSequence.NextJoint[jointGen], GGSequence.NextJoint[jointGen] UNTIL jointNum = -1 DO joint _ FetchJoint[seq.traj, jointNum]; SetJointField[joint, selected, selectClass]; ENDLOOP; <> segGen _ GGSequence.SegmentsInSequence[seq]; FOR seg: Segment _ GGSequence.NextSegment[segGen], GGSequence.NextSegment[segGen] UNTIL seg = NIL DO SetSegmentField[seg, selected, selectClass]; ENDLOOP; }; SaveSelection: PUBLIC PROC [traj: Traj, selectClass: SelectionClass, scene: Scene] = { seq: Sequence _ GGSelect.FindSelectedSequence[traj, scene, selectClass]; IF seq=NIL THEN ClearSelection[traj, selectClass] ELSE SaveSelectionInSequence[seq, selectClass]; }; SaveSelectionInSequence: PUBLIC PROC [seq: Sequence, selectClass: SelectionClass] = { seg: Segment; joint: Joint; FOR i: NAT IN [0..HiSegment[seq.traj]] DO seg _ FetchSegment[seq.traj, i]; SetSegmentField[seg, seq.segments[i], selectClass]; FOR j: NAT IN [0..seg.class.controlPointCount[seg]-1] DO SetControlPointField[seg, j, seq.controlPoints[i][j], selectClass]; ENDLOOP; ENDLOOP; FOR i: NAT IN [0..HiJoint[seq.traj]] DO joint _ FetchJoint[seq.traj, i]; SetJointField[joint, seq.joints[i], selectClass]; ENDLOOP; }; ClearSelection: PUBLIC PROC [traj: Traj, selectClass: SelectionClass] = { seg: Segment; joint: Joint; FOR i: NAT IN [0..HiSegment[traj]] DO seg _ FetchSegment[traj, i]; SetSegmentField[seg, FALSE, selectClass]; FOR j: NAT IN [0..seg.class.controlPointCount[seg]-1] DO SetControlPointField[seg, j, FALSE, selectClass]; ENDLOOP; ENDLOOP; FOR i: NAT IN [0..HiJoint[traj]] DO joint _ FetchJoint[traj, i]; SetJointField[joint, FALSE, selectClass]; ENDLOOP; }; RemakeSelection: PUBLIC PROC [traj: Traj, scene: Scene, selectClass: SelectionClass] = { seq: Sequence _ GGSequence.CreateEmpty[traj]; seg: Segment; joint: Joint; FOR i: NAT IN [0..HiSegment[traj]] DO seg _ FetchSegment[traj, i]; seq.segments[i] _ GetSegmentField[seg, selectClass]; IF seq.segments[i] THEN seq.segCount _ seq.segCount + 1; FOR j: NAT IN [0..seg.class.controlPointCount[seg]-1] DO seq.controlPoints[i][j] _ GetControlPointField[seg, j, selectClass]; IF seq.controlPoints[i][j] THEN seq.controlPointCount _ seq.controlPointCount + 1; ENDLOOP; ENDLOOP; FOR i: NAT IN [0..HiJoint[traj]] DO joint _ FetchJoint[traj, i]; seq.joints[i] _ GetJointField[joint, selectClass]; IF seq.joints[i] THEN seq.jointCount _ seq.jointCount + 1; ENDLOOP; GGSelect.SelectSequence[seq, scene, selectClass]; }; SetControlPointField: PUBLIC PROC [seg: Segment, cpNum: NAT, selected: BOOL, selectClass: SelectionClass] = { seg.class.controlPointFieldSet[seg, cpNum, selected, selectClass]; }; GetControlPointField: PUBLIC PROC [seg: Segment, cpNum: NAT, selectClass: SelectionClass] RETURNS [selected: BOOL] = { selected _ seg.class.controlPointFieldGet[seg, cpNum, selectClass]; }; GetJointField: PUBLIC PROC [joint: Joint, selectClass: SelectionClass] RETURNS [selected: BOOL] = { SELECT selectClass FROM normal => selected _ joint.TselectedInFull.normal; hot => selected _ joint.TselectedInFull.hot; active => selected _ joint.TselectedInFull.active; ENDCASE; }; GetSegmentField: PUBLIC PROC [seg: Segment, selectClass: SelectionClass] RETURNS [selected: BOOL] = { SELECT selectClass FROM normal => selected _ seg.TselectedInFull.normal; hot => selected _ seg.TselectedInFull.hot; active => selected _ seg.TselectedInFull.active; ENDCASE; }; SetJointField: PUBLIC PROC [joint: Joint, selected: BOOL, selectClass: SelectionClass] = { SELECT selectClass FROM normal => joint.TselectedInFull.normal _ selected; hot => joint.TselectedInFull.hot _ selected; active => joint.TselectedInFull.active _ selected; ENDCASE; }; SetSegmentField: PUBLIC PROC [seg: Segment, selected: BOOL, selectClass: SelectionClass] = { SELECT selectClass FROM normal => seg.TselectedInFull.normal _ selected; hot => seg.TselectedInFull.hot _ selected; active => seg.TselectedInFull.active _ selected; ENDCASE; }; <> CopyJoint: PROC [joint: Joint] RETURNS [copy: Joint] = { copy _ NEW[JointObj _ [point: joint.point, TselectedInFull: joint.TselectedInFull] ]; }; <<>> <> <<>> TrajPointGeneratorData: TYPE = REF TrajPointGeneratorDataObj; TrajPointGeneratorDataObj: TYPE = RECORD [ jointsDone: BOOL, jointGen: JointGenerator, cpGen: GGModelTypes.ControlPointGenerator ]; TrajPointPairGeneratorData: TYPE = REF TrajPointPairGeneratorDataObj; TrajPointPairGeneratorDataObj: TYPE = RECORD [ segGen: SegmentGenerator ]; PointsInDescriptor: PUBLIC PROC [seq: Sequence] RETURNS [pointGen: GGModelTypes.PointGenerator] = { pgd: TrajPointGeneratorData; jointGen: JointGenerator; cpGen: GGModelTypes.ControlPointGenerator; jointGen _ GGSequence.JointsInSequence[seq]; cpGen _ GGSequence.ControlPointsInSequence[seq]; pgd _ NEW[TrajPointGeneratorDataObj _ [FALSE, jointGen, cpGen]]; pointGen _ NEW[GGModelTypes.PointGeneratorObj _ [NIL, 0, 0, pgd]]; }; PointPairsInDescriptor: PUBLIC PROC [seq: Sequence] RETURNS [pointPairGen: GGModelTypes.PointPairGenerator] = { pgd: TrajPointPairGeneratorData; segGen: SegmentGenerator; segGen _ GGSequence.SegmentsInSequence[seq]; pgd _ NEW[TrajPointPairGeneratorDataObj _ [segGen]]; pointPairGen _ NEW[GGModelTypes.PointPairGeneratorObj _ [NIL, 0, 0, pgd]]; }; NextPoint: PUBLIC PROC [pointGen: GGModelTypes.PointGenerator] RETURNS [pointAndDone: GGModelTypes.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 { pointAndDone.point _ FetchJointPos[pgd.jointGen.seq.traj, nextJoint]; pointAndDone.done _ FALSE; RETURN; }; }; <