/*********************************************************************
 *
 *         EZWGL, the EZ Widget and Graphics Library
 *
 *             Copyright (C) 1996, 1997  Maorong Zou
 *  
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 **********************************************************************/
/*
 *  June 1996.  Beta Release.
 *  Sept 1996.  Release Version 1.0
 *  Dec 1996.  Release Version 1.1 Beta
 *  April 1997.  Release Version 1.2
 *  November 1997.  Release Version 1.3
 */
/***************************************************************
 *
 * Functions deal with EdgeTables.
 *
 */
#ifndef USE_GL_MACROS

#define _EZ_POLY_ET_C_
#include <stdio.h>
#include "EZ_Widget.h"
#include "EZ_Poly.h"

/************************************************************
 * 
 * Functions implemented in this file
 */
void            EZ_Make_Edge_Table MY_ANSIARGS((
						int          n,
						int          *vts,
						EZ_ETable    *et,
						EZ_Edge      *aet,
						EZ_Edge      *edge_ptr,
						EZ_Scanline  *sline_storage));
void            EZ_Load_Edge MY_ANSIARGS(( EZ_Edge *aet, EZ_Edge *edges));

void            EZ_Insert_Edge MY_ANSIARGS((EZ_ETable       *etb,   
					    EZ_Edge         *edge,  
					    int             scanline,
					    EZ_Scanline     *place,
					    int             *iplace));

void            EZ_Sort_ActiveET MY_ANSIARGS((EZ_Edge *aet));

/************************************************************/

#define BIG_NUMBER      12345678
#define SMALL_NUMBER  (-BIG_NUMBER)

/**************************************************************
 * 
 * Insert "edge" into an edge table "etb"
 */

void EZ_Insert_Edge(etb, edge, scanline, place, iplace)
     int             scanline;    
     EZ_Edge         *edge;  
     EZ_ETable       *etb;   
     EZ_Scanline     *place;
     int             *iplace;              
{
  register EZ_Edge      *eptr, *eprev;
  register EZ_Scanline  *sptr, *sprev;

  /*
   * find the right bucket to insert.
   */
  sprev = &etb->slines;
  sptr = sprev->next;
  while(sptr && (sptr->sline < scanline))
    {
      sprev = sptr;
      sptr = sptr->next;
    }
  
  if( (!sptr) || (sptr->sline > scanline))
    {
      sptr = &(place[(*iplace)++]);
      sptr->next = sprev->next;
      sptr->edgelist = (EZ_Edge *)NULL;
      sprev->next = sptr;
    }
  sptr->sline = scanline;
  /* 
   * now insert the edge into edgelist of the bucket found,
   * keep edgelist sorted according to the x coordinate.
   *
   */
  eprev = (EZ_Edge *)NULL;
  eptr  = sptr->edgelist;
  while(eptr && (eptr->info.x < edge->info.x))
    {
      eprev = eptr;
      eptr = eptr->next;
    }
  edge->next = eptr;
  if(eprev)   eprev->next = edge;
  else        sptr->edgelist = edge;
}

/**************************************************************
 *
 * create and initialize Edge Table and Active Edge Table
 */

