G3dModelImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Bloomenthal, October 21, 1992 5:51 pm PDT
Heckbert, June 22, 1988 4:54:42 pm PDT
Glassner, March 2, 1990 9:41:41 am PST
Shoemake, November 20, 1989 8:29:31 pm PST
DIRECTORY Draw2d, G2dBasic, G2dVector, G3dBasic, G3dDraw, G3dMatrix, G3dModel, G3dPlane, G3dPolygon, G3dShape, G3dSpline, G3dVector, Imager, Real, RefTab, Rope, RuntimeError;
Types and Constants
DrawType: TYPE ~ Draw2d.DrawType;
Border: TYPE ~ G3dBasic.Border;
Box: TYPE ~ G3dBasic.Box;
Pair: TYPE ~ G3dBasic.Pair;
Triple: TYPE ~ G3dBasic.Triple;
Plane: TYPE ~ G3dPlane.Plane;
Ray: TYPE ~ G3dBasic.Ray;
Segment: TYPE ~ G3dBasic.Segment;
NatSequence: TYPE ~ G3dBasic.NatSequence;
NatSequenceRep: TYPE ~ G3dBasic.NatSequenceRep;
IntegerPair: TYPE ~ G3dBasic.IntegerPair;
IntSequence: TYPE ~ G3dBasic.IntSequence;
IntSequenceRep: TYPE ~ G3dBasic.IntSequenceRep;
SurfaceSequence: TYPE ~ G3dBasic.SurfaceSequence;
SurfaceSequenceRep: TYPE ~ G3dBasic.SurfaceSequenceRep;
PairSequence: TYPE ~ G3dBasic.PairSequence;
PairSequenceRep: TYPE ~ G3dBasic.PairSequenceRep;
TripleSequence: TYPE ~ G3dBasic.TripleSequence;
TripleSequenceRep: TYPE ~ G3dBasic.TripleSequenceRep;
Matrix: TYPE ~ G3dMatrix.Matrix;
Viewport: TYPE ~ G3dMatrix.Viewport;
Model: TYPE ~ G3dModel.Model;
ModelRep: TYPE ~ G3dModel.ModelRep;
NearModel: TYPE ~ G3dModel.NearModel;
Polygon: TYPE ~ G3dPolygon.Polygon;
PolygonSequenceRep: TYPE ~ G3dPolygon.PolygonSequenceRep;
Edge: TYPE ~ G3dShape.Edge;
EdgeRep: TYPE ~ G3dShape.EdgeRep;
EdgeSequence: TYPE ~ G3dShape.EdgeSequence;
Face: TYPE ~ G3dShape.Face;
FaceRep: TYPE ~ G3dShape.FaceRep;
FaceSequence: TYPE ~ G3dShape.FaceSequence;
FaceSequenceRep: TYPE ~ G3dShape.FaceSequenceRep;
ScreenSequence: TYPE ~ G3dShape.ScreenSequence;
Shape: TYPE ~ G3dShape.Shape;
ShapeRep: TYPE ~ G3dShape.ShapeRep;
ShapeSequence: TYPE ~ G3dShape.ShapeSequence;
SurfaceProc: TYPE ~ G3dShape.SurfaceProc;
Vertex: TYPE ~ G3dShape.Vertex;
VertexRep: TYPE ~ G3dShape.VertexRep;
VertexSequence: TYPE ~ G3dShape.VertexSequence;
VertexSequenceRep: TYPE ~ G3dShape.VertexSequenceRep;
SplineSequence: TYPE ~ G3dSpline.SplineSequence;
SplineSequenceRep: TYPE ~ G3dSpline.SplineSequenceRep;
Context: TYPE ~ Imager.Context;
BoundsFault: ERROR ~ RuntimeError.BoundsFault;
Error: PUBLIC SIGNAL [code: ATOM, reason: ROPE] = CODE;
Creation
ShapeFromRevolvedCurve:
PUBLIC
PROC [
curve: PairSequence,
axis: Ray,
res: NAT,
start: NAT ¬ 0,
stop: NAT ¬ LAST[NAT]]
RETURNS [s: Shape]
~ {
newStop: NAT ¬ MIN[res, stop];
nStrips: NAT ¬ IF start < newStop THEN newStop-start ELSE res-start+newStop;
mat: G3dMatrix.Matrix ¬ G3dMatrix.ObtainMatrix[];
nthVertex, nthPolygon: CARD ¬ 0;
nPolygons: CARD ¬ nStrips*(curve.length-1);
nVertices: CARD ¬ (IF nStrips = res THEN nStrips ELSE nStrips+1)*curve.length;
curveNormals: PairSequence ¬ NormalsFromCurve[curve];
s ¬ NEW[ShapeRep];
s.surfaces ¬ NEW[SurfaceSequenceRep[nPolygons]];
s.vertices ¬ NEW[VertexSequenceRep[nVertices]];
s.surfaces.length ¬ nPolygons;
s.vertices.length ¬ nVertices;
s.vertices.valid[normal] ¬ TRUE;
FOR n:
NAT
IN [0..
IF nStrips = res
THEN nStrips-1
ELSE nStrips]
DO
a: NAT ¬ (n+start) MOD res;
theta: REAL ¬ 2.0*G3dBasic.PI*REAL[a]/REAL[res];
mat ¬ G3dMatrix.MakeRotateAbout[axis.axis, theta, FALSE, axis.base, mat];
FOR i:
NAT
IN [0..curve.length)
DO
p: Pair ¬ curve[i];
pn: Pair ¬ curveNormals[i];
v: Vertex ¬ s.vertices[nthVertex] ¬ NEW[VertexRep];
v.point ¬ G3dMatrix.Transform[[p.x, 0.0, p.y], mat];
v.normal ¬ G3dMatrix.TransformVec[[pn.x, 0.0, pn.y], mat];
nthVertex ¬ nthVertex+1;
ENDLOOP;
ENDLOOP;
FOR n:
NAT
IN [0..nStrips)
DO
FOR i:
NAT
IN [0..curve.length-1)
DO
p: NatSequence ¬ s.surfaces[nthPolygon].vertices ¬ NEW[NatSequenceRep[4]];
p.length ¬ 4;
p[0] ¬ n*curve.length+i;
p[1] ¬ p[0]+1;
p[2] ¬ (p[1]+curve.length) MOD nVertices;
p[3] ¬ (p[0]+curve.length) MOD nVertices;
nthPolygon ¬ nthPolygon+1;
ENDLOOP;
ENDLOOP;
G3dMatrix.ReleaseMatrix[mat];
};
ShapeFromPointsPolys:
PUBLIC
PROC [
name: ROPE,
points: TripleSequence,
polys: SurfaceSequence]
RETURNS [s: Shape]
~ {
s ¬ NEW[ShapeRep ¬ [name: name, surfaces: polys, matrix: G3dMatrix.Identity[]]];
s.vertices ¬ NEW[VertexSequenceRep[points.length]];
s.vertices.length ¬ points.length;
FOR n:
NAT
IN [0..points.length)
DO
s.vertices[n] ¬ NEW[VertexRep ¬ [point: points[n]]];
ENDLOOP;
s.objectExtent ¬ G3dShape.BoundingBox[s];
};
PolyFromTwoPoints:
PROC [n0, n1:
NAT]
RETURNS [poly: NatSequence] ~ {
poly ¬ NEW[NatSequenceRep[2]];
poly[0] ¬ n0;
poly[1] ¬ n1;
poly.length ¬ 2;
};
ShapeFrom2dCurve:
PUBLIC
PROC [name:
ROPE, curve: PairSequence]
RETURNS
[s:
Shape]
~
{
IF curve #
NIL
AND curve.length > 1
THEN {
triples: TripleSequence ¬ NEW[TripleSequenceRep[curve.length]];
triples.length ¬ curve.length;
FOR n:
NAT
IN [0..curve.length)
DO
p: Pair ¬ curve[n];
triples[n] ¬ [p.x, 0.0, p.y];
ENDLOOP;
s ¬ ShapeFrom3dCurve[name, triples];
};
};
ShapeFrom3dCurve:
PUBLIC
PROC [name:
ROPE, curve: TripleSequence]
RETURNS [s: Shape]
~ {
IF curve #
NIL
AND curve.length > 1
THEN {
polys: SurfaceSequence ¬ NEW[SurfaceSequenceRep[curve.length-1]];
polys.length ¬ curve.length-1;
FOR n: NAT IN [0..curve.length-1) DO polys[n].vertices ¬ PolyFromTwoPoints[n, n+1]; ENDLOOP;
s ¬ ShapeFromPointsPolys[name, curve, polys];
};
};
ShapeFromSegments:
PUBLIC
PROC [name:
ROPE, segments:
LIST
OF Segment]
RETURNS [s: Shape]
~ {
nPoints, nPolys: NAT ¬ 0;
FOR l:
LIST
OF Segment ¬ segments, l.rest
WHILE l #
NIL
DO
nPolys ¬ nPolys+1;
nPoints ¬ nPoints+2;
ENDLOOP;
IF nPoints > 1
THEN {
AddVertex:
PROC [p: Triple] ~ {
s.vertices[s.vertices.length] ¬ NEW[VertexRep ¬ [point: p]];
s.vertices.length ¬ s.vertices.length+1;
};
s ¬ NEW[ShapeRep ¬ [name: name, matrix: G3dMatrix.Identity[]]];
s.surfaces ¬ NEW[SurfaceSequenceRep[nPolys]];
s.vertices ¬ NEW[VertexSequenceRep[nPoints]];
FOR l:
LIST
OF Segment ¬ segments, l.rest
WHILE l #
NIL
DO
AddVertex[l.first.p0];
AddVertex[l.first.p1];
s.surfaces[s.surfaces.length].vertices ¬ PolyFromTwoPoints[s.vertices.length-2,s.vertices.length-1];
s.surfaces.length ¬ s.surfaces.length+1;
ENDLOOP;
s.objectExtent ¬ G3dShape.BoundingBox[s];
};
};
ShapeFromPoints:
PUBLIC
PROC [name:
ROPE, points: TripleSequence]
RETURNS [Shape] ~ {
ERROR;
};
Cleaving
SkipNPolys:
PUBLIC
PROC [shape: Shape, nPolys:
NAT] ~ {
FOR n:
NAT
IN [nPolys..shape.surfaces.length)
DO
shape.surfaces[n-nPolys] ¬ shape.surfaces[n];
ENDLOOP;
shape.surfaces.length ¬ shape.surfaces.length-nPolys;
};
Hash:
PROC [key:
REF
ANY]
RETURNS [
CARD] ~ {
i: REF IntegerPair ~ NARROW[key];
RETURN[i.x+i.y];
};
Equal:
PROC [key1, key2:
REF
ANY]
RETURNS [
BOOL] ~ {
RETURN[NARROW[key1, REF IntegerPair]^ = NARROW[key2, REF IntegerPair]^];
};
Cleave:
PUBLIC
PROC [shape: Shape, plane: Plane, cap:
BOOL ¬
FALSE]
RETURNS [neg, pos: Shape ¬ NIL]
~ {
CapInfo: TYPE ~ RECORD [vID, pID0, pID1: INTEGER ¬ -1];
Caps: TYPE ~ RECORD [len: CARD ¬ 0, s: SEQUENCE max: CARD OF REF CapInfo];
VertexState: TYPE ~ RECORD [v: Vertex, dist: REAL, sign: Border, old, pos, neg: INTEGER];
VertexStates: TYPE ~ RECORD [element: SEQUENCE max: CARD OF VertexState];
IF shape #
NIL
AND shape.vertices #
NIL
AND shape.vertices.length > 2
THEN {
Init:
PROC
RETURNS [s: Shape] ~ {
s ¬ NEW[ShapeRep];
s.vertices ¬ NEW[VertexSequenceRep[10]];
s.surfaces ¬ NEW[SurfaceSequenceRep[10]];
s.faces ¬ NEW[FaceSequenceRep[10]];
};
AddVertex:
PROC [s: Shape, v: Vertex] ~ {
s.vertices ¬ G3dShape.AddToVertexSequence[s.vertices, NEW[VertexRep ¬ v^]];
};
AddPoly:
PROC [s: Shape, poly: NatSequence, old:
NAT] ~ {
s.surfaces ¬ G3dBasic.AddToSurfaceSequence[s.surfaces, [NIL, poly]];
IF shape.faces #
NIL
AND shape.faces.length > old
THEN
s.faces ¬ G3dShape.AddToFaceSequence[s.faces, NEW[FaceRep ¬ shape.faces[old]^]];
};
AllInPlane:
PROC [poly: NatSequence]
RETURNS [b:
BOOL ¬
TRUE] ~ {
FOR i:
INTEGER
IN [0..poly.length)
DO
IF vertexStates[poly[i]].sign # edge THEN RETURN[FALSE];
ENDLOOP;
};
ToCaps:
PROC [caps:
REF Caps, vID, pID:
INTEGER]
RETURNS [ret:
REF Caps] ~ {
IF (ret ¬ caps) = NIL THEN ret ¬ NEW[Caps[1]];
FOR i:
INTEGER
IN [0..ret.len)
DO
IF ret[i].vID = vID THEN {ret[i].pID1 ¬ pID; RETURN};
ENDLOOP;
IF ret.len = ret.max
THEN {
old: REF Caps ¬ ret;
ret ¬ NEW[Caps[MAX[2*old.max, 3]]];
FOR i: INTEGER IN [0..(ret.len ¬ old.len)) DO ret[i] ¬ old[i]; ENDLOOP;
};
ret[ret.len] ¬ NEW[CapInfo ¬ [vID, pID, -1]];
ret.len ¬ ret.len+1;
};
PosNegToCaps:
PROC [posVid, negVid, posPid, negPid:
INTEGER] ~ {
posCaps ¬ ToCaps[posCaps, posVid, posPid];
negCaps ¬ ToCaps[negCaps, negVid, negPid];
};
Cap:
PROC [s: Shape, caps:
REF Caps] ~ {
This will fail for concave polygons (ie, a polygon cut more than once by the plane).
GetAny:
PROC
RETURNS [r:
REF CapInfo] ~ {
FOR i: INTEGER IN [0..caps.len) DO IF (r ¬ caps[i]) # NIL THEN EXIT; ENDLOOP;
};
GetForIDAndDelete:
PROC [pID:
INTEGER]
RETURNS [r:
REF CapInfo] ~ {
FOR
i:
INTEGER
IN
[0..caps.len)
DO
IF (r¬caps[i]) # NIL AND (r.pID0=pID OR r.pID1=pID) THEN {caps[i]¬NIL; RETURN};
ENDLOOP;
RETURN[NIL];
};
a, b: REF CapInfo;
WHILE (a ¬ GetAny[]) #
NIL
DO
poly: NatSequence ¬ NIL;
start, id: INTEGER ¬ a.pID0;
WHILE (b ¬ GetForIDAndDelete[id]) #
NIL
DO
poly ¬ G2dBasic.AddToNatSequence[poly, b.vID];
id ¬ IF b.pID0 = id THEN b.pID1 ELSE b.pID0;
ENDLOOP;
IF id = start THEN s.surfaces ¬ G3dBasic.AddToSurfaceSequence[s.surfaces, [, poly]];
ENDLOOP;
};
posCaps, negCaps: REF Caps ¬ NIL;
hash: RefTab.Ref ¬ RefTab.Create[equal: Equal, hash: Hash];
vertexStates: REF VertexStates ¬ NEW[VertexStates[shape.vertices.length]];
pos ¬ Init[];
neg ¬ Init[];
FOR i:
INTEGER
IN [0..shape.vertices.length)
DO
v: Vertex ¬ shape.vertices[i];
dist: REAL ¬ G3dPlane.DistanceToPoint[v.point, plane, FALSE];
sign: Border ¬ SELECT dist FROM > 0.01 => inside, < -0.01 => outside, ENDCASE => edge;
vertexStates[i] ¬ [v, dist, sign, i, pos.vertices.length, neg.vertices.length];
IF sign = outside OR sign = edge THEN AddVertex[neg, v];
IF sign = inside OR sign = edge THEN AddVertex[pos, v];
ENDLOOP;
FOR n: INTEGER IN [0..shape.surfaces.length) DO
};
Curve Procedures
DrawCurves:
PUBLIC PROC [
context: Context,
shape: Shape,
view: Matrix,
viewport: Viewport ¬ [],
type: DrawType ¬ solid,
backFaces: BOOL ¬ TRUE,
screens: ScreenSequence ¬ NIL,
forceTransform: BOOL ¬ FALSE,
forInterpress: BOOL ¬ FALSE]
RETURNS [ScreenSequence]
~ {
FrontFacingAction: SurfaceProc ~ {
i0: CARD ¬ surface[surface.length-1];
FOR n:
INTEGER
IN [0..surface.length)
DO
i1: CARD ¬ surface[n];
index: INT ¬ G3dShape.FindEdge[shape.edges, i0, i1];
i0 ¬ i1;
IF index = -1 THEN LOOP;
IF type # solid
THEN G3dDraw.DotCurve[context, model.curves[index], x, viewport]
ELSE G3dDraw.Curve[context, model.curves[index], x, viewport,, forInterpress];
ENDLOOP;
};
model: Model ¬ GetModel[shape];
mat: Matrix ¬ IF shape.matrix # NIL THEN shape.matrix ELSE G3dMatrix.Identity[];
x: Matrix ¬ G3dMatrix.Mul[shape.matrix ¬ mat, view, G3dMatrix.ObtainMatrix[]];
IF model = NIL THEN RETURN[NIL];
IF forceTransform
OR screens =
NIL
OR
NOT screens.screensValid
THEN screens ¬ G3dDraw.SetScreenCoords[context, shape, x, viewport, screens];
IF shape.edges = NIL THEN shape.edges ¬ G3dShape.MakeEdges[shape];
IF backFaces
THEN G3dDraw.Curves[context, model.curves, x, viewport, type # solid, forInterpress]
ELSE G3dShape.DoWithFacingPolygons[shape, x, FrontFacingAction];
RETURN[screens];
};
SetCurves:
PUBLIC PROC [shape: Shape] ~ {
model: Model ¬ GetModel[shape];
IF G3dShape.VertexValid[shape, normal]
AND shape.edges #
NIL
THEN {
IF model.curves =
NIL
OR model.curves.maxLength < shape.edges.length
THEN model.curves ¬ NEW[SplineSequenceRep[shape.edges.length]];
model.curves.length ¬ shape.edges.length;
FOR n:
NAT
IN [0..shape.edges.length)
DO
e: Edge ¬ shape.edges[n];
v0: Vertex ¬ shape.vertices[e.v0];
v1: Vertex ¬ shape.vertices[e.v1];
model.curves[n] ¬ G3dSpline.SplineFromPointsAndNormals[
v0.point, v1.point, v0.normal, v1.normal,, model.curves[n]];
ENDLOOP;
};
MakeCurves:
PUBLIC PROC [
vertices, normals: TripleSequence,
edges: EdgeSequence,
curves: SplineSequence ¬ NIL]
RETURNS [SplineSequence]
~ {
IF vertices #
NIL
AND normals #
NIL
AND edges #
NIL
THEN {
IF curves =
NIL
OR curves.maxLength < edges.length
THEN curves ¬ NEW[SplineSequenceRep[edges.length]];
curves.length ¬ edges.length;
FOR n:
NAT
IN [0..edges.length)
DO
e: Edge ¬ edges[n];
curves[n] ¬ G3dSpline.SplineFromPointsAndNormals[
vertices[e.v0], vertices[e.v1], normals[e.v0], normals[e.v1], , curves[n]];
ENDLOOP;
};
RETURN[curves];
};
NormalsFromCurve:
PUBLIC
PROC
[curve:
PairSequence]
RETURNS [curveNormals: PairSequence]
Unit normals are created by rotating estimated tangents left 90° as we follow curve.
If data is specified in the opposite order, the normals may be 180° out of phase.
For evenly spaced data points tangent estimation is no problem; irregularly spaced points require more care. The intent here is to average the (normalized) incoming and outgoing vectors at an interior point when their magnitudes are the same, so simple data works as expected. However when one of the vectors is small compared to the other, it gets more weight (after normalization)--on the assumption that the curve is parameterized by arc length so nearly incident points provide a better tangent estimate.
~ {
curveNormals ¬ NEW[PairSequenceRep[curve.length]];
curveNormals.length ¬ curve.length;
SELECT curve.length
FROM
= 2 => {
tangent: Pair ¬ G2dVector.Unit[G2dVector.Sub[curve[1], curve[0]]];
curveNormals[0] ¬ curveNormals[1] ¬ [-tangent.y, tangent.x];
};
> 2 => {
dm, dp, tangent: Pair;
delm, delp, delSum: REAL;
Estimate tangent from chord length parameterized parabola
FOR i:
NAT
IN [1..curve.length-1)
DO
dm ¬ G2dVector.Sub[curve[i], curve[i-1]];
dp ¬ G2dVector.Sub[curve[i+1], curve[i]];
delm ¬ ABS[dm.x] + ABS[dm.y];
delp ¬ ABS[dp.x] + ABS[dp.y];
delSum ¬ delm+delp;
tangent ¬ G2dVector.Combine[G2dVector.Unit[dm], delp/delSum,
G2dVector.Unit[dp], delm/delSum];
curveNormals[i] ¬ G2dVector.Unit[[-tangent.y, tangent.x]];
ENDLOOP;
Estimate tangent at trailing end using left-over values
tangent ¬ G2dVector.Combine[G2dVector.Unit[dp], 2.0*delSum,
G2dVector.Unit[tangent], -1];
curveNormals[curve.length-1] ¬ G2dVector.Unit[[-tangent.y, tangent.x]];
Estimate tangent at leading end
dm ¬ G2dVector.Sub[curve[1], curve[0]];
dp ¬ G2dVector.Sub[curve[2], curve[1]];
delm ¬ ABS[dm.x] + ABS[dm.y];
delp ¬ ABS[dp.x] + ABS[dp.y];
delSum ¬ delm+delp;
tangent ¬ G2dVector.Combine[G2dVector.Unit[dm], 2.0*delSum,
[curveNormals[1].y, -curveNormals[1].x], -1];
curveNormals[0] ¬ G2dVector.Unit[[-tangent.y, tangent.x]];
};
ENDCASE;
};