/*
** License Applicability. Except to the extent portions of this file are
** made subject to an alternative license as permitted in the SGI Free
** Software License B, Version 1.1 (the "License"), the contents of this
** file are subject only to the provisions of the License. You may not use
** this file except in compliance with the License. You may obtain a copy
** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
**
** http://oss.sgi.com/projects/FreeB
**
** Note that, as provided in the License, the Software is distributed on an
** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
** PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
**
** Original Code. The Original Code is: OpenGL Sample Implementation,
** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
** Copyright in any portions created by third parties is as indicated
** elsewhere herein. All Rights Reserved.
**
** Additional Notice Provisions: The application programming interfaces
** established by SGI in conjunction with the Original Code are The
** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
** Window System(R) (Version 1.3), released October 19, 1998. This software
** was created using the OpenGL(R) version 1.2.1 Sample Implementation
** published by SGI, but has not been independently verified as being
** compliant with the OpenGL(R) version 1.2.1 Specification.
*/

/*
 * subdivider.cxx
 *
 * $Date: 2001/03/17 00:25:41 $ $Revision: 1.1 $
 * $Header: /cvsroot/mesa3d/Mesa/si-glu/libnurbs/internals/subdivider.cc,v 1.1 2001/03/17 00:25:41 brianp Exp $
 */

#include "glimports.h"
#include "myassert.h"
#include "mystdio.h"
#include "subdivider.h"
#include "arc.h"
#include "bezierarc.h"
#include "bin.h"
#include "renderhints.h"
#include "backend.h"
#include "mapdesc.h"
#include "quilt.h"
#include "patchlist.h"
#include "patch.h"
#include "nurbsconsts.h"
#include "trimvertpool.h"
#include "simplemath.h"

#include "polyUtil.h" //for function area()

//#define  PARTITION_TEST
#ifdef PARTITION_TEST
#include "partitionY.h"
#include "monoTriangulation.h"
#include "dataTransform.h"
#include "monoChain.h"

#endif


#define OPTIMIZE_UNTRIMED_CASE


Bin*
Subdivider::makePatchBoundary( const REAL *from, const REAL *to )
{ 
    Bin* ret = new Bin();
    REAL smin = from[0];
    REAL smax = to[0];
    REAL tmin = from[1];
    REAL tmax = to[1];

    pjarc = 0;

    Arc_ptr jarc = new(arcpool) Arc( arc_bottom, 0 );
    arctessellator.bezier( jarc, smin, smax, tmin, tmin );
    ret->addarc( jarc  );
    pjarc = jarc->append( pjarc );

    jarc = new(arcpool) Arc( arc_right, 0 );
    arctessellator.bezier( jarc, smax, smax, tmin, tmax );
    ret->addarc( jarc  );
    pjarc = jarc->append( pjarc );

    jarc = new(arcpool) Arc( arc_top, 0 );
    arctessellator.bezier( jarc, smax, smin, tmax, tmax );
    ret->addarc( jarc  );
    pjarc = jarc->append( pjarc );

    jarc = new(arcpool) Arc( arc_left, 0 );
    arctessellator.bezier( jarc, smin, smin, tmax, tmin );
    ret->addarc( jarc  );
    jarc->append( pjarc );

    assert( jarc->check() != 0 );
    return ret;
}

/*---------------------------------------------------------------------------
 * Subdivider - construct a subdivider
 *---------------------------------------------------------------------------
 */

Subdivider::Subdivider( Renderhints& r, Backend& b ) 
	: arcpool( sizeof( Arc), 1, "arcpool" ),
 	  bezierarcpool( sizeof( BezierArc ), 1, "Bezarcpool" ),
	  pwlarcpool( sizeof( PwlArc ), 1, "Pwlarcpool" ),
	  renderhints( r ),
	  arctessellator( trimvertexpool, pwlarcpool ), 
	  backend( b ),
	  slicer( b )
{
}

void
Subdivider::setJumpbuffer( JumpBuffer *j )
{
    jumpbuffer = j;
}

/*---------------------------------------------------------------------------
 * clear - reset all state after possible error condition
 *---------------------------------------------------------------------------
 */

