<> <> <> <> <<>> 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.