<<>> <> <> <> DIRECTORY Basics, G3dBasic, G3dBox, G3dIO, G3dOctree, G3dPlane, FS, FileNames, IO, Real, RefText, Rope; G3dOctreeImpl: CEDAR MONITOR IMPORTS Basics, G3dBox, G3dIO, G3dOctree, G3dPlane, FS, FileNames, IO, Real, RefText, Rope EXPORTS G3dOctree ~ BEGIN <> ROPE: TYPE ~ Rope.ROPE; STREAM: TYPE ~ IO.STREAM; Box: TYPE ~ G3dBasic.Box; Pair: TYPE ~ G3dBasic.Pair; Triple: TYPE ~ G3dBasic.Triple; Plane: TYPE ~ G3dPlane.Plane; ActiveFaces: TYPE ~ G3dOctree.ActiveFaces; Axis: TYPE ~ G3dOctree.Axis; Corner: TYPE ~ G3dOctree.Corner; CornerDataProc: TYPE ~ G3dOctree.CornerDataProc; CornerProc: TYPE ~ G3dOctree.CornerProc; CornerRep: TYPE ~ G3dOctree.CornerRep; Corners: TYPE ~ G3dOctree.Corners; Cross: TYPE ~ G3dOctree.Cross; CrossArray: TYPE ~ G3dOctree.CrossArray; CrossPolygonProc: TYPE ~ G3dOctree.CrossPolygonProc; CrossRep: TYPE ~ G3dOctree.CrossRep; CrossSequence: TYPE ~ G3dOctree.CrossSequence; CrossSequenceRep: TYPE ~ G3dOctree.CrossSequenceRep; CrossedEdge: TYPE ~ G3dOctree.CrossedEdge; CrossedEdges: TYPE ~ G3dOctree.CrossedEdges; Cube: TYPE ~ G3dOctree.Cube; CubeObject: TYPE ~ G3dOctree.CubeObject; CubeProc: TYPE ~ G3dOctree.CubeProc; CubeRep: TYPE ~ G3dOctree.CubeRep; CubeStack: TYPE ~ G3dOctree.CubeStack; CubeStackRep: TYPE ~ G3dOctree.CubeStackRep; CubeSequence: TYPE ~ G3dOctree.CubeSequence; CubeSequenceRep: TYPE ~ G3dOctree.CubeSequenceRep; Direction: TYPE ~ G3dOctree.Direction; DirectionCorners: TYPE ~ G3dOctree.DirectionCorners; DirectionPairs: TYPE ~ G3dOctree.DirectionPairs; DirectionSelect: TYPE ~ G3dOctree.DirectionSelect; DirectionType: TYPE ~ G3dOctree.DirectionType; Edge: TYPE ~ G3dOctree.Edge; Face: TYPE ~ G3dOctree.Face; FaceNeighborInfo: TYPE ~ G3dOctree.FaceNeighborInfo; Intersection: TYPE ~ G3dOctree.Intersection; IntersectionList: TYPE ~ G3dOctree.IntersectionList; IntersectionType: TYPE ~ G3dOctree.IntersectionType; Kids: TYPE ~ G3dOctree.Kids; Neighborhood: TYPE ~ G3dOctree.Neighborhood; Octant: TYPE ~ G3dOctree.Octant; OctantPairs: TYPE ~ G3dOctree.OctantPairs; Octree: TYPE ~ G3dOctree.Octree; OctreeMode: TYPE ~ G3dOctree.OctreeMode; OctreeRep: TYPE ~ G3dOctree.OctreeRep; OctreeType: TYPE ~ G3dOctree.OctreeType; FourDirections: TYPE ~ G3dOctree.FourDirections; FourOctants: TYPE ~ G3dOctree.FourOctants; FourCorners: TYPE ~ G3dOctree.FourCorners; ThreeCubes: TYPE ~ G3dOctree.ThreeCubes; ThreeDirections: TYPE ~ G3dOctree.ThreeDirections; ThreeEdges: TYPE ~ G3dOctree.ThreeEdges; TwoCorners: TYPE ~ G3dOctree.TwoCorners; TwoCubes: TYPE ~ G3dOctree.TwoCubes; TwoDirections: TYPE ~ G3dOctree.TwoDirections; TwoFaces: TYPE ~ G3dOctree.TwoFaces; TwoOctants: TYPE ~ G3dOctree.TwoOctants; <> NewCube: PUBLIC PROC [ size: REAL, center: Triple ¬ [], cornerDataProc: CornerDataProc ¬ NIL] RETURNS [Cube] ~ { RETURN[NewKid[NIL, size, lbn, 0, NewCorners[size, center, cornerDataProc]]]; }; NewKid: PROC [ parent: Cube, size: REAL, octant: Octant, level: INT, corners: Corners] RETURNS [cube: Cube] ~ { cube ¬ NEW[CubeRep]; cube.parent ¬ parent; cube.size ¬ size; cube.octant ¬ octant; cube.level ¬ level; cube.terminal ¬ TRUE; cube.corners ¬ corners; }; NewCorner: PROC [ point: Triple, cornerDataProc: CornerDataProc ¬ NIL] RETURNS [corner: Corner] ~ { corner ¬ NEW[CornerRep ¬ [point: point]]; }; NewCorners: PUBLIC PROC [ size: REAL, center: Triple, cornerDataProc: CornerDataProc ¬ NIL] RETURNS [corners: Corners] ~ { d: REAL ~ 0.5*size; l: REAL ~ center.x-d; r: REAL ~ center.x+d; b: REAL ~ center.y-d; t: REAL ~ center.y+d; n: REAL ~ center.z-d; f: REAL ~ center.z+d; corners ¬ [ NewCorner[[l, b, n], cornerDataProc], NewCorner[[l, b, f], cornerDataProc], NewCorner[[l, t, n], cornerDataProc], NewCorner[[l, t, f], cornerDataProc], NewCorner[[r, b, n], cornerDataProc], NewCorner[[r, b, f], cornerDataProc], NewCorner[[r, t, n], cornerDataProc], NewCorner[[r, t, f], cornerDataProc] ]; }; <> Root: PUBLIC PROC [cube: Cube] RETURNS [root: Cube] ~ { root ¬ cube; IF cube # NIL THEN WHILE root.parent # NIL DO root ¬ root.parent; ENDLOOP; }; <<>> CubeOk: PUBLIC PROC [cube: Cube] RETURNS [BOOL] ~ { k: BOOL ¬ FALSE; IF cube = NIL THEN RETURN[FALSE]; FOR o: Octant IN Octant DO IF cube.kids[o] # NIL THEN {k ¬ TRUE; EXIT}; ENDLOOP; IF k = cube.terminal THEN RETURN[FALSE]; FOR o: Octant IN Octant DO k: Cube ¬ cube.kids[o]; IF k # NIL THEN { IF k.parent # cube THEN RETURN[FALSE]; IF NOT CubeOk[k] THEN RETURN[FALSE]; }; ENDLOOP; RETURN[TRUE]; }; FullyPointed: PUBLIC PROC [root: Cube] RETURNS [b: BOOL ¬ TRUE] ~ { bad: ERROR = CODE; Inner: PROC [cube: Cube] ~ { terminal: BOOL; FOR o: Octant IN Octant DO k: Cube ¬ cube.kids[o]; IF o = Octant.FIRST THEN terminal ¬ k = NIL ELSE IF (k # NIL) AND terminal THEN ERROR bad; IF k # NIL THEN Inner[k]; ENDLOOP; }; Inner[root ! bad => {b ¬ FALSE; CONTINUE}]; }; NCubes: PUBLIC PROC [root: Cube] RETURNS [nCubes: INT ¬ 0] ~ { cubeProc: CubeProc ~ {nCubes ¬ nCubes+1}; Apply[root, cubeProc]; }; NTerminalCubes: PUBLIC PROC [root: Cube] RETURNS [nCubes: INT ¬ 0] ~ { cubeProc: CubeProc ~ {nCubes ¬ nCubes+1}; ApplyToTerminal[root, cubeProc]; }; Center: PUBLIC PROC [cube: Cube] RETURNS [Triple] ~ { IF cube # NIL THEN { p0: Triple ~ cube.corners[lbn].point; p1: Triple ~ cube.corners[rtf].point; RETURN[[0.5*(p0.x+p1.x), 0.5*(p0.y+p1.y), 0.5*(p0.z+p1.z)]]; }; RETURN[[0.0, 0.0, 0.0]]; }; Size: PUBLIC PROC [cube: Cube] RETURNS [REAL] ~ { RETURN[IF cube = NIL THEN 0.0 ELSE cube.size]; }; MinSize: PUBLIC PROC [root: Cube] RETURNS [min: REAL] ~ { MinProc: CubeProc ~ {IF cube.size < min THEN min ¬ cube.size}; min ¬ 100000000.0; ApplyToTerminal[root, MinProc]; }; BoxOfCube: PUBLIC PROC [cube: Cube] RETURNS [Box] ~ { RETURN[[cube.corners[lbn].point, cube.corners[rtf].point]]; }; AnyKids: PUBLIC PROC [cube: Cube] RETURNS [BOOL] ~ { FOR o: Octant IN Octant DO IF cube.kids[o] # NIL THEN RETURN[TRUE]; ENDLOOP; RETURN[FALSE]; }; DepthOf: PUBLIC PROC [cube: Cube] RETURNS [d: INT ¬ 0] ~ { Inner: PROC [cube: Cube, level: INT] ~ { ll: INT ¬ level+1; FOR o: Octant IN Octant DO k: Cube ¬ cube.kids[o]; IF k # NIL THEN Inner[k, ll]; REPEAT FINISHED => IF level > d THEN d ¬ level; ENDLOOP; }; Inner[cube, 0]; }; <> Subdivide: PUBLIC PROC [cube: Cube] ~ { IF cube # NIL AND cube.terminal THEN { FaceTry: PROC [d: Direction] RETURNS [corner: Corner ¬ NIL] ~ { KidsTry: PROC [o0a, o0b, o1a, o1b: Octant] RETURNS [corner: Corner ¬ NIL] ~ { IF c.kids[o0a] # NIL THEN RETURN[c.kids[o0a].corners[o0b]]; IF c.kids[o0b] # NIL THEN RETURN[c.kids[o0b].corners[o0a]]; IF c.kids[o1a] # NIL THEN RETURN[c.kids[o1a].corners[o1b]]; IF c.kids[o1b] # NIL THEN RETURN[c.kids[o1b].corners[o1a]]; }; c: Cube ~ neighbors[d]; IF c # NIL AND NOT c.terminal THEN corner ¬ SELECT d FROM l => KidsTry[rbn, rtf, rbf, rtn], r => KidsTry[lbn, ltf, lbf, ltn], b => KidsTry[ltn, rtf, ltf, rtn], t => KidsTry[lbn, rbf, lbf, rbn], n => KidsTry[lbf, rtf, ltf, rbf], f => KidsTry[lbn, rtn, ltn, rbn] ENDCASE => NIL; }; EdgeTry: PROC [d0, d1, d2: Direction, e0, e1, e2: Edge] RETURNS [corner: Corner] ~ { NeighborTry: PROC [d: Direction, e: Edge] RETURNS [corner: Corner ¬ NIL] ~ { KidsTry: PROC [o0, o1: Octant] RETURNS [corner: Corner ¬ NIL] ~ { IF c.kids[o0] # NIL THEN RETURN[c.kids[o0].corners[o1]]; IF c.kids[o1] # NIL THEN RETURN[c.kids[o1].corners[o0]]; }; c: Cube ~ neighbors[d]; IF c # NIL AND NOT c.terminal THEN corner ¬ SELECT e FROM lb => KidsTry[lbn, lbf], lt => KidsTry[ltn, ltf], ln => KidsTry[lbn, ltn], lf => KidsTry[lbf, ltf], rb => KidsTry[rbn, rbf], rt => KidsTry[rtn, rtf], rn => KidsTry[rbn, rtn], rf => KidsTry[rbf, rtf], bn => KidsTry[lbn, rbn], bf => KidsTry[lbf, rbf], tn => KidsTry[ltn, rtn], tf => KidsTry[ltf, rtf], ENDCASE => NIL; }; corner ¬ NeighborTry[d0, e0]; IF corner = NIL THEN corner ¬ NeighborTry[d1, e1]; IF corner = NIL THEN corner ¬ NeighborTry[d2, e2]; }; SubCube: PROC [octant: Octant, corners: Corners] ~ { kid: Cube ~ cube.kids[octant] ¬ NewKid[cube, kSize, octant, kidLevel, corners]; kid.terminal ¬ TRUE; kid.size ¬ kSize; }; kSize: REAL ~ 0.5*Size[cube]; kidLevel: INT ~ cube.level+1; c: Triple ~ Center[cube]; neighbors: Neighborhood ~ Neighbors[cube]; cornerDataProc: CornerDataProc ¬ NIL; ccc: Corner ~ NewCorner[c, cornerDataProc]; lbn: Corner ~ cube.corners[lbn]; lbf: Corner ~ cube.corners[lbf]; ltn: Corner ~ cube.corners[ltn]; ltf: Corner ~ cube.corners[ltf]; rbn: Corner ~ cube.corners[rbn]; rbf: Corner ~ cube.corners[rbf]; rtn: Corner ~ cube.corners[rtn]; rtf: Corner ~ cube.corners[rtf]; lb: Corner ¬ EdgeTry[l, lb, b, rb, rt, lt]; lt: Corner ¬ EdgeTry[l, lt, t, rt, rb, lb]; ln: Corner ¬ EdgeTry[l, ln, n, rn, rf, lf]; lf: Corner ¬ EdgeTry[l, lf, f, rf, rn, ln]; rb: Corner ¬ EdgeTry[r, rb, b, lb, lt, rt]; rt: Corner ¬ EdgeTry[r, rt, t, lt, lb, rb]; rn: Corner ¬ EdgeTry[r, rn, n, ln, lf, rf]; rf: Corner ¬ EdgeTry[r, rf, f, lf, ln, rn]; bn: Corner ¬ EdgeTry[b, bn, n, tn, tf, bf]; bf: Corner ¬ EdgeTry[b, bf, f, tf, tn, bn]; tn: Corner ¬ EdgeTry[t, tn, n, bn, bf, tf]; tf: Corner ¬ EdgeTry[t, tf, f, bf, bn, tn]; l: Corner ¬ FaceTry[l]; r: Corner ¬ FaceTry[r]; b: Corner ¬ FaceTry[b]; t: Corner ¬ FaceTry[t]; n: Corner ¬ FaceTry[n]; f: Corner ¬ FaceTry[f]; IF lb = NIL THEN lb ¬ NewCorner[[c.x-kSize, c.y-kSize, c.z], cornerDataProc]; IF lt = NIL THEN lt ¬ NewCorner[[c.x-kSize, c.y+kSize, c.z], cornerDataProc]; IF ln = NIL THEN ln ¬ NewCorner[[c.x-kSize, c.y, c.z-kSize], cornerDataProc]; IF lf = NIL THEN lf ¬ NewCorner[[c.x-kSize, c.y, c.z+kSize], cornerDataProc]; IF rb = NIL THEN rb ¬ NewCorner[[c.x+kSize, c.y-kSize, c.z], cornerDataProc]; IF rt = NIL THEN rt ¬ NewCorner[[c.x+kSize, c.y+kSize, c.z], cornerDataProc]; IF rn = NIL THEN rn ¬ NewCorner[[c.x+kSize, c.y, c.z-kSize], cornerDataProc]; IF rf = NIL THEN rf ¬ NewCorner[[c.x+kSize, c.y, c.z+kSize], cornerDataProc]; IF bn = NIL THEN bn ¬ NewCorner[[c.x, c.y-kSize, c.z-kSize], cornerDataProc]; IF bf = NIL THEN bf ¬ NewCorner[[c.x, c.y-kSize, c.z+kSize], cornerDataProc]; IF tn = NIL THEN tn ¬ NewCorner[[c.x, c.y+kSize, c.z-kSize], cornerDataProc]; IF tf = NIL THEN tf ¬ NewCorner[[c.x, c.y+kSize, c.z+kSize], cornerDataProc]; IF l = NIL THEN l ¬ NewCorner[[c.x-kSize, c.y, c.z], cornerDataProc]; IF r = NIL THEN r ¬ NewCorner[[c.x+kSize, c.y, c.z], cornerDataProc]; IF b = NIL THEN b ¬ NewCorner[[c.x, c.y-kSize, c.z], cornerDataProc]; IF t = NIL THEN t ¬ NewCorner[[c.x, c.y+kSize, c.z], cornerDataProc]; IF n = NIL THEN n ¬ NewCorner[[c.x, c.y, c.z-kSize], cornerDataProc]; IF f = NIL THEN f ¬ NewCorner[[c.x, c.y, c.z+kSize], cornerDataProc]; cube.terminal ¬ FALSE; SubCube[lbn, [lbn, lb, ln, l, bn, b, n, ccc]]; SubCube[lbf, [lb, lbf, l, lf, b, bf, ccc, f]]; SubCube[ltn, [ln, l, ltn, lt, n, ccc, tn, t]]; SubCube[ltf, [l, lf, lt, ltf, ccc, f, t, tf]]; SubCube[rbn, [bn, b, n, ccc, rbn, rb, rn, r]]; SubCube[rbf, [b, bf, ccc, f, rb, rbf, r, rf]]; SubCube[rtn, [n, ccc, tn, t, rn, r, rtn, rt]]; SubCube[rtf, [ccc, f, t, tf, r, rf, rt, rtf]]; }; }; SubdivideTerminal: PUBLIC PROC [cube: Cube] ~ { cubeProc: CubeProc ~ {Subdivide[cube]}; ApplyToTerminal[cube, cubeProc]; }; RecursivelySubdivide: PUBLIC PROC [cube: Cube, nLevels: INT] ~ { THROUGH [0..nLevels) DO SubdivideTerminal[cube]; ENDLOOP; }; <> TestCube: PROC [cube: Cube] ~ { tol: REAL ~ 0.01*Size[cube]; InnerTest: PROC [cube: Cube] ~ { Test: PROC [o0, o1: Octant, s: {x, y, z}] ~ { IF s # x AND ABS[c[o0].point.x-c[o1].point.x] > tol THEN ERROR; IF s # y AND ABS[c[o0].point.y-c[o1].point.y] > tol THEN ERROR; IF s # z AND ABS[c[o0].point.z-c[o1].point.z] > tol THEN ERROR; }; c: Corners ¬ cube.corners; Test[lbn, ltn, y]; Test[lbf, ltf, y]; Test[lbn, lbf, z]; Test[ltn, ltf, z]; Test[rbn, rtn, y]; Test[rbf, rtf, y]; Test[rbn, rbf, z]; Test[rtn, rtf, z]; Test[lbn, rbn, x]; Test[lbf, rbf, x]; Test[ltn, rtn, x]; Test[ltf, rtf, x]; }; InnerTest[cube]; }; AddCube: PUBLIC PROC [cube: Cube, face: Face, fullyPointed: BOOL ¬ TRUE] RETURNS [root: Cube] ~ { IF cube = NIL THEN RETURN[NIL]; TestCube[cube]; IF FaceNeighbor[cube, face] # NIL THEN RETURN[Root[cube]]; IF cube.parent = NIL THEN { <> cubeProc: CubeProc ~ {cube.level ¬ cube.level+1}; NewParent: PUBLIC PROC [cube: Cube, o: Octant] RETURNS [root: Cube] ~ { oo: Octant ~ G3dOctree.OppositeOctant[o]; d: REAL ~ 2.0*Size[cube]; l, r, b, t, n, f: REAL; c: Triple ~ cube.corners[o].point; pp: Triple ~ cube.corners[oo].point; SELECT oo FROM lbn => {l ¬ pp.x; b ¬ pp.y; n ¬ pp.z; r ¬ pp.x+d; t ¬ pp.y+d; f ¬ pp.z+d}; lbf => {l ¬ pp.x; b ¬ pp.y; f ¬ pp.z; r ¬ pp.x+d; t ¬ pp.y+d; n ¬ pp.z-d}; ltn => {l ¬ pp.x; t ¬ pp.y; n ¬ pp.z; r ¬ pp.x+d; b ¬ pp.y-d; f ¬ pp.z+d}; ltf => {l ¬ pp.x; t ¬ pp.y; f ¬ pp.z; r ¬ pp.x+d; b ¬ pp.y-d; n ¬ pp.z-d}; rbn => {r ¬ pp.x; b ¬ pp.y; n ¬ pp.z; l ¬ pp.x-d; t ¬ pp.y+d; f ¬ pp.z+d}; rbf => {r ¬ pp.x; b ¬ pp.y; f ¬ pp.z; l ¬ pp.x-d; t ¬ pp.y+d; n ¬ pp.z-d}; rtn => {r ¬ pp.x; t ¬ pp.y; n ¬ pp.z; l ¬ pp.x-d; b ¬ pp.y-d; f ¬ pp.z+d}; ENDCASE => {r ¬ pp.x; t ¬ pp.y; f ¬ pp.z; l ¬ pp.x-d; b ¬ pp.y-d; n ¬ pp.z-d}; root ¬ NEW[CubeRep ¬ [size: d]]; root.corners[oo] ¬ cube.corners[oo]; IF oo # lbn THEN root.corners[lbn] ¬ NewCorner[[l, b, n]]; IF oo # lbf THEN root.corners[lbf] ¬ NewCorner[[l, b, f]]; IF oo # ltn THEN root.corners[ltn] ¬ NewCorner[[l, t, n]]; IF oo # ltf THEN root.corners[ltf] ¬ NewCorner[[l, t, f]]; IF oo # rbn THEN root.corners[rbn] ¬ NewCorner[[r, b, n]]; IF oo # rbf THEN root.corners[rbf] ¬ NewCorner[[r, b, f]]; IF oo # rtn THEN root.corners[rtn] ¬ NewCorner[[r, t, n]]; IF oo # rtf THEN root.corners[rtf] ¬ NewCorner[[r, t, f]]; }; o: Octant ~ SELECT face FROM l=> lbn, r=> rtf, b=> rbn, t=> ltf, n=> ltn, ENDCASE=> rbf; opp: Octant ~ G3dOctree.OppositeOctant[o]; cube.parent ¬ root ¬ NewParent[cube, o]; cube.octant ¬ opp; root.corners[opp] ¬ cube.corners[opp]; <> root.kids[opp] ¬ cube; Apply[root, cubeProc]; root.level ¬ 0; TestCube[root]; }; { AddCubeToParent: PROC [parent: Cube, octant: Octant, size: REAL, level: INT] ~ { new: Cube¬ NEW[CubeRep ¬ [size: size, octant: octant, terminal: TRUE, parent: parent]]; c: Corner ¬ parent.corners[octant]; l, r: REAL ¬ parent.corners[octant].point.x; b, t: REAL ¬ parent.corners[octant].point.y; n, f: REAL ¬ parent.corners[octant].point.z; SELECT octant FROM lbn => {r ¬ l+size; t ¬ b+size; f ¬ n+size}; lbf => {r ¬ l+size; t ¬ b+size; n ¬ f-size}; ltn => {r ¬ l+size; b ¬ t-size; f ¬ n+size}; ltf => {r ¬ l+size; b ¬ t-size; n ¬ f-size}; rbn => {l ¬ r-size; t ¬ b+size; f ¬ n+size}; rbf => {l ¬ r-size; t ¬ b+size; n ¬ f-size}; rtn => {l ¬ r-size; b ¬ t-size; f ¬ n+size}; rtf => {l ¬ r-size; b ¬ t-size; n ¬ f-size}; ENDCASE; parent.kids[octant] ¬ new; new.level ¬ level; FOR face: Face IN Face DO neighbor: Cube ~ FaceNeighbor[new, face]; IF neighbor # NIL THEN { n: Corners ~ neighbor.corners; SELECT face FROM l => { new.corners[lbn] ¬ n[rbn]; new.corners[lbf] ¬ n[rbf]; new.corners[ltn] ¬ n[rtn]; new.corners[ltf] ¬ n[rtf]; }; r => { new.corners[rbn] ¬ n[lbn]; new.corners[rbf] ¬ n[lbf]; new.corners[rtn] ¬ n[ltn]; new.corners[rtf] ¬ n[ltf]; }; b => { new.corners[lbn] ¬ n[ltn]; new.corners[lbf] ¬ n[ltf]; new.corners[rbn] ¬ n[rtn]; new.corners[rbf] ¬ n[rtf]; }; t => { new.corners[ltn] ¬ n[lbn]; new.corners[ltf] ¬ n[lbf]; new.corners[rtn] ¬ n[rbn]; new.corners[rtf] ¬ n[rbf]; }; n => { new.corners[lbn] ¬ n[lbf]; new.corners[ltn] ¬ n[ltf]; new.corners[rbn] ¬ n[rbf]; new.corners[rtn] ¬ n[rtf]; }; ENDCASE => { new.corners[lbf] ¬ n[lbn]; new.corners[ltf] ¬ n[ltn]; new.corners[rbf] ¬ n[rbn]; new.corners[rtf] ¬ n[rtn]; }; }; ENDLOOP; new.corners[octant] ¬ parent.corners[octant]; FOR o: Octant IN Octant DO IF new.corners[o] = NIL THEN { new.corners[o] ¬ NEW[CornerRep]; new.corners[o].point ¬ SELECT o FROM lbn => [l, b, n], lbf => [l, b, f], ltn => [l, t, n], ltf => [l, t, f], rbn => [r, b, n], rbf => [r, b, f], rtn => [r, t, n], ENDCASE => [r, t, f]; }; ENDLOOP; TestCube[new]; }; parent: Cube ¬ cube.parent; info: FaceNeighborInfo ~ GetFaceNeighborInfo[cube, face]; IF info.recurse THEN { <> neighborOfParent: Cube ¬ FaceNeighbor[cube.parent, face]; IF neighborOfParent = NIL THEN root ¬ AddCube[cube.parent, face, fullyPointed]; IF (neighborOfParent ¬ FaceNeighbor[cube.parent, face]) = NIL THEN ERROR; parent ¬ neighborOfParent; }; parent.terminal ¬ FALSE; AddCubeToParent[parent, info.nOctant, cube.size, cube.level]; IF fullyPointed THEN FOR o: Octant IN Octant DO IF parent.kids[o] = NIL THEN AddCubeToParent[parent, o, cube.size, cube.level]; ENDLOOP; }; RETURN[Root[cube]]; }; DeleteCube: PUBLIC PROC [cube: Cube] ~ { IF cube # NIL AND cube.parent # NIL THEN cube.parent.kids[cube.octant] ¬ NIL; }; MakeRestrictedOctree: PUBLIC PROC [root: Cube] ~ { Inner: CubeProc ~ { Test: PROC [c: Cube] RETURNS [b: BOOL] ~ {b ¬ c # NIL AND NOT c.terminal}; IF cube # NIL THEN { neighbors: Neighborhood ¬ Neighbors[cube]; FOR face: Face IN Face DO neighbor: Cube ¬ neighbors[DirectionFromFace[face]]; IF neighbor # NIL AND NOT neighbor.terminal THEN { fourOcts: FourOctants ¬ FaceOctants[G3dOctree.OppositeFace[face]]; IF Test[neighbor.kids[fourOcts.o0]] THEN GOTO subdivide; IF Test[neighbor.kids[fourOcts.o1]] THEN GOTO subdivide; IF Test[neighbor.kids[fourOcts.o2]] THEN GOTO subdivide; IF Test[neighbor.kids[fourOcts.o3]] THEN GOTO subdivide; }; ENDLOOP; FOR edge: Edge IN Edge DO neighbor: Cube ¬ neighbors[DirectionFromEdge[edge]]; IF neighbor # NIL AND NOT neighbor.terminal THEN { twoOcts: TwoOctants ¬ EdgeOctants[DiagonalEdge[edge]]; IF Test[neighbor.kids[twoOcts.o0]] THEN GOTO subdivide; IF Test[neighbor.kids[twoOcts.o1]] THEN GOTO subdivide; }; ENDLOOP; FOR octant: Octant IN Octant DO neighbor: Cube ¬ neighbors[DirectionFromOctant[octant]]; IF neighbor # NIL AND NOT neighbor.terminal AND Test[neighbor.kids[G3dOctree.OppositeOctant[octant]]] THEN GOTO subdivide; ENDLOOP; }; EXITS subdivide => { Subdivide[cube]; IF cube.parent # NIL THEN { grandNeighbors: Neighborhood ¬ Neighbors[cube.parent]; threeEdges: ThreeEdges ¬ EdgesFromOctant[cube.octant]; threeDirections: ThreeDirections ¬ DirectionsFromOctant[cube.octant]; [] ¬ Inner[grandNeighbors[DirectionFromOctant[cube.octant]]]; [] ¬ Inner[grandNeighbors[DirectionFromEdge[threeEdges.e0]]]; [] ¬ Inner[grandNeighbors[DirectionFromEdge[threeEdges.e1]]]; [] ¬ Inner[grandNeighbors[DirectionFromEdge[threeEdges.e2]]]; [] ¬ Inner[grandNeighbors[threeDirections.d0]]; [] ¬ Inner[grandNeighbors[threeDirections.d1]]; [] ¬ Inner[grandNeighbors[threeDirections.d2]]; }; }; }; ApplyToTerminal[root, Inner]; }; <> PointInCube: PUBLIC PROC [point: Triple, cube: Cube, fudge: REAL ¬ 0.] RETURNS [BOOL] ~ { IF cube = NIL THEN RETURN[FALSE] ELSE { extra: REAL ~ fudge*Size[cube]; RETURN[ point.x >= cube.corners[lbn].point.x-extra AND point.x <= cube.corners[rbn].point.x+extra AND point.y >= cube.corners[lbn].point.y-extra AND point.y <= cube.corners[ltn].point.y+extra AND point.z >= cube.corners[lbn].point.z-extra AND point.z <= cube.corners[lbf].point.z+extra]; }; }; WhichCube: PUBLIC PROC [point: Triple, root: Cube] RETURNS [Cube] ~ { Test: PROC [cube: Cube] RETURNS [Cube] ~ { FOR octant: Octant IN Octant DO kid: Cube ¬ cube.kids[octant]; IF kid # NIL AND PointInCube[point, kid] THEN RETURN[Test[kid]]; ENDLOOP; RETURN[cube]; }; RETURN[IF PointInCube[point, root] THEN Test[root] ELSE NIL]; }; FirstKid: PUBLIC PROC [cube: Cube] RETURNS [Cube] ~ { IF cube = NIL THEN RETURN[NIL]; FOR o: Octant IN Octant DO IF cube.kids[o] # NIL THEN RETURN[cube.kids[o]]; ENDLOOP; RETURN[NIL]; }; FirstTerminalKid: PUBLIC PROC [cube: Cube] RETURNS [Cube] ~ { kid: Cube ¬ cube; WHILE (kid # NIL AND NOT kid.terminal) DO kid ¬ FirstKid[kid]; ENDLOOP; RETURN[kid]; }; <> Apply: PUBLIC PROC [cube: Cube, cubeProc: CubeProc] ~ { Inner: PROC [cube: Cube] ~ { IF cube # NIL AND cubeProc[cube] THEN FOR o: Octant IN Octant DO Inner[cube.kids[o]]; ENDLOOP; }; Inner[cube]; }; ApplyToKids: PUBLIC PROC [cube: Cube, cubeProc: CubeProc] ~ { Inner: PROC [cube: Cube] ~ { IF cube # NIL AND cubeProc[cube] THEN FOR o: Octant IN Octant DO Inner[cube.kids[o]]; ENDLOOP; }; IF cube # NIL THEN FOR o: Octant IN Octant DO Inner[cube.kids[o]]; ENDLOOP; }; ApplyToTerminal: PUBLIC PROC [cube: Cube, cubeProc: CubeProc] ~ { Inner: PROC [cube: Cube] ~ { IF cube = NIL THEN RETURN; IF cube.terminal THEN {IF NOT cubeProc[cube] THEN RETURN} ELSE FOR o: Octant IN Octant DO Inner[cube.kids[o]]; ENDLOOP; }; Inner[cube]; }; ApplyToLevel: PUBLIC PROC [cube: Cube, cubeProc: CubeProc, level: INT] ~ { Inner: PROC [cube: Cube] ~ { IF cube = NIL OR cube.level > level THEN RETURN; IF cube.level = level THEN {IF NOT cubeProc[cube] THEN RETURN} ELSE FOR o: Octant IN Octant DO Inner[cube.kids[o]]; ENDLOOP; }; Inner[cube]; }; ApplyToTerminalCorners: PUBLIC PROC [cube: Cube, cornerProc: CornerProc] ~ { abort: ERROR = CODE; cubeProc: CubeProc ~ { Test: PROC [d: Direction, o: Octant] ~ { IF neighbors[d] = NIL THEN IF NOT cornerProc[cube.corners[o]] THEN ERROR abort; }; neighbors: Neighborhood ~ Neighbors[cube]; IF NOT cornerProc[cube.corners[lbn]] THEN ERROR abort; Test[f, lbf]; Test[rf, rbf]; Test[r, rbn]; Test[t, ltn]; Test[tf, ltf]; Test[rtf, rtf]; Test[rt, rtn]; }; ApplyToTerminal[cube, cubeProc ! abort => CONTINUE]; }; ApplyToNonParentKidCorners: PUBLIC PROC [cube: Cube, cornerProc: CornerProc] ~ { Apply: PROC [d: Direction] RETURNS [BOOL] ~ {RETURN[cornerProc[directionCorners[d]]]}; directionCorners: DirectionCorners ~ GetDirectionCorners[cube]; IF NOT Apply[ln] THEN RETURN; -- midpoint of ln edge IF NOT Apply[lf] THEN RETURN; -- midpoint of lf edge IF NOT Apply[rn] THEN RETURN; -- midpoint of rn edge IF NOT Apply[rf] THEN RETURN; -- midpoint of rf edge IF NOT Apply[lb] THEN RETURN; -- midpoint of lb edge IF NOT Apply[lt] THEN RETURN; -- midpoint of lt edge IF NOT Apply[rb] THEN RETURN; -- midpoint of rb edge IF NOT Apply[rt] THEN RETURN; -- midpoint of rt edge IF NOT Apply[bn] THEN RETURN; -- midpoint of bn edge IF NOT Apply[tn] THEN RETURN; -- midpoint of tn edge IF NOT Apply[bf] THEN RETURN; -- midpoint of bf edge IF NOT Apply[tf] THEN RETURN; -- midpoint of tf edge IF NOT Apply[l] THEN RETURN; -- center of l face IF NOT Apply[r] THEN RETURN; -- center of r face IF NOT Apply[b] THEN RETURN; -- center of b face IF NOT Apply[t] THEN RETURN; -- center of t face IF NOT Apply[n] THEN RETURN; -- center of n face IF NOT Apply[f] THEN RETURN; -- center of f face IF NOT Apply[c] THEN RETURN; -- center }; <> Dir: TYPE ~ RECORD [x --(l-r)--, y --(b-t)--, z --(n-f)--: [-1..1], none: BOOL ¬ FALSE]; opposites: ARRAY Direction OF Direction ~ InitOpposites[]; sums: ARRAY Direction OF ARRAY Direction OF Direction ~ InitSums[]; fromOctants: ARRAY Octant OF ARRAY Octant OF Direction ~ InitFromOctants[]; decode: ARRAY Direction OF Dir ~ [ none: [0, 0, 0, TRUE], c: [0, 0, 0], l: [-1, 0, 0], r: [1, 0, 0], b: [0, -1, 0], t: [0, 1, 0], n: [0, 0, -1], f: [0, 0, 1], lb: [-1, -1, 0], lt: [-1, 1, 0], ln: [-1, 0, -1], lf: [-1, 0, 1], rb: [1, -1, 0], rt: [1, 1, 0], rn: [1, 0, -1], rf: [1, 0, 1], bn: [0, -1, -1], bf: [0, -1, 1], tn: [0, 1, -1], tf: [0, 1, 1], lbn: [-1, -1, -1], lbf: [-1, -1, 1], ltn: [-1, 1, -1], ltf: [-1, 1, 1], rbn: [1, -1, -1], rbf: [1, -1, 1], rtn: [1, 1, -1], rtf: [1, 1, 1] ]; Encode: PROC [x, y, z: INT] RETURNS [Direction] ~ { FOR d: Direction IN Direction DO dir: Dir ~ decode[d]; IF NOT dir.none AND dir.x = x AND dir.y = y AND dir.z = z THEN RETURN [d] ENDLOOP; RETURN [none] }; InitOpposites: PROC RETURNS [a: ARRAY Direction OF Direction] ~ INLINE { Opposite: PROC [d: Direction] RETURNS [Direction] ~ { a: Dir ~ decode[d]; RETURN [Encode[x: -a.x, y: -a.y, z: -a.z]] }; FOR d: Direction IN Direction DO a[d] ¬ Opposite[d]; ENDLOOP; }; InitSums: PROC RETURNS [a: ARRAY Direction OF ARRAY Direction OF Direction] ~ INLINE { Sum: PROC [d0, d1: Direction] RETURNS [Direction] ~ { d0Dir: Dir ~ decode[d0]; d1Dir: Dir ~ decode[d1]; RETURN[SELECT TRUE FROM d0Dir.none => d1, d1Dir.none => d0, ENDCASE => Encode[d0Dir.x+d1Dir.x, d0Dir.y+d1Dir.y, d0Dir.z+d1Dir.z]]; }; FOR d0: Direction IN Direction DO FOR d1: Direction IN Direction DO a[d0][d1] ¬ Sum[d0, d1]; ENDLOOP; ENDLOOP; }; InitFromOctants: PROC RETURNS [a: ARRAY Octant OF ARRAY Octant OF Direction]~INLINE { FromOctants: PROC [octant0, octant1: Octant] RETURNS [Direction] ~ { Dif: PROC [b0, b1: [-1..1]] RETURNS [[-1..1]] ~ { RETURN[SELECT b1 FROM b0 => 0, > b0 => 1, ENDCASE => -1]; }; d0: Dir ~ decode[DirectionFromOctant[octant0]]; d1: Dir ~ decode[DirectionFromOctant[octant1]]; RETURN[Encode[Dif[d0.x, d1.x], Dif[d0.y, d1.y], Dif[d0.z, d1.z]]]; }; FOR o0: Octant IN Octant DO FOR o1: Octant IN Octant DO a[o0][o1] ¬ FromOctants[o0, o1]; ENDLOOP; ENDLOOP; }; DirectionTypeFromDirection: PUBLIC PROC [direction: Direction] RETURNS [DirectionType] ~ { RETURN[SELECT direction FROM l, r, b, t, n, f => face, lb, lt, ln, lf, rb, rt, rn, rf, bn, bf, tn, tf => edge, lbn, lbf, ltn, ltf, rbn, rbf, rtn, rtf => corner, ENDCASE => none]; }; DirectionFromFace: PUBLIC PROC [face: Face] RETURNS [Direction] ~ { RETURN[SELECT face FROM l => l, r => r, b => b, t => t, n => n, f => f, ENDCASE => none]; }; DirectionFromEdge: PUBLIC PROC [edge: Edge] RETURNS [Direction] ~ { RETURN[SELECT edge FROM lb=>lb, lt=>lt, rb=>rb, rt=>rt, ln=>ln, lf=>lf, rn=>rn, rf=>rf, bn=>bn, bf=>bf, tn=>tn, tf=>tf, ENDCASE => none]; }; DirectionFromOctant: PUBLIC PROC [octant: Octant] RETURNS [Direction] ~ { RETURN[SELECT octant FROM lbn => lbn, lbf => lbf, ltn => ltn, ltf => ltf, rbn => rbn, rbf => rbf, rtn => rtn, rtf => rtf, ENDCASE => none]; }; DirectionFromOctants: PUBLIC PROC [octant0, octant1: Octant] RETURNS [Direction] ~ { RETURN[fromOctants[octant0][octant1]]; }; DirectionFromPoints: PUBLIC PROC [point0, point1: Triple] RETURNS [Direction] ~ { xSame: BOOL ~ point0.x = point1.x; ySame: BOOL ~ point0.y = point1.y; zSame: BOOL ~ point0.z = point1.z; IF xSame THEN { IF ySame THEN RETURN[IF point1.z > point0.z THEN f ELSE n]; IF zSame THEN RETURN[IF point1.y > point0.y THEN t ELSE b]; } ELSE IF ySame THEN { IF xSame THEN RETURN[IF point1.z > point0.z THEN f ELSE n]; IF zSame THEN RETURN[IF point1.x > point0.x THEN r ELSE l]; } ELSE IF zSame THEN { IF xSame THEN RETURN[IF point1.y > point0.y THEN t ELSE b]; IF ySame THEN RETURN[IF point1.x > point0.x THEN r ELSE l]; }; RETURN[none]; }; OppositeFace: PUBLIC PROC [face: Face] RETURNS [Face] ~ { RETURN[SELECT face FROM l => r, r => l, b => t, t => b, n => f, ENDCASE => n]; }; OppositeDirection: PUBLIC PROC [direction: Direction] RETURNS [Direction] ~ { RETURN[opposites[direction]]; }; AddDirection: PUBLIC PROC [d0, d1: Direction] RETURNS [Direction] ~ { RETURN[sums[d0][d1]]; }; EdgeFromDirection: PUBLIC PROC [d: Direction] RETURNS [Edge] ~ { RETURN[SELECT d FROM ln => ln, lf => lf, rn => rn, rf => rf, bn => bn, bf => bf, tn => tn, tf => tf, ENDCASE => ERROR]; }; <<>> OctantFromDirection: PUBLIC PROC [d: Direction] RETURNS [Octant] ~ { RETURN[SELECT d FROM lbn => lbn, lbf => lbf, ltn => ltn, ltf => ltf, rbn => rbn, rbf => rbf, rtn => rtn, rtf => rtf, ENDCASE => ERROR]; }; <<>> OctantFromThreeDirections: PUBLIC PROC [d0, d1, d2: Direction] RETURNS [o: Octant] ~ { o ¬ OctantFromDirection[AddDirection[AddDirection[d0, d1], d2]]; }; FaceFromDirection: PUBLIC PROC [d: Direction] RETURNS [Face] ~ { RETURN[SELECT d FROM l => l, r => r, b => b, t => t, n => n, f => f, ENDCASE => ERROR]; }; FaceDirections: PUBLIC PROC [face: Face] RETURNS [fd: FourDirections] ~ { fd ¬ SELECT face FROM l => [lb, lt, ln, lf], r => [rb, rt, rn, rf], b => [lb, rb, bn, bf], t => [lt, rt, tn, tf], n => [ln, rn, bn, tn], ENDCASE => [lf, rf, bf, tf]; }; NormalFromFace: PUBLIC PROC [face: Face] RETURNS [Triple] ~ { RETURN[SELECT face FROM l => [-1.0, 0.0, 0.0], r => [1.0, 0.0, 0.0], b => [0.0, -1.0, 0.0], t => [0.0, 1.0, 0.0], n => [0.0, 0.0, -1.0], ENDCASE => [0.0, 0.0, 1.0]]; }; <> PlaneFromCubeFace: PUBLIC PROC [cube: Cube, face: Face] RETURNS [p: Plane] ~ { o: Octant ¬ SELECT face FROM l=>lbn, r=>rbn, b=>lbn, t=>ltn, n=>lbn, ENDCASE=>lbf; p ¬ G3dPlane.FromPointAndNormal[cube.corners[o].point, NormalFromFace[face]]; }; EdgesFromOctant: PUBLIC PROC [octant: Octant] RETURNS [ThreeEdges] ~ { RETURN[SELECT octant FROM lbn => [lb, ln, bn], lbf => [lb, lf, bf], ltn => [lt, ln, tn], ltf => [lt, lf, tf], rbn => [rb, rn, bn], rbf => [rb, rf, bf], rtn => [rt, rn, tn], ENDCASE => [rt, rf, tf]]; }; <<>> DirectionsFromOctant: PUBLIC PROC [octant: Octant] RETURNS [ThreeDirections] ~ { RETURN[SELECT octant FROM lbn => [l, b, n], lbf => [l, b, f], ltn => [l, t, n], ltf => [l, t, f], rbn => [r, b, n], rbf => [r, b, f], rtn => [r, t, n], ENDCASE => [r, t, f]]; }; FaceFromEdgeOctant: PUBLIC PROC [edge: Edge, octant: Octant] RETURNS [Face] ~ { RETURN[SELECT edge FROM lb => IF octant = lbn THEN l ELSE b, lt => IF octant = ltn THEN t ELSE l, ln => IF octant = lbn THEN n ELSE l, lf => IF octant = lbf THEN l ELSE f, rb => IF octant = rbn THEN b ELSE r, rt => IF octant = rtn THEN r ELSE t, rn => IF octant = rbn THEN r ELSE n, rf => IF octant = rbf THEN f ELSE r, bn => IF octant = lbn THEN b ELSE n, bf => IF octant = lbf THEN f ELSE b, tn => IF octant = ltn THEN n ELSE t, ENDCASE => IF octant = ltf THEN t ELSE f]; }; NextCWEdge: PUBLIC 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]; }; NextCCWEdge: PUBLIC PROC [edge: Edge, face: Face] RETURNS [Edge] ~ { RETURN[SELECT edge FROM lb => IF face = l THEN ln ELSE bf, lt => IF face = l THEN lf ELSE tn, ln => IF face = l THEN lt ELSE bn, lf => IF face = l THEN lb ELSE tf, rb => IF face = r THEN rf ELSE bn, rt => IF face = r THEN rn ELSE tf, rn => IF face = r THEN rb ELSE tn, rf => IF face = r THEN rt ELSE bf, bn => IF face = b THEN lb ELSE rn, bf => IF face = b THEN rb ELSE lf, tn => IF face = t THEN rt ELSE ln, ENDCASE => IF face = t THEN lt ELSE rf]; }; EdgeDirections: PUBLIC PROC [edge: Edge] RETURNS [ThreeDirections] ~ { RETURN[SELECT edge FROM lb => [l, b, lb], lt => [l, t, lt], ln => [l, n, ln], lf => [l, f, lf], rb => [r, b, rb], rt => [r, t, rt], rn => [r, n, rn], rf => [r, f, rf], bn => [b, n, bn], bf => [b, f, bf], tn => [t, n, tn], tf => [t, f, tf], ENDCASE => [none, none, none]]; }; EdgeFaces: PUBLIC PROC [edge: Edge] RETURNS [TwoFaces] ~ { RETURN[SELECT edge FROM lb => [l, b], lt => [l, t], ln => [l, n], lf => [l, f], rb => [r, b], rt => [r, t], rn => [r, n], rf => [r, f], bn => [b, n], bf => [b, f], tn => [t, n], ENDCASE => [t, f]]; }; EdgeOctants: PUBLIC PROC [edge: Edge] RETURNS [TwoOctants] ~ { RETURN[SELECT edge FROM lb => [lbn, lbf], lt => [ltn, ltf], ln => [lbn, ltn], lf => [lbf, ltf], rb => [rbn, rbf], rt => [rtn, rtf], rn => [rbn, rtn], rf => [rbf, rtf], bn => [lbn, rbn], bf => [lbf, rbf], tn => [ltn, rtn], ENDCASE => [ltf, rtf]]; }; FaceOctants: PUBLIC PROC [face: Face] RETURNS [FourOctants] ~ { RETURN[SELECT face FROM l => [lbf, ltf, ltn, lbn], r => [rbn, rtn, rtf, rbf], b => [lbf, lbn, rbn, rbf], t => [ltn, ltf, rtf, rtn], n => [lbn, ltn, rtn, rbn], ENDCASE => [rbf, rtf, ltf, lbf]]; }; EdgeCorners: PUBLIC PROC [cube: Cube, edge: Edge] RETURNS [TwoCorners] ~ { octants: TwoOctants ~ EdgeOctants[edge]; RETURN[[cube.corners[octants.o0], cube.corners[octants.o1]]]; }; FaceCorners: PUBLIC PROC [cube: Cube, face: Face] RETURNS [FourCorners] ~ { o: FourOctants ~ FaceOctants[face]; RETURN[[cube.corners[o.o0], cube.corners[o.o1], cube.corners[o.o2], cube.corners[o.o3]]]; }; OppositeEdge: PUBLIC PROC [edge: Edge, face: Face] RETURNS [Edge] ~ { RETURN[SELECT edge FROM lb => IF face = l THEN lt ELSE rb, lt => IF face = l THEN lb ELSE rt, ln => IF face = l THEN lf ELSE rn, lf => IF face = l THEN ln ELSE rf, rb => IF face = r THEN rt ELSE lb, rt => IF face = r THEN rb ELSE lt, rn => IF face = r THEN rf ELSE ln, rf => IF face = r THEN rn ELSE lf, bn => IF face = b THEN bf ELSE tn, bf => IF face = b THEN bn ELSE tf, tn => IF face = t THEN tf ELSE bn, ENDCASE => IF face = t THEN tn ELSE bf]; }; InverseEdge: PUBLIC PROC [edge: Edge, face: Face] RETURNS [Edge] ~ { RETURN[SELECT edge FROM lb => IF face = l THEN rb ELSE lt, lt => IF face = l THEN rt ELSE lb, ln => IF face = l THEN rn ELSE lf, lf => IF face = l THEN rf ELSE ln, rb => IF face = r THEN lb ELSE rt, rt => IF face = r THEN lt ELSE rb, rn => IF face = r THEN ln ELSE rf, rf => IF face = r THEN lf ELSE rn, bn => IF face = b THEN tn ELSE bf, bf => IF face = b THEN tf ELSE bn, tn => IF face = t THEN bn ELSE tf, ENDCASE => IF face = t THEN bf ELSE tn]; }; DiagonalEdge: PUBLIC PROC [edge: Edge] RETURNS [Edge] ~ { RETURN[SELECT edge FROM lb => rt, lt => rb, ln => rf, lf => rn, rb => lt, rt => lb, rn => lf, rf => ln, bn => tf, bf => tn, tn => bf, ENDCASE => bn]; }; OtherFace: PUBLIC PROC [edge: Edge, face: Face] RETURNS [Face] ~ { twoFaces: TwoFaces ¬ EdgeFaces[edge]; RETURN[IF face = twoFaces.f0 THEN twoFaces.f1 ELSE twoFaces.f1]; }; AxisFromEdge: PUBLIC PROC [edge: Edge] RETURNS [Axis] ~ { RETURN[SELECT edge FROM bn, tn, bf, tf => x, ln, lf, rn, rf => y, ENDCASE => z]; }; <> GetFaceNeighborInfo: PROC [cube: Cube, face: Face] RETURNS [FaceNeighborInfo] ~ { octantWord: WORD ~ ORD[cube.octant]; faceBit: WORD ~ SELECT face FROM l, r => 4, b, t => 2, ENDCASE => 1; faceWord: WORD ~ Basics.BITAND[octantWord, faceBit]; RETURN[[ VAL[Basics.BITXOR[octantWord, faceBit]], SELECT face FROM l, b, n => faceWord = 0, ENDCASE => faceWord # 0 ]]; }; FaceNeighbor: PUBLIC PROC [cube: Cube, face: Face] RETURNS [Cube] ~ { IF cube = NIL OR cube.parent = NIL THEN RETURN[NIL] ELSE { faceNeighborInfo: FaceNeighborInfo ~ GetFaceNeighborInfo[cube, face]; IF faceNeighborInfo.recurse THEN { parentNeighbor: Cube ¬ FaceNeighbor[cube.parent, face]; IF parentNeighbor = NIL THEN RETURN[NIL] ELSE RETURN[parentNeighbor.kids[faceNeighborInfo.nOctant]]; } ELSE RETURN[cube.parent.kids[faceNeighborInfo.nOctant]]; }; }; EdgeNeighbors: PUBLIC PROC [cube: Cube, edge: Edge] RETURNS [cubes: ThreeCubes] ~ { f1, f2: Face; SELECT edge FROM lb => {f1 ¬ l; f2 ¬ b}; lt => {f1 ¬ l; f2 ¬ t}; rb => {f1 ¬ r; f2 ¬ b}; rt => {f1 ¬ r; f2 ¬ t}; ln => {f1 ¬ l; f2 ¬ n}; lf => {f1 ¬ l; f2 ¬ f}; rn => {f1 ¬ r; f2 ¬ n}; rf => {f1 ¬ r; f2 ¬ f}; bn => {f1 ¬ b; f2 ¬ n}; bf => {f1 ¬ b; f2 ¬ f}; tn => {f1 ¬ t; f2 ¬ n}; tf => {f1 ¬ t; f2 ¬ f}; ENDCASE; cubes.c0 ¬ FaceNeighbor[cube, f1]; cubes.c1 ¬ FaceNeighbor[cube, f2]; cubes.c2 ¬ FaceNeighbor[cubes.c0, f2]; }; Neighbor: PUBLIC PROC [cube: Cube, direction: Direction] RETURNS [x: Cube] ~ { IF cube = NIL THEN RETURN[NIL]; SELECT direction FROM <> l => x ¬ FaceNeighbor[cube, l]; r => x ¬ FaceNeighbor[cube, r]; b => x ¬ FaceNeighbor[cube, b]; t => x ¬ FaceNeighbor[cube, t]; n => x ¬ FaceNeighbor[cube, n]; f => x ¬ FaceNeighbor[cube, f]; <> lb => { l: Cube ¬ FaceNeighbor[cube, l]; x ¬ IF l # NIL THEN FaceNeighbor[l, b] ELSE FaceNeighbor[FaceNeighbor[cube, b], l]; }; lt => { l: Cube ¬ FaceNeighbor[cube, l]; x ¬ IF l # NIL THEN FaceNeighbor[l, t] ELSE FaceNeighbor[FaceNeighbor[cube, t], l]; }; ln => { l: Cube ¬ FaceNeighbor[cube, l]; x ¬ IF l # NIL THEN FaceNeighbor[l, n] ELSE FaceNeighbor[FaceNeighbor[cube, n], l]; }; lf => { l: Cube ¬ FaceNeighbor[cube, l]; x ¬ IF l # NIL THEN FaceNeighbor[l, f] ELSE FaceNeighbor[FaceNeighbor[cube, f], l]; }; rb => { r: Cube ¬ FaceNeighbor[cube, r]; x ¬ IF r # NIL THEN FaceNeighbor[r, b] ELSE FaceNeighbor[FaceNeighbor[cube, b], r]; }; rt => { r: Cube ¬ FaceNeighbor[cube, r]; x ¬ IF r # NIL THEN FaceNeighbor[r, t] ELSE FaceNeighbor[FaceNeighbor[cube, t], r]; }; rn => { r: Cube ¬ FaceNeighbor[cube, r]; x ¬ IF r # NIL THEN FaceNeighbor[r, n] ELSE FaceNeighbor[FaceNeighbor[cube, n], r]; }; rf => { r: Cube ¬ FaceNeighbor[cube, r]; x ¬ IF r # NIL THEN FaceNeighbor[r, f] ELSE FaceNeighbor[FaceNeighbor[cube, f], r]; }; bn => { b: Cube ¬ FaceNeighbor[cube, b]; x ¬ IF b # NIL THEN FaceNeighbor[b, n] ELSE FaceNeighbor[FaceNeighbor[cube, n],b]; }; bf => { b: Cube ¬ FaceNeighbor[cube, b]; x ¬ IF b # NIL THEN FaceNeighbor[b, f] ELSE FaceNeighbor[FaceNeighbor[cube, f],b ]; }; tn => { t: Cube ¬ FaceNeighbor[cube, t]; x ¬ IF t # NIL THEN FaceNeighbor[t, n] ELSE FaceNeighbor[FaceNeighbor[cube, n], t]; }; tf => { t: Cube ¬ FaceNeighbor[cube, t]; x ¬ IF t # NIL THEN FaceNeighbor[t, f] ELSE FaceNeighbor[FaceNeighbor[cube, f], t]; }; <> lbn => { temp: Cube ¬ Neighbor[cube, lb]; IF temp # NIL THEN RETURN[FaceNeighbor[temp, n]]; temp ¬ Neighbor[cube, ln]; IF temp # NIL THEN RETURN[FaceNeighbor[temp, b]]; RETURN[FaceNeighbor[Neighbor[cube, bn], l]]; }; lbf => { temp: Cube ¬ Neighbor[cube, lb]; IF temp # NIL THEN RETURN[FaceNeighbor[temp, f]]; temp ¬ Neighbor[cube, lf]; IF temp # NIL THEN RETURN[FaceNeighbor[temp, b]]; RETURN[FaceNeighbor[Neighbor[cube, bf], l]]; }; ltn => { temp: Cube ¬ Neighbor[cube, lt]; IF temp # NIL THEN RETURN[FaceNeighbor[temp, n]]; temp ¬ Neighbor[cube, ln]; IF temp # NIL THEN RETURN[FaceNeighbor[temp, t]]; RETURN[FaceNeighbor[Neighbor[cube, tn], l]]; }; ltf => { temp: Cube ¬ Neighbor[cube, lt]; IF temp # NIL THEN RETURN[FaceNeighbor[temp, f]]; temp ¬ Neighbor[cube, lf]; IF temp # NIL THEN RETURN[FaceNeighbor[temp, t]]; RETURN[FaceNeighbor[Neighbor[cube, tf], l]]; }; rbn => { temp: Cube ¬ Neighbor[cube, rb]; IF temp # NIL THEN RETURN[FaceNeighbor[temp, n]]; temp ¬ Neighbor[cube, rn]; IF temp # NIL THEN RETURN[FaceNeighbor[temp, b]]; RETURN[FaceNeighbor[Neighbor[cube, bn], r]]; }; rbf => { temp: Cube ¬ Neighbor[cube, rb]; IF temp # NIL THEN RETURN[FaceNeighbor[temp, f]]; temp ¬ Neighbor[cube, rf]; IF temp # NIL THEN RETURN[FaceNeighbor[temp, b]]; RETURN[FaceNeighbor[Neighbor[cube, bf], r]]; }; rtn => { temp: Cube ¬ Neighbor[cube, rt]; IF temp # NIL THEN RETURN[FaceNeighbor[temp, n]]; temp ¬ Neighbor[cube, rn]; IF temp # NIL THEN RETURN[FaceNeighbor[temp, t]]; RETURN[FaceNeighbor[Neighbor[cube, tn], r]]; }; rtf => { temp: Cube ¬ Neighbor[cube, rt]; IF temp # NIL THEN RETURN[FaceNeighbor[temp, f]]; temp ¬ Neighbor[cube, rf]; IF temp # NIL THEN RETURN[FaceNeighbor[temp, t]]; RETURN[FaceNeighbor[Neighbor[cube, tf], r]]; }; ENDCASE => x ¬ cube; }; Neighbors: PUBLIC PROC [cube: Cube] RETURNS [neighbors: Neighborhood] ~ { IF cube # NIL THEN FOR direction: Direction IN Direction DO neighbors[direction] ¬ Neighbor[cube, direction]; ENDLOOP; }; Move: PUBLIC PROC [cube: Cube, direction: Direction] RETURNS [Neighborhood] ~ { RETURN[Neighbors[Neighbor[cube, direction]]]; }; <> GetDirectionCorner: PUBLIC PROC [cube: Cube, d: Direction] RETURNS [c: Corner] ~ { c ¬ SELECT d FROM lbn => cube.corners[lbn], -- lbn corner lbf => cube.corners[lbf], -- lbf corner ltn => cube.corners[ltn], -- ltn corner ltf => cube.corners[ltf], -- ltf corner rbn => cube.corners[rbn], -- rbn corner rbf => cube.corners[rbf], -- rbf corner rtn => cube.corners[rtn], -- rtn corner rtf => cube.corners[rtf], -- rtf corner ln => cube.kids[ltn].corners[lbn], -- midpoint of ln edge lf => cube.kids[ltf].corners[lbf], -- midpoint of lf edge rn => cube.kids[rtn].corners[rbn], -- midpoint of rn edge rf => cube.kids[rtf].corners[rbf], -- midpoint of rf edge lt => cube.kids[ltn].corners[ltf], -- midpoint of lt edge lb => cube.kids[lbn].corners[lbf], -- midpoint of lb edge rt => cube.kids[rtn].corners[rtf], -- midpoint of rt edge rb => cube.kids[rbn].corners[rbf], -- midpoint of rb edge tn => cube.kids[ltn].corners[rtn], -- midpoint of tn edge bn => cube.kids[lbn].corners[rbn], -- midpoint of bn edge tf => cube.kids[ltf].corners[rtf], -- midpoint of tf edge bf => cube.kids[lbf].corners[rbf], -- midpoint of bf edge l => cube.kids[lbn].corners[ltf], -- center of l face r => cube.kids[rbn].corners[rtf], -- center of r face b => cube.kids[lbn].corners[rbf], -- center of b face t => cube.kids[ltn].corners[rtf], -- center of t face n => cube.kids[lbn].corners[rtn], -- center of n face f => cube.kids[lbf].corners[rtf], -- center of f face c => cube.kids[lbn].corners[rtf], -- center of cube ENDCASE => NIL; }; GetDirectionCorners: PUBLIC PROC [cube: Cube] RETURNS [directionCorners: DirectionCorners] ~ { directionCorners[lbn] ¬ cube.corners[lbn]; -- lbn corner directionCorners[lbf] ¬ cube.corners[lbf]; -- lbf corner directionCorners[ltn] ¬ cube.corners[ltn]; -- ltn corner directionCorners[ltf] ¬ cube.corners[ltf]; -- ltf corner directionCorners[rbn] ¬ cube.corners[rbn]; -- rbn corner directionCorners[rbf] ¬ cube.corners[rbf]; -- rbf corner directionCorners[rtn] ¬ cube.corners[rtn]; -- rtn corner directionCorners[rtf] ¬ cube.corners[rtf]; -- rtf corner directionCorners[ln] ¬ cube.kids[ltn].corners[lbn]; -- midpoint of ln edge directionCorners[lf] ¬ cube.kids[ltf].corners[lbf]; -- midpoint of lf edge directionCorners[rn] ¬ cube.kids[rtn].corners[rbn]; -- midpoint of rn edge directionCorners[rf] ¬ cube.kids[rtf].corners[rbf]; -- midpoint of rf edge directionCorners[lt] ¬ cube.kids[ltn].corners[ltf]; -- midpoint of lt edge directionCorners[lb] ¬ cube.kids[lbn].corners[lbf]; -- midpoint of lb edge directionCorners[rt] ¬ cube.kids[rtn].corners[rtf]; -- midpoint of rt edge directionCorners[rb] ¬ cube.kids[rbn].corners[rbf]; -- midpoint of rb edge directionCorners[tn] ¬ cube.kids[ltn].corners[rtn]; -- midpoint of tn edge directionCorners[bn] ¬ cube.kids[lbn].corners[rbn]; -- midpoint of bn edge directionCorners[tf] ¬ cube.kids[ltf].corners[rtf]; -- midpoint of tf edge directionCorners[bf] ¬ cube.kids[lbf].corners[rbf]; -- midpoint of bf edge directionCorners[l] ¬ cube.kids[lbn].corners[ltf]; -- center of l face directionCorners[r] ¬ cube.kids[rbn].corners[rtf]; -- center of r face directionCorners[b] ¬ cube.kids[lbn].corners[rbf]; -- center of b face directionCorners[t] ¬ cube.kids[ltn].corners[rtf]; -- center of t face directionCorners[n] ¬ cube.kids[lbn].corners[rtn]; -- center of n face directionCorners[f] ¬ cube.kids[lbf].corners[rtf]; -- center of f face directionCorners[c] ¬ cube.kids[lbn].corners[rtf]; -- center of cube }; <> SetTerminalField: PUBLIC PROC [root: Cube] ~ { cubeProc: CubeProc ~ { FOR o: Octant IN Octant DO IF cube.kids[o] # NIL THEN { cube.terminal ¬ FALSE; RETURN; }; ENDLOOP; cube.terminal ¬ TRUE; }; Apply[root, cubeProc]; }; SetLevels: PUBLIC PROC [root: Cube] ~ { SetKidsLevel: PROC [cube: Cube, level: INT] ~ { nextLevel: INT ~ level+1; FOR o: Octant IN Octant DO kid: Cube ¬ cube.kids[o]; IF kid = NIL THEN LOOP; kid.level ¬ level; SetKidsLevel[kid, nextLevel]; ENDLOOP; }; root.level ¬ 0; SetKidsLevel[root, 1]; }; GetClientData: PUBLIC PROC [cube: Cube] RETURNS [REF ANY] ~ { RETURN [cube.refAny]; }; SetClientData: PUBLIC PROC [cube: Cube, r: REF ANY] ~ { IF cube # NIL THEN cube.refAny ¬ r; }; <> <> <> <> <> ObjectTrack: TYPE ~ REF ObjectTrackRep; ObjectTrackRep: TYPE ~ RECORD [objects: LIST OF CubeObject _ NIL]; <> <> <> <> <<};>> FromObjects: PUBLIC PROC [objs: LIST OF CubeObject] RETURNS [Cube] ~ { bbox: Box ¬ objs.first.bbox; size: REAL; ans: Cube ¬ NIL; track: ObjectTrack ¬ NEW[ObjectTrackRep]; <> FOR l: LIST OF CubeObject _ objs, l.rest WHILE l # NIL DO bbox ¬ G3dBox.BoxUnionBox[bbox, l.first.bbox]; ENDLOOP; <> size ¬ MAX[bbox.max.x - bbox.min.x, bbox.max.y - bbox.min.y]; size ¬ MAX[size, bbox.max.z - bbox.min.z]; ans ¬ NewCube[size, G3dBox.BoxCenter[bbox]]; track.objects ¬ objs; SetClientData[ans, track]; RETURN[ans]; }; AddObject: PUBLIC PROC [cube: Cube, obj: CubeObject] ~ { track: ObjectTrack ¬ NIL; intersection: Box; hasIntersection: BOOL; <> IF cube = NIL OR obj = NIL THEN RETURN; IF cube.refAny = NIL AND cube.terminal THEN { <> track ¬ NEW[ObjectTrackRep]; track.objects ¬ NIL; cube.refAny ¬ track; }; IF NOT ISTYPE[cube.refAny,ObjectTrack] THEN RETURN; track ¬ NARROW[cube.refAny]; <> [hasIntersection, intersection] ¬ G3dBox.Intersect[BoxOfCube[cube], obj.bbox]; IF NOT hasIntersection THEN RETURN; IF cube.terminal THEN { <> myCo: CubeObject ¬ NEW[G3dOctree.CubeObjectRep ¬ [val: obj.val, bbox: intersection, query: obj.query]]; track.objects ¬ CONS[myCo, track.objects]; } ELSE { <> <> FOR octant: Octant IN Octant DO AddObject[cube.kids[octant], obj]; ENDLOOP; }; }; SubdivideGivenObjects: PUBLIC PROC [cube: Cube, maxPerCube: INT, maxDepth: INT] ~ { <> <<>> track: ObjectTrack; IF cube = NIL OR cube.refAny = NIL OR NOT ISTYPE[cube.refAny,ObjectTrack] THEN RETURN; track ¬ NARROW[cube.refAny]; IF cube.level >= maxDepth THEN RETURN; IF cube.terminal THEN { nObjs: INT ¬ 0; FOR l: LIST OF CubeObject _ track.objects, l.rest WHILE l # NIL DO nObjs ¬ nObjs + 1; ENDLOOP; IF nObjs <= maxPerCube THEN { RETURN; }; <> <> Subdivide[cube]; FOR l: LIST OF CubeObject _ track.objects, l.rest WHILE l # NIL DO FOR octant: Octant IN Octant DO AddObject[cube.kids[octant], l.first]; ENDLOOP; ENDLOOP; track.objects ¬ NIL; <> }; <> FOR octant: Octant IN Octant DO SubdivideGivenObjects[cube.kids[octant], maxPerCube, maxDepth]; ENDLOOP; }; QueryObjects: PUBLIC PROC [cube: Cube, pt: Triple] RETURNS [obj: REF ANY] ~ { track: ObjectTrack; IF cube = NIL THEN RETURN [NIL]; <> <> IF cube.refAny = NIL THEN RETURN [NIL]; IF NOT ISTYPE[cube.refAny,ObjectTrack] THEN { RETURN [NIL]; }; track ¬ NARROW[cube.refAny]; IF NOT G3dBox.PointInBox[BoxOfCube[cube], pt] THEN { RETURN [NIL]; }; IF cube.terminal THEN { FOR l: LIST OF CubeObject _ track.objects, l.rest WHILE l # NIL DO co: CubeObject ¬ l.first; IF G3dBox.PointInBox[co.bbox,pt] THEN { IF co.query[co.val, pt] THEN RETURN [co.val]; }; ENDLOOP; } ELSE { <
> d0, d1, d2: Direction; centerPt: Triple ¬ Center[cube]; octant: Octant; val: REF ANY; d0 ¬ IF pt.x > centerPt.x THEN r ELSE l; d1 ¬ IF pt.y > centerPt.y THEN t ELSE b; d2 ¬ IF pt.z > centerPt.z THEN f ELSE n; octant ¬ OctantFromThreeDirections[d0,d1,d2]; val ¬ QueryObjects[cube.kids[octant], pt]; IF val # NIL THEN RETURN [val]; }; RETURN [NIL]; }; <> SetOctreeFields: PUBLIC PROC [octree: Octree] ~ { IF octree = NIL OR octree.root = NIL THEN RETURN; octree.depth ¬ DepthOf[octree.root]; octree.radius ¬ 0.5*(octree.diameter ¬ Size[octree.root]); octree.terminalRadius ¬ 0.5*(octree.terminalDiameter ¬ MinSize[octree.root]); octree.nTerminalCubes ¬ NTerminalCubes[octree.root]; octree.nCubes ¬ NCubes[octree.root]; }; <> WriteCubesToFile: PUBLIC PROC [fileName: ROPE, cube: Cube, miscInfo: ROPE ¬ NIL] ~ { IF cube # NIL AND fileName # NIL THEN { Inner: PROC [cube: Cube] ~ { AddChar: PROC [c: CHAR] ~ INLINE { text[text.length] ¬ c; text.length ¬ text.length+1; }; text.length ¬ 6; FOR n: INT IN [0..cube.level) DO AddChar['\t]; ENDLOOP; FOR o: Octant IN Octant DO AddChar[IF cube.kids[o] = NIL THEN '0 ELSE '1]; ENDLOOP; AddChar['\n]; IO.PutText[out, text]; FOR o: Octant IN Octant DO IF cube.kids[o] # NIL THEN Inner[cube.kids[o]]; ENDLOOP; }; text: REF TEXT ¬ RefText.ObtainScratch[100]; out: STREAM ¬ FS.StreamOpen[fileName, $create]; center: Triple ¬ Center[cube]; IO.PutFL[out, "Volume~ size: %g\tcenter: %g\t%g\t%g\n\n", LIST[IO.real[cube.size], IO.real[center.x], IO.real[center.y], IO.real[center.z]]]; IF miscInfo # NIL THEN IO.PutF1[out, "MiscInfo~ %g\n\n", IO.rope[miscInfo]]; text ¬ RefText.AppendRope[text, "cube:\t"]; Inner[cube]; IO.Close[out]; RefText.ReleaseScratch[text]; }; }; ReadCubesFromFile: PUBLIC PROC [fileName: ROPE] RETURNS [root: Cube] ~ { IF fileName # NIL THEN { InnerReadCube: PROC [parent: Cube] ~ { CubeLineOK: PROC RETURNS [ok: BOOL ¬ TRUE] ~ { DO text ¬ IO.GetLine[in, text ! IO.EndOfStream => GOTO eof]; IF Rope.Run[id, 0, RefText.TrustTextAsRope[text], 0, TRUE] = 6 THEN RETURN; REPEAT eof => ok ¬ FALSE; ENDLOOP; }; IF CubeLineOK[] THEN { n: INT ¬ 6; WHILE text[n] = '\t DO n ¬ n+1; ENDLOOP; FOR nn: INT IN [n..n+8) DO IF text[nn] = '1 THEN EXIT; REPEAT FINISHED => RETURN; ENDLOOP; Subdivide[parent]; FOR o: Octant IN Octant DO IF text[n] = '0 THEN parent.kids[o] ¬ NIL; n ¬ n+1; ENDLOOP; FOR o: Octant IN Octant DO kid: Cube ¬ parent.kids[o]; IF kid # NIL THEN InnerReadCube[kid]; ENDLOOP; }; }; in: STREAM ¬ FS.StreamOpen[FileNames.ResolveRelativePath[fileName] ! FS.Error => GOTO noOpen]; text: REF TEXT ¬ RefText.ObtainScratch[100]; id: ROPE ¬ "cube:\t"; line: G3dIO.Line ¬ G3dIO.FindKeyWord[in, "Octree",,, G3dIO.ObtainLine[]]; skip1: ROPE ¬ G3dIO.GetWord[line]; size: REAL ¬ G3dIO.GetReal[line]; skip2: ROPE ¬ G3dIO.GetWord[line]; center: Triple ¬ G3dIO.GetTriple[line]; root ¬ NewCube[size, center]; InnerReadCube[root]; G3dIO.ReleaseLine[line]; RefText.ReleaseScratch[text]; IO.Close[in]; EXITS noOpen => RETURN; }; }; <> AddToCubeSequence: PUBLIC PROC [cube: Cube, cubes: CubeSequence ¬ NIL] RETURNS [CubeSequence] ~ { IF cubes = NIL THEN cubes ¬ NEW[CubeSequenceRep[10]]; IF cubes.length = cubes.maxLength THEN cubes ¬ LengthenCubeSequence[cubes]; cubes[cubes.length] ¬ cube; cubes.length ¬ cubes.length+1; RETURN[cubes]; }; LengthenCubeSequence: PUBLIC PROC [cubes: CubeSequence, amount: REAL ¬ 1.3] RETURNS [new: CubeSequence] ~ { new ¬ NEW[CubeSequenceRep[Real.Round[1.3*cubes.length]]]; FOR i: INT IN [0..cubes.length) DO new[i] ¬ cubes[i]; ENDLOOP; new.length ¬ cubes.length; }; <> StackOverflow: PUBLIC ERROR = CODE; StackUnderflow: PUBLIC ERROR = CODE; NewCubeStack: PUBLIC PROC [length: INT] RETURNS [CubeStack] ~ { RETURN[NEW[CubeStackRep[length]]]; }; WriteBottomOfCubeStack: PUBLIC PROC [cube: Cube, cubeStack: CubeStack] ~ { newBottom: INT ~ (cubeStack.bottom+1) MOD cubeStack.maxLength; cubeStack[cubeStack.bottom] ¬ cube; IF newBottom = cubeStack.top THEN ERROR StackOverflow; cubeStack.bottom ¬ newBottom; cubeStack.size ¬ cubeStack.size+1; cubeStack.maxSize ¬ MAX[cubeStack.maxSize, cubeStack.size]; }; ReadTopOfCubeStack: PUBLIC PROC [cubeStack: CubeStack] RETURNS [cube: Cube] ~ { IF cubeStack.bottom = cubeStack.top THEN ERROR StackUnderflow; cube ¬ cubeStack[cubeStack.top]; cubeStack.top ¬ (cubeStack.top+1) MOD cubeStack.maxLength; cubeStack.size ¬ cubeStack.size-1; }; CubeStackSize: PUBLIC PROC [cubeStack: CubeStack] RETURNS [INT] ~ { RETURN[cubeStack.size]; < cubeStack.bottom>> <> <> }; MaxCubeStackSize: PUBLIC PROC [cubeStack: CubeStack] RETURNS [INT] ~ { RETURN[cubeStack.maxSize]; }; CubeStackEmpty: PUBLIC PROC [cubeStack: CubeStack] RETURNS [BOOL] ~ { RETURN[cubeStack.bottom = cubeStack.top]; }; LengthenCubeStack: PUBLIC PROC [cubeStack: CubeStack, amount: REAL ¬ 1.3] RETURNS [newStack: CubeStack] ~ { newStack ¬ NEW[CubeStackRep[Real.Round[amount*cubeStack.maxLength]]]; FOR i: INT IN [0..cubeStack.maxLength) DO newStack[i] ¬ cubeStack[i]; ENDLOOP; newStack.top ¬ cubeStack.top; newStack.bottom ¬ cubeStack.bottom; newStack.size ¬ cubeStack.size; }; <> nScratchCrossSequences: INT ~ 6; scratchCrossSequences: ARRAY [0..nScratchCrossSequences) OF CrossSequence ¬ ALL[NIL]; ObtainCrossSequence: PUBLIC ENTRY PROC RETURNS [crossSequence: CrossSequence] ~ { FOR i: INT IN [0..nScratchCrossSequences) DO crossSequence ¬ scratchCrossSequences[i]; IF crossSequence = NIL THEN LOOP; crossSequence.length ¬ 0; scratchCrossSequences[i] ¬ NIL; RETURN; ENDLOOP; crossSequence ¬ NEW[CrossSequenceRep[100]]; }; ReleaseCrossSequence: PUBLIC ENTRY PROC [crossSequence: CrossSequence] ~ { FOR i: INT IN [0..nScratchCrossSequences) DO IF scratchCrossSequences[i] # NIL THEN LOOP; scratchCrossSequences[i] ¬ crossSequence; RETURN; ENDLOOP; }; <> NoSuchOctant: PUBLIC ERROR [msg: ROPE] ~ CODE; NoSuchFace: PUBLIC ERROR [msg: ROPE] ~ CODE; RopeFromOctant: PUBLIC PROC [octant: Octant] RETURNS [ROPE] ~ { RETURN[Rope.Substr[IO.PutFR1["%g", IO.refAny[NEW[Octant ¬ octant]]], 1]]; }; RopeFromFace: PUBLIC PROC [face: Face] RETURNS [ROPE] ~ { RETURN[Rope.Substr[IO.PutFR1["%g", IO.refAny[NEW[Face ¬ face]]], 1]]; }; <<>> RopeFromDirection: PUBLIC PROC [direction: Direction] RETURNS [ROPE] ~ { RETURN[Rope.Substr[IO.PutFR1["%g", IO.refAny[NEW[Direction ¬ direction]]], 1]]; }; RopeFromDirectionSelect: PUBLIC PROC [directionSelect: DirectionSelect] RETURNS [ROPE] ~ { RETURN[SELECT directionSelect FROM x=>"x", y=>"y", z=>"z", xy=>"xy", xz=>"xz", yz=>"yz", xyz=>"xyz", ENDCASE=>"none"]; }; OctantFromRope: PUBLIC PROC [rope: ROPE] RETURNS [Octant] ~ { FOR o: Octant IN Octant DO IF Equal[RopeFromOctant[o], rope] THEN RETURN[o]; ENDLOOP; ERROR NoSuchOctant[rope]; }; FaceFromRope: PUBLIC PROC [rope: ROPE] RETURNS [Face] ~ { FOR face: Face IN Face DO IF Equal[RopeFromFace[face], rope] THEN RETURN[face]; ENDLOOP; ERROR NoSuchFace[rope]; }; DirectionFromRope: PUBLIC PROC [rope: ROPE] RETURNS [Direction] ~ { FOR d: Direction IN Direction DO IF Equal[RopeFromDirection[d], rope] THEN RETURN[d]; ENDLOOP; RETURN[none]; }; RopeFromRefAny: PUBLIC PROC [ref: REF ANY] RETURNS [rope: ROPE] ~ { ioRope: ROPE ~ IO.PutFR1["%g", IO.refAny[ref]]; text: REF TEXT ~ RefText.AppendRope[RefText.ObtainScratch[90], ioRope, 1]; IF text[0] IN ['a..'z] THEN text[0] ¬ text[0]-('a-'A); rope ¬ Rope.FromRefText[text]; RefText.ReleaseScratch[text]; }; Equal: PROC [r1, r2: ROPE] RETURNS [BOOL] ~ {RETURN[Rope.Equal[r1, r2, FALSE]]}; END.