/*
 * hilbert.c - Create a hilbert curve of spheres and cylinders
 *
 * Version:  (1.0) 30 January 1992
 * Author:  Alexander Enzmann
 *
 * SIZE_FACTOR determines the number of components output.
 */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "def.h"
#include "lib.h"

static int size_factor = 5;
static char *txname;
static double last_x, last_y, x, y;
static double offset_x, offset_y;

static void a(int i, double h);
static void b(int i, double h);
static void c(int i, double h);
static void d(int i, double h);


static void
plot(double x, double y, double h)
{
   COORD4 c0, c1;

   /* cylinder from (last_x, last_y) -> (x, y) */
   SET_COORD4(c0, last_x+offset_x, 0.0, last_y+offset_y, h/4.0);
   SET_COORD4(c1, x+offset_x, 0.0, y+offset_y, h/4.0);
   lib_output_cylcone(&c0, &c1, OUTPUT_CURVES);

   /* sphere at (x, y) */
   lib_output_sphere(&c1, OUTPUT_CURVES);

   /* Reset last position */
   last_x = x;
   last_y = y;
}

static void
a(int i, double h)
{
   if (i>0) {
      d(i-1, h); x -= h; plot(x, y, h);
      a(i-1, h); y -= h; plot(x, y, h);
      a(i-1, h); x += h; plot(x, y, h);
      b(i-1, h);
      }
}

static void
b(int i, double h)
{
   if (i>0) {
      c(i-1, h); y += h; plot(x, y, h);
      b(i-1, h); x += h; plot(x, y, h);
      b(i-1, h); y -= h; plot(x, y, h);
      a(i-1, h);
      }
}

static void
c(int i, double h)
{
   if (i>0) {
      b(i-1, h); x += h; plot(x, y, h);
      c(i-1, h); y += h; plot(x, y, h);
      c(i-1, h); x -= h; plot(x, y, h);
      d(i-1, h);
      }
}

static void
d(int i, double h)
{
   if (i>0) {
      a(i-1, h); y -= h; plot(x, y, h);
      d(i-1, h); x -= h; plot(x, y, h);
      d(i-1, h); y += h; plot(x, y, h);
      c(i-1, h);
      }
}

static void
create_hilbert(int depth, double width)
{
   COORD4 center;
   int i = 0;
   double h = width;

   for (i=0;i<depth;i++)
      h /= 2.0;

   /* Output a sphere at the start of the curve */
   offset_x = offset_y = width / 2.0;
   SET_COORD4(center, offset_x, 0.0, offset_y, h/4.0) ;
   lib_output_sphere(&center, OUTPUT_CURVES);
   last_x = last_y = 0.0;

   /* Crank out the rest of the curve */
   a(depth, h);
}