void		
Subdivider::clear( void )
{
    trimvertexpool.clear();     
    arcpool.clear();
    pwlarcpool.clear();
    bezierarcpool.clear();
}

/*---------------------------------------------------------------------------
 * ~Subdivider - destroy a subdivider
 *---------------------------------------------------------------------------
 */

Subdivider::~Subdivider( void )
{
}

/*---------------------------------------------------------------------------
 * addArc - add a bezier arc to a trim loop and to a bin
 *---------------------------------------------------------------------------
 */
void
Subdivider::addArc( REAL *cpts, Quilt *quilt, long _nuid )
{
    BezierArc *bezierArc = new(bezierarcpool) BezierArc;
    Arc *jarc  		= new(arcpool) Arc( arc_none, _nuid );
    jarc->pwlArc	= 0;
    jarc->bezierArc	= bezierArc;
    bezierArc->order	= quilt->qspec->order;
    bezierArc->stride	= quilt->qspec->stride;
    bezierArc->mapdesc	= quilt->mapdesc;
    bezierArc->cpts	= cpts;
    initialbin.addarc( jarc );
    pjarc		= jarc->append( pjarc );
}

/*---------------------------------------------------------------------------
 * addArc - add a pwl arc to a trim loop and to a bin
 *---------------------------------------------------------------------------
 */

void
Subdivider::addArc( int npts, TrimVertex *pts, long _nuid ) 
{
    Arc *jarc 		= new(arcpool) Arc( arc_none, _nuid );
    jarc->pwlArc	= new(pwlarcpool) PwlArc( npts, pts );        
    initialbin.addarc( jarc  );
    pjarc		= jarc->append( pjarc );
}

void
Subdivider::beginQuilts( void )
{
    qlist = 0;
}

void
Subdivider::addQuilt( Quilt *quilt )
{
    quilt->next = qlist;
    qlist = quilt;
}

/*---------------------------------------------------------------------------
 * drawSurfaces - main entry point for surface tessellation
 *---------------------------------------------------------------------------
 */

