ImagerPath.mesa
Copyright © 1984 Xerox Corporation. All rights reserved.
Doug Wyatt, November 8, 1984 10:02:31 am PST
DIRECTORY
ImagerTransformation USING [Transformation],
Vector2 USING [VEC];
ImagerPath: CEDAR DEFINITIONS
~ BEGIN
VEC: TYPE ~ Vector2.VEC;
PathProc:
TYPE ~
PROC[
pathData: REF,
moveTo: PROC[p: VEC],
lineTo: PROC[p1: VEC],
curveTo: PROC[p1, p2, p3: VEC],
conicTo: PROC[p1, p2: VEC, r: REAL],
arcTo: PROC[p1, p2: VEC]
];
moveTo: Begin a trajectory at p. New lp = p.
lineTo: Extend a straight line segment from lp to p. New lp = p.
curveTo: Extend a cubic curve segment (controlled by p1, p2) from lp to p3. New lp = p3.
Let p0 = lp. The curve segment is defined by its four Bezier control points p0, p1, p2, p3. It starts at p0, tangent to the line [p0, p1]; and ends at p3, tangent to the line [p2, p3]. The segment is bounded by the quadrilateral [p0, p1, p2, p3].
conicTo: Extend a conic curve segment (controlled by p1, r) from lp to p2. New lp = p2.
Let p0 = lp. Let m be the midpoint of [p0, p2]. Let p be the point on [m, p1] such that Length[m, p]/Length[m, p1] = r. The curve segment starts at p0, passes through p, and ends at p2. It is a line if r=0; an ellipse if 0<r<1/2; a parabola if r=1/2; a hyperbola if 1/2<r<1. The segment is bounded by the triangle [p0, p1, p2].
arcTo: Extend a circular arc (controlled by p1) from lp to p2. New lp = p2.
Let p0 = lp. The arc starts at p0, passes through p1, and ends at p2. It is best for p1 to lie near the halfway point along the arc. If p0 and p2 are coincident, arcTo produces a circle: p1 lies on the circle diametrically opposite p0. If p0, p1, and p2 are colinear, the effect is { lineTo[p1]; lineTo[p2] }.
ConicToCurves:
PROC[p0, p1, p2:
VEC, r:
REAL, curve:
PROC[p1, p2, p3:
VEC]];
Turns a conic into a sequence of cubic curves. Unless the conic is a parabola, this will necessarily involve some approximation, but the error will be in the range of floating-point fuzz. The first curve should be assumed to start at p0.
ArcToConics:
PROC[p0, p1, p2:
VEC, conic:
PROC[p1, p2:
VEC, r:
REAL]];
Turns an arc into a sequence of conics.
Transform:
PROC[pathProc: PathProc, pathData:
REF,
m: ImagerTransformation.Transformation ← NIL,
moveTo: PROC[p: VEC] ←,
lineTo: PROC[p1: VEC] ←,
curveTo: PROC[p1, p2, p3: VEC] ←,
conicTo: PROC[p1, p2: VEC, r: REAL] ← NIL,
close: PROC ← NIL
];
Filter:
PROC[pathProc: PathProc, pathData:
REF,
moveTo: PROC[p: VEC] ←,
lineTo: PROC[p1: VEC] ←,
curveTo: PROC[p1, p2, p3: VEC] ←,
conicTo: PROC[p1, p2: VEC, r: REAL] ← NIL,
arcTo: PROC[p1, p2: VEC] ← NIL,
close: PROC ← NIL
];
Trajectory: TYPE ~ REF TrajectoryRep;
TrajectoryRep:
TYPE ~
RECORD[
prev: Trajectory, -- link to preceding trajectory
lp: VEC, -- the last point
variant:
SELECT tag: *
FROM
move => [], -- begin new trajectory at lp
line => [], -- straight line, endpoints [prev.lp, lp]
curve => [p1, p2: VEC], -- cubic curve, Bezier control points [prev.lp, p1, p2, lp]
conic => [p1: VEC, r: REAL], -- conic curve, control points [prev.lp, p1, lp]
arc => [p1: VEC], -- arc of a circle, control points [prev.lp, p1, lp]
ENDCASE
];
A Trajectory is an immutable value. Trajectory-constructing operations take a Trajectory and return a new, extended Trajectory. MoveTo starts a new trajectory; other operations extend the current trajectory with a straight or curved segment. Every trajectory has a last point (lp), which is the end point of the trajectory's last segment.
LastPoint:
PROC[t: Trajectory]
RETURNS[
VEC];
Return t's lp.
MoveTo:
PROC[p:
VEC]
RETURNS[Trajectory];
Create a new trajectory, with a single vertex. The result's lp is p.
LineTo: PROC[t: Trajectory, p: VEC] RETURNS[Trajectory];
LineToX: PROC[t: Trajectory, x: REAL] RETURNS[Trajectory];
LineToY:
PROC[t: Trajectory, y:
REAL]
RETURNS[Trajectory];
LineTo extends t with a straight line segment. The result's lp is p.
t.LineToX[x] is equivalent to t.LineTo[[x, t.lp.y]].
t.LineToY[y] is equivalent to t.LineTo[[t.lp.x, y]].
CurveTo:
PROC[t: Trajectory, p1, p2, p3:
VEC]
RETURNS[Trajectory];
CurveTo extends t with a cubic curve segment. The result's lp is p3.
Let p0 = t.lp. The curve segment is defined by the four Bezier control points p0, p1, p2, p3. It starts at p0, tangent to the line [p0, p1]; and ends at p3, tangent to the line [p2, p3]. It is bounded by the quadrilateral [p0, p1, p2, p3].
ConicTo:
PROC[t: Trajectory, p1, p2:
VEC, r:
REAL]
RETURNS[Trajectory];
ConicTo extends t with a segment of a conic section. The result's lp is p2.
Let p0 = t.lp. Let m be the midpoint of [p0, p2]. Let p be the point on [m, p1] such that Length[m, p]/Length[m, p1] = r. The curve segment starts at p0, passes through p, and ends at p2. It is a line if r=0; an ellipse if 0<r<1/2; a parabola if r=1/2; a hyperbola if 1/2<r<1. It is bounded by the triangle [p0, p1, p2].
ArcTo:
PROC[t: Trajectory, p1, p2:
VEC]
RETURNS[Trajectory];
ArcTo extends t with a circular arc. The result's lp is p2.
Let p0 = t.lp. The arc starts at p0, passes through p1, and ends at p2. It is best for p1 to lie near the halfway point along the arc. If p0 and p2 are coincident, ArcTo produces a circle: p1 lies on the circle diametrically opposite p0. If p0, p1, and p2 are colinear, the effect is t.LineTo[p1].LineTo[p2].
TrajectoryList:
TYPE ~
LIST
OF Trajectory;
MapTrajectoryList: PathProc;
Outline: TYPE ~ REF OutlineRep;
OutlineRep:
TYPE ~
RECORD[pathProc: PathProc, pathData:
REF];
MapOutline: PathProc;
OutlineFromTrajectory: PROC[Trajectory] RETURNS[Outline];
OutlineFromTrajectoryList:
PROC[TrajectoryList]
RETURNS[Outline];
TransformToOutline:
PROC[pathProc: PathProc, pathData:
REF,
m: ImagerTransformation.Transformation ← NIL] RETURNS[Outline];
Clipper: TYPE ~ LIST OF ClipperItem;
ClipperItem: TYPE ~ RECORD[outline: Outline, exclude: BOOL];
END.