DIRECTORY G2dBasic, G3dBasic, G3dOctree, G3dPolygon, G3dMatrix, G3dVector, ImplicitDefs, ImplicitPoints, ImplicitPolygons, ImplicitSurface, MessageWindow, IO, Real, Rope, RuntimeError; ImplicitPolygonsImpl: CEDAR PROGRAM IMPORTS G2dBasic, G3dBasic, G3dOctree, G3dPolygon, G3dMatrix, G3dVector, ImplicitPoints, ImplicitSurface, IO, MessageWindow, Real, Rope, RuntimeError EXPORTS ImplicitPolygons ~ BEGIN Triple: TYPE ~ G3dBasic.Triple; NatSequence: TYPE ~ G3dBasic.NatSequence; NatSequenceRep: TYPE ~ G3dBasic.NatSequenceRep; SurfaceSequence: TYPE ~ G3dBasic.SurfaceSequence; SurfaceSequenceRep: TYPE ~ G3dBasic.SurfaceSequenceRep; TripleSequence: TYPE ~ G3dBasic.TripleSequence; TripleSequenceRep: TYPE ~ G3dBasic.TripleSequenceRep; Matrix: TYPE ~ G3dMatrix.Matrix; Viewport: TYPE ~ G3dMatrix.Viewport; Corner: TYPE ~ G3dOctree.Corner; Corners: TYPE ~ G3dOctree.Corners; Cross: TYPE ~ G3dOctree.Cross; CrossedEdge: TYPE ~ G3dOctree.CrossedEdge; CrossedEdges: TYPE ~ G3dOctree.CrossedEdges; CrossPolygonProc: TYPE ~ G3dOctree.CrossPolygonProc; CrossSequence: TYPE ~ G3dOctree.CrossSequence; CrossSequenceRep: TYPE ~ G3dOctree.CrossSequenceRep; Cube: TYPE ~ G3dOctree.Cube; CubeProc: TYPE ~ G3dOctree.CubeProc; Direction: TYPE ~ G3dOctree.Direction; Edge: TYPE ~ G3dOctree.Edge; Face: TYPE ~ G3dOctree.Face; Octant: TYPE ~ G3dOctree.Octant; ThreeEdges: TYPE ~ G3dOctree.ThreeEdges; TwoOctants: TYPE ~ G3dOctree.TwoOctants; PolygonProc: TYPE ~ G3dPolygon.PolygonProc; PolygonOkProc: TYPE ~ ImplicitDefs.PolygonOkProc; Surface: TYPE ~ ImplicitDefs.Surface; ValueProc: TYPE ~ ImplicitDefs.ValueProc; ROPE: TYPE ~ Rope.ROPE; maxNTriples: CARDINAL ~ ImplicitDefs.maxNTriples; maxNNats: CARDINAL ~ ImplicitDefs.maxNNats; BoundsFault: ERROR ~ RuntimeError.BoundsFault; CrossPolygonizeCube: PUBLIC PROC [ cube: Cube, crossPolygonProc: CrossPolygonProc, scratchCrossSequence: CrossSequence ¬ NIL, recurseTrackFace: BOOL ¬ TRUE] RETURNS [result: ATOM ¬ $Normal] ~ { IF ImplicitPoints.IsCrossed[cube] AND NOT ImplicitPoints.IsOutOfRange[cube] THEN { AddCrossToPolygon: PROC [crossedEdge: CrossedEdge] ~ { cross: Cross ¬ ImplicitPoints.CrossFromCrossedEdge[crossedEdge]; IF NOT cross.ok THEN nBadVertices ¬ nBadVertices+1; IF scratchCrossSequence.length = scratchCrossSequence.maxLength THEN { old: CrossSequence ¬ scratchCrossSequence; scratchCrossSequence ¬ NEW[CrossSequenceRep[2*scratchCrossSequence.length]]; FOR i: INT IN [0..old.length) DO scratchCrossSequence[i] ¬ old[i]; ENDLOOP; scratchCrossSequence.length ¬ old.length; }; scratchCrossSequence[scratchCrossSequence.length] ¬ cross; scratchCrossSequence.length ¬ scratchCrossSequence.length+1; }; RecursiveTrackFace: PROC [cube: Cube, face: Face, edge: Edge] RETURNS [Edge] ~ { dFace: Direction ~ G3dOctree.DirectionFromFace[face]; dFaceOpp: Direction ~ G3dOctree.OppositeDirection[dFace]; corners: Corners ~ cube.corners; octs: TwoOctants ~ G3dOctree.EdgeOctants[edge]; mid: Corner ~ cube.kids[octs.o0].corners[octs.o1]; octant: Octant ¬ IF mid.inside = corners[octs.o0].inside THEN octs.o1 ELSE IF mid.inside = corners[octs.o1].inside THEN octs.o0 ELSE IF mid.inside THEN octs.o1 ELSE octs.o0; -- edge anomaly DO kid: Cube ¬ cube.kids[octant]; edges: ThreeEdges ~ G3dOctree.EdgesFromOctant[octant]; crossedEdges: CrossedEdges ~ ImplicitPoints.CubeCrossedEdges[kid]; IF NOT kid.terminal THEN edge ¬ RecursiveTrackFace[kid, face, edge] ELSE { start: Edge ¬ edge; DO edge ¬ G3dOctree.NextCCWEdge[edge, face]; -- CCW tracking! IF edge = start THEN ERROR; IF crossedEdges[edge].cIn # NIL THEN EXIT; ENDLOOP; AddCrossToPolygon[crossedEdges[edge]]; }; IF edge = edges.e0 OR edge = edges.e1 OR edge = edges.e2 THEN RETURN[edge] ELSE { dEdge: Direction ¬ G3dOctree.DirectionFromEdge[edge]; dOctant: Direction ¬ G3dOctree.DirectionFromOctant[octant]; d: Direction ¬ G3dOctree.AddDirection[dEdge, dFaceOpp]; dOctant ¬ G3dOctree.AddDirection[dOctant, d]; dOctant ¬ G3dOctree.AddDirection[dOctant, d]; octant ¬ G3dOctree.OctantFromDirection[dOctant]; edge ¬ G3dOctree.OppositeEdge[edge, face]; } ENDLOOP; }; NextCrossedEdge: PROC [edge: Edge] RETURNS [Edge] ~ { face: Face ~ G3dOctree.FaceFromEdgeOctant[edge, crossedEdges[edge].oOut]; neighbor: Cube ~ G3dOctree.FaceNeighbor[cube, face]; IF recurseTrackFace AND neighbor # NIL AND NOT neighbor.terminal THEN { oppFace: Face ~ G3dOctree.OppositeFace[face]; invEdge: Edge ~ G3dOctree.InverseEdge[edge, face]; IF result = $Normal THEN result ¬ $RecurseTracked; edge ¬ G3dOctree.InverseEdge[ RecursiveTrackFace[neighbor, oppFace, invEdge], oppFace]; } ELSE { start: Edge ¬ edge; DO edge ¬ G3dOctree.NextCWEdge[edge, face]; IF crossedEdges[edge].cIn # NIL THEN EXIT; IF edge = start THEN ERROR RecurseError; ENDLOOP; AddCrossToPolygon[crossedEdges[edge]]; }; RETURN[edge]; }; Inner: PROC RETURNS [BOOL] ~ { done: ARRAY Edge OF BOOL ¬ ALL[FALSE]; nBadVertices ¬ 0; FOR edge: Edge IN Edge DO IF crossedEdges[edge].cIn # NIL AND NOT done[edge] THEN { e, start: Edge ¬ edge; scratchCrossSequence.length ¬ 0; DO e ¬ NextCrossedEdge[e ! RecurseError => GOTO recurseError]; done[e] ¬ TRUE; IF e = start THEN EXIT; ENDLOOP; IF nBadVertices = scratchCrossSequence.length THEN RETURN[TRUE]; IF NOT crossPolygonProc[nPolygon, scratchCrossSequence] THEN RETURN[TRUE]; nPolygon ¬ nPolygon+1; }; ENDLOOP; RETURN[TRUE]; EXITS recurseError => RETURN[FALSE]; }; RecurseError: ERROR = CODE; nBadVertices, nPolygon: INT ¬ 0; crossedEdges: CrossedEdges ~ ImplicitPoints.CubeCrossedEdges[cube]; IF scratchCrossSequence = NIL OR scratchCrossSequence.maxLength < 100 THEN scratchCrossSequence ¬ NEW[CrossSequenceRep[100]]; IF NOT Inner[] THEN { recurseTrackFace ¬ FALSE; result ¬ $RecurseFailed; [] ¬ Inner[]; }; }; }; PolygonizeCube: PUBLIC PROC [ cube: Cube, polygonProc: PolygonProc, scratchPolygon: NatSequence ¬ NIL, recurseTrackFace: BOOL ¬ TRUE] RETURNS [result: ATOM ¬ $Normal] ~ { crossPolygonProc: CrossPolygonProc ~ { FOR n: INT IN [0..polygon.length) DO scratchPolygon[n] ¬ polygon[n].id; ENDLOOP; scratchPolygon.length ¬ polygon.length; [] ¬ polygonProc[nPolygon, scratchPolygon]; }; crossSequence: CrossSequence ¬ G3dOctree.ObtainCrossSequence[]; IF scratchPolygon = NIL OR scratchPolygon.maxLength < 100 THEN scratchPolygon ¬ NEW[NatSequenceRep[100]]; result ¬ CrossPolygonizeCube[cube, crossPolygonProc, crossSequence, recurseTrackFace]; G3dOctree.ReleaseCrossSequence[crossSequence]; }; SetSurfacePolygons: PUBLIC PROC [ surface: Surface, root: Cube, triangulate: BOOL ¬ FALSE, valueProc: ValueProc ¬ NIL, threshold: REAL ¬ 0.0, cubeProc: CubeProc ¬ NIL, polygonOkProc: PolygonOkProc ¬ NIL, clientData: REF ANY ¬ NIL] RETURNS [message: ROPE] ~ { TriangulateAbort: ERROR = CODE; TerminalCubeProc: CubeProc ~ { AddPolygonToSurface: PROC [polygon: NatSequence] ~ { CheckLength: PROC [n: INT] ~ { IF CARD[polys.length+n+1] >= polys.maxLength THEN polys ¬ G2dBasic.LengthenNatSequence[polys ! RuntimeError.BoundsFault, Real.RealException => { AddPolysToEndOfSurfaceList[polys]; -- first, store full sequence polys ¬ NEW[NatSequenceRep[1000]]; -- now, start new sequence CONTINUE; }]; }; AddPolygon: PROC [polygon: NatSequence] ~ { surface.nPolygons ¬ surface.nPolygons+1; CheckLength[polygon.length]; polys ¬ G2dBasic.AddToNatSequence[polys, polygon.length]; FOR n: INT IN [0..polygon.length) DO polys ¬ G2dBasic.AddToNatSequence[polys, polygon[n]]; ENDLOOP; }; AddTriangle: PROC [i1, i2, i3: INT] ~ { surface.nPolygons ¬ surface.nPolygons+1; CheckLength[3]; polys ¬ G2dBasic.AddToNatSequence[polys, 3]; polys ¬ G2dBasic.AddToNatSequence[polys, i1]; polys ¬ G2dBasic.AddToNatSequence[polys, i2]; polys ¬ G2dBasic.AddToNatSequence[polys, i3]; }; IF polygon.length > 3 AND triangulate AND valueProc # NIL THEN { v1: REAL; center, vector, point: Triple ¬ []; FOR i: INT IN [0..polygon.length) DO center ¬ G3dVector.Add[center, surface.vertices[polygon[i]].point]; ENDLOOP; point ¬ center ¬ G3dVector.Div[center, REAL[polygon.length]]; v1 ¬ valueProc[center, clientData]; vector ¬ ImplicitPoints.GetPointNormal[center, valueProc, v1, delta, clientData]; vector ¬ G3dVector.Mul[vector, delta]; IF (v1 ¬ valueProc[center, clientData]) < 0 THEN vector ¬ G3dVector.Negate[vector]; FOR i: INT IN [0..100) DO v2: REAL ¬ valueProc[point ¬ G3dVector.Add[point, vector], clientData]; IF (v2 > 0.0) # (v1 > 0.0) THEN { p: Triple ¬ ImplicitSurface.SegmentConverge[center, point, v1, v2, valueProc, threshold, clientData].point; n: Triple ¬ ImplicitPoints.GetPointNormal[ p, valueProc, valueProc[p, clientData], delta, clientData]; vid: INT ¬ ImplicitPoints.AddSurfaceVertex[p, n, surface]; FOR j: INT IN [0..polygon.length) DO AddTriangle[vid, polygon[j], polygon[(j+1) MOD polygon.length]]; ENDLOOP; EXIT; }; REPEAT FINISHED => { MessageWindow.Append["can't triangulate "]; AddPolygon[polygon]; }; ENDLOOP; } ELSE AddPolygon[polygon]; }; StorePolygon: PolygonProc ~ { IF polygonOkProc # NIL THEN { scratchPoints.length ¬ scratchPolygon.length; FOR i: INT IN [0..scratchPolygon.length) DO scratchPoints[i] ¬ surface.vertices[scratchPolygon[i]].point; ENDLOOP; IF NOT polygonOkProc[scratchPoints, clientData] THEN RETURN; }; SELECT nPolygon FROM 0 => { length0 ¬ polygon.length; AddPolygonToSurface[polygon]; cube.refAny ¬ polygon; IF cubeProc # NIL AND NOT cubeProc[cube] THEN ERROR TriangulateAbort; }; ENDCASE; }; length0: INT; delta: REAL ¬ IF triangulate AND valueProc # NIL THEN 0.01*cube.size ELSE 0.0; SELECT PolygonizeCube[cube, StorePolygon, scratchPolygon] FROM $RecurseTracked => nRecursiveTrackFaces ¬ nRecursiveTrackFaces+1; $RecurseFailed => nRecurseFailures ¬ nRecurseFailures+1; ENDCASE; }; AddPolysToEndOfSurfaceList: PROC [polys: NatSequence] ~ { IF surface.polygons = NIL THEN surface.polygons ¬ LIST[polys] ELSE FOR l: LIST OF NatSequence ¬ surface.polygons, l.rest DO IF l.rest = NIL THEN {l.rest ¬ LIST[polys]; EXIT}; ENDLOOP; }; nRecursiveTrackFaces, nRecurseFailures: INT ¬ 0; scratchPolygon: NatSequence ¬ NEW[NatSequenceRep[24]]; scratchPoints: TripleSequence ¬ NEW[TripleSequenceRep[24]]; polys: NatSequence ¬ NEW[NatSequenceRep[MIN[maxNNats, MAX[1, surface.vertices.length]]]]; G3dOctree.ApplyToTerminal[root, TerminalCubeProc ! TriangulateAbort => {message ¬ Rope.Concat[message, " *aborted, "]; CONTINUE}; ]; AddPolysToEndOfSurfaceList[polys]; message ¬ IO.PutFR["%g%g polygons", IO.rope[message], IO.int[surface.nPolygons]]; }; ApplyToSurfacePolygons: PUBLIC PROC [surface: Surface, polygonProc: PolygonProc] ~ { IF surface # NIL AND surface.polygons # NIL THEN { nPolygon: INT ¬ 0; polygon: NatSequence ¬ NEW[NatSequenceRep[100]]; FOR l: LIST OF NatSequence ¬ surface.polygons, l.rest WHILE l # NIL DO index: CARDINAL ¬ 0; polygons: NatSequence ~ l.first; WHILE index < polygons.length DO polygon.length ¬ polygons[index]; index ¬ index+1; FOR n: INT IN [0..polygon.length) DO polygon[n] ¬ polygons[index]; index ¬ index+1; ENDLOOP; IF NOT polygonProc[nPolygon, polygon] THEN RETURN; nPolygon ¬ nPolygon+1; ENDLOOP; ENDLOOP; }; }; ApplyToFrontFacingPolygons: PUBLIC PROC [ surface: Surface, view: Matrix, viewport: Viewport, polygonProc: PolygonProc] ~ { abort: ERROR = CODE; Inner: PolygonProc ~ { do: BOOL ¬ SELECT TRUE FROM nPolygon >= surface.faceNormals.length => TRUE, persp => NOT G3dVector.FrontFacingWithPerspective[ surface.faceNormals[nPolygon], surface.faceCenters[nPolygon], inverse], ENDCASE => NOT G3dVector.FrontFacingNoPerspective[surface.faceNormals[nPolygon], view]; IF do AND NOT polygonProc[nPolygon, polygon] THEN ERROR abort; }; inverse: Matrix; persp: BOOL ¬ G3dMatrix.HasPerspective[view]; IF persp THEN inverse ¬ G3dMatrix.Invert[view, G3dMatrix.ObtainMatrix[]]; IF ImplicitSurface.VerticesOK[surface, view, viewport] AND ImplicitSurface.FaceNormalsCentersOK[surface] THEN ApplyToSurfacePolygons[surface, Inner ! abort => CONTINUE]; G3dMatrix.ReleaseMatrix[inverse]; }; SetFaceNormalsCenters: PUBLIC PROC [surface: Surface, setNormals, setCenters: BOOL ¬ TRUE] ~ { IF surface # NIL AND surface.polygons # NIL AND (setNormals OR setCenters) THEN { points: TripleSequence ~ NEW[TripleSequenceRep[24]]; normals: TripleSequence ¬ surface.faceNormals; centers: TripleSequence ¬ surface.faceCenters; polygonProc: PolygonProc ~ { points.length ¬ polygon.length; FOR n: INT IN [0..points.length) DO points[n] ¬ surface.vertices[polygon[n]].point; ENDLOOP; IF setNormals THEN normals ¬ G3dBasic.AddToTripleSequence[normals, G3dPolygon.PolygonNormal[points]]; IF setCenters THEN centers ¬ G3dBasic.AddToTripleSequence[centers, G3dPolygon.PolygonCenter[points]]; }; IF setNormals AND (normals = NIL OR normals.maxLength < surface.nPolygons) THEN normals ¬ NEW[TripleSequenceRep[MIN[maxNTriples, surface.nPolygons]]]; IF setCenters AND (centers = NIL OR centers.maxLength < surface.nPolygons) THEN centers ¬ NEW[TripleSequenceRep[MIN[maxNTriples, surface.nPolygons]]]; ApplyToSurfacePolygons[surface, polygonProc ! BoundsFault => CONTINUE]; surface.faceNormals ¬ normals; surface.faceCenters ¬ centers; }; }; DecodePolygons: PUBLIC PROC [surface: Surface] RETURNS [polygons: SurfaceSequence] ~ { IF surface # NIL AND surface.polygons # NIL THEN { Decoder: PolygonProc ~ { poly: NatSequence ¬ NEW[NatSequenceRep[polygon.length]]; polygons[nPolygon] ¬ [NIL, poly]; poly.length ¬ polygon.length; FOR n: INT IN [0..polygon.length) DO poly[n] ¬ polygon[n]; ENDLOOP; nPolygon ¬ nPolygon+1; }; nPolygon: INT ¬ 0; polygons ¬ NEW[SurfaceSequenceRep[surface.nPolygons]]; polygons.length ¬ surface.nPolygons; ApplyToSurfacePolygons[surface, Decoder]; }; }; END. .. maxTripoints: CARDINAL = (LAST[CARDINAL]-4-SIZE[TripointSequenceRep[0]])/SIZE[Tripoint]; nTripoints: CARDINAL ¬ MIN[maxTripoints, MAX[1, 2*nPoints-4]]; FOR n: INT IN [1..poly.length-2] DO surface.tripoints ¬ AddTripoint[surface.tripoints, [poly[n+1], poly[n], poly[0]]]; ENDLOOP; SetFaceNormals: PUBLIC PROC [surface: Surface] ~ { IF surface # NIL AND surface.tripoints # NIL AND surface.vertices # NIL THEN { num: INT ¬ surface.tripoints.length; surface.faceNx ¬ NEW[RealSequenceRep[num]]; surface.faceNy ¬ NEW[RealSequenceRep[num]]; surface.faceNz ¬ NEW[RealSequenceRep[num]]; surface.faceNx.length ¬ surface.faceNy.length ¬ surface.faceNz.length ¬ num; FOR n: INT IN [0..num) DO p0: Triple ~ surface.vertices[surface.tripoints[n].i0].point; p1: Triple ~ surface.vertices[surface.tripoints[n].i1].point; p2: Triple ~ surface.vertices[surface.tripoints[n].i2].point; v: Triple ~ G3dPolygon.TriangleNormal[p0, p1, p2]; surface.faceNx[n] ¬ v.x; surface.faceNy[n] ¬ v.y; surface.faceNz[n] ¬ v.z; ENDLOOP; }; }; AddTripoint: PUBLIC PROC [tripoints: TripointSequence, tripoint: Tripoint] RETURNS [TripointSequence] ~ { IF tripoints.length >= tripoints.maxLength THEN tripoints ¬ G3dBasic.LengthenTripointSequence[tripoints]; tripoints[tripoints.length] ¬ tripoint; tripoints.length ¬ tripoints.length+1; RETURN[tripoints]; }; NTripoints: PUBLIC PROC [surface: Surface] RETURNS [INTEGER] ~ { RETURN[IF surface = NIL OR surface.tripoints = NIL THEN 0 ELSE surface.tripoints.length]; }; FOR edge: Edge IN Edge DO IF crossedEdges[edge].cIn # NIL AND NOT done[edge] THEN { e, start: Edge ¬ edge; poly.length ¬ 0; DO e ¬ NextCWEdgeCrossed[e]; poly[poly.length] ¬ GetVertexId[crossedEdges[e] ! BoundsFault => GOTO PointsLimit]; poly.length ¬ poly.length+1; done[e] ¬ TRUE; IF e = start THEN EXIT; ENDLOOP; FOR n: INT IN [0..poly.length-2] DO NewTriangle[poly[0], poly[n], poly[n+1] ! BoundsFault => GOTO TriangleLimit]; ENDLOOP; }; REPEAT PointsLimit => NULL; TriangleLimit => trianglesLimited ¬ TRUE; ENDLOOP; ¸ ImplicitPolygonsImpl.mesa Copyright Ó 1985, 1990 by Xerox Corporation. All rights reserved. Bloomenthal, February 27, 1993 4:08 pm PST Polygonization Presumes a restricted octree. Apply crossPolygonProc to each of the polygons in cube. Result is $Normal, $RecurseTracked or $RecurseFailed. Find the surface point associated with this edge: And add it to the polygon: Polygonize the terminal cubes using a modified polygonization algorithm. When proceeding around a face, if the face neighbor is not terminal, then proceed (recursively if necessary) around the next level's face, as shown below, until the next vertex, located on an edge belonging to the parent's face, is found. [Artwork node; type 'Artwork on' to command tool] Face tracking method Find all the edges of kid that are crossed by the surface: prepare to track around edge neighbor: Go CW around face until an edge with a surface vertex. We have a subdivision mismatch: Create the actual polygon by calling the client-supplied procedure, crossPolygonProc, with the polygon number and a sequence of vertex ids: Find edges of cube crossed by the surface: Finesse: PROC [length0: INT, poly1: NatSequence] ~ { tripoints: G3dBasic.TripointSequence; nPoints: INT ~ length0+poly1.length; map: NatSequence ¬ NEW[NatSequenceRep[nPoints]]; points: TripleSequence ¬ NEW[TripleSequenceRep[nPoints]]; indices0: NatSequence ¬ NEW[NatSequenceRep[length0]]; indices1: NatSequence ¬ NEW[NatSequenceRep[poly1.length]]; poly0: NatSequence ¬ NEW[NatSequenceRep[length0]]; FOR n: INT IN [0..poly0.length ¬ length0) DO poly0[n] ¬ polys[polys.length-length0+n]; ENDLOOP; polys.length ¬ polys.length-length0-1; surface.nPolygons ¬ surface.nPolygons-1; points.length ¬ nPoints; FOR n: INT IN [0..indices0.length ¬ poly0.length) DO points[indices0[n] ¬ n] ¬ surface.vertices[map[n] ¬ poly0[n]].point; ENDLOOP; FOR n: INT IN [0..indices1.length ¬ poly1.length) DO m: INT ~ n+poly0.length; points[indices1[n] ¬ m] ¬ surface.vertices[map[m] ¬ poly1[n]].point; ENDLOOP; tripoints ¬ G3dPolygon.Triangulate2Polygons[indices0, indices1, points]; scratchPolygon.length ¬ 3; FOR n: INT IN [0..tripoints.length) DO scratchPolygon[0] ¬ map[tripoints[n].i2]; scratchPolygon[1] ¬ map[tripoints[n].i1]; scratchPolygon[2] ¬ map[tripoints[n].i0]; AddPolygonToSurface[scratchPolygon]; ENDLOOP; }; 1 => Finesse[length0, polygon]; BoundsFault => {message ¬ Rope.Cat[message, " *polygons limited, "]; CONTINUE} message ¬ IO.PutFR["%g (%g changes", IO.rope[message], IO.int[nRecursiveTrackFaces]]; message ¬ IO.PutFR["%g, %g failed)", IO.rope[message], IO.int[nRecurseFailures]]; Miscellany Reverse the sense of the FrontFacing tests since implicit polygons are backwards: The following was an attempt in Triangulate[] to take the original polygon derived from the cube's edge crossedEdges, approximate a central point, and form sub-triangles like pie slices. It didn't work, probably because of problems in Jiggle[]. wheels: WheelSequence ¬ NEW[WheelSequenceRep[triangles.maxLength]]; polyPts: TripleSequence ¬ NEW[TripleSequenceRep[12]]; radius: REAL ~ 0.5*G3dOctree.Size[cube]; SELECT poly.length FROM 3 => AddTripoint[poly[0], poly[1], poly[2] ! BoundsFault => GOTO TriangleLimit]; > 3 => { target: Target ¬ Jiggle[]; IF target.ok THEN SpokePoly[NewPt[target.pt], poly ! BoundsFault => GOTO TriangleLimit] ELSE SweepPoly[poly ! BoundsFault => GOTO TriangleLimit]; }; ENDCASE; Jiggle: PROC RETURNS [target: Target] ~ { FOR n: INT IN [0..poly.length) DO polyPts[n] ¬ points[poly[n]]; ENDLOOP; polyPts.length ¬ poly.length; { v: Triple ~ Vector3d.Normalize[G3dPolygon.PolygonNormal[polyPts]]; a: Triple ~ PolygonCenter[polyPts]; aPot: REAL ~ valueProc[a]; b: Triple ¬ Vector3d.Ray[[a, v], 0.1*radius]; bPot: REAL ¬ valueProc[b]; IF (aPot > 0.0) = (bPot > 0.0) THEN bPot ¬ valueProc[b ¬ Vector3d.Ray[[a, v], -0.1*radius]]; IF (aPot > 0.0) = (bPot > 0.0) THEN target.ok ¬ FALSE ELSE { target ¬ Converge[a, b, aPot, bPot]; target.ok ¬ ImplicitSurface.PointInCube[target.pt, cube]; target.ok ¬ FALSE; }; } }; SpokePoly: PROC [id: INT, poly: REF NatSequence] ~ { FOR n: INT IN [0..poly.length-1) DO AddTripoint[id, poly[n], poly[n+1]]; ENDLOOP; AddTripoint[id, poly[poly.length-1], poly[0]]; wheels[wheels.length].id ¬ id; FOR n: INT IN [0..poly.length) DO wheels[wheels.length].poly[n] ¬ poly[n]; ENDLOOP; wheels.length ¬ wheels.length+1; }; SweepPoly: PROC[poly: REF NatSequence] ~ { FOR n: INT IN [2..poly.length) DO AddTripoint[poly[0], poly[n-1], poly[n]]; ENDLOOP; }; PolygonCenter: PROC [points: TripleSequence] RETURNS [center: Triple ¬ origin] ~ { IF points.length = 0 THEN RETURN; FOR i: INT IN [0..points.length) DO center ¬ Vector3d.Add[center, points[i]]; ENDLOOP; center ¬ Vector3d.Div[center, points.length]; }; In Triangulate[], to divide up polygon according to longest side: (this didn't make much improvement, so the working code is kept simpler.) lengths: ARRAY [0..12) OF REAL; FOR n: INT IN [0..poly.length) DO lengths[n] ¬ Vector3d.Square[ Vector3d.Sub[points[poly[n]], points[poly[(n+1) MOD poly.length]]]]; ENDLOOP; WHILE poly.length > 3 DO Tri: PROC [n0, n1, n2: INT] ~ { NewTriangle[poly[n0], poly[n1], poly[n2]]; FOR n: INT IN [n1..poly.length-1) DO poly[n] ¬ poly[n+1]; ENDLOOP; FOR n: INT IN [n1..poly.length-1) DO lengths[n] ¬ lengths[n+1]; ENDLOOP; poly.length ¬ poly.length-1; }; max: REAL ¬ 0.0; n0, n1, n2, n3: INT; FOR n: INT IN [0..poly.length) DO IF lengths[n] > max THEN {n1 ¬ n; max ¬ lengths[n]}; ENDLOOP; n0 ¬ IF n1 = 0 THEN poly.length-1 ELSE n1-1; n2 ¬ (n1+1) MOD poly.length; n3 ¬ (n2+1) MOD poly.length; IF lengths[n0] > lengths[n2] THEN Tri[n0, n1, n2 ! BoundsFault => GOTO TriangleLimit] ELSE Tri[n1, n2, n3 ! BoundsFault => GOTO TriangleLimit]; ENDLOOP; NewTriangle[poly[0], poly[1], poly[2] ! BoundsFault => GOTO TriangleLimit]; �Ê�ì•NewlineDelimiter ™™JšœB™BJ™*J˜JšÏk œ¯˜¸J˜—šÐblœ�œ�˜#Jš�œŽ˜•Jš�œ˜J˜—šœ�˜J˜Jšœ�œ˜"Jšœ �œ˜+Jšœ�œ˜0Jšœ�œ˜1Jšœ�œ˜7Jšœ�œ˜0Jšœ�œ˜5Jšœ�œ˜#Jšœ �œ˜'Jšœ�œ˜#Jšœ �œ˜%Jšœ�œ˜"Jšœ �œ˜,Jšœ�œ˜-Jšœ�œ˜4Jšœ�œ˜/Jšœ�œ˜4Jšœ�œ˜ Jšœ �œ˜&Jšœ � œ˜)Jšœ�œ˜ Jšœ�œ˜ Jšœ�œ˜#Jšœ �œ˜*Jšœ �œ˜*Jšœ �œ˜-Jšœ�œ˜2Jšœ �œ˜(Jšœ �œ˜+š�œ�œ�œ˜J˜—Jšœ�œ˜3Jšœ �œ˜-—headšÏl™šœ �œ˜/J˜—šÐbnœ�œ�œ˜"J˜ J˜#Jšœ&�œ˜*Jšœ�œ�œ˜Jš�œ �œ ˜ J˜JšÏb™J™7J™5š�œ �œ�œ#�œ˜RšÏnœ�œ˜6J™1J˜@Jš�œ�œ �œ˜3J™š�œ>�œ˜FJ˜*Jšœ�œ2˜LJš �œ�œ�œ�œ#�œ˜KJ˜)J˜—J˜:J˜ÄYéÄQÀ¡£ x j À £¡ ” ç­“Ľ⠤ÄÎÉ'Ä/õÖ ¢ ¥ ¨Ÿ ™ ¡¡ ¡“ ŸŸ ¡“¡¡ ¡™ k À £¡ ” ç­“į“¢°“¢·“Ä~s¶Ä/õÖ™Ä~s¶Ä (ÄÎÉ'Ä (ÄÙ J¡”ÄìÄ (ÄìÄ/õÖÄÙ J¡”ÄìÄUéÄÎÉ'ÄUéÄÙ J¡”Ä~s¶ÄUéÄ~s¶Ä/õÖÄÙ J¡”¡¸ k é k x j¢¯“¢°“¢·“Ä¡¨Ä‘ kÄ9;™ÄxñÈ�ij6}ŽÄ9;�Ä‘ kŽ¡¸ k x jį“¢°“¢·“Ä¡¨Ä‘ kÄ—wY™Äc­8�ij6}ŽÄ—wY�Ä‘ kŽ¡¸ k x jÅxeroxÅxc1-2-2Å helvetica£¡ “¡•¡ —¡¡¨Ä�Î å ¤ÄB ÜÄ• ¢ ¥ ¨  Š¡²“Álevel 1 vertex– k x jÅxeroxÅxc1-2-2Å helvetica£¡ “¡•¡ —¡¡¨Ä�Î å ¤ÄB ÜÄ»I� ¢ ¥ ¨  Š¡²“Álevel 2 vertex– k x jÅxeroxÅxc1-2-2Å helvetica£¡ “¡•¡ —¡¡¨Ä�Î å ¤ÄB ÜÄjC: ¢ ¥ ¨  Š¡²“Á level 1 face– k x jÅxeroxÅxc1-2-2Å helvetica£¡ “¡•¡ —¡¡¨Ä�Î å ¤ÄB ÜÄbu9 ¢ ¥ ¨  Š¡²“Á level 2 face– k x j r jÄ‚‰Ä+WIŠ ªÄ@'¯Äañ¦Ä$4Ä$4¡£ x j ¡¨Ä7  ¤ÄQIËÄ‘[é ¢ ¥ ¨Ÿ ™ ¡¡ ¡“ ŸŸ ¡“¡¡ ¡™ k  À£¡ ” ç­“į“¢°“¢·“Ä@ù¯Ä‘[é™Ä@ù¯Ä^¯‘ÄQIËÄ^¯‘ÄÙ J¡”Ä)«aÄ^¯‘Ä)«aÄ‘[éÄÙ J¡”Ä)«aÄL€ÄQIËÄL€ÄÙ J¡”Ä@ù¯ÄL€Ä@ù¯Ä‘[éÄÙ J¡”¡¡¢¡ ¬¡º k é k x j r jÄ‚‰Ä+WIŠ ªÄmG!Ä�÷ìÄåNÄåN¡£  À£¡ ” ç­“į“¢°“¢·“Ä-_°Ä‘[é™Ä-_°Ä`Ü—ÄQIËÄ`Ü—ÄÙ J¡”Ä5í�Ä`Ü—Ä5í�Ä‘[éÄÙ J¡”Ä5í�ÄV±�ÄQIËÄV±�ÄÙ J¡”Ä-_°ÄV±�Ä-_°Ä‘[éÄÙ J¡”¡Ä¢¡ ¯¡º k é k x j r jÄ‚‰Ä+WIŠ ªÄ5^‰Äj ­Ä )ÓÄYé¡£ x j  À£¡ ” ç­“Ľ⠤ÄQIËÄ‘[é ¢ ¥ ¨Ÿ ™ ¡¡ ¡“ ŸŸ ¡“¡¡ ¡™ k  À£¡ ” ç­“į“¢°“¢·“Ä=›Ä‘[é™Ä=›Ä=aÄQIËÄ=aÄÙ J¡”Ä9U�Ä=aÄ9U�Ä‘[éÄÙ J¡”Ä9U�Äñ*ÄQIËÄñ*ÄÙ J¡”Ä=›Äñ*Ä=›Ä‘[éÄÙ J¡”¡¸ k é kÄ¡¨ ¡ ¡™ x j r jÄI€ÛÄFiŠ ªÄQË{Äþ SÄOqÄ¡£À À£¡ ” ç­“į“¢°“¢·“Ä/FÄŠ ;™Äß'ŽÄÄ¢¡  ¡º k é k x jÅxeroxÅxc1-2-2Å helvetica£¡ “¡•¡ —¡¡¨Ä�Î å ¤ÄB ÜÄË ¢ ¥ ¨  Š¡²“Á desired edge– k x j r jÄOˆ‰Äã ªÄ$i^Ä$ ;Ä åíÄ åí¡£ x jÀ À£¡ ” ç­“ÄÕà¤ÄQIËÄ‘[é ¢ ¥ ¨Ÿ ™ ¡¡ ¡“ ŸŸ ¡“¡¡ ¡™ kÀ À£¡ ” ç­“¡¯“¢°“¢·“Ä$Ç^Ä‘[é™Ä$Ç^Ä$·:ÄQIËÄ$·:ÄÙ J¡”Ä!LÄ$·:Ä!LÄ‘[éÄÙ J¡”Ä!LÄ$D;ÄQIËÄ$D;ÄÙ J¡”Ä$Ç^Ä$D;Ä$Ç^Ä‘[éÄÙ J¡”¡¸ k é k x j r jÄÛÁ®ÄWù_Š ªÄ$i^Ä$ ;Ä åíÄ åí¡£ x jÀ À£¡ ” ç­“ÄÕà¤ÄQIËÄ‘[é ¢ ¥ ¨Ÿ ™ ¡¡ ¡“ ŸŸ ¡“¡¡ ¡™ kÀ À£¡ ” ç­“¡¯“¢°“¢·“Ä$Ç^Ä‘[é™Ä$Ç^Ä$·:ÄQIËÄ$·:ÄÙ J¡”Ä!LÄ$·:Ä!LÄ‘[éÄÙ J¡”Ä!LÄ$D;ÄQIËÄ$D;ÄÙ J¡”Ä$Ç^Ä$D;Ä$Ç^Ä‘[éÄÙ J¡”¡¸ k é k x j r jÄ6 Ä+WIŠ ªÄ$i^Ä$ ;Ä åíÄ åí¡£ x jÀ À£¡ ” ç­“ÄÕà¤ÄQIËÄ‘[é ¢ ¥ ¨Ÿ ™ ¡¡ ¡“ ŸŸ ¡“¡¡ ¡™ kÀ À£¡ ” ç­“¡¯“¢°“¢·“Ä$Ç^Ä‘[é™Ä$Ç^Ä$·:ÄQIËÄ$·:ÄÙ J¡”Ä!LÄ$·:Ä!LÄ‘[éÄÙ J¡”Ä!LÄ$D;ÄQIËÄ$D;ÄÙ J¡”Ä$Ç^Ä$D;Ä$Ç^Ä‘[éÄÙ J¡”¡¸ k é k x j r jÄ@•³ÄEGbŠ ªÄ$i^Ä$ ;Ä åíÄ åí¡£ x jÀ À£¡ ” ç­“ÄÕà¤ÄQIËÄ‘[é ¢ ¥ ¨Ÿ ™ ¡¡ ¡“ ŸŸ ¡“¡¡ ¡™ kÀ À£¡ ” ç­“¡¯“¢°“¢·“Ä$Ç^Ä‘[é™Ä$Ç^Ä$·:ÄQIËÄ$·:ÄÙ J¡”Ä!LÄ$·:Ä!LÄ‘[éÄÙ J¡”Ä!LÄ$D;ÄQIËÄ$D;ÄÙ J¡”Ä$Ç^Ä$D;Ä$Ç^Ä‘[éÄÙ J¡”¡¸ k é kÀ  £¡ ” ç­“ ¡ ¡™ x j r jÄOˆ‰ÄøÅŠ ªÄáSÄFpiÄ £EĽ=¡£ À £¡ ” ç­“ÄŽ9ö…¯“¢°“¢·“ÄB×ÛÄ"`™™ÄsL™ÄJNq—ÄB×ÛÄ"`™—ÄsL™Ä@:—˜ k é kÄ¡¨ ¡ ¡™ x j r jÄOˆ‰Ä ªÄ 1(Äüý>Äi  Ä¥½ô¡£À À£¡ ” ç­“į“¢°“¢·“Ä·(ÄúÓ|™Ä@eÄ#Oî—Ä obÄŸÖ——ÄQIËÄ‘[é—ÄÄ¢¡  ¡º k é k k k gš¢3™3Icenterš¡™M™J˜5J˜9J˜ J˜/J˜2šœ�œ%˜8Jš�œ˜ š�œ�œ%˜,Jš�œ˜ Jš �œ�œ �œ �œ Ïc˜=——š�˜J˜J˜6J™:J˜Bš�œ�œ ˜Jš�œ¡œ˜/š�œ˜J˜š�˜Jšœ*£Ðcs£ ˜:Jš�œ�œ�œ˜Jš�œ�œ�œ�œ˜*Jš�œ˜—J˜&J˜——š�œ�œ�œ˜8Jš�œ�œ˜š�œ˜J™&J˜5J˜;J˜7J˜.J˜.J˜0J˜*J˜——Jš�œ˜—J˜—š œ�œ �œ ˜5J™6J˜IJ˜4š �œ�œ �œ�œ�œ˜@š�œ˜J™J˜-J˜2Jš�œ�œ˜2˜Jš¡œ'˜9—J˜—š�œ˜J˜š�˜J˜(Jš�œ�œ�œ�œ˜*Jš�œ�œ�œ˜(Jš�œ˜—J˜&J˜——Jš�œ˜ J˜—š¢œ�œ�œ�œ˜Jš œ�œ�œ�œ�œ�œ˜&J˜š�œ �œ�˜š �œ�œ�œ�œ �œ˜9J˜J˜ š�˜Jšœ(�œ˜;Jšœ �œ˜Jš�œ �œ�œ˜Jš�œ˜—Jš�œ,�œ�œ�œ˜@J™CJ™GJš �œ�œ2�œ�œ�œ˜JJ˜J˜—Jš�œ˜—Jš�œ�œ˜ š�˜Jšœ�œ�œ˜—J˜—Jšœ�œ�œ˜Jšœ�œ˜ J™*J˜Cš�œ�œ�œ%˜EJš�œ�œ˜7—š�œ�œ �œ˜Jšœ�œ˜J˜J˜ J˜—J˜—J˜J˜—š œ�œ�œ˜J˜ J˜Jšœ�œ˜"Jšœ�œ�œ˜Jš�œ �œ ˜ J˜š¡œ˜&š�œ�œ�œ�˜$J˜"Jš�œ˜—J˜'J˜+J˜—J˜?š�œ�œ�œ˜9Jš�œ�œ˜/—J˜VJ˜.J˜J˜—š¢œ�œ�œ˜!J˜J˜ Jšœ �œ�œ˜Jšœ�œ˜Jšœ �œ˜Jšœ�œ˜Jšœ�œ˜#Jšœ �œ�œ�œ˜Jš�œ �œ˜J˜Jšœ�œ�œ˜š¡œ˜š¢œ�œ �œ™4J™%Jšœ �œ™$Jšœ�œ™0Jšœ�œ™9Jšœ�œ™5Jšœ�œ™:Jšœ�œ™2š�œ�œ�œ�™,J™)Jš�œ™—J™&J™(J™š�œ�œ�œ%�™4J™DJš�œ™—š�œ�œ�œ%�™4Jšœ�œ™J™DJš�œ™—J™HJ™š�œ�œ�œ�™&J™)J™)J™)J™$Jš�œ™—J™—š¢œ�œ˜4š¢ œ�œ�œ˜š�œ�œ'�˜2˜,˜1Jšœ%£˜BJšœ�œ£˜?Jš�œ˜ J˜———J˜—š¢ œ�œ˜+J˜(J˜J˜9š�œ�œ�œ�˜$J˜5Jš�œ˜—J˜—š¢ œ�œ�œ˜'J˜(J˜J˜,J˜-J˜-J˜-J˜—š�œ�œ �œ �˜9š�œ˜Jšœ�œ˜ J˜#š�œ�œ�œ�˜$J˜CJš�œ˜—Jšœ'�œ˜=J˜#J˜QJ˜&Jš�œÏsœ¥œ¥œ¥œ¥�¥œ¥œ¥œ˜Sš�œ�œ�œ �˜Jšœ�œ?˜Gš�œ�œ˜"J˜k˜*J˜;—Jšœ�œ2˜:š�œ�œ�œ�˜$Jšœ+�œ˜@Jš�œ˜—Jš�œ˜J˜—š�œ�œ˜J˜+J˜J˜—Jš�œ˜—J˜—Jš�œ˜—J˜—š¡ œ˜š�œ�œ�œ˜J˜-š�œ�œ�œ�˜+J˜=Jš�œ˜—Jš�œ�œ*�œ�œ˜J˜AJ˜8Jš�œ˜—J˜—š¢œ�œ˜9š�œ�˜Jš�œ�œ˜#š�œ�œ�œ(�˜=Jš �œ �œ�œ �œ �œ˜2Jš�œ˜——J˜—Jšœ(�œ˜0Jšœ�œ˜6Jšœ �œ˜;Jšœ�œ�œ �œ ˜Y˜2JšœD�œ˜NJšœF� œ™PJ˜—J˜"Jšœ �œ�œ�œ˜QJšœ �œ�œ�œ™UJšœ �œ�œ�œ™QJ˜——šŸ ™ š¢œ� œ1˜Tš �œ �œ�œ�œ�œ˜2Jšœ �œ˜Jšœ�œ˜0š �œ�œ�œ(�œ�œ�˜FJšœ�œ˜J˜ š�œ�˜ J˜!J˜š�œ�œ�œ�˜$J˜Jšœ£˜Jš�œ˜—Jš�œ�œ �œ�œ˜2J˜Jš�œ˜—Jš�œ˜—J˜—J˜J˜—š¢œ� œ˜)J˜J˜ J˜J˜J˜Jšœ�œ�œ˜š¢œ˜J™Qšœ�œ�œ�œ�˜Jšœ*�œ˜/šœ �œ&˜2J˜G—š�œ˜ Jš�œI˜L——Jš �œ�œ�œ �œ�œ˜>J˜—J˜Jšœ�œ"˜-Jš�œ�œ<˜Iš�œ4�˜:˜.Jš�œ2�œ˜@——J˜!J˜J˜—š ¢œ�œ�œ,�œ�œ˜ZJ˜š�œ �œ�œ�œ�œ �œ �œ˜QJšœ�œ˜4J˜.J˜.š¢ œ˜J˜š�œ�œ�œ�˜#J˜/Jš�œ˜—š�œ �˜J˜R—š�œ �˜J˜R—J˜—š�œ �œ �œ�œ'˜JJš�œ �œ�œ#˜K—š�œ �œ �œ�œ'˜JJš�œ �œ�œ#˜K—Jšœ=�œ˜GJ˜J˜J˜—J˜J˜—š¢œ�œ�œ�œ ˜Vš �œ �œ�œ�œ�œ˜2š¢œ˜Jšœ�œ!˜8Jšœ�œ˜!J˜š�œ�œ�œ�˜$J˜Jš�œ˜—J˜J˜—Jšœ �œ˜Jšœ �œ(˜6J˜$J˜)J˜—J˜——J˜Jš�œ˜˜J™Jš œ�œ�œ�œ�œ�œ ˜XJšœ �œ�œ�œ˜>J˜š�œ�œ�œ�˜#J˜RJš�œ˜J˜—š¢œ�œ�œ˜2š�œ �œ�œ�œ�œ�œ�œ˜NJšœ�œ˜$Jšœ�œ˜+Jšœ�œ˜+Jšœ�œ˜+J˜Lš�œ�œ�œ �˜J˜=J˜=J˜=J˜2J˜J˜J˜Jš�œ˜—J˜—J˜J˜—š¢ œ�œ�œ2˜JJš�œ˜J˜š�œ(˜*Jš�œ:˜>—J˜'J˜&Jš�œ ˜J˜J˜—š ¢ œ�œ�œ�œ�œ˜@Jš�œ�œ �œ�œ�œ�œ�œ˜YJ˜J˜—J™[J™bJ™6™Jšœ�œ(™CJšœ�œ™5Jšœ�œ™(J™š�œ �™Jšœ<�œ™P™J™š�œ ™ Jš�œ3�œ™JJš�œ!�œ™9—J™—Jš�œ™J™—š¢œ�œ�œ™)Jš �œ�œ�œ�œ�œ™HJ™J™J™BJ™#Jšœ�œ™J™-Jšœ�œ™š�œ™Jš�œ9™=—š�œ™Jš�œ �™š�œ™J™$J™9Jšœ �œ™J™——J™J™J™—š¢ œ�œ�œ�œ™4Jš �œ�œ�œ�œ%�œ™QJ™.J™Jš �œ�œ�œ�œ*�œ™SJ™ J™J™—š¢ œ�œ�œ™*Jš �œ�œ�œ�œ*�œ™TJ™J™—š¢ œ�œ�œ™RJš�œ�œ�œ™!Jš �œ�œ�œ�œ*�œ™VJ™-J™J™——Jš¡A™AJ™Iš�œ �œ�˜š �œ�œ�œ�œ �œ˜9Jšœ �œ �œ�œ™J˜J˜š�˜J˜JšœA�œ˜SJ˜Jšœ �œ˜Jš�œ �œ�œ˜Jš�œ˜—š�œ�œ�œ�™!™Jšœ0�œ™D—Jš�œ™—š�œ�™š¢œ�œ�œ™J™*Jš �œ�œ�œ�œ�œ™BJš �œ�œ�œ�œ�œ™HJ™J™—Jšœ�œ™Jšœ�œ™š�œ�œ�œ�™!Jš�œ�œ™4Jš�œ™—Jšœ�œ�œ�œ™,Jšœ �œ ™Jšœ �œ ™š�œ™Jš�œ!�œ™8Jš�œ!�œ™9—Jš�œ™—Jšœ7�œ™Kš�œ�œ�œ�˜#Jšœ9�œ˜MJš�œ˜—J˜—š�˜Jšœ�œ˜Jšœ$�œ˜)—Jš�œ˜J™———…—<ÀÕd