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];
};