void
Subdivider::drawSurfaces( long nuid )
{
    renderhints.init( );

    if (qlist == NULL) 
      {
	//initialbin could be nonempty due to some errors
	freejarcs(initialbin);
	return;
      }

    for( Quilt *q = qlist; q; q = q->next ) {
	if( q->isCulled( ) == CULL_TRIVIAL_REJECT ) {
	    freejarcs( initialbin );
	    return;
	}
    }


    REAL from[2], to[2];
    qlist->getRange( from, to, spbrkpts, tpbrkpts );
#ifdef OPTIMIZE_UNTRIMED_CASE
    //perform optimization only when the samplng method is 
    //DOMAIN_DISTANCE and the display methdo is either 
    //fill or outline_polygon.
    int optimize = (is_domain_distance_sampling && (renderhints.display_method != N_OUTLINE_PATCH));
#endif

    if( ! initialbin.isnonempty() ) {
#ifdef OPTIMIZE_UNTRIMED_CASE
        if(! optimize )
	  {	

	  makeBorderTrim( from, to );
	  }
#else
	makeBorderTrim( from, to );
#endif
    } else {
	REAL rate[2];
	qlist->findRates( spbrkpts, tpbrkpts, rate );

    	if( decompose( initialbin, min(rate[0], rate[1]) ) ) 
	    mylongjmp( jumpbuffer, 31 );
    }

    backend.bgnsurf( renderhints.wiretris, renderhints.wirequads, nuid );

#ifdef PARTITION_TEST
 if(    initialbin.isnonempty() && spbrkpts.end-2 == spbrkpts.start && 
	tpbrkpts.end-2 == tpbrkpts.start)
{
    for(int i=spbrkpts.start; i<spbrkpts.end-1; i++){
      for(int j=tpbrkpts.start; j<tpbrkpts.end-1; j++){
	Real pta[2], ptb[2];
	pta[0] = spbrkpts.pts[i];
	ptb[0] = spbrkpts.pts[i+1];
	pta[1] = tpbrkpts.pts[j];
	ptb[1] = tpbrkpts.pts[j+1];
	qlist->downloadAll(pta, ptb, backend);

	directedLine *poly;

	  {

	    poly = bin_to_DLineLoops(initialbin);
	    
	    poly=poly->deleteDegenerateLinesAllPolygons();	    	

    sampledLine* retSampledLines;
//printf("before MC_partition\n");	    
	    poly = MC_partitionY(poly, &retSampledLines);
//printf("after MC_partition\n");	    

	  }


	{
	  primStream pStream(5000,5000);
	  directedLine* temp;

	  for(temp=poly; temp != NULL; temp=temp->getNextPolygon())

	    monoTriangulation(temp, &pStream);

	  slicer.evalStream(&pStream);

	}
	//need to clean up space
      }
    }
    freejarcs( initialbin );
    backend.endsurf();
    return;

    /*
    printf("num_polygons=%i\n", poly->numPolygons());
    printf("num_edges=%i\n", poly->numEdgesAllPolygons());
    poly->writeAllPolygons("zloutputFile");
    return;
    {
      primStream pStream(20,20);
      for(directedLine* tempD = poly; tempD != NULL; tempD = tempD->getNextPolygon())
	monoTriangulation(tempD, &pStream);
    }
    return;
    */
}
#endif //PARTITION_TEST


#ifdef OPTIMIZE_UNTRIMED_CASE
    if( (!initialbin.isnonempty())  && optimize )
      {
	int i,j;
	int num_u_steps;
        int num_v_steps;
	for(i=spbrkpts.start; i<spbrkpts.end-1; i++){
	  for(j=tpbrkpts.start; j<tpbrkpts.end-1; j++){
	    Real pta[2], ptb[2];
	    pta[0] = spbrkpts.pts[i];
	    ptb[0] = spbrkpts.pts[i+1];
	    pta[1] = tpbrkpts.pts[j];
	    ptb[1] = tpbrkpts.pts[j+1];
	    qlist->downloadAll(pta, ptb, backend);
	   
            num_u_steps = (int) (domain_distance_u_rate * (ptb[0]-pta[0]));
            num_v_steps = (int) (domain_distance_v_rate * (ptb[1]-pta[1]));

            if(num_u_steps <= 0) num_u_steps = 1;
            if(num_v_steps <= 0) num_v_steps = 1;

	    backend.surfgrid(pta[0], ptb[0], num_u_steps, 
			     ptb[1], pta[1], num_v_steps);
	    backend.surfmesh(0,0,num_u_steps,num_v_steps);

        

	    continue;
	    /* the following is left for reference purpose, don't delete
	    {
	    Bin* tempSource;	
	      Patchlist patchlist(qlist, pta, ptb);
	      patchlist.getstepsize();

	      tempSource=makePatchBoundary(pta, ptb);

	      tessellation(*tempSource, patchlist);

	      render(*tempSource);
	      delete tempSource;
	    }
	    */
	  }
	}
      }
    else
      subdivideInS( initialbin );
#else

    subdivideInS( initialbin );
#endif

    backend.endsurf();

}

void
Subdivider::subdivideInS( Bin& source )
{
    if( renderhints.display_method == N_OUTLINE_PARAM ) {
	outline( source );
	freejarcs( source );
    } else {
	setArcTypeBezier();
	setNonDegenerate();
	splitInS( source, spbrkpts.start, spbrkpts.end );
    }
}


/*---------------------------------------------------------------------------
 * splitInS - split a patch and a bin by an isoparametric line
 *---------------------------------------------------------------------------
 */

void
Subdivider::splitInS( Bin& source, int start, int end )
{
    if( source.isnonempty() ) {
        if( start != end ) {
	    int	i = start + (end - start) / 2;
	    Bin left, right;
	    split( source, left, right, 0, spbrkpts.pts[i] );
	    splitInS( left, start, i );
	    splitInS( right, i+1, end );
        } else {
	    if( start == spbrkpts.start || start == spbrkpts.end ) {
		freejarcs( source );
	    } else if( renderhints.display_method == N_OUTLINE_PARAM_S ) {
		outline( source );
		freejarcs( source );
	    } else {
		setArcTypeBezier();
		setNonDegenerate();
		s_index = start;
		splitInT( source, tpbrkpts.start, tpbrkpts.end );
	    }
        }
    } 
}

