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. ¨PEBezierImpl.mesa Copyright (C) 1984 by Xerox Corporation. All rights reserved. Written by Darlene Plebon on August 17, 1983 2:35 pm Bezier routines for the Path Editor. This routine runs the bezier subdivision algorithm on a bezier cubic segment applying the specified client procedure to each straight line segment generated. The client procedure is passed the endpoints of the line segment and the value of the parameter t at the endpoints. This routine uses flatness and maximum depth tests to control the recursion. This routine runs the bezier subdivision algorithm on a bezier cubic segment applying the specified client procedure to each point generated. The client procedure is passed the point and the value of the parameter t at that point. The subdivision is halted when we get down to pixel resolution. Generate the last point since DoBezierSubdivideUntilPoints misses it. This routine runs the bezier subdivision algorithm on a bezier cubic segment applying the specified client procedure to each point generated. The client procedure is passed the point and the value of the parameter t at that point. The subdivision is halted when we get down to pixel resolution. Note that this routine only calls the client procedure with the first of the two knots in a segment of fine enough resolution, thus missing the very last point in the bezier segment that the client called us with. This routine converts a bezier spline segment from our segment representation to the bezier representation used by the CGCubic routines. This routine converts a bezier spline segment from the bezier representation used by the CGCubic routines to our segment representation. Two vertices within epsilon of each other are considered identical, and only one of the two is inserted in the segment, thus permitting lines and quadratics. This routine always generates a segment with at least one vertex in it. Note that the fp field of the segment is not initialized properly since this is a pointer back into another segment. This routine splits a bezier spline segment into two segments at the specified point on the curve, recomputing the control points so that the two new segments form the original curve. Êð˜Jšœ™J™>Jšœ4™4J˜J™$J˜šÏk ˜ Jšœœ˜$Jšœ œ˜"Jšœ ˜ Jšœœ"˜7Jšœœ)˜6J˜—šœœ˜Jšœ#˜+Jšœ ˜—J˜Jšœœ/˜9J˜š Ïnœœ/œ œ œ ˜†J™ßJšœœ˜Jšœ œ˜Jšœœœ%˜[šœ˜Jšœœ˜Jšœ˜Jšœ!˜!JšœT˜TJšœT˜TJšœ˜—J˜—J˜š žœœ œ-œ œ ˜xJ™¨JšœI˜IJšœE™EJšœ˜J˜—J˜šžœ œ-œ œ ˜sJ™ÿJšœœ˜Jšœ-œ˜Mšœ˜Jšœœ˜Jšœ˜Jšœ!˜!JšœG˜GJšœG˜GJšœ˜—J˜—J˜šžœœœ˜QJ™ˆJšœœ&˜>šœ œœ˜Jšœ œ˜Jšœ œ˜%Jšœ3˜3Jšœ˜—šœ˜˜Jšœ˜Jšœ˜Jšœ˜J˜—˜Jšœ˜Jšœ)˜)Jšœ)˜)J˜—˜Jšœ)˜)Jšœ)˜)Jšœ.˜.J˜—šœ˜ Jšœ)˜)Jšœ.˜.Jšœ3˜3Jšœ˜——J˜—J˜šžœœœ˜QJ™æšž œ œœ œ˜AJšœœ˜Jšœ!˜'J˜—Jšœ œ˜,Jšœ5œ"œ˜_Jš œ!œœ6œ"œ˜«Jšœ!œ6œ"œ˜‡J˜—J˜š ž œœ œœœ˜fJ™·Jšœ œ ˜J˜ Jšœ˜JšœE˜EJšœE˜EJšœE˜EJšœE˜EJšœ?˜?Jšœ?˜?Jšœ1˜1Jšœ1˜1Jšœ1˜1Jšœ1˜1Jšœ7˜7Jšœ7˜7Jšœ˜Jšœ˜J˜—J˜Jš˜J˜—…—¼T