200 lines
7.3 KiB
JavaScript
200 lines
7.3 KiB
JavaScript
/**
|
|
* Copyright 2000, Silicon Graphics, Inc. All Rights Reserved.
|
|
* Copyright 2012, Google Inc. All Rights Reserved.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to
|
|
* deal in the Software without restriction, including without limitation the
|
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
* sell copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice including the dates of first publication and
|
|
* either this permission notice or a reference to http://oss.sgi.com/projects/FreeB/
|
|
* shall be included in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
|
* IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*
|
|
* Original Code. The Original Code is: OpenGL Sample Implementation,
|
|
* Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
|
|
* Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
|
|
* Copyright in any portions created by third parties is as indicated
|
|
* elsewhere herein. All Rights Reserved.
|
|
*/
|
|
|
|
/**
|
|
* @author ericv@cs.stanford.edu (Eric Veach)
|
|
* @author bckenny@google.com (Brendan Kenny)
|
|
*/
|
|
|
|
// require libtess
|
|
// require libtess.geom
|
|
// require libtess.GluFace
|
|
// require libtess.GluMesh
|
|
/*global libtess */
|
|
|
|
goog.provide('libtess.tessmono');
|
|
goog.require('libtess');
|
|
goog.require('libtess.geom');
|
|
goog.require('libtess.mesh');
|
|
|
|
|
|
/**
|
|
* tessellateMonoRegion(face) tessellates a monotone region
|
|
* (what else would it do??). The region must consist of a single
|
|
* loop of half-edges (see mesh.js) oriented CCW. "Monotone" in this
|
|
* case means that any vertical line intersects the interior of the
|
|
* region in a single interval.
|
|
*
|
|
* Tessellation consists of adding interior edges (actually pairs of
|
|
* half-edges), to split the region into non-overlapping triangles.
|
|
* @private
|
|
* @param {libtess.GluFace} face [description].
|
|
*/
|
|
libtess.tessmono.tessellateMonoRegion_ = function(face) {
|
|
/* The basic idea is explained in Preparata and Shamos (which I don''t
|
|
* have handy right now), although their implementation is more
|
|
* complicated than this one. The are two edge chains, an upper chain
|
|
* and a lower chain. We process all vertices from both chains in order,
|
|
* from right to left.
|
|
*
|
|
* The algorithm ensures that the following invariant holds after each
|
|
* vertex is processed: the untessellated region consists of two
|
|
* chains, where one chain (say the upper) is a single edge, and
|
|
* the other chain is concave. The left vertex of the single edge
|
|
* is always to the left of all vertices in the concave chain.
|
|
*
|
|
* Each step consists of adding the rightmost unprocessed vertex to one
|
|
* of the two chains, and forming a fan of triangles from the rightmost
|
|
* of two chain endpoints. Determining whether we can add each triangle
|
|
* to the fan is a simple orientation test. By making the fan as large
|
|
* as possible, we restore the invariant (check it yourself).
|
|
*
|
|
* All edges are oriented CCW around the boundary of the region.
|
|
* First, find the half-edge whose origin vertex is rightmost.
|
|
* Since the sweep goes from left to right, face.anEdge should
|
|
* be close to the edge we want.
|
|
*/
|
|
|
|
var up = face.anEdge;
|
|
libtess.assert(up.lNext !== up && up.lNext.lNext !== up);
|
|
|
|
for (; libtess.geom.vertLeq(up.dst(), up.org); up = up.lPrev()) { }
|
|
for (; libtess.geom.vertLeq(up.org, up.dst()); up = up.lNext) { }
|
|
|
|
var lo = up.lPrev();
|
|
|
|
var tempHalfEdge;
|
|
while (up.lNext !== lo) {
|
|
if (libtess.geom.vertLeq(up.dst(), lo.org)) {
|
|
// up.dst() is on the left. It is safe to form triangles from lo.org.
|
|
// The edgeGoesLeft test guarantees progress even when some triangles
|
|
// are CW, given that the upper and lower chains are truly monotone.
|
|
while (lo.lNext !== up && (libtess.geom.edgeGoesLeft(lo.lNext) ||
|
|
libtess.geom.edgeSign(lo.org, lo.dst(), lo.lNext.dst()) <= 0)) {
|
|
|
|
tempHalfEdge = libtess.mesh.connect(lo.lNext, lo);
|
|
lo = tempHalfEdge.sym;
|
|
}
|
|
lo = lo.lPrev();
|
|
|
|
} else {
|
|
// lo.org is on the left. We can make CCW triangles from up.dst().
|
|
while (lo.lNext !== up && (libtess.geom.edgeGoesRight(up.lPrev()) ||
|
|
libtess.geom.edgeSign(up.dst(), up.org, up.lPrev().org) >= 0)) {
|
|
|
|
tempHalfEdge = libtess.mesh.connect(up, up.lPrev());
|
|
up = tempHalfEdge.sym;
|
|
}
|
|
up = up.lNext;
|
|
}
|
|
}
|
|
|
|
// Now lo.org == up.dst() == the leftmost vertex. The remaining region
|
|
// can be tessellated in a fan from this leftmost vertex.
|
|
libtess.assert(lo.lNext !== up);
|
|
while (lo.lNext.lNext !== up) {
|
|
tempHalfEdge = libtess.mesh.connect(lo.lNext, lo);
|
|
lo = tempHalfEdge.sym;
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* tessellateInterior(mesh) tessellates each region of
|
|
* the mesh which is marked "inside" the polygon. Each such region
|
|
* must be monotone.
|
|
*
|
|
* @param {libtess.GluMesh} mesh [description].
|
|
*/
|
|
libtess.tessmono.tessellateInterior = function(mesh) {
|
|
var next;
|
|
for (var f = mesh.fHead.next; f !== mesh.fHead; f = next) {
|
|
// Make sure we don''t try to tessellate the new triangles.
|
|
next = f.next;
|
|
if (f.inside) {
|
|
libtess.tessmono.tessellateMonoRegion_(f);
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* discardExterior(mesh) zaps (ie. sets to null) all faces
|
|
* which are not marked "inside" the polygon. Since further mesh operations
|
|
* on null faces are not allowed, the main purpose is to clean up the
|
|
* mesh so that exterior loops are not represented in the data structure.
|
|
*
|
|
* @param {libtess.GluMesh} mesh [description].
|
|
*/
|
|
libtess.tessmono.discardExterior = function(mesh) {
|
|
var next;
|
|
for (var f = mesh.fHead.next; f !== mesh.fHead; f = next) {
|
|
// Since f will be destroyed, save its next pointer.
|
|
next = f.next;
|
|
if (!f.inside) {
|
|
libtess.mesh.zapFace(f);
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* setWindingNumber(mesh, value, keepOnlyBoundary) resets the
|
|
* winding numbers on all edges so that regions marked "inside" the
|
|
* polygon have a winding number of "value", and regions outside
|
|
* have a winding number of 0.
|
|
*
|
|
* If keepOnlyBoundary is true, it also deletes all edges which do not
|
|
* separate an interior region from an exterior one.
|
|
*
|
|
* @param {libtess.GluMesh} mesh [description].
|
|
* @param {number} value Winding number to set (int).
|
|
* @param {boolean} keepOnlyBoundary Keep only boundary.
|
|
*/
|
|
libtess.tessmono.setWindingNumber = function(mesh, value, keepOnlyBoundary) {
|
|
var eNext;
|
|
for (var e = mesh.eHead.next; e !== mesh.eHead; e = eNext) {
|
|
eNext = e.next;
|
|
|
|
if (e.rFace().inside !== e.lFace.inside) {
|
|
// This is a boundary edge (one side is interior, one is exterior).
|
|
e.winding = (e.lFace.inside) ? value : -value;
|
|
|
|
} else {
|
|
// Both regions are interior, or both are exterior.
|
|
if (!keepOnlyBoundary) {
|
|
e.winding = 0;
|
|
|
|
} else {
|
|
libtess.mesh.deleteEdge(e);
|
|
}
|
|
}
|
|
}
|
|
};
|