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;
G3dModelImpl: CEDAR MONITOR
IMPORTS G2dBasic, G2dVector, G3dBasic, G3dDraw, G3dMatrix, G3dPlane, G3dPolygon, G3dShape, G3dSpline, G3dVector, RefTab, RuntimeError
EXPORTS G3dModel
~ BEGIN
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;
ROPE:      TYPE ~ Rope.ROPE;
BoundsFault:    ERROR ~ RuntimeError.BoundsFault;
Error:      PUBLIC SIGNAL [code: ATOM, reason: ROPE] = CODE;
File IO
ShapeFromFile: PUBLIC PROC [
fileName: ROPE,
points: BOOL ¬ TRUE,
polygons: BOOL ¬ FALSE,
faceCenters: BOOL ¬ TRUE,
faceNormals: BOOL ¬ TRUE,
edges: BOOL ¬ TRUE,
curves: BOOL ¬ FALSE,
normal: BOOL ¬ FALSE,
center: BOOL ¬ FALSE,
lines: BOOL ¬ FALSE,
accs: BOOL ¬ FALSE,
area: BOOL ¬ FALSE]
RETURNS [s: Shape]
~ {
s ¬ G3dShape.ShapeFromFile[fileName];
SetShape[s, points, polygons, faceCenters, faceNormals, edges, curves, normal, center, lines, accs, area];
};
Shape Specification
GetModel: PUBLIC PROC [shape: Shape] RETURNS [m: Model] ~ {
m ¬ SELECT TRUE FROM
shape = NIL => NIL,
shape.modelData # NIL => NARROW[shape.modelData],
ENDCASE => (shape.modelData ¬ NEW[ModelRep]);
};
SetShape: PUBLIC PROC [
shape: Shape,
points: BOOL ¬ TRUE,
polygons: BOOL ¬ FALSE,
faceCenters: BOOL ¬ TRUE,
faceNormals: BOOL ¬ TRUE,
edges: BOOL ¬ TRUE,
curves: BOOL ¬ FALSE,
normal: BOOL ¬ FALSE,
center: BOOL ¬ FALSE,
lines: BOOL ¬ FALSE,
accs: BOOL ¬ FALSE,
area: BOOL ¬ FALSE]
~ {
IF points THEN SetPoints[shape];
IF faceCenters THEN G3dShape.SetFaceCenters[shape];
IF faceNormals AND NOT G3dShape.FaceValid[shape, normal]
THEN G3dShape.SetFaceNormals[shape, FALSE];
IF polygons THEN SetPolygons[shape, normal, center, area, accs, lines];
IF edges THEN shape.edges ¬ G3dShape.MakeEdges[shape ! BoundsFault => CONTINUE];
IF curves THEN SetCurves[shape];
};
Vertex Procedures
SetPoints: PUBLIC PROC [shape: Shape] ~ {
m: Model ¬ GetModel[shape];
m.points ¬ G3dShape.PointsFromShape[shape, m.points];
};
IsolateVertices: PUBLIC PROC [shape: Shape] ~ {
model: Model ¬ GetModel[shape];
nvert, vertno: NAT ¬ 0;
old: VertexSequence ¬ shape.vertices;
compute nvert, the number of new vertices: one per polygon per vertex, and allocate them
FOR i: NAT IN [0..shape.surfaces.length) DO
nvert ¬ nvert+shape.surfaces[i].vertices.length;
ENDLOOP;
shape.vertices ¬ NEW[VertexSequenceRep[nvert]]; -- allocate new vertices
shape.vertices.valid ¬ old.valid;
shape.vertices.length ¬ nvert;
shape.verticesIsolated ¬ TRUE;
modify polygon indices and build new vertex list
FOR i: NAT IN [0..shape.surfaces.length) DO       -- for each polygon
poly: NatSequence ¬ shape.surfaces[i].vertices;     -- this polygon
FOR j: NAT IN [0..poly.length) DO         -- for each vertex of poly
shape.vertices[vertno] ¬ NEW[VertexRep ¬ old[poly[j]]^];  -- new vertex
poly[j] ¬ vertno;        -- modify index list
vertno ¬ vertno+1;
ENDLOOP;
ENDLOOP;
};
DeleteVertices: PUBLIC PROC [shape: Shape, vId0, vId1: NAT] ~ {
nPoly: NAT ¬ 0;
nDeleted: NAT ¬ ABS[vId1-vId0]+1;
FOR n: NAT IN [MIN[vId0, vId1]..shape.vertices.length-nDeleted) DO
shape.vertices[n] ¬ shape.vertices[n] ¬ shape.vertices[n+nDeleted];
ENDLOOP;
WHILE nPoly < shape.surfaces.length DO
poly: NatSequence ¬ shape.surfaces[nPoly].vertices;
FOR p: NAT IN [0..poly.length) DO
SELECT poly[p] FROM
IN [vId0..vId1] => {  -- this polygon now bogus
FOR n: NAT IN [nPoly..shape.surfaces.length-1) DO
shape.surfaces[n] ¬ shape.surfaces[n-1];
ENDLOOP;
shape.surfaces.length ¬ shape.surfaces.length-1;
LOOP;
};
> vId1 => poly[p] ¬ poly[p]-nDeleted;
ENDCASE;
nPoly ¬ nPoly+1;
ENDLOOP;
ENDLOOP;
};
Polygon Procedures
SetPolygons: PUBLIC PROC [
shape: Shape,
normal, center, area, accs, lines: BOOL,
complyWithVertices: BOOL ¬ TRUE]
~ {
model: Model ¬ GetModel[shape];
normalFromFace: BOOL ¬ normal AND G3dShape.FaceValid[shape, normal];
IF normalFromFace THEN normal ¬ FALSE; -- we'll get from shape.faces
IF shape.vertices = NIL AND shape.surfaces = NIL THEN RETURN;
IF model.polygons = NIL OR model.polygons.maxLength < shape.surfaces.length
THEN model.polygons ¬ NEW[PolygonSequenceRep[shape.surfaces.length]];
model.polygons.length ¬ shape.surfaces.length;
FOR n: NAT IN [0..shape.surfaces.length) DO
poly: NatSequence ¬ shape.surfaces[n].vertices;
polygon: Polygon ¬ model.polygons[n] ¬ NEW[G3dPolygon.PolygonRep];
polygon.points ¬ NEW[TripleSequenceRep[poly.length]];
polygon.points.length ¬ poly.length;
polygon.indices ¬ poly;
FOR n: NAT IN [0..poly.length) DO
polygon.points[n] ¬ shape.vertices[poly[n]].point;
ENDLOOP;
IF normalFromFace THEN {  -- if complyWithVertices, assume faces[n].normal complies
v: Triple ¬ shape.faces[n].normal;
polygon.plane ¬ [v.x, v.y, v.z, G3dPlane.DFromNormalAndPoints[v, polygon.points]];
IF normalFromFace THEN polygon.normal ¬ v;
};
G3dPolygon.SetPolygon[polygon, polygon.points, normal, center, area, accs, lines];
ENDLOOP;
IF complyWithVertices AND shape.vertices # NIL AND normal THEN
we didn't have face normals, so comply with vertices by testing polygon plane or normal
FOR n: NAT IN [0..shape.surfaces.length) DO
p: Polygon ¬ model.polygons[n];
v: Triple ¬ IF normal THEN p.normal ELSE [p.plane.x, p.plane.y, p.plane.z];
IF G3dVector.Dot[v, shape.vertices[p.indices[0]].normal] < 0.0 THEN {
p.plane ¬ [-p.plane.x, -p.plane.y, -p.plane.z, -p.plane.w];
IF normal THEN p.normal ¬ [-p.plane.x, -p.plane.y, -p.plane.z];
};
ENDLOOP;
};
CullBadPolygons: PUBLIC PROC [polygons: SurfaceSequence, maxNVertices: NAT] ~ {
n: NAT ¬ 0;
DO
poly: NatSequence ¬ polygons[n].vertices;
FOR nn: NAT IN [0..poly.length) DO
IF poly[nn] >= maxNVertices THEN { -- overwrite this polygon
polygons.length ¬ polygons.length-1;
IF n # polygons.length THEN polygons[n] ¬ polygons[polygons.length];
EXIT;
};
REPEAT
FINISHED => n ¬ n+1;
ENDLOOP;
IF n >= polygons.length THEN RETURN;
ENDLOOP;
};
Nearness Procedures
EnsureModelPolygons: PROC [s: Shape] RETURNS [m: Model] ~ {
IF (m ¬ GetModel[s]) # NIL THEN {
ok: BOOL ¬ m.polygons # NIL AND m.polygons.length # 0;
IF ok THEN {
p: Polygon ¬ m.polygons[0];
ok ¬ p.plane # [] AND p.accs # NIL AND p.lines # NIL;
};
IF NOT ok THEN SetPolygons[s, TRUE, TRUE, FALSE, TRUE, TRUE];
};
};
IntersectWithRay: PUBLIC PROC [shape: Shape, ray: Ray] RETURNS [near: NearModel] ~ {
model: Model ¬ EnsureModelPolygons[shape];
minSqDistance: REAL ¬ Real.LargestNumber;
IF model = NIL THEN RETURN;
FOR n: NAT IN [0..model.polygons.length) DO
polygon: Polygon ¬ model.polygons[n];
pdDot: REAL ¬ G3dVector.Dot[polygon.normal, ray.axis]; -- see G3dPlaneImpl
IF pdDot # 0.0 THEN {
alpha: REAL ¬ (-polygon.plane.w-G3dVector.Dot[polygon.normal, ray.base])/pdDot;
IF alpha >= 0.0 THEN {   -- if < 0.0 then no intersection in direction of ray
intersection: Triple ¬ G3dVector.ScaleRay[ray, alpha];
sqDistance: REAL ¬ G3dVector.SquareDistance[intersection, ray.base];
IF sqDistance < minSqDistance THEN {
IF G3dPolygon.InsidePolygon[intersection, polygon] # outside THEN {
minSqDistance ¬ sqDistance;
near ¬ [intersection, FALSE, n];
};
};
};
};
ENDLOOP;
};
ClosestPoint: PUBLIC PROC [shape: Shape, point: Triple] RETURNS [near: NearModel] ~ {
model: Model ¬ EnsureModelPolygons[shape];
minDistance: REAL ¬ Real.LargestNumber;
IF model # NIL THEN FOR n: NAT IN [0..model.polygons.length) DO
polygon: Polygon ¬ model.polygons[n];
nearPolygon: G3dPolygon.NearPolygon ¬ G3dPolygon.NearestToPolygon[polygon, point];
IF nearPolygon.distance < minDistance THEN {
near ¬ [nearPolygon.point, FALSE, n];
minDistance ¬ nearPolygon.distance;
};
ENDLOOP;
};
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
[Artwork node; type 'Artwork on' to command tool]
oldPoly: NatSequence ¬ shape.surfaces[n].vertices;
posPoly, negPoly: NatSequence ¬ NIL;
IF AllInPlane[oldPoly]
THEN { -- case 1
IF cap THEN FOR i: INTEGER IN [0..oldPoly.length) DO
vs: VertexState ¬ vertexStates[oldPoly[i]];
posPoly ¬ G2dBasic.AddToNatSequence[posPoly, vs.pos];
negPoly ¬ G2dBasic.AddToNatSequence[negPoly, vs.neg];
ENDLOOP;
}
ELSE { -- case 2, 3, 4, or 5
nInside, nOutside, nEdge: INTEGER ¬ 0;
s1: VertexState ¬ vertexStates[oldPoly[oldPoly.length-1]];
FOR i: INTEGER IN [0..oldPoly.length) DO
SELECT vertexStates[oldPoly[i]].sign FROM
inside => nInside ¬ nInside+1;
outside => nOutside ¬ nOutside+1;
ENDCASE => nEdge ¬ nEdge+1;
ENDLOOP;
FOR i: INTEGER IN [0..oldPoly.length) DO
s0: VertexState ¬ s1;
s1 ¬ vertexStates[oldPoly[i]];
IF s0.sign # s1.sign AND s0.sign # edge AND s1.sign # edge THEN { -- interp.
indices: IntegerPair; -- pos and neg indices for interpolated vertex
key: REF IntegerPair ¬NEW[IntegerPair¬[MIN[s0.old,s1.old],MAX[s0.old,s1.old]]];
ref: REF ¬ RefTab.Fetch[hash, key].val;
IF ref # NIL
THEN indices ¬ NARROW[ref, REF IntegerPair]^ -- previously stored
ELSE { -- interpolate and store
v: Vertex ¬ G3dShape.InterpolateVertex[s0.dist/(s0.dist-s1.dist), s0.v, s1.v];
indices ¬ [pos.vertices.length, neg.vertices.length];
[] ¬ RefTab.Store[hash, key, NEW[IntegerPair ¬ indices]];
AddVertex[pos, v];
AddVertex[neg, v];
};
IF cap THEN
PosNegToCaps[indices.x, indices.y, pos.surfaces.length, neg.surfaces.length];
posPoly ¬ G2dBasic.AddToNatSequence[posPoly, indices.x]; -- case 2
negPoly ¬ G2dBasic.AddToNatSequence[negPoly, indices.y]; -- case 2
};
IF cap AND s1.sign = edge THEN SELECT TRUE FROM
nInside > 0 AND nOutside > 0 => -- case 4 (rightmost poly in fig)
PosNegToCaps[s1.pos, s1.neg, pos.surfaces.length, neg.surfaces.length];
nInside > 0 AND nEdge >= 2 => -- case 5 (leftmost poly in fig)
posCaps ¬ ToCaps[posCaps, s1.pos, pos.surfaces.length];
nOutside > 0 AND nEdge >= 2 => -- case 5 (leftmost poly in fig)
negCaps ¬ ToCaps[negCaps, s1.neg, neg.surfaces.length];
ENDCASE; -- case 3
IF nInside > 0 AND s1.sign # outside
THEN posPoly ¬ G2dBasic.AddToNatSequence[posPoly, s1.pos];
IF nOutside > 0 AND s1.sign # inside
THEN negPoly ¬ G2dBasic.AddToNatSequence[negPoly, s1.neg];
ENDLOOP;
};
IF posPoly # NIL THEN AddPoly[pos, posPoly, n]; -- add poly if not fully clipped
IF negPoly # NIL THEN AddPoly[neg, G3dPolygon.PolygonReverse[negPoly], n];
ENDLOOP;
IF posCaps # NIL THEN Cap[pos, posCaps];
IF negCaps # NIL THEN Cap[neg, negCaps];
pos.edges ¬ G3dShape.MakeEdges[pos ! BoundsFault => CONTINUE];
neg.edges ¬ G3dShape.MakeEdges[neg ! BoundsFault => CONTINUE];
};
};
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;
};
END.
..
MakeEdges: PUBLIC PROC [polygons: SurfaceSequence, edges: EdgeSequence ¬ NIL]
RETURNS [EdgeSequence] ~ {
n0, n1, min, max: CARD;
IF vertices = NIL OR polygons = NIL THEN RETURN[NIL];
IF edges = NIL OR edges.maxLength < vertices.length
THEN edges ¬ NEW[EdgeSequence[vertices.length]];
FOR i: NAT IN [0..polygons.length) DO
polygon: NatSequence ~ polygons[i];
n0 ¬ polygon[polygon.length-1];
FOR ii: NAT IN [0..polygon.length) DO
n1 ¬ polygon[ii];
IF n0 < n1 THEN {min ¬ n0; max ¬ n1} ELSE {min ¬ n1; max ¬ n0};
IF min+1 > edges.length THEN edges.length ¬ min+1;
FOR l: LIST OF NAT ¬ edges[min], l.rest WHILE l # NIL DO
IF l.first = max THEN EXIT;
REPEAT
FINISHED => edges[min] ¬ CONS[max, edges[min]];
ENDLOOP;
n0 ¬ n1;
ENDLOOP;
ENDLOOP;
RETURN[edges];
};