ImplicitMinimalImpl.mesa
Copyright © 1991 by Xerox Corporation. All rights reserved.
Bloomenthal, August 11, 1992 4:02 pm PDT
DIRECTORY Basics, G3dBasic, G3dShape, G3dVector, ImplicitDefs, ImplicitMinimal, Random, Real, Rope;
ImplicitMinimalImpl: CEDAR PROGRAM
IMPORTS Basics, G3dBasic, G3dShape, G3dVector, Random, Real, Rope
EXPORTS ImplicitMinimal
~ BEGIN
Types
Triple:   TYPE ~ G3dBasic.Triple;
NatSequence:  TYPE ~ G3dBasic.NatSequence;
ValueProc:  TYPE ~ ImplicitDefs.ValueProc;
Vertex:   TYPE ~ ImplicitMinimal.Vertex;
Vertices:   TYPE ~ ImplicitMinimal.Vertices;
ROPE:    TYPE ~ Rope.ROPE;
Cube:    TYPE ~ RECORD [i, j, k: INTEGER, lbn, lbf, ltn, ltf, rbn, rbf, rtn, rtf: Corner];
Corner:   TYPE ~ RECORD [i, j, k: INTEGER ¬ 0, x, y, z, value: REAL ¬ 0];
Face:    TYPE ~ {l, r, b, t, n, f, unknown};
Edge:    TYPE ~ {lb, lt, ln, lf, rb, rt, rn, rf, bn, bf, tn, tf};
Entry:   TYPE ~ RECORD [i, j, k, id: INTEGER];
Table:    TYPE ~ RECORD [s: SEQUENCE length: CARDINAL OF LIST OF Entry];
IntegerArray: TYPE ~ ImplicitMinimal.IntegerArray;
l(left): -x,-i; r(right): +x,+i; b(bottom): -y,-j; t(top): +y,+j; n(near): -z,-k; f(far): +z,+k
the lbn corner of cube (i, j, k) corresponds with location (i, j, k).
A `Minimal' Polygonizer
Abort: ERROR = CODE;
Polygonize: PUBLIC PROC [
function: ValueProc,
start: Triple ¬ [],
level: REAL,
size: REAL,
delta: REAL,
clientData: REF ANY ¬ NIL,
polygonAction: ImplicitMinimal.PolygonProc ¬ NIL,
triangleAction: ImplicitMinimal.TriangleProc ¬ NIL,
adaptive: REAL ¬ 0.0]
RETURNS [error: ROPE ¬ NIL]
~ {
ENABLE Abort => {error ¬ "aborted"; GOTO Failed};
Poly: PROC [c: Cube] ~ {
DirectedFace: PROC [edge: Edge, dir: BOOL] RETURNS [Face] ~ {
return face about which negative to positive direction
along edge is clockwise if dir or counter-clockwise if not dir.
Sign: PROC [value: REAL] RETURNS [b: BOOL] ~ {
b ¬ (dir AND value > 0.0) OR (NOT dir AND value < 0.0);
};
RETURN[SELECT edge FROM
lb   => IF Sign[c.lbn.value] THEN l ELSE b,
lt   => IF Sign[c.ltn.value] THEN t ELSE l,
ln   => IF Sign[c.lbn.value] THEN n ELSE l,
lf   => IF Sign[c.lbf.value] THEN l ELSE f,
rb   => IF Sign[c.rbn.value] THEN b ELSE r,
rt   => IF Sign[c.rtn.value] THEN r ELSE t,
rn   => IF Sign[c.rbn.value] THEN r ELSE n,
rf   => IF Sign[c.rbf.value] THEN f ELSE r,
bn   => IF Sign[c.lbn.value] THEN b ELSE n,
bf   => IF Sign[c.lbf.value] THEN f ELSE b,
tn   => IF Sign[c.ltn.value] THEN n ELSE t,
ENDCASE => IF Sign[c.ltf.value] THEN t ELSE f];
};
VertexID: PROC [e: Edge] RETURNS [id: INTEGER] ~ {
Return -1 if no vertex on edge; otherwise, return vertex (compute & save if unknown)
v: Vertex;
table: REF Table;
dir: Face; -- from p1 to p2; e.g., if p1 = lbn and p2 = lbf, direction = f
p1, p2: Corner;
SELECT e FROM
lb => {table ¬ zEdges; dir ¬ f; p1 ¬ c.lbn; p2 ¬ c.lbf};
lt => {table ¬ zEdges; dir ¬ f; p1 ¬ c.ltn; p2 ¬ c.ltf};
ln => {table ¬ yEdges; dir ¬ t; p1 ¬ c.lbn; p2 ¬ c.ltn};
lf => {table ¬ yEdges; dir ¬ t; p1 ¬ c.lbf; p2 ¬ c.ltf};
rb => {table ¬ zEdges; dir ¬ f; p1 ¬ c.rbn; p2 ¬ c.rbf};
rt => {table ¬ zEdges; dir ¬ f; p1 ¬ c.rtn; p2 ¬ c.rtf};
rn => {table ¬ yEdges; dir ¬ t; p1 ¬ c.rbn; p2 ¬ c.rtn};
rf => {table ¬ yEdges; dir ¬ t; p1 ¬ c.rbf; p2 ¬ c.rtf};
bn => {table ¬ xEdges; dir ¬ r; p1 ¬ c.lbn; p2 ¬ c.rbn};
bf => {table ¬ xEdges; dir ¬ r; p1 ¬ c.lbf; p2 ¬ c.rbf};
tn => {table ¬ xEdges; dir ¬ r; p1 ¬ c.ltn; p2 ¬ c.rtn};
tf => {table ¬ xEdges; dir ¬ r; p1 ¬ c.ltf; p2 ¬ c.rtf};
ENDCASE;
IF ABS[p1.value] < tiny AND ABS[p2.value] < tiny THEN RETURN[-1]; -- no vertex
IF (p1.value < 0.0) = (p2.value < 0.0) THEN RETURN[-1]; -- no vertex
IF (id ¬ GetIJK[table, p1.i, p1.j, p1.k]) # -1 THEN RETURN[id];
v.position ¬ Converge[[p1.x, p1.y, p1.z], [p2.x, p2.y, p2.z], p1.value, level, function, clientData, dir];
v.normal ¬ Normal[v.position, function, clientData, delta];
vertices ¬ AddToVertices[vertices, v];
[] ¬ SetIJK[table, p1.i, p1.j, p1.k, id ¬ vertices.length-1];
};
AddPolyCenter: PROC [level, nVertices: INTEGER, ids: IntegerArray] ~ {
NewVertex: PROC [p: Triple] ~ {
vertices ¬ AddToVertices[vertices, [p, Normal[p, function, clientData, delta]]];
};
Opp: PROC [a,b: REAL] RETURNS [c: BOOL]~{c¬(a<0 AND b>0) OR (a>0 AND b<0)};
Add: PROC [a,b: Triple] RETURNS [c: Triple] ~ {c¬[a.x+b.x, a.y+b.y, a.z+b.z]};
Mul: PROC [a: Triple, s: REAL] RETURNS [c: Triple] ~ {c¬[s*a.x, s*a.y, s*a.z]};
val: REAL;
p0, p1, normal: Triple ¬ [0, 0, 0];
vertexID: INTEGER ¬ vertices.length;
tris: IntegerArray;
FOR i: NAT IN [0..nVertices) DO p0 ¬ Add[p0, vertices[ids[i]].position]; ENDLOOP;
val ¬ function[p0 ¬ Mul[p0, 1.0/REAL[nVertices]], clientData];
normal ¬ Normal[p0, function, clientData, delta];
FOR i: NAT IN [0..10) DO
IF Opp[val, function[p1 ¬ Add[p0, Mul[normal, step*i]], clientData]] OR
Opp[val, function[p1 ¬ Add[p0, Mul[normal, -step*i]], clientData]] THEN {
NewVertex[Converge[p0, p1, val, level, function, clientData]];
EXIT;
};
REPEAT FINISHED => NewVertex[p0];
ENDLOOP;
tris[0] ¬ vertexID;
IF triangleAction # NIL THEN
FOR i: NAT IN [0..nVertices) DO
IF NOT triangleAction[vertexID, ids[i], ids[(i+1) MOD nVertices], vertices]
THEN ERROR Abort;
IF level = 0 THEN {
tris[1] ¬ ids[i];
tris[2] ¬ ids[(i+1) MOD nVertices];
IF Subdivide[3, tris] THEN AddPolyCenter[1, 3, tris];
};
ENDLOOP;
IF polygonAction # NIL THEN {
FOR i: NAT IN [0..nVertices) DO
tris[1] ¬ ids[i];
tris[2] ¬ ids[(i+1) MOD nVertices];
IF NOT polygonAction[[3, tris], vertices] THEN ERROR Abort;
IF level = 0 AND Subdivide[3, tris] THEN AddPolyCenter[1, 3, tris];
ENDLOOP;
};
};
Subdivide: PROC [nVerts: INTEGER, ids: IntegerArray] RETURNS [BOOL] ~ {
aveNormal: Triple ¬ [0., 0., 0.];
FOR i: NAT IN [0..nVertices) DO
aveNormal ¬ G3dVector.Add[aveNormal, vertices[ids[i]].normal];
ENDLOOP;
aveNormal ¬ G3dVector.Unit[aveNormal];
FOR i: NAT IN [0..nVerts) DO
IF G3dVector.Dot[aveNormal, vertices[ids[i]].normal] < adaptive THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE];
};
tiny: REAL ¬ 1.0e-3;
id, nVertices, nPos: INTEGER ¬ 0;
ids: IntegerArray;
done: ARRAY Edge OF BOOL ¬ ALL[FALSE];
FOR l: LIST OF Corner ¬ LIST[c.lbn, c.lbf, c.ltn, c.ltf, c.rbn, c.rbf, c.rtn, c.rtf],
l.rest WHILE l # NIL DO
IF l.first.value > 0.0 THEN nPos ¬ nPos+1;
ENDLOOP;
FOR edge: Edge IN Edge DO
IF NOT done[edge] AND (ids[0] ¬ VertexID[edge]) # -1 THEN {
e, start: Edge ¬ edge;
face: Face ¬ DirectedFace[e, nPos > 4];
nVertices ¬ 1;
DO
done[e ¬ NextCWEdge[e, face]] ¬ TRUE;
IF e = start THEN EXIT;
IF (id ¬ VertexID[e]) # -1 THEN {
IF nVertices = 6 THEN ERROR; -- RETURN;
ids[nVertices] ¬ id;
nVertices ¬ nVertices+1;
face ¬ OtherFace[e, face];
};
ENDLOOP;
IF nVertices < 3 THEN RETURN; -- ERROR
IF triangleAction # NIL THEN AddPolyCenter[0, nVertices, ids];
IF polygonAction # NIL THEN {
IF adaptive # 0.0 AND triangleAction = NIL AND Subdivide[nVertices, ids]
THEN AddPolyCenter[0, nVertices, ids]
ELSE IF NOT polygonAction[[nVertices, ids], vertices] THEN ERROR Abort;
};
};
ENDLOOP;
};
Set: PROC [x, y, z: REAL, i, j, k: INTEGER] RETURNS [Corner] ~ {
RETURN[[i, j, k, x, y, z, function[[x, y, z], clientData]-level]];
};
TestFace: PROC [i, j, k: INTEGER, c1, c2, c3, c4: Corner, face: Face] ~ {
cube: Cube;
sign: BOOL ¬ c1.value > 0;
lbn, lbf, ltn, ltf, rbn, rbf, rtn, rtf: Corner;
IF (c2.value > 0) = sign AND (c3.value > 0) = sign AND (c4.value > 0) = sign THEN RETURN;
IF SetIJK[centers, i, j, k, 1] THEN RETURN;
SELECT face FROM
l => { -- given right face (rbn, rbf, rtn, rtf); compute left (lbn, lbf, ltn, ltf)
x: REAL ¬ c1.x-size;
i: INTEGER ¬ c1.i-1;
rbn ¬ c1; rbf ¬ c2; rtn ¬ c3; rtf ¬ c4;
lbn ¬ Set[x, rbn.y, rbn.z, i, rbn.j, rtn.k];
lbf ¬ Set[x, rbf.y, rbf.z, i, rbf.j, rbf.k];
ltn ¬ Set[x, rtn.y, rtn.z, i, rtn.j, rtn.k];
ltf ¬ Set[x, rtf.y, rtf.z, i, rtf.j, rtf.k];
};
r => { -- given left face (lbn, lbf, ltn, ltf); compute right (rbn, rbf, rtn, rtf)
x: REAL ¬ c1.x+size;
i: INTEGER ¬ c1.i+1;
lbn ¬ c1; lbf ¬ c2; ltn ¬ c3; ltf ¬ c4;
rbn ¬ Set[x, lbn.y, lbn.z, i, lbn.j, lbn.k];
rbf ¬ Set[x, lbf.y, lbf.z, i, lbf.j, lbf.k];
rtn ¬ Set[x, ltn.y, ltn.z, i, ltn.j, ltn.k];
rtf ¬ Set[x, ltf.y, ltf.z, i, ltf.j, ltf.k];
};
b => { -- given top face (ltn, ltf, rtn, rtf); compute bottom (lbn, lbf, rbn, rbf) face
y: REAL ¬ c1.y-size;
j: INTEGER ¬ c1.j-1;
ltn ¬ c1; ltf ¬ c2; rtn ¬ c3; rtf ¬ c4;
lbn ¬ Set[ltn.x, y, ltn.z, ltn.i, j, ltn.k];
lbf ¬ Set[ltf.x, y, ltf.z, ltf.i, j, ltf.k];
rbn ¬ Set[rtn.x, y, rtn.z, rtn.i, j, rtn.k];
rbf ¬ Set[rtf.x, y, rtf.z, rtf.i, j, rtf.k];
};
t => { -- given bottom face (lbn, lbf, rbn, rbf); compute top (ltn, ltf, rtn, rtf)
y: REAL ¬ c1.y+size;
j: INTEGER ¬ c1.j+1;
lbn ¬ c1; lbf ¬ c2; rbn ¬ c3; rbf ¬ c4;
ltn ¬ Set[lbn.x, y, lbn.z, lbn.i, j, lbn.k];
ltf ¬ Set[lbf.x, y, lbf.z, lbf.i, j, lbf.k];
rtn ¬ Set[rbn.x, y, rbn.z, rbn.i, j, rbn.k];
rtf ¬ Set[rbf.x, y, rbf.z, rbf.i, j, rbf.k];
};
n => { -- given far face (lbf, ltf, rbf, rtf); compute near (lbn, ltn, rbn, rtn)
z: REAL ¬ c1.z-size;
k: INTEGER ¬ c1.k-1;
lbf ¬ c1; ltf ¬ c2; rbf ¬ c3; rtf ¬ c4;
lbn ¬ Set[lbf.x, lbf.y, z, lbf.i, lbf.j, k];
ltn ¬ Set[ltf.x, ltf.y, z, ltf.i, ltf.j, k];
rbn ¬ Set[rbf.x, rbf.y, z, rbf.i, rbf.j, k];
rtn ¬ Set[rtf.x, rtf.y, z, rtf.i, rtf.j, k];
};
f => { -- given near face (lbn, ltn, rbn, rtn); compute far (lbf, ltf, rbf, rtf)
z: REAL ¬ c1.z+size;
k: INTEGER ¬ c1.k+1;
lbn ¬ c1; ltn ¬ c2; rbn ¬ c3; rtn ¬ c4;
lbf ¬ Set[lbn.x, lbn.y, z, lbn.i, lbn.j, k];
ltf ¬ Set[ltn.x, ltn.y, z, ltn.i, ltn.j, k];
rbf ¬ Set[rbn.x, rbn.y, z, rbn.i, rbn.j, k];
rtf ¬ Set[rtn.x, rtn.y, z, rtn.i, rtn.j, k];
};
ENDCASE;
cube ¬ [i, j, k, lbn, lbf, ltn, ltf, rbn, rbf, rtn, rtf];
cubes ¬ IF cubes = NIL THEN LIST[cube] ELSE CONS[cube, cubes];
};
Propagate: PROC [c: Cube] ~ {
simple, due to cube symmetry
TestFace[c.i-1, c.j, c.k, c.lbn, c.lbf, c.ltn, c.ltf, l];
TestFace[c.i+1, c.j, c.k, c.rbn, c.rbf, c.rtn, c.rtf, r];
TestFace[c.i, c.j-1, c.k, c.lbn, c.lbf, c.rbn, c.rbf, b];
TestFace[c.i, c.j+1, c.k, c.ltn, c.ltf, c.rtn, c.rtf, t];
TestFace[c.i, c.j, c.k-1, c.lbn, c.ltn, c.rbn, c.rtn, n];
TestFace[c.i, c.j, c.k+1, c.lbf, c.ltf, c.rbf, c.rtf, f];
};
vertices: Vertices ¬ NIL;
xEdges: REF Table ¬ NEW[Table[hashSize]];
yEdges: REF Table ¬ NEW[Table[hashSize]];
zEdges: REF Table ¬ NEW[Table[hashSize]];
centers: REF Table ¬ NEW[Table[hashSize]];
lbn, lbf, ltn, ltf, rbn, rbf, rtn, rtf: Corner;
cubes: LIST OF Cube;
step: REAL ~ 0.1*size;
cen: Triple ¬ FindStart[start, function, clientData, size, level
! Error => {error ¬ reason; GOTO Failed}];
lbn ¬ Set[cen.x-0.5*size, cen.y-0.5*size, cen.z-0.5*size, 0, 0, 0];
lbf ¬ Set[cen.x-0.5*size, cen.y-0.5*size, cen.z+0.5*size, 0, 0, 1];
ltn ¬ Set[cen.x-0.5*size, cen.y+0.5*size, cen.z-0.5*size, 0, 1, 0];
ltf ¬ Set[cen.x-0.5*size, cen.y+0.5*size, cen.z+0.5*size, 0, 1, 1];
rbn ¬ Set[cen.x+0.5*size, cen.y-0.5*size, cen.z-0.5*size, 1, 0, 0];
rbf ¬ Set[cen.x+0.5*size, cen.y-0.5*size, cen.z+0.5*size, 1, 0, 1];
rtn ¬ Set[cen.x+0.5*size, cen.y+0.5*size, cen.z-0.5*size, 1, 1, 0];
rtf ¬ Set[cen.x+0.5*size, cen.y+0.5*size, cen.z+0.5*size, 1, 1, 1];
cubes ¬ LIST[[0, 0, 0, lbn, lbf, ltn, ltf, rbn, rbf, rtn, rtf]];
WHILE cubes # NIL DO
c: Cube ¬ cubes.first;
debug ¬ c.i=2 AND c.j=2 AND c.k=4;
Poly[c];
cubes ¬ cubes.rest;
Propagate[c];
ENDLOOP;
EXITS Failed => RETURN[error];
};
debug: BOOL ¬ FALSE;
PrintCube: PROC [c: Cube, title: ROPE] ~ {IF debug THEN TerminalIO.PutF["%g i, j, k= %g, %g, %g\n", IO.rope[title], IO.int[c.i], IO.int[c.j], IO.int[c.k]]};
Sparse 3d Storage
hashNBits: INTEGER ~ 5;
hashSize: INTEGER ~ Basics.BITSHIFT[1, 3*hashNBits];
Hash: PROC [i, j, k: INTEGER] RETURNS [id: INTEGER] ~ INLINE {
Courtesy Brian Wyvill
mask: INTEGER ~ Basics.BITSHIFT[1, hashNBits]-1;
id ¬ Basics.BITOR[
Basics.BITSHIFT[
Basics.BITOR[
Basics.BITSHIFT[Basics.BITAND[ABS[i], mask], hashNBits],
Basics.BITAND[ABS[j], mask]],
hashNBits],
Basics.BITAND[ABS[k], mask]];
};
SetIJK: PROC [table: REF Table, i, j, k, id: INTEGER] RETURNS [b: BOOL ¬ FALSE] ~ {
Return true if already set; otherwise, set and return false.
index: INTEGER ¬ Hash[i, j, k];
list: LIST OF Entry ¬ table[index];
IF list = NIL THEN {table[index] ¬ LIST[[i, j, k, id]]; RETURN};
FOR l: LIST OF Entry ¬ list, l.rest WHILE l # NIL DO
IF l.first.i = i AND l.first.j = j AND l.first.k = k THEN {l.first.id ¬ id; RETURN[TRUE]};
ENDLOOP;
table[index] ¬ CONS[[i, j, k, id], list];
};
GetIJK: PROC [table: REF Table, i, j, k: INTEGER] RETURNS [id: INTEGER ¬ -1] ~ {
Get integer at i, j, k; id = -1 if [i, j, k] not set.
FOR l: LIST OF Entry ¬ table[Hash[i, j, k]], l.rest WHILE l # NIL DO
IF l.first.i = i AND l.first.j = j AND l.first.k = k THEN RETURN[l.first.id];
ENDLOOP;
};
Miscellany
AddToVertices: PROC [vertices: Vertices, v: Vertex] RETURNS [new: Vertices] ~ {
new ¬ IF vertices = NIL THEN NEW[ImplicitMinimal.VerticesRep[1]] ELSE vertices;
IF new.length = new.maxLength THEN {
old: Vertices ¬ new;
new ¬ NEW[ImplicitMinimal.VerticesRep[MAX[Real.Ceiling[1.5*old.maxLength], 3]]];
FOR i: NAT IN [0..new.length ¬ old.length) DO new[i] ¬ old[i]; ENDLOOP;
};
new[new.length] ¬ v;
new.length ¬ new.length+1;
};
Error: PUBLIC ERROR [reason: ROPE] = CODE;
ShapeFromFunction: PUBLIC PROC [
function: ValueProc,
start: Triple ¬ [],
level, size, delta: REAL,
clientData: REF ANY ¬ NIL,
status: ImplicitMinimal.StatusProc ¬ NIL,
triangulate: BOOL ¬ FALSE,
adaptive: REAL ¬ 0.0]
RETURNS [s: G3dShape.Shape ¬ NIL]
~ {
Tri: ImplicitMinimal.TriangleProc ~ {
triangle: NatSequence ¬ NEW[G3dBasic.NatSequenceRep[3]];
triangle[0] ¬ i2; triangle[1] ¬ i1; triangle[2] ¬ i0; -- reverse triangle direction
triangle.length ¬ 3;
RETURN[SavePoly[triangle, vertices]];
};
Poly: ImplicitMinimal.PolygonProc ~ {
poly: NatSequence ¬ NEW[G3dBasic.NatSequenceRep[polygon.nVertices]];
FOR i: NAT IN [0..poly.length ¬ polygon.nVertices) DO
poly[i] ¬ polygon.ids[poly.length-1-i]; -- reverse polygon direction
ENDLOOP;
RETURN[SavePoly[poly, vertices]];
};
SavePoly: PROC [polygon: NatSequence, vertices: Vertices] RETURNS [cont: BOOL ¬ TRUE] ~ {
points ¬ vertices;
polys ¬ G3dBasic.AddToSurfaceSequence[polys, [NIL, polygon]];
IF status # NIL AND NOT status[polygon, vertices] THEN RETURN[FALSE];
};
points: ImplicitMinimal.Vertices ¬ NIL;
polys: G3dBasic.SurfaceSequence ¬ NIL;
tri: ImplicitMinimal.TriangleProc ¬ IF triangulate THEN Tri ELSE NIL;
poly: ImplicitMinimal.PolygonProc ¬ IF triangulate THEN NIL ELSE Poly;
error: ROPE ¬ Polygonize[function, start, level, size, delta, clientData, poly, tri, adaptive];
IF points = NIL OR points.length = 0 THEN
error ¬ Rope.Cat[error, IF error # NIL THEN ", " ELSE NIL, "no vertices"];
IF polys = NIL OR polys.length = 0 THEN
error ¬ Rope.Cat[error, IF error # NIL THEN ", " ELSE NIL, "no polygons"];
IF error # NIL
THEN Error[error]
ELSE {
type: ATOM ¬ IF triangulate THEN $Triangulated ELSE $ConvexPolygon;
vertices: G3dBasic.TripleSequence ¬ NEW[G3dBasic.TripleSequenceRep[points.length]];
normals: G3dBasic.TripleSequence ¬ NEW[G3dBasic.TripleSequenceRep[points.length]];
FOR n: NAT IN [0..vertices.length ¬ normals.length ¬ points.length) DO
vertices[n] ¬ points[n].position;
normals[n] ¬ points[n].normal;
ENDLOOP;
s ¬ G3dShape.ShapeFromData[NIL, polys, vertices, normals,,,, FALSE,, type];
s.selected ¬ TRUE;
};
};
Triangulate: PUBLIC PROC [
shape: G3dShape.Shape,  -- polygonized shape to be triangulated
function: ValueProc,
level, size, delta: REAL,
clientData: REF ANY ¬ NIL]
RETURNS [s: G3dShape.Shape]
~ {
Opp: PROC [a, b: REAL] RETURNS [c: BOOL] ~ {c ¬ (a<0 AND b>0) OR (a>0 AND b<0)};
nTriangles: NAT ¬ 0;
val, step: REAL ¬ 0.1*size;
IF shape = NIL THEN RETURN[NIL];
FOR i: NAT IN [0..shape.surfaces.length) DO
nTriangles ¬ nTriangles+shape.surfaces[i].vertices.length;
ENDLOOP;
s ¬ NEW[G3dShape.ShapeRep ¬ [vertices: G3dShape.CopyVertexSequence[shape.vertices]]];
s.surfaces ¬ NEW[G3dBasic.SurfaceSequenceRep[nTriangles]];
FOR i: NAT IN [0..shape.surfaces.length) DO
p, q, n: Triple ¬ [0, 0, 0];
poly: NatSequence ¬ shape.surfaces[i].vertices;
FOR i: NAT IN [0..poly.length) DO
p ¬ G3dVector.Add[p, shape.vertices[poly[i]].point];
ENDLOOP;
val ¬ function[p ¬ G3dVector.Mul[p, 1.0/REAL[poly.length]], clientData];
n ¬ Normal[p, function, clientData, delta];
FOR j: NAT IN [0..10) DO
IF Opp[val, function[q ¬ G3dVector.ScaleRay[[p, n], -step*i], clientData]-level] OR
Opp[val, function[q ¬ G3dVector.ScaleRay[[p, n], step*i], clientData]-level] THEN {
position: Triple ¬ Converge[p, q, val, level, function, clientData, unknown];
normal: Triple ¬ Normal[position, function, clientData, delta];
v: G3dShape.Vertex ¬ NEW[G3dShape.VertexRep ¬ [position, normal]];
p0: NAT ¬ shape.vertices.length;
shape.vertices ¬ G3dShape.AddToVertexSequence[shape.vertices, v];
FOR p: NAT IN [0..poly.length) DO
nats: NatSequence ¬ NEW[G3dBasic.NatSequenceRep[3]];
nats.length ¬ 3;
nats[0] ¬ p0; nats[1] ¬ poly[p]; nats[2] ¬ poly[(p+1) MOD poly.length];
s.surfaces[s.surfaces.length].vertices ¬ nats;
s.surfaces.length ¬ s.surfaces.length+1;
ENDLOOP;
EXIT;
};
ENDLOOP;
ENDLOOP;
};
Topology
NextCWEdge: PROC [edge: Edge, face: Face] RETURNS [Edge] ~ {
RETURN[SELECT edge FROM
lb   => IF face = l THEN lf ELSE bn,
lt   => IF face = l THEN ln ELSE tf,
ln   => IF face = l THEN lb ELSE tn,
lf   => IF face = l THEN lt ELSE bf,
rb   => IF face = r THEN rn ELSE bf,
rt   => IF face = r THEN rf ELSE tn,
rn   => IF face = r THEN rt ELSE bn,
rf   => IF face = r THEN rb ELSE tf,
bn   => IF face = b THEN rb ELSE ln,
bf   => IF face = b THEN lb ELSE rf,
tn   => IF face = t THEN lt ELSE rn,
ENDCASE => IF face = t THEN rt ELSE lf];
};
OtherFace: PROC [edge: Edge, face: Face] RETURNS [Face] ~ {
RETURN[SELECT edge FROM
lb   => IF face = b THEN l ELSE b,
lt   => IF face = l THEN t ELSE l,
ln   => IF face = l THEN n ELSE l,
lf   => IF face = f THEN l ELSE f,
rb   => IF face = r THEN b ELSE r,
rt   => IF face = t THEN r ELSE t,
rn   => IF face = n THEN r ELSE n,
rf   => IF face = r THEN f ELSE r,
bn   => IF face = n THEN b ELSE n,
bf   => IF face = b THEN f ELSE b,
tn   => IF face = t THEN n ELSE t,
ENDCASE => IF face = f THEN t ELSE f];
};
Geometry
FindStart: PUBLIC PROC [
ballpark: Triple,
function: ValueProc,
clientData: REF ANY,
size, level: REAL]
RETURNS [c: Triple]
~ {
Found: TYPE ~ RECORD [bad: BOOL, p: Triple];
Find: PROC [sign: {negative, positive}] RETURNS [found: Found ¬ [FALSE, ballpark]] ~ {
value: REAL;
FOR i: NAT IN [0..5000) DO
found.p.x ¬ found.p.x+size*(Random.NextInt[rs]/REAL[LAST[INT]]-0.5);
found.p.y ¬ found.p.y+size*(Random.NextInt[rs]/REAL[LAST[INT]]-0.5);
found.p.z ¬ found.p.z+size*(Random.NextInt[rs]/REAL[LAST[INT]]-0.5);
value ¬ function[found.p, clientData]-level;
IF (sign = positive AND value > 0) OR (sign = negative AND value < 0) THEN RETURN;
size ¬ size*1.005;
ENDLOOP;
found.bad ¬ TRUE;
};
rs: Random.RandomStream ¬ Random.Create[];
pos: Found ¬ Find[positive];
neg: Found ¬ Find[negative];
IF pos.bad OR neg.bad
THEN Error[Rope.Cat["can't find (", IF pos.bad THEN "+" ELSE "-", ") starting point"]]
ELSE c ¬ Converge[pos.p, neg.p, 1.0, level, function, clientData, unknown];
};
Converge: PROC [
pos, neg: Triple,
vpos, level: REAL,
function: ValueProc,
clientData: REF ANY,
direction: Face ¬ unknown]
RETURNS [p: Triple]
~ {
positive: BOOL;
nTries: NAT ¬ 0;
p ¬ pos;
IF vpos < 0 THEN {c: Triple ¬ neg; neg ¬ pos; pos ¬ c};
DO
SELECT direction FROM
n, f => p.z ¬ 0.5*(pos.z+neg.z);
b, t => p.y ¬ 0.5*(pos.y+neg.y);
l, r => p.x ¬ 0.5*(pos.x+neg.x);
ENDCASE => p ¬ G3dVector.Midpoint[pos, neg];
IF (nTries ¬ nTries+1) = 10 THEN EXIT;
positive ¬ (function[p, clientData]-level) > 0.0;
SELECT direction FROM
n, f => IF positive THEN pos.z ¬ p.z ELSE neg.z ¬ p.z;
b, t => IF positive THEN pos.y ¬ p.y ELSE neg.y ¬ p.y;
l, r => IF positive THEN pos.x ¬ p.x ELSE neg.x ¬ p.x;
ENDCASE => IF positive THEN pos ¬ p ELSE neg ¬ p;
ENDLOOP;
};
Normal: PROC [point: Triple, function: ValueProc, clientData: REF ANY, delta: REAL]
RETURNS [n: Triple]
~ {
opposite usual gradient so surface normals point outwards
v: REAL ¬ function[point, clientData];
x: REAL ¬ v-function[[point.x+delta, point.y, point.z], clientData];
y: REAL ¬ v-function[[point.x, point.y+delta, point.z], clientData];
z: REAL ¬ v-function[[point.x, point.y, point.z+delta], clientData];
n ¬ G3dVector.Unit[[x, y, z]];
};
END.
Debug and/or Old
PrintCorner: PROC [name: ROPE, c: Corner] ~ {
TerminalIO.PutF["%g=[%g,%g,%g],value=%g\n",IO.rope[name],IO.real[c.x],IO.real[c.y],IO.real[c.z],IO.real[c.value]];
};
PrintCube: PROC [lbn, lbf, ltn, ltf, rbn, rbf, rtn, rtf: Corner] ~ {
FOR l: LIST OF Corner ¬ LIST[lbn, lbf, ltn, ltf, rbn, rbf, rtn, rtf], l.rest WHILE l # NIL DO
TerminalIO.PutF["[%g,%g,%g] ", IO.real[l.first.x], IO.real[l.first.y], IO.real[l.first.z]];
ENDLOOP;
TerminalIO.PutF["\n"];
};
Find: PROC [sign: {negative, positive}] RETURNS [c: Triple] ~ {
value, stepSize: REAL ¬ 0.1;
FOR n: NAT IN [0..5) DO
FOR longitude: REAL ¬ 0.0, longitude+30.0 WHILE longitude <= 360.0 DO
FOR lattitude: REAL ¬ 0.0, lattitude+30.0 WHILE lattitude <= 360.0 DO
c ¬ G3dVector.CartesianFromPolar[longitude, lattitude, n*stepSize];
value ¬ function[c, clientData]-level;
IF sign = negative AND value <= 0.0 THEN RETURN;
IF sign = positive AND value >= 0.0 THEN RETURN;
ENDLOOP;
ENDLOOP;
ENDLOOP;
ERROR startError;
};