static void
create_textured_plane()
{
   printf("define test_map\n");
   printf("   color_map(\n");
   printf("      [0,   0.1, red,     orange]\n");
   printf("      [0.1, 0.3, orange,  blue]\n");
   printf("      [0.3, 0.5, blue,    skyblue]\n");
   printf("      [0.5, 0.7, skyblue, orange]\n");
   printf("      [0.7, 0.9, orange,  magenta]\n");
   printf("      [0.9, 1.0, magenta, red],\n");
   printf("      <1, 1, 1>)\n\n");
   printf("// Simple color map texture\n");
   printf("define noise_texture\n");
   printf("texture {\n");
   printf("   special surface {\n");
   printf("      color test_map[noise(P)]\n");
   printf("      ambient 0.2\n");
   printf("      diffuse 0.8\n");
   printf("      specular white, 0.5\n");
   printf("      microfacet Reitz 10\n");
   printf("      reflection white, 0.2\n");
   printf("      }\n");
   printf("   scale <0.3, 0.3, 0.3>\n");
   printf("   }\n\n");
   printf("define marble_turb 4\n");
   printf("define marble_fn  (sawtooth(P[0] +");
   printf(" marble_turb * noise(P,8)) + 1) / 2\n\n");
   printf("define white_marble_map\n");
   printf("   color_map(\n");
   printf("      [0.0, 0.8, <1.0, 1.0, 1.0>, <0.7, 0.7, 0.7>]\n");
   printf("      [0.8, 1.0, <0.7, 0.7, 0.7>, <0.1, 0.1, 0.1>],\n");
   printf("      white)\n\n");
   printf("// Simple marble textures\n");
   printf("define white_marble\n");
   printf("texture {\n");
   printf("   special surface {\n");
   printf("      color white_marble_map[marble_fn]\n");
   printf("      ambient 0.2\n");
   printf("      diffuse 0.8\n");
   printf("      specular white, 0.5\n");
   printf("      microfacet Reitz 10\n");
   printf("      reflection white, 0.2\n");
   printf("      }\n");
   printf("   scale <0.6, 0.6, 0.6>\n");
   printf("   }\n\n");
   printf("object {\n");
   printf("   polygon 4, <-20, -0.4, -20>, <-20, -0.4, 20>,\n");
   printf("              < 20, -0.4,  20>, < 20, -0.4,-20>\n");
   printf("   texture {\n");
   printf("      hexagon white_marble, %s, noise_texture\n", txname);
   printf("      scale <0.2, 0.2, 0.2>\n");
   printf("      }\n");
   printf("   }\n\n");
}

void
main(int argc, char *argv[])
{
    COORD4  back_color, curve_color ;
    COORD4  center_pt, light, dir;
    COORD4  from, at, up, msphere;
    double offset = 1.0;
    int i;

   /* Start by defining which raytracer we will be using */
   switch (argc) {
      case 2:
	 size_factor = atoi(argv[1]);
	 break;
      defaut:
	 break;
      }

   /* We are using Polyray */
   lib_set_raytracer(OUTPUT_POLYRAY);

    /* output viewpoint */
    SET_COORD(from, 0.0, 5.0,-3.0) ;
    SET_COORD(at,   0.0, 0.0, 0.0) ;
    SET_COORD(up,   0.0, 1.0, 0.0) ;
    lib_output_viewpoint( &from, &at, &up, 30.0, 1.0, 1.0, 512, 512);

    /* output background color - dark blue */
    SET_COORD( back_color, 0.039, 0.18, 0.376 ) ;
    lib_output_background_color( &back_color ) ;

    /* output light source */
    SET_COORD4( light,-5.0, 10.0,-20.0, 0.5) ;
    lib_output_light( &light ) ;
    SET_COORD4( light, 5.0, 10.0, 20.0, 0.5) ;
    lib_output_light( &light ) ;

    /* Make some mirrored spheres below the hilbert curve */
    SET_COORD(curve_color, 1.0, 1.0, 1.0 ) ;
    txname = lib_output_color(NULL, &curve_color,
			      0.1, 0.2, 0.8, 0.2, 5.0, 0.0, 0.0);
    
    for (i=1;i<size_factor;i++)
       offset /= 2.0;
    SET_COORD4(msphere,offset-1.0,-0.2, offset-1.0, 0.2);
    lib_output_sphere(&msphere, OUTPUT_CURVES);
    SET_COORD4(msphere,offset-1.0,-0.2, 1, 0.2);
    lib_output_sphere(&msphere, OUTPUT_CURVES);
    SET_COORD4(msphere,1,-0.2, offset-1.0, 0.2);
    lib_output_sphere(&msphere, OUTPUT_CURVES);
    SET_COORD4(msphere,1,-0.2, 1, 0.2);
    lib_output_sphere(&msphere, OUTPUT_CURVES);

    /* Dump out a plane with hexagonal tiling */
    create_textured_plane();

    /* output color - red */
    SET_COORD(curve_color, 1.0, 0.2, 0.2);
    lib_output_color(NULL, &curve_color, 0.2, 0.7, 0.0, 0.6, 5.0, 0.0, 1.0);

    /* compute and output hilbert curve */
    create_hilbert(size_factor, 2.0);
}