/*---------------------------------------------------------------------------
 * splitInT - split a patch and a bin by an isoparametric line
 *---------------------------------------------------------------------------
 */

void
Subdivider::splitInT( Bin& source, int start, int end )
{
    if( source.isnonempty() ) {
        if( start != end ) {
	    int	i = start + (end - start) / 2;
	    Bin left, right;
	    split( source, left, right, 1, tpbrkpts.pts[i] );
	    splitInT( left, start, i );
	    splitInT( right, i+1, end );
        } else {
	    if( start == tpbrkpts.start || start == tpbrkpts.end ) {
		freejarcs( source );
	    } else if( renderhints.display_method == N_OUTLINE_PARAM_ST ) {
		outline( source );
		freejarcs( source );
	    } else {
		t_index = start;
		setArcTypeBezier();
		setDegenerate();

		REAL pta[2], ptb[2];
		pta[0] = spbrkpts.pts[s_index-1];
		pta[1] = tpbrkpts.pts[t_index-1];

		ptb[0] = spbrkpts.pts[s_index];
		ptb[1] = tpbrkpts.pts[t_index];
		qlist->downloadAll( pta, ptb, backend );
	    
		Patchlist patchlist( qlist, pta, ptb );
/*
printf("-------samplingSplit-----\n");
source.show("samplingSplit source");
*/
		samplingSplit( source, patchlist, renderhints.maxsubdivisions, 0 );
		setNonDegenerate();
		setArcTypeBezier();
	    }
        }
    } 
}

/*--------------------------------------------------------------------------
 * samplingSplit - recursively subdivide patch, cull check each subpatch  
 *--------------------------------------------------------------------------
 */

void
Subdivider::samplingSplit( 
    Bin& source, 
    Patchlist& patchlist, 
    int subdivisions, 
    int param )
{
    if( ! source.isnonempty() ) return;

    if( patchlist.cullCheck() == CULL_TRIVIAL_REJECT ) {
	freejarcs( source );
	return;
    }

    patchlist.getstepsize();

    if( renderhints.display_method == N_OUTLINE_PATCH ) {
        tessellation( source, patchlist );
	outline( source );
	freejarcs( source );
	return;
    } 

    //patchlist.clamp();

    tessellation( source, patchlist );

    if( patchlist.needsSamplingSubdivision() && (subdivisions > 0) ) {
	if( ! patchlist.needsSubdivision( 0 ) )
	    param = 1;
	else if( ! patchlist.needsSubdivision( 1 ) )
	    param = 0;
	else
	    param = 1 - param;

	Bin left, right;
	REAL mid = ( patchlist.pspec[param].range[0] +
		     patchlist.pspec[param].range[1] ) * 0.5;
	split( source, left, right, param, mid );
	Patchlist subpatchlist( patchlist, param, mid );
	samplingSplit( left, subpatchlist, subdivisions-1, param );
	samplingSplit( right, patchlist, subdivisions-1, param );
    } else {
	setArcTypePwl();
	setDegenerate();
	nonSamplingSplit( source, patchlist, subdivisions, param );
	setDegenerate();
	setArcTypeBezier();
    }
}

void
Subdivider::nonSamplingSplit( 
    Bin& source, 
    Patchlist& patchlist, 
    int subdivisions, 
    int param )
{
    if( patchlist.needsNonSamplingSubdivision() && (subdivisions > 0) ) {
	param = 1 - param;

	Bin left, right;
	REAL mid = ( patchlist.pspec[param].range[0] +
		     patchlist.pspec[param].range[1] ) * 0.5;
	split( source, left, right, param, mid );
	Patchlist subpatchlist( patchlist, param, mid );
	if( left.isnonempty() )
	    if( subpatchlist.cullCheck() == CULL_TRIVIAL_REJECT ) 
		freejarcs( left );
	    else
	        nonSamplingSplit( left, subpatchlist, subdivisions-1, param );
	if( right.isnonempty() ) 
	    if( patchlist.cullCheck() == CULL_TRIVIAL_REJECT ) 
		freejarcs( right );
	    else
	        nonSamplingSplit( right, patchlist, subdivisions-1, param );

    } else {
	// make bbox calls
	patchlist.bbox();
	backend.patch( patchlist.pspec[0].range[0], patchlist.pspec[0].range[1],
		       patchlist.pspec[1].range[0], patchlist.pspec[1].range[1] );
    
	if( renderhints.display_method == N_OUTLINE_SUBDIV ) {
	    outline( source );
	    freejarcs( source );
	} else {
	    setArcTypePwl();
	    setDegenerate();
	    findIrregularS( source );
	    monosplitInS( source, smbrkpts.start, smbrkpts.end );
	}
    }
}

