Draw3dImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Bloomenthal, February 26, 1987 7:28:41 pm PST
DIRECTORY Clip3d, Draw2d, Draw3d, Imager, ImagerBackdoor, IO, Matrix3d, Polygons3d, Real, Rope, Spline3d, Vector2, Vector3d;
Draw3dImpl: CEDAR PROGRAM
IMPORTS Clip3d, Draw2d, Imager, ImagerBackdoor, IO, Matrix3d, Real, Polygons3d, Spline3d, Vector2, Vector3d
EXPORTS Draw3d
~ BEGIN
OPEN Draw3d;
Point Marking
PairClip: TYPE ~ RECORD [pair: Pair, clipped: BOOL];
TransformAndClip: PROC [point: Triple, matrix: Matrix] RETURNS [PairClip] ~ {
IF matrix[2][3] # 0.0
THEN {
q: Quad ← Matrix3d.TransformH[point, matrix];
IF q.z+q.w < 0.0 THEN RETURN[[[0.0, 0.0], TRUE]];
RETURN[[[q.x/q.w, q.y/q.w], FALSE]];
}
ELSE RETURN[[Matrix3d.TransformD[point, matrix], FALSE]];
};
Mark: PUBLIC PROC [
context: Context,
point: Triple,
matrix: Matrix,
label: ROPENIL,
markType: MarkType ← cross]
~ {
IF markType # none OR label # NIL THEN {
pairClip: PairClip ~ TransformAndClip[point, matrix];
IF pairClip.clipped THEN RETURN;
IF markType # none THEN Draw2d.Mark[context, pairClip.pair, markType];
IF label # NIL THEN Draw2d.Label[context, [pairClip.pair.x+6, pairClip.pair.y], label];
};
};
DoWithPoint: PUBLIC PROC [
context: Context, point: Triple, matrix: Matrix, pointProc: PointProc] ~ {
pairClip: PairClip ~ TransformAndClip[point, matrix];
IF NOT pairClip.clipped THEN pointProc[context, pairClip.pair];
};
Straight Line Drawing
Segment: PUBLIC PROC [
context: Context,
point0, point1: Triple,
matrix: Matrix,
drawType: DrawType ← solid]
~ {
c0, c1: Pair;
IF Matrix3d.HasPerspective[matrix] THEN {
off: BOOL;
[c0, c1, off] ←
Clip3d.NearH[Matrix3d.TransformH[point0, matrix], Matrix3d.TransformH[point1, matrix]];
IF NOT off THEN Draw2d.Line[context, c0, c1, drawType];
}
ELSE {
c0 ← Matrix3d.TransformD[point0, matrix];
c1 ← Matrix3d.TransformD[point1, matrix];
Draw2d.Line[context, c0, c1, drawType];
};
};
Vector: PUBLIC PROC [
context: Context,
base, vector: Triple,
matrix: Matrix,
label: ROPENIL,
scale: REAL ← 0.2,
markType: MarkType ← none,
drawType: DrawType ← solid]
~ {
c0, c1, d: Pair;
end: Triple ← Vector3d.Add[base, vector];
IF Matrix3d.HasPerspective[matrix] THEN {
off: BOOL;
[c0, c1, off] ←
Clip3d.NearH[Matrix3d.TransformH[base, matrix], Matrix3d.TransformH[end, matrix]];
IF off THEN RETURN;
IF markType # none THEN Mark[context, base, matrix, , markType];
}
ELSE {
c0 ← Matrix3d.TransformD[base, matrix];
c1 ← Matrix3d.TransformD[end, matrix];
IF markType # none THEN Draw2d.Mark[context, c0, markType];
};
d ← [scale*(c1.x-c0.x), scale*(c1.y-c0.y)];
c1 ← [c0.x+d.x, c0.y+d.y];
Draw2d.Arrow[context, c0, c1, drawType];
IF label # NIL THEN {
Draw2d.Label[
context,
IF d = [0, 0] THEN c1 ELSE Vector2.Add[[c1.x, c1.y-4], Vector2.Mul[Vector2.Unit[d], 6]],
label];
};
};
Axes: PUBLIC PROC [context: Context, matrix: Matrix] ~ {
Vector[context, [0.0, 0.0, 0.0], [1.0, 0.0, 0.0], matrix, "X"];
Vector[context, [0.0, 0.0, 0.0], [0.0, 1.0, 0.0], matrix, "Y"];
Vector[context, [0.0, 0.0, 0.0], [0.0, 0.0, 1.0], matrix, "Z"];
};
Curve Drawing
Curve: PUBLIC PROC [context: Context, coeffs: Coeffs, matrix: Matrix ← NIL] ~ {
IF coeffs # NIL THEN {
persp: BOOL ← Matrix3d.HasPerspective[matrix];
xc: Coeffs ← IF matrix = NIL THEN coeffs ELSE Matrix3d.Mul[coeffs, matrix];
nSegs: INTEGER ← Spline3d.Resolution[xc, 1.0];
dif: Coeffs ← Spline3d.FwdDif[xc, nSegs];
nCoords: NATIF persp THEN 3 ELSE 1;
p0, p1: Pair ← [dif[0][0], dif[0][1]];
q0, q1: Quad ← [dif[0][0], dif[0][1], dif[0][2], dif[0][3]];
FOR i: INTEGER IN[0..nSegs) DO
IF persp
THEN q1 ← [q0.x+dif[1][0], q0.y+dif[1][1], q0.z+dif[1][2], q0.w+dif[1][3]]
ELSE p1 ← [p0.x+dif[1][0], p0.y+dif[1][1]];
FOR j: INTEGER IN[0..nCoords] DO
dif[1][j] ← dif[1][j]+dif[2][j];
dif[2][j] ← dif[2][j]+dif[3][j];
ENDLOOP;
IF persp THEN {
c0, c1: Pair;
offScreen: BOOL;
[c0, c1, offScreen] ← Clip3d.NearH[q0, q1];
IF NOT offScreen THEN Draw2d.Line[context, c0, c1];
q0 ← q1;
}
ELSE {
Draw2d.Line[context, p0, p1];
p0 ← p1;
};
ENDLOOP;
};
};
DotCurve: PUBLIC PROC [context: Context, coeffs: Coeffs, matrix: Matrix ← NIL] ~ {
IF coeffs # NIL THEN {
persp: BOOL ← Matrix3d.HasPerspective[matrix];
xc: Coeffs ← IF matrix = NIL THEN coeffs ELSE Matrix3d.Mul[coeffs, matrix];
nSegs: INTEGER ← 4*Spline3d.Resolution[xc, 1.0];
dif: Coeffs ← Spline3d.FwdDif[xc, nSegs];
nCoords: NATIF persp THEN 3 ELSE 1;
p: Pair ← [dif[0][0], dif[0][1]];
q: Quad ← [dif[0][0], dif[0][1], dif[0][2], dif[0][3]];
FOR i: INTEGER IN[0..nSegs) DO
IF persp
THEN {IF q.w+q.z >= 0.0 THEN Imager.MaskRectangle[context, [q.x/q.w, q.y/q.w,1,1]]}
ELSE Imager.MaskRectangle[context, [p.x, p.y, 1, 1]];
IF persp
THEN q ← [q.x+dif[1][0], q.y+dif[1][1], q.z+dif[1][2], q.w+dif[1][3]]
ELSE p ← [p.x+dif[1][0], p.y+dif[1][1]];
FOR j: INTEGER IN[0..nCoords] DO
dif[1][j] ← dif[1][j]+dif[2][j];
dif[2][j] ← dif[2][j]+dif[3][j];
ENDLOOP;
ENDLOOP;
};
};
BezierPolygon: PUBLIC PROC [
context: Context,
bezier: Bezier,
matrix: Matrix ← NIL,
drawType: Draw2d.DrawType ← solid,
close: BOOLFALSE]
~ {
Segment[context, bezier.b0, bezier.b1, matrix, drawType];
Segment[context, bezier.b1, bezier.b2, matrix, drawType];
Segment[context, bezier.b2, bezier.b3, matrix, drawType];
IF close THEN Segment[context, bezier.b0, bezier.b3, matrix, drawType];
};
Triangle Drawing
Triangle: PUBLIC PROC [
context: Context,
p0, p1, p2: Triple,
view: Matrix]
~ {
pp0: Pair ← Matrix3d.TransformD[p0, view];
pp1: Pair ← Matrix3d.TransformD[p1, view];
pp2: Pair ← Matrix3d.TransformD[p2, view];
Draw2d.Line[context, pp0, pp1];
Draw2d.Line[context, pp1, pp2];
Draw2d.Line[context, pp2, pp0];
};
FrontFacingTriangle: PUBLIC PROC [
context: Context,
p0, p1, p2: Triple,
view: Matrix,
normal: Triple ← origin]
~ {
IF normal = origin THEN normal ← Polygons3d.TriangleNormal[p0, p1, p2];
IF normal.x*view[0][2]+normal.y*view[1][2]+normal.z*view[2][2] >= 0.0
THEN Triangle[context, p0, p1, p2, view];
};
Polygon Drawing
PolygonPairs: PUBLIC PROC [
triples: TripleSequence,
view: Matrix,
pairs: PairSequence ← NIL]
RETURNS [PairSequence]
~ {
IF triples # NIL AND view # NIL THEN {
IF pairs = NIL OR pairs.length < triples.length
THEN pairs ← NEW[PairSequenceRep[triples.length]];
pairs.length ← triples.length;
FOR n: NAT IN[0..triples.length) DO
pairs[n] ← Matrix3d.TransformD[triples[n], view];
ENDLOOP;
};
RETURN[pairs];
};
Polygon: PUBLIC PROC [
context: Context,
poly: REF NatSequence,
pairs: PairSequence ← NIL,
triples: TripleSequence ← NIL,
view: Matrix ← NIL]
~ {
stop: CARDINAL ~ poly.length-1;
IF pairs = NIL AND view # NIL AND triples # NIL THEN {
pairs ← NEW[PairSequenceRep[triples.length]];
FOR n: NAT IN [0..poly.length) DO
i: NAT ~ poly[n];
pairs[i] ← Matrix3d.TransformD[triples[i], view];
ENDLOOP;
};
IF pairs # NIL AND poly # NIL AND poly.length > 0 THEN {
FOR i: NAT IN[0..stop) DO
Draw2d.Line[context, pairs[poly[i]], pairs[poly[i+1]]];
ENDLOOP;
Draw2d.Line[context, pairs[poly[stop]], pairs[poly[0]]];
};
};
FrontFacingPolygon: PUBLIC PROC [
context: Context,
poly: REF NatSequence,
view: Matrix,
normal: Triple ← origin,
pairs: PairSequence ← NIL,
triples: TripleSequence ← NIL]
~ {
IF normal = origin AND triples = NIL THEN RETURN;
IF normal = origin THEN {
vertices: TripleSequence ← NEW[TripleSequenceRep[poly.length]];
FOR n: NAT IN [0..poly.length) DO vertices[n] ← triples[poly[n]]; ENDLOOP;
normal ← Polygons3d.PolygonNormal[vertices];
};
IF normal.x*view[0][2]+normal.y*view[1][2]+normal.z*view[2][2] >= 0.0
THEN Polygon[context, poly, pairs, triples, view];
};
Polygons: PUBLIC PROC [
context: Context,
polygons: REF NatTable,
pairs: PairSequence ← NIL,
triples: TripleSequence ← NIL,
view: Matrix ← NIL]
~ {
IF pairs = NIL AND view # NIL AND triples # NIL THEN {
pairs ← NEW[PairSequenceRep[triples.length]];
FOR n: NAT IN [0..triples.length) DO
pairs[n] ← Matrix3d.TransformD[triples[n], view];
ENDLOOP;
};
IF polygons # NIL AND pairs # NIL THEN FOR n: NAT IN[0..polygons.length) DO
poly: REF NatSequence ~ polygons[n];
stop: CARDINAL ~ poly.length-1;
IF poly = NIL OR poly.length < 1 THEN LOOP;
FOR i: NAT IN[0..stop) DO
Draw2d.Line[context, pairs[poly[i]], pairs[poly[i+1]]];
ENDLOOP;
Draw2d.Line[context, pairs[poly[stop]], pairs[poly[0]]];
ENDLOOP;
};
FrontFacingPolygons: PUBLIC PROC [
context: Context,
polygons: REF NatTable,
view: Matrix,
normals: TripleSequence,
pairs: PairSequence ← NIL,
triples: TripleSequence ← NIL]
~ {
IF polygons = NIL OR view = NIL OR normals = NIL OR (pairs = NIL AND triples = NIL)
THEN RETURN;
IF pairs = NIL THEN {
pairs ← NEW[PairSequenceRep[triples.length]];
FOR n: NAT IN [0..triples.length) DO
pairs[n] ← Matrix3d.TransformD[triples[n], view];
ENDLOOP;
};
FOR n: NAT IN[0..polygons.length) DO
poly: REF NatSequence ← polygons[n];
IF poly = NIL OR poly.length < 1 THEN LOOP;
IF normals[n].x*view[0][2]+normals[n].y*view[1][2]+normals[n].z*view[2][2] < 0.0
THEN LOOP;
FOR i: NAT IN[0..poly.length-1) DO
Draw2d.Line[context, pairs[poly[i]], pairs[poly[i+1]]];
ENDLOOP;
Draw2d.Line[context, pairs[poly[poly.length-1]], pairs[poly[0]]];
ENDLOOP;
};
Labelling
Pendant: PUBLIC PROC [
context: Context,
view: Matrix,
size: REAL ← .07,
wPos, hPos: REAL ← .7,
names: ARRAY [0..6) OF ROPEALL[NIL]]
~ {
mx: REAL ← 0.0;
v: ARRAY [0..6) OF V;
V: TYPE ~ RECORD[rope: ROPE, vec: Triple];
axes: ARRAY [0..6) OF Triple ~ [[1, 0, 0], [0, 1, 0], [0, 0, 1], [-1, 0, 0], [0, -1, 0], [0, 0, -1]];
c: Pair;
rect: Imager.Rectangle ← [0.0, 0.0, 600.0, 400.0];
rect ← ImagerBackdoor.GetBounds[context ! Imager.Error => CONTINUE];
c ← [rect.w*(1+wPos)/2, rect.h*(1+hPos)/2];
FOR n: NAT IN [0..6) DO
v[n] ← [names[n], Matrix3d.TransformVec[axes[n], view]];
ENDLOOP;
FOR n: NAT IN [0..6) DO mx ← MAX[mx, Vector2.Square[[v[n].vec.x, v[n].vec.y]]]; ENDLOOP;
mx ← MAX[rect.w, rect.h]*size/Real.SqRt[mx];
FOR n: NAT IN [0..6) DO v[n].vec ← Vector3d.Mul[v[n].vec, mx]; ENDLOOP;
DO  -- order v by increasing z:
d: BOOLFALSE;
FOR n: NAT IN [0..5) DO
IF v[n].vec.z<v[n+1].vec.z THEN {t: V ← v[n]; v[n] ← v[n+1]; v[n+1] ← t; d ← TRUE};
ENDLOOP;
IF NOT d THEN EXIT;
ENDLOOP;
FOR n: NAT IN [0..6) DO
IF v[n].rope = NIL THEN LOOP;
Imager.SetColor[context, Imager.white];
Imager.MaskRectangle[context, [c.x+v[n].vec.x, c.y+v[n].vec.y-4, 8, 8]];
Imager.SetColor[context, Imager.black];
Draw2d.Label[context, [c.x+v[n].vec.x, c.y+v[n].vec.y-4], v[n].rope];
ENDLOOP;
FOR n: NAT IN [0..6) DO
IF v[n].rope # NIL THEN {
a: Pair ← IF v[n].vec.x = 0 AND v[n].vec.y = 0
THEN [0, 0] ELSE Vector2.Mul[Vector2.Unit[[v[n].vec.x, v[n].vec.y]], 6.0];
Draw2d.Arrow[context, c, [c.x+v[n].vec.x-a.x, c.y+v[n].vec.y-a.y]];
};
ENDLOOP;
};
LabelPairs: PUBLIC PROC [context: Context, pairs: PairSequence] ~ {
IF pairs # NIL THEN FOR n: NAT IN[0..pairs.length) DO
Draw2d.Label[context, pairs[n], IO.PutFR["%g", IO.int[n]]];
ENDLOOP;
};
END.
..
To be used sometime after Cedar7.0:
TransformPointToDisplay: PUBLIC PROC [context3d: Context3d, point: Triple]
RETURNS [displayPoint: Triple] ~ {
xpoint: Triple;
[xpoint, ] ← ThreeDScenes.XfmPtToEyeSpace[context3d, point];
displayPoint ← ThreeDScenes.XfmPtToDisplay[context3d, NIL, xpoint];
};
PerspPP: PUBLIC PROC [p0, p1: Triple, context: Context, focalLength, tanHalfFov: REAL] ~ {
Draw segment p0p1 with perspective determined by focalLength and the tangent of 1/2 the field of view.
ClipZ: PROC [p0, p1: Triple, zLim: REAL] RETURNS [c0, c1: Triple, off: BOOL] ~ {
dx, dy, dz: REAL;
a0: REAL ← 0.0;
a1: REAL ← 1.0;
IF p0.z < zLim THEN {
IF p1.z < zLim THEN RETURN[c0, c1, TRUE];
a0 ← p0.z/(p0.z-p1.z);
}
ELSE IF p1.z < zLim THEN a1 ← p0.z/(p0.z-p1.z);
IF a0 > a1 THEN RETURN[c0, c1, TRUE];
dx ← p1.x-p0.x;
dy ← p1.y-p0.y;
dz ← p1.z-p0.z;
RETURN[[p0.x+a0*dx,p0.y+a0*dy,p0.z+a0*dz], [p0.x+a1*dx,p0.y+a1*dy,p0.z+a1*dz], FALSE];
};
off: BOOL ← FALSE;
c0, c1: Triple;
[c0, c1, off] ← ClipZ[p0, p1, -focalLength+0.01];
IF NOT off THEN {
f0: REAL ← 1.0/(1.0+c0.z*tanHalfFov);
f1: REAL ← 1.0/(1.0+c1.z*tanHalfFov);
Draw2d.Solid[context, [f0*c0.x, f0*c0.y], [f1*c1.x, f1*c1.y]];
};
};