MODULE MakeRawCube EXPORTS Main;

(*
  Creates a ".top" file for a cube whose faces
  are triangulated grids of N by N cells, as created by 
  Triang.MakeGrid.   
  
  The vertex coordinates are integers ranging from "(0,0,0)"
  to "(2N,2N,2N)".  Cell corners have three even coordinates;
  cell centers have one even and two odd coordinates.
*)

IMPORT Oct, Map, LR3, Triang, Fmt, ParseParams, Process, Wr, Stdio, Thread;
FROM Triang IMPORT Topology, Coords, OrgV; 
FROM Map IMPORT Arc;
FROM Oct IMPORT Oprev, Flip;
FROM Stdio IMPORT stderr;
  
TYPE
  Options = RECORD
      gridOrder: CARDINAL;
    END;

PROCEDURE Main() =
  BEGIN
    WITH 
      o = GetOptions(),
      diag = MakeCubeTriang(o.gridOrder),  
      top = Triang.MakeTopology(diag[0]),      
      c = ComputeCoordinates(top, diag, o.gridOrder)^
    DO 
      Triang.Write("cube-" & Fmt.Int(o.gridOrder), top, c);
    END;    
  END Main;

PROCEDURE MakeCubeTriang(N: CARDINAL): ARRAY [0..5] OF Arc =
  (*
    Builds a cube from six triangulated grids.
    Returns a diagonal arc out of one corner from each face,
    on the outside of the cube.
    
    Face 0 is the bottom, faces 1..4 are the sides, face 5 is the
    top, as shown below.
    
  |      +------+
  |      |      |
  |      |      |
  |      |5     |
  |      +------+------+------+------+
  |      |      |      |      |      |
  |      |      |      |      |      |
  |      |1     |2     |3     |4     |
  |      +------+------+------+------+
  |      |      |
  |      |      |
  |      |0     |
  |      +------+
  *)               
    
  VAR face: ARRAY [0..5] OF ARRAY [0..7] OF Arc;
      diag: ARRAY [0..5] OF Arc;
  BEGIN
    FOR i := 0 TO 5 DO 
      face[i] := Triang.MakeGrid(N, N);
      diag[i] := Oprev(face[i][0]);
    END;
    
    (* Glue faces: *)
    
    EVAL Triang.Glue(face[5][2], Flip(face[1][5]), N);
    EVAL Triang.Glue(face[5][4], Flip(face[2][5]), N);
    EVAL Triang.Glue(face[5][6], Flip(face[3][5]), N);
    EVAL Triang.Glue(face[5][0], Flip(face[4][5]), N);

    EVAL Triang.Glue(face[1][0], Flip(face[4][3]), N);
    EVAL Triang.Glue(face[2][0], Flip(face[1][3]), N);
    EVAL Triang.Glue(face[3][0], Flip(face[2][3]), N);
    EVAL Triang.Glue(face[4][0], Flip(face[3][3]), N);

    EVAL Triang.Glue(face[0][6], Flip(face[1][1]), N);
    EVAL Triang.Glue(face[0][4], Flip(face[2][1]), N);
    EVAL Triang.Glue(face[0][2], Flip(face[3][1]), N);
    EVAL Triang.Glue(face[0][0], Flip(face[4][1]), N);

    RETURN diag
  END MakeCubeTriang;
      
PROCEDURE ComputeCoordinates(
    READONLY top: Topology;
    READONLY diag: ARRAY [0..5] OF Arc;
    N: CARDINAL;
  ): REF Coords =
  (*
    Computes the vertex coordinates, assuming "diag[i]"
    is a diagonal arc out of a corner of face "i", numbered 
    and placed as shown above.  The coordinates
    will range from 0 to "2*N".
  *)
  TYPE Z3 = ARRAY [0..2] OF INTEGER;
  BEGIN
    WITH
      r = NEW(REF Coords, top.NV),
      c = r^,
      sz = 2*N
    DO
    
      PROCEDURE SetFaceCoords(
          a: Arc;
          READONLY o, dx, dy: Z3;
        ) =
        (*
          Sets the vertex coordinates in a grid, given a northeast
          pointing arc "a" out of its southwest corner.  Assumes the
          origin of "a" is at the point "o", and the side vectors of
          the grid are "2*N*dx" and "2*N*dy".
        *)

        PROCEDURE SetVertexCoords(e: Arc; x, y: CARDINAL) =
          BEGIN
            c[OrgV(e).num] := LR3.T{
              FLOAT(o[0] + dx[0]*x + dy[0]*y, LONGREAL),
              FLOAT(o[1] + dx[1]*x + dy[1]*y, LONGREAL),
              FLOAT(o[2] + dx[2]*x + dy[2]*y, LONGREAL)
            };
          END SetVertexCoords;

        BEGIN
          Triang.EnumGridVertices(Oprev(a), N, N, SetVertexCoords)
        END SetFaceCoords;
        
      BEGIN
        SetFaceCoords(diag[0], Z3{00, 00, 00}, Z3{00, +1, 00}, Z3{+1, 00, 00});
        SetFaceCoords(diag[1], Z3{sz, 00, 00}, Z3{00, +1, 00}, Z3{00, 00, +1});
        SetFaceCoords(diag[2], Z3{sz, sz, 00}, Z3{-1, 00, 00}, Z3{00, 00, +1});
        SetFaceCoords(diag[3], Z3{00, sz, 00}, Z3{00, -1, 00}, Z3{00, 00, +1});
        SetFaceCoords(diag[4], Z3{00, 00, 00}, Z3{+1, 00, 00}, Z3{00, 00, +1});
        SetFaceCoords(diag[5], Z3{sz, 00, sz}, Z3{00, +1, 00}, Z3{-1, 00, 00});
      END;
      RETURN r
    END
  END ComputeCoordinates;

PROCEDURE GetOptions (): Options =
  <* FATAL Thread.Alerted, Wr.Failure *>
  VAR o: Options;
  BEGIN
    WITH pp = NEW(ParseParams.T).init(stderr) DO
      TRY
        pp.getKeyword("-gridOrder");                               

        o.gridOrder := pp.getNextInt(1, 100);                         
        pp.finish();                                       

      EXCEPT                                                            
      | ParseParams.Error =>                                              
          Wr.PutText(stderr, "Usage: MakeRawCube -gridOrder <num>\n");
          Process.Exit (1);                                              
      END
    END;
    RETURN o
  END GetOptions;
 
BEGIN
  Main(); 
END MakeRawCube.