ImplicitSurfaceImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Bloomenthal, February 27, 1993 3:37 pm PST
DIRECTORY G3dBasic, G3dMatrix, G3dModel, G3dOctree, G3dPolygon, G3dShape, G3dVector, ImplicitAdapt, ImplicitDefs, ImplicitPoints, ImplicitPolygons, ImplicitSurface, IO, Random, Rope;
ImplicitSurfaceImpl: CEDAR PROGRAM
IMPORTS G3dMatrix, G3dModel, G3dOctree, G3dShape, G3dVector, ImplicitAdapt, ImplicitPoints, ImplicitPolygons, IO, Random, Rope
EXPORTS ImplicitSurface
~ BEGIN
NoSurfacePoint: PUBLIC ERROR = CODE;
abort:    ERROR = CODE;
Triple:   TYPE ~ G3dBasic.Triple;
Matrix:   TYPE ~ G3dMatrix.Matrix;
Viewport:   TYPE ~ G3dMatrix.Viewport;
Corner:   TYPE ~ G3dOctree.Corner;
Cube:    TYPE ~ G3dOctree.Cube;
CubeProc:  TYPE ~ G3dOctree.CubeProc;
CubeStack:  TYPE ~ G3dOctree.CubeStack;
Direction:   TYPE ~ G3dOctree.Direction;
Edge:    TYPE ~ G3dOctree.Edge;
Face:    TYPE ~ G3dOctree.Face;
Octant:   TYPE ~ G3dOctree.Octant;
Octree:   TYPE ~ G3dOctree.Octree;
OctreeMode:  TYPE ~ G3dOctree.OctreeMode;
OctreeRep:  TYPE ~ G3dOctree.OctreeRep;
TwoCorners:  TYPE ~ G3dOctree.TwoCorners;
TwoFaces:  TYPE ~ G3dOctree.TwoFaces;
PolygonProc:  TYPE ~ G3dPolygon.PolygonProc;
Shape:    TYPE ~ G3dShape.Shape;
Vertex:   TYPE ~ G3dShape.Vertex;
ColorProc:   TYPE ~ ImplicitDefs.ColorProc;
EdgeMode:  TYPE ~ ImplicitDefs.EdgeMode;
NormalProc:  TYPE ~ ImplicitDefs.NormalProc;
PolygonOkProc: TYPE ~ ImplicitDefs.PolygonOkProc;
StatusProc:  TYPE ~ ImplicitDefs.StatusProc;
SurfaceProc:  TYPE ~ ImplicitDefs.SurfaceProc;
Surface:   TYPE ~ ImplicitDefs.Surface;
Target:   TYPE ~ ImplicitDefs.Target;
TextureProc:  TYPE ~ ImplicitDefs.TextureProc;
ValueProc:  TYPE ~ ImplicitDefs.ValueProc;
VertexOkProc: TYPE ~ ImplicitDefs.VertexOkProc;
ROPE:    TYPE ~ Rope.ROPE;
Surface Creation
MakeSurface: PUBLIC PROC [
surface: Surface,
octreeMode: OctreeMode,
valueProc: ValueProc,
threshold: REAL ← 1.0,
triangulate: BOOL ¬ FALSE,
vertexOkProc: VertexOkProc ← NIL,
polygonOkProc: PolygonOkProc ← NIL,
normalProc: NormalProc ← NIL,
colorProc: ColorProc ← NIL,
textureProc: TextureProc ← NIL,
statusProc: StatusProc ← NIL,
tolerance: REAL ← 0.0001,
edgeMode: EdgeMode ← binarySectioning,
clientData: REF ANYNIL]
~ {
IF statusProc = NIL OR statusProc[$MakeOctree] # $Abort THEN {
surface.octree ← MakeOctree[octreeMode, valueProc, threshold, normalProc, statusProc, clientData];
[] ¬ MakePolygons[surface, valueProc, threshold, triangulate, vertexOkProc, polygonOkProc, normalProc, colorProc, textureProc, statusProc, tolerance, edgeMode, clientData];
};
};
MakePolygons: PUBLIC PROC [
surface: Surface,
valueProc: ValueProc,
threshold: REAL ← 1.0,
triangulate: BOOL ¬ FALSE,
vertexOkProc: VertexOkProc ← NIL,
polygonOkProc: PolygonOkProc ← NIL,
normalProc: NormalProc ← NIL,
colorProc: ColorProc ← NIL,
textureProc: TextureProc ← NIL,
statusProc: StatusProc ← NIL,
tolerance: REAL ← 0.0001,
edgeMode: EdgeMode ← binarySectioning,
clientData: REF ANYNIL]
RETURNS [nEvaluations: INT ¬ 0]
~ {
ENABLE abort => CONTINUE;
CheckStatus: PROC [type: ATOM, commatize: BOOLFALSE] ~ {
IF statusProc = NIL THEN RETURN;
IF statusProc[type] = $Abort THEN ERROR abort;
IF NOT Rope.IsEmpty[msg] AND commatize THEN msg ← Rope.Concat[", ", msg];
IF NOT Rope.IsEmpty[msg] AND statusProc[msg] = $Abort THEN ERROR abort;
};
cubeProc: CubeProc ~ {
IF statusProc # NIL AND statusProc[cube] = $Abort THEN RETURN[FALSE];
};
msg: ROPE;
IF surface = NIL OR surface.octree = NIL THEN RETURN;
CheckStatus[$SetCorners, TRUE];
nEvaluations ¬
ImplicitPoints.SetTerminalCubeCornerValuesAndCrossedPoints[
surface.octree.root, valueProc, threshold, normalProc,
cubeProc, tolerance, edgeMode, clientData];
CheckStatus[$MakeVertices];
msg ← ImplicitPoints.SetSurfaceVertices[surface, surface.octree.root, valueProc, threshold, normalProc, cubeProc, vertexOkProc, clientData];
CheckStatus[$MakePolygons];
msg ← ImplicitPolygons.SetSurfacePolygons[
surface, surface.octree.root, triangulate, valueProc, threshold, cubeProc, polygonOkProc, clientData];
CheckStatus[$MakeNormals, TRUE];
ImplicitPolygons.SetFaceNormalsCenters[surface];
msg ← "normals";
CheckStatus[$MakeTextures, TRUE];
ImplicitPoints.SetVertexTextures[surface, textureProc, cubeProc, clientData];
ImplicitPoints.SetVertexColors[surface, colorProc, cubeProc, clientData];
msg ← IF textureProc # NIL THEN "texture" ELSE NIL;
CheckStatus[$Done];
};
Octree Creation
MakeOctree: PUBLIC PROC [
octreeMode: OctreeMode,
valueProc: ValueProc,
threshold: REAL ← 1.0,
normalProc: NormalProc ← NIL,
statusProc: StatusProc ← NIL,
clientData: REF ANYNIL]
RETURNS [octree: Octree]
~ {
ENABLE abort => {IF octree # NIL THEN octree.completed ← FALSE; CONTINUE};
CheckStatus: PROC [ref: REF] ~ {
IF statusProc # NIL AND statusProc[ref] = $Abort THEN ERROR abort;
};
CheckStatus[$MakeOctree];
WITH t: octreeMode SELECT FROM
converge => octree ← ConvergeOctree[
t.rootSize, t.recurseMin, t.recurseMax, valueProc, threshold,, statusProc, clientData];
track => octree ← IF t.duringAdaptMax > 0
THEN ImplicitAdapt.Track[t.cubeSize, t.surfacePoint, valueProc, threshold, normalProc, statusProc, clientData, t.duringAdaptMax, t.flatness, t.tolerance]
ELSE TrackOctree[t.cubeSize, t.surfacePoint, valueProc, threshold, statusProc, clientData];
ENDCASE => RETURN;
TRUSTED {octree.mode ← octreeMode};
CheckStatus[IO.PutFR1["%g cubes", IO.int[G3dOctree.NTerminalCubes[octree.root]]]];
IF octreeMode.postAdaptMax > 0 THEN CheckStatus[$AdaptOctree];
IF octreeMode.postAdaptMax > 0 THEN {
rope: ROPE ← ImplicitAdapt.Subdivide[octree.root, octreeMode.postAdaptMax, octreeMode.flatness, valueProc, threshold, normalProc, statusProc, clientData];
CheckStatus[rope];
};
G3dOctree.SetOctreeFields[octree];
};
TrackOctree: PUBLIC PROC [
cubeSize: REAL,
surfacePoint: Triple,
valueProc: ValueProc,
threshold: REAL ← 1.0,
statusProc: StatusProc ← NIL,
clientData: REF ANYNIL]
RETURNS [o: Octree]
~ {
ENABLE abort => {IF o # NIL THEN o.completed ← FALSE; CONTINUE};
ActiveFaces: TYPE ~ ARRAY Face OF BOOLALL[FALSE];
Reject: CubeProc ~ {IF cube.refAny = $Rejected THEN cube.parent.kids[cube.octant] ← NIL};
CheckStatus: PROC [cube: Cube] ~ {
IF statusProc # NIL THEN SELECT statusProc[cube] FROM
$Abort  => ERROR abort;
$RejectCube => {cube.refAny ← $Rejected; o.nCubes ← o.nCubes-1; rejected ← TRUE};
ENDCASE;
};
GetActiveFaces: PROC [cube: Cube] RETURNS [activeFaces: ActiveFaces] ~ {
ImplicitPoints.SetCornerValues[cube, valueProc, threshold, clientData];
FOR e: Edge IN Edge DO
corners: TwoCorners ← G3dOctree.EdgeCorners[cube, e];
IF corners.c0.inside # corners.c1.inside AND
NOT corners.c0.outOfRange AND
NOT corners.c1.outOfRange THEN {
twoFaces: TwoFaces ~ G3dOctree.EdgeFaces[e];
activeFaces[twoFaces.f0] ← activeFaces[twoFaces.f1] ← TRUE;
};
ENDLOOP;
};
ProcessTopOfStack: PROC [cubeStack: CubeStack] ~ {
new: Cube;
cube: Cube ← G3dOctree.ReadTopOfCubeStack[cubeStack];
IF cube.refAny # $Rejected THEN {
activeFaces: ActiveFaces ~ GetActiveFaces[cube];
FOR ff: Face IN Face DO
IF activeFaces[ff] AND G3dOctree.FaceNeighbor[cube, ff] = NIL THEN {
o.root ← G3dOctree.AddCube[cube, ff, FALSE];
o.nCubes ← o.nCubes+1;
IF NOT (new ← G3dOctree.FaceNeighbor[cube, ff]).terminal THEN ERROR;
G3dOctree.WriteBottomOfCubeStack[new, cubeStack];
CheckStatus[new];
};
ENDLOOP;
};
};
rejected: BOOLFALSE;
cubeStack: CubeStack ← G3dOctree.NewCubeStack[20000];
o ← NEW[OctreeRep ← [mode: [0, 0.0, 0.001, track[cubeSize, surfacePoint, 0]]]];
o.root ← G3dOctree.NewCube[cubeSize, surfacePoint];
G3dOctree.WriteBottomOfCubeStack[o.root, cubeStack];
CheckStatus[o.root];
WHILE NOT G3dOctree.CubeStackEmpty[cubeStack] DO
ProcessTopOfStack[cubeStack];
ENDLOOP;
IF rejected AND o.nCubes > 1 THEN G3dOctree.ApplyToTerminal[o.root, Reject];
o.completed ← TRUE;
};
ConvergeOctree: PUBLIC PROC [
rootSize: REAL,
recurseMin: NAT,
recurseMax: NAT,
valueProc: ValueProc,
threshold: REAL ← 1.0,
surfaceProc: SurfaceProc ← NIL,
statusProc: StatusProc ← NIL,
clientData: REF ANYNIL]
RETURNS [o: Octree]
~ {
ENABLE abort => {o.completed ← FALSE; CONTINUE};
localSurfaceProc: SurfaceProc ~ IF surfaceProc # NIL THEN surfaceProc ELSE DefaultSurface;
DefaultSurface: SurfaceProc ~ {
inside, once: BOOLFALSE;
IF cube = NIL THEN RETURN[FALSE];
FOR o: Octant IN Octant DO
c: Corner ~ cube.corners[o];
IF NOT c.valueSet
THEN ImplicitPoints.SetCornerValue[c, valueProc[c.point, clientData, c]-threshold];
IF c.nearestSet AND G3dOctree.PointInCube[c.nearest, cube] THEN RETURN[TRUE];
IF c.outOfRange THEN LOOP;
IF NOT once
THEN {inside ← c.inside; once ← TRUE}
ELSE IF c.inside # inside THEN RETURN[TRUE];  -- surface crosses cube
ENDLOOP;
RETURN[FALSE];
};
Inner: PROC [cube: Cube] ~ {
IF cube.level < recurseMax THEN {
G3dOctree.Subdivide[cube];
FOR o: Octant IN Octant DO
kid: Cube ~ cube.kids[o];
IF kid.level = 1 AND statusProc # NIL AND statusProc[kid]=$Abort THEN ERROR abort;
IF cube.level <= recurseMin OR localSurfaceProc[kid]
THEN Inner[kid]
ELSE cube.kids[o] ← NIL;
ENDLOOP;
}
ELSE cube.terminal ← TRUE;
};
o ← NEW[OctreeRep ← [mode: [0, 0.0, 0.001, converge[rootSize, recurseMin, recurseMax]]]];
Inner[o.root ← G3dOctree.NewCube[rootSize]];
o.completed ← TRUE;
Diverge[root]; -- suspect
};
Surface Computation
PointOnSurface: PUBLIC PROC [
surfaceHull: Cube,
valueProc: ValueProc,
threshold: REAL ← 1.0,
recurseLimit: NAT,
clientData: REF ANYNIL]
RETURNS [point: Triple]
~ {
ContainsSurface: PROC [cube: Cube] RETURNS [BOOL] ~ {
IF ImplicitPoints.IsCrossed[cube] THEN RETURN[TRUE];
FOR o: Octant IN Octant DO
c: Corner ~ cube.corners[o];
IF c.nearestSet AND G3dOctree.PointInCube[c.nearest, cube] THEN {
point ← c.nearest;
RETURN[TRUE];
};
ENDLOOP;
RETURN[FALSE];
};
ConvergeOnCrossedEdge: PROC [cube: Cube] RETURNS [point: Triple] ~ {
twoCorners: TwoCorners ← ImplicitPoints.CrossedEdgeCorners[cube];
in: Corner ← twoCorners.c0;
out: Corner ← twoCorners.c1;
IF in.value < 0.0 THEN {c: Corner ← in; in ← out; out ← c};
point ← SegmentConverge[in.point, out.point, in.value, out.value, valueProc, threshold, clientData, none, 0.00001,, 50].point;
};
Find: PROC [cube: Cube] RETURNS [found: BOOLFALSE] ~ {
DivideAndTestKids: CubeProc ~ {
G3dOctree.Subdivide[cube];
FOR o: Octant IN Octant DO
kid: Cube ~ cube.kids[o];
ImplicitPoints.SetCornerValues[kid, valueProc, threshold, clientData];
IF NOT ImplicitPoints.IsCrossed[kid] THEN LOOP;
point ← ConvergeOnCrossedEdge[kid];
found ← TRUE;
RETURN[FALSE];
ENDLOOP;
};
FOR level: NAT IN [0..recurseLimit) DO
G3dOctree.ApplyToLevel[cube, DivideAndTestKids, level];
IF found THEN RETURN;
ENDLOOP;
};
IF surfaceHull = NIL OR NOT Find[surfaceHull] THEN ERROR NoSurfacePoint ;
};
SurfacePoint: PUBLIC PROC [
in, out: Triple,
valueProc: ValueProc,
threshold: REAL ← 1.0,
clientData: REF ANYNIL]
RETURNS [p: Triple]
~ {
vIn: REAL ← valueProc[in, clientData]-threshold;
vOut: REAL ← valueProc[out, clientData]-threshold;
p ← IF vIn*vOut > 0.0
THEN FindStart[G3dVector.Midpoint[in, out], valueProc, 0.001, threshold]
ELSE SegmentConverge[in, out, vIn, vOut, valueProc, threshold, clientData].point;
};
FindStart: PUBLIC PROC [
ballpark: Triple,
valueProc: ValueProc,
size, level: REAL,
clientData: REF ANY ¬ NIL]
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 ¬ valueProc[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 NoSurfacePoint
ELSE c ¬ SegmentConverge[pos.p, neg.p, valueProc[pos.p, clientData], valueProc[neg.p, clientData], valueProc, level, clientData].point;
};
SegmentConverge: PUBLIC PROC [
pIn, pOut: Triple,
vIn, vOut: REAL,
valueProc: ValueProc,
threshold: REAL ← 1.0,
clientData: REF ANYNIL,
direction: Direction ← none,
tolerance: REAL ← 0.0001,
edgeMode: EdgeMode ← binarySectioning,
nTriesLimit: NAT ← 15]
RETURNS [t: Target ¬ [[], 0.0, 8]]
~ {
IF vIn < 0.0 THEN {temp: Triple ¬ pIn; pIn ¬ pOut; pOut ¬ temp};
THROUGH [0..8) DO
p: Triple ← SELECT direction FROM
n, f => [pIn.x, pIn.y, 0.5*(pIn.z+pOut.z)],
b, t => [pIn.x, 0.5*(pIn.y+pOut.y), pIn.z],
l, r => [0.5*(pIn.x+pOut.x), pIn.y, pIn.z],
ENDCASE => G3dVector.Midpoint[pIn, pOut];
IF (t.value ¬ valueProc[p, clientData]-threshold) > 0.0 THEN pIn ¬ p ELSE pOut ¬ p;
ENDLOOP;
t.point ¬ G3dVector.Midpoint[pIn, pOut];
};
SegmentConverge: PUBLIC PROC [
pIn, pOut: Triple,
vIn, vOut: REAL,
valueProc: ValueProc,
threshold: REAL ← 1.0,
clientData: REF ANYNIL,
direction: Direction ← none,
tolerance: REAL ← 0.0001,
edgeMode: EdgeMode ← binarySectioning,
nTriesLimit: NAT ← 15]
RETURNS [Target]
~ {
Inner: PROC [pIn, pOut: Triple, vIn, vOut: REAL] RETURNS [Target] ~ {
point: Triple;
a, d, dAbs, v: REAL ← 0.5;
IF edgeMode = regulaFalsi THEN {
dV: REAL ~ vIn-vOut;
IF dV # 0.0 THEN a ← vIn/dV;
};
d SELECT direction FROM
n, f => pOut.z-pIn.z,
b, t => pOut.y-pIn.y,
l, r => pOut.x-pIn.x,
ENDCASE => G3dVector.Distance[pIn, pOut];
dAbs ABS[d];
point ← SELECT direction FROM
n, f => [pIn.x, pIn.y, pIn.z+a*d],
b, t => [pIn.x, pIn.y+a*d, pIn.z],
l, r => [pIn.x+a*d, pIn.y, pIn.z],
ENDCASE => IF edgeMode = regulaFalsi
THEN G3dVector.Interp[a, pIn, pOut]
ELSE G3dVector.Midpoint[pIn, pOut];
v valueProc[point, clientData]-threshold;
IF nTries = 0 THEN epsilon ← dAbs*tolerance;
nTries ← nTries+1;
nTriesLimit shouldn't be necessary for well-behaved, exactly evaluated functions, but . . .
IF dAbs < epsilon THEN RETURN[[point, v, nTries]];
IF edgeMode = regulaFalsi AND nTries > nRegulaFalsiTriesLimit THEN {
nTries ← nTriesLimit/3;
edgeMode ← binarySectioning;
};
IF nTries > nTriesLimit THEN RETURN[[point, v, nTries]];
RETURN[IF v < 0.0
THEN Inner[pIn, point, vIn, v]
ELSE Inner[point, pOut, v, vOut]];
};
epsilon: REAL;
nTries: NAT ← 0;
nRegulaFalsiTriesLimit: NAT ← nTriesLimit/2;
IF direction = none THEN direction ← G3dOctree.DirectionFromPoints[pIn, pOut];
IF valueProc = NIL
THEN RETURN[[G3dVector.Midpoint[pIn, pOut], 0]]
ELSE RETURN[Inner[pIn, pOut, vIn, vOut]];
};
Diverge: PROC [root: Cube] ~ {
Add cubes to root which were originally missed but face cubes cut by the surface.
cubeProc: CubeProc ~ {
FOR e: Edge IN Edge DO
corners: TwoCorners ← G3dOctree.EdgeCorners[cube, e];
IF corners.c0.inside # corners.c1.inside THEN {
c0, c1: Cube;
faces: TwoFaces ~ G3dOctree.EdgeFaces[e];
IF (c0 ← G3dOctree.FaceNeighbor[cube, faces.f0]) = NIL
THEN c0 ← G3dOctree.AddCube[cube, faces.f0, --FALSE?--];
IF (c1 ← G3dOctree.FaceNeighbor[cube, faces.f1]) = NIL
THEN c1 ← G3dOctree.AddCube[cube, faces.f1, --FALSE?--];
IF G3dOctree.FaceNeighbor[c0, faces.f1] = NIL
THEN [] ← G3dOctree.AddCube[c0, faces.f1, --FALSE?--];
};
ENDLOOP;
};
G3dOctree.ApplyToTerminal[root, cubeProc];
};
Conversions
ShapeFromSurface: PUBLIC PROC [surface: Surface] RETURNS [shape: Shape] ~ {
IF surface # NIL AND surface.vertices # NIL AND surface.polygons # NIL THEN {
shape ← NEW[G3dShape.ShapeRep];
shape.matrix ← G3dMatrix.Identity[];
shape.type ← $ConvexPolygon;
shape.surfaces ← ImplicitPolygons.DecodePolygons[surface];
shape.vertices ← NEW[G3dShape.VertexSequenceRep[surface.vertices.length]];
shape.vertices.length ← surface.vertices.length;
shape.vertices.valid[normal] ← surface.vertexValidities[normal];
shape.vertices.valid[color] ← surface.vertexValidities[color];
shape.vertices.valid[texture] ← surface.vertexValidities[texture];
FOR n: NAT IN [0..surface.vertices.length) DO
v: Vertex ← shape.vertices[n] ← NEW[G3dShape.VertexRep];
surfaceVertex: Vertex ← surface.vertices[n];
v.point ← surfaceVertex.point;
IF surface.vertexValidities[normal] THEN v.normal ← surfaceVertex.normal;
IF surface.vertexValidities[texture] THEN v.texture ← surfaceVertex.texture;
IF surface.vertexValidities[color] THEN v.color ← surfaceVertex.color;
ENDLOOP;
shape.faces ← NEW[G3dShape.FaceSequenceRep[shape.surfaces.length]];
shape.faces.length ← shape.surfaces.length;
FOR n: NAT IN [0..shape.surfaces.length) DO
f: G3dShape.Face ← shape.faces[n] ← NEW[G3dShape.FaceRep];
f.normal ← surface.faceNormals[n];
f.center ← surface.faceCenters[n];
ENDLOOP;
shape.edges ← G3dShape.MakeEdges[shape];
G3dModel.SetCurves[shape];
};
};
SetSurfaceCurves: PUBLIC PROC [surface: Surface] ~ {
shape: Shape ← ShapeFromSurface[surface];
shape.edges ← G3dShape.MakeEdges[shape];
surface.curves ← G3dModel.MakeCurves[
ImplicitPoints.DecodePoints[surface],
ImplicitPoints.DecodeNormals[surface],
shape.edges];
surface.curvesValid ← TRUE;
};
Attributes
NPolygons: PUBLIC PROC [surface: Surface] RETURNS [CARDINAL] ~ {
RETURN[IF surface = NIL THEN 0 ELSE surface.nPolygons];
};
NTriangles: PROC [surface: Surface] RETURNS [nTriangles: CARDINAL ← 0] ~ {
Note: We should make this a PUBLIC proc sometime.
EachPoly: PolygonProc ~ {nTriangles ← nTriangles+polygon.length-2};
ImplicitPolygons.ApplyToSurfacePolygons[surface, EachPoly];
};
NEdges: PUBLIC PROC [surface: Surface] RETURNS [nEdges: CARDINAL ← 0] ~ {
CountEdges: PolygonProc ~ {nEdges ← polygon.length};
ImplicitPolygons.ApplyToSurfacePolygons[surface, CountEdges];
};
NVertices: PUBLIC PROC [surface: Surface] RETURNS [CARDINAL] ~ {
RETURN[IF surface = NIL OR surface.vertices = NIL THEN 0 ELSE surface.vertices.length];
};
EulerNumber: PUBLIC PROC [surface: Surface] RETURNS [CARDINAL] ~ {
RETURN[NPolygons[surface]-NEdges[surface]+NVertices[surface]];
};
Miscellany
SurfaceOK: PUBLIC PROC [surface: Surface] RETURNS [ok: BOOL] ~ {
ok ← surface # NIL AND surface.vertices # NIL AND surface.polygons # NIL;
};
VerticesOK: PUBLIC PROC [surface: Surface, view: Matrix, viewport: Viewport]
RETURNS [ok: BOOL]
~ {
IF (ok ← SurfaceOK[surface])
THEN ImplicitPoints.SetVertexScreenCoords[surface, view, viewport];
};
VertexNormalsOK: PUBLIC PROC [surface: Surface] RETURNS [ok: BOOL] ~ {
ok ← SurfaceOK[surface];
IF ok AND NOT surface.vertexValidities[normal] THEN ImplicitPoints.SetVertexNormals[surface];
};
FaceNormalsCentersOK: PUBLIC PROC [surface: Surface] RETURNS [ok: BOOL] ~ {
ok ← SurfaceOK[surface];
IF ok AND (surface.faceNormals = NIL OR surface.faceNormals.length = 0)
OR (surface.faceCenters = NIL OR surface.faceCenters.length = 0)
THEN ImplicitPolygons.SetFaceNormalsCenters[surface];
};
SetNearest: PUBLIC PROC [corner: Corner, nearest: Triple, squareDistance: REAL ← 0.0] ~ {
IF corner = NIL THEN RETURN;
corner.nearest ← nearest;
corner.nearestSet ← TRUE;
corner.squareDistance ← squareDistance;
};
CornerWithMaxValue: PUBLIC PROC [cube: Cube] RETURNS [corner: Corner] ~ {
max: REAL ← -100000.0;
FOR o: Octant IN Octant DO
c: Corner ~ cube.corners[o];
IF c.value > max THEN {corner ← c; max ← c.value};
ENDLOOP;
};
END.