UNPKG

icosahedron-draw

Version:

renders points of an icosahedron

1,303 lines (1,157 loc) 106 kB
var gpcas = gpcas || {}; gpcas.util = {}; gpcas.geometry = {}; ////////// //Object.prototype.equals = function(x) { function equals(x1, x) { var p; for (p in x1) { if (typeof(x[p]) == 'undefined') { return false; } } for (p in x1) { if (x1[p]) { switch (typeof(x1[p])) { case 'object': if (!equals(x1[p], x[p])) { return false; } break; case 'function': if (typeof(x[p]) == 'undefined' || (p != 'equals' && x1[p].toString() != x[p].toString())) return false; break; default: if (x1[p] != x[p]) { return false; } } } else { if (x[p]) return false; } } for (p in x) { if (typeof(x1[p]) == 'undefined') { return false; } } return true; } ///point var Point = function (x, y) { this.x = x; this.y = y; }; gpcas.Point = Point; ////////////// CLASS ArrayHelper //////////////////////////////////// gpcas.util.ArrayHelper = function () { }; var static = gpcas.util.ArrayHelper; static.create2DArray = function (x, y) { var a = []; for (var i = 0; i < x; i++) { a[i] = []; } return a; }; static.valueEqual = function (obj1, obj2) { if (obj1 == obj2) return true; if (equals(obj1, obj2)) return true; return false; } static.sortPointsClockwise = function (vertices) { var isArrayList = false; if (vertices instanceof ArrayList) { vertices = vertices.toArray(); isArrayList = true; } //point var maxTop = null; var maxBottom = null; var maxLeft = null; var maxRight = null; var maxLeftIndex; var newVertices = vertices; for (var i = 0; i < vertices.length; i++) { var vertex = vertices[i]; if ((maxTop == null) || (maxTop.y > vertex.y) || ((maxTop.y == vertex.y) && (vertex.x < maxTop.x))) { maxTop = vertex; } if ((maxBottom == null) || (maxBottom.y < vertex.y) || ((maxBottom.y == vertex.y) && (vertex.x > maxBottom.x))) { maxBottom = vertex; } if ((maxLeft == null) || (maxLeft.x > vertex.x) || ((maxLeft.x == vertex.x) && (vertex.y > maxLeft.y))) { maxLeft = vertex; maxLeftIndex = i; } if ((maxRight == null) || (maxRight.x < vertex.x) || ((maxRight.x == vertex.x) && (vertex.y < maxRight.y))) { maxRight = vertex; } } if (maxLeftIndex > 0) { newVertices = [] var j = 0; for (var i = maxLeftIndex; i < vertices.length; i++) { newVertices[j++] = vertices[i]; } for (var i = 0; i < maxLeftIndex; i++) { newVertices[j++] = vertices[i]; } vertices = newVertices; } var reverse = false; for (var i = 0; i < vertices.length; i++) { var vertex = vertices[i]; if (equals(vertex, maxBottom)) { reverse = true; break; } else if (equals(vertex, maxTop)) { break; } } if (reverse) { newVertices = [] newVertices[0] = vertices[0]; var j = 1; for (var i = vertices.length - 1; i > 0; i--) { newVertices[j++] = vertices[i]; } vertices = newVertices; } return (isArrayList ? (new ArrayList(vertices)) : (vertices)); } /////////////// END ArrayHelper //////////////////////////////////////////////// var ArrayHelper = gpcas.util.ArrayHelper; ////////////////// CLASS ArrayList ///////////////////////// gpcas.util.ArrayList = function (arr) { this._array = []; if (arr != null) { this._array = arr; } }; var ArrayList = gpcas.util.ArrayList; var p = gpcas.util.ArrayList.prototype; p.add = function (value) { this._array.push(value); }; p.get = function (index) { return this._array[index]; }; p.size = function () { return this._array.length; }; p.clear = function () { this._array = []; }; p.equals = function (list) { if (this._array.length != list.size()) return false; for (var i = 0; i < this._array.length; i++) { var obj1 = this._array[i]; var obj2 = list.get(i); if (!ArrayHelper.valueEqual(obj1, obj2)) { return false; } } return true; } p.hashCode = function () { return 0; }; p.isEmpty = function () { return (this._array.length == 0); } p.toArray = function () { return this._array; } ///////////////// END ArrayList //////////////////////// var Clip = gpcas.geometry.Clip = function () { }; gpcas.geometry.Clip.DEBUG = true; gpcas.geometry.Clip.GPC_EPSILON = 2.2204460492503131e-016; gpcas.geometry.Clip.GPC_VERSION = "2.31"; gpcas.geometry.Clip.LEFT = 0; gpcas.geometry.Clip.RIGHT = 1; gpcas.geometry.Clip.ABOVE = 0; gpcas.geometry.Clip.BELOW = 1; gpcas.geometry.Clip.CLIP = 0; gpcas.geometry.Clip.SUBJ = 1; var p = gpcas.geometry.Clip.prototype; var static = gpcas.geometry.Clip; // ---------------------- // --- Static Methods --- // ---------------------- /** * Return the intersection of <code>p1</code> and <code>p2</code> where the * return type is of <code>polyClass</code>. See the note in the class description * for more on <ocde>polyClass</code>. * * @param p1 One of the polygons to performt he intersection with * @param p2 One of the polygons to performt he intersection with * @param polyClass The type of <code>Poly</code> to return */ static.intersection = function (p1, p2, polyClass) { if (polyClass == null || polyClass == undefined) { polyClass = "PolyDefault"; } return Clip.clip(OperationType.GPC_INT, p1, p2, polyClass); }; /** * Return the union of <code>p1</code> and <code>p2</code> where the * return type is of <code>polyClass</code>. See the note in the class description * for more on <ocde>polyClass</code>. * * @param p1 One of the polygons to performt he union with * @param p2 One of the polygons to performt he union with * @param polyClass The type of <code>Poly</code> to return */ static.union = function (p1, p2, polyClass) { if (polyClass == null || polyClass == undefined) { polyClass = "PolyDefault"; } return Clip.clip(OperationType.GPC_UNION, p1, p2, polyClass); }; /** * Return the xor of <code>p1</code> and <code>p2</code> where the * return type is of <code>polyClass</code>. See the note in the class description * for more on <ocde>polyClass</code>. * * @param p1 One of the polygons to performt he xor with * @param p2 One of the polygons to performt he xor with * @param polyClass The type of <code>Poly</code> to return */ static.xor = function (p1, p2, polyClass) { if (polyClass == null || polyClass == undefined) { polyClass = "PolyDefault"; } return Clip.clip(OperationType.GPC_XOR, p1, p2, polyClass); }; /** * Return the difference of <code>p1</code> and <code>p2</code> where the * return type is of <code>polyClass</code>. See the note in the class description * for more on <ocde>polyClass</code>. * * @param p1 Polygon from which second polygon will be substracted * @param p2 Second polygon * @param polyClass The type of <code>Poly</code> to return */ static.difference = function (p1, p2, polyClass) { if (polyClass == null || polyClass == undefined) { polyClass = "PolyDefault"; } return Clip.clip(OperationType.GPC_DIFF, p2, p1, polyClass); } static.intersection = function (p1, p2) { return Clip.clip(OperationType.GPC_INT, p1, p2, "PolyDefault.class"); } // ----------------------- // --- Private Methods --- // ----------------------- /** * Create a new <code>Poly</code> type object using <code>polyClass</code>. */ static.createNewPoly = function (polyClass) { /* TODO : try { return (Poly)polyClass.newInstance(); } catch( var e:Exception) { throw new RuntimeException(e); }*/ if (polyClass == "PolySimple") { return new PolySimple(); } if (polyClass == "PolyDefault") { return new PolyDefault(); } if (polyClass == "PolyDefault.class") { return new PolyDefault(); } return null; } /** * <code>clip()</code> is the main method of the clipper algorithm. * This is where the conversion from really begins. */ static.clip = function (op, subj, clip, polyClass) { var result = Clip.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()) { Clip.minimax_test(subj, clip, op); } //console.log("SUBJ " + subj); //console.log("CLIP " + clip); /* Build LMT */ var lmt_table = new LmtTable(); var sbte = new ScanBeamTreeEntries(); var s_heap = null; var c_heap = null; if (!subj.isEmpty()) { s_heap = Clip.build_lmt(lmt_table, sbte, subj, Clip.SUBJ, op); } if (Clip.DEBUG) { //console.log(""); //console.log(" ------------ After build_lmt for subj ---------"); lmt_table.print(); } if (!clip.isEmpty()) { c_heap = Clip.build_lmt(lmt_table, sbte, clip, Clip.CLIP, op); } if (Clip.DEBUG) { //console.log(""); //console.log(" ------------ 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 */ var sbt = sbte.build_sbt(); var parity = []; parity[0] = Clip.LEFT; parity[1] = Clip.LEFT; /* Invert clip polygon for difference operation */ if (op == OperationType.GPC_DIFF) { parity[Clip.CLIP] = Clip.RIGHT; } if (Clip.DEBUG) { //console.log(sbt); } var local_min = lmt_table.top_node; var out_poly = new TopPolygonNode(); // used to create resulting Poly var aet = new AetTree(); var scanbeam = 0; /* Process each scanbeam */ while (scanbeam < sbt.length) { /* Set yb and yt to the bottom and top of the scanbeam */ var yb = sbt[scanbeam++]; var yt = 0.0; var dy = 0.0; 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 (var edge = local_min.first_bound; (edge != null); edge = edge.next_bound) { Clip.add_edge_to_aet(aet, edge); } local_min = local_min.next; } } if (Clip.DEBUG) { aet.print(); } /* Set dummy previous x value */ var px = -Number.MAX_VALUE; /* Create bundles within AET */ var e0 = aet.top_node; var e1 = aet.top_node; /* Set up bundle fields of first edge */ aet.top_node.bundle[Clip.ABOVE][ aet.top_node.type ] = (aet.top_node.top.y != yb) ? 1 : 0; aet.top_node.bundle[Clip.ABOVE][ ((aet.top_node.type == 0) ? 1 : 0) ] = 0; aet.top_node.bstate[Clip.ABOVE] = BundleState.UNBUNDLED; for (var next_edge = aet.top_node.next; (next_edge != null); next_edge = next_edge.next) { var ne_type = next_edge.type; var ne_type_opp = ((next_edge.type == 0) ? 1 : 0); //next edge type opposite /* Set up bundle fields of next edge */ next_edge.bundle[Clip.ABOVE][ ne_type ] = (next_edge.top.y != yb) ? 1 : 0; next_edge.bundle[Clip.ABOVE][ ne_type_opp ] = 0; next_edge.bstate[Clip.ABOVE] = BundleState.UNBUNDLED; /* Bundle edges above the scanbeam boundary if they coincide */ if (next_edge.bundle[Clip.ABOVE][ne_type] == 1) { if (Clip.EQ(e0.xb, next_edge.xb) && Clip.EQ(e0.dx, next_edge.dx) && (e0.top.y != yb)) { next_edge.bundle[Clip.ABOVE][ ne_type ] ^= e0.bundle[Clip.ABOVE][ ne_type ]; next_edge.bundle[Clip.ABOVE][ ne_type_opp ] = e0.bundle[Clip.ABOVE][ ne_type_opp ]; next_edge.bstate[Clip.ABOVE] = BundleState.BUNDLE_HEAD; e0.bundle[Clip.ABOVE][Clip.CLIP] = 0; e0.bundle[Clip.ABOVE][Clip.SUBJ] = 0; e0.bstate[Clip.ABOVE] = BundleState.BUNDLE_TAIL; } e0 = next_edge; } } var horiz = []; horiz[Clip.CLIP] = HState.NH; horiz[Clip.SUBJ] = HState.NH; var exists = []; exists[Clip.CLIP] = 0; exists[Clip.SUBJ] = 0; var cf = null; /* Process each edge at this scanbeam boundary */ for (var edge = aet.top_node; (edge != null); edge = edge.next) { exists[Clip.CLIP] = edge.bundle[Clip.ABOVE][Clip.CLIP] + (edge.bundle[Clip.BELOW][Clip.CLIP] << 1); exists[Clip.SUBJ] = edge.bundle[Clip.ABOVE][Clip.SUBJ] + (edge.bundle[Clip.BELOW][Clip.SUBJ] << 1); if ((exists[Clip.CLIP] != 0) || (exists[Clip.SUBJ] != 0)) { /* Set bundle side */ edge.bside[Clip.CLIP] = parity[Clip.CLIP]; edge.bside[Clip.SUBJ] = parity[Clip.SUBJ]; var contributing = false; var br = 0; var bl = 0; var tr = 0; var tl = 0; /* Determine contributing status and quadrant occupancies */ if ((op == OperationType.GPC_DIFF) || (op == OperationType.GPC_INT)) { contributing = ((exists[Clip.CLIP] != 0) && ((parity[Clip.SUBJ] != 0) || (horiz[Clip.SUBJ] != 0))) || ((exists[Clip.SUBJ] != 0) && ((parity[Clip.CLIP] != 0) || (horiz[Clip.CLIP] != 0))) || ((exists[Clip.CLIP] != 0) && (exists[Clip.SUBJ] != 0) && (parity[Clip.CLIP] == parity[Clip.SUBJ])); br = ((parity[Clip.CLIP] != 0) && (parity[Clip.SUBJ] != 0)) ? 1 : 0; bl = ( ((parity[Clip.CLIP] ^ edge.bundle[Clip.ABOVE][Clip.CLIP]) != 0) && ((parity[Clip.SUBJ] ^ edge.bundle[Clip.ABOVE][Clip.SUBJ]) != 0) ) ? 1 : 0; tr = ( ((parity[Clip.CLIP] ^ ((horiz[Clip.CLIP] != HState.NH) ? 1 : 0)) != 0) && ((parity[Clip.SUBJ] ^ ((horiz[Clip.SUBJ] != HState.NH) ? 1 : 0)) != 0) ) ? 1 : 0; tl = (((parity[Clip.CLIP] ^ ((horiz[Clip.CLIP] != HState.NH) ? 1 : 0) ^ edge.bundle[Clip.BELOW][Clip.CLIP]) != 0) && ((parity[Clip.SUBJ] ^ ((horiz[Clip.SUBJ] != HState.NH) ? 1 : 0) ^ edge.bundle[Clip.BELOW][Clip.SUBJ]) != 0)) ? 1 : 0; } else if (op == OperationType.GPC_XOR) { contributing = (exists[Clip.CLIP] != 0) || (exists[Clip.SUBJ] != 0); br = (parity[Clip.CLIP]) ^ (parity[Clip.SUBJ]); bl = (parity[Clip.CLIP] ^ edge.bundle[Clip.ABOVE][Clip.CLIP]) ^ (parity[Clip.SUBJ] ^ edge.bundle[Clip.ABOVE][Clip.SUBJ]); tr = (parity[Clip.CLIP] ^ ((horiz[Clip.CLIP] != HState.NH) ? 1 : 0)) ^ (parity[Clip.SUBJ] ^ ((horiz[Clip.SUBJ] != HState.NH) ? 1 : 0)); tl = (parity[Clip.CLIP] ^ ((horiz[Clip.CLIP] != HState.NH) ? 1 : 0) ^ edge.bundle[Clip.BELOW][Clip.CLIP]) ^ (parity[Clip.SUBJ] ^ ((horiz[Clip.SUBJ] != HState.NH) ? 1 : 0) ^ edge.bundle[Clip.BELOW][Clip.SUBJ]); } else if (op == OperationType.GPC_UNION) { contributing = ((exists[Clip.CLIP] != 0) && (!(parity[Clip.SUBJ] != 0) || (horiz[Clip.SUBJ] != 0))) || ((exists[Clip.SUBJ] != 0) && (!(parity[Clip.CLIP] != 0) || (horiz[Clip.CLIP] != 0))) || ((exists[Clip.CLIP] != 0) && (exists[Clip.SUBJ] != 0) && (parity[Clip.CLIP] == parity[Clip.SUBJ])); br = ((parity[Clip.CLIP] != 0) || (parity[Clip.SUBJ] != 0)) ? 1 : 0; bl = (((parity[Clip.CLIP] ^ edge.bundle[Clip.ABOVE][Clip.CLIP]) != 0) || ((parity[Clip.SUBJ] ^ edge.bundle[Clip.ABOVE][Clip.SUBJ]) != 0)) ? 1 : 0; tr = ( ((parity[Clip.CLIP] ^ ((horiz[Clip.CLIP] != HState.NH) ? 1 : 0)) != 0) || ((parity[Clip.SUBJ] ^ ((horiz[Clip.SUBJ] != HState.NH) ? 1 : 0)) != 0) ) ? 1 : 0; tl = ( ((parity[Clip.CLIP] ^ ((horiz[Clip.CLIP] != HState.NH) ? 1 : 0) ^ edge.bundle[Clip.BELOW][Clip.CLIP]) != 0) || ((parity[Clip.SUBJ] ^ ((horiz[Clip.SUBJ] != HState.NH) ? 1 : 0) ^ edge.bundle[Clip.BELOW][Clip.SUBJ]) != 0) ) ? 1 : 0; } else { //console.log("ERROR : Unknown op"); } /* Update parity */ parity[Clip.CLIP] ^= edge.bundle[Clip.ABOVE][Clip.CLIP]; parity[Clip.SUBJ] ^= edge.bundle[Clip.ABOVE][Clip.SUBJ]; /* Update horizontal state */ if (exists[Clip.CLIP] != 0) { horiz[Clip.CLIP] = HState.next_h_state[horiz[Clip.CLIP]][((exists[Clip.CLIP] - 1) << 1) + parity[Clip.CLIP]]; } if (exists[Clip.SUBJ] != 0) { horiz[Clip.SUBJ] = HState.next_h_state[horiz[Clip.SUBJ]][((exists[Clip.SUBJ] - 1) << 1) + parity[Clip.SUBJ]]; } if (contributing) { var xb = edge.xb; var vclass = VertexType.getType(tr, tl, br, bl); switch (vclass) { case VertexType.EMN: case VertexType.IMN: edge.outp[Clip.ABOVE] = out_poly.add_local_min(xb, yb); px = xb; cf = edge.outp[Clip.ABOVE]; break; case VertexType.ERI: if (xb != px) { cf.add_right(xb, yb); px = xb; } edge.outp[Clip.ABOVE] = cf; cf = null; break; case VertexType.ELI: edge.outp[Clip.BELOW].add_left(xb, yb); px = xb; cf = edge.outp[Clip.BELOW]; break; case VertexType.EMX: if (xb != px) { cf.add_left(xb, yb); px = xb; } out_poly.merge_right(cf, edge.outp[Clip.BELOW]); cf = null; break; case VertexType.ILI: if (xb != px) { cf.add_left(xb, yb); px = xb; } edge.outp[Clip.ABOVE] = cf; cf = null; break; case VertexType.IRI: edge.outp[Clip.BELOW].add_right(xb, yb); px = xb; cf = edge.outp[Clip.BELOW]; edge.outp[Clip.BELOW] = null; break; case VertexType.IMX: if (xb != px) { cf.add_right(xb, yb); px = xb; } out_poly.merge_left(cf, edge.outp[Clip.BELOW]); cf = null; edge.outp[Clip.BELOW] = null; break; case VertexType.IMM: if (xb != px) { cf.add_right(xb, yb); px = xb; } out_poly.merge_left(cf, edge.outp[Clip.BELOW]); edge.outp[Clip.BELOW] = null; edge.outp[Clip.ABOVE] = out_poly.add_local_min(xb, yb); cf = edge.outp[Clip.ABOVE]; break; case VertexType.EMM: if (xb != px) { cf.add_left(xb, yb); px = xb; } out_poly.merge_right(cf, edge.outp[Clip.BELOW]); edge.outp[Clip.BELOW] = null; edge.outp[Clip.ABOVE] = out_poly.add_local_min(xb, yb); cf = edge.outp[Clip.ABOVE]; break; case VertexType.LED: if (edge.bot.y == yb) edge.outp[Clip.BELOW].add_left(xb, yb); edge.outp[Clip.ABOVE] = edge.outp[Clip.BELOW]; px = xb; break; case VertexType.RED: if (edge.bot.y == yb) edge.outp[Clip.BELOW].add_right(xb, yb); edge.outp[Clip.ABOVE] = edge.outp[Clip.BELOW]; px = xb; break; default: break; } /* End of switch */ } /* End of contributing conditional */ } /* End of edge exists conditional */ if (Clip.DEBUG) { out_poly.print(); } out_poly.print(); } /* End of AET loop */ /* Delete terminating edges from the AET, otherwise compute xt */ for (var edge = aet.top_node; (edge != null); edge = edge.next) { if (edge.top.y == yb) { var prev_edge = edge.prev; var 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[Clip.BELOW] == BundleState.BUNDLE_HEAD) && (prev_edge != null)) { if (prev_edge.bstate[Clip.BELOW] == BundleState.BUNDLE_TAIL) { prev_edge.outp[Clip.BELOW] = edge.outp[Clip.BELOW]; prev_edge.bstate[Clip.BELOW] = BundleState.UNBUNDLED; if (prev_edge.prev != null) { if (prev_edge.prev.bstate[Clip.BELOW] == BundleState.BUNDLE_TAIL) { prev_edge.bstate[Clip.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 */ var it_table = new ItNodeTable(); it_table.build_intersection_table(aet, dy); /* Process each node in the intersection table */ for (var 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[Clip.ABOVE][Clip.CLIP] != 0) || (e0.bundle[Clip.ABOVE][Clip.SUBJ] != 0)) && ((e1.bundle[Clip.ABOVE][Clip.CLIP] != 0) || (e1.bundle[Clip.ABOVE][Clip.SUBJ] != 0))) { var p = e0.outp[Clip.ABOVE]; var q = e1.outp[Clip.ABOVE]; var ix = intersect.point.x; var iy = intersect.point.y + yb; var in_clip = ( ( (e0.bundle[Clip.ABOVE][Clip.CLIP] != 0) && !(e0.bside[Clip.CLIP] != 0)) || ( (e1.bundle[Clip.ABOVE][Clip.CLIP] != 0) && (e1.bside[Clip.CLIP] != 0)) || (!(e0.bundle[Clip.ABOVE][Clip.CLIP] != 0) && !(e1.bundle[Clip.ABOVE][Clip.CLIP] != 0) && (e0.bside[Clip.CLIP] != 0) && (e1.bside[Clip.CLIP] != 0) ) ) ? 1 : 0; var in_subj = ( ( (e0.bundle[Clip.ABOVE][Clip.SUBJ] != 0) && !(e0.bside[Clip.SUBJ] != 0)) || ( (e1.bundle[Clip.ABOVE][Clip.SUBJ] != 0) && (e1.bside[Clip.SUBJ] != 0)) || (!(e0.bundle[Clip.ABOVE][Clip.SUBJ] != 0) && !(e1.bundle[Clip.ABOVE][Clip.SUBJ] != 0) && (e0.bside[Clip.SUBJ] != 0) && (e1.bside[Clip.SUBJ] != 0) ) ) ? 1 : 0; var tr = 0 var tl = 0; var br = 0; var 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[Clip.ABOVE][Clip.CLIP]) != 0) && ((in_subj ^ e1.bundle[Clip.ABOVE][Clip.SUBJ]) != 0)) ? 1 : 0; br = (((in_clip ^ e0.bundle[Clip.ABOVE][Clip.CLIP]) != 0) && ((in_subj ^ e0.bundle[Clip.ABOVE][Clip.SUBJ]) != 0)) ? 1 : 0; bl = (((in_clip ^ e1.bundle[Clip.ABOVE][Clip.CLIP] ^ e0.bundle[Clip.ABOVE][Clip.CLIP]) != 0) && ((in_subj ^ e1.bundle[Clip.ABOVE][Clip.SUBJ] ^ e0.bundle[Clip.ABOVE][Clip.SUBJ]) != 0) ) ? 1 : 0; } else if (op == OperationType.GPC_XOR) { tr = in_clip ^ in_subj; tl = (in_clip ^ e1.bundle[Clip.ABOVE][Clip.CLIP]) ^ (in_subj ^ e1.bundle[Clip.ABOVE][Clip.SUBJ]); br = (in_clip ^ e0.bundle[Clip.ABOVE][Clip.CLIP]) ^ (in_subj ^ e0.bundle[Clip.ABOVE][Clip.SUBJ]); bl = (in_clip ^ e1.bundle[Clip.ABOVE][Clip.CLIP] ^ e0.bundle[Clip.ABOVE][Clip.CLIP]) ^ (in_subj ^ e1.bundle[Clip.ABOVE][Clip.SUBJ] ^ e0.bundle[Clip.ABOVE][Clip.SUBJ]); } else if (op == OperationType.GPC_UNION) { tr = ((in_clip != 0) || (in_subj != 0)) ? 1 : 0; tl = (((in_clip ^ e1.bundle[Clip.ABOVE][Clip.CLIP]) != 0) || ((in_subj ^ e1.bundle[Clip.ABOVE][Clip.SUBJ]) != 0)) ? 1 : 0; br = (((in_clip ^ e0.bundle[Clip.ABOVE][Clip.CLIP]) != 0) || ((in_subj ^ e0.bundle[Clip.ABOVE][Clip.SUBJ]) != 0)) ? 1 : 0; bl = (((in_clip ^ e1.bundle[Clip.ABOVE][Clip.CLIP] ^ e0.bundle[Clip.ABOVE][Clip.CLIP]) != 0) || ((in_subj ^ e1.bundle[Clip.ABOVE][Clip.SUBJ] ^ e0.bundle[Clip.ABOVE][Clip.SUBJ]) != 0)) ? 1 : 0; } else { //console.log("ERROR : Unknown op type, "+op); } var vclass = VertexType.getType(tr, tl, br, bl); switch (vclass) { case VertexType.EMN: e0.outp[Clip.ABOVE] = out_poly.add_local_min(ix, iy); e1.outp[Clip.ABOVE] = e0.outp[Clip.ABOVE]; break; case VertexType.ERI: if (p != null) { p.add_right(ix, iy); e1.outp[Clip.ABOVE] = p; e0.outp[Clip.ABOVE] = null; } break; case VertexType.ELI: if (q != null) { q.add_left(ix, iy); e0.outp[Clip.ABOVE] = q; e1.outp[Clip.ABOVE] = null; } break; case VertexType.EMX: if ((p != null) && (q != null)) { p.add_left(ix, iy); out_poly.merge_right(p, q); e0.outp[Clip.ABOVE] = null; e1.outp[Clip.ABOVE] = null; } break; case VertexType.IMN: e0.outp[Clip.ABOVE] = out_poly.add_local_min(ix, iy); e1.outp[Clip.ABOVE] = e0.outp[Clip.ABOVE]; break; case VertexType.ILI: if (p != null) { p.add_left(ix, iy); e1.outp[Clip.ABOVE] = p; e0.outp[Clip.ABOVE] = null; } break; case VertexType.IRI: if (q != null) { q.add_right(ix, iy); e0.outp[Clip.ABOVE] = q; e1.outp[Clip.ABOVE] = null; } break; case VertexType.IMX: if ((p != null) && (q != null)) { p.add_right(ix, iy); out_poly.merge_left(p, q); e0.outp[Clip.ABOVE] = null; e1.outp[Clip.ABOVE] = null; } break; case VertexType.IMM: if ((p != null) && (q != null)) { p.add_right(ix, iy); out_poly.merge_left(p, q); e0.outp[Clip.ABOVE] = out_poly.add_local_min(ix, iy); e1.outp[Clip.ABOVE] = e0.outp[Clip.ABOVE]; } break; case VertexType.EMM: if ((p != null) && (q != null)) { p.add_left(ix, iy); out_poly.merge_right(p, q); e0.outp[Clip.ABOVE] = out_poly.add_local_min(ix, iy); e1.outp[Clip.ABOVE] = e0.outp[Clip.ABOVE]; } break; default: break; } /* End of switch */ } /* End of contributing intersection conditional */ /* Swap bundle sides in response to edge crossing */ if (e0.bundle[Clip.ABOVE][Clip.CLIP] != 0) e1.bside[Clip.CLIP] = (e1.bside[Clip.CLIP] == 0) ? 1 : 0; if (e1.bundle[Clip.ABOVE][Clip.CLIP] != 0) e0.bside[Clip.CLIP] = (e0.bside[Clip.CLIP] == 0) ? 1 : 0; if (e0.bundle[Clip.ABOVE][Clip.SUBJ] != 0) e1.bside[Clip.SUBJ] = (e1.bside[Clip.SUBJ] == 0) ? 1 : 0; if (e1.bundle[Clip.ABOVE][Clip.SUBJ] != 0) e0.bside[Clip.SUBJ] = (e0.bside[Clip.SUBJ] == 0) ? 1 : 0; /* Swap e0 and e1 bundles in the AET */ var prev_edge = e0.prev; var next_edge = e1.next; if (next_edge != null) { next_edge.prev = e0; } if (e0.bstate[Clip.ABOVE] == BundleState.BUNDLE_HEAD) { var search = true; while (search) { prev_edge = prev_edge.prev; if (prev_edge != null) { if (prev_edge.bstate[Clip.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 (Clip.DEBUG) { out_poly.print(); } } /* End of IT loop*/ /* Prepare for next scanbeam */ for (var edge = aet.top_node; (edge != null); edge = edge.next) { var next_edge = edge.next; var succ_edge = edge.succ; if ((edge.top.y == yt) && (succ_edge != null)) { /* Replace AET edge by its successor */ succ_edge.outp[Clip.BELOW] = edge.outp[Clip.ABOVE]; succ_edge.bstate[Clip.BELOW] = edge.bstate[Clip.ABOVE]; succ_edge.bundle[Clip.BELOW][Clip.CLIP] = edge.bundle[Clip.ABOVE][Clip.CLIP]; succ_edge.bundle[Clip.BELOW][Clip.SUBJ] = edge.bundle[Clip.ABOVE][Clip.SUBJ]; var 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[Clip.BELOW] = edge.outp[Clip.ABOVE]; edge.bstate[Clip.BELOW] = edge.bstate[Clip.ABOVE]; edge.bundle[Clip.BELOW][Clip.CLIP] = edge.bundle[Clip.ABOVE][Clip.CLIP]; edge.bundle[Clip.BELOW][Clip.SUBJ] = edge.bundle[Clip.ABOVE][Clip.SUBJ]; edge.xb = edge.xt; } edge.outp[Clip.ABOVE] = null; } } } /* === END OF SCANBEAM PROCESSING ================================== */ /* Generate result polygon from out_poly */ result = out_poly.getResult(polyClass); //console.log("result = "+result); return result; } static.EQ = function (a, b) { return (Math.abs(a - b) <= Clip.GPC_EPSILON); } static.PREV_INDEX = function (i, n) { return ((i - 1 + n) % n); } static.NEXT_INDEX = function (i, n) { return ((i + 1) % n); } static.OPTIMAL = function (p, i) { return (p.getY(Clip.PREV_INDEX(i, p.getNumPoints())) != p.getY(i)) || (p.getY(Clip.NEXT_INDEX(i, p.getNumPoints())) != p.getY(i)); } static.create_contour_bboxes = function (p) { var box = []; /* Construct contour bounding boxes */ for (var c = 0; c < p.getNumInnerPoly(); c++) { var inner_poly = p.getInnerPoly(c); box[c] = inner_poly.getBounds(); } return box; } static.minimax_test = function (subj, clip, op) { var s_bbox = Clip.create_contour_bboxes(subj); var c_bbox = Clip.create_contour_bboxes(clip); var subj_num_poly = subj.getNumInnerPoly(); var clip_num_poly = clip.getNumInnerPoly(); var o_table = ArrayHelper.create2DArray(subj_num_poly, clip_num_poly); /* Check all subject contour bounding boxes against clip boxes */ for (var s = 0; s < subj_num_poly; s++) { for (var c = 0; c < clip_num_poly; c++) { o_table[s][c] = (!((s_bbox[s].getMaxX() < c_bbox[c].getMinX()) || (s_bbox[s].getMinX() > c_bbox[c].getMaxX()))) && (!((s_bbox[s].getMaxY() < c_bbox[c].getMinY()) || (s_bbox[s].getMinY() > c_bbox[c].getMaxY()))); } } /* For each clip contour, search for any subject contour overlaps */ for (var c = 0; c < clip_num_poly; c++) { var overlap = false; for (var s = 0; !overlap && (s < subj_num_poly); s++) { overlap = o_table[s][c]; } if (!overlap) { clip.setContributing(c, false); // Flag non contributing status } } if (op == OperationType.GPC_INT) { /* For each subject contour, search for any clip contour overlaps */ for (var s = 0; s < subj_num_poly; s++) { var overlap = false; for (var c = 0; !overlap && (c < clip_num_poly); c++) { overlap = o_table[s][c]; } if (!overlap) { subj.setContributing(s, false); // Flag non contributing status } } } } static.bound_list = function (lmt_table, y) { if (lmt_table.top_node == null) { lmt_table.top_node = new LmtNode(y); return lmt_table.top_node; } else { var prev = null; var node = lmt_table.top_node; var done = false; while (!done) { if (y < node.y) { /* Insert a new LMT node before the current node */ var existing_node = node; node = new LmtNode(y); node.next = existing_node; if (prev == null) { lmt_table.top_node = node; } else { prev.next = node; } // if( existing_node == lmt_table.top_node ) // { // lmt_table.top_node = node ; // } done = true; } else if (y > node.y) { /* Head further up the LMT */ if (node.next == null) { node.next = new LmtNode(y); node = node.next; done = true; } else { prev = node; node = node.next; } } else { /* Use this existing LMT node */ done = true; } } return node; } } static.insert_bound = function (lmt_node, e) { if (lmt_node.first_bound == null) { /* Link node e to the tail of the list */ lmt_node.first_bound = e; } else { var done = false; var prev_bound = null; var current_bound = lmt_node.first_bound; while (!done) { /* Do primary sort on the x field */ if (e.bot.x < current_bound.bot.x) { /* Insert a new node mid-list */ if (prev_bound == null) { lmt_node.first_bound = e; } else { prev_bound.next_bound = e; } e.next_bound = current_bound; // EdgeNode existing_bound = current_bound ; // current_bound = e ; // current_bound.next_bound = existing_bound ; // if( lmt_node.first_bound == existing_bound ) // { // lmt_node.first_bound = current_bound ; // } done = true; } else if (e.bot.x == current_bound.bot.x) { /* Do secondary sort on the dx field */ if (e.dx < current_bound.dx) { /* Insert a new node mid-list */ if (prev_bound == null) { lmt_node.first_bound = e; } else { prev_bound.next_bound = e; } e.next_bound = current_bound; // EdgeNode existing_bound = current_bound ; // current_bound = e ; // current_bound.next_bound = existing_bound ; // if( lmt_node.first_bound == existing_bound ) // { // lmt_node.first_bound = current_bound ; // } done = true; } else { /* Head further down the list */ if (current_bound.next_bound == null) { current_bound.next_bound = e; done = true; } else { prev_bound = current_bound; current_bound = current_bound.next_bound; } } } else { /* Head further down the list */ if (current_bound.next_bound == null) { current_bound.next_bound = e; done = true; } else { prev_bound = current_bound; current_bound = current_bound.next_bound; } } } } } static.add_edge_to_aet = function (aet, edge) { if (aet.top_node == null) { /* Append edge onto the tail end of the AET */ aet.top_node = edge; edge.prev = null; edge.next = null; } else { var current_edge = aet.top_node; var prev = null; var done = false; while (!done) { /* Do primary sort on the xb field */ if (edge.xb < current_edge.xb) { /* Insert edge here (before the AET edge) */ edge.prev = prev; edge.next = current_edge; current_edge.prev = edge; if (prev == null) { aet.top_node = edge; } else { prev.next = edge; } // if( current_edge == aet.top_node ) // { // aet.top_node = edge ; // } // current_edge = edge ; done = true; } else if (edge.xb == current_edge.xb) { /* Do secondary sort on the dx field */ if (edge.dx < current_edge.dx) { /* Insert edge here (before the AET edge) */ edge.prev = prev; edge.next = current_edge; current_edge.prev = edge; if (prev == null) { aet.top_node = edge; } else { prev.next = edge; } // if( current_edge == aet.top_node ) // { // aet.top_node = edge ; // } // current_edge = edge ; done = true; } else { /* Head further into the AET */ prev = current_edge; if (current_edge.next == null) { current_edge.next = edge; edge.prev = current_edge; edge.next = null; done = true; } else { current_edge = current_edge.next; } } } else { /* Head further into the AET */ prev = current_edge; if (current_edge.next == null) { current_edge.next = edge; edge.prev = current_edge; edge.next = null; done = true; } else { current_edge = current_edge.next; } } } } } static.add_to_sbtree = function (sbte, y) { if (sbte.sb_tree == null) { /* Add a new tree node here */ sbte.sb_tree = new ScanBeamTree(y); sbte.sbt_entries++; return; } var tree_node = sbte.sb_tree; var done = false; while (!done) { if (tree_node.y > y) { if (tree_node.less == null) { tree_node.less = new ScanBeamTree(y); sbte.sbt_entries++; done = true; } else { tree_node = tree_node.less; } } else if (tree_node.y < y) { if (tree_node.more == null) { tree_node.more = new ScanBeamTree(y); sbte.sbt_entries++; done = true; } else { tree_node = tree_node.more; } } else { done = true; } } } static.build_lmt = function (lmt_table, sbte, p, type, //poly type SUBJ/Clip.CLIP op) { /* Create the entire input polygon edge table in one go */ var edge_table = new EdgeTable(); for (var c = 0; c < p.getNumInnerPoly(); c++) { var ip = p.getInnerPoly(c); if (!ip.isContributing(0)) { /* Ignore the non-contributing contour */ ip.setContributing(0, true); } else { /* Perform contour optimisation */ var num_vertices = 0; var e_index = 0; edge_table = new EdgeTable(); for (var i = 0; i < ip.getNumPoints(); i++) { if (Clip.OPTIMAL(ip, i)) { var x = ip.getX(i); var y = ip.getY(i); edge_table.addNode(x, y); /* Record vertex in the scanbeam table */ Clip.add_to_sbtree(sbte, ip.getY(i)); num_vertices++; } } /* Do the contour forward pass */ for (var min = 0; min < num_vertices; min++) { /* If a forward local minimum... */ if (edge_table.FWD_MIN(min)) { /* Search for the next local maximum... */ var num_edges = 1; var max = Clip.NEXT_INDEX(min, num_vertices); while (edge_table.NOT_FMAX(max)) { num_edges++; max = Clip.NEXT_INDEX(max, num_vertices); } /* Build the next edge list */ var v = min; var e = edge_table.getNode(e_index); e.bstate[Clip.BELOW] = BundleState.UNBUNDLED; e.bundle[Clip.BELOW][Clip.CLIP] = 0; e.bundle[Clip.BELOW][Clip.SUBJ] = 0; for (var i = 0; i < num_edges; i++) { var ei = edge_table.getNode(e_index + i); var ev = edge_table.getNode(v); ei.xb = ev.vertex.x; ei.bot.x = ev.vertex.x; ei.bot.y = ev.vertex.y; v = Clip.NEXT_INDEX(v, num_vertices); ev = edge_table.getNode(v); ei.top.x = ev.vertex.x; ei.top.y = ev.vertex.y; ei.dx = (ev.vertex.x - ei.bot.x) / (ei.top.y - ei.bot.y); ei.type = type; ei.outp[Clip.ABOVE] = null; ei.outp[Clip.BELOW] = null; ei.next = null; ei.prev = null; ei.succ = ((num_edges > 1) && (i < (num_edges - 1))) ? edge_table.getNode(e_index + i + 1) : null; ei.pred = ((num_edges > 1) && (i > 0)) ? edge_table.getNode(e_index + i - 1) : null; ei.next_bound = null;