/*
 * gears.c - Create a set of gears.  Each gear face has 144 vertices, and
 *   contains concavities.  Note that the first 3 vertices of all polygons
 *   define the two edges of a convex section of the polygon.  Background
 *   square floor is reflective.  Some gears are clipped.
 *   Five light sources.
 *
 * Version:  2.2 (11/17/87)
 * Author:  Eric Haines, 3D/Eye, Inc.
 *
 * SIZE_FACTOR determines the number of polygons output.
 *     Total gears = SF**3:  concave polygons = 2 * SF**3
 *     rectangles = 4*TEETH * SF**3
 *
 * SIZE_FACTOR   # gears # gear faces    # rectangles
 *       1          1          2           144
 *       2          8         16          1152
 *       3         27         54          3888
 *       4         64        128          9216
 */

#include <stdio.h>
#include <math.h>
#ifdef MAC
#include <console.h>
#endif
#include "def.h"
#include "lib.h"

/* These may be read from the command line */
static int size_factor      = 2;
static int raytracer_format = OUTPUT_POVRAY;

/* define number of teeth on a gear - must be a multiple of 4 */
#define TEETH 32

/* define ratio of radius taken up by teeth and the gear thickness */
#define EDGE_RATIO   0.1
#define DEPTH_RATIO  0.1

/* Create gear tooth */
static void
create_tooth(double gear_angle, double tooth_angle,
        COORD4 *center, COORD4 *outer_pt,
        COORD4 *inner_pt, COORD4 *edge_pts)
{
    MATRIX   mx ;


    lib_create_rotate_matrix( mx
             , Z_AXIS
             , gear_angle - 0.19 * tooth_angle ) ;
    lib_transform_coord( &edge_pts[0], outer_pt, mx ) ;
    ADD2_COORD( edge_pts[0], *center ) ;
    lib_create_rotate_matrix( mx
             , Z_AXIS
             , gear_angle + 0.19 * tooth_angle ) ;
    lib_transform_coord( &edge_pts[1], outer_pt, mx ) ;
    ADD2_COORD( edge_pts[1], *center ) ;
    lib_create_rotate_matrix( mx
             , Z_AXIS
             , gear_angle + 0.3 * tooth_angle ) ;
    lib_transform_coord( &edge_pts[2], inner_pt, mx ) ;
    ADD2_COORD( edge_pts[2], *center ) ;
    lib_create_rotate_matrix( mx
             , Z_AXIS
             , gear_angle + 0.7 * tooth_angle ) ;
    lib_transform_coord( &edge_pts[3], inner_pt, mx ) ;
    ADD2_COORD( edge_pts[3], *center ) ;
}


/* Create gear */
static void
create_gear(COORD4 *center, double offset_angle, double outer_radius,
       double inner_radius, double thickness)
{
    COORD4   side_pts[4], gear_pts[4*TEETH], outer_pt, inner_pt ;
    long   next_side, num_side, num_teeth ;
    double   gear_angle, tooth_angle ;


    outer_pt.x = outer_radius ;
    outer_pt.y = 0.0 ;
    outer_pt.z = 0.0 ;
    outer_pt.w = 1.0 ;
    inner_pt.x = inner_radius ;
    inner_pt.y = 0.0 ;
    inner_pt.z = 0.0 ;
    inner_pt.w = 1.0 ;

    tooth_angle = 2.0 * PI / (double)TEETH ;

    /* output gear top */
    for ( num_teeth = 0 ; num_teeth < TEETH ; ++num_teeth ) {
   gear_angle = offset_angle +
      2.0 * PI * (double)num_teeth / (double)TEETH ;
   create_tooth( gear_angle
          , tooth_angle
          , center
          , &outer_pt
          , &inner_pt
          , &gear_pts[num_teeth*4]
          ) ;
    }
    lib_output_polygon(4*TEETH, gear_pts);

    /* output teeth */
    for ( num_side = 0 ; num_side < 4 * TEETH ; ++num_side ) {
   next_side = ( num_side + 1 ) % ( 4 * TEETH ) ;
   COPY_COORD( side_pts[0], gear_pts[num_side] ) ;
   COPY_COORD( side_pts[1], gear_pts[num_side] ) ;
   side_pts[1].z -= thickness ;
   COPY_COORD( side_pts[2], gear_pts[next_side] ) ;
   side_pts[2].z -= thickness ;
   COPY_COORD( side_pts[3], gear_pts[next_side] ) ;
   lib_output_polygon(4, side_pts);
    }

    /* output gear bottom */
    outer_pt.z = inner_pt.z = -thickness ;
    for ( num_teeth = 0 ; num_teeth < TEETH ; ++num_teeth ) {
   gear_angle = offset_angle -
      2.0 * PI * (double)num_teeth / (double)TEETH ;
   create_tooth( gear_angle
          , -tooth_angle
          , center
          , &outer_pt
          , &inner_pt
          , &gear_pts[num_teeth*4]
          ) ;
    }
    lib_output_polygon(4*TEETH, gear_pts);
}

