<<>> <> <> <> DIRECTORY CedarProcess, Draw2d, G3dBasic, G3dCubeDraw, G3dMatrix, G3dOctree, G3dVector, Imager, ImagerPath, Process, Rope; G3dCubeDrawImpl: CEDAR PROGRAM IMPORTS CedarProcess, Draw2d, G3dMatrix, G3dOctree, G3dVector, Imager, ImagerPath, Process EXPORTS G3dCubeDraw ~ BEGIN DrawType: TYPE ~ Draw2d.DrawType; Pair: TYPE ~ G3dBasic.Pair; Quad: TYPE ~ G3dBasic.Quad; Triple: TYPE ~ G3dBasic.Triple; Matrix: TYPE ~ G3dMatrix.Matrix; Viewport: TYPE ~ G3dMatrix.Viewport; Corner: TYPE ~ G3dOctree.Corner; Corners: TYPE ~ G3dOctree.Corners; Cube: TYPE ~ G3dOctree.Cube; CubeProc: TYPE ~ G3dOctree.CubeProc; Direction: TYPE ~ G3dOctree.Direction; DirectionPairs: TYPE ~ G3dOctree.DirectionPairs; Face: TYPE ~ G3dOctree.Face; Neighborhood: TYPE ~ G3dOctree.Neighborhood; Octant: TYPE ~ G3dOctree.Octant; OctantPairs: TYPE ~ G3dOctree.OctantPairs; ThreeEdges: TYPE ~ G3dOctree.ThreeEdges; FourCorners: TYPE ~ G3dOctree.FourCorners; Context: TYPE ~ Imager.Context; ROPE: TYPE ~ Rope.ROPE; <> TransformDirections: PUBLIC PROC [cube: Cube, view: Matrix, viewport: Viewport ¬ []] RETURNS [pairs: DirectionPairs] ~ { VP: PROC [p: Pair] RETURNS [x: Pair] ~ { x ¬ [viewport.scale.x*p.x+viewport.translate.x, viewport.scale.y*p.y+viewport.translate.y]; }; d: REAL ~ 0.5*G3dOctree.Size[cube]; c: Triple ~ G3dOctree.Center[cube]; pairs[c] ¬ VP[G3dMatrix.TransformD[c, view]]; pairs[lbn] ¬ VP[G3dMatrix.TransformD[cube.corners[lbn].point, view]]; pairs[lbf] ¬ VP[G3dMatrix.TransformD[cube.corners[lbf].point, view]]; pairs[ltn] ¬ VP[G3dMatrix.TransformD[cube.corners[ltn].point, view]]; pairs[ltf] ¬ VP[G3dMatrix.TransformD[cube.corners[ltf].point, view]]; pairs[rbn] ¬ VP[G3dMatrix.TransformD[cube.corners[rbn].point, view]]; pairs[rbf] ¬ VP[G3dMatrix.TransformD[cube.corners[rbf].point, view]]; pairs[rtn] ¬ VP[G3dMatrix.TransformD[cube.corners[rtn].point, view]]; pairs[rtf] ¬ VP[G3dMatrix.TransformD[cube.corners[rtf].point, view]]; pairs[lb] ¬ VP[G3dMatrix.TransformD[[c.x-d, c.y-d, c.z], view]]; pairs[lt] ¬ VP[G3dMatrix.TransformD[[c.x-d, c.y+d, c.z], view]]; pairs[ln] ¬ VP[G3dMatrix.TransformD[[c.x-d, c.y, c.z-d], view]]; pairs[lf] ¬ VP[G3dMatrix.TransformD[[c.x-d, c.y, c.z+d], view]]; pairs[rb] ¬ VP[G3dMatrix.TransformD[[c.x+d, c.y-d, c.z], view]]; pairs[rt] ¬ VP[G3dMatrix.TransformD[[c.x+d, c.y+d, c.z], view]]; pairs[rn] ¬ VP[G3dMatrix.TransformD[[c.x+d, c.y, c.z-d], view]]; pairs[rf] ¬ VP[G3dMatrix.TransformD[[c.x+d, c.y, c.z+d], view]]; pairs[bn] ¬ VP[G3dMatrix.TransformD[[c.x, c.y-d, c.z-d], view]]; pairs[bf] ¬ VP[G3dMatrix.TransformD[[c.x, c.y-d, c.z+d], view]]; pairs[tn] ¬ VP[G3dMatrix.TransformD[[c.x, c.y+d, c.z-d], view]]; pairs[tf] ¬ VP[G3dMatrix.TransformD[[c.x, c.y+d, c.z+d], view]]; pairs[l] ¬ VP[G3dMatrix.TransformD[[c.x-d, c.y, c.z], view]]; pairs[r] ¬ VP[G3dMatrix.TransformD[[c.x+d, c.y, c.z], view]]; pairs[b] ¬ VP[G3dMatrix.TransformD[[c.x, c.y-d, c.z], view]]; pairs[t] ¬ VP[G3dMatrix.TransformD[[c.x, c.y+d, c.z], view]]; pairs[n] ¬ VP[G3dMatrix.TransformD[[c.x, c.y, c.z-d], view]]; pairs[f] ¬ VP[G3dMatrix.TransformD[[c.x, c.y, c.z+d], view]]; }; TransformOctants: PUBLIC PROC [cube: Cube, view: Matrix, viewport: Viewport ¬ []] RETURNS [pairs: OctantPairs] ~ { VP: PROC [p: Pair] RETURNS [x: Pair] ~ { x ¬ [viewport.scale.x*p.x+viewport.translate.x, viewport.scale.y*p.y+viewport.translate.y]; }; pairs[lbn] ¬ VP[G3dMatrix.TransformD[cube.corners[lbn].point, view]]; pairs[lbf] ¬ VP[G3dMatrix.TransformD[cube.corners[lbf].point, view]]; pairs[ltn] ¬ VP[G3dMatrix.TransformD[cube.corners[ltn].point, view]]; pairs[ltf] ¬ VP[G3dMatrix.TransformD[cube.corners[ltf].point, view]]; pairs[rbn] ¬ VP[G3dMatrix.TransformD[cube.corners[rbn].point, view]]; pairs[rbf] ¬ VP[G3dMatrix.TransformD[cube.corners[rbf].point, view]]; pairs[rtn] ¬ VP[G3dMatrix.TransformD[cube.corners[rtn].point, view]]; pairs[rtf] ¬ VP[G3dMatrix.TransformD[cube.corners[rtf].point, view]]; }; <> TerminalCubes: PUBLIC PROC [ context: Context, cube: Cube, view: Matrix, viewport: Viewport ¬ [], drawType: DrawType ¬ dashed] ~ { cubeProc: CubeProc ~ { Process.CheckForAbort[]; SimpleCube[context, cube, view, viewport, TRUE, drawType]; }; G3dOctree.ApplyToTerminal[cube, cubeProc]; }; <<>> RecursiveCubes: PUBLIC PROC [ context: Context, cube: Cube, view: Matrix, viewport: Viewport ¬ [], drawType: DrawType ¬ dashed, terminalCenters: BOOL ¬ TRUE] ~ { cubeProc: CubeProc ~ { Process.CheckForAbort[]; IF cube.terminal AND terminalCenters THEN CubeCenter[context, cube, view, viewport] ELSE SimpleCube[context, cube, view, viewport, TRUE, drawType]; }; G3dOctree.Apply[cube, cubeProc]; }; SimpleCube: PUBLIC PROC [ context: Context, cube: Cube, view: Matrix, viewport: Viewport ¬ [], optimize: BOOL ¬ FALSE, drawType: DrawType ¬ dashed] ~ { IF cube # NIL AND view # NIL THEN SimpleCubeFromPairs[ context, TransformOctants[cube, view], IF optimize THEN cube ELSE NIL, drawType]; }; SimpleCubeFromPairs: PUBLIC PROC [ context: Context, pairs: OctantPairs, cube: Cube ¬ NIL, drawType: DrawType ¬ dashed] ~ { IF cube # NIL THEN { l: Cube ~ G3dOctree.FaceNeighbor[cube, l]; r: Cube ~ G3dOctree.FaceNeighbor[cube, r]; b: Cube ~ G3dOctree.FaceNeighbor[cube, b]; t: Cube ~ G3dOctree.FaceNeighbor[cube, t]; n: Cube ~ G3dOctree.FaceNeighbor[cube, n]; f: Cube ~ G3dOctree.FaceNeighbor[cube, f]; rf: Cube ~ IF r # NIL THEN G3dOctree.FaceNeighbor[r, f] ELSE G3dOctree.FaceNeighbor[f, r]; rt: Cube ~ IF r # NIL THEN G3dOctree.FaceNeighbor[r, t] ELSE G3dOctree.FaceNeighbor[t, r]; tf: Cube ~ IF t # NIL THEN G3dOctree.FaceNeighbor[t, f] ELSE G3dOctree.FaceNeighbor[f, t]; Draw2d.Line[context, pairs[lbn], pairs[lbf], drawType]; -- lb Draw2d.Line[context, pairs[lbn], pairs[ltn], drawType]; -- ln Draw2d.Line[context, pairs[lbn], pairs[rbn], drawType]; -- bn IF rf=NIL THEN Draw2d.Line[context, pairs[rbf], pairs[rtf], drawType]; -- rf IF rt=NIL THEN Draw2d.Line[context, pairs[rtn], pairs[rtf], drawType]; -- rt IF tf=NIL THEN Draw2d.Line[context, pairs[ltf], pairs[rtf], drawType]; -- tf IF l=NIL AND t=NIL THEN Draw2d.Line[context, pairs[ltn], pairs[ltf], drawType]; -- lt IF l=NIL AND f=NIL THEN Draw2d.Line[context, pairs[lbf], pairs[ltf], drawType]; -- lf IF t=NIL AND n=NIL THEN Draw2d.Line[context, pairs[ltn], pairs[rtn], drawType];-- tn IF b=NIL AND f=NIL THEN Draw2d.Line[context, pairs[lbf], pairs[rbf], drawType];-- bf IF r=NIL AND n=NIL THEN Draw2d.Line[context, pairs[rbn], pairs[rtn], drawType];--rn IF r=NIL AND b=NIL THEN Draw2d.Line[context, pairs[rbn], pairs[rbf], drawType];--rb } ELSE { Draw2d.Line[context, pairs[lbn], pairs[lbf], drawType]; -- lb edge Draw2d.Line[context, pairs[ltn], pairs[ltf], drawType]; -- lt edge Draw2d.Line[context, pairs[lbn], pairs[ltn], drawType]; -- ln edge Draw2d.Line[context, pairs[lbf], pairs[ltf], drawType]; -- lf edge Draw2d.Line[context, pairs[lbn], pairs[rbn], drawType]; -- bn edge Draw2d.Line[context, pairs[ltn], pairs[rtn], drawType]; -- tn edge Draw2d.Line[context, pairs[lbf], pairs[rbf], drawType]; -- bf edge Draw2d.Line[context, pairs[ltf], pairs[rtf], drawType]; -- tf edge Draw2d.Line[context, pairs[rbn], pairs[rtn], drawType]; -- rn edge Draw2d.Line[context, pairs[rbf], pairs[rtf], drawType]; -- rf edge Draw2d.Line[context, pairs[rbn], pairs[rbf], drawType]; -- rb edge Draw2d.Line[context, pairs[rtn], pairs[rtf], drawType]; -- rt edge }; }; FaceOnly: PUBLIC PROC [ context: Context, cube: Cube, face: Face, view: Matrix, viewport: Viewport ¬ [], drawType: DrawType ¬ dashed] ~ { VP: PROC [p: Pair] RETURNS [x: Pair] ~ { x ¬ [viewport.scale.x*p.x+viewport.translate.x, viewport.scale.y*p.y+viewport.translate.y]; }; corners: Corners ~ cube.corners; faceCorners: ARRAY [0..4) OF Corner ¬ SELECT face FROM l => [corners[lbn], corners[lbf], corners[ltf], corners[ltn]], r => [corners[rbn], corners[rbf], corners[rtf], corners[rtn]], b => [corners[lbn], corners[lbf], corners[rbf], corners[rbn]], t => [corners[ltn], corners[ltf], corners[rtf], corners[rtn]], n => [corners[lbn], corners[ltn], corners[rtn], corners[rbn]], ENDCASE => [corners[lbf], corners[ltf], corners[rtf], corners[rbf]]; p0: Pair ¬ VP[G3dMatrix.TransformD[faceCorners[3].point, view]]; FOR n: NAT IN [0..4) DO p1: Pair ¬ VP[G3dMatrix.TransformD[faceCorners[n].point, view]]; Draw2d.Line[context, p0, p1, drawType]; p0 ¬ p1; ENDLOOP; }; FancyCube: PUBLIC PROC [ context: Context, cube: Cube, view: Matrix, viewport: Viewport ¬ [], label: BOOL ¬ TRUE, markKids: BOOL ¬ FALSE] ~ { IF cube = NIL OR view = NIL THEN RETURN; FancyCubeFromPairs[context, TransformDirections[cube, view, viewport],,, dashed, label]; IF markKids THEN FOR o: Octant IN Octant DO IF cube.kids[o] # NIL THEN CubeCenter[context, cube.kids[o], view, viewport]; ENDLOOP; }; FancyCubeFromPairs: PUBLIC PROC [ context: Context, pairs: DirectionPairs, cornerConnect: DrawType ¬ solid, edgeConnect: DrawType ¬ dashed, faceConnect: DrawType ¬ dotted, label: BOOL ¬ TRUE] ~ { Draw2d.Line[context, pairs[lbn], pairs[rbn], cornerConnect]; -- left to right edges: Draw2d.Line[context, pairs[lbf], pairs[rbf], cornerConnect]; Draw2d.Line[context, pairs[ltn], pairs[rtn], cornerConnect]; Draw2d.Line[context, pairs[ltf], pairs[rtf], cornerConnect]; Draw2d.Line[context, pairs[lb], pairs[rb], edgeConnect]; Draw2d.Line[context, pairs[lt], pairs[rt], edgeConnect]; Draw2d.Line[context, pairs[ln], pairs[rn], edgeConnect]; Draw2d.Line[context, pairs[lf], pairs[rf], edgeConnect]; Draw2d.Line[context, pairs[l], pairs[r], faceConnect]; Draw2d.Line[context, pairs[lbn], pairs[ltn], cornerConnect]; -- bottom to top edges: Draw2d.Line[context, pairs[rbn], pairs[rtn], cornerConnect]; Draw2d.Line[context, pairs[lbf], pairs[ltf], cornerConnect]; Draw2d.Line[context, pairs[rbf], pairs[rtf], cornerConnect]; Draw2d.Line[context, pairs[lb], pairs[lt], edgeConnect]; Draw2d.Line[context, pairs[rb], pairs[rt], edgeConnect]; Draw2d.Line[context, pairs[bn], pairs[tn], edgeConnect]; Draw2d.Line[context, pairs[bf], pairs[tf], edgeConnect]; Draw2d.Line[context, pairs[b], pairs[t], faceConnect]; Draw2d.Line[context, pairs[lbn], pairs[lbf], cornerConnect]; -- near to far edges: Draw2d.Line[context, pairs[rbn], pairs[rbf], cornerConnect]; Draw2d.Line[context, pairs[ltn], pairs[ltf], cornerConnect]; Draw2d.Line[context, pairs[rtn], pairs[rtf], cornerConnect]; Draw2d.Line[context, pairs[ln], pairs[lf], edgeConnect]; Draw2d.Line[context, pairs[rn], pairs[rf], edgeConnect]; Draw2d.Line[context, pairs[bn], pairs[bf], edgeConnect]; Draw2d.Line[context, pairs[tn], pairs[tf], edgeConnect]; Draw2d.Line[context, pairs[n], pairs[f], faceConnect]; IF label THEN LabelDirections[context, pairs]; }; Neighbors: PUBLIC PROC [ context: Context, neighborhood: Neighborhood, view: Matrix, viewport: Viewport ¬ [], drawType: DrawType ¬ dashed] ~ { DirectionsToCheck: PROC [direction: Direction] RETURNS [directionsToCheck: ARRAY Face OF Direction] ~ { directionsToCheck ¬ SELECT direction FROM <> c => [l, r, b, t, n, f], <> l => [none, c, lb, lt, ln, lf], r => [c, none, rb, rt, rn, rf], b => [lb, rb, none, c, bn, bf], t => [lt, rt, c, none, tn, tf], n => [ln, rn, bn, tn, none, c], f => [lf, rf, bf, tf, c, none], <> lb => [none, b, none, l, lbn, lbf], lt => [none, t, l, none, ltn, ltf], ln => [none, n, lbn, ltn, none, l], lf => [none, f, lbf, ltf, l, none], rb => [b, none, none, r, rbn, rbf], rt => [t, none, r, none, rtn, rtf], rn => [n, none, rbn, rtn, none, r], rf => [f, none, rbf, rtf, r, none], bn => [lbn, rbn, none, n, none, b], bf => [lbf, rbf, none, f, b, none], tn => [ltn, rtn, n, none, none, t], tf => [ltf, rtf, f, none, t, none], <> lbn => [none, bn, none, ln, none, lb], lbf => [none, bf, none, lf, lb, none], ltn => [none, tn, ln, none, none, lt], ltf => [none, tf, lf, none, lt, none], rbn => [bn, none, none, rn, none, rb], rbf => [bf, none, none, rf, rb, none], rtn => [tn, none, rn, none, none, rt], rtf => [tf, none, rf, none, rt, none], ENDCASE => [none, none, none, none, none, none]; }; visible: ARRAY Face OF BOOL; visible[r] ¬ NOT(visible[l] ¬ G3dVector.FrontFacingNoPerspective[[-1, 0, 0], view]); visible[t] ¬ NOT(visible[b] ¬ G3dVector.FrontFacingNoPerspective[[0, -1, 0], view]); visible[f] ¬ NOT(visible[n] ¬ G3dVector.FrontFacingNoPerspective[[0, 0, -1], view]); FOR direction: Direction IN Direction DO cube: Cube ~ neighborhood[direction]; IF cube # NIL THEN { directionsToCheck: ARRAY Face OF Direction ¬ DirectionsToCheck[direction]; FOR face: Face IN Face DO IF visible[face] THEN { check: Direction ¬ directionsToCheck[face]; IF check = none OR neighborhood[check] = NIL THEN FaceOnly[context, cube, face, view, viewport, drawType]; }; ENDLOOP; }; ENDLOOP; }; <> HLEOctree: PUBLIC PROC [context: Context, root: Cube, view: Matrix, viewport: Viewport ¬ []] ~ { VP: PROC [p: Pair] RETURNS [x: Pair] ~ { x ¬ [viewport.scale.x*p.x+viewport.translate.x, viewport.scale.y*p.y+viewport.translate.y]; }; ApplyToCube: PROC [cube: Cube] ~ { CedarProcess.CheckAbort[]; IF cube = NIL THEN RETURN; IF cube.terminal THEN { DrawFace: PROC [face: Face] ~ { fourCorners: FourCorners ¬ G3dOctree.FaceCorners[cube, face]; p0: Pair ¬ VP[G3dMatrix.TransformD[fourCorners.c0.point, view]]; p1: Pair ¬ VP[G3dMatrix.TransformD[fourCorners.c1.point, view]]; p2: Pair ¬ VP[G3dMatrix.TransformD[fourCorners.c2.point, view]]; p3: Pair ¬ VP[G3dMatrix.TransformD[fourCorners.c3.point, view]]; trajectory: Imager.Trajectory ¬ ImagerPath.MoveTo[p0]; trajectory ¬ ImagerPath.LineTo[trajectory, p1]; trajectory ¬ ImagerPath.LineTo[trajectory, p2]; trajectory ¬ ImagerPath.LineTo[trajectory, p3]; Imager.SetColor[context, Imager.white]; Imager.MaskFillTrajectory[context, trajectory]; Imager.SetColor[context, Imager.black]; Imager.MaskStrokeTrajectory[context, trajectory, TRUE]; }; DrawFace[f0]; DrawFace[f1]; DrawFace[f2]; } ELSE FOR n: NAT IN [0..8) DO ApplyToCube[cube.kids[o[n]]]; ENDLOOP; }; x: Triple ¬ G3dMatrix.TransformVec[[1.0, 0.0, 0.0], view]; y: Triple ¬ G3dMatrix.TransformVec[[0.0, 1.0, 0.0], view]; z: Triple ¬ G3dMatrix.TransformVec[[0.0, 0.0, 1.0], view]; d0: Direction ¬ SELECT MAX[ABS[x.z], ABS[y.z], ABS[z.z]] FROM ABS[x.z] => IF x.z < 0.0 THEN l ELSE r, ABS[y.z] => IF y.z < 0.0 THEN b ELSE t, ENDCASE => IF z.z < 0.0 THEN n ELSE f; d1: Direction ¬ SELECT d0 FROM l, r => IF ABS[y.z] > ABS[z.z] THEN IF y.z < 0.0 THEN b ELSE t ELSE IF z.z < 0.0 THEN n ELSE f, b, t => IF ABS[x.z] > ABS[z.z] THEN IF x.z < 0.0 THEN l ELSE r ELSE IF z.z < 0.0 THEN n ELSE f, ENDCASE => IF ABS[x.z] > ABS[y.z] THEN IF x.z < 0.0 THEN l ELSE r ELSE IF y.z < 0.0 THEN b ELSE t; d2: Direction ¬ SELECT d0 FROM l,r=> SELECT d1 FROM b,t=> IF z.z<0 THEN n ELSE f, ENDCASE => IF y.z<0 THEN b ELSE t, b,t=> SELECT d1 FROM l,r=> IF z.z<0 THEN n ELSE f, ENDCASE => IF x.z<0 THEN l ELSE r, n,f=> SELECT d1 FROM l,r=> IF y.z<0 THEN b ELSE t, ENDCASE => IF x.z<0 THEN l ELSE r, ENDCASE => none; d0Opp: Direction ¬ G3dOctree.OppositeDirection[d0]; d1Opp: Direction ¬ G3dOctree.OppositeDirection[d1]; d2Opp: Direction ¬ G3dOctree.OppositeDirection[d2]; edges: ThreeEdges; f0: Face ¬ G3dOctree.FaceFromDirection[d0Opp]; f1: Face ¬ G3dOctree.FaceFromDirection[d1Opp]; f2: Face ¬ G3dOctree.FaceFromDirection[d2Opp]; o: ARRAY[0..8) OF Octant; o[0] ¬ G3dOctree.OctantFromThreeDirections[d0, d1, d2]; o[1] ¬ G3dOctree.OctantFromThreeDirections[d0, d1, d2Opp]; o[2] ¬ G3dOctree.OctantFromThreeDirections[d0, d1Opp, d2]; o[3] ¬ G3dOctree.OctantFromThreeDirections[d0, d1Opp, d2Opp]; o[4] ¬ G3dOctree.OctantFromThreeDirections[d0Opp, d1, d2]; o[5] ¬ G3dOctree.OctantFromThreeDirections[d0Opp, d1, d2Opp]; o[6] ¬ G3dOctree.OctantFromThreeDirections[d0Opp, d1Opp, d2]; o[7] ¬ G3dOctree.OctantFromThreeDirections[d0Opp, d1Opp, d2Opp]; edges ¬ G3dOctree.EdgesFromOctant[o[7]]; Imager.SetStrokeWidth[context, 1.0]; ApplyToCube[root]; }; <> LabelCube: PUBLIC PROC [ context: Context, cube: Cube, direction: Direction, view: Matrix, viewport: Viewport ¬ []] ~ { VP: PROC [p: Pair] RETURNS [x: Pair] ~ { x ¬ [viewport.scale.x*p.x+viewport.translate.x, viewport.scale.y*p.y+viewport.translate.y]; }; pair: Pair; IF view[2][3] # 0.0 THEN { q: G3dBasic.Quad ¬ G3dMatrix.TransformH[G3dOctree.Center[cube], view]; IF q.z+q.w < 0.0 THEN RETURN; pair ¬ VP[[q.x/q.w, q.y/q.w]] } ELSE pair ¬ VP[G3dMatrix.TransformD[G3dOctree.Center[cube], view]]; Draw2d.Label[context, [pair.x+6, pair.y], G3dOctree.RopeFromDirection[direction]]; }; LabelDirections: PUBLIC PROC [context: Context, pairs: DirectionPairs] ~ { Inner: PROC [direction: Direction, rope: ROPE] ~ { Draw2d.Label[context, [pairs[direction].x+6, pairs[direction].y], rope]; Draw2d.Circle[context, pairs[direction], 2.0, TRUE]; <> }; Inner[c, "c"]; Inner[lbn, "lbn"]; Inner[lbf, "lbf"]; Inner[ltn, "ltn"]; Inner[ltf, "ltf"]; Inner[rbn, "rbn"]; Inner[rbf, "rbf"]; Inner[rtn, "rtn"]; Inner[rtf, "rtf"]; Inner[lb, "lb"]; Inner[lt, "lt"]; Inner[ln, "ln"]; Inner[lf, "lf"]; Inner[rb, "rb"]; Inner[rt, "rt"]; Inner[rn, "rn"]; Inner[rf, "rf"]; Inner[bn, "bn"]; Inner[bf, "bf"]; Inner[tn, "tn"]; Inner[tf, "tf"]; Inner[l, "l"]; Inner[r, "r"]; Inner[b, "b"]; Inner[t, "t"]; Inner[n, "n"]; Inner[f, "f"]; }; LabelOctants: PUBLIC PROC [context: Context, pairs: OctantPairs] ~ { Inner: PROC [octant: Octant, rope: ROPE] ~ { Draw2d.Label[context, [pairs[octant].x+6, pairs[octant].y], rope]; Draw2d.Mark[context, pairs[octant], dot]; }; Inner[lbn, "lbn"]; Inner[lbf, "lbf"]; Inner[ltn, "ltn"]; Inner[ltf, "ltf"]; Inner[rbn, "rbn"]; Inner[rbf, "rbf"]; Inner[rtn, "rtn"]; Inner[rtf, "rtf"]; }; <> CubeCenter: PUBLIC PROC [context: Context, cube: Cube, view: Matrix, viewport: Viewport ¬[]] ~ { VP: PROC [p: Pair] RETURNS [x: Pair] ~ { x ¬ [viewport.scale.x*p.x+viewport.translate.x, viewport.scale.y*p.y+viewport.translate.y]; }; p: Pair; IF view[2][3] # 0.0 THEN { q: Quad ~ G3dMatrix.TransformH[G3dOctree.Center[cube], view]; IF q.z+q.w < 0.0 THEN RETURN; p ¬ VP[[q.x/q.w, q.y/q.w]] } ELSE p ¬ VP[G3dMatrix.TransformD[G3dOctree.Center[cube], view]]; Draw2d.Circle[context, p, 5.0, TRUE]; <> }; END.