/*
 * tree.c - Creates a tree using Aono & Kunii's generation method.
 *     (See IEEE CG&A May 1984).  A square polygon is placed beneath the
 *     tree to act as a field.  No tree branch is clipped.  Seven light sources.
 *
 * Version:  2.2 (11/17/87)
 * Author:  Eric Haines, 3D/Eye, Inc.
 *
 * SIZE_FACTOR determines the number of objects output.
 *     Total objects = 2**(SF+1)-1 cones and spheres + 1 square polygon.
 *
 *     SIZE_FACTOR	# spheres	   # cones	# squares
 *	    1		     3		       3	     1
 *	    2		     7		       7	     1
 *	    3		    15		      15	     1
 *
 *	   11		  4095		    4095	     1
 */

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include "def.h"
#include "lib.h"

/* the following affect the shape of the tree */
#define	BR_ANGLE_0		40.0
#define	BR_ANGLE_1		25.0
#define	BR_CONTR_0		0.65
#define	BR_CONTR_1		0.70
#define	BR_DIAMETER		0.67
#define	DIV_ANGLE		140.0
#define	WIDTH_HEIGHTH_RATIO	0.15

static	MATRIX	rst_mx[2] ;

/* These may be read from the command line */
static int size_factor      = 6;
static int raytracer_format = OUTPUT_POVRAY;
static int output_format    = OUTPUT_CURVES;

/* grow tree branches recursively */
void
grow_tree(cur_mx, scale, depth)
   MATRIX cur_mx;
   double scale;
   int depth;
{
    COORD4 apex, base, vec;
    int i;
    MATRIX new_mx;

    /* output branch */
    SET_COORD4( vec, 0.0, 0.0, 0.0, 1.0 ) ;
    lib_transform_coord( &base, &vec, cur_mx ) ;
    base.w = scale * WIDTH_HEIGHTH_RATIO ;

    SET_COORD4( vec, 0.0, 0.0, 1.0, 1.0 ) ;
    lib_transform_coord( &apex, &vec, cur_mx ) ;
    apex.w = base.w * BR_DIAMETER ;

    lib_output_cylcone( &base, &apex, output_format ) ;
    lib_output_sphere( &apex, output_format ) ;

    if ( depth > 0 ) {
	--depth ;

	for ( i = 0; i < 2; ++i ) {
	    lib_matrix_multiply( new_mx, rst_mx[i], cur_mx ) ;
	    grow_tree( new_mx, scale * BR_DIAMETER, depth ) ;
	}
    }
}

/*
 * Set up matrices for growth of each branch with respect to the
 * parent branch, then grow each branch.
 */
void
create_tree()
{
    double  branch_angle, branch_contraction, divergence ;
    long    i ;
    MATRIX  ident_mx, temp1_mx, temp2_mx, tempr_mx, tempst_mx ;


    for ( i = 0 ; i < 2 ; ++i ) {
	if ( i == 0 ) {
	    branch_angle = BR_ANGLE_0 ;
	    divergence = 90.0 ;
	    branch_contraction = BR_CONTR_0 ;
	}
	else {
	    branch_angle = BR_ANGLE_1 ;
	    divergence = DIV_ANGLE + 90.0 ;
	    branch_contraction = BR_CONTR_1 ;
	}

	/* rotate along X axis by branching angle */
	lib_create_rotate_matrix( temp1_mx, X_AXIS, branch_angle*PI/180.0 ) ;

	/* rotate along Z axis by divergence angle */
	lib_create_rotate_matrix( temp2_mx, Z_AXIS, divergence*PI/180.0 ) ;

	lib_matrix_multiply( tempr_mx, temp1_mx, temp2_mx ) ;

	/* include translation of branch, scaled */
	lib_create_identity_matrix( tempst_mx ) ;
	tempst_mx[0][0] = tempst_mx[1][1] = tempst_mx[2][2] =
							branch_contraction ;
	tempst_mx[3][2] = 1.0 ;

	/* concatenate */
	lib_matrix_multiply( rst_mx[i], tempr_mx, tempst_mx ) ;
    }

    /* set up initial matrix */
    lib_create_identity_matrix( ident_mx ) ;
    grow_tree( ident_mx, 1.0, size_factor ) ;
}
void
main(argc,argv)
   int argc ;
   char *argv[] ;
{
    COORD4  field[4];
    COORD4  from, at, up;
    COORD4  light;
    COORD4  back_color, tree_color, lower_left, upper_right;
    double  lscale;

   /* 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);

   /* output viewpoint */
   SET_COORD( from, 4.5, 0.4, 2.0 ) ;
   SET_COORD( at, 0.0, 0.0, 1.5 ) ;
   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 */
   if (raytracer_format == OUTPUT_NFF || raytracer_format == OUTPUT_RTRACE)
      lscale = 1.0;
   else
      lscale = 1.0 / sqrt(3.0);
   SET_COORD4( light, -5.0, 5.0, 50.0, lscale ) ;
   lib_output_light( &light ) ;
   SET_COORD4( light, 30.0, -30.0, 30.0, lscale ) ;
   lib_output_light( &light ) ;
   SET_COORD4( light, -40.0, -30.0, 20.0, lscale ) ;
   lib_output_light( &light ) ;

   /* output field polygon - green */
   SET_COORD( back_color, 0.2, 0.7, 0.2 ) ;
   lib_output_color(NULL, &back_color, 0.2, 0.8, 0.0, 0.0, 0.0, 0.0, 1.0 );
   SET_COORD( field[0],  50.0,  50.0, 0.0 ) ;
   SET_COORD( field[1], -50.0,  50.0, 0.0 ) ;
   SET_COORD( field[2], -50.0, -50.0, 0.0 ) ;
   SET_COORD( field[3],  50.0, -50.0, 0.0 ) ;
   lib_output_polygon( 4, field ) ;

   /* Output a blue box */
   SET_COORD( back_color, 0.2, 0.2, 0.7 ) ;
   lib_output_color(NULL, &back_color, 0.2, 0.8, 0.0, 0.0, 0.0, 0.0, 1.0 );
   SET_COORD(lower_left, -1, -1, -0.2);
   SET_COORD(upper_right, 1, 1, 0.2);
   lib_output_box(&lower_left, &upper_right);

   /* set up tree color - brown */
   SET_COORD( tree_color, 0.55, 0.4, 0.2 ) ;
   lib_output_color(NULL, &tree_color, 0.2, 0.7, 0.0, 0.3, 5.0, 0.0, 1.0 );

   /* create tree */
   create_tree();

   /* Make sure everything is cleaned up */
   if (raytracer_format == OUTPUT_RTRACE ||
       raytracer_format == OUTPUT_PLG) {
      lib_set_raytracer(raytracer_format);
      lib_flush_definitions();
      }
}