/*--------------------------------------------------------------------------
 * tessellation - set tessellation of interior and boundary of patch
 *--------------------------------------------------------------------------
 */

void
Subdivider::tessellation( Bin& bin, Patchlist &patchlist )
{
    // tessellate unsampled trim curves
    tessellate( bin, patchlist.pspec[1].sidestep[1], patchlist.pspec[0].sidestep[1],
	 patchlist.pspec[1].sidestep[0], patchlist.pspec[0].sidestep[0] );

    // set interior sampling rates
    slicer.setstriptessellation( patchlist.pspec[0].stepsize, patchlist.pspec[1].stepsize );

    //added by zl: set the order which will be used in slicer.c++
    slicer.set_ulinear( (patchlist.get_uorder() == 2));
    slicer.set_vlinear( (patchlist.get_vorder() == 2));

    // set boundary sampling rates
    stepsizes[0] = patchlist.pspec[1].stepsize;
    stepsizes[1] = patchlist.pspec[0].stepsize;
    stepsizes[2] = patchlist.pspec[1].stepsize;
    stepsizes[3] = patchlist.pspec[0].stepsize;
}

/*---------------------------------------------------------------------------
 * monosplitInS - split a patch and a bin by an isoparametric line
 *---------------------------------------------------------------------------
 */

void
Subdivider::monosplitInS( Bin& source, int start, int end )
{
    if( source.isnonempty() ) {
        if( start != end ) {
	    int	i = start + (end - start) / 2;
	    Bin left, right;
	    split( source, left, right, 0, smbrkpts.pts[i] );
	    monosplitInS( left, start, i );
	    monosplitInS( right, i+1, end );
        } else {
	    if( renderhints.display_method == N_OUTLINE_SUBDIV_S ) {
		outline( source );
		freejarcs( source );
	    } else {
		setArcTypePwl();
		setDegenerate();
		findIrregularT( source );
		monosplitInT( source, tmbrkpts.start, tmbrkpts.end );
	    }
        }
    } 
}

/*---------------------------------------------------------------------------
 * monosplitInT - split a patch and a bin by an isoparametric line
 *---------------------------------------------------------------------------
 */

void
Subdivider::monosplitInT( Bin& source, int start, int end )
{
    if( source.isnonempty() ) {
        if( start != end ) {
	    int	i = start + (end - start) / 2;
	    Bin left, right;
	    split( source, left, right, 1, tmbrkpts.pts[i] );
	    monosplitInT( left, start, i );
	    monosplitInT( right, i+1, end );
        } else {
	    if( renderhints.display_method == N_OUTLINE_SUBDIV_ST ) {
		outline( source );
		freejarcs( source );
	    } else {
/*
printf("*******render\n");
source.show("source\n");
*/
		render( source );
		freejarcs( source );
	    }
        }
    } 
}


/*----------------------------------------------------------------------------
 * findIrregularS - determine points of non-monotonicity is s direction
 *----------------------------------------------------------------------------
 */

