UNPKG

poly2tri

Version:

A 2D constrained Delaunay triangulation library

562 lines (513 loc) 16.1 kB
/* * Poly2Tri Copyright (c) 2009-2014, Poly2Tri Contributors * http://code.google.com/p/poly2tri/ * * poly2tri.js (JavaScript port) (c) 2009-2014, Poly2Tri Contributors * https://github.com/r3mi/poly2tri.js * * All rights reserved. * * Distributed under the 3-clause BSD License, see LICENSE.txt */ /* jshint maxcomplexity:10 */ "use strict"; /* * Note * ==== * the structure of this JavaScript version of poly2tri intentionally follows * as closely as possible the structure of the reference C++ version, to make it * easier to keep the 2 versions in sync. */ var xy = require("./xy"); // ---------------------------------------------------------------------Triangle /** * Triangle class.<br> * Triangle-based data structures are known to have better performance than * quad-edge structures. * See: J. Shewchuk, "Triangle: Engineering a 2D Quality Mesh Generator and * Delaunay Triangulator", "Triangulations in CGAL" * * @constructor * @struct * @param {!XY} pa point object with {x,y} * @param {!XY} pb point object with {x,y} * @param {!XY} pc point object with {x,y} */ var Triangle = function(a, b, c) { /** * Triangle points * @private * @type {Array.<XY>} */ this.points_ = [a, b, c]; /** * Neighbor list * @private * @type {Array.<Triangle>} */ this.neighbors_ = [null, null, null]; /** * Has this triangle been marked as an interior triangle? * @private * @type {boolean} */ this.interior_ = false; /** * Flags to determine if an edge is a Constrained edge * @private * @type {Array.<boolean>} */ this.constrained_edge = [false, false, false]; /** * Flags to determine if an edge is a Delauney edge * @private * @type {Array.<boolean>} */ this.delaunay_edge = [false, false, false]; }; var p2s = xy.toString; /** * For pretty printing ex. <code>"[(5;42)(10;20)(21;30)]"</code>. * @public * @return {string} */ Triangle.prototype.toString = function() { return ("[" + p2s(this.points_[0]) + p2s(this.points_[1]) + p2s(this.points_[2]) + "]"); }; /** * Get one vertice of the triangle. * The output triangles of a triangulation have vertices which are references * to the initial input points (not copies): any custom fields in the * initial points can be retrieved in the output triangles. * @example * var contour = [{x:100, y:100, id:1}, {x:100, y:300, id:2}, {x:300, y:300, id:3}]; * var swctx = new poly2tri.SweepContext(contour); * swctx.triangulate(); * var triangles = swctx.getTriangles(); * typeof triangles[0].getPoint(0).id * // → "number" * @param {number} index - vertice index: 0, 1 or 2 * @public * @returns {XY} */ Triangle.prototype.getPoint = function(index) { return this.points_[index]; }; /** * For backward compatibility * @function * @deprecated use {@linkcode Triangle#getPoint} instead */ Triangle.prototype.GetPoint = Triangle.prototype.getPoint; /** * Get all 3 vertices of the triangle as an array * @public * @return {Array.<XY>} */ // Method added in the JavaScript version (was not present in the c++ version) Triangle.prototype.getPoints = function() { return this.points_; }; /** * @private * @param {number} index * @returns {?Triangle} */ Triangle.prototype.getNeighbor = function(index) { return this.neighbors_[index]; }; /** * Test if this Triangle contains the Point object given as parameter as one of its vertices. * Only point references are compared, not values. * @public * @param {XY} point - point object with {x,y} * @return {boolean} <code>True</code> if the Point object is of the Triangle's vertices, * <code>false</code> otherwise. */ Triangle.prototype.containsPoint = function(point) { var points = this.points_; // Here we are comparing point references, not values return (point === points[0] || point === points[1] || point === points[2]); }; /** * Test if this Triangle contains the Edge object given as parameter as its * bounding edges. Only point references are compared, not values. * @private * @param {Edge} edge * @return {boolean} <code>True</code> if the Edge object is of the Triangle's bounding * edges, <code>false</code> otherwise. */ Triangle.prototype.containsEdge = function(edge) { return this.containsPoint(edge.p) && this.containsPoint(edge.q); }; /** * Test if this Triangle contains the two Point objects given as parameters among its vertices. * Only point references are compared, not values. * @param {XY} p1 - point object with {x,y} * @param {XY} p2 - point object with {x,y} * @return {boolean} */ Triangle.prototype.containsPoints = function(p1, p2) { return this.containsPoint(p1) && this.containsPoint(p2); }; /** * Has this triangle been marked as an interior triangle? * @returns {boolean} */ Triangle.prototype.isInterior = function() { return this.interior_; }; /** * Mark this triangle as an interior triangle * @private * @param {boolean} interior * @returns {Triangle} this */ Triangle.prototype.setInterior = function(interior) { this.interior_ = interior; return this; }; /** * Update neighbor pointers. * @private * @param {XY} p1 - point object with {x,y} * @param {XY} p2 - point object with {x,y} * @param {Triangle} t Triangle object. * @throws {Error} if can't find objects */ Triangle.prototype.markNeighborPointers = function(p1, p2, t) { var points = this.points_; // Here we are comparing point references, not values if ((p1 === points[2] && p2 === points[1]) || (p1 === points[1] && p2 === points[2])) { this.neighbors_[0] = t; } else if ((p1 === points[0] && p2 === points[2]) || (p1 === points[2] && p2 === points[0])) { this.neighbors_[1] = t; } else if ((p1 === points[0] && p2 === points[1]) || (p1 === points[1] && p2 === points[0])) { this.neighbors_[2] = t; } else { throw new Error('poly2tri Invalid Triangle.markNeighborPointers() call'); } }; /** * Exhaustive search to update neighbor pointers * @private * @param {!Triangle} t */ Triangle.prototype.markNeighbor = function(t) { var points = this.points_; if (t.containsPoints(points[1], points[2])) { this.neighbors_[0] = t; t.markNeighborPointers(points[1], points[2], this); } else if (t.containsPoints(points[0], points[2])) { this.neighbors_[1] = t; t.markNeighborPointers(points[0], points[2], this); } else if (t.containsPoints(points[0], points[1])) { this.neighbors_[2] = t; t.markNeighborPointers(points[0], points[1], this); } }; Triangle.prototype.clearNeighbors = function() { this.neighbors_[0] = null; this.neighbors_[1] = null; this.neighbors_[2] = null; }; Triangle.prototype.clearDelaunayEdges = function() { this.delaunay_edge[0] = false; this.delaunay_edge[1] = false; this.delaunay_edge[2] = false; }; /** * Returns the point clockwise to the given point. * @private * @param {XY} p - point object with {x,y} */ Triangle.prototype.pointCW = function(p) { var points = this.points_; // Here we are comparing point references, not values if (p === points[0]) { return points[2]; } else if (p === points[1]) { return points[0]; } else if (p === points[2]) { return points[1]; } else { return null; } }; /** * Returns the point counter-clockwise to the given point. * @private * @param {XY} p - point object with {x,y} */ Triangle.prototype.pointCCW = function(p) { var points = this.points_; // Here we are comparing point references, not values if (p === points[0]) { return points[1]; } else if (p === points[1]) { return points[2]; } else if (p === points[2]) { return points[0]; } else { return null; } }; /** * Returns the neighbor clockwise to given point. * @private * @param {XY} p - point object with {x,y} */ Triangle.prototype.neighborCW = function(p) { // Here we are comparing point references, not values if (p === this.points_[0]) { return this.neighbors_[1]; } else if (p === this.points_[1]) { return this.neighbors_[2]; } else { return this.neighbors_[0]; } }; /** * Returns the neighbor counter-clockwise to given point. * @private * @param {XY} p - point object with {x,y} */ Triangle.prototype.neighborCCW = function(p) { // Here we are comparing point references, not values if (p === this.points_[0]) { return this.neighbors_[2]; } else if (p === this.points_[1]) { return this.neighbors_[0]; } else { return this.neighbors_[1]; } }; Triangle.prototype.getConstrainedEdgeCW = function(p) { // Here we are comparing point references, not values if (p === this.points_[0]) { return this.constrained_edge[1]; } else if (p === this.points_[1]) { return this.constrained_edge[2]; } else { return this.constrained_edge[0]; } }; Triangle.prototype.getConstrainedEdgeCCW = function(p) { // Here we are comparing point references, not values if (p === this.points_[0]) { return this.constrained_edge[2]; } else if (p === this.points_[1]) { return this.constrained_edge[0]; } else { return this.constrained_edge[1]; } }; // Additional check from Java version (see issue #88) Triangle.prototype.getConstrainedEdgeAcross = function(p) { // Here we are comparing point references, not values if (p === this.points_[0]) { return this.constrained_edge[0]; } else if (p === this.points_[1]) { return this.constrained_edge[1]; } else { return this.constrained_edge[2]; } }; Triangle.prototype.setConstrainedEdgeCW = function(p, ce) { // Here we are comparing point references, not values if (p === this.points_[0]) { this.constrained_edge[1] = ce; } else if (p === this.points_[1]) { this.constrained_edge[2] = ce; } else { this.constrained_edge[0] = ce; } }; Triangle.prototype.setConstrainedEdgeCCW = function(p, ce) { // Here we are comparing point references, not values if (p === this.points_[0]) { this.constrained_edge[2] = ce; } else if (p === this.points_[1]) { this.constrained_edge[0] = ce; } else { this.constrained_edge[1] = ce; } }; Triangle.prototype.getDelaunayEdgeCW = function(p) { // Here we are comparing point references, not values if (p === this.points_[0]) { return this.delaunay_edge[1]; } else if (p === this.points_[1]) { return this.delaunay_edge[2]; } else { return this.delaunay_edge[0]; } }; Triangle.prototype.getDelaunayEdgeCCW = function(p) { // Here we are comparing point references, not values if (p === this.points_[0]) { return this.delaunay_edge[2]; } else if (p === this.points_[1]) { return this.delaunay_edge[0]; } else { return this.delaunay_edge[1]; } }; Triangle.prototype.setDelaunayEdgeCW = function(p, e) { // Here we are comparing point references, not values if (p === this.points_[0]) { this.delaunay_edge[1] = e; } else if (p === this.points_[1]) { this.delaunay_edge[2] = e; } else { this.delaunay_edge[0] = e; } }; Triangle.prototype.setDelaunayEdgeCCW = function(p, e) { // Here we are comparing point references, not values if (p === this.points_[0]) { this.delaunay_edge[2] = e; } else if (p === this.points_[1]) { this.delaunay_edge[0] = e; } else { this.delaunay_edge[1] = e; } }; /** * The neighbor across to given point. * @private * @param {XY} p - point object with {x,y} * @returns {Triangle} */ Triangle.prototype.neighborAcross = function(p) { // Here we are comparing point references, not values if (p === this.points_[0]) { return this.neighbors_[0]; } else if (p === this.points_[1]) { return this.neighbors_[1]; } else { return this.neighbors_[2]; } }; /** * @private * @param {!Triangle} t Triangle object. * @param {XY} p - point object with {x,y} */ Triangle.prototype.oppositePoint = function(t, p) { var cw = t.pointCW(p); return this.pointCW(cw); }; /** * Legalize triangle by rotating clockwise around oPoint * @private * @param {XY} opoint - point object with {x,y} * @param {XY} npoint - point object with {x,y} * @throws {Error} if oPoint can not be found */ Triangle.prototype.legalize = function(opoint, npoint) { var points = this.points_; // Here we are comparing point references, not values if (opoint === points[0]) { points[1] = points[0]; points[0] = points[2]; points[2] = npoint; } else if (opoint === points[1]) { points[2] = points[1]; points[1] = points[0]; points[0] = npoint; } else if (opoint === points[2]) { points[0] = points[2]; points[2] = points[1]; points[1] = npoint; } else { throw new Error('poly2tri Invalid Triangle.legalize() call'); } }; /** * Returns the index of a point in the triangle. * The point *must* be a reference to one of the triangle's vertices. * @private * @param {XY} p - point object with {x,y} * @returns {number} index 0, 1 or 2 * @throws {Error} if p can not be found */ Triangle.prototype.index = function(p) { var points = this.points_; // Here we are comparing point references, not values if (p === points[0]) { return 0; } else if (p === points[1]) { return 1; } else if (p === points[2]) { return 2; } else { throw new Error('poly2tri Invalid Triangle.index() call'); } }; /** * @private * @param {XY} p1 - point object with {x,y} * @param {XY} p2 - point object with {x,y} * @return {number} index 0, 1 or 2, or -1 if errror */ Triangle.prototype.edgeIndex = function(p1, p2) { var points = this.points_; // Here we are comparing point references, not values if (p1 === points[0]) { if (p2 === points[1]) { return 2; } else if (p2 === points[2]) { return 1; } } else if (p1 === points[1]) { if (p2 === points[2]) { return 0; } else if (p2 === points[0]) { return 2; } } else if (p1 === points[2]) { if (p2 === points[0]) { return 1; } else if (p2 === points[1]) { return 0; } } return -1; }; /** * Mark an edge of this triangle as constrained. * @private * @param {number} index - edge index */ Triangle.prototype.markConstrainedEdgeByIndex = function(index) { this.constrained_edge[index] = true; }; /** * Mark an edge of this triangle as constrained. * @private * @param {Edge} edge instance */ Triangle.prototype.markConstrainedEdgeByEdge = function(edge) { this.markConstrainedEdgeByPoints(edge.p, edge.q); }; /** * Mark an edge of this triangle as constrained. * This method takes two Point instances defining the edge of the triangle. * @private * @param {XY} p - point object with {x,y} * @param {XY} q - point object with {x,y} */ Triangle.prototype.markConstrainedEdgeByPoints = function(p, q) { var points = this.points_; // Here we are comparing point references, not values if ((q === points[0] && p === points[1]) || (q === points[1] && p === points[0])) { this.constrained_edge[2] = true; } else if ((q === points[0] && p === points[2]) || (q === points[2] && p === points[0])) { this.constrained_edge[1] = true; } else if ((q === points[1] && p === points[2]) || (q === points[2] && p === points[1])) { this.constrained_edge[0] = true; } }; // ---------------------------------------------------------Exports (public API) module.exports = Triangle;