ImagerStrokeImpl.mesa
Michael Plass, July 25, 1983 11:04 am
DIRECTORY ImagerBasic, ImagerMasks, ImagerStroke, ImagerTransform, Real, RealFns, ScanConverter, LFUtil;
ImagerStrokeImpl: CEDAR PROGRAM
IMPORTS ImagerTransform, Real, RealFns, ImagerMasks, ScanConverter, LFUtil
EXPORTS ImagerStroke
~ BEGIN
Pair: TYPE ~ ImagerBasic.Pair;
Bezier: TYPE ~ ImagerBasic.Bezier;
Transformation: TYPE ~ ImagerBasic.Transformation;
StrokeEnds: TYPE ~ ImagerBasic.StrokeEnds;
Mask: TYPE ~ ImagerMasks.Mask;
ClientSpaceTolerance: PROC [clientToDevice: Transformation] RETURNS [tolerance: REAL] ~ {
This is a stupid way to compute this.
Tdc: Transformation ← ImagerTransform.Invert[clientToDevice];
tolerance ← 999999999;
FOR theta: NAT IN [0..360) DO
s: REAL ← RealFns.CosDeg[theta];
f: REAL ← RealFns.SinDeg[theta];
p: Pair ← ImagerTransform.TransformVec[[s, f], Tdc];
tolerance ← MIN[tolerance, Real.SqRt[p.x*p.x+p.y*p.y]];
ENDLOOP;
};
PolygonSides: PROC [radius, tolerance: REAL] RETURNS [INT] ~ {
RETURN [2*Real.Fix[3.1416*Real.SqRt[radius/(2*tolerance)]/2 + 1.0]]
};
Dot: PROC [a, b: Pair] RETURNS [REAL] ~ {RETURN [a.x*b.x+a.y*b.y]};
Add: PROC [a, b: Pair] RETURNS [Pair] ~ {RETURN [[a.x+b.x, a.y+b.y]]};
Sub: PROC [a, b: Pair] RETURNS [Pair] ~ {RETURN [[a.x-b.x, a.y-b.y]]};
Split: PROC [bezier: Bezier, t: REAL] RETURNS [Bezier, Bezier] ~ {
r: REAL ← 1 - t;
Mid: PROC [a, b: Pair] RETURNS [Pair] ~ {RETURN [[a.x*t+b.x*r, a.y*t+b.y*r]]};
b01: Pair ← Mid[bezier.b0, bezier.b1];
b12: Pair ← Mid[bezier.b1, bezier.b2];
b23: Pair ← Mid[bezier.b2, bezier.b3];
b02: Pair ← Mid[b01, b12];
b13: Pair ← Mid[b12, b23];
b03: Pair ← Mid[b02, b13];
RETURN [[bezier.b0, b01, b02, b03], [b03, b13, b23, bezier.b3]]
};
StrokeBezier: PROC [bezier: Bezier, radius: Pair, sin, cos: REAL, move: PROC[Pair], line: PROC[Pair], curve: PROC[Pair, Pair, Pair]] RETURNS [rFinal: Pair] ~ {
radius is a vector 90 degrees counterclockwise from bezier.b1-bezier.b0, with its magnitude the desired radius.
sin and cos are for the rotation required to get to the next side of the polygon.
RecursiveStroke: PROC [bezier: Bezier, radius: Pair] ~ {
NextStop: PROC RETURNS [r: Pair, t: REAL] ~ {
ParameterWhenPerpendicularTo: PROC [v: Pair] RETURNS [REAL] ~ {
d0: REAL ~ Dot[v, Sub[bezier.b1, bezier.b0]];
d1: REAL ~ Dot[v, Sub[bezier.b2, bezier.b1]];
d2: REAL ~ Dot[v, Sub[bezier.b3, bezier.b2]];
a: REAL ~ d0 - 2*d1 + d2;
b: REAL ~ 2*(d1 - d0);
c: REAL ~ d0;
t0, t1: REAL ← 2;
IF ABS[a] > 0 THEN {
d: REAL ~ b*b-4*a*c;
IF d>=0 THEN {
sqrt: REAL ~ Real.SqRt[d];
t0 ← (-b-(IF b>0 THEN sqrt ELSE -sqrt))/2.0;
t1 ← IF ABS[t0] > 0 THEN c/t0 ELSE 0.0;
};
}
ELSE IF ABS[b] > 0 THEN t0 ← -c/b
ELSE IF c = 0 THEN t0 ← 1;
IF t0 <= 0 THEN t0 ← 2;
IF t1 <= 0 THEN t1 ← 2;
RETURN [MIN[t0, t1]]
};
r1: Pair ← [radius.x*cos+radius.y*sin, -radius.x*sin+radius.y*cos];
r2: Pair ← [radius.x*cos-radius.y*sin, radius.x*sin+radius.y*cos];
t1: REAL ← ParameterWhenPerpendicularTo[r1];
t2: REAL ← ParameterWhenPerpendicularTo[r2];
IF t2 < t1 THEN RETURN [r2, t2]
ELSE RETURN [r1, t1]
};
r1: Pair;
t1: REAL;
rest: Bezier;
[r1, t1] ← NextStop[];
IF t1 >= 1 THEN {
rFinal ← [bezier.b2.y-bezier.b3.y, bezier.b3.x-bezier.b2.x];
IF ABS[rFinal.x] + ABS[rFinal.y] < 1.0e-20 THEN rFinal ← radius
ELSE {
scale: REAL ← Real.SqRt[(radius.x*radius.x + radius.y*radius.y)/(rFinal.x*rFinal.x + rFinal.y*rFinal.y)];
rFinal ← [rFinal.x*scale, rFinal.y*scale];
};
t1 ← 1; r1 ← rFinal;
}
ELSE [bezier, rest] ← Split[bezier, t1];
curve[Add[bezier.b1, radius], Add[bezier.b2, radius], Add[bezier.b3, radius]];
line[Add[bezier.b3, r1]];
IF t1 # 1 THEN RecursiveStroke[rest, r1];
line[Sub[bezier.b3, r1]];
curve[Sub[bezier.b2, radius], Sub[bezier.b1, radius], Sub[bezier.b0, radius]];
};
move[Add[bezier.b0, radius]];
RecursiveStroke[bezier, radius];
};
GetPoints: PROC ~ {
s, f: INTEGER;
[s, f] ← LFUtil.GetPoint[]; ImagerMasks.MaskConstant[ImagerMasks.LFDisplay[], ImagerMasks.Box[s, f, 1, 1], ImagerMasks.inkTile, 0];
bz.b0 ← [s, f];
[s, f] ← LFUtil.GetPoint[]; ImagerMasks.MaskConstant[ImagerMasks.LFDisplay[], ImagerMasks.Box[s, f, 1, 1], ImagerMasks.inkTile, 0];
bz.b1 ← [s, f];
[s, f] ← LFUtil.GetPoint[]; ImagerMasks.MaskConstant[ImagerMasks.LFDisplay[], ImagerMasks.Box[s, f, 1, 1], ImagerMasks.inkTile, 0];
bz.b2 ← [s, f];
[s, f] ← LFUtil.GetPoint[]; ImagerMasks.MaskConstant[ImagerMasks.LFDisplay[], ImagerMasks.Box[s, f, 1, 1], ImagerMasks.inkTile, 0];
bz.b3 ← [s, f];
};
bz: Bezier;
deviceTolerance: REAL ← 0.125;
DrawBezier: PROC [w: REAL] ~ {
width: REALMAX[w, 0.5];
n: INT ← PolygonSides[width/2, deviceTolerance];
sin: REAL ← RealFns.SinDeg[360.0/n];
cos: REAL ← RealFns.CosDeg[360.0/n];
GenPath: PROC [move: PROC[Pair], line: PROC[Pair], curve: PROC[Pair, Pair, Pair]] = {
Move: PROC [p: Pair] ~ {move[[p.y, p.x]]};
Line: PROC [p: Pair] ~ {line[[p.y, p.x]]};
Curve: PROC [p1, p2, p3: Pair] ~ {curve[[p1.y, p1.x], [p2.y, p2.x], [p3.y, p3.x]]};
[] ← StrokeBezier[bz, r, sin, cos, Move, Line, Curve];
};
devicePath: ScanConverter.DevicePath ← ScanConverter.Allocate[];
mask: Mask;
Runs: PROC[run: PROC[s, fMin: INTEGER, fSize: NAT], repeat: PROC[timesToRepeatScanline: NAT]] ~ {
BoxFromScanConverter: PROC [x, y, w, h: INTEGER] = {
IF h # 1 THEN ERROR;
run[s: y, fMin: x, fSize: w];
};
devicePath.ScanConvert[proc: BoxFromScanConverter];
};
r: Pair ← [bz.b0.y-bz.b1.y, bz.b1.x-bz.b0.x];
IF ABS[r.x] + ABS[r.y] < 1.0e-20 THEN r ← [1, 0]
ELSE {
scale: REAL ← Real.SqRt[(width/2)*(width/2)/(r.x*r.x + r.y*r.y)];
r ← [r.x*scale, r.y*scale];
};
devicePath.PushPath[GenPath];
mask ← ImagerMasks.Create[Runs];
ImagerMasks.MaskConstant[ImagerMasks.LFDisplay[], mask, ImagerMasks.inkTile, 0, [xor, null]];
};
END.