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 JBBJ*JJk Jbl#JJJJJ"J +J0J1J7J0J5J#J 'J#J %J"J ,J-J4J/J4J J &J )J J J#J *J *J -J2J (J +JJ3J -headl /Jbn"J J#J&*JJ  JJbJ7J5 #Rn6J1J@J 3J>FJ*J2LJ #KJ)JJ:JYQxj⠤'/֠k~s/֙~s (' ( J (/ JU'U J~sU~s/ Jkkxj k9;xȏ6}9; kkxj kwYc86}wY kkxjxeroxxc1-2-2 helvetica 堤B level 1 vertexkxjxeroxxc1-2-2 helvetica 堤B Ilevel 2 vertexkxjxeroxxc1-2-2 helvetica 堤B jC: level 1 facekxjxeroxxc1-2-2 helvetica 堤B bu9 level 2 facekxjrj+WI@'a$4$4xj7 QI[頢k@[@^QI^ J)a^)a[ J)aLQIL J@L@[ Jkkxjrj+WImG!NN-_[-_`QI` J5`5[ J5VQIV J-_V-_[ Jkkxjrj+WI5^j  )Y顣xj⠤QI[頢k=[==aQI=a J9U=a9U[ J9U*QI* J=*=[ JkkxjrjIFiQ{ SOq/F ;'kkxjxeroxxc1-2-2 helvetica 堤B  desired edgekxjrjO$i^$ ;  xjàQI[頢k$^[$^$:QI$: J!L$:!L[ J!L$D;QI$D; J$^$D;$^[ JkkxjrjW_$i^$ ;  xjàQI[頢k$^[$^$:QI$: J!L$:!L[ J!L$D;QI$D; J$^$D;$^[ Jkkxjrj6 +WI$i^$ ;  xjàQI[頢k$^[$^$:QI$: J!L$:!L[ J!L$D;QI$D; J$^$D;$^[ Jkkxjrj@EGb$i^$ ;  xjàQI[頢k$^[$^$:QI$: J!L$:!L[ J!L$D;QI$D; J$^$D;$^[ JkkxjrjOŊSFpi E=9B"`sLJNqB"`sL@:kkxjrjO 1(>i  (|@e#O obQI[kkkkg33IcenterMJ5J9J J/J2%8J %,J J    c=JJ6J:JB J/JJ*cs :JJ*JJ&J8JJ&J5J;J7J.J.J0J*JJJ  5J6JIJ4  @JJ-J2J2J'9JJJ(J*J(JJ&JJ JJ &J   9JJ J(;J J JJ,@JCJGJ 2JJJJJ JJJJ J*JC%EJ7 JJJ JJJJJ JJ"JJ  J&$J"JJ'J+JJ?9J/JVJ.JJ!JJ J JJ JJ#J J JJ 4J%J $J0J9J5J:J2,J)JJ&J(J%4JDJ%4JJDJJHJ&J)J)J)J$JJ4 '2,1J%BJ?J JJ +J(JJ9$J5JJ 'J(JJ,J-J-J-J  9J J#$JCJJ'=J#JQJ&JsS J?G"Jk*J;J2:$J+@JJJJ+JJJJJJ J-+J=JJ*JAJ8JJ9J#(=J    2JJJ(0J6J ;J  Y2JDNJF PJJ"J QJ UJ QJ  1T  2J J0 (FJJ  J!J$JJJJ 2JJJJJJ )JJ JJJJJQJ*/ &2JG JILJ  >JJJ"-JJ#JRJJ2 NJ$J+J+J+JL J=J=J=J2JJJJJJJ 2JJJ(*J:>J'J&J JJ @J YJJJ[JbJ6J(CJ5J(J J<PJ J3JJ!9JJJ)J HJJJBJ#JJ-JJ9=J J$J9J JJJJ 4J %QJ.JJ *SJ JJ *J *TJJ RJ!J *VJ-JJJAAJI   9J  JJJJASJJ J J!J0DJJ*J BJ HJJJJ!J4JJ,J  J  J!8J!9JJ7K#J9MJJJJ$)JJ<d