void
Subdivider::findIrregularS( Bin& bin )
{
    assert( bin.firstarc()->check() != 0 );

    smbrkpts.grow( bin.numarcs() );

    for( Arc_ptr jarc=bin.firstarc(); jarc; jarc=bin.nextarc() ) {
	REAL *a = jarc->prev->tail();
	REAL *b = jarc->tail();
	REAL *c = jarc->head();

	if( b[1] == a[1] && b[1] == c[1] ) continue;

	//corrected code
	if((b[1]<=a[1] && b[1] <= c[1]) ||
	   (b[1]>=a[1] && b[1] >= c[1]))
	  {
	    //each arc (jarc, jarc->prev, jarc->next) is a 
	    //monotone arc consisting of multiple line segements.
	    //it may happen that jarc->prev and jarc->next are the same,
	    //that is, jarc->prev and jarc form a closed loop.
	    //In such case, a and c will be the same.
            if(a[0]==c[0] && a[1] == c[1])
	      {
		if(jarc->pwlArc->npts >2)
		  {
		    c = jarc->pwlArc->pts[jarc->pwlArc->npts-2].param;
		  }
		else
		  {
		    assert(jarc->prev->pwlArc->npts>2);
		    a = jarc->prev->pwlArc->pts[jarc->prev->pwlArc->npts-2].param;
		  }
		    
	      }
	    if(area(a,b,c) < 0)
	      {
		smbrkpts.add(b[0]);
	      }

	  }

	/* old code, 
	if( b[1] <= a[1] && b[1] <= c[1] ) {
	    if( ! ccwTurn_tr( jarc->prev, jarc ) )
                smbrkpts.add( b[0] );
	} else if( b[1] >= a[1] && b[1] >= c[1] ) {
	    if( ! ccwTurn_tl( jarc->prev, jarc ) )
                smbrkpts.add( b[0] );
        }
	*/

    }

    smbrkpts.filter();
} 

/*----------------------------------------------------------------------------
 * findIrregularT - determine points of non-monotonicity in t direction
 *		     where one arc is parallel to the s axis.
 *----------------------------------------------------------------------------
 */

void
Subdivider::findIrregularT( Bin& bin )
{
    assert( bin.firstarc()->check() != 0 );

    tmbrkpts.grow( bin.numarcs() );

    for( Arc_ptr jarc=bin.firstarc(); jarc; jarc=bin.nextarc() ) {
	REAL *a = jarc->prev->tail();
	REAL *b = jarc->tail();
	REAL *c = jarc->head();

	if( b[0] == a[0] && b[0] == c[0] ) continue;

	if( b[0] <= a[0] && b[0] <= c[0] ) {
	    if( a[1] != b[1] && b[1] != c[1] ) continue; 
	    if( ! ccwTurn_sr( jarc->prev, jarc ) )
                tmbrkpts.add( b[1] );
	} else if ( b[0] >= a[0] && b[0] >= c[0] ) {
	    if( a[1] != b[1] && b[1] != c[1] ) continue; 
	    if( ! ccwTurn_sl( jarc->prev, jarc ) )
                tmbrkpts.add( b[1] );
	}
    }
    tmbrkpts.filter( );
}

/*-----------------------------------------------------------------------------
 * makeBorderTrim - if no user input trimming data then create 
 * a trimming curve around the boundaries of the Quilt.  The curve consists of
 * four Jordan arcs, one for each side of the Quilt, connected, of course,
 * head to tail. 
 *-----------------------------------------------------------------------------
 */

void
Subdivider::makeBorderTrim( const REAL *from, const REAL *to )
{ 
    REAL smin = from[0];
    REAL smax = to[0];
    REAL tmin = from[1];
    REAL tmax = to[1];

    pjarc = 0;

    Arc_ptr jarc = new(arcpool) Arc( arc_bottom, 0 );
    arctessellator.bezier( jarc, smin, smax, tmin, tmin );
    initialbin.addarc( jarc  );
    pjarc = jarc->append( pjarc );

    jarc = new(arcpool) Arc( arc_right, 0 );
    arctessellator.bezier( jarc, smax, smax, tmin, tmax );
    initialbin.addarc( jarc  );
    pjarc = jarc->append( pjarc );

    jarc = new(arcpool) Arc( arc_top, 0 );
    arctessellator.bezier( jarc, smax, smin, tmax, tmax );
    initialbin.addarc( jarc  );
    pjarc = jarc->append( pjarc );

    jarc = new(arcpool) Arc( arc_left, 0 );
    arctessellator.bezier( jarc, smin, smin, tmax, tmin );
    initialbin.addarc( jarc  );
    jarc->append( pjarc );

    assert( jarc->check() != 0 );
}

/*----------------------------------------------------------------------------
 * render - renders all monotone regions in a bin and frees the bin
 *----------------------------------------------------------------------------
 */

