/*
* 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;
import geomerative.RClip.OperationType;
/**
* 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 FastRClip {
// -----------------
// --- Constants ---
// -----------------
// 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 FastRClip() {
}
// ----------------------
// --- Static Methods ---
// ----------------------
// -----------------------
// --- 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.
*/
static RPolygon clip(OperationType op, RPolygon subj, RPolygon clip, Class polyClass) {
RPolygon result = createNewPoly(polyClass);
TopPolygonNode out_poly = new TopPolygonNode(); // used to create resulting RPolygon
/* 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 null;
}
/* 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 (!clip.isEmpty()) {
build_lmt(lmt_table, sbte, clip, CLIP, op);
}
/* Return a NULL result if no contours contribute */
if (lmt_table.top_node == null) {
return null;
}
/* Build scanbeam table from scanbeam tree */
float[] sbt = sbte.build_sbt();
int parity_clip = LEFT;
int parity_subj = LEFT;
/* Invert clip polygon for difference operation */
if (op == OperationType.GPC_DIFF) {
parity_clip = RIGHT;
}
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 */
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;
}
}
/* 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_clip = HState.NH;
int horiz_subj = HState.NH;
int exists_clip = 0;
int 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) // DIFFERENT!
{
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 */
}
/* 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.ie0;
e1 = intersect.ie1;
/* 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;
}
/* 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
*/
static RMesh clip(OperationType op, RPolygon subj, RPolygon clip) {
PolygonNode tlist = null;
float nx = 0;
/* 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 null;
}
/* 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 (!clip.isEmpty()) {
build_lmt(lmt_table, sbte, clip, CLIP, op);
}
/* Return a NULL result if no contours contribute */
if (lmt_table.top_node == null) {
return null;
}
/* Build scanbeam table from scanbeam tree */
float[] sbt = sbte.build_sbt();
int parity_clip = LEFT;
int parity_subj = LEFT;
/* Invert clip polygon for difference operation */
if (op == OperationType.GPC_DIFF) {
parity_clip = RIGHT;
}
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 */
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;
}
}
/* 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_clip = HState.NH;
int horiz_subj = HState.NH;
int exists_clip = 0;
int exists_subj = 0;
EdgeNode cf = null;
int cft = VertexType.LED;
/* 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) // DIFFERENT!
{
float 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 (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.ie0;
e1 = intersect.ie1;
/* 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);
}
EdgeNode next_edge = e1.next;
EdgeNode 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 */
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.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 (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 tristrip from tlist */
VertexNode lt, ltn, rt, rtn;
PolygonNode tnn, tn;
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