<> <> <> DIRECTORY CGCubic USING [Bezier, Flat, Split], PEAlgebra USING [DistanceSquared], PEBezier, PETrajectoryOps USING [VertexListLength, InsertVertex], PETypes USING [VertexRec, Point, Segment, SegmentRec]; PEBezierImpl: CEDAR PROGRAM IMPORTS PEAlgebra, CGCubic, PETrajectoryOps EXPORTS PEBezier = BEGIN OPEN PEAlgebra, PEBezier, PETrajectoryOps, PETypes; BezierSubdivideUntilLines: PUBLIC PROCEDURE [bezier: Bezier, proc: BezierLineProc, depth: NAT _ 0, t0: REAL _ 0.0, t3: REAL _ 1.0] = { <> eps: REAL = 1.5; maxDepth: NAT = 10; IF depth >= maxDepth OR CGCubic.Flat[bezier, eps] THEN proc[t0: t0, t3: t3, bezier: bezier] ELSE { tMid: REAL _ (t0 + t3) / 2.0; b1, b2: Bezier; [b1, b2] _ CGCubic.Split[bezier]; BezierSubdivideUntilLines[bezier: b1, proc: proc, depth: depth+1, t0: t0, t3: tMid]; BezierSubdivideUntilLines[bezier: b2, proc: proc, depth: depth+1, t0: tMid, t3: t3]; }; }; BezierSubdivideUntilPoints: PUBLIC PROCEDURE [bezier: Bezier, proc: BezierPointProc, t0: REAL _ 0.0, t3: REAL _ 1.0] = { <> DoBezierSubdivideUntilPoints[bezier: bezier, proc: proc, t0: t0, t3: t3]; <> proc[t: t3, p: bezier.b3]; }; DoBezierSubdivideUntilPoints: PROCEDURE [bezier: Bezier, proc: BezierPointProc, t0: REAL _ 0.0, t3: REAL _ 1.0] = { <> eps: REAL = 1.0; IF DistanceSquared[bezier.b0, bezier.b3] < eps THEN proc[t: t0, p: bezier.b0] ELSE { tMid: REAL _ (t0 + t3) / 2.0; b1, b2: Bezier; [b1, b2] _ CGCubic.Split[bezier]; DoBezierSubdivideUntilPoints[bezier: b1, proc: proc, t0: t0, t3: tMid]; DoBezierSubdivideUntilPoints[bezier: b2, proc: proc, t0: tMid, t3: t3]; }; }; SegmentToBezier: PUBLIC PROCEDURE [segment: Segment] RETURNS [bezier: Bezier] = { <> numberVertices: CARDINAL _ VertexListLength[segment.vertices]; bezier.b0 _ SELECT TRUE FROM segment = NIL => [0.0, 0.0], segment.fp # NIL => segment.fp.point, numberVertices > 0 => segment.vertices.first.point, ENDCASE => [0.0, 0.0]; SELECT numberVertices FROM 0 => { bezier.b1 _ bezier.b0; bezier.b2 _ bezier.b0; bezier.b3 _ bezier.b0; }; 1 => { bezier.b1 _ bezier.b0; bezier.b2 _ segment.vertices.first.point; bezier.b3 _ segment.vertices.first.point; }; 2 => { bezier.b1 _ segment.vertices.first.point; bezier.b2 _ segment.vertices.first.point; bezier.b3 _ segment.vertices.rest.first.point; }; ENDCASE => { bezier.b1 _ segment.vertices.first.point; bezier.b2 _ segment.vertices.rest.first.point; bezier.b3 _ segment.vertices.rest.rest.first.point; }; }; BezierToSegment: PUBLIC PROCEDURE [bezier: Bezier] RETURNS [segment: Segment] = { <> Identical: PROCEDURE [p0, p1: Point] RETURNS [equal: BOOLEAN] = { eps: REAL = 0.05; RETURN [DistanceSquared[p0, p1] < eps]; }; segment _ NEW[SegmentRec _ [type: curveTo]]; [segment.vertices,] _ InsertVertex[segment.vertices, NEW[VertexRec _ [point: bezier.b3]], NIL]; IF ~Identical[bezier.b1,bezier.b2] AND ~Identical[bezier.b2,bezier.b3] THEN [segment.vertices,] _ InsertVertex[segment.vertices, NEW[VertexRec _ [point: bezier.b2]], NIL]; IF ~Identical[bezier.b0,bezier.b1] THEN [segment.vertices,] _ InsertVertex[segment.vertices, NEW[VertexRec _ [point: bezier.b1]], NIL]; }; SplitBezier: PUBLIC PROCEDURE [originalBezier: Bezier, t: REAL] RETURNS [bezier1, bezier2: Bezier] = { <> OneMinusT: REAL _ 1.0 - t; temp: Point; bezier1.b0 _ originalBezier.b0; bezier1.b1.x _ OneMinusT*originalBezier.b0.x + t*originalBezier.b1.x; bezier1.b1.y _ OneMinusT*originalBezier.b0.y + t*originalBezier.b1.y; bezier2.b2.x _ OneMinusT*originalBezier.b2.x + t*originalBezier.b3.x; bezier2.b2.y _ OneMinusT*originalBezier.b2.y + t*originalBezier.b3.y; temp.x _ OneMinusT*originalBezier.b1.x + t*originalBezier.b2.x; temp.y _ OneMinusT*originalBezier.b1.y + t*originalBezier.b2.y; bezier1.b2.x _ OneMinusT*bezier1.b1.x + t*temp.x; bezier1.b2.y _ OneMinusT*bezier1.b1.y + t*temp.y; bezier2.b1.x _ OneMinusT*temp.x + t*bezier2.b2.x; bezier2.b1.y _ OneMinusT*temp.y + t*bezier2.b2.y; bezier1.b3.x _ OneMinusT*bezier1.b2.x + t*bezier2.b1.x; bezier1.b3.y _ OneMinusT*bezier1.b2.y + t*bezier2.b1.y; bezier2.b0 _ bezier1.b3; bezier2.b3 _ originalBezier.b3; }; END.