/*
* The SEI Software Open Source License, Version 1.0
*
* Copyright (c) 2004, Solution Engineering, Inc.
* All rights reserved.
*
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Solution Engineering, Inc. (http://www.seisw.com/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 3. The name "Solution Engineering" must not be used to endorse or
* promote products derived from this software without prior
* written permission. For written permission, please contact
* admin@seisw.com.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL SOLUTION ENGINEERING, INC. OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*/
package geomerative ;
import java.util.ArrayList ;
import java.util.List ;
/**
* Clip
is a Java version of the General RPolygon Clipper algorithm
* developed by Alan Murta (gpc@cs.man.ac.uk). The home page for the original source can be
* found at
* http://www.cs.man.ac.uk/aig/staff/alan/software/.
*
* polyClass:
Some of the public methods below take a polyClass
* argument. This java.lang.Class
object is assumed to implement the RPolygon
* interface and have a no argument constructor. This was done so that the user of the algorithm
* could create their own classes that implement the RPolygon
interface and still uses
* this algorithm.
*
* Implementation Note: The converted algorithm does support the difference
* operation, but a public method has not been provided and it has not been tested. To do so,
* simply follow what has been done for intersection.
*
* @author Dan Bridenbecker, Solution Engineering, Inc.
*/
class RClip
{
// -----------------
// --- Constants ---
// -----------------
private static final boolean DEBUG = false ;
// Maximum precision for floats
private static final double GPC_EPSILON = 2.2204460492503131e-016 ;
//private static final float GPC_EPSILON = 1.192092896e-07F;
static final String GPC_VERSION = "2.31";
private static final int LEFT = 0 ;
private static final int RIGHT = 1 ;
private static final int ABOVE = 0 ;
private static final int BELOW = 1 ;
private static final int CLIP = 0 ;
private static final int SUBJ = 1 ;
private static final boolean INVERT_TRISTRIPS = false ;
// ------------------------
// --- Member Variables ---
// ------------------------
// --------------------
// --- Constructors ---
// --------------------
/** Creates a new instance of Clip */
private RClip()
{
}
// ----------------------
// --- Static Methods ---
// ----------------------
/**
* Return the intersection of p1
and p2
where the
* return type is of polyClass
. See the note in the class description
* for more on RPolygon
to return
*/
static RPolygon intersection( RPolygon p1, RPolygon p2, Class polyClass )
{
return clip( OperationType.GPC_INT, p1, p2, polyClass );
}
/**
* Return the union of p1
and p2
where the
* return type is of polyClass
. See the note in the class description
* for more on RPolygon
to return
*/
static RPolygon union( RPolygon p1, RPolygon p2, Class polyClass )
{
return clip( OperationType.GPC_UNION, p1, p2, polyClass );
}
/**
* Return the xor of p1
and p2
where the
* return type is of polyClass
. See the note in the class description
* for more on RPolygon
to return
*/
static RPolygon xor( RPolygon p1, RPolygon p2, Class polyClass )
{
return clip( OperationType.GPC_XOR, p1, p2, polyClass );
}
/**
* Return the diff of p1
and p2
where the
* return type is of polyClass
. See the note in the class description
* for more on RPolygon
to return
*/
static RPolygon diff( RPolygon p1, RPolygon p2, Class polyClass )
{
return clip( OperationType.GPC_DIFF, p1, p2, polyClass );
}
/**
* Return the intersection of p1
and p2
where the
* return type is of PolyDefault
.
*
* @param p1 One of the polygons to performt he intersection with
* @param p2 One of the polygons to performt he intersection with
*/
static RPolygon intersection( RPolygon p1, RPolygon p2 )
{
return clip( OperationType.GPC_INT, p1, p2, RPolygon.class );
}
/**
* Return the union of p1
and p2
where the
* return type is of PolyDefault
.
*
* @param p1 One of the polygons to performt he union with
* @param p2 One of the polygons to performt he union with
*/
static RPolygon union( RPolygon p1, RPolygon p2 )
{
return clip( OperationType.GPC_UNION, p1, p2, RPolygon.class );
}
/**
* Return the xor of p1
and p2
where the
* return type is of PolyDefault
.
*
* @param p1 One of the polygons to performt he xor with
* @param p2 One of the polygons to performt he xor with
*/
static RPolygon xor( RPolygon p1, RPolygon p2 )
{
return clip( OperationType.GPC_XOR, p1, p2, RPolygon.class );
}
/**
* Return the diff of p1
and p2
where the
* return type is of PolyDefault
.
*
* @param p1 One of the polygons to performt he diff with
* @param p2 One of the polygons to performt he diff with
*/
static RPolygon diff( RPolygon p1, RPolygon p2 )
{
return clip( OperationType.GPC_DIFF, p1, p2, RPolygon.class );
}
/**
* Updates p1
.
*
* @param p1 One of the polygons to performt he diff with
*/
static RPolygon update( RPolygon p1 )
{
return clip( OperationType.GPC_DIFF, p1, new RPolygon(), RPolygon.class );
}
// -----------------------
// --- Private Methods ---
// -----------------------
/**
* Create a new RPolygon
type object using polyClass
.
*/
private static RPolygon createNewPoly( Class polyClass )
{
try
{
return (RPolygon)polyClass.newInstance();
}
catch( InstantiationException | IllegalAccessException e )
{
throw new RuntimeException(e);
}
}
/**
* clip()
is the main method of the clipper algorithm.
* This is where the conversion from really begins.
*/
private static RPolygon clip( OperationType op, RPolygon subj, RPolygon clip, Class polyClass )
{
if(RG.useFastClip) {
return FastRClip.clip(op, subj, clip, polyClass);
}
RPolygon result = createNewPoly( polyClass ) ;
/* Test for trivial NULL result cases */
if( (subj.isEmpty() && clip.isEmpty()) ||
(subj.isEmpty() && ((op == OperationType.GPC_INT) || (op == OperationType.GPC_DIFF))) ||
(clip.isEmpty() && (op == OperationType.GPC_INT)) )
{
return result ;
}
/* Identify potentialy contributing contours */
if( ((op == OperationType.GPC_INT) || (op == OperationType.GPC_DIFF)) &&
!subj.isEmpty() && !clip.isEmpty() )
{
minimax_test(subj, clip, op);
}
/* Build LMT */
LmtTable lmt_table = new LmtTable();
ScanBeamTreeEntries sbte = new ScanBeamTreeEntries();
if (!subj.isEmpty())
{
build_lmt(lmt_table, sbte, subj, SUBJ, op);
}
if( DEBUG )
{
System.out.println("");
System.out.println(" ------------ After build_lmt for subj ---------");
lmt_table.print();
}
if (!clip.isEmpty())
{
build_lmt(lmt_table, sbte, clip, CLIP, op);
}
if( DEBUG )
{
System.out.println("");
System.out.println(" ------------ After build_lmt for clip ---------");
lmt_table.print();
}
/* Return a NULL result if no contours contribute */
if (lmt_table.top_node == null)
{
return result;
}
/* Build scanbeam table from scanbeam tree */
float[] sbt = sbte.build_sbt();
int[] parity = new int[2] ;
parity[0] = LEFT ;
parity[1] = LEFT ;
/* Invert clip polygon for difference operation */
if (op == OperationType.GPC_DIFF)
{
parity[CLIP]= RIGHT;
}
if( DEBUG )
{
print_sbt(sbt);
}
LmtNode local_min = lmt_table.top_node ;
TopPolygonNode out_poly = new TopPolygonNode(); // used to create resulting RPolygon
AetTree aet = new AetTree();
int scanbeam = 0 ;
/* Process each scanbeam */
while( scanbeam < sbt.length )
{
/* Set yb and yt to the bottom and top of the scanbeam */
float yb = sbt[scanbeam++];
float yt = 0.0F ;
float dy = 0.0F ;
if( scanbeam < sbt.length )
{
yt = sbt[scanbeam];
dy = yt - yb;
}
/* === SCANBEAM BOUNDARY PROCESSING ================================ */
/* If LMT node corresponding to yb exists */
if (local_min != null )
{
if (local_min.y == yb)
{
/* Add edges starting at this local minimum to the AET */
for( EdgeNode edge = local_min.first_bound; (edge != null) ; edge= edge.next_bound)
{
add_edge_to_aet( aet, edge );
}
local_min = local_min.next;
}
}
if( DEBUG )
{
aet.print();
}
/* Set dummy previous x value */
float px = -Float.MAX_VALUE ;
/* Create bundles within AET */
EdgeNode e0 = aet.top_node ;
EdgeNode e1 = aet.top_node ;
/* Set up bundle fields of first edge */
aet.top_node.bundle[ABOVE][ aet.top_node.type ] = (aet.top_node.top.y != yb) ? 1 : 0;
aet.top_node.bundle[ABOVE][ ((aet.top_node.type==0) ? 1 : 0) ] = 0;
aet.top_node.bstate[ABOVE] = BundleState.UNBUNDLED;
for (EdgeNode next_edge= aet.top_node.next ; (next_edge != null); next_edge = next_edge.next)
{
int ne_type = next_edge.type ;
int ne_type_opp = ((next_edge.type==0) ? 1 : 0); //next edge type opposite
/* Set up bundle fields of next edge */
next_edge.bundle[ABOVE][ ne_type ]= (next_edge.top.y != yb) ? 1 : 0;
next_edge.bundle[ABOVE][ ne_type_opp ] = 0 ;
next_edge.bstate[ABOVE] = BundleState.UNBUNDLED;
/* Bundle edges above the scanbeam boundary if they coincide */
if ( next_edge.bundle[ABOVE][ne_type] == 1 )
{
if (EQ(e0.xb, next_edge.xb) && EQ(e0.dx, next_edge.dx) && (e0.top.y != yb))
{
next_edge.bundle[ABOVE][ ne_type ] ^= e0.bundle[ABOVE][ ne_type ];
next_edge.bundle[ABOVE][ ne_type_opp ] = e0.bundle[ABOVE][ ne_type_opp ];
next_edge.bstate[ABOVE] = BundleState.BUNDLE_HEAD;
e0.bundle[ABOVE][CLIP] = 0;
e0.bundle[ABOVE][SUBJ] = 0;
e0.bstate[ABOVE] = BundleState.BUNDLE_TAIL;
}
e0 = next_edge;
}
}
int[] horiz = new int[2] ;
horiz[CLIP]= HState.NH;
horiz[SUBJ]= HState.NH;
int[] exists = new int[2] ;
exists[CLIP] = 0 ;
exists[SUBJ] = 0 ;
PolygonNode cf = null ;
/* Process each edge at this scanbeam boundary */
for (EdgeNode edge= aet.top_node ; (edge != null); edge = edge.next )
{
exists[CLIP] = edge.bundle[ABOVE][CLIP] + (edge.bundle[BELOW][CLIP] << 1);
exists[SUBJ] = edge.bundle[ABOVE][SUBJ] + (edge.bundle[BELOW][SUBJ] << 1);
if( (exists[CLIP] != 0) || (exists[SUBJ] != 0) )
{
/* Set bundle side */
edge.bside[CLIP] = parity[CLIP];
edge.bside[SUBJ] = parity[SUBJ];
boolean contributing = false ;
int br=0, bl=0, tr=0, tl=0 ;
/* Determine contributing status and quadrant occupancies */
if( (op == OperationType.GPC_DIFF) || (op == OperationType.GPC_INT) )
{
contributing= ((exists[CLIP]!=0) && ((parity[SUBJ]!=0) || (horiz[SUBJ]!=0))) ||
((exists[SUBJ]!=0) && ((parity[CLIP]!=0) || (horiz[CLIP]!=0))) ||
((exists[CLIP]!=0) && (exists[SUBJ]!=0) && (parity[CLIP] == parity[SUBJ]));
br = ((parity[CLIP]!=0) && (parity[SUBJ]!=0)) ? 1 : 0;
bl = ( ((parity[CLIP] ^ edge.bundle[ABOVE][CLIP])!=0) &&
((parity[SUBJ] ^ edge.bundle[ABOVE][SUBJ])!=0) ) ? 1 : 0;
tr = ( ((parity[CLIP] ^ ((horiz[CLIP]!=HState.NH)?1:0)) !=0) &&
((parity[SUBJ] ^ ((horiz[SUBJ]!=HState.NH)?1:0)) !=0) ) ? 1 : 0;
tl = (((parity[CLIP] ^ ((horiz[CLIP]!=HState.NH)?1:0) ^ edge.bundle[BELOW][CLIP])!=0) &&
((parity[SUBJ] ^ ((horiz[SUBJ]!=HState.NH)?1:0) ^ edge.bundle[BELOW][SUBJ])!=0))?1:0;
}
else if( op == OperationType.GPC_XOR )
{
contributing= (exists[CLIP]!=0) || (exists[SUBJ]!=0);
br= (parity[CLIP]) ^ (parity[SUBJ]);
bl= (parity[CLIP] ^ edge.bundle[ABOVE][CLIP]) ^ (parity[SUBJ] ^ edge.bundle[ABOVE][SUBJ]);
tr= (parity[CLIP] ^ ((horiz[CLIP]!=HState.NH)?1:0)) ^ (parity[SUBJ] ^ ((horiz[SUBJ]!=HState.NH)?1:0));
tl= (parity[CLIP] ^ ((horiz[CLIP]!=HState.NH)?1:0) ^ edge.bundle[BELOW][CLIP])
^ (parity[SUBJ] ^ ((horiz[SUBJ]!=HState.NH)?1:0) ^ edge.bundle[BELOW][SUBJ]);
}
else if( op == OperationType.GPC_UNION )
{
contributing= ((exists[CLIP]!=0) && (!(parity[SUBJ]!=0) || (horiz[SUBJ]!=0))) ||
((exists[SUBJ]!=0) && (!(parity[CLIP]!=0) || (horiz[CLIP]!=0))) ||
((exists[CLIP]!=0) && (exists[SUBJ]!=0) && (parity[CLIP] == parity[SUBJ]));
br= ((parity[CLIP]!=0) || (parity[SUBJ]!=0))?1:0;
bl= (((parity[CLIP] ^ edge.bundle[ABOVE][CLIP])!=0) || ((parity[SUBJ] ^ edge.bundle[ABOVE][SUBJ])!=0))?1:0;
tr= ( ((parity[CLIP] ^ ((horiz[CLIP]!=HState.NH)?1:0))!=0) ||
((parity[SUBJ] ^ ((horiz[SUBJ]!=HState.NH)?1:0))!=0) ) ?1:0;
tl= ( ((parity[CLIP] ^ ((horiz[CLIP]!=HState.NH)?1:0) ^ edge.bundle[BELOW][CLIP])!=0) ||
((parity[SUBJ] ^ ((horiz[SUBJ]!=HState.NH)?1:0) ^ edge.bundle[BELOW][SUBJ])!=0) ) ? 1:0;
}
else
{
throw new IllegalStateException("Unknown op");
}
/* Update parity */
parity[CLIP] ^= edge.bundle[ABOVE][CLIP];
parity[SUBJ] ^= edge.bundle[ABOVE][SUBJ];
/* Update horizontal state */
if (exists[CLIP]!=0)
{
horiz[CLIP] = HState.next_h_state[horiz[CLIP]][((exists[CLIP] - 1) << 1) + parity[CLIP]];
}
if( exists[SUBJ]!=0)
{
horiz[SUBJ] = HState.next_h_state[horiz[SUBJ]][((exists[SUBJ] - 1) << 1) + parity[SUBJ]];
}
if (contributing)
{
float xb = edge.xb;
int vclass = VertexType.getType( tr, tl, br, bl );
switch (vclass)
{
case VertexType.EMN:
case VertexType.IMN:
edge.outp[ABOVE] = out_poly.add_local_min(xb, yb);
px = xb;
cf = edge.outp[ABOVE];
break;
case VertexType.ERI:
if (xb != px)
{
cf.add_right( xb, yb);
px= xb;
}
edge.outp[ABOVE]= cf;
cf= null;
break;
case VertexType.ELI:
edge.outp[BELOW].add_left( xb, yb);
px= xb;
cf= edge.outp[BELOW];
break;
case VertexType.EMX:
if (xb != px)
{
cf.add_left( xb, yb);
px= xb;
}
out_poly.merge_right(cf, edge.outp[BELOW]);
cf= null;
break;
case VertexType.ILI:
if (xb != px)
{
cf.add_left( xb, yb);
px= xb;
}
edge.outp[ABOVE]= cf;
cf= null;
break;
case VertexType.IRI:
edge.outp[BELOW].add_right( xb, yb );
px= xb;
cf= edge.outp[BELOW];
edge.outp[BELOW]= null;
break;
case VertexType.IMX:
if (xb != px)
{
cf.add_right( xb, yb );
px= xb;
}
out_poly.merge_left(cf, edge.outp[BELOW]);
cf= null;
edge.outp[BELOW]= null;
break;
case VertexType.IMM:
if (xb != px)
{
cf.add_right( xb, yb);
px= xb;
}
out_poly.merge_left(cf, edge.outp[BELOW]);
edge.outp[BELOW]= null;
edge.outp[ABOVE] = out_poly.add_local_min(xb, yb);
cf= edge.outp[ABOVE];
break;
case VertexType.EMM:
if (xb != px)
{
cf.add_left( xb, yb);
px= xb;
}
out_poly.merge_right(cf, edge.outp[BELOW]);
edge.outp[BELOW]= null;
edge.outp[ABOVE] = out_poly.add_local_min(xb, yb);
cf= edge.outp[ABOVE];
break;
case VertexType.LED:
if (edge.bot.y == yb)
edge.outp[BELOW].add_left( xb, yb);
edge.outp[ABOVE]= edge.outp[BELOW];
px= xb;
break;
case VertexType.RED:
if (edge.bot.y == yb)
edge.outp[BELOW].add_right( xb, yb );
edge.outp[ABOVE]= edge.outp[BELOW];
px= xb;
break;
default:
break;
} /* End of switch */
} /* End of contributing conditional */
} /* End of edge exists conditional */
if( DEBUG )
{
out_poly.print();
}
} /* End of AET loop */
/* Delete terminating edges from the AET, otherwise compute xt */
for (EdgeNode edge = aet.top_node ; (edge != null); edge = edge.next)
{
if (edge.top.y == yb)
{
EdgeNode prev_edge = edge.prev;
EdgeNode next_edge= edge.next;
if (prev_edge != null)
prev_edge.next = next_edge;
else
aet.top_node = next_edge;
if (next_edge != null )
next_edge.prev = prev_edge;
/* Copy bundle head state to the adjacent tail edge if required */
if ((edge.bstate[BELOW] == BundleState.BUNDLE_HEAD) && (prev_edge!=null))
{
if (prev_edge.bstate[BELOW] == BundleState.BUNDLE_TAIL)
{
prev_edge.outp[BELOW]= edge.outp[BELOW];
prev_edge.bstate[BELOW]= BundleState.UNBUNDLED;
if ( prev_edge.prev != null)
{
if (prev_edge.prev.bstate[BELOW] == BundleState.BUNDLE_TAIL)
{
prev_edge.bstate[BELOW] = BundleState.BUNDLE_HEAD;
}
}
}
}
}
else
{
if (edge.top.y == yt)
edge.xt= edge.top.x;
else
edge.xt= edge.bot.x + edge.dx * (yt - edge.bot.y);
}
}
if (scanbeam < sbte.sbt_entries )
{
/* === SCANBEAM INTERIOR PROCESSING ============================== */
/* Build intersection table for the current scanbeam */
ItNodeTable it_table = new ItNodeTable();
it_table.build_intersection_table(aet, dy);
/* Process each node in the intersection table */
for (ItNode intersect = it_table.top_node ; (intersect != null); intersect = intersect.next)
{
e0= intersect.ie[0];
e1= intersect.ie[1];
/* Only generate output for contributing intersections */
if ( ((e0.bundle[ABOVE][CLIP]!=0) || (e0.bundle[ABOVE][SUBJ]!=0)) &&
((e1.bundle[ABOVE][CLIP]!=0) || (e1.bundle[ABOVE][SUBJ]!=0)))
{
PolygonNode p = e0.outp[ABOVE];
PolygonNode q = e1.outp[ABOVE];
float ix = intersect.point.x;
float iy = intersect.point.y + yb;
int in_clip = ( ( (e0.bundle[ABOVE][CLIP]!=0) && !(e0.bside[CLIP]!=0)) ||
( (e1.bundle[ABOVE][CLIP]!=0) && (e1.bside[CLIP]!=0)) ||
(!(e0.bundle[ABOVE][CLIP]!=0) && !(e1.bundle[ABOVE][CLIP]!=0) &&
(e0.bside[CLIP]!=0) && (e1.bside[CLIP]!=0) ) ) ? 1 : 0;
int in_subj = ( ( (e0.bundle[ABOVE][SUBJ]!=0) && !(e0.bside[SUBJ]!=0)) ||
( (e1.bundle[ABOVE][SUBJ]!=0) && (e1.bside[SUBJ]!=0)) ||
(!(e0.bundle[ABOVE][SUBJ]!=0) && !(e1.bundle[ABOVE][SUBJ]!=0) &&
(e0.bside[SUBJ]!=0) && (e1.bside[SUBJ]!=0) ) ) ? 1 : 0;
int tr=0, tl=0, br=0, bl=0 ;
/* Determine quadrant occupancies */
if( (op == OperationType.GPC_DIFF) || (op == OperationType.GPC_INT) )
{
tr= ((in_clip!=0) && (in_subj!=0)) ? 1 : 0;
tl= (((in_clip ^ e1.bundle[ABOVE][CLIP])!=0) && ((in_subj ^ e1.bundle[ABOVE][SUBJ])!=0))?1:0;
br= (((in_clip ^ e0.bundle[ABOVE][CLIP])!=0) && ((in_subj ^ e0.bundle[ABOVE][SUBJ])!=0))?1:0;
bl= (((in_clip ^ e1.bundle[ABOVE][CLIP] ^ e0.bundle[ABOVE][CLIP])!=0) &&
((in_subj ^ e1.bundle[ABOVE][SUBJ] ^ e0.bundle[ABOVE][SUBJ])!=0) ) ? 1:0;
}
else if( op == OperationType.GPC_XOR )
{
tr= (in_clip)^ (in_subj);
tl= (in_clip ^ e1.bundle[ABOVE][CLIP]) ^ (in_subj ^ e1.bundle[ABOVE][SUBJ]);
br= (in_clip ^ e0.bundle[ABOVE][CLIP]) ^ (in_subj ^ e0.bundle[ABOVE][SUBJ]);
bl= (in_clip ^ e1.bundle[ABOVE][CLIP] ^ e0.bundle[ABOVE][CLIP])
^ (in_subj ^ e1.bundle[ABOVE][SUBJ] ^ e0.bundle[ABOVE][SUBJ]);
}
else if( op == OperationType.GPC_UNION )
{
tr= ((in_clip!=0) || (in_subj!=0)) ? 1 : 0;
tl= (((in_clip ^ e1.bundle[ABOVE][CLIP])!=0) || ((in_subj ^ e1.bundle[ABOVE][SUBJ])!=0)) ? 1 : 0;
br= (((in_clip ^ e0.bundle[ABOVE][CLIP])!=0) || ((in_subj ^ e0.bundle[ABOVE][SUBJ])!=0)) ? 1 : 0;
bl= (((in_clip ^ e1.bundle[ABOVE][CLIP] ^ e0.bundle[ABOVE][CLIP])!=0) ||
((in_subj ^ e1.bundle[ABOVE][SUBJ] ^ e0.bundle[ABOVE][SUBJ])!=0)) ? 1 : 0;
}
else
{
throw new IllegalStateException("Unknown op type, "+op);
}
int vclass = VertexType.getType( tr, tl, br, bl );
switch (vclass)
{
case VertexType.EMN:
e0.outp[ABOVE] = out_poly.add_local_min(ix, iy);
e1.outp[ABOVE] = e0.outp[ABOVE];
break;
case VertexType.ERI:
if (p != null)
{
p.add_right(ix, iy);
e1.outp[ABOVE]= p;
e0.outp[ABOVE]= null;
}
break;
case VertexType.ELI:
if (q != null)
{
q.add_left(ix, iy);
e0.outp[ABOVE]= q;
e1.outp[ABOVE]= null;
}
break;
case VertexType.EMX:
if ((p!=null) && (q!=null))
{
p.add_left( ix, iy);
out_poly.merge_right(p, q);
e0.outp[ABOVE]= null;
e1.outp[ABOVE]= null;
}
break;
case VertexType.IMN:
e0.outp[ABOVE] = out_poly.add_local_min(ix, iy);
e1.outp[ABOVE]= e0.outp[ABOVE];
break;
case VertexType.ILI:
if (p != null)
{
p.add_left(ix, iy);
e1.outp[ABOVE]= p;
e0.outp[ABOVE]= null;
}
break;
case VertexType.IRI:
if (q!=null)
{
q.add_right(ix, iy);
e0.outp[ABOVE]= q;
e1.outp[ABOVE]= null;
}
break;
case VertexType.IMX:
if ((p!=null) && (q!=null))
{
p.add_right(ix, iy);
out_poly.merge_left(p, q);
e0.outp[ABOVE]= null;
e1.outp[ABOVE]= null;
}
break;
case VertexType.IMM:
if ((p!=null) && (q!=null))
{
p.add_right(ix, iy);
out_poly.merge_left(p, q);
e0.outp[ABOVE] = out_poly.add_local_min(ix, iy);
e1.outp[ABOVE]= e0.outp[ABOVE];
}
break;
case VertexType.EMM:
if ((p!=null) && (q!=null))
{
p.add_left(ix, iy);
out_poly.merge_right(p, q);
e0.outp[ABOVE] = out_poly.add_local_min(ix, iy);
e1.outp[ABOVE] = e0.outp[ABOVE];
}
break;
default:
break;
} /* End of switch */
} /* End of contributing intersection conditional */
/* Swap bundle sides in response to edge crossing */
if (e0.bundle[ABOVE][CLIP]!=0)
e1.bside[CLIP] = (e1.bside[CLIP]==0)?1:0;
if (e1.bundle[ABOVE][CLIP]!=0)
e0.bside[CLIP]= (e0.bside[CLIP]==0)?1:0;
if (e0.bundle[ABOVE][SUBJ]!=0)
e1.bside[SUBJ]= (e1.bside[SUBJ]==0)?1:0;
if (e1.bundle[ABOVE][SUBJ]!=0)
e0.bside[SUBJ]= (e0.bside[SUBJ]==0)?1:0;
/* Swap e0 and e1 bundles in the AET */
EdgeNode prev_edge = e0.prev;
EdgeNode next_edge = e1.next;
if (next_edge != null)
{
next_edge.prev = e0;
}
if (e0.bstate[ABOVE] == BundleState.BUNDLE_HEAD)
{
boolean search = true;
while (search)
{
prev_edge= prev_edge.prev;
if (prev_edge != null)
{
if (prev_edge.bstate[ABOVE] != BundleState.BUNDLE_TAIL)
{
search= false;
}
}
else
{
search= false;
}
}
}
if (prev_edge == null)
{
aet.top_node.prev = e1;
e1.next = aet.top_node;
aet.top_node = e0.next;
}
else
{
prev_edge.next.prev = e1;
e1.next = prev_edge.next;
prev_edge.next = e0.next;
}
e0.next.prev = prev_edge;
e1.next.prev = e1;
e0.next = next_edge;
if( DEBUG )
{
out_poly.print();
}
} /* End of IT loop*/
/* Prepare for next scanbeam */
for ( EdgeNode edge = aet.top_node; (edge != null); edge = edge.next)
{
EdgeNode next_edge = edge.next;
EdgeNode succ_edge = edge.succ;
if ((edge.top.y == yt) && (succ_edge!=null))
{
/* Replace AET edge by its successor */
succ_edge.outp[BELOW]= edge.outp[ABOVE];
succ_edge.bstate[BELOW]= edge.bstate[ABOVE];
succ_edge.bundle[BELOW][CLIP]= edge.bundle[ABOVE][CLIP];
succ_edge.bundle[BELOW][SUBJ]= edge.bundle[ABOVE][SUBJ];
EdgeNode prev_edge = edge.prev;
if ( prev_edge != null )
prev_edge.next = succ_edge;
else
aet.top_node = succ_edge;
if (next_edge != null)
next_edge.prev= succ_edge;
succ_edge.prev = prev_edge;
succ_edge.next = next_edge;
}
else
{
/* Update this edge */
edge.outp[BELOW]= edge.outp[ABOVE];
edge.bstate[BELOW]= edge.bstate[ABOVE];
edge.bundle[BELOW][CLIP]= edge.bundle[ABOVE][CLIP];
edge.bundle[BELOW][SUBJ]= edge.bundle[ABOVE][SUBJ];
edge.xb= edge.xt;
}
edge.outp[ABOVE]= null;
}
}
} /* === END OF SCANBEAM PROCESSING ================================== */
/* Generate result polygon from out_poly */
result = out_poly.getResult(polyClass);
return result ;
}
/**
* Clipper to output tristrips
*/
private static RMesh clip( OperationType op, RPolygon subj, RPolygon clip )
{
if(RG.useFastClip) {
return FastRClip.clip(op, subj, clip);
}
PolygonNode tlist=null, tnn, tn;
EdgeNode prev_edge, next_edge, edge, cf=null, succ_edge, e0, e1;
VertexNode lt, ltn, rt, rtn;
int cft=VertexType.LED;
float []sbt;
float xb, px, nx=0, yb, yt, dy, ix, iy;
/* Test for trivial NULL result cases */
if( (subj.isEmpty() && clip.isEmpty()) ||
(subj.isEmpty() && ((op == OperationType.GPC_INT) || (op == OperationType.GPC_DIFF))) ||
(clip.isEmpty() && (op == OperationType.GPC_INT)) )
{
return new RMesh() ;
}
/* Identify potentialy contributing contours */
if( ((op == OperationType.GPC_INT) || (op == OperationType.GPC_DIFF)) &&
!subj.isEmpty() && !clip.isEmpty() )
{
minimax_test(subj, clip, op);
}
/* Build LMT */
LmtTable lmt_table = new LmtTable();
ScanBeamTreeEntries sbte = new ScanBeamTreeEntries();
if (!subj.isEmpty())
{
build_lmt(lmt_table, sbte, subj, SUBJ, op);
}
if( DEBUG )
{
System.out.println("");
System.out.println(" ------------ After build_lmt for subj ---------");
lmt_table.print();
}
if (!clip.isEmpty())
{
build_lmt(lmt_table, sbte, clip, CLIP, op);
}
if( DEBUG )
{
System.out.println("");
System.out.println(" ------------ After build_lmt for clip ---------");
lmt_table.print();
}
/* Return a NULL result if no contours contribute */
if (lmt_table.top_node == null)
{
return new RMesh();
}
/* Build scanbeam table from scanbeam tree */
sbt = sbte.build_sbt();
int[] parity = new int[2] ;
parity[0] = LEFT ;
parity[1] = LEFT ;
/* Invert clip polygon for difference operation */
if (op == OperationType.GPC_DIFF)
{
parity[CLIP]= RIGHT;
}
if( DEBUG )
{
print_sbt(sbt);
}
LmtNode local_min = lmt_table.top_node ;
AetTree aet = new AetTree();
int scanbeam = 0 ;
/* Process each scanbeam */
while( scanbeam < sbt.length )
{
/* Set yb and yt to the bottom and top of the scanbeam */
yb = sbt[scanbeam++];
yt = 0.0F ;
dy = 0.0F ;
if( scanbeam < sbt.length )
{
yt = sbt[scanbeam];
dy = yt - yb;
}
/* === SCANBEAM BOUNDARY PROCESSING ================================ */
/* If LMT node corresponding to yb exists */
if (local_min != null )
{
if (local_min.y == yb)
{
/* Add edges starting at this local minimum to the AET */
for( edge = local_min.first_bound; (edge != null) ; edge= edge.next_bound)
{
add_edge_to_aet( aet, edge );
}
local_min = local_min.next;
}
}
if( DEBUG )
{
aet.print();
}
/* Set dummy previous x value */
px = -Float.MAX_VALUE ;
/* Create bundles within AET */
e0 = aet.top_node ;
e1 = aet.top_node ;
/* Set up bundle fields of first edge */
aet.top_node.bundle[ABOVE][ aet.top_node.type ] = (aet.top_node.top.y != yb) ? 1 : 0;
aet.top_node.bundle[ABOVE][ ((aet.top_node.type==0) ? 1 : 0) ] = 0;
aet.top_node.bstate[ABOVE] = BundleState.UNBUNDLED;
for (next_edge= aet.top_node.next ; (next_edge != null); next_edge = next_edge.next)
{
int ne_type = next_edge.type ;
int ne_type_opp = ((next_edge.type==0) ? 1 : 0); //next edge type opposite
/* Set up bundle fields of next edge */
next_edge.bundle[ABOVE][ ne_type ]= (next_edge.top.y != yb) ? 1 : 0;
next_edge.bundle[ABOVE][ ne_type_opp ] = 0 ;
next_edge.bstate[ABOVE] = BundleState.UNBUNDLED;
/* Bundle edges above the scanbeam boundary if they coincide */
if ( next_edge.bundle[ABOVE][ne_type] == 1 )
{
if (EQ(e0.xb, next_edge.xb) && EQ(e0.dx, next_edge.dx) && (e0.top.y != yb))
{
next_edge.bundle[ABOVE][ ne_type ] ^= e0.bundle[ABOVE][ ne_type ];
next_edge.bundle[ABOVE][ ne_type_opp ] = e0.bundle[ABOVE][ ne_type_opp ];
next_edge.bstate[ABOVE] = BundleState.BUNDLE_HEAD;
e0.bundle[ABOVE][CLIP] = 0;
e0.bundle[ABOVE][SUBJ] = 0;
e0.bstate[ABOVE] = BundleState.BUNDLE_TAIL;
}
e0 = next_edge;
}
}
int[] horiz = new int[2] ;
horiz[CLIP]= HState.NH;
horiz[SUBJ]= HState.NH;
int[] exists = new int[2] ;
exists[CLIP] = 0 ;
exists[SUBJ] = 0 ;
/* Process each edge at this scanbeam boundary */
for (edge= aet.top_node ; (edge != null); edge = edge.next )
{
exists[CLIP] = edge.bundle[ABOVE][CLIP] + (edge.bundle[BELOW][CLIP] << 1);
exists[SUBJ] = edge.bundle[ABOVE][SUBJ] + (edge.bundle[BELOW][SUBJ] << 1);
if( (exists[CLIP] != 0) || (exists[SUBJ] != 0) )
{
/* Set bundle side */
edge.bside[CLIP] = parity[CLIP];
edge.bside[SUBJ] = parity[SUBJ];
boolean contributing = false ;
int br=0, bl=0, tr=0, tl=0 ;
/* Determine contributing status and quadrant occupancies */
if( (op == OperationType.GPC_DIFF) || (op == OperationType.GPC_INT) )
{
contributing= ((exists[CLIP]!=0) && ((parity[SUBJ]!=0) || (horiz[SUBJ]!=0))) ||
((exists[SUBJ]!=0) && ((parity[CLIP]!=0) || (horiz[CLIP]!=0))) ||
((exists[CLIP]!=0) && (exists[SUBJ]!=0) && (parity[CLIP] == parity[SUBJ]));
br = ((parity[CLIP]!=0) && (parity[SUBJ]!=0)) ? 1 : 0;
bl = ( ((parity[CLIP] ^ edge.bundle[ABOVE][CLIP])!=0) &&
((parity[SUBJ] ^ edge.bundle[ABOVE][SUBJ])!=0) ) ? 1 : 0;
tr = ( ((parity[CLIP] ^ ((horiz[CLIP]!=HState.NH)?1:0)) !=0) &&
((parity[SUBJ] ^ ((horiz[SUBJ]!=HState.NH)?1:0)) !=0) ) ? 1 : 0;
tl = (((parity[CLIP] ^ ((horiz[CLIP]!=HState.NH)?1:0) ^ edge.bundle[BELOW][CLIP])!=0) &&
((parity[SUBJ] ^ ((horiz[SUBJ]!=HState.NH)?1:0) ^ edge.bundle[BELOW][SUBJ])!=0))?1:0;
}
else if( op == OperationType.GPC_XOR )
{
contributing= (exists[CLIP]!=0) || (exists[SUBJ]!=0);
br= (parity[CLIP]) ^ (parity[SUBJ]);
bl= (parity[CLIP] ^ edge.bundle[ABOVE][CLIP]) ^ (parity[SUBJ] ^ edge.bundle[ABOVE][SUBJ]);
tr= (parity[CLIP] ^ ((horiz[CLIP]!=HState.NH)?1:0)) ^ (parity[SUBJ] ^ ((horiz[SUBJ]!=HState.NH)?1:0));
tl= (parity[CLIP] ^ ((horiz[CLIP]!=HState.NH)?1:0) ^ edge.bundle[BELOW][CLIP])
^ (parity[SUBJ] ^ ((horiz[SUBJ]!=HState.NH)?1:0) ^ edge.bundle[BELOW][SUBJ]);
}
else if( op == OperationType.GPC_UNION )
{
contributing= ((exists[CLIP]!=0) && (!(parity[SUBJ]!=0) || (horiz[SUBJ]!=0))) ||
((exists[SUBJ]!=0) && (!(parity[CLIP]!=0) || (horiz[CLIP]!=0))) ||
((exists[CLIP]!=0) && (exists[SUBJ]!=0) && (parity[CLIP] == parity[SUBJ]));
br= ((parity[CLIP]!=0) || (parity[SUBJ]!=0))?1:0;
bl= (((parity[CLIP] ^ edge.bundle[ABOVE][CLIP])!=0) || ((parity[SUBJ] ^ edge.bundle[ABOVE][SUBJ])!=0))?1:0;
tr= ( ((parity[CLIP] ^ ((horiz[CLIP]!=HState.NH)?1:0))!=0) ||
((parity[SUBJ] ^ ((horiz[SUBJ]!=HState.NH)?1:0))!=0) ) ?1:0;
tl= ( ((parity[CLIP] ^ ((horiz[CLIP]!=HState.NH)?1:0) ^ edge.bundle[BELOW][CLIP])!=0) ||
((parity[SUBJ] ^ ((horiz[SUBJ]!=HState.NH)?1:0) ^ edge.bundle[BELOW][SUBJ])!=0) ) ? 1:0;
}
else
{
throw new IllegalStateException("Unknown op");
}
/* Update parity */
parity[CLIP] ^= edge.bundle[ABOVE][CLIP];
parity[SUBJ] ^= edge.bundle[ABOVE][SUBJ];
/* Update horizontal state */
if (exists[CLIP]!=0)
{
horiz[CLIP] = HState.next_h_state[horiz[CLIP]][((exists[CLIP] - 1) << 1) + parity[CLIP]];
}
if( exists[SUBJ]!=0)
{
horiz[SUBJ] = HState.next_h_state[horiz[SUBJ]][((exists[SUBJ] - 1) << 1) + parity[SUBJ]];
}
if (contributing)
{
xb = edge.xb;
int vclass = VertexType.getType( tr, tl, br, bl );
switch (vclass)
{
case VertexType.EMN:
tlist=new_tristrip(tlist, edge, xb, yb);
cf= edge;
break;
case VertexType.ERI:
edge.outp[ABOVE]= cf.outp[ABOVE];
if (xb != cf.xb)
{
VERTEX(edge, ABOVE, RIGHT, xb, yb);
}
cf= null;
break;
case VertexType.ELI:
VERTEX(edge, BELOW, LEFT, xb, yb);
edge.outp[ABOVE]= null;
cf= edge;
break;
case VertexType.EMX:
if (xb != cf.xb)
{
VERTEX(edge, BELOW, RIGHT, xb, yb);
}
edge.outp[ABOVE] = null;
cf= null;
break;
case VertexType.IMN:
if (cft == VertexType.LED)
{
if (cf.bot.y != yb)
{
VERTEX(cf, BELOW, LEFT, cf.xb, yb);
}
tlist=new_tristrip(tlist, cf, cf.xb, yb);
}
edge.outp[ABOVE]= cf.outp[ABOVE];
VERTEX(edge, ABOVE, RIGHT, xb, yb);
break;
case VertexType.ILI:
tlist=new_tristrip(tlist, edge, xb, yb);
cf= edge;
cft= VertexType.ILI;
break;
case VertexType.IRI:
if (cft == VertexType.LED)
{
if (cf.bot.y != yb)
{
VERTEX(cf, BELOW, LEFT, cf.xb, yb);
}
tlist=new_tristrip(tlist, cf, cf.xb, yb);
}
VERTEX(edge, BELOW, RIGHT, xb, yb);
edge.outp[ABOVE]= null;
break;
case VertexType.IMX:
VERTEX(edge, BELOW, LEFT, xb, yb);
edge.outp[ABOVE]= null;
cft= VertexType.IMX;
break;
case VertexType.IMM:
VERTEX(edge, BELOW, LEFT, xb, yb);
edge.outp[ABOVE]= cf.outp[ABOVE];
if (xb != cf.xb)
{
VERTEX(cf, ABOVE, RIGHT, xb, yb);
}
cf= edge;
break;
case VertexType.EMM:
VERTEX(edge, BELOW, RIGHT, xb, yb);
edge.outp[ABOVE]= null;
tlist=new_tristrip(tlist, edge, xb, yb);
cf= edge;
break;
case VertexType.LED:
if (edge.bot.y == yb)
VERTEX(edge, BELOW, LEFT, xb, yb);
edge.outp[ABOVE]= edge.outp[BELOW];
cf= edge;
cft= VertexType.LED;
break;
case VertexType.RED:
edge.outp[ABOVE]= cf.outp[ABOVE];
if (cft == VertexType.LED)
{
if (cf.bot.y == yb)
{
VERTEX(edge, BELOW, RIGHT, xb, yb);
}
else
{
if (edge.bot.y == yb)
{
VERTEX(cf, BELOW, LEFT, cf.xb, yb);
VERTEX(edge, BELOW, RIGHT, xb, yb);
}
}
}
else
{
VERTEX(edge, BELOW, RIGHT, xb, yb);
VERTEX(edge, ABOVE, RIGHT, xb, yb);
}
cf= null;
break;
default:
break;
} /* End of switch */
} /* End of contributing conditional */
} /* End of edge exists conditional */
} /* End of AET loop */
/* Delete terminating edges from the AET, otherwise compute xt */
for (edge = aet.top_node ; (edge != null); edge = edge.next)
{
if (edge.top.y == yb)
{
prev_edge = edge.prev;
next_edge= edge.next;
if (prev_edge != null)
prev_edge.next = next_edge;
else
aet.top_node = next_edge;
if (next_edge != null )
next_edge.prev = prev_edge;
/* Copy bundle head state to the adjacent tail edge if required */
if ((edge.bstate[BELOW] == BundleState.BUNDLE_HEAD) && (prev_edge!=null))
{
if (prev_edge.bstate[BELOW] == BundleState.BUNDLE_TAIL)
{
prev_edge.outp[BELOW]= edge.outp[BELOW];
prev_edge.bstate[BELOW]= BundleState.UNBUNDLED;
if ( prev_edge.prev != null)
{
if (prev_edge.prev.bstate[BELOW] == BundleState.BUNDLE_TAIL)
{
prev_edge.bstate[BELOW] = BundleState.BUNDLE_HEAD;
}
}
}
}
}
else
{
if (edge.top.y == yt)
edge.xt= edge.top.x;
else
edge.xt= edge.bot.x + edge.dx * (yt - edge.bot.y);
}
}
if (scanbeam < sbte.sbt_entries )
{
/* === SCANBEAM INTERIOR PROCESSING ============================== */
/* Build intersection table for the current scanbeam */
ItNodeTable it_table = new ItNodeTable();
it_table.build_intersection_table(aet, dy);
/* Process each node in the intersection table */
for (ItNode intersect = it_table.top_node ; (intersect != null); intersect = intersect.next)
{
e0= intersect.ie[0];
e1= intersect.ie[1];
/* Only generate output for contributing intersections */
if ( ((e0.bundle[ABOVE][CLIP]!=0) || (e0.bundle[ABOVE][SUBJ]!=0)) &&
((e1.bundle[ABOVE][CLIP]!=0) || (e1.bundle[ABOVE][SUBJ]!=0)))
{
PolygonNode p = e0.outp[ABOVE];
PolygonNode q = e1.outp[ABOVE];
ix = intersect.point.x;
iy = intersect.point.y + yb;
int in_clip = ( ( (e0.bundle[ABOVE][CLIP]!=0) && !(e0.bside[CLIP]!=0)) ||
( (e1.bundle[ABOVE][CLIP]!=0) && (e1.bside[CLIP]!=0)) ||
(!(e0.bundle[ABOVE][CLIP]!=0) && !(e1.bundle[ABOVE][CLIP]!=0) &&
(e0.bside[CLIP]!=0) && (e1.bside[CLIP]!=0) ) ) ? 1 : 0;
int in_subj = ( ( (e0.bundle[ABOVE][SUBJ]!=0) && !(e0.bside[SUBJ]!=0)) ||
( (e1.bundle[ABOVE][SUBJ]!=0) && (e1.bside[SUBJ]!=0)) ||
(!(e0.bundle[ABOVE][SUBJ]!=0) && !(e1.bundle[ABOVE][SUBJ]!=0) &&
(e0.bside[SUBJ]!=0) && (e1.bside[SUBJ]!=0) ) ) ? 1 : 0;
int tr=0, tl=0, br=0, bl=0 ;
/* Determine quadrant occupancies */
if( (op == OperationType.GPC_DIFF) || (op == OperationType.GPC_INT) )
{
tr= ((in_clip!=0) && (in_subj!=0)) ? 1 : 0;
tl= (((in_clip ^ e1.bundle[ABOVE][CLIP])!=0) && ((in_subj ^ e1.bundle[ABOVE][SUBJ])!=0))?1:0;
br= (((in_clip ^ e0.bundle[ABOVE][CLIP])!=0) && ((in_subj ^ e0.bundle[ABOVE][SUBJ])!=0))?1:0;
bl= (((in_clip ^ e1.bundle[ABOVE][CLIP] ^ e0.bundle[ABOVE][CLIP])!=0) &&
((in_subj ^ e1.bundle[ABOVE][SUBJ] ^ e0.bundle[ABOVE][SUBJ])!=0) ) ? 1:0;
}
else if( op == OperationType.GPC_XOR )
{
tr= (in_clip)^ (in_subj);
tl= (in_clip ^ e1.bundle[ABOVE][CLIP]) ^ (in_subj ^ e1.bundle[ABOVE][SUBJ]);
br= (in_clip ^ e0.bundle[ABOVE][CLIP]) ^ (in_subj ^ e0.bundle[ABOVE][SUBJ]);
bl= (in_clip ^ e1.bundle[ABOVE][CLIP] ^ e0.bundle[ABOVE][CLIP])
^ (in_subj ^ e1.bundle[ABOVE][SUBJ] ^ e0.bundle[ABOVE][SUBJ]);
}
else if( op == OperationType.GPC_UNION )
{
tr= ((in_clip!=0) || (in_subj!=0)) ? 1 : 0;
tl= (((in_clip ^ e1.bundle[ABOVE][CLIP])!=0) || ((in_subj ^ e1.bundle[ABOVE][SUBJ])!=0)) ? 1 : 0;
br= (((in_clip ^ e0.bundle[ABOVE][CLIP])!=0) || ((in_subj ^ e0.bundle[ABOVE][SUBJ])!=0)) ? 1 : 0;
bl= (((in_clip ^ e1.bundle[ABOVE][CLIP] ^ e0.bundle[ABOVE][CLIP])!=0) ||
((in_subj ^ e1.bundle[ABOVE][SUBJ] ^ e0.bundle[ABOVE][SUBJ])!=0)) ? 1 : 0;
}
else
{
throw new IllegalStateException("Unknown op type, "+op);
}
next_edge = e1.next;
prev_edge = e0.prev;
int vclass = VertexType.getType( tr, tl, br, bl );
switch (vclass)
{
case VertexType.EMN:
tlist=new_tristrip(tlist, e1, ix, iy);
e1.outp[ABOVE] = e0.outp[ABOVE];
break;
case VertexType.ERI:
if (p != null)
{
px = P_EDGE(prev_edge, e0, ABOVE, px, iy);
VERTEX(prev_edge, ABOVE, LEFT, px, iy);
VERTEX(e0, ABOVE, RIGHT, ix, iy);
e1.outp[ABOVE]= e0.outp[ABOVE];
e0.outp[ABOVE]= null;
}
break;
case VertexType.ELI:
if (q != null)
{
nx = N_EDGE(next_edge, e1, ABOVE, nx, iy);
VERTEX(e1, ABOVE, LEFT, ix, iy);
VERTEX(next_edge, ABOVE, RIGHT, nx, iy);
e0.outp[ABOVE]= e1.outp[ABOVE];
e1.outp[ABOVE]= null;
}
break;
case VertexType.EMX:
if ((p!=null) && (q!=null))
{
VERTEX(e0, ABOVE, LEFT, ix, iy);
e0.outp[ABOVE]= null;
e1.outp[ABOVE]= null;
}
break;
case VertexType.IMN:
px = P_EDGE(prev_edge, e0, ABOVE, px, iy);
VERTEX(prev_edge, ABOVE, LEFT, px, iy);
nx = N_EDGE(next_edge, e1, ABOVE, nx, iy);
VERTEX(next_edge, ABOVE, RIGHT, nx, iy);
tlist=new_tristrip(tlist, prev_edge, px, iy);
e1.outp[ABOVE]= prev_edge.outp[ABOVE];
VERTEX(e1, ABOVE, RIGHT, ix, iy);
tlist=new_tristrip(tlist, e0, ix, iy);
next_edge.outp[ABOVE]= e0.outp[ABOVE];
VERTEX(next_edge, ABOVE, RIGHT, nx, iy);
break;
case VertexType.ILI:
if (p != null)
{
VERTEX(e0, ABOVE, LEFT, ix, iy);
nx = N_EDGE(next_edge, e1, ABOVE, nx, iy);
VERTEX(next_edge, ABOVE, RIGHT, nx, iy);
e1.outp[ABOVE]= e0.outp[ABOVE];
e0.outp[ABOVE]= null;
}
break;
case VertexType.IRI:
if (q!=null)
{
VERTEX(e1, ABOVE, RIGHT, ix, iy);
px = P_EDGE(prev_edge, e0, ABOVE, px, iy);
VERTEX(prev_edge, ABOVE, LEFT, px, iy);
e0.outp[ABOVE]= e1.outp[ABOVE];
e1.outp[ABOVE]= null;
}
break;
case VertexType.IMX:
if ((p!=null) && (q!=null))
{
VERTEX(e0, ABOVE, RIGHT, ix, iy);
VERTEX(e1, ABOVE, LEFT, ix, iy);
e0.outp[ABOVE]= null;
e1.outp[ABOVE]= null;
px = P_EDGE(prev_edge, e0, ABOVE, px, iy);
VERTEX(prev_edge, ABOVE, LEFT, px, iy);
tlist=new_tristrip(tlist, prev_edge, px, iy);
nx = N_EDGE(next_edge, e1, ABOVE, nx, iy);
VERTEX(next_edge, ABOVE, RIGHT, nx, iy);
next_edge.outp[ABOVE]= prev_edge.outp[ABOVE];
VERTEX(next_edge, ABOVE, RIGHT, nx, iy);
}
break;
case VertexType.IMM:
if ((p!=null) && (q!=null))
{
VERTEX(e0, ABOVE, RIGHT, ix, iy);
VERTEX(e1, ABOVE, LEFT, ix, iy);
px = P_EDGE(prev_edge, e0, ABOVE, px, iy);
VERTEX(prev_edge, ABOVE, LEFT, px, iy);
tlist=new_tristrip(tlist, prev_edge, px, iy);
nx = N_EDGE(next_edge, e1, ABOVE, nx, iy);
VERTEX(next_edge, ABOVE, RIGHT, nx, iy);
e1.outp[ABOVE]= prev_edge.outp[ABOVE];
VERTEX(e1, ABOVE, RIGHT, ix, iy);
tlist=new_tristrip(tlist, e0, ix, iy);
next_edge.outp[ABOVE]= e0.outp[ABOVE];
VERTEX(next_edge, ABOVE, RIGHT, nx, iy);
}
break;
case VertexType.EMM:
if ((p!=null) && (q!=null))
{
VERTEX(e0, ABOVE, LEFT, ix, iy);
tlist=new_tristrip(tlist, e1, ix, iy);
e1.outp[ABOVE] = e0.outp[ABOVE];
}
break;
default:
break;
} /* End of switch */
} /* End of contributing intersection conditional */
/* Swap bundle sides in response to edge crossing */
if (e0.bundle[ABOVE][CLIP]!=0)
e1.bside[CLIP] = (e1.bside[CLIP]==0)?1:0;
if (e1.bundle[ABOVE][CLIP]!=0)
e0.bside[CLIP]= (e0.bside[CLIP]==0)?1:0;
if (e0.bundle[ABOVE][SUBJ]!=0)
e1.bside[SUBJ]= (e1.bside[SUBJ]==0)?1:0;
if (e1.bundle[ABOVE][SUBJ]!=0)
e0.bside[SUBJ]= (e0.bside[SUBJ]==0)?1:0;
/* Swap e0 and e1 bundles in the AET */
prev_edge = e0.prev;
next_edge = e1.next;
if (next_edge != null)
{
next_edge.prev = e0;
}
if (e0.bstate[ABOVE] == BundleState.BUNDLE_HEAD)
{
boolean search = true;
while (search)
{
prev_edge= prev_edge.prev;
if (prev_edge != null)
{
if (prev_edge.bundle[ABOVE][CLIP]!=0
|| prev_edge.bundle[ABOVE][SUBJ]!=0
|| (prev_edge.bstate[ABOVE] == BundleState.BUNDLE_HEAD))
{
search= false;
}
}
else
{
search= false;
}
}
}
if (prev_edge == null)
{
e1.next = aet.top_node;
aet.top_node = e0.next;
}
else
{
e1.next = prev_edge.next;
prev_edge.next = e0.next;
}
e0.next.prev = prev_edge;
e1.next.prev = e1;
e0.next = next_edge;
} /* End of IT loop*/
/* Prepare for next scanbeam */
for ( edge = aet.top_node; (edge != null); edge = edge.next)
{
next_edge = edge.next;
succ_edge = edge.succ;
if ((edge.top.y == yt) && (succ_edge!=null))
{
/* Replace AET edge by its successor */
succ_edge.outp[BELOW]= edge.outp[ABOVE];
succ_edge.bstate[BELOW]= edge.bstate[ABOVE];
succ_edge.bundle[BELOW][CLIP]= edge.bundle[ABOVE][CLIP];
succ_edge.bundle[BELOW][SUBJ]= edge.bundle[ABOVE][SUBJ];
prev_edge = edge.prev;
if ( prev_edge != null )
prev_edge.next = succ_edge;
else
aet.top_node = succ_edge;
if (next_edge != null)
next_edge.prev= succ_edge;
succ_edge.prev = prev_edge;
succ_edge.next = next_edge;
}
else
{
/* Update this edge */
edge.outp[BELOW]= edge.outp[ABOVE];
edge.bstate[BELOW]= edge.bstate[ABOVE];
edge.bundle[BELOW][CLIP]= edge.bundle[ABOVE][CLIP];
edge.bundle[BELOW][SUBJ]= edge.bundle[ABOVE][SUBJ];
edge.xb= edge.xt;
}
edge.outp[ABOVE]= null;
}
}
} /* === END OF SCANBEAM PROCESSING ================================== */
/* Generate result tristrip from tlist */
RMesh result = new RMesh();
if (count_tristrips(tlist) > 0)
{
int s, v;
s= 0;
for (tn= tlist; tn!=null; tn= tnn)
{
tnn= tn.next;
if (tn.active > 2)
{
/* Valid tristrip: copy the vertices and free the heap */
RStrip strip = new RStrip();
v= 0;
if (INVERT_TRISTRIPS == true)
{
lt= tn.v[RIGHT];
rt= tn.v[LEFT];
}
else
{
lt= tn.v[LEFT];
rt= tn.v[RIGHT];
}
while (lt!=null || rt!=null)
{
if (lt!=null)
{
ltn= lt.next;
strip.add(lt.x,lt.y);
v++;
lt= ltn;
}
if (rt!=null)
{
rtn= rt.next;
strip.add(rt.x,rt.y);
v++;
rt= rtn;
}
}
result.addStrip(strip);
s++;
}
else
{
/* Invalid tristrip: just free the heap */
for (lt= tn.v[LEFT]; lt!=null; lt= ltn)
{
ltn= lt.next;
}
for (rt= tn.v[RIGHT]; rt!=null; rt= rtn)
{
rtn= rt.next;
}
}
}
}
return result ;
}
public static RMesh polygonToMesh(RPolygon s)
{
RPolygon c = new RPolygon();
RPolygon s_clean = s.removeOpenContours();
/*
for(int i=0; i