/*
 * rings.c - Create objects with 6 pentagonal rings which connect the midpoints
 *     of the edges of a dodecahedron.  A pyramid of these objects is formed,
 *     which the viewer looks upon from the point.  A plane is placed behind
 *     the pyramid for shadows.  No object is clipped.  Three light sources.
 *
 * Version:  2.2 (11/17/87)
 * Author:  Eric Haines, 3D/Eye, Inc.
 *
 * SIZE_FACTOR determines the number of objects output.
 *     Each object has 30 cylinders and 30 spheres.
 *     Total objects = SF*SF + (SF-1)*(SF-1) + ... + 1 plus 1 backdrop square.
 *     formula for # of spheres or cylinders = 5*SF*(SF+1)*(2*SF+1)
 *
 *     SIZE_FACTOR      # spheres       # cylinders     # squares
 *          1               30                30             1
 *          2              150               150             1
 *          3              420               420             1
 *
 *          7             4200              4200             1
 */

#include <stdio.h>
#include <math.h>
#ifdef MAC
#include <console.h>
#endif

#include "def.h"
#include "lib.h"

static int size_factor      = 2;
static int raytracer_format = OUTPUT_POVRAY;

/* if spread out is > 1, succeeding layers spread out more */
#define SPREAD_OUT              1

/* Create the set of 30 points needed to generate the rings */
static void
create_dodec(minor_radius, vertex)
   double minor_radius;
   COORD4 vertex[30];
{
    long    num_vertex, num_pentagon ;
    COORD4  temp_vertex ;
    MATRIX  x_matrix, z_matrix ;
    double  scale, x_rotation, z_rotation ;


    /* scale object to fit in a sphere of radius 1 */

    scale = 1.0 / ( 1.0 + minor_radius ) ;
    /*
     * define one pentagon as on the XY plane, with points starting along +X
     * and N fifths of the way around the Z axis.
     */
    for ( num_vertex = 0 ; num_vertex < 5 ; ++num_vertex ) {
        vertex[num_vertex].x = scale * cos( (double)num_vertex * 2.0*PI/5.0 ) ;
        vertex[num_vertex].y = scale * sin( (double)num_vertex * 2.0*PI/5.0 ) ;
        vertex[num_vertex].z = 0.0 ;
        vertex[num_vertex].w = 1.0 ;
    }

    /*
     * find the rotation angle (in radians) along the X axis:
     * angle between two adjacent dodecahedron faces.
     */
    x_rotation = 2.0 *
                acos( cos( (double)(PI/3.0) ) / sin( (double)(PI/5.0) ) ) ;
    lib_create_rotate_matrix( x_matrix, X_AXIS, x_rotation ) ;

    /*
     * Find each of the other 5 pentagons:  rotate along the X axis,
     * then rotate on the Z axis.
     */
    for ( num_pentagon = 1 ; num_pentagon < 6 ; ++num_pentagon ) {
        /*
         * find the rotation angle (in radians) along the Z axis:
         * 1/10th plus N fifths of the way around * 2 * PI.
         */
        z_rotation = PI*( 2.0*(double)(num_pentagon-1)+1.0 ) / 5.0 ;
        lib_create_rotate_matrix( z_matrix, Z_AXIS, z_rotation ) ;

        for ( num_vertex = 0 ; num_vertex < 5 ; ++num_vertex ) {

            lib_transform_coord( &temp_vertex
                               , &vertex[num_vertex]
                               , x_matrix
                               ) ;

            lib_transform_coord( &vertex[5*num_pentagon+num_vertex]
                               , &temp_vertex
                               , z_matrix
                               ) ;
        }
    }
}

