DIRECTORY CubicPaths, Imager USING [Rectangle, VEC], ImagerPath USING [MoveToProc, CurveToProc], ImagerTransformation USING [Transform, Transformation], CubicSplines USING [CoeffsSequence, X, Y], Vector2 USING [Square, Sub, Add, Length, Mul], CubicPathsExtras, Cubic2; CubicPathsImpl: CEDAR PROGRAM IMPORTS Cubic2, ImagerTransformation, Vector2 EXPORTS CubicPaths, CubicPathsExtras ~ BEGIN OPEN CubicPaths; VEC: TYPE = Imager.VEC; X: NAT = CubicSplines.X; Y: NAT = CubicSplines.Y; PathFromCubic: PUBLIC PROC[coeffs: CubicSplines.CoeffsSequence] RETURNS[Path] = { path: Path _ NEW[PathRec]; path.cubics _ NEW[PathSequence[coeffs.length]]; FOR i: NAT IN [0..coeffs.length) DO cfs: Cubic2.Coeffs _ [ c3: [coeffs[i].t3[X], coeffs[i].t3[Y]], c2: [coeffs[i].t2[X], coeffs[i].t2[Y]], c1: [coeffs[i].t1[X], coeffs[i].t1[Y]], c0: [coeffs[i].t0[X], coeffs[i].t0[Y]]]; path.cubics[i] _ Cubic2.CoeffsToBezier[cfs]; ENDLOOP; UpdateBounds[path]; RETURN[path]; }; EnumeratePath: PUBLIC PROC [ path: Path, moveTo: ImagerPath.MoveToProc, curveTo: ImagerPath.CurveToProc] = { moveTo[path.cubics[0].b0]; FOR i: NAT IN [0..path.cubics.length) DO curveTo[path.cubics[i].b1, path.cubics[i].b2, path.cubics[i].b3]; ENDLOOP; }; WalkPath: PUBLIC PROC[path: Path, tol: REAL, proc: PointProc] = { subdivide: PROC[bezier: Cubic2.Bezier] RETURNS [quit: BOOLEAN]= { quit _ FALSE; IF Cubic2.Flat[bezier, tol] THEN { len: REAL _ Vector2.Length[Vector2.Sub[bezier.b0, bezier.b3]]; IF len > 0 THEN { step: REAL _ tol/len; alpha: REAL _ step; pt: VEC; UNTIL alpha > 1 DO pt _ Vector2.Add[Vector2.Mul[bezier.b3,alpha],Vector2.Mul[bezier.b0,1-alpha]]; IF proc[pt] THEN {quit _ TRUE; EXIT}; alpha _ alpha+step; ENDLOOP; } ELSE quit _ proc[bezier.b3]; RETURN[quit]} ELSE { b1, b2: Cubic2.Bezier; [b1,b2] _ Cubic2.Split[bezier]; IF NOT subdivide[b1] THEN [] _ subdivide[b2]; }; }; IF NOT proc[path.cubics[0].b0] THEN FOR i: NAT IN [0..path.cubics.length) DO [] _ subdivide[path.cubics[i]]; ENDLOOP; }; PointOnPath: PUBLIC PROC[pt: VEC, path: Path, tol: REAL] RETURNS[BOOLEAN] = { tolSq: REAL _ tol*tol; found: BOOLEAN _ FALSE; hit: PointProc = { IF Vector2.Square[Vector2.Sub[p, pt]] <= tolSq THEN found _ TRUE; RETURN[found]; }; IF pt.x < path.bounds.x OR pt.y < path.bounds.y OR pt.x>(path.bounds.x+path.bounds.w) OR pt.y > (path.bounds.y+path.bounds.h) THEN RETURN[FALSE] ELSE WalkPath[path, tol, hit]; RETURN[found]; }; TranslatePath: PUBLIC PROC[path: Path, offset: VEC] = { FOR i: NAT IN [0..path.cubics.length) DO path.cubics[i].b0 _ Vector2.Add[offset, path.cubics[i].b0]; path.cubics[i].b1 _ Vector2.Add[offset, path.cubics[i].b1]; path.cubics[i].b2 _ Vector2.Add[offset, path.cubics[i].b2]; path.cubics[i].b3 _ Vector2.Add[offset, path.cubics[i].b3]; ENDLOOP; path.bounds.x _ path.bounds.x+offset.x; path.bounds.y _ path.bounds.y+offset.y; }; TransformPath: PUBLIC PROC[path: Path, tranformation: ImagerTransformation.Transformation] = { pt: VEC _ [path.bounds.x, path.bounds.y]; FOR i: NAT IN [0..path.cubics.length) DO path.cubics[i].b0 _ ImagerTransformation.Transform[tranformation, path.cubics[i].b0]; path.cubics[i].b1 _ ImagerTransformation.Transform[tranformation, path.cubics[i].b1]; path.cubics[i].b2 _ ImagerTransformation.Transform[tranformation, path.cubics[i].b2]; path.cubics[i].b3 _ ImagerTransformation.Transform[tranformation, path.cubics[i].b3]; ENDLOOP; UpdateBounds[path]; }; UpdateBounds: PUBLIC PROC[path: Path] = { minX, minY: REAL _ 10E6; maxX, maxY: REAL _ -10E6; pointProc: PointProc = { IF p.x > maxX THEN maxX _ p.x; IF p.y > maxY THEN maxY _ p.y; IF p.x < minX THEN minX _ p.x; IF p.y < minY THEN minY _ p.y; }; WalkPath[path, 1.0, pointProc]; path.bounds _ [x: minX, y: minY, w: maxX-minX, h: maxY-minY]; }; ClosestPoint: PUBLIC PROC[pt: VEC, path: Path] RETURNS[VEC] = { closest: VEC _ path.cubics[0].b0; minD: REAL _ 10E6; test: PointProc = { thisD: REAL _ Vector2.Square[Vector2.Sub[p, pt]]; IF thisD < minD THEN {closest _ p; minD _ thisD}; }; WalkPath[path, 1.0, test]; RETURN[closest]; }; CopyPath: PUBLIC PROC[path: Path] RETURNS[Path] = { new: Path _ NEW[PathRec]; new.cubics _ NEW[PathSequence[path.cubics.length]]; FOR i: NAT IN [0..path.cubics.length) DO new.cubics[i] _ path.cubics[i]; ENDLOOP; new.bounds _ path.bounds; RETURN[new]; }; ReversePath: PUBLIC PROC[path: Path] = { --reverses it in place last: NAT _ path.cubics.length-1; FOR i: NAT IN [0..path.cubics.length/2) DO temp: Cubic2.Bezier _ path.cubics[i]; path.cubics[i] _ path.cubics[last-i]; path.cubics[last-i] _ temp; ENDLOOP; FOR i: NAT IN [0..path.cubics.length) DO temp: VEC _ path.cubics[i].b0; path.cubics[i].b0 _ path.cubics[i].b3; path.cubics[i].b3 _ temp; temp _ path.cubics[i].b1; path.cubics[i].b1 _ path.cubics[i].b2; path.cubics[i].b2 _ temp; ENDLOOP; }; END. ΜCubicPathsImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Written by: Maureen Stone August 5, 1985 8:49:21 pm PDT Kurlander August 15, 1986 10:08:11 am PDT PointProc: TYPE = PROC [p: VEC] RETURNS[stop: BOOLEAN _ FALSE]; Path: TYPE = RECORD[bounds: Imager.Rectangle, cubics: REF PathSequence]; PathSequence: TYPE = RECORD[bezier: SEQUENCE length: NAT OF Cubic2.Bezier]; CubicPathsExtras all below plus made UpdateBounds Public Κχ˜codešœΟkœ™Kšœ Οmœ1™˜Kšœœ ˜Kšœœ˜Kšœœ˜šœ ˜KšœN˜NKšœ œ œœ˜%K˜Kšœ˜—K˜—Kšœ˜Jšœ˜ —šœ˜Jšœ˜J˜Jšœœœ˜-J˜—J˜—š œœœœœ˜LKšœ˜Kšœ˜—Kšœ˜—š Ÿ œ œœœœœ˜MKšœœ ˜Kšœœœ˜˜Kšœ-œ œ˜AKšœ˜K˜—šœœœ#˜UKšœ&œœœ˜:—Kšœ˜Kšœ˜Kšœ˜—šŸ œ œœ˜7šœœœ˜(K˜;K˜;K˜;K˜;Kšœ˜—K˜'K˜'K˜—šŸ œ œD˜^Kšœœ"˜)šœœœ˜(KšœU˜UKšœU˜UKšœU˜UKšœU˜UKšœ˜—K˜Kšœ˜—šŸ œ œ˜)Kšœ œ˜Kšœ œ ˜šœ˜Kšœ œ ˜Kšœ œ ˜Kšœ œ ˜Kšœ œ ˜Kšœ˜—K˜K˜=K˜—K˜K™8š Ÿ œ œœœœ˜?Kšœ œ˜!Kšœœ˜šœ˜Kšœœ&˜1Kšœœ˜1K˜—K˜Kšœ ˜K˜—šŸœ œ œ ˜3Kšœ œ ˜Kšœ œ#˜3šœœœ˜(Kšœ˜Kšœ˜—K˜Kšœ˜ K˜—šŸ œ œΟc˜?Kšœœ˜!šœœœ˜*K˜%Kšœ%˜%Kšœ˜Kšœ˜—šœœœ˜(Kšœœ˜K˜&K˜K˜K˜&K˜Kšœ˜—K˜—K™Kšœ˜—…—–Y