void
Subdivider::render( Bin& bin )
{
    bin.markall();

#ifdef N_ISOLINE_S
    slicer.setisolines( ( renderhints.display_method == N_ISOLINE_S ) ? 1 : 0 );
#else
    slicer.setisolines( 0 );
#endif

    for( Arc_ptr jarc=bin.firstarc(); jarc; jarc=bin.nextarc() ) {
	if( jarc->ismarked() ) {
	    assert( jarc->check( ) != 0 );
	    Arc_ptr jarchead = jarc;
	    do {
		jarc->clearmark();
		jarc = jarc->next;
	    } while (jarc != jarchead);
	    slicer.slice( jarc );
	}
    }
}

/*---------------------------------------------------------------------------
 * outline - render the trimmed patch by outlining the boundary 
 *---------------------------------------------------------------------------
 */

void
Subdivider::outline( Bin& bin )
{
    bin.markall();
    for( Arc_ptr jarc=bin.firstarc(); jarc; jarc=bin.nextarc() ) {
	if( jarc->ismarked() ) {
	    assert( jarc->check( ) != 0 );
	    Arc_ptr jarchead = jarc;
	    do {
		slicer.outline( jarc );
		jarc->clearmark();
		jarc = jarc->prev;
	    } while (jarc != jarchead);
	}
    }
}

/*---------------------------------------------------------------------------
 * freejarcs - free all arcs in a bin
 *---------------------------------------------------------------------------
 */

void
Subdivider::freejarcs( Bin& bin )
{
    bin.adopt();	/* XXX - should not be necessary */

    Arc_ptr jarc;
    while( (jarc = bin.removearc()) != NULL ) {
	if( jarc->pwlArc ) jarc->pwlArc->deleteMe( pwlarcpool ); jarc->pwlArc = 0;
	if( jarc->bezierArc) jarc->bezierArc->deleteMe( bezierarcpool ); jarc->bezierArc = 0;
	jarc->deleteMe( arcpool );
    }
}

/*----------------------------------------------------------------------------
 * tessellate - tessellate all Bezier arcs in a bin
 * 		   1) only accepts linear Bezier arcs as input 
 * 		   2) the Bezier arcs are stored in the pwlArc structure
 * 		   3) only vertical or horizontal lines work
 * 		-- should 
 * 		   1) represent Bezier arcs in BezierArc structure
 * 		      (this requires a multitude of changes to the code)
 * 		   2) accept high degree Bezier arcs (hard)
 * 		   3) map the curve onto the surface to determine tessellation
 * 		   4) work for curves of arbitrary geometry
 *----------------------------------------------------------------------------
 */


void
Subdivider::tessellate( Bin& bin, REAL rrate, REAL trate, REAL lrate, REAL brate )
{
    for( Arc_ptr jarc=bin.firstarc(); jarc; jarc=bin.nextarc() ) {
	if( jarc->isbezier( ) ) {
    	    assert( jarc->pwlArc->npts == 2 );	
	    TrimVertex  *pts = jarc->pwlArc->pts;
    	    REAL s1 = pts[0].param[0];
    	    REAL t1 = pts[0].param[1];
    	    REAL s2 = pts[1].param[0];
    	    REAL t2 = pts[1].param[1];
	    
    	    jarc->pwlArc->deleteMe( pwlarcpool ); jarc->pwlArc = 0;
	    
	    switch( jarc->getside() ) {
		case arc_left:
		    assert( s1 == s2 );
		    arctessellator.pwl_left( jarc, s1, t1, t2, lrate );
		    break;
		case arc_right:
		    assert( s1 == s2 );
		    arctessellator.pwl_right( jarc, s1, t1, t2, rrate );
		    break;
		case arc_top:
		    assert( t1 == t2 );
		    arctessellator.pwl_top( jarc, t1, s1, s2, trate );
		    break;
		case arc_bottom:
		    assert( t1 == t2 );
		    arctessellator.pwl_bottom( jarc, t1, s1, s2, brate );
		    break;
		case arc_none:
		    (void) abort();
		    break;
	    }
	    assert( ! jarc->isbezier() );
    	    assert( jarc->check() != 0 );
	}
    }
}
