DIRECTORY Imager, ImagerPath, Real, RealFns, Vector2, JaMIDash; JaMIDashImpl: CEDAR PROGRAM IMPORTS Imager, ImagerPath, Real, RealFns, Vector2 EXPORTS JaMIDash ~ BEGIN testPattern: ARRAY [0..10) OF REAL _ ALL[10.0]; TestPattern: PROC [i: INT] RETURNS [REAL] ~ {RETURN [testPattern[i]]}; TestPath: PathProc ~ { moveTo[[100, 100]]; lineTo[[100, 200]]; lineTo[[200, 100]]; arcTo[[200, 200],[100, 200]]; }; Context: TYPE ~ Imager.Context; PathProc: TYPE ~ ImagerPath.PathProc; VEC: TYPE ~ Vector2.VEC; Bezier: TYPE ~ ARRAY [0..4) OF VEC; Add: PROC [v1, v2: VEC] RETURNS [VEC] ~ { RETURN[[v1.x+v2.x, v1.y+v2.y]] }; Sub: PROC [v1, v2: VEC] RETURNS [VEC] ~ { RETURN[[v1.x-v2.x, v1.y-v2.y]] }; RealPlusOrMinus: TYPE ~ RECORD [value: REAL, error: REAL]; nPts: NAT _ 6; AverageSpeed: PROC [p: Bezier] RETURNS [RealPlusOrMinus] ~ { nReal: REAL ~ nPts; min: REAL _ Real.LargestNumber; max: REAL _ 0; delta: ARRAY [0..3) OF VEC ~ [Sub[p[1], p[0]], Sub[p[2], p[1]], Sub[p[3], p[2]]]; FOR i: NAT IN [0..nPts] DO t: REAL ~ i/nReal; s: REAL ~ 1.0-t; d01: VEC _ Add[Vector2.Mul[delta[0], t], Vector2.Mul[delta[1], s]]; d12: VEC _ Add[Vector2.Mul[delta[1], t], Vector2.Mul[delta[2], s]]; d012: VEC _ 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 _ 7; 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]; }; MeasurePath: PUBLIC PROC [path: PathProc] RETURNS [sum: REAL _ 0.0] ~ { lp: VEC _ [0,0]; move: PROC [p0: VEC] ~ { lp _ p0; }; line: PROC [p1: VEC] ~ { sum _ sum + Vector2.Length[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]; }; MaskDashedStroke: PUBLIC PROC [context: Context, path: PathProc, patternSize: INT, pattern: PROC [i: INT] RETURNS [REAL], offset: REAL, length: REAL _ 0.0] ~ { pathUnits: REAL ~ MeasurePath[path]; stretch: REAL ~ IF length = 0.0 OR pathUnits = 0.0 THEN 1.0 ELSE pathUnits/length; lp: VEC _ [0,0]; index: INT _ 0; -- index of currently active pattern element. residual: REAL _ pattern[0]*stretch; -- remaining part of current pattern element, in master units. used: REAL _ 0.0; -- amount of pattern used, in master units. on: BOOL _ TRUE; Advance: PROC [patternOffset: REAL, startAction, stopAction: PROC _ NIL] ~ { UNTIL used >= patternOffset DO IF residual > 0.0 THEN { IF used+residual <= patternOffset THEN {used _ used+residual; residual _ 0.0} ELSE {residual _ used+residual - patternOffset; used _ patternOffset}; } ELSE { IF on AND stopAction#NIL THEN stopAction[]; index _ index + 1; IF index = patternSize THEN index _ 0; residual _ pattern[index]*stretch; on _ NOT on; IF on AND startAction#NIL THEN startAction[]; }; ENDLOOP; }; DashedPath: PathProc ~ { move: PROC [p0: VEC] ~ {lp _ p0; IF on THEN moveTo[lp]}; line: PROC [p1: VEC] ~ { delta: VEC ~ Vector2.Sub[p1, lp]; d: REAL ~ Vector2.Length[delta]; segmentStart: REAL ~ used; segmentEnd: REAL ~ used + d; start: PROC ~ { s: REAL _ (used-segmentStart)/d; moveTo[Vector2.Add[lp, Vector2.Mul[delta, s]]]; }; stop: PROC ~ { s: REAL _ (used-segmentStart)/d; lineTo[Vector2.Add[lp, Vector2.Mul[delta, s]]]; }; Advance[segmentEnd, start, stop]; IF on THEN lineTo[p1]; lp _ p1; }; curve: PROC [p1, p2, p3: VEC] ~ { piece: PROC [p: Bezier, speed: REAL] ~ { segmentStart: REAL ~ used; segmentEnd: REAL ~ used + speed; dashStartParam: REAL _ 0; needMove: BOOL _ FALSE; start: PROC ~ { dashStartParam _ (used-segmentStart)/speed; needMove _ TRUE; }; stop: PROC ~ { dashEndParam: REAL _ (used-segmentStart)/speed; b: Bezier _ SubPiece[p, dashStartParam, dashEndParam]; IF needMove THEN moveTo[b[0]]; curveTo[b[1], b[2], b[3]]; needMove _ FALSE; }; Advance[segmentEnd, start, stop]; IF on THEN stop[]; }; 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]; }; patternTotal: REAL _ 0.0; FOR i: INT IN [0..patternSize) DO p: REAL _ pattern[i]; IF p < 0 THEN ERROR; patternTotal _ patternTotal + pattern[i]; ENDLOOP; IF patternTotal <= 0 THEN ERROR; WHILE offset < 0 DO offset _ offset + 2*patternTotal ENDLOOP; Advance[offset*stretch]; Imager.MaskStroke[context, DashedPath]; }; END. LJaMIDashImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Michael Plass, December 9, 1985 9:41:29 am PST 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. The deltas form a Bezier description of the velocity profile. We want its average magnitude. Still have part of the current piece to use up. The current piece is all used up; go on to the next. Κ ž˜™Icodešœ Οmœ1™˜>Kšœ˜Kšœ˜Kšžœ˜—Kšœ˜Kšœ˜K˜—š   œžœžœžœžœ ˜CKšœžœ˜š   œžœžœžœžœžœ˜6Kšžœ˜#Kšœ˜—Kšœžœ˜#Kšœžœ˜"Kšœžœ˜"Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšžœžœžœžœ˜>Kšœ˜K˜—š œžœžœžœ ˜=šžœ žœ˜Kšœžœ˜Kšœ ˜ Kšœ˜Kšœ˜—Kšžœ žœžœ˜,Kšžœ˜ Kšœ˜K˜—š   œžœžœžœžœ ˜GKšœžœ ˜šœžœžœ˜Kšœ˜Kšœ˜—šœžœžœ˜Kšœ(˜(Kšœ˜Kšœ˜—šœžœžœ˜(Kšœ˜Kšœ˜—šœžœžœžœ˜)Kšœ˜Kšœ žœ˜(Kšžœžœ(˜?Kšœ˜Kšœ˜—šœžœ žœžœ˜&Kšœ.˜.Kšœ˜—Kšœžœ žœ0˜FKšœM˜MKšœ˜K˜—š œžœžœ1žœ žœžœžœžœ žœ žœ ˜ŸKšœ žœ˜$Kš œ žœžœžœžœžœ˜RKšœžœ ˜KšœžœΟc-˜=Kšœ žœ‘>˜cKšœžœ‘+˜=Kšœžœžœ˜š  œžœžœžœžœ˜Lšžœž˜šžœ˜šžœ˜K™/šžœ˜!Kšžœ'˜+KšžœB˜F—Kšœ˜—šžœ˜K™4Kšžœžœ žœžœ˜+Kšœžœžœ ˜9Kšœ"˜"Kšœžœ˜ Kšžœžœ žœžœ˜-Kšœ˜——Kšžœ˜—Kšœ˜—š  œ˜Kš œžœžœžœžœ ˜8šœžœžœ˜Kšœžœ˜!Kšœžœ˜ Kšœžœ˜Kšœ žœ ˜šœžœ˜Kšœžœ˜ Kšœ/˜/Kšœ˜—šœžœ˜Kšœžœ˜ Kšœ/˜/Kšœ˜—Kšœ!˜!Kšžœžœ ˜Kšœ˜Kšœ˜—šœžœžœ˜!šœžœžœ˜(Kšœžœ˜Kšœ žœ˜ Kšœžœ˜Kšœ žœžœ˜šœžœ˜Kšœ+˜+Kšœ žœ˜Kšœ˜—šœžœ˜Kšœžœ˜/Kšœ6˜6Kšžœ žœ˜Kšœ˜Kšœ žœ˜Kšœ˜—Kšœ!˜!Kšžœžœ˜Kšœ˜—Kšœ˜Kšœ žœ˜(Kšžœžœ(˜?Kšœ˜Kšœ˜—šœžœ žœžœ˜&Kšœ.˜.Kšœ˜—Kšœžœ žœ0˜FKšœM˜MKšœ˜—Kšœžœ˜šžœžœžœž˜!Kšœžœ˜Kšžœžœžœ˜Kšœ)˜)Kšžœ˜—Kšžœžœžœ˜ Kšžœ žœ"žœ˜=Kšœ˜Kšœ'˜'Kšœ˜K˜——Kšžœ˜—…—$ψ