/*---------------------------------------------------------------------------
 *    draw.c
 *
 *     This file contains routines that calculate self hidden surfaces,
 *  priorizes the link and face drawing order, and then displays the arm on
 *  the screen.
 *---------------------------------------------------------------------------
*/

#include  <exec/types.h>
#include  <intuition/intuition.h>
#include  <lattice/stdio.h>
#include  "defs.h"
#include  "ds.h"


draw_wire_frame_arm (rp)

struct RastPort  *rp ;
{
 LINK_PTR      l ;
 FACE_PTR      f ;
 int           *x,  *y ;
 int           firstx,  firsty ;
 int           i,  j,  k ; 

 /*---------------------------------------------------------------------------
  *    Draw a wire frame view of the arm.  The pre-calculated current int X
  * and Y coordinates are used. 
  *---------------------------------------------------------------------------
 */
 SetAPen (rp, COLOR7) ;

 l  = &arm.link [UPPER] ;

 for (k = 0;  k < NUM_LINKS;  ++k)   {
     f  = &l->face[0] ;

     for (i = 0;  i < l->num_faces;  ++i)  {
         firstx  = *(x  = &f->x [0]) ;
         firsty  = *(y  = &f->y [0]) ;

         Move (rp, firstx, firsty) ;

         for (j = 1;  j < f->num_vertices;  ++j)
             Draw (rp, *(++x), *(++y)) ;
        
         Draw (rp, firstx, firsty) ;
         ++f ;
     }
     ++l ;
 }
}


draw_solid_arm (rp)

struct RastPort  *rp ;
{ 
 LINK_PTR      l ;
 FACE_PTR      f ;
 int           *x,  *y,  *ord_l ;
 int           i,  j,  k ;

 /*---------------------------------------------------------------------------
  *    Draw a solid view of the arm with hidden surfaces removed.
  *  The pre-calculated current int X and Y coordinates are used. 
  *    Back faces of solids are eliminated and the order of drawing the 
  * links and hand faces are calculated, based on a simple average of the 
  * maximum and minimum link (or hand face) Z coordinates.  
  *    It is not necessary to priorize the drawing order for the upper
  * and lower arm faces due to there simplicity.
  *---------------------------------------------------------------------------
 */
 mark_hidden_surfaces () ;

 priorize_draw_order () ;

 ord_l  = arm.l_order ;

 for (k = 0;  k < NUM_LINKS;  ++k)   {

     l  = &arm.link [*ord_l++] ;

     if (l->num_faces == arm.link [HAND].num_faces)
        draw_hand (l, rp) ;

     else   {
        f  = &l->face [0] ;
  
        for (i = 0;  i < l->num_faces;  ++i)  {
            if (f->visible)   {         
                SetAPen (rp, f->color) ;
                x  = &f->x [0] ;
                y  = &f->y [0] ;

                AreaMove (rp, *x, *y) ;
       
                for (j = 1;  j < f->num_vertices;  ++j)
                    AreaDraw (rp, *(++x), *(++y)) ;
                                         
                AreaEnd (rp) ;
            }
        ++f ;
        }
     }
 }
}


draw_hand (l, rp)

LINK_PTR         l ;
struct RastPort  *rp ;
{ 
 FACE_PTR      f ;
 int           *x,  *y,  *ord_f ;
 int           i,  j ;
 
 /*----------------------------------------------------------------------------
  *   Draw the hand.  A face is drawn if it is visible (not a back plane of 
  * a solid).  The hand faces are drawn in the order specified in the 
  * "arm.link [HAND].f_order [MAX_FACES]" array.  This array contains the 
  * correct index order to draw the hand faces.
  *----------------------------------------------------------------------------
 */
 ord_f  = l->f_order ;

 for (i = 0;  i < l->num_faces;  ++i)  {
     f  = &l->face [*ord_f++] ;

     if (f->visible)   {  
        SetAPen (rp, f->color) ;
        x  = &f->x [0] ;
        y  = &f->y [0] ;

        AreaMove (rp, *x, *y) ;
       
        for (j = 1;  j < f->num_vertices;  ++j)
            AreaDraw (rp, *(++x), *(++y)) ;
                                            
            AreaEnd (rp) ;
    }
 }
}


mark_hidden_surfaces ()