void EZ_Make_Edge_Table(n, vts, et, aet, edge_ptr, sline_storage)
     int          n;
     int          *vts;
     EZ_ETable    *et;
     EZ_Edge      *aet;
     EZ_Edge      *edge_ptr;
     EZ_Scanline  *sline_storage;
{
  int          dy, dx, itmp;
  int          sline_index = 0;
  int          order;
  int          current_vt, prev_vt;
  int          (*screen)[3], *prevptr, *currentptr;
  register int *topptr, *bottomptr;

  /*
   * initialize the active edge table
   */
  aet->next = (EZ_Edge *)NULL;
  aet->prev = (EZ_Edge *)NULL;
  aet->info.x = SMALL_NUMBER;
  
  /*
   * initialize the edge table
   */
  
  et->slines.next = (EZ_Scanline *)NULL;
  et->ymin = BIG_NUMBER;
  et->ymax = SMALL_NUMBER;

  screen = EZ_VBuf.screen;  
  prev_vt = vts[n-1];
  prevptr = screen[prev_vt];
  while(n--)
    {
      current_vt = *vts++;
      currentptr = screen[current_vt];

      if(currentptr[1] != prevptr[1])
	{
	  if(currentptr[1] > prevptr[1])
	    {
	      topptr = prevptr;
	      bottomptr = currentptr;
	      order = 0;
	    }
	  else
	    {
	      topptr = currentptr;
	      bottomptr = prevptr;
	      order = 1;
	    }
	  edge_ptr->ymax = bottomptr[1] - 1;
	  dy = bottomptr[1] - topptr[1];
	 
	  (edge_ptr->info).x = topptr[0];
	  dx = bottomptr[0] - topptr[0];

	  if( dx < 0) 
	    {
	      itmp = dx/dy;
	      (edge_ptr->info).s = itmp;
	      itmp--;
	      (edge_ptr->info).s1 = itmp;
	      itmp = 2*(dy * itmp - dx);
	      (edge_ptr->info).incr1 = itmp;
	      (edge_ptr->info).incr2 = itmp + (dy << 1);
	      (edge_ptr->info).d = itmp;
	    } 
	  else
	    { 
	      itmp = dx / dy;
	      (edge_ptr->info).s = itmp; 
	      itmp++;
	      (edge_ptr->info).s1 = itmp ;
	      itmp = 2*(dx - itmp * dy); 
	      (edge_ptr->info).incr1 = itmp;
	      (edge_ptr->info).d = (edge_ptr->info).incr2 = itmp + (dy << 1);
	    }
	  itmp = EZ_AuxTable[dy];
	  (edge_ptr->info).z = topptr[2];
	  (edge_ptr->info).dz = ((bottomptr[2]- topptr[2]) >> AUX_SHIFT_BITS) * itmp;
	  if(EZ_PolygonShadingMode == EZ_GOURAUD)
	    {
	      int (*rgbaptr)[4] = EZ_VBuf.rgba;
	      int *topclr, *bottomclr;

	      if(order)
		{
		  topclr = rgbaptr[current_vt];
		  bottomclr = rgbaptr[prev_vt];
		}
	      else
		{
		  topclr = rgbaptr[prev_vt];
		  bottomclr = rgbaptr[current_vt];
		}
	      if( EZ_ColorMode == EZ_RGB_MODE)
		{
		  int dr,dg,db;
		  (edge_ptr->info).clr.ri = topclr[0]; 
		  (edge_ptr->info).clr.g = topclr[1]; 
		  (edge_ptr->info).clr.b = topclr[2]; 
		  dr = ((bottomclr[0] - topclr[0]) >> AUX_SHIFT_BITS);
		  dg = ((bottomclr[1] - topclr[1]) >> AUX_SHIFT_BITS);
		  db = ((bottomclr[2] - topclr[2]) >> AUX_SHIFT_BITS);
		  (edge_ptr->info).clr.dri= (dr  - (dr >> FIX_NEG1)) * itmp;
		  (edge_ptr->info).clr.dg = (dg  - (dg >> FIX_NEG1)) * itmp;
		  (edge_ptr->info).clr.db = (db  - (db >> FIX_NEG1)) * itmp;
		}
	      else
		{
		  int dc;
		  (edge_ptr->info).clr.ri = topclr[0]; 
		  dc = ((bottomclr[0] - topclr[0]) >> AUX_SHIFT_BITS);
		  (edge_ptr->info).clr.dri = (dc - (dc >> FIX_NEG1)) * itmp;
		}
	    }
	  EZ_Insert_Edge(et,edge_ptr, topptr[1], sline_storage, &sline_index);

	  itmp = prevptr[1];
	  if(itmp > et->ymax) et->ymax = itmp;
	  if(itmp < et->ymin) et->ymin = itmp;

	  edge_ptr++;
	}
      prev_vt = current_vt;
      prevptr = currentptr;
    }
}
/**************************************************************
 *
 *  Load the edgelist 'edges' into the active edge table
 */

void EZ_Load_Edge(aet, edges)
     register EZ_Edge *aet, *edges;
{
  register EZ_Edge *prev, *tmp;
  
  prev = aet;
  aet = aet->next;

  while(edges)
    {
      while( aet && (aet->info.x < edges->info.x))
	{
	  prev = aet;
	  aet = aet->next;
	}
      tmp = edges->next;
      edges->next = aet;
      if(aet) aet->prev = edges;
      edges->prev = prev;
      prev->next = edges;
      prev = edges;
      
      edges = tmp;
    }
}

/**************************************************************
 *
 *  Sort the active edge table according to x coordinates
 */

void EZ_Sort_ActiveET(aet)
     register EZ_Edge *aet;
{
  register EZ_Edge *ptr;
  register EZ_Edge *pinsert;
  register EZ_Edge *prev;

  aet = aet->next;
  while(aet)
    {
      ptr = aet;
      pinsert = aet;
      while( ptr->prev->info.x > aet->info.x)
	ptr = ptr->prev;
      aet = aet->next;
      if(ptr != pinsert)
	{
	  prev = ptr->prev;
	  pinsert->prev->next = aet;
	  if(aet) aet->prev = pinsert->prev;
	  pinsert->next = ptr;
	  ptr->prev->next = pinsert;
	  ptr->prev = pinsert;
	  pinsert->prev = prev;
	}
    }
}
/************************************************************************/
#undef _EZ_POLY_ET_C_
#endif
