// obj.cmd

// Surface Evolver script for creating Wavefront OBJ format 3D graphics file.
// Result file requires EvolverOBJcolors.mtl file in same directory.

// File format reference: http://www.martinreddy.net/gfx/3d/OBJ.spec

// Edges are shown as 4-sided tubes.  The relative tube diameter
// can be adjusted where actual_tube_radius is set below.

/* Usage:
     Set which edges and facets you want to show with the
     "show edge where ..." and "show facet where ..." commands.
     Set obj_double_sided to 1 if you want back sides of facets.
     Set obj_edge_flag to 0 if you want no edges shown.
     Run obj and redirect output to file, for example
         obj >>> "filename.obj"
*/

define vertex attribute obj_vnum integer

obj_double_sided := 0  // set to 1 to do front and back facets
obj_edge_flag := 1    // set to 1 to do small tubes around shown edges
                      // or 0 to do edges as lines
obj_tube_radius := 0.002;  // relative size of edge tubes.

obj_edge_tubes := {

 local tube_sides,t_angle,actual_tube_radius;
 local ax,ay,az,bx,by,bz,mag,inx,cinx;

 // Doing edges one by one, using negative relative vertex indexing, so
 // vertices can be listed with the tube faces.

 tube_sides := 4;
 t_angle := 2*pi/tube_sides;
 actual_tube_radius := obj_tube_radius*sqrt(max(vertex,x^2+y^2+z^2));

 for ( cinx := 0 ; cinx <= 15 ; cinx += 1 )
 { printf "usemtl color%d\n",cinx;
   foreach edge ee where color == cinx and show do
   {
     // find two orthogonal directions to edge; if possible,
     // one along adjacent facet
     if ee.valence > 0 then
     { ax := ee.facet[1].x;
       ay := ee.facet[1].y;
       az := ee.facet[1].z;
     }
     else
     { if abs(ee.x) < abs(ee.y) and abs(ee.x) < abs(ee.z) then
       { ax := 1; ay := 0; az := 0;}
       else if abs(ee.y) < abs(ee.z) then
       { ax := 0; ay := 1; az := 0; }
       else
       { ax := 0; ay := 0; az := 1; };
     };
     bx := ay*ee.z - az*ee.y;
     by := az*ee.x - ax*ee.z;
     bz := ax*ee.y - ay*ee.x;
     ax := by*ee.z - bz*ee.y;
     ay := bz*ee.x - bx*ee.z;
     az := bx*ee.y - by*ee.x;
     // normalize to radius
     mag := sqrt(ax^2+ay^2+az^2);
     if mag <= 0 then continue;
     ax *= actual_tube_radius/mag;
     ay *= actual_tube_radius/mag;
     az *= actual_tube_radius/mag;
     mag := sqrt(bx^2+by^2+bz^2);
     if mag <= 0 then continue;
     bx *= actual_tube_radius/mag;
     by *= actual_tube_radius/mag;
     bz *= actual_tube_radius/mag;
  
     for ( inx := 0 ; inx < tube_sides ; inx += 1 )
     {
        printf "v %f %f %f\n",ee.vertex[1].x+ax*cos(inx*t_angle)+bx*sin(inx*t_angle),
           ee.vertex[1].y+ay*cos(inx*t_angle)+by*sin(inx*t_angle),
           ee.vertex[1].z+az*cos(inx*t_angle)+bz*sin(inx*t_angle);
        printf "v %f %f %f\n",ee.vertex[1].x+ee.x+ax*cos(inx*t_angle)+bx*sin(inx*t_angle),
          ee.vertex[1].y+ee.y+ay*cos(inx*t_angle)+by*sin(inx*t_angle),
          ee.vertex[1].z+ee.z+az*cos(inx*t_angle)+bz*sin(inx*t_angle);
     };
     
     /* quads should work, but Meshlab interprets them as single triangles!
     printf "f  -6 -5 -7 -8\n";
     printf "f  -4 -3 -5 -6\n";
     printf "f  -2 -1 -3 -4\n";
     printf "f  -8 -7 -1 -2\n";  
     */
     printf "f  -6 -5 -7\n";
     printf "f  -6 -7 -8\n";
     printf "f  -4 -3 -5\n";
     printf "f  -4 -5 -6\n";
     printf "f  -2 -1 -3\n";
     printf "f  -2 -3 -4\n";
     printf "f  -8 -7 -1\n";
     printf "f  -8 -1 -2\n";  
   }
 }

} // end obj_edge_tubes

// Edges as lines
obj_edge_lines := {
 local cinx;
 for ( cinx := 0 ; cinx <= 15 ; cinx += 1 )
 { printf "usemtl color%d\n",cinx;
   foreach edge ee where color == cinx and show do
   {
      printf "l %d %d\n",ee.vertex[1].obj_vnum,
           ee.vertex[2].obj_vnum;
   }
 }

} // end obj_edge_lines

obj := {
  local vnumber,cinx;

  if rgb_colors then
  { errprintf "obj error: obj script does not do RGB colors; do rgb_colors off.\n";
    abort;
  };

  if torus then
  { errprintf "Cannot run 'obj' command in torus mode. Do 'detorus' first.\n";
    abort;
  };

  if symmetry_group then
  { errprintf "Cannot run 'obj' command in symmetry group mode. Do 'detorus' first.\n";
    abort;
  };

  if space_dimension != 3 then
  { errprintf "The 'obj' command must be run in three-dimensional space.\n";
    abort;
  };

  if surface_dimension == 1 then
  { errprintf "The 'obj' command is not meant for the string model.\n";
    abort;
  };

  if simplex_representation then
  { errprintf "The 'obj' command is not meant for the simplex model.\n";
    abort;
  };

  if lagrange_order >= 2 then
  { errprintf "The 'obj' command is meant for the linear model, not quadratic or Lagrange.\n";
    abort;
  };

  printf "# OBJ version of %s \n",datafilename;
  printf "# Produced by Surface Evolver with obj.cmd script\n\n";

  printf "mtllib EvolverOBJcolors.mtl\n\n";

  vnumber := 0;
  foreach vertex vv do
  { printf "v %f %f %f\n",vv.x,vv.y,vv.z;
    vnumber += 1;
    vv.obj_vnum := vnumber;
  };
  
  for ( cinx := 0 ; cinx <= 15 ; cinx += 1 )
  { printf "usemtl color%d\n",cinx;
    foreach facet ff where show and frontcolor == cinx do
      printf "f %d %d %d\n",ff.vertex[1].obj_vnum,
           ff.vertex[2].obj_vnum,ff.vertex[3].obj_vnum;
    if obj_double_sided then
      foreach facet ff where show and backcolor == cinx do
        printf "f %d %d %d\n",ff.vertex[1].obj_vnum,
           ff.vertex[3].obj_vnum,ff.vertex[2].obj_vnum;
  };

  if obj_edge_flag then
    obj_edge_tubes
  else
    obj_edge_lines;

} // end obj

/* Usage:
     Set which edges and facets you want to show with the
     "show edge where ..." and "show facet where ..." commands.
     Set obj_double_sided to 1 if you want back sides of facets.
     Set obj_edge_flag to 0 if you want no edges shown.
     Run obj and redirect output to file, for example
         obj >>> "filename.obj"
*/