{
 LINK_PTR       l ;
 FACE_PTR       f ;
 FFP_POINT_PTR  p1,  p2,  p0 ;
 int            *c1,  *c2 ;     
 int            x1X,  x1Y,  x1Z,  x2X,  x2Y,  x2Z ;
 int            a,  b,  c,  d, t1, ;
 int            i,  j,  k ;

 /*---------------------------------------------------------------------------
  *   Calculate back planes of all the arm links.  The plane equation for each
  * face is calculated using the equation in David Rogers "Procedural
  * Elements for Computer Graphics", p.209.  The dot product of the face
  * normal and a vector at infinity (directly behind the viewpoint) is
  * calculated.  The sign of the result determines whether the face is on the
  * back side of an object.
  *--------------------------------------------------------------------------
 */
 l  = &arm.link [UPPER] ;

 for (k = 0;  k < NUM_LINKS;  ++k)   {

     f  = &l->face[0] ;

     for (i = 0;  i < l->num_faces;  ++i)  {
         p0  =  p1  = &f->ffp_curr_vertex [0] ;  
         p2  = &f->ffp_curr_vertex [1] ;
         a  =  b  =  c  =   ffp.zero.i ;

         for (j = 1;  j < f->num_vertices;  ++j)    {
             c1   = (int *) p1 ;     c2   = (int *) p2 ;

             x1X  = *(c1+X) ;     x1Y  = *(c1+Y) ;     x1Z  = *(c1+Z) ;  
             x2X  = *(c2+X) ;     x2Y  = *(c2+Y) ;     x2Z  = *(c2+Z) ;    

             a  = SPAdd (a, SPMul (SPSub (x2Y, x1Y), SPAdd (x2Z, x1Z) )) ;
             b  = SPAdd (b, SPMul (SPSub (x2Z, x1Z), SPAdd (x2X, x1X) )) ;
             c  = SPAdd (c, SPMul (SPSub (x2X, x1X), SPAdd (x2Y, x1Y) )) ;
                                                    
             ++p1 ;  ++p2 ;             
         }
         c1   = (int *) p1 ;      c2   = (int *) p0 ;

         x1X  = *(c1+X) ;     x1Y  = *(c1+Y) ;     x1Z  = *(c1+Z) ;  
         x2X  = *(c2+X) ;     x2Y  = *(c2+Y) ;     x2Z  = *(c2+Z) ;    

         a  = SPAdd (a, SPMul (SPSub (x2Y, x1Y), SPAdd (x2Z, x1Z) )) ;
         b  = SPAdd (b, SPMul (SPSub (x2Z, x1Z), SPAdd (x2X, x1X) )) ;
         c  = SPAdd (c, SPMul (SPSub (x2X, x1X), SPAdd (x2Y, x1Y) )) ;
         d =  SPNeg (SPAdd (SPMul (a, x2X), SPAdd (SPMul (b, x2Y),
                                                   SPMul (c, x2Z )))) ;
         f->plane_eqn [X]  = a ; 
         f->plane_eqn [Y]  = b ;
         f->plane_eqn [Z]  = c ;
         f->plane_eqn [W]  = d ;

         t1  = hidden_surface_dot_product (f->plane_eqn) ;
     
         if (SPCmp (t1, ffp.zero.i) == 1)
            f->visible  = NO ;
         else
            f->visible  = YES ;

         ++f ;
     }
     ++l ;
 }
}


hidden_surface_dot_product (plane_eqn)

int     plane_eqn [] ;
{
 int    sum,  i;

 /*---------------------------------------------------------------------------
  *   Take the dot product of the plane equation and a pre-calculated
  * FFP point at infinity.
  *---------------------------------------------------------------------------
 */
 sum  = ffp.zero.i ;

 for (i = 0;  i < 4;  ++i)
     sum  = SPAdd (sum, SPMul (plane_eqn [i], ffp.test_pt [i].i)) ;

 return (sum) ;
}


priorize_draw_order ()
{
 LINK_PTR      l ;
 FACE_PTR      f ;
 int  link_sort [NUM_LINKS] ;
 int  face_sort [MAX_FACES] ;
 int  i ;

 /*---------------------------------------------------------------------------
  *    Priorize the drawing order of the arm links and hand faces in order
  * to remove surfaces hidden by other parts of the arm.
  *    The average of the maximum and minimum link (or face) Z coordinates is
  * calculated and stored in an  array to be sorted.  The indices of the 
  * sorted links (or faces) are stored in the "f_order[]" and "l_order[]",
  * arrays in the arm data structure.
  *---------------------------------------------------------------------------
 */
 find_max_min_coords (Z) ;

 l  = &arm.link [0] ;
 f  = &arm.link [HAND].face [0] ;

 for (i = 0;  i < NUM_LINKS;  ++i)   {
     link_sort [i]  = SPDiv (ffp.two.i, SPAdd (l->lmin [Z], l->lmax [Z])) ;
     ++l ;
 }
 for (i = 0;  i < arm.link [HAND].num_faces;  ++i)   {
     face_sort [i]  =  SPDiv (ffp.two.i, SPAdd (f->fmin [Z], f->fmax [Z])) ;
     ++f ;
 }
 sort_items (link_sort, NUM_LINKS, arm.l_order) ; 
 sort_items (face_sort, arm.link [HAND].num_faces, arm.link [HAND].f_order) ;
}


sort_items (array, num, order)

int  array [],  num,  order [] ;
{
 int  max_z,  i,  j,  k ;

 /*---------------------------------------------------------------------------
  *   Destructively sort the array items and store the original indices of
  * the sorted elements in another array.  This sorting technique is commonly
  * referred to as the "stupid sort".  Not a lot of items to sort though.
  *---------------------------------------------------------------------------
 */
 
 for (i = 0;  i < num;  ++i)   {
     max_z  = ffp.small_num.i ;
     for (j = 0;  j < num;  ++j)   {
         if (SPCmp (array [j], max_z) == 1)   {
            max_z  = array [j] ;
            k    = j ;
         }
     }
     array [k]  = ffp.small_num.i ;
     order [i]  = k ;
 }
}