void
main(argc, argv)
   int argc;
   char *argv[];
{
    COORD4  base_pt, apex_pt, offset;
    COORD4  wall[4], dodec[30];
    COORD4  from, at, up, dir;
    COORD4  wvec, light;
    COORD4  back_color, ring_color[6];
    long    prev_elem, num_elem;
    long    num_depth, num_objx, num_objz;
    double  lscale, radius;
    double  spread, y_diff, xz_diff;
    char *c[6];
    int cind = 0;

    /* 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);
   lib_set_polygonalization(3, 3);


    radius = 0.07412 ;  /* cone and sphere radius */

    /* calculate spread of objects */
    spread = 1 / sin( (double)( PI/8.0 ) ) ;
    if ( SPREAD_OUT <= spread ) {
        y_diff = spread / SPREAD_OUT ;
        xz_diff = 1.0 ;
    }
    else {
        y_diff = 1.0 ;
        xz_diff = SPREAD_OUT / spread ;
    }

    /* output viewpoint */
    SET_COORD( from, -1.0, -spread, 0.5 ) ;
    SET_COORD( at, from.x, from.y + 1.0, from.z ) ;
    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 */
    /* note that the background color should never be seen */
    SET_COORD( back_color, 0.078, 0.361, 0.753 ) ;
    lib_output_background_color( &back_color ) ;

    /* output light source */
    lscale = (raytracer_format == OUTPUT_NFF ||
	      raytracer_format == OUTPUT_RTRACE ? 1.0 : 1.0/sqrt(3.0));
    SET_COORD4(light, 3.0, -spread, 3.0, lscale);
    lib_output_light(&light);
    SET_COORD4(light, -4.0, -spread, 1.0, lscale);
    lib_output_light(&light);
    SET_COORD4(light, 2.0, -spread, -4.0, lscale);
    lib_output_light(&light);

    /* output wall polygon - white */
    SET_COORD( back_color, 1.0, 1.0, 1.0 ) ;
    lib_output_color(NULL, &back_color, 0.2, 0.7, 0.0, 0.0, 0.0, 0.0, 0.0);
    /* just spans 45 degree view + 1% */
    wvec.y = y_diff * ( size_factor + 1 ) ;
    wvec.x = wvec.z = 1.01 * ( wvec.y - from.y ) * tan( PI / 8.0 ) ;
    SET_COORD( wall[0],  wvec.x+from.x, wvec.y,  wvec.z+from.z ) ;
    SET_COORD( wall[1], -wvec.x+from.x, wvec.y,  wvec.z+from.z ) ;
    SET_COORD( wall[2], -wvec.x+from.x, wvec.y, -wvec.z+from.z ) ;
    SET_COORD( wall[3],  wvec.x+from.x, wvec.y, -wvec.z+from.z ) ;
    lib_output_polygon(4, wall);

    /* set up ring colors - RGB and complements */
    SET_COORD(ring_color[0], 1.0, 0.0, 0.0);
    SET_COORD(ring_color[1], 0.0, 1.0, 0.0);
    SET_COORD(ring_color[2], 0.0, 0.0, 1.0);
    SET_COORD(ring_color[3], 0.0, 1.0, 1.0);
    SET_COORD(ring_color[4], 1.0, 0.0, 1.0);
    SET_COORD(ring_color[5], 1.0, 1.0, 0.0);

    create_dodec( radius, dodec ) ;
    /* radius of osculating cylinders and spheres (no derivation given) */
    base_pt.w = apex_pt.w = radius ;

    for ( num_depth = 0 ; num_depth < size_factor ; ++num_depth ) {
        offset.y = y_diff * (double)(num_depth+1) ;
        for ( num_objz = 0 ; num_objz <= num_depth ; ++num_objz ) {
            offset.z = xz_diff * (double)(2*num_objz - num_depth) ;
            for ( num_objx = 0 ; num_objx <= num_depth ; ++num_objx ) {
                offset.x = xz_diff * (double)(2*num_objx - num_depth) ;
                for ( num_elem = 0 ; num_elem < 30 ; ++num_elem ) {
                    COPY_COORD( base_pt, dodec[num_elem] ) ;
                    ADD2_COORD( base_pt, offset ) ;
                    if ( num_elem%5 == 0 ) {
                        prev_elem = num_elem + 4 ;
                        /* new ring beginning - output color */
                        cind = num_elem/5;
		        lib_output_color(NULL, &ring_color[cind], 0.2, 0.5, 0.0,
					 0.5, 10.0, 0.0, 0.0);
                       }
                    else {
                        prev_elem = num_elem - 1 ;
                       }
                    COPY_COORD( apex_pt, dodec[prev_elem] ) ;
                    ADD2_COORD( apex_pt, offset ) ;

                    lib_output_cylcone( &base_pt, &apex_pt, OUTPUT_CURVES);
                    lib_output_sphere( &base_pt, OUTPUT_CURVES);
                }
            }
        }
    }

   /* Make sure everything is cleaned up */
   if (raytracer_format == OUTPUT_RTRACE ||
       raytracer_format == OUTPUT_PLG) {
      lib_set_raytracer(raytracer_format);
      lib_flush_definitions();
      }
}
