<> <> 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] ~ { <> 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] ~ { <> <> 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: REAL _ MAX[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.