PixelGraphicsImpl.mesa
Contains utilities for doing various kinds of graphing, especially related to working on a raster, and drawing splines.
Eric Nickell, April 25, 1985 7:54:24 am PST
DIRECTORY
Real USING [RoundLI],
PixelGraphics
;
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: INTABS[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: INTABS[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: INTABS[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: INTABS[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.