Files
openlayers/src/libtess.js/tessmono.js
2013-10-01 15:14:12 +02:00

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);
}
}
}
};