PixelGraphicsImpl:
CEDAR PROGRAM
IMPORTS Real
EXPORTS PixelGraphics
= BEGIN
OPEN PixelGraphics;
FourByFour:
TYPE ~
RECORD [
a1, a2, a3, a4,
b1, b2, b3, b4,
c1, c2, c3, c4,
d1, d2, d3, d4: REAL
];
Line:
PUBLIC
PROC [p0, p1: Point]
RETURNS [p:
LIST
OF Point] ~ {
Returns the list of points which constitute the line between p0 and p1.
The points returned will be in order, but no guarantee is made as to which endpoint is found at the beginning of the LIST and which is at the end.
pTail: LIST OF Point;
pTail ← p ← LIST [p0];
[] ← AddPointsForLineToTail[p0, p1, pTail];
};
Lines:
PUBLIC
PROC [vertices:
LIST
OF Point]
RETURNS [p:
LIST
OF Point] ~ {
Returns the list of points which constitute the lines between successive points in p.
The points returned will be in order, but no guarantee is made as to which endpoint is found at the beginning of the LIST and which is at the end.
pTail: LIST OF Point;
IF vertices=NIL THEN RETURN [NIL];
pTail ← p ← LIST[vertices.first];
FOR each:
LIST
OF Point ← vertices, each.rest
UNTIL each.rest=
NIL
DO
pTail ← AddPointsForLineToTail[each.first, each.rest.first, pTail];
ENDLOOP;
};
InterpolateLines:
PUBLIC
PROC [vertices:
LIST
OF Point, proc:
PROC [p: Point]] ~ {
IF vertices=NIL THEN RETURN; --Nothing more to do
proc[vertices.first]; --Call for the first point
FOR each:
LIST
OF Point ← vertices, each.rest
UNTIL each.rest=
NIL
DO
InterpolatePoints[each.first, each.rest.first, proc];
ENDLOOP;
};
Spline:
PUBLIC
PROC [m: Cubic, segments:
CARDINAL]
RETURNS [p:
LIST
OF Point] ~ {
Finds the set of interpolation points for the spline, breaking the spline into segments parts.
h: REAL ← 1.0/segments;
h2: REAL ← h*h;
h3: REAL ← h2*h;
pTail: LIST OF Point;
x, y, dx, dy, d2x, d2y, d3x, d3y: REAL;
x ← m.dx;
y ← m.dy;
dx ← m.ax*h3 + m.bx*h2 + m.cx*h;
dy ← m.ay*h3 + m.by*h2 + m.cy*h;
d2x ← 6*m.ax*h3 + 2*m.bx*h2;
d2y ← 6*m.ay*h3 + 2*m.by*h2;
d3x ← 6*m.ax*h3;
d3y ← 6*m.ay*h3;
pTail ← p ← LIST [[Real.RoundLI[x], Real.RoundLI[y]]];
THROUGH [1 .. segments ]
DO
x ← x + dx;
dx ← dx + d2x;
d2x ← d2x + d3x;
y ← y + dy;
dy ← dy + d2y;
d2y ← d2y + d3y;
pTail ← pTail.rest ← LIST [[Real.RoundLI[x], Real.RoundLI[y]]];
ENDLOOP;
};
InterpolateSpline:
PUBLIC
PROC [m: Cubic, segments:
CARDINAL, proc:
PROC [p: Point]] ~ {
InterpolateLines[Spline[m: m, segments: segments], proc];
};
Bezier:
PUBLIC
PROC [p1, p2, p3, p4: Point]
RETURNS [m: Cubic] ~ {
Generates the Cubic matrix for a Bezier spline given the four control points.
m ← Mul4x4By4x2 [p1, p2, p3, p4, 1,
[-1, 3, -3, 1,
3, -6, 3, 0,
-3, 3, 0, 0,
1, 0, 0, 0]
];
};
Simple:
PUBLIC
PROC [p1, p2, p3, p4: Point]
RETURNS [m: Cubic] ~ {
Generates the Cubic matrix for a Cubic spline given the four control points.
m ← Mul4x4By4x2 [p1, p2, p3, p4, 2,
[-9, 27,-27, 9,
18,-45, 36, -9,
-11, 18, -9, 2,
2, 0, 0, 0]
];
};
BSpline:
PUBLIC
PROC [p1, p2, p3, p4: Point]
RETURNS [m: Cubic] ~ {
Generates the Cubic matrix for a B-Spline given the four control points.
m ← Mul4x4By4x2 [p1, p2, p3, p4, 6,
[-1, 3, -3, 1,
3, -6, 3, 0,
-3, 0, 3, 0,
1, 4, 1, 0]
];
};
CatmullRom:
PUBLIC
PROC [p1, p2, p3, p4: Point]
RETURNS [m: Cubic] ~ {
Generates the Cubic matrix for a Catmull-Rom spline given the four control points.
m ← Mul4x4By4x2 [p1, p2, p3, p4, 2,
[-1, 3, -3, 1,
2, -5, 4, -1,
-1, 0, 1, 0,
0, 2, 0, 0]
];
};
Hermite:
PUBLIC
PROC [p1, q1, p2, q2: Point]
RETURNS [m: Cubic] ~ {
Generates the Cubic matrix for a Hermite spline given the two control points and the tangent vectors at those points.
m ← Mul4x4By4x2 [p1, p2, q1, q2, 1,
[ 2, -2, 1, 1,
-3, 3, -2, -1,
0, 0, 1, 0,
1, 0, 0, 0]
];
};
AddPointsForLineToTail:
PROC [p0, p1: Point, inTail:
LIST
OF Point]
RETURNS [tail:
LIST
OF Point] ~
INLINE {
dx: INT ~ IF p0.x < p1.x THEN 1 ELSE -1;
dy: INT ~ IF p0.y < p1.y THEN 1 ELSE -1;
x: INT ← p0.x;
y: INT ← p0.y;
tail ← inTail;
IF
ABS[p0.x-p1.x] >
ABS[p0.y-p1.y]
THEN {
Here, we always change in x, and sometimes in y.
dErrX: INT ← ABS[p1.y-p0.y];
dErrY: INT ← -ABS[p1.x-p0.x];
error: INT ← dErrY/2;
THROUGH (0 .. -dErrY]
DO
x ← x+dx;
error ← error + dErrX;
IF error > 0 THEN {y ← y+dy; error ← error + dErrY};
tail ← tail.rest ← LIST[[x, y]];
ENDLOOP;
}
ELSE {
Here, we always change in y, and sometimes in x.
dErrY: INT ← ABS[p1.x-p0.x];
dErrX: INT ← -ABS[p1.y-p0.y];
error: INT ← dErrX/2;
THROUGH (0 .. -dErrX]
DO
y ← y+dy;
error ← error + dErrY;
IF error > 0 THEN {x ← x+dx; error ← error + dErrX};
tail ← tail.rest ← LIST[[x, y]];
ENDLOOP;
};
};
InterpolatePoints:
PUBLIC
PROC [p0, p1: Point, proc:
PROC [p: Point]] ~ {
dx: INT ~ IF p0.x < p1.x THEN 1 ELSE -1;
dy: INT ~ IF p0.y < p1.y THEN 1 ELSE -1;
x: INT ← p0.x;
y: INT ← p0.y;
IF
ABS[p0.x-p1.x] >
ABS[p0.y-p1.y]
THEN {
Here, we always change in x, and sometimes in y.
dErrX: INT ← ABS[p1.y-p0.y];
dErrY: INT ← -ABS[p1.x-p0.x];
error: INT ← dErrY/2;
THROUGH (0 .. -dErrY]
DO
x ← x+dx;
error ← error + dErrX;
IF error > 0 THEN {y ← y+dy; error ← error + dErrY};
proc[[x,y]];
ENDLOOP;
}
ELSE {
Here, we always change in y, and sometimes in x.
dErrY: INT ← ABS[p1.x-p0.x];
dErrX: INT ← -ABS[p1.y-p0.y];
error: INT ← dErrX/2;
THROUGH (0 .. -dErrX]
DO
y ← y+dy;
error ← error + dErrY;
IF error > 0 THEN {x ← x+dx; error ← error + dErrX};
proc[[x,y]];
ENDLOOP;
};
};
Mul4x4By4x2:
PROC [p1, p2, p3, p4: Point, divisor:
REAL, m: FourByFour]
RETURNS [cubic: Cubic] ~
INLINE {
OPEN cubic, m;
ax ← (a1*p1.x + a2*p2.x + a3*p3.x + a4*p4.x)/divisor;
ay ← (a1*p1.y + a2*p2.y + a3*p3.y + a4*p4.y)/divisor;
bx ← (b1*p1.x + b2*p2.x + b3*p3.x + b4*p4.x)/divisor;
by ← (b1*p1.y + b2*p2.y + b3*p3.y + b4*p4.y)/divisor;
cx ← (c1*p1.x + c2*p2.x + c3*p3.x + c4*p4.x)/divisor;
cy ← (c1*p1.y + c2*p2.y + c3*p3.y + c4*p4.y)/divisor;
dx ← (d1*p1.x + d2*p2.x + d3*p3.x + d4*p4.x)/divisor;
dy ← (d1*p1.y + d2*p2.y + d3*p3.y + d4*p4.y)/divisor;
};
END.