/*
======================================================================
libpr2.c

Eric Haines
Ernie Wright  01 Jun 05

A library of primitive object output routines, part 2 of 3.  Modified
for use in a LightWave Modeler plug-in.  The public routines are

   lib_output_cylcone()
   lib_output_disc()
   lib_output_sq_sphere()
   lib_output_sphere()
   lib_output_box()

These don't interact directly with Modeler.  They're all implemented
as calls to routines in libply.c,

   lib_output_polygon_cylcone()
   lib_output_polygon_disc()
   lib_output_polypatch()
   lib_output_polygon_sphere()
   lib_output_polygon_box()

where we finally get down to the level of emitting actual polygons.
====================================================================== */


#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>

#include "lib.h"


void lib_output_cylcone( COORD4 base_pt, COORD4 apex_pt, int curve_format )
{
   lib_output_polygon_cylcone( base_pt, apex_pt );
}


void lib_output_disc( COORD3 center, COORD3 normal, double iradius,
   double oradius, int curve_format )
{
   lib_output_polygon_disc( center, normal, iradius, oradius );
}


static void sq_sphere_val( double a1, double a2, double a3, double n,
   double e, double u, double v, COORD3 P)
{
   double cu, su, cv, sv;
   double icu, isu, icv, isv;

   cu = cos(u); su = sin(u);
   cv = cos(v); sv = sin(v);
   icu = SGN(cu); isu = SGN(su);
   icv = SGN(cv); isv = SGN(sv);
   cu = fabs(cu); cv = fabs(cv);
   su = fabs(su); sv = fabs(sv);
   P[X] = a1 * POW(cv, n) * POW(cu, e) * icv * icu;
   P[Y] = a2 * POW(cv, n) * POW(su, e) * icv * isu;
   P[Z] = a3 * POW(sv, n) * isv;
}


static void sq_sphere_norm( double a1, double a2, double a3, double n,
   double e, double u, double v, COORD3 N)
{
   double cu, su, cv, sv;
   double icu, isu, icv, isv;

   cu = cos(u); su = sin(u);
   cv = cos(v); sv = sin(v);
   icu = SGN(cu); isu = SGN(su);
   icv = SGN(cv); isv = SGN(sv);

   /* May be some singularities in the values, lets catch them & put
    * a fudged normal into N */
   if (e < 2 || n < 2) {
      if (ABS(cu) < 1.0e-3 || ABS(su) < 1.0e-3 ||
         ABS(cu) < 1.0e-3 || ABS(su) < 1.0e-3) {
         SET_COORD3(N, cu*cv, su*cv, sv);
         lib_normalize_vector(N);
         return;
      }
   }

   cu = fabs(cu); cv = fabs(cv);
   su = fabs(su); sv = fabs(sv);

   N[X] = a1 * POW(cv, 2-n) * POW(cu, 2-e) * icv * icu;
   N[Y] = a2 * POW(cv, 2-n) * POW(su, 2-e) * icv * isu;
   N[Z] = a3 * POW(sv, 2-n) * isv;
   lib_normalize_vector(N);
}


static void lib_output_polygon_sq_sphere( COORD3 center_pt,
   double a1, double a2, double a3, double n, double e )
{
   int i, j, u_res, v_res;
   double u, delta_u, v, delta_v;
   COORD3 verts[4], norms[4];

   u_res = 4 * gU_resolution;
   v_res = 4 * gV_resolution;
   delta_u = 2.0 * PI / (double)u_res;
   delta_v = PI / (double)v_res;

   for (i=0,u=0.0;i<u_res;i++,u+=delta_u) {
      for (j=0,v=-PI/2.0;j<v_res;j++,v+=delta_v) {
         if (j == 0) {
            sq_sphere_val(a1, a2, a3, n, e, u, v, verts[0]);
            sq_sphere_norm(a1, a2, a3, n, e, u, v, norms[0]);
            sq_sphere_val(a1, a2, a3, n, e, u, v+delta_v, verts[1]);
            sq_sphere_norm(a1, a2, a3, n, e, u, v+delta_v, norms[1]);
            sq_sphere_val(a1, a2, a3, n, e, u+delta_u, v+delta_v, verts[2]);
            sq_sphere_norm(a1, a2, a3, n, e, u+delta_u, v+delta_v,norms[2]);
            ADD3_COORD3(verts[0], verts[0], center_pt);
            ADD3_COORD3(verts[1], verts[1], center_pt);
            ADD3_COORD3(verts[2], verts[2], center_pt);
            lib_output_polypatch(3, verts, norms);
         } else if (j == v_res-1) {
            sq_sphere_val(a1, a2, a3, n, e, u, v, verts[0]);
            sq_sphere_norm(a1, a2, a3, n, e, u, v, norms[0]);
            sq_sphere_val(a1, a2, a3, n, e, u, v+delta_v, verts[1]);
            sq_sphere_norm(a1, a2, a3, n, e, u, v+delta_v, norms[1]);
            sq_sphere_val(a1, a2, a3, n, e, u+delta_u, v, verts[2]);
            sq_sphere_norm(a1, a2, a3, n, e, u+delta_u, v, norms[2]);
            ADD3_COORD3(verts[0], verts[0], center_pt);
            ADD3_COORD3(verts[1], verts[1], center_pt);
            ADD3_COORD3(verts[2], verts[2], center_pt);
            lib_output_polypatch(3, verts, norms);
         } else {
            sq_sphere_val(a1, a2, a3, n, e, u, v, verts[0]);
            sq_sphere_norm(a1, a2, a3, n, e, u, v, norms[0]);
            sq_sphere_val(a1, a2, a3, n, e, u, v+delta_v, verts[1]);
            sq_sphere_norm(a1, a2, a3, n, e, u, v+delta_v, norms[1]);
            sq_sphere_val(a1, a2, a3, n, e, u+delta_u, v+delta_v, verts[2]);
            sq_sphere_norm(a1, a2, a3, n, e, u+delta_u, v+delta_v,norms[2]);
            ADD3_COORD3(verts[0], verts[0], center_pt);
            ADD3_COORD3(verts[1], verts[1], center_pt);
            ADD3_COORD3(verts[2], verts[2], center_pt);
            lib_output_polypatch(3, verts, norms);
            COPY_COORD3(verts[1], verts[2]);
            COPY_COORD3(norms[1], norms[2]);
            sq_sphere_val(a1, a2, a3, n, e, u+delta_u, v, verts[2]);
            sq_sphere_norm(a1, a2, a3, n, e, u+delta_u, v, norms[2]);
            ADD3_COORD3(verts[2], verts[2], center_pt);
            lib_output_polypatch(3, verts, norms);
         }
      }
   }
}


void lib_output_sq_sphere( COORD3 center_pt, double a1, double a2, double a3,
   double n, double e, int curve_format )
{
   lib_output_polygon_sq_sphere( center_pt, a1, a2, a3, n, e );
}


void lib_output_sphere( COORD4 center_pt, int curve_format )
{
   lib_output_polygon_sphere(center_pt);
}


void lib_output_box( COORD3 p1, COORD3 p2 )
{
   lib_output_polygon_box(p1, p2);
}