void
main(int argc, char *argv[])
{
   COORD4  back_color, gear_color ;
   COORD4  center_pt, floor[4], light, offset, zero_pt ;
   COORD4  from, at, up, dir;
   double  angle, color_scale, outer_radius, thickness, lscale;
   long    ix, iy, iz ;

   /* Start by defining which raytracer we will be using */
   switch (argc) {
      case 3:
	 raytracer_format = atoi(argv[2]);
      case 2:
	 size_factor = atoi(argv[1]);
	 break;
      defaut:
	 break;
      }

   lib_set_output_file(stdout);
   if (raytracer_format == OUTPUT_RTRACE ||
       raytracer_format == OUTPUT_PLG)
      lib_set_raytracer(OUTPUT_DELAYED);
   else
      lib_set_raytracer(raytracer_format);

   /* output viewpoint */
   SET_COORD(from, -1.1, -2.1, 2.6);
   SET_COORD(at, 0.0, 0.0, 0.0);
   SET_COORD(up, 0.0, 0.0, 1.0);
   lib_output_viewpoint(&from, &at, &up, 45.0, 1.0, 1.0, 512, 512);

   /* output background color - UNC sky blue */
   SET_COORD(back_color, 0.078, 0.361, 0.753);
   lib_output_background_color( &back_color);

   /* output light sources */
   lscale = (raytracer_format == OUTPUT_NFF ||
	     raytracer_format == OUTPUT_RTRACE ? 1.0 : 1.0 / sqrt(5.0));
   SET_COORD4(light, 2.0, 4.0, 4.0, lscale);
   lib_output_light(&light);
   SET_COORD4(light, -2.0, 4.0, 3.0, lscale);
   lib_output_light(&light);
   SET_COORD4(light, 2.0, -2.5, 2.5, lscale);
   lib_output_light(&light);
   SET_COORD4(light, -1.0, -4.0, 2.0, lscale);
   lib_output_light(&light);

   /* just behind the eye */
   SET_COORD4(light, -1.111, -2.121, 2.626, lscale);
   lib_output_light(&light);

   /* output floor polygon - off white */
   SET_COORD( back_color, 1.0, 0.85, 0.7 ) ;
   lib_output_color(NULL, &back_color, 0.1, 0.3, 0.6, 0.0, 0.0, 0.0, 0.0);
   SET_COORD( floor[0], 2.0, 2.0, 0.0 ) ;
   SET_COORD( floor[1], -2.0, 2.0, 0.0 ) ;
   SET_COORD( floor[2], -2.0, -2.0, 0.0 ) ;
   SET_COORD( floor[3], 2.0, -2.0, 0.0 ) ;
   lib_output_polygon(4, floor);

   outer_radius = 1.0 / ((double)size_factor - (double)(size_factor - 1) *
			 EDGE_RATIO / 2.0);
   /* calculate first gear center */
   zero_pt.x = zero_pt.y = -1.0 + outer_radius;
   zero_pt.z = 1.0 ;
   /* calculate offset */
   offset.x = offset.y = outer_radius * (2.0 - EDGE_RATIO);
   offset.z = -1.0 / (double)size_factor;

    /* create gears */
   for ( iz = 0 ; iz < size_factor ; ++iz ) {
      center_pt.z = zero_pt.z + (double)iz * offset.z ;
      for ( iy = 0 ; iy < size_factor ; ++iy ) {
         center_pt.y = zero_pt.y + (double)iy * offset.y ;
         for ( ix = 0 ; ix < size_factor ; ++ix ) {
            center_pt.x = zero_pt.x + (double)ix * offset.x ;

            /* output pseudo-random gear color */
            SET_COORD(gear_color,
                      0.01 + FRACTION( (double)(ix*3+iy*2+iz+1)*5.0/7.0 ),
                      0.01 + FRACTION( (double)(iy*3+iz*2+ix+1)*3.0/7.0 ),
                      0.01 + FRACTION( (double)(iz*3+ix*2+iy+1)*2.0/7.0 ));
            color_scale = MAX3( gear_color.x, gear_color.y, gear_color.z );
            gear_color.x /= color_scale ;
            gear_color.y /= color_scale ;
            gear_color.z /= color_scale ;
            if ((ix*4 + iy*2 + iz ) % 5 == 0)
               lib_output_color(NULL, &gear_color, 0.1, 0.2, 0.0, 0.0, 0.0,
                                0.7, 1.1);
            else
               lib_output_color(NULL, &gear_color, 0.2, 0.8, 0.0, 0.0, 0.0,
				0.0, 0.0);


            /* output gear */
            angle = PI * (double)( (ix+iy+iz) % 2 ) / (double)(TEETH);
            thickness = MIN( DEPTH_RATIO, 1.0 / ( 2.0 * (double)size_factor));
            create_gear(&center_pt, angle, outer_radius,
                        (1.0 - EDGE_RATIO) * outer_radius, thickness);
            }
         }
      }

   /* Make sure everything is cleaned up */
   if (raytracer_format == OUTPUT_RTRACE ||
       raytracer_format == OUTPUT_PLG) {
      lib_set_raytracer(raytracer_format);
      lib_flush_definitions();
      }
}
