Nov13Impl.mesa
Copyright (C) 1985, Xerox Corporation. All rights reserved.
Michael Plass, November 13, 1985 0:02:53 am PST
Doug Wyatt, November 25, 1985 3:03:51 pm PST
DIRECTORY Vector2, Imager, Real, Rope, ImagerPath, Interpress, ImagerInterpress, RealFns;
Nov13Impl: CEDAR PROGRAM
IMPORTS Imager, ImagerPath, ImagerInterpress, Real, RealFns, Vector2
~ BEGIN
Context: TYPE ~ Imager.Context;
VEC: TYPE ~ Vector2.VEC;
ROPE: TYPE ~ Rope.ROPE;
PathProc: TYPE ~ Imager.PathProc;
constructionWidth: REAL ← 1.0;
dotWidth: REAL ← 6.0;
dashSize: REAL ← 6.0;
curveWidth: REAL ← 3.0;
MeasurePath: PROC [path: PathProc] RETURNS [sum: REAL ← 0.0] ~ {
Bezier: TYPE ~ ARRAY [0..4) OF VEC;
RealPlusOrMinus: TYPE ~ RECORD [value: REAL, error: REAL];
nPts: NAT ← 6;
AverageSpeed: PROC [p: Bezier] RETURNS [RealPlusOrMinus] ~ {
Computes an approximation to the average speed of the point moving along the curve as a function of its parameter, along with estimated error bounds. Since the curve is parameterized from zero to one, its average speed is numerically equal to its arc length.
nReal: REAL ~ nPts;
min: REAL ← Real.LargestNumber;
max: REAL ← 0;
delta: ARRAY [0..3) OF VEC ~ [Vector2.Sub[p[1], p[0]], Vector2.Sub[p[2], p[1]], Vector2.Sub[p[3], p[2]]];
The deltas form a Bezier description of the velocity profile. We want its average magnitude.
FOR i: NAT IN [0..nPts] DO
t: REAL ~ i/nReal;
s: REAL ~ 1.0-t;
d01: VEC ← Vector2.Add[Vector2.Mul[delta[0], t], Vector2.Mul[delta[1], s]];
d12: VEC ← Vector2.Add[Vector2.Mul[delta[1], t], Vector2.Mul[delta[2], s]];
d012: VEC ← Vector2.Add[Vector2.Mul[d01, t], Vector2.Mul[d12, s]];
sqr: REAL ~ Vector2.Square[d012];
IF sqr > max THEN max ← sqr;
IF sqr < min THEN min ← sqr;
ENDLOOP;
max ← RealFns.SqRt[max];
min ← RealFns.SqRt[min];
RETURN [[(max+min)*1.5, (max-min)*1.5]]
};
sigBits: NAT ← 5;
DivideUntilSmooth: PROC [p: Bezier, piece: PROC [p: Bezier, speed: REAL], fullScale: REAL] ~ {
a: RealPlusOrMinus ← AverageSpeed[p];
UNTIL Real.FScale[a.error, sigBits] <= fullScale DO
Mid: PROC [a,b: VEC] RETURNS [VEC] ~ INLINE {RETURN [[Real.FScale[a.x+b.x, -1], Real.FScale[a.y+b.y, -1]]]};
p01: VEC ~ Mid[p[0],p[1]];
p12: VEC ~ Mid[p[1],p[2]];
p23: VEC ~ Mid[p[2],p[3]];
p012: VEC ~ Mid[p01,p12];
p123: VEC ~ Mid[p12,p23];
p0123: VEC ~ Mid[p012,p123];
DivideUntilSmooth[[p[0], p01, p012, p0123], piece, fullScale];
p ← [p0123, p123, p23, p[3]];
a ← AverageSpeed[p];
ENDLOOP;
piece[p, a.value];
};
SubDivide: PROC [b: Bezier, t: REAL, hi: BOOL] RETURNS [Bezier] ~ {
s: REAL ~ 1-t;
Interpolate: PROC [a, b: VEC] RETURNS [VEC] ~ INLINE {
RETURN [[a.x*s+b.x*t, a.y*s+b.y*t]]
};
q1 : VEC ~ Interpolate[b[0], b[1]];
q2: VEC ~ Interpolate[b[1], b[2]];
q3: VEC ~ Interpolate[b[2], b[3]];
qp1: VEC ~ Interpolate[q1, q2];
qp2: VEC ~ Interpolate[q2, q3];
q: VEC ~ Interpolate[qp1, qp2];
RETURN [IF hi THEN [q, qp2, q3, b[3]] ELSE [b[0], q1, qp1, q]]
};
SubPiece: PROC [b: Bezier, t0, t1: REAL] RETURNS [Bezier] ~ {
IF t1 # 1.0 THEN {
b ← SubDivide[b, t1, FALSE];
t0 ← t0/t1;
t1 ← 1;
};
IF t0 # 0.0 THEN b ← SubDivide[b, t0, TRUE];
RETURN [b];
};
lp: VEC ← [0,0];
move: PROC [p0: VEC] ~ {
lp ← p0;
};
line: PROC [p1: VEC] ~ {
sum ← sum + Vector2.Length[Vector2.Sub[p1, lp]];
lp ← p1;
};
piece: PROC [p: Bezier, speed: REAL] ~ {
sum ← sum + speed;
};
curve: PROC [p1, p2, p3: VEC] ~ TRUSTED {
p: Bezier ~ [lp, p1, p2, p3];
fullScale: REAL ~ AverageSpeed[p].value;
IF fullScale > 0.0 THEN DivideUntilSmooth[p, piece, fullScale];
lp ← p3;
};
conic: PROC [p1, p2: VEC, r: REAL] ~ {
ImagerPath.ConicToCurves[lp, p1, p2, r, curve]
};
arc: PROC [p1, p2: VEC] ~ {ImagerPath.ArcToConics[lp, p1, p2, conic]};
path[moveTo: move, lineTo: line, curveTo: curve, conicTo: conic, arcTo: arc];
};
Bisect: PROC [context: Context, a, b: VEC, t: REAL, wScale: REAL ← 1.0] RETURNS [c: VEC] ~ {
path: PathProc ~ {moveTo[a]; lineTo[b]};
pattern: PROC [i: NAT] RETURNS [REAL] ~ {RETURN [dashSize]};
len: REAL ~ MeasurePath[path];
nDashes: NAT ← Real.Round[len/(2*dashSize)+1];
IF nDashes MOD 2 = 1 THEN nDashes ← nDashes + 1;
Imager.SetStrokeWidth[context, constructionWidth*wScale];
Imager.SetStrokeEnd[context, round];
IF wScale # 0 THEN Imager.MaskDashedStroke[context, path, 1, pattern, dashSize/2, nDashes*2*dashSize];
Imager.SetStrokeWidth[context, dotWidth];
Imager.MaskVector[context, a, a];
Imager.MaskVector[context, b, b];
c ← b.Sub[a].Mul[t].Add[a];
Imager.MaskVector[context, c, c];
};
BezierSkeleton: PROC [context: Imager.Context, p: ARRAY [0..4) OF VEC, t: REAL] ~ {
p01: VEC ~ Bisect[context, p[0], p[1], t, 2];
p12: VEC ~ Bisect[context, p[1], p[2], t, 2];
p23: VEC ~ Bisect[context, p[2], p[3], t, 2];
p012: VEC ~ Bisect[context, p01, p12, t];
p123: VEC ~ Bisect[context, p12, p23, t];
p0123: VEC ~ Bisect[context, p012, p123, t];
};
BezierCurve: PROC [context: Imager.Context, p: ARRAY [0..4) OF VEC] ~ {
path: PathProc ~ {moveTo[p[0]]; curveTo[p[1], p[2], p[3]]};
Imager.SetStrokeEnd[context, round];
Imager.MaskStroke[context, path];
};
bezierExample: ARRAY [0..4) OF VEC ← [[155.0,345.0], [159.0,501.0], [394.0,510.0], [453.0,313.0]];
splitRatio: REAL ← 0.5;
BezierExample: PROC [context: Imager.Context] ~ {
Imager.SetGray[context, 1];
Imager.SetStrokeWidth[context, curveWidth];
BezierCurve[context, bezierExample];
Imager.SetGray[context, .7];
Imager.SetStrokeWidth[context, curveWidth-2];
BezierCurve[context, bezierExample];
Imager.SetGray[context, 1];
BezierSkeleton[context, bezierExample, splitRatio];
};
ConicCurve: PROC [context: Imager.Context, p: ARRAY [0..3) OF VEC, r: REAL] ~ {
path: PathProc ~ {moveTo[p[0]]; conicTo[p[1], p[2], r]};
Imager.SetStrokeEnd[context, round];
Imager.MaskStroke[context, path];
};
ConicSkeleton: PROC [context: Imager.Context, p: ARRAY [0..3) OF VEC, r: REAL, w: REAL ← 1] ~ {
p0: VEC ~ Bisect[context, p[0], p[1], 0, 2*w];
p2: VEC ~ Bisect[context, p[1], p[2], 1, 2*w];
p02: VEC ~ Bisect[context, p[0], p[2], 0.5, w];
m: VEC ~ Bisect[context, p02, p[1], r, w];
};
conicExample: ARRAY [0..3) OF VEC ← [[155.0,345.0], [225.0,505.0], [453.0,313.0]];
conicRatio: REAL ← 0.75;
ConicInstance: PROC [context: Imager.Context, conicRatio: REAL, w: REAL] ~ {
Imager.SetGray[context, 1];
Imager.SetStrokeWidth[context, curveWidth];
ConicCurve[context, conicExample, conicRatio];
Imager.SetGray[context, .7];
Imager.SetStrokeWidth[context, curveWidth-2];
ConicCurve[context, conicExample, conicRatio];
Imager.SetGray[context, 1];
ConicSkeleton[context, conicExample, conicRatio, w];
};
ConicExample: PROC [context: Imager.Context] ~ {
ConicInstance[context, 0.25, 0];
ConicInstance[context, 0.5, 0];
ConicInstance[context, 0.75, 1];
};
ArcCurve: PROC [context: Imager.Context, p: ARRAY [0..3) OF VEC] ~ {
path: PathProc ~ {moveTo[p[0]]; arcTo[p[1], p[2]]};
Imager.SetStrokeEnd[context, round];
Imager.SetGray[context, 1];
Imager.SetStrokeWidth[context, curveWidth];
Imager.MaskStroke[context, path];
Imager.SetGray[context, .7];
Imager.SetStrokeWidth[context, curveWidth-2];
Imager.MaskStroke[context, path];
Imager.SetGray[context, 1];
[] ← Bisect[context, p[0], p[1], 0];
[] ← Bisect[context, p[1], p[2], 0];
};
ArcExample: PROC [context: Imager.Context] ~ {
Imager.TranslateT[context, [0, 72]];
ArcCurve[context, conicExample];
ArcCurve[context, [[155.0,345.0-160], [453.0,313.0-160], [155.0,345.0-160]]];
Imager.TranslateT[context, [0, -72]];
};
pat: ARRAY [0..4) OF REAL ← [18,9,0,9];
FillInstance: PROC [context: Imager.Context, parity: BOOL] ~ {
pattern: PROC [i: NAT] RETURNS [REAL] ~ {RETURN [pat[i]]};
path: PathProc ~ {moveTo[[93.0,127.0]]; lineTo[[175.0,331.0]]; lineTo[[247.0,123.0]]; lineTo[[62.0,270.0]]; lineTo[[302.0,288.0]]; lineTo[[93.0,127.0]];};
len: REAL ~ MeasurePath[path];
nDashes: NAT ← Real.Round[len/(2*pat[0])+1];
Imager.SetGray[context, 0.3];
Imager.MaskFill[context, path, parity];
Imager.SetGray[context, 1];
Imager.SetStrokeWidth[context, 3];
Imager.SetStrokeEnd[context, round];
Imager.MaskDashedStroke[context, path, 4, pattern, 0, nDashes*2*pat[0]];
};
FillExample: PROC [context: Imager.Context] ~ {
FillInstance[context, FALSE];
Imager.TranslateT[context, [210.0,300.0]];
FillInstance[context, TRUE];
Imager.TranslateT[context, [-210.0,-300.0]];
};
tightPat: ARRAY [0..2) OF REAL ← [0.5, 2.5];
tightWidth: REAL ← 72+36;
TightExample: PROC [context: Imager.Context] ~ {
pattern: PROC [i: NAT] RETURNS [REAL] ~ {RETURN [tightPat[i]]};
path: PathProc ~ {moveTo[[0,0]]; curveTo[[10, 10], [100, 10], [100, -300]]};
Imager.TranslateT[context, [300, 500]];
Imager.SetStrokeWidth[context, 2];
Imager.SetStrokeEnd[context, round];
Imager.MaskStroke[context, path];
Imager.SetStrokeWidth[context, tightWidth];
Imager.SetStrokeEnd[context, butt];
Imager.MaskDashedStroke[context, path, 2, pattern, 0, 0];
Imager.TranslateT[context, [-300, -500]];
};
MakeIP: PROC ~ {
points: REAL ← 0.0254/72;
interpress: ImagerInterpress.Ref ← ImagerInterpress.Create["Nov13.interpress"];
This is where the output will go.
ImagerInterpress.DoPage[interpress, BezierExample, points];
ImagerInterpress.DoPage[interpress, ConicExample, points];
ImagerInterpress.DoPage[interpress, ArcExample, points];
ImagerInterpress.DoPage[interpress, FillExample, points];
ImagerInterpress.DoPage[interpress, TightExample, points];
ImagerInterpress.Close[interpress];
};
END.