Import libtess.js
This commit is contained in:
165
src/libtess.js/dict/Dict.js
Normal file
165
src/libtess.js/dict/Dict.js
Normal file
@@ -0,0 +1,165 @@
|
||||
/**
|
||||
* 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 Eric Veach, July 1994
|
||||
* @author Brendan Kenny
|
||||
*/
|
||||
|
||||
// require libtess
|
||||
/*global libtess */
|
||||
|
||||
// TODO(bckenny): better typing for DictKey? actually libtess.ActiveRegion
|
||||
/** @typedef {Object} */
|
||||
libtess.dictKey;
|
||||
|
||||
// TODO(bckenny): better typing for all of this, really. no need not to eg use tess as frame directly
|
||||
|
||||
/**
|
||||
* [Dict description]
|
||||
*
|
||||
* @constructor
|
||||
* @param {Object} frame [description]
|
||||
* @param {function(Object, Object, Object): boolean} leq [description]
|
||||
*/
|
||||
libtess.Dict = function(frame, leq) {
|
||||
/**
|
||||
* [head description]
|
||||
* @type {libtess.DictNode}
|
||||
*/
|
||||
this.head = new libtess.DictNode();
|
||||
this.head.next = this.head;
|
||||
this.head.prev = this.head;
|
||||
|
||||
// TODO(bckenny): better typing? see above
|
||||
/**
|
||||
* [frame description]
|
||||
* @type {Object}
|
||||
*/
|
||||
this.frame = frame;
|
||||
|
||||
/**
|
||||
* [leq_ description]
|
||||
* @private
|
||||
* @type {function(Object, libtess.dictKey, libtess.dictKey): boolean}
|
||||
*/
|
||||
this.leq_ = /** @type {function(Object, libtess.dictKey, libtess.dictKey): boolean} */(leq);
|
||||
};
|
||||
|
||||
/**
|
||||
* [deleteDict description]
|
||||
*/
|
||||
libtess.Dict.prototype.deleteDict = function() {
|
||||
// TODO(bckenny): unnecessary, I think.
|
||||
// for (var node = libtess.head.next; node !== libtess.head; node = node.next) {
|
||||
// memFree(node);
|
||||
// }
|
||||
// memFree(dict);
|
||||
|
||||
// NOTE(bckenny): nulled at callsite (sweep.doneEdgeDict_)
|
||||
};
|
||||
|
||||
/**
|
||||
* [insertBefore description]
|
||||
* @param {libtess.DictNode} node [description]
|
||||
* @param {Object} key [description]
|
||||
* @return {libtess.DictNode} [description]
|
||||
*/
|
||||
libtess.Dict.prototype.insertBefore = function(node, key) {
|
||||
do {
|
||||
node = node.prev;
|
||||
} while(node.key !== null && !this.leq_(this.frame, node.key, key));
|
||||
|
||||
var newNode = new libtess.DictNode();
|
||||
|
||||
newNode.key = key;
|
||||
newNode.next = node.next;
|
||||
node.next.prev = newNode;
|
||||
newNode.prev = node;
|
||||
node.next = newNode;
|
||||
|
||||
return newNode;
|
||||
};
|
||||
|
||||
/**
|
||||
* [insert description]
|
||||
* @param {Object} key [description]
|
||||
* @return {libtess.DictNode} [description]
|
||||
*/
|
||||
libtess.Dict.prototype.insert = function(key) {
|
||||
// NOTE(bckenny): from a macro in dict.h/dict-list.h
|
||||
return this.insertBefore(this.head, key);
|
||||
};
|
||||
|
||||
/**
|
||||
* [deleteNode description]
|
||||
* @param {libtess.DictNode} node [description]
|
||||
*/
|
||||
libtess.Dict.prototype.deleteNode = function(node) {
|
||||
// NOTE(bckenny): nulled at callsite (sweep.deleteRegion_)
|
||||
node.next.prev = node.prev;
|
||||
node.prev.next = node.next;
|
||||
// memFree( node ); TODO(bckenny)
|
||||
};
|
||||
|
||||
/**
|
||||
* Search returns the node with the smallest key greater than or equal
|
||||
* to the given key. If there is no such key, returns a node whose
|
||||
* key is null. Similarly, max(d).getSucc() has a null key, etc.
|
||||
*
|
||||
* @param {Object} key [description]
|
||||
* @return {libtess.DictNode} [description]
|
||||
*/
|
||||
libtess.Dict.prototype.search = function(key) {
|
||||
var node = this.head;
|
||||
|
||||
do {
|
||||
node = node.next;
|
||||
} while(node.key !== null && !this.leq_(this.frame, key, node.key));
|
||||
|
||||
return node;
|
||||
};
|
||||
|
||||
/**
|
||||
* [getMin description]
|
||||
* @return {libtess.DictNode} [description]
|
||||
*/
|
||||
libtess.Dict.prototype.getMin = function() {
|
||||
// NOTE(bckenny): from a macro in dict.h/dict-list.h
|
||||
return this.head.next;
|
||||
};
|
||||
|
||||
/**
|
||||
* [getMax description]
|
||||
* @return {libtess.DictNode} [description]
|
||||
*/
|
||||
libtess.Dict.prototype.getMax = function() {
|
||||
// NOTE(bckenny): from a macro in dict.h/dict-list.h
|
||||
return this.head.prev;
|
||||
};
|
||||
90
src/libtess.js/dict/DictNode.js
Normal file
90
src/libtess.js/dict/DictNode.js
Normal file
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* 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 Eric Veach, July 1994
|
||||
* @author Brendan Kenny
|
||||
*/
|
||||
|
||||
// require libtess
|
||||
// require libtess.Dict
|
||||
/*global libtess */
|
||||
|
||||
// TODO(bckenny): better typing for DictKey?
|
||||
|
||||
/**
|
||||
* [DictNode description]
|
||||
* @constructor
|
||||
*/
|
||||
libtess.DictNode = function() {
|
||||
// TODO(bckenny): could probably move all three properties to opt params
|
||||
/**
|
||||
* [key description]
|
||||
* @type {libtess.dictKey}
|
||||
*/
|
||||
this.key = null;
|
||||
|
||||
/**
|
||||
* [next description]
|
||||
* @type {libtess.DictNode}
|
||||
*/
|
||||
this.next = null;
|
||||
|
||||
/**
|
||||
* [prev description]
|
||||
* @type {libtess.DictNode}
|
||||
*/
|
||||
this.prev = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* [getKey description]
|
||||
* @return {libtess.dictKey} [description]
|
||||
*/
|
||||
libtess.DictNode.prototype.getKey = function() {
|
||||
return this.key;
|
||||
};
|
||||
|
||||
/**
|
||||
* [getSucc description]
|
||||
* @return {libtess.DictNode} [description]
|
||||
*/
|
||||
libtess.DictNode.prototype.getSucc = function() {
|
||||
// TODO(bckenny): unabreviated naming?
|
||||
return this.next;
|
||||
};
|
||||
|
||||
/**
|
||||
* [getPred description]
|
||||
* @return {libtess.DictNode} [description]
|
||||
*/
|
||||
libtess.DictNode.prototype.getPred = function() {
|
||||
// TODO(bckenny): unabreviated naming?
|
||||
return this.prev;
|
||||
};
|
||||
393
src/libtess.js/geom.js
Normal file
393
src/libtess.js/geom.js
Normal file
@@ -0,0 +1,393 @@
|
||||
/**
|
||||
* 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 Eric Veach, July 1994
|
||||
* @author Brendan Kenny
|
||||
*/
|
||||
|
||||
// TODO(bckenny): get rid of this:
|
||||
// NOTE(bckenny): using NO_BRANCH_CONDITIONS = false
|
||||
|
||||
// require libtess
|
||||
// require libtess.GluHalfEdge
|
||||
// require libtess.GluVertex
|
||||
/*global libtess */
|
||||
|
||||
|
||||
libtess.geom = function() {
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* [vertEq description]
|
||||
*
|
||||
* @param {libtess.GluVertex} u [description]
|
||||
* @param {libtess.GluVertex} v [description]
|
||||
* @return {boolean} [description]
|
||||
*/
|
||||
libtess.geom.vertEq = function(u, v) {
|
||||
return u.s === v.s && u.t === v.t;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if u is lexicographically <= v.
|
||||
*
|
||||
* @param {libtess.GluVertex} u [description]
|
||||
* @param {libtess.GluVertex} v [description]
|
||||
* @return {boolean}
|
||||
*/
|
||||
libtess.geom.vertLeq = function(u, v) {
|
||||
return (u.s < v.s) || (u.s === v.s && u.t <= v.t);
|
||||
};
|
||||
|
||||
/**
|
||||
* Given three vertices u,v,w such that geom.vertLeq(u,v) && geom.vertLeq(v,w),
|
||||
* evaluates the t-coord of the edge uw at the s-coord of the vertex v.
|
||||
* Returns v.t - (uw)(v.s), ie. the signed distance from uw to v.
|
||||
* If uw is vertical (and thus passes thru v), the result is zero.
|
||||
*
|
||||
* The calculation is extremely accurate and stable, even when v
|
||||
* is very close to u or w. In particular if we set v.t = 0 and
|
||||
* let r be the negated result (this evaluates (uw)(v.s)), then
|
||||
* r is guaranteed to satisfy MIN(u.t,w.t) <= r <= MAX(u.t,w.t).
|
||||
*
|
||||
* @param {libtess.GluVertex} u [description]
|
||||
* @param {libtess.GluVertex} v [description]
|
||||
* @param {libtess.GluVertex} w [description]
|
||||
* @return {number} double
|
||||
*/
|
||||
libtess.geom.edgeEval = function(u, v, w) {
|
||||
var gapL, gapR;
|
||||
|
||||
libtess.assert(libtess.geom.vertLeq(u, v) && libtess.geom.vertLeq(v, w));
|
||||
|
||||
gapL = v.s - u.s;
|
||||
gapR = w.s - v.s;
|
||||
|
||||
if (gapL + gapR > 0) {
|
||||
if (gapL < gapR) {
|
||||
return (v.t - u.t) + (u.t - w.t) * (gapL / (gapL + gapR));
|
||||
} else {
|
||||
return (v.t - w.t) + (w.t - u.t) * (gapR / (gapL + gapR));
|
||||
}
|
||||
}
|
||||
|
||||
// vertical line
|
||||
return 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a number whose sign matches geom.edgeEval(u,v,w) but which
|
||||
* is cheaper to evaluate. Returns > 0, == 0 , or < 0
|
||||
* as v is above, on, or below the edge uw.
|
||||
*
|
||||
* @param {libtess.GluVertex} u [description]
|
||||
* @param {libtess.GluVertex} v [description]
|
||||
* @param {libtess.GluVertex} w [description]
|
||||
* @return {number} double
|
||||
*/
|
||||
libtess.geom.edgeSign = function(u, v, w) {
|
||||
var gapL, gapR;
|
||||
|
||||
libtess.assert(libtess.geom.vertLeq(u, v) && libtess.geom.vertLeq(v, w));
|
||||
|
||||
gapL = v.s - u.s;
|
||||
gapR = w.s - v.s;
|
||||
|
||||
if (gapL + gapR > 0) {
|
||||
return (v.t - w.t) * gapL + (v.t - u.t) * gapR;
|
||||
}
|
||||
|
||||
// vertical line
|
||||
return 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Version of VertLeq with s and t transposed.
|
||||
* Returns true if u is lexicographically <= v.
|
||||
*
|
||||
* @param {libtess.GluVertex} u [description]
|
||||
* @param {libtess.GluVertex} v [description]
|
||||
* @return {boolean}
|
||||
*/
|
||||
libtess.geom.transLeq = function(u, v) {
|
||||
return (u.t < v.t) || (u.t === v.t && u.s <= v.s);
|
||||
};
|
||||
|
||||
/**
|
||||
* Version of geom.edgeEval with s and t transposed.
|
||||
* Given three vertices u,v,w such that geom.transLeq(u,v) && geom.transLeq(v,w),
|
||||
* evaluates the t-coord of the edge uw at the s-coord of the vertex v.
|
||||
* Returns v.s - (uw)(v.t), ie. the signed distance from uw to v.
|
||||
* If uw is vertical (and thus passes thru v), the result is zero.
|
||||
*
|
||||
* The calculation is extremely accurate and stable, even when v
|
||||
* is very close to u or w. In particular if we set v.s = 0 and
|
||||
* let r be the negated result (this evaluates (uw)(v.t)), then
|
||||
* r is guaranteed to satisfy MIN(u.s,w.s) <= r <= MAX(u.s,w.s).
|
||||
*
|
||||
* @param {libtess.GluVertex} u [description]
|
||||
* @param {libtess.GluVertex} v [description]
|
||||
* @param {libtess.GluVertex} w [description]
|
||||
* @return {number} double
|
||||
*/
|
||||
libtess.geom.transEval = function(u, v, w) {
|
||||
var gapL, gapR;
|
||||
|
||||
libtess.assert(libtess.geom.transLeq(u, v) && libtess.geom.transLeq(v, w));
|
||||
|
||||
gapL = v.t - u.t;
|
||||
gapR = w.t - v.t;
|
||||
|
||||
if (gapL + gapR > 0) {
|
||||
if (gapL < gapR) {
|
||||
return (v.s - u.s) + (u.s - w.s) * (gapL / (gapL + gapR));
|
||||
} else {
|
||||
return (v.s - w.s) + (w.s - u.s) * (gapR / (gapL + gapR));
|
||||
}
|
||||
}
|
||||
|
||||
// vertical line
|
||||
return 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Version of geom.edgeSign with s and t transposed.
|
||||
* Returns a number whose sign matches geom.transEval(u,v,w) but which
|
||||
* is cheaper to evaluate. Returns > 0, == 0 , or < 0
|
||||
* as v is above, on, or below the edge uw.
|
||||
*
|
||||
* @param {libtess.GluVertex} u [description]
|
||||
* @param {libtess.GluVertex} v [description]
|
||||
* @param {libtess.GluVertex} w [description]
|
||||
* @return {number} double
|
||||
*/
|
||||
libtess.geom.transSign = function(u, v, w) {
|
||||
var gapL, gapR;
|
||||
|
||||
libtess.assert(libtess.geom.transLeq(u, v) && libtess.geom.transLeq(v, w));
|
||||
|
||||
gapL = v.t - u.t;
|
||||
gapR = w.t - v.t;
|
||||
|
||||
if (gapL + gapR > 0) {
|
||||
return (v.s - w.s) * gapL + (v.s - u.s) * gapR;
|
||||
}
|
||||
|
||||
// vertical line
|
||||
return 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* [edgeGoesLeft description]
|
||||
*
|
||||
* @param {libtess.GluHalfEdge} e [description]
|
||||
* @return {boolean} [description]
|
||||
*/
|
||||
libtess.geom.edgeGoesLeft = function(e) {
|
||||
return libtess.geom.vertLeq(e.dst(), e.org);
|
||||
};
|
||||
|
||||
/**
|
||||
* [edgeGoesRight description]
|
||||
*
|
||||
* @param {libtess.GluHalfEdge} e [description]
|
||||
* @return {boolean} [description]
|
||||
*/
|
||||
libtess.geom.edgeGoesRight = function(e) {
|
||||
return libtess.geom.vertLeq(e.org, e.dst());
|
||||
};
|
||||
|
||||
/**
|
||||
* [vertL1dist description]
|
||||
*
|
||||
* @param {libtess.GluVertex} u [description]
|
||||
* @param {libtess.GluVertex} v [description]
|
||||
* @return {number} [description]
|
||||
*/
|
||||
libtess.geom.vertL1dist = function(u, v) {
|
||||
return Math.abs(u.s - v.s) + Math.abs(u.t - v.t);
|
||||
};
|
||||
|
||||
/**
|
||||
* For almost-degenerate situations, the results are not reliable.
|
||||
* Unless the floating-point arithmetic can be performed without
|
||||
* rounding errors, *any* implementation will give incorrect results
|
||||
* on some degenerate inputs, so the client must have some way to
|
||||
* handle this situation.
|
||||
*
|
||||
* @param {libtess.GluVertex} u [description]
|
||||
* @param {libtess.GluVertex} v [description]
|
||||
* @param {libtess.GluVertex} w [description]
|
||||
* @return {boolean}
|
||||
*/
|
||||
libtess.geom.vertCCW = function(u, v, w) {
|
||||
return (u.s*(v.t - w.t) + v.s*(w.t - u.t) + w.s*(u.t - v.t)) >= 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Given parameters a,x,b,y returns the value (b*x+a*y)/(a+b),
|
||||
* or (x+y)/2 if a==b==0. It requires that a,b >= 0, and enforces
|
||||
* this in the rare case that one argument is slightly negative.
|
||||
* The implementation is extremely stable numerically.
|
||||
* In particular it guarantees that the result r satisfies
|
||||
* MIN(x,y) <= r <= MAX(x,y), and the results are very accurate
|
||||
* even when a and b differ greatly in magnitude.
|
||||
*
|
||||
* @private
|
||||
* @param {number} a [description]
|
||||
* @param {number} x [description]
|
||||
* @param {number} b [description]
|
||||
* @param {number} y [description]
|
||||
* @return {number} [description]
|
||||
*/
|
||||
libtess.geom.interpolate_ = function(a, x, b, y) {
|
||||
//(a = (a < 0) ? 0 : a, b = (b < 0) ? 0 : b, ((a <= b) ? ((b == 0) ? ((x+y) / 2) : (x + (y-x) * (a/(a+b)))) : (y + (x-y) * (b/(a+b)))))
|
||||
a = (a < 0) ? 0 : a;
|
||||
b = (b < 0) ? 0 : b;
|
||||
|
||||
if (a <= b) {
|
||||
if (b === 0) {
|
||||
return (x+y) / 2;
|
||||
} else {
|
||||
return x + (y-x) * (a/(a+b));
|
||||
}
|
||||
} else {
|
||||
return y + (x-y) * (b/(a+b));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Given edges (o1,d1) and (o2,d2), compute their point of intersection.
|
||||
* The computed point is guaranteed to lie in the intersection of the
|
||||
* bounding rectangles defined by each edge.
|
||||
*
|
||||
* @param {libtess.GluVertex} o1 [description]
|
||||
* @param {libtess.GluVertex} d1 [description]
|
||||
* @param {libtess.GluVertex} o2 [description]
|
||||
* @param {libtess.GluVertex} d2 [description]
|
||||
* @param {libtess.GluVertex} v output
|
||||
*/
|
||||
libtess.geom.edgeIntersect = function(o1, d1, o2, d2, v) {
|
||||
/* This is certainly not the most efficient way to find the intersection
|
||||
* of two line segments, but it is very numerically stable.
|
||||
*
|
||||
* Strategy: find the two middle vertices in the VertLeq ordering,
|
||||
* and interpolate the intersection s-value from these. Then repeat
|
||||
* using the TransLeq ordering to find the intersection t-value.
|
||||
*/
|
||||
var z1, z2;
|
||||
var tmp;
|
||||
|
||||
if (!libtess.geom.vertLeq(o1, d1)) {
|
||||
// Swap(o1, d1);
|
||||
tmp = o1;
|
||||
o1 = d1;
|
||||
d1 = tmp;
|
||||
}
|
||||
if (!libtess.geom.vertLeq(o2, d2)) {
|
||||
// Swap(o2, d2);
|
||||
tmp = o2;
|
||||
o2 = d2;
|
||||
d2 = tmp;
|
||||
}
|
||||
if (!libtess.geom.vertLeq(o1, o2)) {
|
||||
// Swap(o1, o2);
|
||||
tmp = o1;
|
||||
o1 = o2;
|
||||
o2 = tmp;
|
||||
// Swap(d1, d2);
|
||||
tmp = d1;
|
||||
d1 = d2;
|
||||
d2 = tmp;
|
||||
}
|
||||
|
||||
if (!libtess.geom.vertLeq(o2, d1)) {
|
||||
// Technically, no intersection -- do our best
|
||||
v.s = (o2.s + d1.s) / 2;
|
||||
|
||||
} else if (libtess.geom.vertLeq(d1, d2)) {
|
||||
// Interpolate between o2 and d1
|
||||
z1 = libtess.geom.edgeEval(o1, o2, d1);
|
||||
z2 = libtess.geom.edgeEval(o2, d1, d2);
|
||||
if (z1+z2 < 0) { z1 = -z1; z2 = -z2; }
|
||||
v.s = libtess.geom.interpolate_(z1, o2.s, z2, d1.s);
|
||||
|
||||
} else {
|
||||
// Interpolate between o2 and d2
|
||||
z1 = libtess.geom.edgeSign(o1, o2, d1);
|
||||
z2 = -libtess.geom.edgeSign(o1, d2, d1);
|
||||
if (z1+z2 < 0) { z1 = -z1; z2 = -z2; }
|
||||
v.s = libtess.geom.interpolate_(z1, o2.s, z2, d2.s);
|
||||
}
|
||||
|
||||
// Now repeat the process for t
|
||||
if (!libtess.geom.transLeq(o1, d1)) {
|
||||
// Swap(o1, d1);
|
||||
tmp = o1;
|
||||
o1 = d1;
|
||||
d1 = tmp;
|
||||
}
|
||||
if (!libtess.geom.transLeq(o2, d2)) {
|
||||
// Swap(o2, d2);
|
||||
tmp = o2;
|
||||
o2 = d2;
|
||||
d2 = tmp;
|
||||
}
|
||||
if (!libtess.geom.transLeq(o1, o2)) {
|
||||
// Swap(o1, o2);
|
||||
tmp = o1;
|
||||
o1 = o2;
|
||||
o2 = tmp;
|
||||
// Swap(d1, d2);
|
||||
tmp = d1;
|
||||
d1 = d2;
|
||||
d2 = tmp;
|
||||
}
|
||||
|
||||
if (!libtess.geom.transLeq(o2, d1)) {
|
||||
// Technically, no intersection -- do our best
|
||||
v.t = (o2.t + d1.t) / 2;
|
||||
|
||||
} else if (libtess.geom.transLeq(d1, d2)) {
|
||||
// Interpolate between o2 and d1
|
||||
z1 = libtess.geom.transEval(o1, o2, d1);
|
||||
z2 = libtess.geom.transEval(o2, d1, d2);
|
||||
if (z1+z2 < 0) { z1 = -z1; z2 = -z2; }
|
||||
v.t = libtess.geom.interpolate_(z1, o2.t, z2, d1.t);
|
||||
|
||||
} else {
|
||||
// Interpolate between o2 and d2
|
||||
z1 = libtess.geom.transSign(o1, o2, d1);
|
||||
z2 = -libtess.geom.transSign(o1, d2, d1);
|
||||
if (z1+z2 < 0) { z1 = -z1; z2 = -z2; }
|
||||
v.t = libtess.geom.interpolate_(z1, o2.t, z2, d2.t);
|
||||
}
|
||||
};
|
||||
211
src/libtess.js/libtess.js
Normal file
211
src/libtess.js/libtess.js
Normal file
@@ -0,0 +1,211 @@
|
||||
/**
|
||||
* 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 Eric Veach, July 1994
|
||||
* @author Brendan Kenny
|
||||
*/
|
||||
|
||||
/**
|
||||
* Base namespace.
|
||||
*/
|
||||
var libtess = libtess || {};
|
||||
|
||||
/**
|
||||
* @define {boolean} [DEBUG description]
|
||||
*/
|
||||
libtess.DEBUG = false;
|
||||
|
||||
|
||||
/**
|
||||
* Checks if the condition evaluates to true if libtess.DEBUG is true.
|
||||
* @param {*} condition The condition to check.
|
||||
* @param {string=} opt_message Error message in case of failure.
|
||||
* @throws {Error} Assertion failed, the condition evaluates to false.
|
||||
*/
|
||||
libtess.assert = function(condition, opt_message) {
|
||||
if (libtess.DEBUG && !condition) {
|
||||
throw new Error('Assertion failed' + (opt_message ? ': ' + opt_message : ''));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* [sweepDebugEvent description]
|
||||
* @param {libtess.GluTesselator} tess
|
||||
*/
|
||||
libtess.sweepDebugEvent = function(tess) {
|
||||
// TODO(bckenny): closure debug flag/debugger support
|
||||
// sweep event updated
|
||||
};
|
||||
|
||||
/**
|
||||
* [GLU_TESS_MAX_COORD description]
|
||||
* @type {number}
|
||||
* @const
|
||||
*/
|
||||
libtess.GLU_TESS_MAX_COORD = 1e150;
|
||||
// NOTE(bckenny): from glu.pl generator
|
||||
|
||||
/**
|
||||
* [TRUE_PROJECT description]
|
||||
* TODO(bckenny): see alg-outline for description
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
libtess.TRUE_PROJECT = false;
|
||||
|
||||
/**
|
||||
* We cache vertex data for single-contour polygons so that we can
|
||||
* try a quick-and-dirty decomposition first.
|
||||
* @type {number}
|
||||
* @const
|
||||
*/
|
||||
libtess.TESS_MAX_CACHE = 100;
|
||||
|
||||
/**
|
||||
* [GLU_TESS_DEFAULT_TOLERANCE description]
|
||||
* @type {number}
|
||||
* @const
|
||||
*/
|
||||
libtess.GLU_TESS_DEFAULT_TOLERANCE = 0.0;
|
||||
|
||||
/**
|
||||
* The begin/end calls must be properly nested. We keep track of
|
||||
* the current state to enforce the ordering.
|
||||
*
|
||||
* @enum {number}
|
||||
*/
|
||||
libtess.tessState = {
|
||||
// TODO(bckenny): only used in GluTesselator, probably move there
|
||||
T_DORMANT: 0,
|
||||
T_IN_POLYGON: 1,
|
||||
T_IN_CONTOUR: 2
|
||||
};
|
||||
|
||||
/**
|
||||
* The input contours parition the plane into regions. A winding
|
||||
* rule determines which of these regions are inside the polygon.
|
||||
*
|
||||
* For a single contour C, the winding number of a point x is simply
|
||||
* the signed number of revolutions we make around x as we travel
|
||||
* once around C (where CCW is positive). When there are several
|
||||
* contours, the individual winding numbers are summed. This
|
||||
* procedure associates a signed integer value with each point x in
|
||||
* the plane. Note that the winding number is the same for all
|
||||
* points in a single region.
|
||||
*
|
||||
* The winding rule classifies a region as "inside" if its winding
|
||||
* number belongs to the chosen category (odd, nonzero, positive,
|
||||
* negative, or absolute value of at least two). The current GLU
|
||||
* tesselator implements the "odd" rule. The "nonzero" rule is another
|
||||
* common way to define the interior. The other three rules are
|
||||
* useful for polygon CSG operations.
|
||||
*
|
||||
* @enum {number}
|
||||
*/
|
||||
libtess.windingRule = {
|
||||
// NOTE(bckenny): values from enumglu.spec
|
||||
// TODO(bckenny): need to export when compiled
|
||||
GLU_TESS_WINDING_ODD: 100130,
|
||||
GLU_TESS_WINDING_NONZERO: 100131,
|
||||
GLU_TESS_WINDING_POSITIVE: 100132,
|
||||
GLU_TESS_WINDING_NEGATIVE: 100133,
|
||||
GLU_TESS_WINDING_ABS_GEQ_TWO: 100134
|
||||
};
|
||||
|
||||
/**
|
||||
* The type of primitive return from a "begin" callback. GL_LINE_LOOP is only
|
||||
* returned when GLU_TESS_BOUNDARY_ONLY is true. Values of enum match WebGL
|
||||
* constants.
|
||||
*
|
||||
* @enum {number}
|
||||
*/
|
||||
libtess.primitiveType = {
|
||||
// TODO(bckenny): doc types
|
||||
// TODO(bckenny): need to export when compiled, but can just use webgl constants when available
|
||||
GL_LINE_LOOP: 2,
|
||||
GL_TRIANGLES: 4,
|
||||
GL_TRIANGLE_STRIP: 5,
|
||||
GL_TRIANGLE_FAN: 6
|
||||
};
|
||||
|
||||
/**
|
||||
* The types of errors provided to error callback.
|
||||
* @enum {number}
|
||||
*/
|
||||
libtess.errorType = {
|
||||
// TODO(bckenny) doc types
|
||||
// NOTE(bckenny): values from enumglu.spec
|
||||
GLU_TESS_MISSING_BEGIN_POLYGON: 100151,
|
||||
GLU_TESS_MISSING_END_POLYGON: 100153,
|
||||
GLU_TESS_MISSING_BEGIN_CONTOUR: 100152,
|
||||
GLU_TESS_MISSING_END_CONTOUR: 100154,
|
||||
GLU_TESS_COORD_TOO_LARGE: 100155,
|
||||
GLU_TESS_NEED_COMBINE_CALLBACK: 100156
|
||||
};
|
||||
|
||||
/**
|
||||
* GLU enums necessary for this project.
|
||||
* see enumglu.spec
|
||||
* TODO(bckenny): better source for these?
|
||||
*
|
||||
* @enum {number}
|
||||
*/
|
||||
libtess.gluEnum = {
|
||||
// NOTE(bckenny): values from enumglu.spec
|
||||
// TODO(bckenny): most enums under here? drop GLU? or rename in other ways
|
||||
GLU_TESS_MESH: 100112, // from tess.c
|
||||
GLU_TESS_TOLERANCE: 100142,
|
||||
GLU_TESS_WINDING_RULE: 100140,
|
||||
GLU_TESS_BOUNDARY_ONLY: 100141,
|
||||
|
||||
// TODO(bckenny): should this live in errorType?
|
||||
GLU_INVALID_ENUM: 100900,
|
||||
GLU_INVALID_VALUE: 100901,
|
||||
|
||||
GLU_TESS_BEGIN: 100100,
|
||||
GLU_TESS_VERTEX: 100101,
|
||||
GLU_TESS_END: 100102,
|
||||
GLU_TESS_ERROR: 100103,
|
||||
GLU_TESS_EDGE_FLAG: 100104,
|
||||
GLU_TESS_COMBINE: 100105,
|
||||
GLU_TESS_BEGIN_DATA: 100106,
|
||||
GLU_TESS_VERTEX_DATA: 100107,
|
||||
GLU_TESS_END_DATA: 100108,
|
||||
GLU_TESS_ERROR_DATA: 100109,
|
||||
GLU_TESS_EDGE_FLAG_DATA: 100110,
|
||||
GLU_TESS_COMBINE_DATA: 100111
|
||||
};
|
||||
|
||||
/** @typedef {number} */
|
||||
libtess.PQHandle;
|
||||
|
||||
// TODO(bckenny): better typing on key?
|
||||
/** @typedef {Object} */
|
||||
libtess.PQKey;
|
||||
56
src/libtess.js/libtess/CachedVertex.js
Normal file
56
src/libtess.js/libtess/CachedVertex.js
Normal file
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* 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 Eric Veach, July 1994
|
||||
* @author Brendan Kenny
|
||||
*/
|
||||
|
||||
// require libtess
|
||||
/*global libtess */
|
||||
|
||||
/**
|
||||
* Cached vertex data for single-countour polygons for quick-and-dirty
|
||||
* decomposition.
|
||||
* @constructor
|
||||
*/
|
||||
libtess.CachedVertex = function() {
|
||||
/**
|
||||
* [coords description]
|
||||
* @type {Array.<number>}
|
||||
*/
|
||||
this.coords = [0, 0, 0];
|
||||
// TODO(bckenny): better way to init?
|
||||
|
||||
/**
|
||||
* [data description]
|
||||
* @type {Object}
|
||||
*/
|
||||
this.data = null;
|
||||
};
|
||||
875
src/libtess.js/libtess/GluTesselator.js
Normal file
875
src/libtess.js/libtess/GluTesselator.js
Normal file
@@ -0,0 +1,875 @@
|
||||
/**
|
||||
* 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 Eric Veach, July 1994
|
||||
* @author Brendan Kenny
|
||||
*/
|
||||
|
||||
// require libtess
|
||||
// require libtess.mesh
|
||||
// require libtess.tessmono
|
||||
// require libtess.render
|
||||
// require libtess.normal
|
||||
// require libtess.sweep
|
||||
/*global libtess */
|
||||
|
||||
// TODO(bckenny): options for just triangles, just tristrips, single tristrip w/ resets
|
||||
// other primitives with index buffer? would have to add a better tristrip extractor
|
||||
// monotone poly -> tristrip seems possible...
|
||||
|
||||
// TODO(bckenny): create more javascript-y API, e.g. make gluTessEndPolygon async,
|
||||
// don't require so many temp objects created
|
||||
|
||||
/**
|
||||
* [GluTesselator description]
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
libtess.GluTesselator = function() {
|
||||
// Only initialize fields which can be changed by the api. Other fields
|
||||
// are initialized where they are used.
|
||||
|
||||
// TODO(bckenny): many of these can be made private
|
||||
// TODO(bckenny): can we combine call* and call*Data functions?
|
||||
|
||||
/*** state needed for collecting the input data ***/
|
||||
|
||||
/**
|
||||
* what begin/end calls have we seen?
|
||||
* @type {libtess.tessState}
|
||||
*/
|
||||
this.state = libtess.tessState.T_DORMANT;
|
||||
|
||||
/**
|
||||
* lastEdge_.org is the most recent vertex
|
||||
* @private
|
||||
* @type {libtess.GluHalfEdge}
|
||||
*/
|
||||
this.lastEdge_ = null;
|
||||
|
||||
/**
|
||||
* stores the input contours, and eventually the tessellation itself
|
||||
* @type {libtess.GluMesh}
|
||||
*/
|
||||
this.mesh = null;
|
||||
// NOTE(bckenny): initialized in this.emptyCache_
|
||||
|
||||
/**
|
||||
* Error callback.
|
||||
* @private
|
||||
* @type {?function((libtess.errorType|libtess.gluEnum))}
|
||||
*/
|
||||
this.callError_ = null;
|
||||
|
||||
|
||||
/*** state needed for projecting onto the sweep plane ***/
|
||||
|
||||
/**
|
||||
* user-specified normal (if provided)
|
||||
* @type {Array.<number>}
|
||||
*/
|
||||
this.normal = [0, 0, 0];
|
||||
// TODO(bckenny): better way to init these arrays?
|
||||
|
||||
/**
|
||||
* unit vector in s-direction (debugging)
|
||||
* @type {Array.<number>}
|
||||
*/
|
||||
this.sUnit = [0, 0, 0];
|
||||
|
||||
/**
|
||||
* unit vector in t-direction (debugging)
|
||||
* @type {Array.<number>}
|
||||
*/
|
||||
this.tUnit = [0, 0, 0];
|
||||
|
||||
/*** state needed for the line sweep ***/
|
||||
// TODO(bckenny): this could be moved to a sweep state object of some sort
|
||||
|
||||
/**
|
||||
* tolerance for merging features
|
||||
* @type {number}
|
||||
*/
|
||||
this.relTolerance = libtess.GLU_TESS_DEFAULT_TOLERANCE;
|
||||
|
||||
/**
|
||||
* rule for determining polygon interior
|
||||
* @type {libtess.windingRule}
|
||||
*/
|
||||
this.windingRule = libtess.windingRule.GLU_TESS_WINDING_ODD;
|
||||
|
||||
/**
|
||||
* fatal error: needed combine callback
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.fatalError = false;
|
||||
|
||||
/**
|
||||
* edge dictionary for sweep line
|
||||
* @type {libtess.Dict}
|
||||
*/
|
||||
this.dict = null;
|
||||
// NOTE(bckenny): dict initialized in sweep.initEdgeDict_, removed in sweep.doneEdgeDict_
|
||||
|
||||
/**
|
||||
* priority queue of vertex events
|
||||
* @type {libtess.PriorityQ}
|
||||
*/
|
||||
this.pq = null;
|
||||
// NOTE(bckenny): pq initialized in sweep.initPriorityQ
|
||||
|
||||
/**
|
||||
* current sweep event being processed
|
||||
* @type {libtess.GluVertex}
|
||||
*/
|
||||
this.event = null;
|
||||
|
||||
/**
|
||||
* Combine callback.
|
||||
* @private
|
||||
* @type {?function(Array.<number>, Array.<Object>, Array.<number>): Object}
|
||||
*/
|
||||
this.callCombine_ = null;
|
||||
|
||||
/*** state needed for rendering callbacks (see render.js) ***/
|
||||
|
||||
/**
|
||||
* mark boundary edges (use EdgeFlag)
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.flagBoundary = false;
|
||||
|
||||
/**
|
||||
* Extract contours, not triangles
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.boundaryOnly = false;
|
||||
|
||||
/**
|
||||
* list of triangles which could not be rendered as strips or fans
|
||||
* @type {libtess.GluFace}
|
||||
*/
|
||||
this.lonelyTriList = null;
|
||||
|
||||
/**
|
||||
* Begin callback.
|
||||
* @private
|
||||
* @type {?function(libtess.primitiveType)}
|
||||
*/
|
||||
this.callBegin_ = null;
|
||||
|
||||
/**
|
||||
* Edge flag callback.
|
||||
* @private
|
||||
* @type {?function(boolean)}
|
||||
*/
|
||||
this.callEdgeFlag_ = null;
|
||||
|
||||
/**
|
||||
* Vertex callback.
|
||||
* @private
|
||||
* @type {?function(Object)}
|
||||
*/
|
||||
this.callVertex_ = null;
|
||||
|
||||
/**
|
||||
* End callback.
|
||||
* @private
|
||||
* @type {?function()}
|
||||
*/
|
||||
this.callEnd_ = null;
|
||||
|
||||
/**
|
||||
* Mesh callback.
|
||||
* @private
|
||||
* @type {?function(libtess.GluMesh)}
|
||||
*/
|
||||
this.callMesh_ = null;
|
||||
|
||||
/*** rendering callbacks that also pass polygon data ***/
|
||||
/**
|
||||
* BeginData callback.
|
||||
* @private
|
||||
* @type {?function(libtess.primitiveType, Object)}
|
||||
*/
|
||||
this.callBeginData_ = null;
|
||||
|
||||
/**
|
||||
* EdgeFlagData callback.
|
||||
* @private
|
||||
* @type {?function(boolean, Object)}
|
||||
*/
|
||||
this.callEdgeFlagData_ = null;
|
||||
|
||||
/**
|
||||
* VertexData callback.
|
||||
* @private
|
||||
* @type {?function(Object, Object)}
|
||||
*/
|
||||
this.callVertexData_ = null;
|
||||
|
||||
/**
|
||||
* EndData callback.
|
||||
* @private
|
||||
* @type {?function(Object)}
|
||||
*/
|
||||
this.callEndData_ = null;
|
||||
|
||||
/**
|
||||
* ErrorData callback.
|
||||
* @private
|
||||
* @type {?function((libtess.errorType|libtess.gluEnum), Object)}
|
||||
*/
|
||||
this.callErrorData_ = null;
|
||||
|
||||
/**
|
||||
* CombineData callback.
|
||||
* @private
|
||||
* @type {?function(Array.<number>, Array.<Object>, Array.<number>, Object): Object}
|
||||
*/
|
||||
this.callCombineData_ = null;
|
||||
|
||||
/**
|
||||
* client data for current polygon
|
||||
* @private
|
||||
* @type {Object}
|
||||
*/
|
||||
this.polygonData_ = null;
|
||||
|
||||
/*** state needed to cache single-contour polygons for renderCache() ***/
|
||||
/**
|
||||
* empty cache on next vertex() call
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.emptyCache = false;
|
||||
// TODO(bckenny): possibly rename to be clear it's a boolean
|
||||
|
||||
/**
|
||||
* number of cached vertices
|
||||
* @type {number}
|
||||
*/
|
||||
this.cacheCount = 0;
|
||||
|
||||
/**
|
||||
* the vertex data
|
||||
* @type {Array.<libtess.CachedVertex>}
|
||||
*/
|
||||
this.cache = new Array(libtess.TESS_MAX_CACHE);
|
||||
|
||||
// TODO(bckenny): fill now? or init on demand
|
||||
for (var i = 0; i < libtess.TESS_MAX_CACHE; i++) {
|
||||
this.cache[i] = new libtess.CachedVertex();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Destory the tesselator object. See README.
|
||||
*/
|
||||
libtess.GluTesselator.prototype.gluDeleteTess = function() {
|
||||
// TODO(bckenny): do we need a public API for this still?
|
||||
this.requireState_(libtess.tessState.T_DORMANT);
|
||||
// memFree(tess); TODO(bckenny)
|
||||
};
|
||||
|
||||
/**
|
||||
* Set properties for control over tesselation. See README.
|
||||
* @param {libtess.gluEnum} which [description]
|
||||
* @param {number|boolean} value [description]
|
||||
*/
|
||||
libtess.GluTesselator.prototype.gluTessProperty = function(which, value) {
|
||||
// TODO(bckenny): split into more setters?
|
||||
// TODO(bckenny): in any case, we can do better than this switch statement
|
||||
|
||||
switch (which) {
|
||||
case libtess.gluEnum.GLU_TESS_TOLERANCE:
|
||||
if (value < 0 || value > 1) {
|
||||
break;
|
||||
}
|
||||
this.relTolerance = /** @type {number} */(value);
|
||||
return;
|
||||
|
||||
case libtess.gluEnum.GLU_TESS_WINDING_RULE:
|
||||
var windingRule = /** @type {libtess.windingRule} */(value);
|
||||
|
||||
switch (windingRule) {
|
||||
case libtess.windingRule.GLU_TESS_WINDING_ODD:
|
||||
case libtess.windingRule.GLU_TESS_WINDING_NONZERO:
|
||||
case libtess.windingRule.GLU_TESS_WINDING_POSITIVE:
|
||||
case libtess.windingRule.GLU_TESS_WINDING_NEGATIVE:
|
||||
case libtess.windingRule.GLU_TESS_WINDING_ABS_GEQ_TWO:
|
||||
this.windingRule = windingRule;
|
||||
return;
|
||||
default:
|
||||
}
|
||||
break;
|
||||
|
||||
case libtess.gluEnum.GLU_TESS_BOUNDARY_ONLY:
|
||||
// TODO(bckenny): added boolean param type. make sure ok.
|
||||
this.boundaryOnly = !!value;
|
||||
return;
|
||||
|
||||
default:
|
||||
this.callErrorOrErrorData(libtess.gluEnum.GLU_INVALID_ENUM);
|
||||
return;
|
||||
}
|
||||
this.callErrorOrErrorData(libtess.gluEnum.GLU_INVALID_VALUE);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns tessellator property
|
||||
* @param {libtess.gluEnum} which [description]
|
||||
* @return {number|boolean} [description]
|
||||
*/
|
||||
libtess.GluTesselator.prototype.gluGetTessProperty = function(which) {
|
||||
// TODO(bckenny): as above, split into more getters? and improve on switch statement
|
||||
// why are these being asserted in getter but not setter?
|
||||
|
||||
switch (which) {
|
||||
case libtess.gluEnum.GLU_TESS_TOLERANCE:
|
||||
// tolerance should be in range [0..1]
|
||||
libtess.assert(0 <= this.relTolerance && this.relTolerance <= 1);
|
||||
return this.relTolerance;
|
||||
|
||||
case libtess.gluEnum.GLU_TESS_WINDING_RULE:
|
||||
var rule = this.windingRule;
|
||||
libtess.assert(rule === libtess.windingRule.GLU_TESS_WINDING_ODD ||
|
||||
rule === libtess.windingRule.GLU_TESS_WINDING_NONZERO ||
|
||||
rule === libtess.windingRule.GLU_TESS_WINDING_POSITIVE ||
|
||||
rule === libtess.windingRule.GLU_TESS_WINDING_NEGATIVE ||
|
||||
rule === libtess.windingRule.GLU_TESS_WINDING_ABS_GEQ_TWO);
|
||||
return rule;
|
||||
|
||||
case libtess.gluEnum.GLU_TESS_BOUNDARY_ONLY:
|
||||
libtess.assert(this.boundaryOnly === true || this.boundaryOnly === false);
|
||||
return this.boundaryOnly;
|
||||
|
||||
default:
|
||||
this.callErrorOrErrorData(libtess.gluEnum.GLU_INVALID_ENUM);
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Lets the user supply the polygon normal, if known. All input data
|
||||
* is projected into a plane perpendicular to the normal before
|
||||
* tesselation. All output triangles are oriented CCW with
|
||||
* respect to the normal (CW orientation can be obtained by
|
||||
* reversing the sign of the supplied normal). For example, if
|
||||
* you know that all polygons lie in the x-y plane, call
|
||||
* "tess.gluTessNormal(0.0, 0.0, 1.0)" before rendering any polygons.
|
||||
*
|
||||
* @param {number} x [description]
|
||||
* @param {number} y [description]
|
||||
* @param {number} z [description]
|
||||
*/
|
||||
libtess.GluTesselator.prototype.gluTessNormal = function(x, y, z) {
|
||||
this.normal[0] = x;
|
||||
this.normal[1] = y;
|
||||
this.normal[2] = z;
|
||||
};
|
||||
|
||||
/**
|
||||
* Specify callbacks. See README. A null or undefined opt_fn removes current callback.
|
||||
*
|
||||
* @param {libtess.gluEnum} which [description]
|
||||
* @param {?function()=} opt_fn [description]
|
||||
*/
|
||||
libtess.GluTesselator.prototype.gluTessCallback = function(which, opt_fn) {
|
||||
var fn = !opt_fn ? null : opt_fn;
|
||||
// TODO(bckenny): better opt_fn typing?
|
||||
|
||||
switch(which) {
|
||||
case libtess.gluEnum.GLU_TESS_BEGIN:
|
||||
this.callBegin_ = /** @type {function(libtess.primitiveType)} */ (fn);
|
||||
return;
|
||||
|
||||
case libtess.gluEnum.GLU_TESS_BEGIN_DATA:
|
||||
this.callBeginData_ =
|
||||
/** @type {function(libtess.primitiveType, Object)} */ (fn);
|
||||
return;
|
||||
|
||||
case libtess.gluEnum.GLU_TESS_EDGE_FLAG:
|
||||
this.callEdgeFlag_ = /** @type {function(boolean)} */ (fn);
|
||||
// If the client wants boundary edges to be flagged,
|
||||
// we render everything as separate triangles (no strips or fans).
|
||||
this.flagBoundary = (!!fn);
|
||||
return;
|
||||
|
||||
case libtess.gluEnum.GLU_TESS_EDGE_FLAG_DATA:
|
||||
this.callEdgeFlagData_ = /** @type {function(boolean, Object)} */ (fn);
|
||||
// If the client wants boundary edges to be flagged,
|
||||
// we render everything as separate triangles (no strips or fans).
|
||||
this.flagBoundary = (!!fn);
|
||||
return;
|
||||
|
||||
case libtess.gluEnum.GLU_TESS_VERTEX:
|
||||
this.callVertex_ = /** @type {function(Object)} */ (fn);
|
||||
return;
|
||||
|
||||
case libtess.gluEnum.GLU_TESS_VERTEX_DATA:
|
||||
this.callVertexData_ = /** @type {function(Object, Object)} */ (fn);
|
||||
return;
|
||||
|
||||
case libtess.gluEnum.GLU_TESS_END:
|
||||
this.callEnd_ = /** @type {function()} */ (fn);
|
||||
return;
|
||||
|
||||
case libtess.gluEnum.GLU_TESS_END_DATA:
|
||||
this.callEndData_ = /** @type {function(Object)} */ (fn);
|
||||
return;
|
||||
|
||||
case libtess.gluEnum.GLU_TESS_ERROR:
|
||||
this.callError_ = /** @type {function((libtess.errorType|libtess.gluEnum))} */ (fn);
|
||||
return;
|
||||
|
||||
case libtess.gluEnum.GLU_TESS_ERROR_DATA:
|
||||
this.callErrorData_ =
|
||||
/** @type {function((libtess.errorType|libtess.gluEnum), Object)} */ (fn);
|
||||
return;
|
||||
|
||||
case libtess.gluEnum.GLU_TESS_COMBINE:
|
||||
this.callCombine_ = /** @type {function(Array.<number>, Array.<Object>, Array.<number>): Object} */ (fn);
|
||||
return;
|
||||
|
||||
case libtess.gluEnum.GLU_TESS_COMBINE_DATA:
|
||||
this.callCombineData_ = /** @type {function(Array.<number>, Array.<Object>, Array.<number>, Object): Object} */ (fn);
|
||||
return;
|
||||
|
||||
case libtess.gluEnum.GLU_TESS_MESH:
|
||||
this.callMesh_ = /** @type {function(libtess.GluMesh)} */ (fn);
|
||||
return;
|
||||
|
||||
default:
|
||||
this.callErrorOrErrorData(libtess.gluEnum.GLU_INVALID_ENUM);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Specify a vertex and associated data. Must be within calls to
|
||||
* beginContour/endContour. See README.
|
||||
*
|
||||
* @param {Array.<number>} coords [description]
|
||||
* @param {Object} data [description]
|
||||
*/
|
||||
libtess.GluTesselator.prototype.gluTessVertex = function(coords, data) {
|
||||
var tooLarge = false;
|
||||
|
||||
// TODO(bckenny): pool allocation?
|
||||
var clamped = [0, 0, 0];
|
||||
|
||||
this.requireState_(libtess.tessState.T_IN_CONTOUR);
|
||||
|
||||
if (this.emptyCache) {
|
||||
this.emptyCache_();
|
||||
this.lastEdge_ = null;
|
||||
}
|
||||
|
||||
for (var i = 0; i < 3; ++i) {
|
||||
var x = coords[i];
|
||||
if (x < -libtess.GLU_TESS_MAX_COORD) {
|
||||
x = -libtess.GLU_TESS_MAX_COORD;
|
||||
tooLarge = true;
|
||||
}
|
||||
if (x > libtess.GLU_TESS_MAX_COORD) {
|
||||
x = libtess.GLU_TESS_MAX_COORD;
|
||||
tooLarge = true;
|
||||
}
|
||||
clamped[i] = x;
|
||||
}
|
||||
|
||||
if (tooLarge) {
|
||||
this.callErrorOrErrorData(libtess.errorType.GLU_TESS_COORD_TOO_LARGE);
|
||||
}
|
||||
|
||||
if (this.mesh === null) {
|
||||
if (this.cacheCount < libtess.TESS_MAX_CACHE) {
|
||||
this.cacheVertex_(clamped, data);
|
||||
return;
|
||||
}
|
||||
|
||||
// cache is full, create mesh and add cached verts to it
|
||||
this.emptyCache_();
|
||||
}
|
||||
|
||||
this.addVertex_(clamped, data);
|
||||
};
|
||||
|
||||
/**
|
||||
* [gluTessBeginPolygon description]
|
||||
* @param {Object} data Client data for current polygon
|
||||
*/
|
||||
libtess.GluTesselator.prototype.gluTessBeginPolygon = function(data) {
|
||||
this.requireState_(libtess.tessState.T_DORMANT);
|
||||
|
||||
this.state = libtess.tessState.T_IN_POLYGON;
|
||||
this.cacheCount = 0;
|
||||
this.emptyCache = false;
|
||||
this.mesh = null;
|
||||
|
||||
this.polygonData_ = data;
|
||||
};
|
||||
|
||||
/**
|
||||
* [gluTessBeginContour description]
|
||||
*/
|
||||
libtess.GluTesselator.prototype.gluTessBeginContour = function() {
|
||||
this.requireState_(libtess.tessState.T_IN_POLYGON);
|
||||
|
||||
this.state = libtess.tessState.T_IN_CONTOUR;
|
||||
this.lastEdge_ = null;
|
||||
if (this.cacheCount > 0) {
|
||||
// Just set a flag so we don't get confused by empty contours
|
||||
// -- these can be generated accidentally with the obsolete
|
||||
// NextContour() interface.
|
||||
// TODO(bckenny): we aren't implementing NextContour() interface.
|
||||
this.emptyCache = true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* [gluTessEndContour description]
|
||||
*/
|
||||
libtess.GluTesselator.prototype.gluTessEndContour = function() {
|
||||
this.requireState_(libtess.tessState.T_IN_CONTOUR);
|
||||
this.state = libtess.tessState.T_IN_POLYGON;
|
||||
};
|
||||
|
||||
/**
|
||||
* [gluTessEndPolygon description]
|
||||
*/
|
||||
libtess.GluTesselator.prototype.gluTessEndPolygon = function() {
|
||||
this.requireState_(libtess.tessState.T_IN_POLYGON);
|
||||
this.state = libtess.tessState.T_DORMANT;
|
||||
|
||||
if (this.mesh === null) {
|
||||
if (!this.flagBoundary && !this.callMesh_) {
|
||||
// Try some special code to make the easy cases go quickly
|
||||
// (eg. convex polygons). This code does NOT handle multiple contours,
|
||||
// intersections, edge flags, and of course it does not generate
|
||||
// an explicit mesh either.
|
||||
if (libtess.render.renderCache(this)) {
|
||||
// TODO(bckenny): why only clear polygonData? does more need to be cleared?
|
||||
this.polygonData_ = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.emptyCache_();
|
||||
}
|
||||
|
||||
// Determine the polygon normal and project vertices onto the plane
|
||||
// of the polygon.
|
||||
libtess.normal.projectPolygon(this);
|
||||
|
||||
// computeInterior(tess) computes the planar arrangement specified
|
||||
// by the given contours, and further subdivides this arrangement
|
||||
// into regions. Each region is marked "inside" if it belongs
|
||||
// to the polygon, according to the rule given by this.windingRule.
|
||||
// Each interior region is guaranteed be monotone.
|
||||
libtess.sweep.computeInterior(this);
|
||||
|
||||
if (!this.fatalError) {
|
||||
// If the user wants only the boundary contours, we throw away all edges
|
||||
// except those which separate the interior from the exterior.
|
||||
// Otherwise we tessellate all the regions marked "inside".
|
||||
if (this.boundaryOnly) {
|
||||
libtess.tessmono.setWindingNumber(this.mesh, 1, true);
|
||||
|
||||
} else {
|
||||
libtess.tessmono.tessellateInterior(this.mesh);
|
||||
}
|
||||
|
||||
this.mesh.checkMesh();
|
||||
|
||||
if (this.callBegin_ || this.callEnd_ || this.callVertex_ ||
|
||||
this.callEdgeFlag_ || this.callBeginData_ || this.callEndData_ ||
|
||||
this.callVertexData_ || this.callEdgeFlagData_) {
|
||||
|
||||
if (this.boundaryOnly) {
|
||||
// output boundary contours
|
||||
libtess.render.renderBoundary(this, this.mesh);
|
||||
|
||||
} else {
|
||||
// output strips and fans
|
||||
libtess.render.renderMesh(this, this.mesh);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.callMesh_) {
|
||||
// Throw away the exterior faces, so that all faces are interior.
|
||||
// This way the user doesn't have to check the "inside" flag,
|
||||
// and we don't need to even reveal its existence. It also leaves
|
||||
// the freedom for an implementation to not generate the exterior
|
||||
// faces in the first place.
|
||||
libtess.tessmono.discardExterior(this.mesh);
|
||||
// user wants the mesh itself
|
||||
this.callMesh_(this.mesh);
|
||||
|
||||
this.mesh = null;
|
||||
this.polygonData_ = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
libtess.mesh.deleteMesh(this.mesh);
|
||||
this.polygonData_ = null;
|
||||
this.mesh = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the tessellator to its original dormant state.
|
||||
* @private
|
||||
*/
|
||||
libtess.GluTesselator.prototype.makeDormant_ = function() {
|
||||
if (this.mesh) {
|
||||
libtess.mesh.deleteMesh(this.mesh);
|
||||
}
|
||||
this.state = libtess.tessState.T_DORMANT;
|
||||
this.lastEdge_ = null;
|
||||
this.mesh = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* [requireState_ description]
|
||||
* @private
|
||||
* @param {libtess.tessState} state [description]
|
||||
*/
|
||||
libtess.GluTesselator.prototype.requireState_ = function(state) {
|
||||
if (this.state !== state) {
|
||||
this.gotoState_(state);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* [gotoState_ description]
|
||||
* @private
|
||||
* @param {libtess.tessState} newState [description]
|
||||
*/
|
||||
libtess.GluTesselator.prototype.gotoState_ = function(newState) {
|
||||
while (this.state !== newState) {
|
||||
// We change the current state one level at a time, to get to the desired
|
||||
// state.
|
||||
if (this.state < newState) {
|
||||
switch (this.state) {
|
||||
case libtess.tessState.T_DORMANT:
|
||||
this.callErrorOrErrorData(
|
||||
libtess.errorType.GLU_TESS_MISSING_BEGIN_POLYGON);
|
||||
this.gluTessBeginPolygon(null);
|
||||
break;
|
||||
|
||||
case libtess.tessState.T_IN_POLYGON:
|
||||
this.callErrorOrErrorData(
|
||||
libtess.errorType.GLU_TESS_MISSING_BEGIN_CONTOUR);
|
||||
this.gluTessBeginContour();
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
switch (this.state) {
|
||||
case libtess.tessState.T_IN_CONTOUR:
|
||||
this.callErrorOrErrorData(
|
||||
libtess.errorType.GLU_TESS_MISSING_END_CONTOUR);
|
||||
this.gluTessEndContour();
|
||||
break;
|
||||
|
||||
case libtess.tessState.T_IN_POLYGON:
|
||||
this.callErrorOrErrorData(
|
||||
libtess.errorType.GLU_TESS_MISSING_END_POLYGON);
|
||||
// this.gluTessEndPolygon() is too much work!
|
||||
this.makeDormant_();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* [addVertex_ description]
|
||||
* @private
|
||||
* @param {Array.<number>} coords [description]
|
||||
* @param {Object} data [description]
|
||||
*/
|
||||
libtess.GluTesselator.prototype.addVertex_ = function(coords, data) {
|
||||
var e = this.lastEdge_;
|
||||
if (e === null) {
|
||||
// Make a self-loop (one vertex, one edge).
|
||||
e = libtess.mesh.makeEdge(this.mesh);
|
||||
libtess.mesh.meshSplice(e, e.sym);
|
||||
|
||||
} else {
|
||||
// Create a new vertex and edge which immediately follow e
|
||||
// in the ordering around the left face.
|
||||
libtess.mesh.splitEdge(e);
|
||||
e = e.lNext;
|
||||
}
|
||||
|
||||
// The new vertex is now e.org.
|
||||
e.org.data = data;
|
||||
e.org.coords[0] = coords[0];
|
||||
e.org.coords[1] = coords[1];
|
||||
e.org.coords[2] = coords[2];
|
||||
|
||||
// The winding of an edge says how the winding number changes as we
|
||||
// cross from the edge''s right face to its left face. We add the
|
||||
// vertices in such an order that a CCW contour will add +1 to
|
||||
// the winding number of the region inside the contour.
|
||||
e.winding = 1;
|
||||
e.sym.winding = -1;
|
||||
|
||||
this.lastEdge_ = e;
|
||||
};
|
||||
|
||||
/**
|
||||
* [cacheVertex_ description]
|
||||
* @private
|
||||
* @param {Array.<number>} coords [description]
|
||||
* @param {Object} data [description]
|
||||
*/
|
||||
libtess.GluTesselator.prototype.cacheVertex_ = function(coords, data) {
|
||||
var v = this.cache[this.cacheCount];
|
||||
|
||||
v.data = data;
|
||||
v.coords[0] = coords[0];
|
||||
v.coords[1] = coords[1];
|
||||
v.coords[2] = coords[2];
|
||||
++this.cacheCount;
|
||||
};
|
||||
|
||||
/**
|
||||
* [emptyCache_ description]
|
||||
* @private
|
||||
*/
|
||||
libtess.GluTesselator.prototype.emptyCache_ = function() {
|
||||
// NOTE(bckenny): surprise!
|
||||
this.mesh = new libtess.GluMesh();
|
||||
|
||||
for (var i = 0; i < this.cacheCount; i++) {
|
||||
var v = this.cache[i];
|
||||
this.addVertex_(v.coords, v.data);
|
||||
}
|
||||
|
||||
this.cacheCount = 0;
|
||||
this.emptyCache = false;
|
||||
};
|
||||
|
||||
// TODO(bckenny): all following conditional callbacks could be simplified
|
||||
// TODO(bckenny): using null for now, but may rework
|
||||
// TODO(bckenny): should add documentation that references in callback are volatile (or make a copy)
|
||||
// see README callback descriptions
|
||||
/**
|
||||
* [callBeginOrBeginData description]
|
||||
* @param {libtess.primitiveType} type [description]
|
||||
*/
|
||||
libtess.GluTesselator.prototype.callBeginOrBeginData = function(type) {
|
||||
if (this.callBeginData_) {
|
||||
this.callBeginData_(type, this.polygonData_);
|
||||
|
||||
} else if (this.callBegin_) {
|
||||
this.callBegin_(type);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* [callVertexOrVertexData description]
|
||||
* @param {Object} data [description]
|
||||
*/
|
||||
libtess.GluTesselator.prototype.callVertexOrVertexData = function(data) {
|
||||
if (this.callVertexData_) {
|
||||
this.callVertexData_(data, this.polygonData_);
|
||||
|
||||
} else if (this.callVertex_) {
|
||||
this.callVertex_(data);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* [callEdgeFlagOrEdgeFlagData description]
|
||||
* @param {boolean} flag [description]
|
||||
*/
|
||||
libtess.GluTesselator.prototype.callEdgeFlagOrEdgeFlagData = function(flag) {
|
||||
if (this.callEdgeFlagData_) {
|
||||
this.callEdgeFlagData_(flag, this.polygonData_);
|
||||
|
||||
} else if (this.callEdgeFlag_) {
|
||||
this.callEdgeFlag_(flag);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* [callEndOrEndData description]
|
||||
*/
|
||||
libtess.GluTesselator.prototype.callEndOrEndData = function() {
|
||||
if (this.callEndData_) {
|
||||
this.callEndData_(this.polygonData_);
|
||||
|
||||
} else if (this.callEnd_) {
|
||||
this.callEnd_();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* [callCombineOrCombineData description]
|
||||
* @param {Array.<number>} coords [description]
|
||||
* @param {Array.<Object>} data [description]
|
||||
* @param {Array.<number>} weight [description]
|
||||
* @return {Object} Interpolated vertex
|
||||
*/
|
||||
libtess.GluTesselator.prototype.callCombineOrCombineData =
|
||||
function(coords, data, weight) {
|
||||
|
||||
var interpData;
|
||||
if (this.callCombineData_) {
|
||||
interpData = this.callCombineData_(coords, data, weight, this.polygonData_);
|
||||
|
||||
} else if (this.callCombine_) {
|
||||
interpData = this.callCombine_(coords, data, weight);
|
||||
}
|
||||
|
||||
// TODO(bckenny): can't be undefined
|
||||
if (interpData === undefined) {
|
||||
interpData = null;
|
||||
}
|
||||
return interpData;
|
||||
};
|
||||
|
||||
// TODO(bckenny): combine the enums in libtess
|
||||
/**
|
||||
* [callErrorOrErrorData description]
|
||||
* @param {(libtess.errorType|libtess.gluEnum)} errno [description]
|
||||
*/
|
||||
libtess.GluTesselator.prototype.callErrorOrErrorData = function(errno) {
|
||||
if (this.callErrorData_) {
|
||||
this.callErrorData_(errno, this.polygonData_);
|
||||
|
||||
} else if (this.callError_) {
|
||||
this.callError_(errno);
|
||||
}
|
||||
};
|
||||
618
src/libtess.js/mesh.js
Normal file
618
src/libtess.js/mesh.js
Normal file
@@ -0,0 +1,618 @@
|
||||
/**
|
||||
* 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 Eric Veach, July 1994
|
||||
* @author Brendan Kenny
|
||||
*/
|
||||
|
||||
// require libtess
|
||||
// require libtess.GluFace
|
||||
// require libtess.GluHalfEdge
|
||||
// require libtess.GluMesh
|
||||
// require libtess.GluVertex
|
||||
/*global libtess */
|
||||
|
||||
// TODO(bckenny): could maybe merge GluMesh and mesh.js since these are
|
||||
// operations on the mesh
|
||||
|
||||
libtess.mesh = function() {
|
||||
|
||||
};
|
||||
|
||||
/****************** Basic Edge Operations **********************/
|
||||
|
||||
/**
|
||||
* makeEdge creates one edge, two vertices, and a loop (face).
|
||||
* The loop consists of the two new half-edges.
|
||||
*
|
||||
* @param {libtess.GluMesh} mesh [description]
|
||||
* @return {libtess.GluHalfEdge} [description]
|
||||
*/
|
||||
libtess.mesh.makeEdge = function(mesh) {
|
||||
// TODO(bckenny): probably move to GluMesh, but needs Make* methods with it
|
||||
|
||||
var e = libtess.mesh.makeEdgePair_(mesh.eHead);
|
||||
|
||||
// complete edge with vertices and face (see mesh.makeEdgePair_)
|
||||
libtess.mesh.makeVertex_(e, mesh.vHead);
|
||||
libtess.mesh.makeVertex_(e.sym, mesh.vHead );
|
||||
libtess.mesh.makeFace_(e, mesh.fHead);
|
||||
|
||||
return e;
|
||||
};
|
||||
|
||||
/**
|
||||
* meshSplice(eOrg, eDst) is the basic operation for changing the
|
||||
* mesh connectivity and topology. It changes the mesh so that
|
||||
* eOrg.oNext <- OLD( eDst.oNext )
|
||||
* eDst.oNext <- OLD( eOrg.oNext )
|
||||
* where OLD(...) means the value before the meshSplice operation.
|
||||
*
|
||||
* This can have two effects on the vertex structure:
|
||||
* - if eOrg.org != eDst.org, the two vertices are merged together
|
||||
* - if eOrg.org == eDst.org, the origin is split into two vertices
|
||||
* In both cases, eDst.org is changed and eOrg.org is untouched.
|
||||
*
|
||||
* Similarly (and independently) for the face structure,
|
||||
* - if eOrg.lFace == eDst.lFace, one loop is split into two
|
||||
* - if eOrg.lFace != eDst.lFace, two distinct loops are joined into one
|
||||
* In both cases, eDst.lFace is changed and eOrg.lFace is unaffected.
|
||||
*
|
||||
* Some special cases:
|
||||
* If eDst == eOrg, the operation has no effect.
|
||||
* If eDst == eOrg.lNext, the new face will have a single edge.
|
||||
* If eDst == eOrg.lPrev(), the old face will have a single edge.
|
||||
* If eDst == eOrg.oNext, the new vertex will have a single edge.
|
||||
* If eDst == eOrg.oPrev(), the old vertex will have a single edge.
|
||||
*
|
||||
* @param {libtess.GluHalfEdge} eOrg [description]
|
||||
* @param {libtess.GluHalfEdge} eDst [description]
|
||||
*/
|
||||
libtess.mesh.meshSplice = function(eOrg, eDst) {
|
||||
// TODO: more descriptive name?
|
||||
|
||||
var joiningLoops = false;
|
||||
var joiningVertices = false;
|
||||
|
||||
if (eOrg === eDst) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (eDst.org !== eOrg.org) {
|
||||
// We are merging two disjoint vertices -- destroy eDst.org
|
||||
joiningVertices = true;
|
||||
libtess.mesh.killVertex_(eDst.org, eOrg.org);
|
||||
}
|
||||
|
||||
if (eDst.lFace !== eOrg.lFace) {
|
||||
// We are connecting two disjoint loops -- destroy eDst.lFace
|
||||
joiningLoops = true;
|
||||
libtess.mesh.killFace_(eDst.lFace, eOrg.lFace);
|
||||
}
|
||||
|
||||
// Change the edge structure
|
||||
libtess.mesh.splice_(eDst, eOrg);
|
||||
|
||||
if (!joiningVertices) {
|
||||
// We split one vertex into two -- the new vertex is eDst.org.
|
||||
// Make sure the old vertex points to a valid half-edge.
|
||||
libtess.mesh.makeVertex_(eDst, eOrg.org);
|
||||
eOrg.org.anEdge = eOrg;
|
||||
}
|
||||
|
||||
if (!joiningLoops) {
|
||||
// We split one loop into two -- the new loop is eDst.lFace.
|
||||
// Make sure the old face points to a valid half-edge.
|
||||
libtess.mesh.makeFace_(eDst, eOrg.lFace);
|
||||
eOrg.lFace.anEdge = eOrg;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* deleteEdge(eDel) removes the edge eDel. There are several cases:
|
||||
* if (eDel.lFace != eDel.rFace()), we join two loops into one; the loop
|
||||
* eDel.lFace is deleted. Otherwise, we are splitting one loop into two;
|
||||
* the newly created loop will contain eDel.dst(). If the deletion of eDel
|
||||
* would create isolated vertices, those are deleted as well.
|
||||
*
|
||||
* This function could be implemented as two calls to __gl_meshSplice
|
||||
* plus a few calls to memFree, but this would allocate and delete
|
||||
* unnecessary vertices and faces.
|
||||
*
|
||||
* @param {libtess.GluHalfEdge} eDel [description]
|
||||
*/
|
||||
libtess.mesh.deleteEdge = function(eDel) {
|
||||
var eDelSym = eDel.sym;
|
||||
var joiningLoops = false;
|
||||
|
||||
// First step: disconnect the origin vertex eDel.org. We make all
|
||||
// changes to get a consistent mesh in this "intermediate" state.
|
||||
if (eDel.lFace !== eDel.rFace()) {
|
||||
// We are joining two loops into one -- remove the left face
|
||||
joiningLoops = true;
|
||||
libtess.mesh.killFace_(eDel.lFace, eDel.rFace());
|
||||
}
|
||||
|
||||
if (eDel.oNext === eDel ) {
|
||||
libtess.mesh.killVertex_(eDel.org, null);
|
||||
|
||||
} else {
|
||||
// Make sure that eDel.org and eDel.rFace() point to valid half-edges
|
||||
eDel.rFace().anEdge = eDel.oPrev();
|
||||
eDel.org.anEdge = eDel.oNext;
|
||||
|
||||
libtess.mesh.splice_(eDel, eDel.oPrev());
|
||||
|
||||
if (!joiningLoops) {
|
||||
// We are splitting one loop into two -- create a new loop for eDel.
|
||||
libtess.mesh.makeFace_(eDel, eDel.lFace);
|
||||
}
|
||||
}
|
||||
|
||||
// Claim: the mesh is now in a consistent state, except that eDel.org
|
||||
// may have been deleted. Now we disconnect eDel.dst().
|
||||
if (eDelSym.oNext === eDelSym ) {
|
||||
libtess.mesh.killVertex_(eDelSym.org, null);
|
||||
libtess.mesh.killFace_(eDelSym.lFace, null);
|
||||
|
||||
} else {
|
||||
// Make sure that eDel.dst() and eDel.lFace point to valid half-edges
|
||||
eDel.lFace.anEdge = eDelSym.oPrev();
|
||||
eDelSym.org.anEdge = eDelSym.oNext;
|
||||
libtess.mesh.splice_(eDelSym, eDelSym.oPrev());
|
||||
}
|
||||
|
||||
// Any isolated vertices or faces have already been freed.
|
||||
libtess.mesh.killEdge_(eDel);
|
||||
};
|
||||
|
||||
/******************** Other Edge Operations **********************/
|
||||
|
||||
/* All these routines can be implemented with the basic edge
|
||||
* operations above. They are provided for convenience and efficiency.
|
||||
*/
|
||||
|
||||
/**
|
||||
* addEdgeVertex(eOrg) creates a new edge eNew such that
|
||||
* eNew == eOrg.lNext, and eNew.dst() is a newly created vertex.
|
||||
* eOrg and eNew will have the same left face.
|
||||
*
|
||||
* @param {libtess.GluHalfEdge} eOrg [description]
|
||||
* @return {libtess.GluHalfEdge} [description]
|
||||
*/
|
||||
libtess.mesh.addEdgeVertex = function(eOrg) {
|
||||
// TODO(bckenny): why is it named this?
|
||||
|
||||
var eNew = libtess.mesh.makeEdgePair_(eOrg);
|
||||
var eNewSym = eNew.sym;
|
||||
|
||||
// Connect the new edge appropriately
|
||||
libtess.mesh.splice_(eNew, eOrg.lNext);
|
||||
|
||||
// Set the vertex and face information
|
||||
eNew.org = eOrg.dst();
|
||||
|
||||
libtess.mesh.makeVertex_(eNewSym, eNew.org );
|
||||
|
||||
eNew.lFace = eNewSym.lFace = eOrg.lFace;
|
||||
|
||||
return eNew;
|
||||
};
|
||||
|
||||
/**
|
||||
* splitEdge(eOrg) splits eOrg into two edges eOrg and eNew,
|
||||
* such that eNew == eOrg.lNext. The new vertex is eOrg.dst() == eNew.org.
|
||||
* eOrg and eNew will have the same left face.
|
||||
*
|
||||
* @param {libtess.GluHalfEdge} eOrg [description]
|
||||
* @return {!libtess.GluHalfEdge} [description]
|
||||
*/
|
||||
libtess.mesh.splitEdge = function(eOrg) {
|
||||
var tempHalfEdge = libtess.mesh.addEdgeVertex(eOrg);
|
||||
var eNew = tempHalfEdge.sym;
|
||||
|
||||
// Disconnect eOrg from eOrg.dst() and connect it to eNew.org
|
||||
libtess.mesh.splice_(eOrg.sym, eOrg.sym.oPrev());
|
||||
libtess.mesh.splice_(eOrg.sym, eNew);
|
||||
|
||||
// Set the vertex and face information
|
||||
eOrg.sym.org = eNew.org; // NOTE(bckenny): assignment to dst
|
||||
eNew.dst().anEdge = eNew.sym; // may have pointed to eOrg.sym
|
||||
eNew.sym.lFace = eOrg.rFace(); // NOTE(bckenny): assignment to rFace
|
||||
eNew.winding = eOrg.winding; // copy old winding information
|
||||
eNew.sym.winding = eOrg.sym.winding;
|
||||
|
||||
return eNew;
|
||||
};
|
||||
|
||||
/**
|
||||
* connect(eOrg, eDst) creates a new edge from eOrg.dst()
|
||||
* to eDst.org, and returns the corresponding half-edge eNew.
|
||||
* If eOrg.lFace == eDst.lFace, this splits one loop into two,
|
||||
* and the newly created loop is eNew.lFace. Otherwise, two disjoint
|
||||
* loops are merged into one, and the loop eDst.lFace is destroyed.
|
||||
*
|
||||
* If (eOrg == eDst), the new face will have only two edges.
|
||||
* If (eOrg.lNext == eDst), the old face is reduced to a single edge.
|
||||
* If (eOrg.lNext.lNext == eDst), the old face is reduced to two edges.
|
||||
*
|
||||
* @param {libtess.GluHalfEdge} eOrg [description]
|
||||
* @param {libtess.GluHalfEdge} eDst [description]
|
||||
* @return {!libtess.GluHalfEdge} [description]
|
||||
*/
|
||||
libtess.mesh.connect = function(eOrg, eDst) {
|
||||
var joiningLoops = false;
|
||||
var eNew = libtess.mesh.makeEdgePair_(eOrg);
|
||||
var eNewSym = eNew.sym;
|
||||
|
||||
if (eDst.lFace !== eOrg.lFace) {
|
||||
// We are connecting two disjoint loops -- destroy eDst.lFace
|
||||
joiningLoops = true;
|
||||
libtess.mesh.killFace_(eDst.lFace, eOrg.lFace);
|
||||
}
|
||||
|
||||
// Connect the new edge appropriately
|
||||
libtess.mesh.splice_(eNew, eOrg.lNext);
|
||||
libtess.mesh.splice_(eNewSym, eDst);
|
||||
|
||||
// Set the vertex and face information
|
||||
eNew.org = eOrg.dst();
|
||||
eNewSym.org = eDst.org;
|
||||
eNew.lFace = eNewSym.lFace = eOrg.lFace;
|
||||
|
||||
// Make sure the old face points to a valid half-edge
|
||||
eOrg.lFace.anEdge = eNewSym;
|
||||
|
||||
if (!joiningLoops) {
|
||||
// We split one loop into two -- the new loop is eNew.lFace
|
||||
libtess.mesh.makeFace_(eNew, eOrg.lFace );
|
||||
}
|
||||
return eNew;
|
||||
};
|
||||
|
||||
/******************** Other Operations **********************/
|
||||
|
||||
/**
|
||||
* zapFace(fZap) destroys a face and removes it from the
|
||||
* global face list. All edges of fZap will have a null pointer as their
|
||||
* left face. Any edges which also have a null pointer as their right face
|
||||
* are deleted entirely (along with any isolated vertices this produces).
|
||||
* An entire mesh can be deleted by zapping its faces, one at a time,
|
||||
* in any order. Zapped faces cannot be used in further mesh operations!
|
||||
*
|
||||
* @param {libtess.GluFace} fZap [description]
|
||||
*/
|
||||
libtess.mesh.zapFace = function(fZap) {
|
||||
var eStart = fZap.anEdge;
|
||||
|
||||
// walk around face, deleting edges whose right face is also NULL
|
||||
var eNext = eStart.lNext;
|
||||
var e;
|
||||
do {
|
||||
e = eNext;
|
||||
eNext = e.lNext;
|
||||
|
||||
e.lFace = null;
|
||||
if (e.rFace() === null) {
|
||||
// delete the edge -- see mesh.deleteEdge above
|
||||
if (e.oNext === e) {
|
||||
libtess.mesh.killVertex_(e.org, null);
|
||||
|
||||
} else {
|
||||
// Make sure that e.org points to a valid half-edge
|
||||
e.org.anEdge = e.oNext;
|
||||
libtess.mesh.splice_(e, e.oPrev());
|
||||
}
|
||||
|
||||
var eSym = e.sym;
|
||||
|
||||
if (eSym.oNext === eSym) {
|
||||
libtess.mesh.killVertex_(eSym.org, null);
|
||||
|
||||
} else {
|
||||
// Make sure that eSym.org points to a valid half-edge
|
||||
eSym.org.anEdge = eSym.oNext;
|
||||
libtess.mesh.splice_(eSym, eSym.oPrev());
|
||||
}
|
||||
libtess.mesh.killEdge_(e);
|
||||
}
|
||||
} while(e !== eStart);
|
||||
|
||||
// delete from circular doubly-linked list
|
||||
var fPrev = fZap.prev;
|
||||
var fNext = fZap.next;
|
||||
fNext.prev = fPrev;
|
||||
fPrev.next = fNext;
|
||||
|
||||
// TODO(bckenny): memFree( fZap );
|
||||
// TODO(bckenny): probably null at callsite
|
||||
};
|
||||
|
||||
/**
|
||||
* meshUnion() forms the union of all structures in
|
||||
* both meshes, and returns the new mesh (the old meshes are destroyed).
|
||||
*
|
||||
* @param {libtess.GluMesh} mesh1 [description]
|
||||
* @param {libtess.GluMesh} mesh2 [description]
|
||||
* @return {libtess.GluMesh} [description]
|
||||
*/
|
||||
libtess.mesh.meshUnion = function(mesh1, mesh2) {
|
||||
// TODO(bceknny): probably move to GluMesh method
|
||||
var f1 = mesh1.fHead;
|
||||
var v1 = mesh1.vHead;
|
||||
var e1 = mesh1.eHead;
|
||||
|
||||
var f2 = mesh2.fHead;
|
||||
var v2 = mesh2.vHead;
|
||||
var e2 = mesh2.eHead;
|
||||
|
||||
// Add the faces, vertices, and edges of mesh2 to those of mesh1
|
||||
if (f2.next !== f2) {
|
||||
f1.prev.next = f2.next;
|
||||
f2.next.prev = f1.prev;
|
||||
f2.prev.next = f1;
|
||||
f1.prev = f2.prev;
|
||||
}
|
||||
|
||||
if (v2.next !== v2) {
|
||||
v1.prev.next = v2.next;
|
||||
v2.next.prev = v1.prev;
|
||||
v2.prev.next = v1;
|
||||
v1.prev = v2.prev;
|
||||
}
|
||||
|
||||
if (e2.next !== e2) {
|
||||
e1.sym.next.sym.next = e2.next;
|
||||
e2.next.sym.next = e1.sym.next;
|
||||
e2.sym.next.sym.next = e1;
|
||||
e1.sym.next = e2.sym.next;
|
||||
}
|
||||
|
||||
// TODO(bckenny): memFree(mesh2);
|
||||
// TODO(bckenny): probably null at callsite
|
||||
return mesh1;
|
||||
};
|
||||
|
||||
/**
|
||||
* deleteMesh(mesh) will free all storage for any valid mesh.
|
||||
* @param {libtess.GluMesh} mesh [description]
|
||||
*/
|
||||
libtess.mesh.deleteMesh = function(mesh) {
|
||||
// TODO(bckenny): unnecessary, I think.
|
||||
// TODO(bckenny): might want to explicitly null at callsite
|
||||
// lots of memFrees. see also DELETE_BY_ZAPPING
|
||||
};
|
||||
|
||||
/************************ Utility Routines ************************/
|
||||
|
||||
/**
|
||||
* Creates a new pair of half-edges which form their own loop.
|
||||
* No vertex or face structures are allocated, but these must be assigned
|
||||
* before the current edge operation is completed.
|
||||
*
|
||||
* TODO(bckenny): warning about eNext strictly being first of pair? (see code)
|
||||
*
|
||||
* @private
|
||||
* @param {libtess.GluHalfEdge} eNext [description]
|
||||
* @return {libtess.GluHalfEdge} [description]
|
||||
*/
|
||||
libtess.mesh.makeEdgePair_ = function(eNext) {
|
||||
var e = new libtess.GluHalfEdge();
|
||||
var eSym = new libtess.GluHalfEdge();
|
||||
|
||||
// TODO(bckenny): how do we ensure this? see above comment in jsdoc
|
||||
// Make sure eNext points to the first edge of the edge pair
|
||||
// if (eNext->Sym < eNext ) { eNext = eNext->Sym; }
|
||||
|
||||
// NOTE(bckenny): check this for bugs in current implementation!
|
||||
|
||||
// Insert in circular doubly-linked list before eNext.
|
||||
// Note that the prev pointer is stored in sym.next.
|
||||
var ePrev = eNext.sym.next;
|
||||
eSym.next = ePrev;
|
||||
ePrev.sym.next = e;
|
||||
e.next = eNext;
|
||||
eNext.sym.next = eSym;
|
||||
|
||||
e.sym = eSym;
|
||||
e.oNext = e;
|
||||
e.lNext = eSym;
|
||||
|
||||
eSym.sym = e;
|
||||
eSym.oNext = eSym;
|
||||
eSym.lNext = e;
|
||||
|
||||
return e;
|
||||
};
|
||||
|
||||
/**
|
||||
* splice_ is best described by the Guibas/Stolfi paper or the
|
||||
* CS348a notes. Basically, it modifies the mesh so that
|
||||
* a.oNext and b.oNext are exchanged. This can have various effects
|
||||
* depending on whether a and b belong to different face or vertex rings.
|
||||
* For more explanation see mesh.meshSplice below.
|
||||
*
|
||||
* @private
|
||||
* @param {libtess.GluHalfEdge} a [description]
|
||||
* @param {libtess.GluHalfEdge} b [description]
|
||||
*/
|
||||
libtess.mesh.splice_ = function(a, b) {
|
||||
var aONext = a.oNext;
|
||||
var bONext = b.oNext;
|
||||
|
||||
aONext.sym.lNext = b;
|
||||
bONext.sym.lNext = a;
|
||||
a.oNext = bONext;
|
||||
b.oNext = aONext;
|
||||
};
|
||||
|
||||
/**
|
||||
* makeVertex_(eOrig, vNext) attaches a new vertex and makes it the
|
||||
* origin of all edges in the vertex loop to which eOrig belongs. "vNext" gives
|
||||
* a place to insert the new vertex in the global vertex list. We insert
|
||||
* the new vertex *before* vNext so that algorithms which walk the vertex
|
||||
* list will not see the newly created vertices.
|
||||
*
|
||||
* NOTE: unlike original, acutally allocates new vertex.
|
||||
*
|
||||
* @private
|
||||
* @param {libtess.GluHalfEdge} eOrig [description]
|
||||
* @param {libtess.GluVertex} vNext [description]
|
||||
*/
|
||||
libtess.mesh.makeVertex_ = function(eOrig, vNext) {
|
||||
// insert in circular doubly-linked list before vNext
|
||||
var vPrev = vNext.prev;
|
||||
var vNew = new libtess.GluVertex(vNext, vPrev);
|
||||
vPrev.next = vNew;
|
||||
vNext.prev = vNew;
|
||||
|
||||
vNew.anEdge = eOrig;
|
||||
// leave coords, s, t undefined
|
||||
// TODO(bckenny): does above line mean 0 specifically, or does it matter?
|
||||
|
||||
// fix other edges on this vertex loop
|
||||
var e = eOrig;
|
||||
do {
|
||||
e.org = vNew;
|
||||
e = e.oNext;
|
||||
} while(e !== eOrig);
|
||||
};
|
||||
|
||||
/**
|
||||
* makeFace_(eOrig, fNext) attaches a new face and makes it the left
|
||||
* face of all edges in the face loop to which eOrig belongs. "fNext" gives
|
||||
* a place to insert the new face in the global face list. We insert
|
||||
* the new face *before* fNext so that algorithms which walk the face
|
||||
* list will not see the newly created faces.
|
||||
*
|
||||
* NOTE: unlike original, acutally allocates new face.
|
||||
*
|
||||
* @private
|
||||
* @param {libtess.GluHalfEdge} eOrig [description]
|
||||
* @param {libtess.GluFace} fNext [description]
|
||||
*/
|
||||
libtess.mesh.makeFace_ = function(eOrig, fNext) {
|
||||
// insert in circular doubly-linked list before fNext
|
||||
var fPrev = fNext.prev;
|
||||
var fNew = new libtess.GluFace(fNext, fPrev);
|
||||
fPrev.next = fNew;
|
||||
fNext.prev = fNew;
|
||||
|
||||
fNew.anEdge = eOrig;
|
||||
|
||||
// The new face is marked "inside" if the old one was. This is a
|
||||
// convenience for the common case where a face has been split in two.
|
||||
fNew.inside = fNext.inside;
|
||||
|
||||
// fix other edges on this face loop
|
||||
var e = eOrig;
|
||||
do {
|
||||
e.lFace = fNew;
|
||||
e = e.lNext;
|
||||
} while(e !== eOrig);
|
||||
};
|
||||
|
||||
/**
|
||||
* killEdge_ destroys an edge (the half-edges eDel and eDel.sym),
|
||||
* and removes from the global edge list.
|
||||
*
|
||||
* @private
|
||||
* @param {libtess.GluHalfEdge} eDel [description]
|
||||
*/
|
||||
libtess.mesh.killEdge_ = function(eDel) {
|
||||
// TODO(bckenny): in this case, no need to worry(?), but check when checking mesh.makeEdgePair_
|
||||
// Half-edges are allocated in pairs, see EdgePair above
|
||||
// if (eDel->Sym < eDel ) { eDel = eDel->Sym; }
|
||||
|
||||
// delete from circular doubly-linked list
|
||||
var eNext = eDel.next;
|
||||
var ePrev = eDel.sym.next;
|
||||
eNext.sym.next = ePrev;
|
||||
ePrev.sym.next = eNext;
|
||||
|
||||
// TODO(bckenny): memFree( eDel ); (which also frees eDel.sym)
|
||||
// TODO(bckenny): need to null at callsites?
|
||||
};
|
||||
|
||||
/**
|
||||
* killVertex_ destroys a vertex and removes it from the global
|
||||
* vertex list. It updates the vertex loop to point to a given new vertex.
|
||||
*
|
||||
* @private
|
||||
* @param {libtess.GluVertex} vDel [description]
|
||||
* @param {libtess.GluVertex} newOrg [description]
|
||||
*/
|
||||
libtess.mesh.killVertex_ = function(vDel, newOrg) {
|
||||
var eStart = vDel.anEdge;
|
||||
|
||||
// change the origin of all affected edges
|
||||
var e = eStart;
|
||||
do {
|
||||
e.org = newOrg;
|
||||
e = e.oNext;
|
||||
} while(e !== eStart);
|
||||
|
||||
// delete from circular doubly-linked list
|
||||
var vPrev = vDel.prev;
|
||||
var vNext = vDel.next;
|
||||
vNext.prev = vPrev;
|
||||
vPrev.next = vNext;
|
||||
|
||||
// TODO(bckenny): memFree( vDel );
|
||||
// TODO(bckenny): need to null at callsites?
|
||||
};
|
||||
|
||||
/**
|
||||
* killFace_ destroys a face and removes it from the global face
|
||||
* list. It updates the face loop to point to a given new face.
|
||||
*
|
||||
* @private
|
||||
* @param {libtess.GluFace} fDel [description]
|
||||
* @param {libtess.GluFace} newLFace [description]
|
||||
*/
|
||||
libtess.mesh.killFace_ = function(fDel, newLFace) {
|
||||
var eStart = fDel.anEdge;
|
||||
|
||||
// change the left face of all affected edges
|
||||
var e = eStart;
|
||||
do {
|
||||
e.lFace = newLFace;
|
||||
e = e.lNext;
|
||||
} while(e !== eStart);
|
||||
|
||||
// delete from circular doubly-linked list
|
||||
var fPrev = fDel.prev;
|
||||
var fNext = fDel.next;
|
||||
fNext.prev = fPrev;
|
||||
fPrev.next = fNext;
|
||||
|
||||
// TODO(bckenny): memFree( fDel );
|
||||
// TODO(bckenny): need to null at callsites?
|
||||
};
|
||||
93
src/libtess.js/mesh/GluFace.js
Normal file
93
src/libtess.js/mesh/GluFace.js
Normal file
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* 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 Eric Veach, July 1994
|
||||
* @author Brendan Kenny
|
||||
*/
|
||||
|
||||
// require libtess
|
||||
// requre libtess.GluHalfEdge
|
||||
/*global libtess */
|
||||
|
||||
/**
|
||||
* Each face has a pointer to the next and previous faces in the
|
||||
* circular list, and a pointer to a half-edge with this face as
|
||||
* the left face (null if this is the dummy header). There is also
|
||||
* a field "data" for client data.
|
||||
*
|
||||
* @param {libtess.GluFace=} opt_nextFace [description]
|
||||
* @param {libtess.GluFace=} opt_prevFace [description]
|
||||
* @constructor
|
||||
*/
|
||||
libtess.GluFace = function(opt_nextFace, opt_prevFace) {
|
||||
// TODO(bckenny): reverse order of params?
|
||||
|
||||
/**
|
||||
* next face (never null)
|
||||
* @type {!libtess.GluFace}
|
||||
*/
|
||||
this.next = opt_nextFace || this;
|
||||
|
||||
/**
|
||||
* previous face (never NULL)
|
||||
* @type {!libtess.GluFace}
|
||||
*/
|
||||
this.prev = opt_prevFace || this;
|
||||
|
||||
/**
|
||||
* A half edge with this left face.
|
||||
* @type {libtess.GluHalfEdge}
|
||||
*/
|
||||
this.anEdge = null;
|
||||
|
||||
/**
|
||||
* room for client's data
|
||||
* @type {Object}
|
||||
*/
|
||||
this.data = null;
|
||||
|
||||
/**
|
||||
* "stack" for conversion to strips
|
||||
* @type {libtess.GluFace}
|
||||
*/
|
||||
this.trail = null;
|
||||
|
||||
/**
|
||||
* Flag for conversion to strips.
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.marked = false;
|
||||
|
||||
/**
|
||||
* This face is in the polygon interior.
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.inside = false;
|
||||
};
|
||||
191
src/libtess.js/mesh/GluHalfEdge.js
Normal file
191
src/libtess.js/mesh/GluHalfEdge.js
Normal file
@@ -0,0 +1,191 @@
|
||||
/**
|
||||
* 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 Eric Veach, July 1994
|
||||
* @author Brendan Kenny
|
||||
*/
|
||||
|
||||
// require libtess
|
||||
// require libtess.GluFace
|
||||
// require libtess.GluVertex
|
||||
// require libtess.ActiveRegion
|
||||
/*global libtess */
|
||||
|
||||
/**
|
||||
* The fundamental data structure is the "half-edge". Two half-edges
|
||||
* go together to make an edge, but they point in opposite directions.
|
||||
* Each half-edge has a pointer to its mate (the "symmetric" half-edge sym),
|
||||
* its origin vertex (org), the face on its left side (lFace), and the
|
||||
* adjacent half-edges in the CCW direction around the origin vertex
|
||||
* (oNext) and around the left face (lNext). There is also a "next"
|
||||
* pointer for the global edge list (see below).
|
||||
*
|
||||
* The notation used for mesh navigation:
|
||||
* sym = the mate of a half-edge (same edge, but opposite direction)
|
||||
* oNext = edge CCW around origin vertex (keep same origin)
|
||||
* dNext = edge CCW around destination vertex (keep same dest)
|
||||
* lNext = edge CCW around left face (dest becomes new origin)
|
||||
* rNext = edge CCW around right face (origin becomes new dest)
|
||||
*
|
||||
* "prev" means to substitute CW for CCW in the definitions above.
|
||||
*
|
||||
* The circular edge list is special; since half-edges always occur
|
||||
* in pairs (e and e.sym), each half-edge stores a pointer in only
|
||||
* one direction. Starting at eHead and following the e.next pointers
|
||||
* will visit each *edge* once (ie. e or e.sym, but not both).
|
||||
* e.sym stores a pointer in the opposite direction, thus it is
|
||||
* always true that e.sym.next.sym.next === e.
|
||||
*
|
||||
* @param {libtess.GluHalfEdge=} opt_nextEdge [description]
|
||||
* @constructor
|
||||
*/
|
||||
libtess.GluHalfEdge = function(opt_nextEdge) {
|
||||
// TODO(bckenny): are these the right defaults? (from gl_meshNewMesh requirements)
|
||||
|
||||
/**
|
||||
* doubly-linked list (prev==sym->next)
|
||||
* @type {!libtess.GluHalfEdge}
|
||||
*/
|
||||
this.next = opt_nextEdge || this;
|
||||
|
||||
// TODO(bckenny): how can this be required if created in pairs? move to factory creation only?
|
||||
/**
|
||||
* same edge, opposite direction
|
||||
* @type {libtess.GluHalfEdge}
|
||||
*/
|
||||
this.sym = null;
|
||||
|
||||
/**
|
||||
* next edge CCW around origin
|
||||
* @type {libtess.GluHalfEdge}
|
||||
*/
|
||||
this.oNext = null;
|
||||
|
||||
/**
|
||||
* next edge CCW around left face
|
||||
* @type {libtess.GluHalfEdge}
|
||||
*/
|
||||
this.lNext = null;
|
||||
|
||||
/**
|
||||
* origin vertex (oVertex too long)
|
||||
* @type {libtess.GluVertex}
|
||||
*/
|
||||
this.org = null;
|
||||
|
||||
/**
|
||||
* left face
|
||||
* @type {libtess.GluFace}
|
||||
*/
|
||||
this.lFace = null;
|
||||
|
||||
// Internal data (keep hidden)
|
||||
// NOTE(bckenny): can't be private, though...
|
||||
|
||||
/**
|
||||
* a region with this upper edge (see sweep.js)
|
||||
* @type {libtess.ActiveRegion}
|
||||
*/
|
||||
this.activeRegion = null;
|
||||
|
||||
/**
|
||||
* change in winding number when crossing from the right face to the left face
|
||||
* @type {number}
|
||||
*/
|
||||
this.winding = 0;
|
||||
};
|
||||
|
||||
// NOTE(bckenny): the following came from macros in mesh
|
||||
// TODO(bckenny): using methods as aliases for sym connections for now.
|
||||
// not sure about this approach. getters? renames?
|
||||
|
||||
/**
|
||||
* [rFace description]
|
||||
* @return {libtess.GluFace} [description]
|
||||
*/
|
||||
libtess.GluHalfEdge.prototype.rFace = function() {
|
||||
return this.sym.lFace;
|
||||
};
|
||||
|
||||
/**
|
||||
* [dst description]
|
||||
* @return {libtess.GluVertex} [description]
|
||||
*/
|
||||
libtess.GluHalfEdge.prototype.dst = function() {
|
||||
return this.sym.org;
|
||||
};
|
||||
|
||||
/**
|
||||
* [oPrev description]
|
||||
* @return {libtess.GluHalfEdge} [description]
|
||||
*/
|
||||
libtess.GluHalfEdge.prototype.oPrev = function() {
|
||||
return this.sym.lNext;
|
||||
};
|
||||
|
||||
/**
|
||||
* [lPrev description]
|
||||
* @return {libtess.GluHalfEdge} [description]
|
||||
*/
|
||||
libtess.GluHalfEdge.prototype.lPrev = function() {
|
||||
return this.oNext.sym;
|
||||
};
|
||||
|
||||
/**
|
||||
* [dPrev description]
|
||||
* @return {libtess.GluHalfEdge} [description]
|
||||
*/
|
||||
libtess.GluHalfEdge.prototype.dPrev = function() {
|
||||
return this.lNext.sym;
|
||||
};
|
||||
|
||||
/**
|
||||
* [rPrev description]
|
||||
* @return {libtess.GluHalfEdge} [description]
|
||||
*/
|
||||
libtess.GluHalfEdge.prototype.rPrev = function() {
|
||||
return this.sym.oNext;
|
||||
};
|
||||
|
||||
/**
|
||||
* [dNext description]
|
||||
* @return {libtess.GluHalfEdge} [description]
|
||||
*/
|
||||
libtess.GluHalfEdge.prototype.dNext = function() {
|
||||
return this.rPrev().sym;
|
||||
};
|
||||
|
||||
/**
|
||||
* [rNext description]
|
||||
* @return {libtess.GluHalfEdge} [description]
|
||||
*/
|
||||
libtess.GluHalfEdge.prototype.rNext = function() {
|
||||
return this.oPrev().sym;
|
||||
};
|
||||
141
src/libtess.js/mesh/GluMesh.js
Normal file
141
src/libtess.js/mesh/GluMesh.js
Normal file
@@ -0,0 +1,141 @@
|
||||
/**
|
||||
* 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 Eric Veach, July 1994
|
||||
* @author Brendan Kenny
|
||||
*/
|
||||
|
||||
// require libtess.GluFace
|
||||
// require libtess.GluHalfEdge
|
||||
// require libtess.GluVertex
|
||||
/*global libtess */
|
||||
|
||||
/**
|
||||
* Creates a new mesh with no edges, no vertices,
|
||||
* and no loops (what we usually call a "face").
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
libtess.GluMesh = function() {
|
||||
/**
|
||||
* dummy header for vertex list
|
||||
* @type {libtess.GluVertex}
|
||||
*/
|
||||
this.vHead = new libtess.GluVertex();
|
||||
|
||||
/**
|
||||
* dummy header for face list
|
||||
* @type {libtess.GluFace}
|
||||
*/
|
||||
this.fHead = new libtess.GluFace();
|
||||
|
||||
/**
|
||||
* dummy header for edge list
|
||||
* @type {libtess.GluHalfEdge}
|
||||
*/
|
||||
this.eHead = new libtess.GluHalfEdge();
|
||||
|
||||
/**
|
||||
* and its symmetric counterpart
|
||||
* @type {libtess.GluHalfEdge}
|
||||
*/
|
||||
this.eHeadSym = new libtess.GluHalfEdge();
|
||||
|
||||
// TODO(bckenny): better way to pair these?
|
||||
this.eHead.sym = this.eHeadSym;
|
||||
this.eHeadSym.sym = this.eHead;
|
||||
};
|
||||
|
||||
// TODO(bckenny): #ifndef NDEBUG
|
||||
/**
|
||||
* Checks mesh for self-consistency.
|
||||
*/
|
||||
libtess.GluMesh.prototype.checkMesh = function() {
|
||||
if (!libtess.DEBUG) {
|
||||
return;
|
||||
}
|
||||
|
||||
var fHead = this.fHead;
|
||||
var vHead = this.vHead;
|
||||
var eHead = this.eHead;
|
||||
|
||||
var e;
|
||||
|
||||
// faces
|
||||
var f;
|
||||
var fPrev = fHead;
|
||||
for (fPrev = fHead; (f = fPrev.next) !== fHead; fPrev = f) {
|
||||
libtess.assert(f.prev === fPrev);
|
||||
e = f.anEdge;
|
||||
do {
|
||||
libtess.assert(e.sym !== e);
|
||||
libtess.assert(e.sym.sym === e);
|
||||
libtess.assert(e.lNext.oNext.sym === e);
|
||||
libtess.assert(e.oNext.sym.lNext === e);
|
||||
libtess.assert(e.lFace === f);
|
||||
e = e.lNext;
|
||||
} while(e !== f.anEdge);
|
||||
}
|
||||
libtess.assert(f.prev === fPrev && f.anEdge === null && f.data === null);
|
||||
|
||||
// vertices
|
||||
var v;
|
||||
var vPrev = vHead;
|
||||
for (vPrev = vHead; (v = vPrev.next) !== vHead; vPrev = v) {
|
||||
libtess.assert(v.prev === vPrev);
|
||||
e = v.anEdge;
|
||||
do {
|
||||
libtess.assert(e.sym !== e);
|
||||
libtess.assert(e.sym.sym === e);
|
||||
libtess.assert(e.lNext.oNext.sym === e);
|
||||
libtess.assert(e.oNext.sym.lNext === e);
|
||||
libtess.assert(e.org === v);
|
||||
e = e.oNext;
|
||||
} while(e !== v.anEdge);
|
||||
}
|
||||
libtess.assert(v.prev === vPrev && v.anEdge === null && v.data === null);
|
||||
|
||||
// edges
|
||||
var ePrev = eHead;
|
||||
for (ePrev = eHead; (e = ePrev.next) !== eHead; ePrev = e) {
|
||||
libtess.assert(e.sym.next === ePrev.sym);
|
||||
libtess.assert(e.sym !== e);
|
||||
libtess.assert(e.sym.sym === e);
|
||||
libtess.assert(e.org !== null);
|
||||
libtess.assert(e.dst() !== null);
|
||||
libtess.assert(e.lNext.oNext.sym === e);
|
||||
libtess.assert(e.oNext.sym.lNext === e);
|
||||
}
|
||||
libtess.assert(e.sym.next === ePrev.sym &&
|
||||
e.sym === this.eHeadSym &&
|
||||
e.sym.sym === e &&
|
||||
e.org === null && e.dst() === null &&
|
||||
e.lFace === null && e.rFace() === null);
|
||||
};
|
||||
102
src/libtess.js/mesh/GluVertex.js
Normal file
102
src/libtess.js/mesh/GluVertex.js
Normal file
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* 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 Eric Veach, July 1994
|
||||
* @author Brendan Kenny
|
||||
*/
|
||||
|
||||
// requre libtess.GluHalfEdge
|
||||
/*global libtess */
|
||||
|
||||
/**
|
||||
* Each vertex has a pointer to next and previous vertices in the
|
||||
* circular list, and a pointer to a half-edge with this vertex as
|
||||
* the origin (null if this is the dummy header). There is also a
|
||||
* field "data" for client data.
|
||||
*
|
||||
* @param {libtess.GluVertex=} opt_nextVertex [description]
|
||||
* @param {libtess.GluVertex=} opt_prevVertex [description]
|
||||
* @constructor
|
||||
*/
|
||||
libtess.GluVertex = function(opt_nextVertex, opt_prevVertex) {
|
||||
// TODO(bckenny): reverse order of params?
|
||||
|
||||
/**
|
||||
* Next vertex (never null).
|
||||
* @type {!libtess.GluVertex}
|
||||
*/
|
||||
this.next = opt_nextVertex || this;
|
||||
|
||||
/**
|
||||
* Previous vertex (never null).
|
||||
* @type {!libtess.GluVertex}
|
||||
*/
|
||||
this.prev = opt_prevVertex || this;
|
||||
|
||||
/**
|
||||
* A half-edge with this origin.
|
||||
* @type {libtess.GluHalfEdge}
|
||||
*/
|
||||
this.anEdge = null;
|
||||
|
||||
/**
|
||||
* The client's data.
|
||||
* @type {Object}
|
||||
*/
|
||||
this.data = null;
|
||||
|
||||
/**
|
||||
* The vertex location in 3D.
|
||||
* @type {Array.<number>}
|
||||
*/
|
||||
this.coords = [0, 0, 0];
|
||||
// TODO(bckenny): we may want to rethink coords, either eliminate (using s
|
||||
// and t and user data) or index into contiguous storage?
|
||||
|
||||
/**
|
||||
* Component of projection onto the sweep plane.
|
||||
* @type {number}
|
||||
*/
|
||||
this.s = 0;
|
||||
|
||||
/**
|
||||
* Component of projection onto the sweep plane.
|
||||
* @type {number}
|
||||
*/
|
||||
this.t = 0;
|
||||
|
||||
/**
|
||||
* To allow deletion from priority queue.
|
||||
* @type {?libtess.PQHandle}
|
||||
*/
|
||||
this.pqHandle = null;
|
||||
// NOTE(bckenny): pqHandle inited in sweep
|
||||
// TODO(bckenny): can we have a numeric default value? null may do bad things
|
||||
};
|
||||
290
src/libtess.js/normal.js
Normal file
290
src/libtess.js/normal.js
Normal file
@@ -0,0 +1,290 @@
|
||||
/**
|
||||
* 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 Eric Veach, July 1994
|
||||
* @author Brendan Kenny
|
||||
*/
|
||||
|
||||
// require libtess
|
||||
// require libtess.GluTesselator
|
||||
/*global libtess */
|
||||
|
||||
libtess.normal = function() {
|
||||
|
||||
};
|
||||
|
||||
// TODO(bckenny): NOTE:
|
||||
/* The "feature merging" is not intended to be complete. There are
|
||||
* special cases where edges are nearly parallel to the sweep line
|
||||
* which are not implemented. The algorithm should still behave
|
||||
* robustly (ie. produce a reasonable tesselation) in the presence
|
||||
* of such edges, however it may miss features which could have been
|
||||
* merged. We could minimize this effect by choosing the sweep line
|
||||
* direction to be something unusual (ie. not parallel to one of the
|
||||
* coordinate axes).
|
||||
*/
|
||||
/*#if defined(SLANTED_SWEEP)
|
||||
#define S_UNIT_X 0.50941539564955385 // Pre-normalized
|
||||
#define S_UNIT_Y 0.86052074622010633
|
||||
#endif
|
||||
*/
|
||||
/**
|
||||
* @type {number}
|
||||
* @private
|
||||
* @const
|
||||
*/
|
||||
libtess.normal.S_UNIT_X_ = 1.0;
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
* @private
|
||||
* @const
|
||||
*/
|
||||
libtess.normal.S_UNIT_Y_ = 0.0;
|
||||
|
||||
/**
|
||||
* projectPolygon determines the polygon normal
|
||||
* and projects vertices onto the plane of the polygon.
|
||||
*
|
||||
* @param {libtess.GluTesselator} tess [description]
|
||||
*/
|
||||
libtess.normal.projectPolygon = function(tess) {
|
||||
var computedNormal = false;
|
||||
|
||||
var norm = [0, 0, 0];
|
||||
norm[0] = tess.normal[0]; // TODO(bckenny): better way to init these?
|
||||
norm[1] = tess.normal[1];
|
||||
norm[2] = tess.normal[2];
|
||||
if (norm[0] === 0 && norm[1] === 0 && norm[2] === 0) {
|
||||
libtess.normal.computeNormal_(tess, norm);
|
||||
computedNormal = true;
|
||||
}
|
||||
|
||||
var sUnit = tess.sUnit;
|
||||
var tUnit = tess.tUnit;
|
||||
var i = libtess.normal.longAxis_(norm);
|
||||
|
||||
if (libtess.TRUE_PROJECT) {
|
||||
// Choose the initial sUnit vector to be approximately perpendicular
|
||||
// to the normal.
|
||||
libtess.normal.normalize_(norm);
|
||||
|
||||
sUnit[i] = 0;
|
||||
sUnit[(i+1)%3] = libtess.normal.S_UNIT_X_;
|
||||
sUnit[(i+2)%3] = libtess.normal.S_UNIT_Y_;
|
||||
|
||||
// Now make it exactly perpendicular
|
||||
var w = libtess.normal.dot_(sUnit, norm);
|
||||
sUnit[0] -= w * norm[0];
|
||||
sUnit[1] -= w * norm[1];
|
||||
sUnit[2] -= w * norm[2];
|
||||
libtess.normal.normalize_(sUnit);
|
||||
|
||||
// Choose tUnit so that (sUnit,tUnit,norm) form a right-handed frame
|
||||
tUnit[0] = norm[1]*sUnit[2] - norm[2]*sUnit[1];
|
||||
tUnit[1] = norm[2]*sUnit[0] - norm[0]*sUnit[2];
|
||||
tUnit[2] = norm[0]*sUnit[1] - norm[1]*sUnit[0];
|
||||
libtess.normal.normalize_(tUnit);
|
||||
|
||||
} else {
|
||||
// Project perpendicular to a coordinate axis -- better numerically
|
||||
sUnit[i] = 0;
|
||||
sUnit[(i+1)%3] = libtess.normal.S_UNIT_X_;
|
||||
sUnit[(i+2)%3] = libtess.normal.S_UNIT_Y_;
|
||||
|
||||
tUnit[i] = 0;
|
||||
tUnit[(i+1)%3] = (norm[i] > 0) ? -libtess.normal.S_UNIT_Y_ : libtess.normal.S_UNIT_Y_;
|
||||
tUnit[(i+2)%3] = (norm[i] > 0) ? libtess.normal.S_UNIT_X_ : -libtess.normal.S_UNIT_X_;
|
||||
}
|
||||
|
||||
// Project the vertices onto the sweep plane
|
||||
var vHead = tess.mesh.vHead;
|
||||
for (var v = vHead.next; v !== vHead; v = v.next) {
|
||||
v.s = libtess.normal.dot_(v.coords, sUnit);
|
||||
v.t = libtess.normal.dot_(v.coords, tUnit);
|
||||
}
|
||||
|
||||
if (computedNormal) {
|
||||
libtess.normal.checkOrientation_(tess);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Dot product.
|
||||
* @private
|
||||
* @param {Array.<number>} u [description]
|
||||
* @param {Array.<number>} v [description]
|
||||
* @return {number} [description]
|
||||
*/
|
||||
libtess.normal.dot_ = function(u, v) {
|
||||
return u[0]*v[0] + u[1]*v[1] + u[2]*v[2];
|
||||
};
|
||||
|
||||
/**
|
||||
* Normalize vector v
|
||||
* @private
|
||||
* @param {Array.<number>} v [description]
|
||||
*/
|
||||
libtess.normal.normalize_ = function(v) {
|
||||
var len = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
|
||||
|
||||
libtess.assert(len > 0);
|
||||
len = Math.sqrt(len);
|
||||
v[0] /= len;
|
||||
v[1] /= len;
|
||||
v[2] /= len;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the index of the longest component of vector v.
|
||||
* @private
|
||||
* @param {Array.<number>} v [description]
|
||||
* @return {number} The index of the longest component.
|
||||
*/
|
||||
libtess.normal.longAxis_ = function(v) {
|
||||
var i = 0;
|
||||
|
||||
if (Math.abs(v[1]) > Math.abs(v[0])) {
|
||||
i = 1;
|
||||
}
|
||||
if (Math.abs(v[2]) > Math.abs(v[i])) {
|
||||
i = 2;
|
||||
}
|
||||
|
||||
return i;
|
||||
};
|
||||
|
||||
/**
|
||||
* [computeNormal description]
|
||||
*
|
||||
* @private
|
||||
* @param {libtess.GluTesselator} tess [description]
|
||||
* @param {Array.<number>} norm [description]
|
||||
*/
|
||||
libtess.normal.computeNormal_ = function(tess, norm) {
|
||||
// TODO(bckenny): better way to init these
|
||||
// TODO(bckenny): can pool these, but only called once per poly
|
||||
var maxVal = [0, 0, 0];
|
||||
var minVal = [0, 0, 0];
|
||||
var d1 = [0, 0, 0];
|
||||
var d2 = [0, 0, 0];
|
||||
var tNorm = [0, 0, 0];
|
||||
|
||||
maxVal[0] = maxVal[1] = maxVal[2] = -2 * libtess.GLU_TESS_MAX_COORD;
|
||||
minVal[0] = minVal[1] = minVal[2] = 2 * libtess.GLU_TESS_MAX_COORD;
|
||||
|
||||
// TODO(bckenny): better way to init these
|
||||
var maxVert = new Array(3);
|
||||
var minVert = new Array(3);
|
||||
|
||||
var i;
|
||||
var v;
|
||||
var vHead = tess.mesh.vHead;
|
||||
for (v = vHead.next; v !== vHead; v = v.next) {
|
||||
for (i = 0; i < 3; ++i) {
|
||||
var c = v.coords[i];
|
||||
if (c < minVal[i]) { minVal[i] = c; minVert[i] = v; }
|
||||
if (c > maxVal[i]) { maxVal[i] = c; maxVert[i] = v; }
|
||||
}
|
||||
}
|
||||
|
||||
// Find two vertices separated by at least 1/sqrt(3) of the maximum
|
||||
// distance between any two vertices
|
||||
i = 0;
|
||||
if (maxVal[1] - minVal[1] > maxVal[0] - minVal[0]) { i = 1; }
|
||||
if (maxVal[2] - minVal[2] > maxVal[i] - minVal[i]) { i = 2; }
|
||||
if (minVal[i] >= maxVal[i]) {
|
||||
// All vertices are the same -- normal doesn't matter
|
||||
norm[0] = 0; norm[1] = 0; norm[2] = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
// Look for a third vertex which forms the triangle with maximum area
|
||||
// (Length of normal == twice the triangle area)
|
||||
var maxLen2 = 0;
|
||||
var v1 = minVert[i];
|
||||
var v2 = maxVert[i];
|
||||
d1[0] = v1.coords[0] - v2.coords[0];
|
||||
d1[1] = v1.coords[1] - v2.coords[1];
|
||||
d1[2] = v1.coords[2] - v2.coords[2];
|
||||
for (v = vHead.next; v !== vHead; v = v.next) {
|
||||
d2[0] = v.coords[0] - v2.coords[0];
|
||||
d2[1] = v.coords[1] - v2.coords[1];
|
||||
d2[2] = v.coords[2] - v2.coords[2];
|
||||
tNorm[0] = d1[1]*d2[2] - d1[2]*d2[1];
|
||||
tNorm[1] = d1[2]*d2[0] - d1[0]*d2[2];
|
||||
tNorm[2] = d1[0]*d2[1] - d1[1]*d2[0];
|
||||
var tLen2 = tNorm[0]*tNorm[0] + tNorm[1]*tNorm[1] + tNorm[2]*tNorm[2];
|
||||
if (tLen2 > maxLen2) {
|
||||
maxLen2 = tLen2;
|
||||
norm[0] = tNorm[0];
|
||||
norm[1] = tNorm[1];
|
||||
norm[2] = tNorm[2];
|
||||
}
|
||||
}
|
||||
|
||||
if (maxLen2 <= 0) {
|
||||
// All points lie on a single line -- any decent normal will do
|
||||
norm[0] = norm[1] = norm[2] = 0;
|
||||
norm[libtess.normal.longAxis_(d1)] = 1;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* [checkOrientation description]
|
||||
*
|
||||
* @private
|
||||
* @param {libtess.GluTesselator} tess [description]
|
||||
*/
|
||||
libtess.normal.checkOrientation_ = function(tess) {
|
||||
// When we compute the normal automatically, we choose the orientation
|
||||
// so that the the sum of the signed areas of all contours is non-negative.
|
||||
var area = 0;
|
||||
var fHead = tess.mesh.fHead;
|
||||
for (var f = fHead.next; f !== fHead; f = f.next) {
|
||||
var e = f.anEdge;
|
||||
if (e.winding <= 0) { continue; }
|
||||
do {
|
||||
area += (e.org.s - e.dst().s) * (e.org.t + e.dst().t);
|
||||
e = e.lNext;
|
||||
} while(e !== f.anEdge);
|
||||
}
|
||||
|
||||
if (area < 0) {
|
||||
// Reverse the orientation by flipping all the t-coordinates
|
||||
var vHead = tess.mesh.vHead;
|
||||
for (var v = vHead.next; v !== vHead; v = v.next) {
|
||||
v.t = - v.t;
|
||||
}
|
||||
tess.tUnit[0] = -tess.tUnit[0];
|
||||
tess.tUnit[1] = -tess.tUnit[1];
|
||||
tess.tUnit[2] = -tess.tUnit[2];
|
||||
}
|
||||
};
|
||||
85
src/libtess.js/priorityq/PQHandleElem.js
Normal file
85
src/libtess.js/priorityq/PQHandleElem.js
Normal file
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* 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 Eric Veach, July 1994
|
||||
* @author Brendan Kenny
|
||||
*/
|
||||
|
||||
// require libtess
|
||||
/*global libtess */
|
||||
|
||||
// TODO(bckenny): more specific typing on key
|
||||
|
||||
/**
|
||||
* [PQHandleElem description]
|
||||
* @constructor
|
||||
*/
|
||||
libtess.PQHandleElem = function() {
|
||||
// TODO(bckenny): if key could instead be an indexed into another store, makes heap storage a lot easier
|
||||
|
||||
/**
|
||||
* [key description]
|
||||
* @type {libtess.PQKey}
|
||||
*/
|
||||
this.key = null;
|
||||
|
||||
/**
|
||||
* [node description]
|
||||
* @type {libtess.PQHandle}
|
||||
*/
|
||||
this.node = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Allocate a PQHandleElem array of size size. If oldArray is not null, its
|
||||
* contents are copied to the beginning of the new array. The rest of the array
|
||||
* is filled with new PQHandleElems.
|
||||
*
|
||||
* @param {?Array.<libtess.PQHandleElem>} oldArray [description]
|
||||
* @param {number} size [description]
|
||||
* @return {Array.<libtess.PQHandleElem>} [description]
|
||||
*/
|
||||
libtess.PQHandleElem.realloc = function(oldArray, size) {
|
||||
var newArray = new Array(size);
|
||||
|
||||
// TODO(bckenny): better to reallocate array? or grow array?
|
||||
var index = 0;
|
||||
if (oldArray !== null) {
|
||||
for (; index < oldArray.length; index++) {
|
||||
newArray[index] = oldArray[index];
|
||||
}
|
||||
}
|
||||
|
||||
for (; index < size; index++) {
|
||||
newArray[index] = new libtess.PQHandleElem();
|
||||
}
|
||||
|
||||
return newArray;
|
||||
};
|
||||
78
src/libtess.js/priorityq/PQNode.js
Normal file
78
src/libtess.js/priorityq/PQNode.js
Normal file
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* 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 Eric Veach, July 1994
|
||||
* @author Brendan Kenny
|
||||
*/
|
||||
|
||||
// require libtess
|
||||
/*global libtess */
|
||||
|
||||
// TODO(bckenny): maybe just have these created inline as literals
|
||||
// (or unboxed directly - PQHandle is just an array index number)
|
||||
|
||||
/**
|
||||
* [PQNode description]
|
||||
* @constructor
|
||||
*/
|
||||
libtess.PQNode = function() {
|
||||
/**
|
||||
* [handle description]
|
||||
* @type {libtess.PQHandle}
|
||||
*/
|
||||
this.handle = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Allocate a PQNode array of size size. If oldArray is not null, its contents
|
||||
* are copied to the beginning of the new array. The rest of the array is
|
||||
* filled with new PQNodes.
|
||||
*
|
||||
* @param {?Array.<libtess.PQNode>} oldArray [description]
|
||||
* @param {number} size [description]
|
||||
* @return {Array.<libtess.PQNode>} [description]
|
||||
*/
|
||||
libtess.PQNode.realloc = function(oldArray, size) {
|
||||
var newArray = new Array(size);
|
||||
|
||||
// TODO(bckenny): better to reallocate array? or grow array?
|
||||
var index = 0;
|
||||
if (oldArray !== null) {
|
||||
for (; index < oldArray.length; index++) {
|
||||
newArray[index] = oldArray[index];
|
||||
}
|
||||
}
|
||||
|
||||
for (; index < size; index++) {
|
||||
newArray[index] = new libtess.PQNode();
|
||||
}
|
||||
|
||||
return newArray;
|
||||
};
|
||||
313
src/libtess.js/priorityq/PriorityQ.js
Normal file
313
src/libtess.js/priorityq/PriorityQ.js
Normal file
@@ -0,0 +1,313 @@
|
||||
/**
|
||||
* 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 Eric Veach, July 1994
|
||||
* @author Brendan Kenny
|
||||
*/
|
||||
|
||||
// require libtess
|
||||
// require libtess.PriorityQHeap
|
||||
/*global libtess */
|
||||
|
||||
// TODO(bckenny): preallocating arrays may actually be hurting us in sort
|
||||
// performance (esp if theres some undefs in there)
|
||||
|
||||
/**
|
||||
* [PriorityQ description]
|
||||
* @constructor
|
||||
* @param {function(Object, Object): boolean} leq [description]
|
||||
*/
|
||||
libtess.PriorityQ = function(leq) {
|
||||
/**
|
||||
* [keys description]
|
||||
* @private
|
||||
* @type {Array.<libtess.PQKey>}
|
||||
*/
|
||||
this.keys_ = libtess.PriorityQ.prototype.PQKeyRealloc_(null, libtess.PriorityQ.INIT_SIZE_);
|
||||
|
||||
/**
|
||||
* Array of indexes into this.keys_
|
||||
* @private
|
||||
* @type {Array.<number>}
|
||||
*/
|
||||
this.order_ = null;
|
||||
|
||||
/**
|
||||
* [size description]
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
this.size_ = 0;
|
||||
|
||||
/**
|
||||
* [max_ description]
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
this.max_ = libtess.PriorityQ.INIT_SIZE_;
|
||||
|
||||
/**
|
||||
* [initialized description]
|
||||
* @private
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.initialized_ = false;
|
||||
|
||||
// TODO(bckenny): leq was inlined by define in original, but appears to just
|
||||
// be vertLeq, as passed. keep an eye on this as to why its not used.
|
||||
/**
|
||||
* [leq description]
|
||||
* @private
|
||||
* @type {function(libtess.PQKey, libtess.PQKey): boolean}
|
||||
*/
|
||||
this.leq_ = /** @type {function(libtess.PQKey, libtess.PQKey): boolean} */(leq);
|
||||
|
||||
/**
|
||||
* [heap_ description]
|
||||
* @private
|
||||
* @type {libtess.PriorityQHeap}
|
||||
*/
|
||||
this.heap_ = new libtess.PriorityQHeap(this.leq_);
|
||||
};
|
||||
|
||||
/**
|
||||
* [INIT_SIZE_ description]
|
||||
* @private
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
libtess.PriorityQ.INIT_SIZE_ = 32;
|
||||
|
||||
/**
|
||||
* [deleteQ description]
|
||||
*/
|
||||
libtess.PriorityQ.prototype.deleteQ = function() {
|
||||
// TODO(bckenny): unnecessary, I think.
|
||||
this.heap_.deleteHeap();
|
||||
this.heap_ = null;
|
||||
this.order_ = null;
|
||||
this.keys_ = null;
|
||||
// NOTE(bckenny): nulled at callsite (sweep.donePriorityQ_)
|
||||
};
|
||||
|
||||
/**
|
||||
* [init description]
|
||||
*/
|
||||
libtess.PriorityQ.prototype.init = function() {
|
||||
// TODO(bckenny): reuse. in theory, we don't have to empty this, as access is
|
||||
// dictated by this.size_, but array.sort doesn't know that
|
||||
this.order_ = [];
|
||||
|
||||
// Create an array of indirect pointers to the keys, so that
|
||||
// the handles we have returned are still valid.
|
||||
// TODO(bckenny): valid for when? it appears we can just store indexes into keys_, but what did this mean?
|
||||
for (var i = 0; i < this.size_; i++) {
|
||||
this.order_[i] = i;
|
||||
}
|
||||
|
||||
// sort the indirect pointers in descending order of the keys themselves
|
||||
// TODO(bckenny): make sure it's ok that keys[a] === keys[b] returns 1
|
||||
// TODO(bckenny): unstable sort means we may get slightly different polys in different
|
||||
// browsers, but only when passing in equal points
|
||||
// TODO(bckenny): make less awkward closure?
|
||||
var comparator = (function(keys, leq) {
|
||||
return function(a, b) {
|
||||
return leq(keys[a], keys[b]) ? 1 : -1;
|
||||
};
|
||||
})(this.keys_, this.leq_);
|
||||
this.order_.sort(comparator);
|
||||
|
||||
this.max_ = this.size_;
|
||||
this.initialized_ = true;
|
||||
this.heap_.init();
|
||||
|
||||
// TODO(bckenny):
|
||||
// #ifndef NDEBUG
|
||||
if (libtess.DEBUG) {
|
||||
var p = 0;
|
||||
var r = p + this.size_ - 1;
|
||||
for (i = p; i < r; ++i) {
|
||||
libtess.assert(this.leq_(this.keys_[this.order_[i+1]], this.keys_[this.order_[i]]));
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
};
|
||||
|
||||
/**
|
||||
* [insert description]
|
||||
* @param {libtess.PQKey} keyNew [description]
|
||||
* @return {libtess.PQHandle} [description]
|
||||
*/
|
||||
libtess.PriorityQ.prototype.insert = function(keyNew) {
|
||||
// NOTE(bckenny): originally returned LONG_MAX as alloc failure signal. no longer does.
|
||||
if (this.initialized_) {
|
||||
return this.heap_.insert(keyNew);
|
||||
}
|
||||
|
||||
var curr = this.size_;
|
||||
if (++this.size_ >= this.max_) {
|
||||
// If the heap overflows, double its size.
|
||||
this.max_ *= 2;
|
||||
this.keys_ = libtess.PriorityQ.prototype.PQKeyRealloc_(this.keys_, this.max_);
|
||||
}
|
||||
|
||||
this.keys_[curr] = keyNew;
|
||||
|
||||
// Negative handles index the sorted array.
|
||||
return -(curr+1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Allocate a PQKey array of size size. If oldArray is not null, its
|
||||
* contents are copied to the beginning of the new array. The rest of the array
|
||||
* is filled with nulls.
|
||||
*
|
||||
* @private
|
||||
* @param {?Array.<libtess.PQKey>} oldArray [description]
|
||||
* @param {number} size [description]
|
||||
* @return {Array.<(?libtess.PQKey)>} [description]
|
||||
*/
|
||||
libtess.PriorityQ.prototype.PQKeyRealloc_ = function(oldArray, size) {
|
||||
// TODO(bckenny): double check return type. can we have ? there?
|
||||
var newArray = new Array(size);
|
||||
|
||||
// TODO(bckenny): better to reallocate array? or grow array?
|
||||
var index = 0;
|
||||
if (oldArray !== null) {
|
||||
for (; index < oldArray.length; index++) {
|
||||
newArray[index] = oldArray[index];
|
||||
}
|
||||
}
|
||||
|
||||
for (; index < size; index++) {
|
||||
newArray[index] = null;
|
||||
}
|
||||
|
||||
return newArray;
|
||||
};
|
||||
|
||||
/**
|
||||
* [keyLessThan_ description]
|
||||
* @private
|
||||
* @param {number} x [description]
|
||||
* @param {number} y [description]
|
||||
* @return {boolean} [description]
|
||||
*/
|
||||
libtess.PriorityQ.prototype.keyLessThan_ = function(x, y) {
|
||||
// NOTE(bckenny): was macro LT
|
||||
var keyX = this.keys_[x];
|
||||
var keyY = this.keys_[y];
|
||||
return !this.leq_(keyY, keyX);
|
||||
};
|
||||
|
||||
/**
|
||||
* [keyGreaterThan_ description]
|
||||
* @private
|
||||
* @param {number} x [description]
|
||||
* @param {number} y [description]
|
||||
* @return {boolean} [description]
|
||||
*/
|
||||
libtess.PriorityQ.prototype.keyGreaterThan_ = function(x, y) {
|
||||
// NOTE(bckenny): was macro GT
|
||||
var keyX = this.keys_[x];
|
||||
var keyY = this.keys_[y];
|
||||
return !this.leq_(keyX, keyY);
|
||||
};
|
||||
|
||||
/**
|
||||
* [extractMin description]
|
||||
* @return {libtess.PQKey} [description]
|
||||
*/
|
||||
libtess.PriorityQ.prototype.extractMin = function() {
|
||||
if (this.size_ === 0) {
|
||||
return this.heap_.extractMin();
|
||||
}
|
||||
|
||||
var sortMin = this.keys_[this.order_[this.size_-1]];
|
||||
if (!this.heap_.isEmpty()) {
|
||||
var heapMin = this.heap_.minimum();
|
||||
if (this.leq_(heapMin, sortMin)) {
|
||||
return this.heap_.extractMin();
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
--this.size_;
|
||||
} while(this.size_ > 0 && this.keys_[this.order_[this.size_-1]] === null);
|
||||
|
||||
return sortMin;
|
||||
};
|
||||
|
||||
/**
|
||||
* [minimum description]
|
||||
* @return {libtess.PQKey} [description]
|
||||
*/
|
||||
libtess.PriorityQ.prototype.minimum = function() {
|
||||
if (this.size_ === 0) {
|
||||
return this.heap_.minimum();
|
||||
}
|
||||
|
||||
var sortMin = this.keys_[this.order_[this.size_-1]];
|
||||
if (!this.heap_.isEmpty()) {
|
||||
var heapMin = this.heap_.minimum();
|
||||
if (this.leq_(heapMin, sortMin)) {
|
||||
return heapMin;
|
||||
}
|
||||
}
|
||||
|
||||
return sortMin;
|
||||
};
|
||||
|
||||
/**
|
||||
* [isEmpty description]
|
||||
* @return {boolean} [description]
|
||||
*/
|
||||
libtess.PriorityQ.prototype.isEmpty = function() {
|
||||
return (this.size_ === 0) && this.heap_.isEmpty();
|
||||
};
|
||||
|
||||
/**
|
||||
* [remove description]
|
||||
* @param {libtess.PQHandle} curr [description]
|
||||
*/
|
||||
libtess.PriorityQ.prototype.remove = function(curr) {
|
||||
if (curr >= 0) {
|
||||
this.heap_.remove(curr);
|
||||
return;
|
||||
}
|
||||
curr = -(curr+1);
|
||||
|
||||
libtess.assert(curr < this.max_ && this.keys_[curr] !== null);
|
||||
|
||||
this.keys_[curr] = null;
|
||||
while(this.size_ > 0 && this.keys_[this.order_[this.size_-1]] === null) {
|
||||
--this.size_;
|
||||
}
|
||||
};
|
||||
300
src/libtess.js/priorityq/PriorityQHeap.js
Normal file
300
src/libtess.js/priorityq/PriorityQHeap.js
Normal file
@@ -0,0 +1,300 @@
|
||||
/**
|
||||
* 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 Eric Veach, July 1994
|
||||
* @author Brendan Kenny
|
||||
*/
|
||||
|
||||
// require libtess
|
||||
// require libtess.PQNode
|
||||
// require libtess.PQHandleElem
|
||||
/*global libtess */
|
||||
|
||||
// TODO(bckenny): keys appear to always be GluVertex in this case?
|
||||
|
||||
/**
|
||||
* [PriorityQHeap description]
|
||||
* @constructor
|
||||
* @param {function(libtess.PQKey, libtess.PQKey): boolean} leq [description]
|
||||
*/
|
||||
libtess.PriorityQHeap = function(leq) {
|
||||
/**
|
||||
* The heap itself. Active nodes are stored in the range 1..size. Each node
|
||||
* stores only an index into handles.
|
||||
* @private
|
||||
* @type {Array.<libtess.PQNode>}
|
||||
*/
|
||||
this.nodes_ = libtess.PQNode.realloc(null, libtess.PriorityQHeap.INIT_SIZE_ + 1);
|
||||
|
||||
/**
|
||||
* Each handle stores a key, plus a pointer back to the node which currently
|
||||
* represents that key (ie. nodes[handles[i].node].handle == i).
|
||||
* @private
|
||||
* @type {Array.<libtess.PQHandleElem>}
|
||||
*/
|
||||
this.handles_ = libtess.PQHandleElem.realloc(null, libtess.PriorityQHeap.INIT_SIZE_ + 1);
|
||||
|
||||
// TODO(bckenny): size and max should probably be libtess.PQHandle for correct typing (see PriorityQ.js)
|
||||
/**
|
||||
* The size of the queue.
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
this.size_ = 0;
|
||||
|
||||
/**
|
||||
* The queue's current allocated space.
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
this.max_ = libtess.PriorityQHeap.INIT_SIZE_;
|
||||
|
||||
/**
|
||||
* The index of the next free hole in the handles array. Handle in that slot
|
||||
* has next item in freeList in its node propert. If there are no holes,
|
||||
* freeList === 0 and one at the end of handles must be use.
|
||||
* @private
|
||||
* @type {libtess.PQHandle}
|
||||
*/
|
||||
this.freeList_ = 0;
|
||||
|
||||
/**
|
||||
* Indicates that the heap has been initialized via init. If false, inserts
|
||||
* are fast insertions at the end of a list. If true, all inserts will now be
|
||||
* correctly ordered in the queue before returning.
|
||||
* @private
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.initialized_ = false;
|
||||
|
||||
// TODO(bckenny): leq was inlined by define in original, but appears to
|
||||
// be vertLeq, as passed. Using injected version, but is it better just to manually inline?
|
||||
/**
|
||||
* [leq description]
|
||||
* @private
|
||||
* @type {function(libtess.PQKey, libtess.PQKey): boolean}
|
||||
*/
|
||||
this.leq_ = leq;
|
||||
|
||||
// so that minimum returns null
|
||||
this.nodes_[1].handle = 1;
|
||||
};
|
||||
|
||||
/**
|
||||
* [INIT_SIZE_ description]
|
||||
* @private
|
||||
* @const
|
||||
* @type {number}
|
||||
*/
|
||||
libtess.PriorityQHeap.INIT_SIZE_ = 32;
|
||||
|
||||
/**
|
||||
* [deleteHeap description]
|
||||
*/
|
||||
libtess.PriorityQHeap.prototype.deleteHeap = function() {
|
||||
// TODO(bckenny): unnecessary, I think.
|
||||
this.handles_ = null;
|
||||
this.nodes_ = null;
|
||||
// NOTE(bckenny): nulled at callsite in PriorityQ.deleteQ
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializing ordering of the heap. Must be called before any method other than
|
||||
* insert is called to ensure correctness when removing or querying.
|
||||
*/
|
||||
libtess.PriorityQHeap.prototype.init = function() {
|
||||
// This method of building a heap is O(n), rather than O(n lg n).
|
||||
for(var i = this.size_; i >= 1; --i) {
|
||||
this.floatDown_(i);
|
||||
}
|
||||
|
||||
this.initialized_ = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Insert a new key into the heap.
|
||||
* @param {libtess.PQKey} keyNew The key to insert.
|
||||
* @return {libtess.PQHandle} A handle that can be used to remove the key.
|
||||
*/
|
||||
libtess.PriorityQHeap.prototype.insert = function(keyNew) {
|
||||
var curr = ++this.size_;
|
||||
|
||||
// if the heap overflows, double its size.
|
||||
if ((curr*2) > this.max_) {
|
||||
this.max_ *= 2;
|
||||
this.nodes_ = libtess.PQNode.realloc(this.nodes_, this.max_ + 1);
|
||||
this.handles_ = libtess.PQHandleElem.realloc(this.handles_, this.max_ + 1);
|
||||
}
|
||||
|
||||
var free;
|
||||
if (this.freeList_ === 0) {
|
||||
free = curr;
|
||||
} else {
|
||||
free = this.freeList_;
|
||||
this.freeList_ = this.handles_[free].node;
|
||||
}
|
||||
|
||||
this.nodes_[curr].handle = free;
|
||||
this.handles_[free].node = curr;
|
||||
this.handles_[free].key = keyNew;
|
||||
|
||||
if (this.initialized_) {
|
||||
this.floatUp_(curr);
|
||||
}
|
||||
|
||||
return free;
|
||||
};
|
||||
|
||||
/**
|
||||
* @return {boolean} Whether the heap is empty.
|
||||
*/
|
||||
libtess.PriorityQHeap.prototype.isEmpty = function() {
|
||||
return this.size_ === 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the minimum key in the heap. If the heap is empty, null will be
|
||||
* returned.
|
||||
* @return {libtess.PQKey} [description]
|
||||
*/
|
||||
libtess.PriorityQHeap.prototype.minimum = function() {
|
||||
return this.handles_[this.nodes_[1].handle].key;
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the minimum key from the heap and returns it. If the heap is empty,
|
||||
* null will be returned.
|
||||
* @return {libtess.PQKey} [description]
|
||||
*/
|
||||
libtess.PriorityQHeap.prototype.extractMin = function() {
|
||||
var n = this.nodes_;
|
||||
var h = this.handles_;
|
||||
var hMin = n[1].handle;
|
||||
var min = h[hMin].key;
|
||||
|
||||
if (this.size_ > 0) {
|
||||
n[1].handle = n[this.size_].handle;
|
||||
h[n[1].handle].node = 1;
|
||||
|
||||
h[hMin].key = null;
|
||||
h[hMin].node = this.freeList_;
|
||||
this.freeList_ = hMin;
|
||||
|
||||
if (--this.size_ > 0 ) {
|
||||
this.floatDown_(1);
|
||||
}
|
||||
}
|
||||
|
||||
return min;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove key associated with handle hCurr (returned from insert) from heap.
|
||||
* @param {libtess.PQHandle} hCurr [description]
|
||||
*/
|
||||
libtess.PriorityQHeap.prototype.remove = function(hCurr) {
|
||||
var n = this.nodes_;
|
||||
var h = this.handles_;
|
||||
|
||||
libtess.assert(hCurr >= 1 && hCurr <= this.max_ && h[hCurr].key !== null);
|
||||
|
||||
var curr = h[hCurr].node;
|
||||
n[curr].handle = n[this.size_].handle;
|
||||
h[n[curr].handle].node = curr;
|
||||
|
||||
if (curr <= --this.size_) {
|
||||
if (curr <= 1 || this.leq_(h[n[curr>>1].handle].key, h[n[curr].handle].key)) {
|
||||
this.floatDown_(curr);
|
||||
} else {
|
||||
this.floatUp_(curr);
|
||||
}
|
||||
}
|
||||
|
||||
h[hCurr].key = null;
|
||||
h[hCurr].node = this.freeList_;
|
||||
this.freeList_ = hCurr;
|
||||
};
|
||||
|
||||
/**
|
||||
* [floatDown_ description]
|
||||
* @private
|
||||
* @param {libtess.PQHandle} curr [description]
|
||||
*/
|
||||
libtess.PriorityQHeap.prototype.floatDown_ = function(curr) {
|
||||
var n = this.nodes_;
|
||||
var h = this.handles_;
|
||||
|
||||
var hCurr = n[curr].handle;
|
||||
for( ;; ) {
|
||||
// The children of node i are nodes 2i and 2i+1.
|
||||
// set child to the index of the child with the minimum key
|
||||
var child = curr << 1;
|
||||
if (child < this.size_ && this.leq_(h[n[child+1].handle].key, h[n[child].handle].key)) {
|
||||
++child;
|
||||
}
|
||||
|
||||
libtess.assert(child <= this.max_);
|
||||
|
||||
var hChild = n[child].handle;
|
||||
if (child > this.size_ || this.leq_(h[hCurr].key, h[hChild].key)) {
|
||||
n[curr].handle = hCurr;
|
||||
h[hCurr].node = curr;
|
||||
break;
|
||||
}
|
||||
n[curr].handle = hChild;
|
||||
h[hChild].node = curr;
|
||||
curr = child;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* [floatUp_ description]
|
||||
* @private
|
||||
* @param {libtess.PQHandle} curr [description]
|
||||
*/
|
||||
libtess.PriorityQHeap.prototype.floatUp_ = function(curr) {
|
||||
var n = this.nodes_;
|
||||
var h = this.handles_;
|
||||
|
||||
var hCurr = n[curr].handle;
|
||||
for( ;; ) {
|
||||
var parent = curr >> 1;
|
||||
var hParent = n[parent].handle;
|
||||
if (parent === 0 || this.leq_(h[hParent].key, h[hCurr].key)) {
|
||||
n[curr].handle = hCurr;
|
||||
h[hCurr].node = curr;
|
||||
break;
|
||||
}
|
||||
|
||||
n[curr].handle = hParent;
|
||||
h[hParent].node = curr;
|
||||
curr = parent;
|
||||
}
|
||||
};
|
||||
591
src/libtess.js/render.js
Normal file
591
src/libtess.js/render.js
Normal file
@@ -0,0 +1,591 @@
|
||||
/**
|
||||
* 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 Eric Veach, July 1994
|
||||
* @author Brendan Kenny
|
||||
*/
|
||||
|
||||
// require libtess
|
||||
// require libtess.CachedVertex
|
||||
// require libtess.GluTesselator
|
||||
// require libtess.GluFace
|
||||
// require libtess.GluHalfEdge
|
||||
// require libtess.GluMesh
|
||||
/*global libtess */
|
||||
|
||||
// TODO(bckenny): most of these doc strings are probably more internal comments
|
||||
|
||||
libtess.render = function() {
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* [SIGN_INCONSISTENT_ description]
|
||||
* @type {number}
|
||||
* @private
|
||||
* @const
|
||||
*/
|
||||
libtess.render.SIGN_INCONSISTENT_ = 2;
|
||||
|
||||
/**
|
||||
* render.renderMesh(tess, mesh) takes a mesh and breaks it into triangle
|
||||
* fans, strips, and separate triangles. A substantial effort is made
|
||||
* to use as few rendering primitives as possible (i.e. to make the fans
|
||||
* and strips as large as possible).
|
||||
*
|
||||
* The rendering output is provided as callbacks (see the api).
|
||||
*
|
||||
* @param {libtess.GluTesselator} tess [description]
|
||||
* @param {libtess.GluMesh} mesh [description]
|
||||
*/
|
||||
libtess.render.renderMesh = function(tess, mesh) {
|
||||
// Make a list of separate triangles so we can render them all at once
|
||||
tess.lonelyTriList = null;
|
||||
|
||||
var f;
|
||||
for(f = mesh.fHead.next; f !== mesh.fHead; f = f.next) {
|
||||
f.marked = false;
|
||||
}
|
||||
for(f = mesh.fHead.next; f !== mesh.fHead; f = f.next) {
|
||||
// We examine all faces in an arbitrary order. Whenever we find
|
||||
// an unprocessed face F, we output a group of faces including F
|
||||
// whose size is maximum.
|
||||
if (f.inside && ! f.marked) {
|
||||
libtess.render.renderMaximumFaceGroup_(tess, f);
|
||||
libtess.assert(f.marked);
|
||||
}
|
||||
}
|
||||
if (tess.lonelyTriList !== null) {
|
||||
libtess.render.renderLonelyTriangles_(tess, tess.lonelyTriList);
|
||||
tess.lonelyTriList = null;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* render.renderBoundary(tess, mesh) takes a mesh, and outputs one
|
||||
* contour for each face marked "inside". The rendering output is
|
||||
* provided as callbacks (see the api).
|
||||
*
|
||||
* @param {libtess.GluTesselator} tess [description]
|
||||
* @param {libtess.GluMesh} mesh [description]
|
||||
*/
|
||||
libtess.render.renderBoundary = function(tess, mesh) {
|
||||
for (var f = mesh.fHead.next; f !== mesh.fHead; f = f.next) {
|
||||
if (f.inside) {
|
||||
tess.callBeginOrBeginData(libtess.primitiveType.GL_LINE_LOOP);
|
||||
|
||||
var e = f.anEdge;
|
||||
do {
|
||||
tess.callVertexOrVertexData(e.org.data);
|
||||
e = e.lNext;
|
||||
} while (e !== f.anEdge);
|
||||
|
||||
tess.callEndOrEndData();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* render.renderCache(tess) takes a single contour and tries to render it
|
||||
* as a triangle fan. This handles convex polygons, as well as some
|
||||
* non-convex polygons if we get lucky.
|
||||
*
|
||||
* Returns true if the polygon was successfully rendered. The rendering
|
||||
* output is provided as callbacks (see the api).
|
||||
*
|
||||
* @param {libtess.GluTesselator} tess [description]
|
||||
* @return {boolean} [description]
|
||||
*/
|
||||
libtess.render.renderCache = function(tess) {
|
||||
if (tess.cacheCount < 3) {
|
||||
// degenerate contour -- no output
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO(bckenny): better init?
|
||||
var norm = [0, 0, 0];
|
||||
norm[0] = tess.normal[0];
|
||||
norm[1] = tess.normal[1];
|
||||
norm[2] = tess.normal[2];
|
||||
if (norm[0] === 0 && norm[1] === 0 && norm[2] === 0) {
|
||||
libtess.render.computeNormal_(tess, norm, false);
|
||||
}
|
||||
|
||||
var sign = libtess.render.computeNormal_(tess, norm, true);
|
||||
if (sign === libtess.render.SIGN_INCONSISTENT_) {
|
||||
// fan triangles did not have a consistent orientation
|
||||
return false;
|
||||
}
|
||||
if (sign === 0) {
|
||||
// all triangles were degenerate
|
||||
return true;
|
||||
}
|
||||
|
||||
// make sure we do the right thing for each winding rule
|
||||
switch(tess.windingRule) {
|
||||
case libtess.windingRule.GLU_TESS_WINDING_ODD:
|
||||
case libtess.windingRule.GLU_TESS_WINDING_NONZERO:
|
||||
break;
|
||||
case libtess.windingRule.GLU_TESS_WINDING_POSITIVE:
|
||||
if (sign < 0) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case libtess.windingRule.GLU_TESS_WINDING_NEGATIVE:
|
||||
if (sign > 0) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case libtess.windingRule.GLU_TESS_WINDING_ABS_GEQ_TWO:
|
||||
return true;
|
||||
}
|
||||
|
||||
tess.callBeginOrBeginData(tess.boundaryOnly ?
|
||||
libtess.primitiveType.GL_LINE_LOOP : (tess.cacheCount > 3) ?
|
||||
libtess.primitiveType.GL_TRIANGLE_FAN : libtess.primitiveType.GL_TRIANGLES);
|
||||
|
||||
// indexes into tess.cache to replace pointers
|
||||
// TODO(bckenny): refactor to be more straightforward
|
||||
var v0 = 0;
|
||||
var vn = v0 + tess.cacheCount;
|
||||
var vc;
|
||||
|
||||
tess.callVertexOrVertexData(tess.cache[v0].data);
|
||||
if (sign > 0) {
|
||||
for (vc = v0+1; vc < vn; ++vc) {
|
||||
tess.callVertexOrVertexData(tess.cache[vc].data);
|
||||
}
|
||||
} else {
|
||||
for(vc = vn-1; vc > v0; --vc) {
|
||||
tess.callVertexOrVertexData(tess.cache[vc].data);
|
||||
}
|
||||
}
|
||||
tess.callEndOrEndData();
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if face has been marked temporarily.
|
||||
* @private
|
||||
* @param {libtess.GluFace} f [description]
|
||||
* @return {boolean} [description]
|
||||
*/
|
||||
libtess.render.marked_ = function(f) {
|
||||
// NOTE(bckenny): originally macro
|
||||
return (!f.inside || f.marked);
|
||||
};
|
||||
|
||||
/**
|
||||
* [freeTrail description]
|
||||
* @private
|
||||
* @param {libtess.GluFace} t [description]
|
||||
*/
|
||||
libtess.render.freeTrail_ = function(t) {
|
||||
// NOTE(bckenny): originally macro
|
||||
while (t !== null) {
|
||||
t.marked = false;
|
||||
t = t.trail;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* eOrig.lFace is the face we want to render. We want to find the size
|
||||
* of a maximal fan around eOrig.org. To do this we just walk around
|
||||
* the origin vertex as far as possible in both directions.
|
||||
* @private
|
||||
* @param {libtess.GluHalfEdge} eOrig [description]
|
||||
* @return {libtess.FaceCount} [description]
|
||||
*/
|
||||
libtess.render.maximumFan_ = function(eOrig) {
|
||||
// TODO(bckenny): probably have dest FaceCount passed in (see renderMaximumFaceGroup)
|
||||
var newFace = new libtess.FaceCount(0, null, libtess.render.renderFan_);
|
||||
|
||||
var trail = null;
|
||||
var e;
|
||||
|
||||
for(e = eOrig; !libtess.render.marked_(e.lFace); e = e.oNext) {
|
||||
// NOTE(bckenny): AddToTrail(e.lFace, trail) macro
|
||||
e.lFace.trail = trail;
|
||||
trail = e.lFace;
|
||||
e.lFace.marked = true;
|
||||
|
||||
++newFace.size;
|
||||
}
|
||||
for(e = eOrig; !libtess.render.marked_(e.rFace()); e = e.oPrev()) {
|
||||
// NOTE(bckenny): AddToTrail(e.rFace(), trail) macro
|
||||
e.rFace().trail = trail;
|
||||
trail = e.rFace();
|
||||
e.rFace().marked = true;
|
||||
|
||||
++newFace.size;
|
||||
}
|
||||
newFace.eStart = e;
|
||||
|
||||
libtess.render.freeTrail_(trail);
|
||||
return newFace;
|
||||
};
|
||||
|
||||
/**
|
||||
* Here we are looking for a maximal strip that contains the vertices
|
||||
* eOrig.org, eOrig.dst(), eOrig.lNext.dst() (in that order or the
|
||||
* reverse, such that all triangles are oriented CCW).
|
||||
*
|
||||
* Again we walk forward and backward as far as possible. However for
|
||||
* strips there is a twist: to get CCW orientations, there must be
|
||||
* an *even* number of triangles in the strip on one side of eOrig.
|
||||
* We walk the strip starting on a side with an even number of triangles;
|
||||
* if both side have an odd number, we are forced to shorten one side.
|
||||
* @private
|
||||
* @param {libtess.GluHalfEdge} eOrig [description]
|
||||
* @return {libtess.FaceCount} [description]
|
||||
*/
|
||||
libtess.render.maximumStrip_ = function(eOrig) {
|
||||
// TODO(bckenny): probably have dest FaceCount passed in (see renderMaximumFaceGroup)
|
||||
var newFace = new libtess.FaceCount(0, null, libtess.render.renderStrip_);
|
||||
|
||||
var headSize = 0;
|
||||
var tailSize = 0;
|
||||
|
||||
var trail = null;
|
||||
|
||||
var e;
|
||||
var eTail;
|
||||
var eHead;
|
||||
|
||||
for (e = eOrig; !libtess.render.marked_(e.lFace); ++tailSize, e = e.oNext) {
|
||||
// NOTE(bckenny): AddToTrail(e.lFace, trail) macro
|
||||
e.lFace.trail = trail;
|
||||
trail = e.lFace;
|
||||
e.lFace.marked = true;
|
||||
|
||||
++tailSize;
|
||||
e = e.dPrev();
|
||||
if (libtess.render.marked_(e.lFace)) {
|
||||
break;
|
||||
}
|
||||
// NOTE(bckenny): AddToTrail(e.lFace, trail) macro
|
||||
e.lFace.trail = trail;
|
||||
trail = e.lFace;
|
||||
e.lFace.marked = true;
|
||||
}
|
||||
eTail = e;
|
||||
|
||||
for (e = eOrig; !libtess.render.marked_(e.rFace()); ++headSize, e = e.dNext()) {
|
||||
// NOTE(bckenny): AddToTrail(e.rFace(), trail) macro
|
||||
e.rFace().trail = trail;
|
||||
trail = e.rFace();
|
||||
e.rFace().marked = true;
|
||||
|
||||
++headSize;
|
||||
e = e.oPrev();
|
||||
if (libtess.render.marked_(e.rFace())) {
|
||||
break;
|
||||
}
|
||||
// NOTE(bckenny): AddToTrail(e.rFace(), trail) macro
|
||||
e.rFace().trail = trail;
|
||||
trail = e.rFace();
|
||||
e.rFace().marked = true;
|
||||
}
|
||||
eHead = e;
|
||||
|
||||
newFace.size = tailSize + headSize;
|
||||
if ((tailSize & 1) === 0) { // isEven
|
||||
newFace.eStart = eTail.sym;
|
||||
|
||||
} else if ((headSize & 1) === 0) { // isEven
|
||||
newFace.eStart = eHead;
|
||||
|
||||
} else {
|
||||
// Both sides have odd length, we must shorten one of them. In fact,
|
||||
// we must start from eHead to guarantee inclusion of eOrig.lFace.
|
||||
--newFace.size;
|
||||
newFace.eStart = eHead.oNext;
|
||||
}
|
||||
|
||||
libtess.render.freeTrail_(trail);
|
||||
return newFace;
|
||||
};
|
||||
|
||||
/**
|
||||
* Render as many CCW triangles as possible in a fan starting from
|
||||
* edge "e". The fan *should* contain exactly "size" triangles
|
||||
* (otherwise we've goofed up somewhere).
|
||||
* @private
|
||||
* @param {libtess.GluTesselator} tess [description]
|
||||
* @param {libtess.GluHalfEdge} e [description]
|
||||
* @param {number} size [description]
|
||||
*/
|
||||
libtess.render.renderFan_ = function(tess, e, size) {
|
||||
tess.callBeginOrBeginData(libtess.primitiveType.GL_TRIANGLE_FAN);
|
||||
tess.callVertexOrVertexData(e.org.data);
|
||||
tess.callVertexOrVertexData(e.dst().data);
|
||||
|
||||
while (!libtess.render.marked_(e.lFace)) {
|
||||
e.lFace.marked = true;
|
||||
--size;
|
||||
e = e.oNext;
|
||||
tess.callVertexOrVertexData(e.dst().data);
|
||||
}
|
||||
|
||||
libtess.assert(size === 0);
|
||||
tess.callEndOrEndData();
|
||||
};
|
||||
|
||||
/**
|
||||
* Render as many CCW triangles as possible in a strip starting from
|
||||
* edge e. The strip *should* contain exactly "size" triangles
|
||||
* (otherwise we've goofed up somewhere).
|
||||
* @private
|
||||
* @param {libtess.GluTesselator} tess [description]
|
||||
* @param {libtess.GluHalfEdge} e [description]
|
||||
* @param {number} size [description]
|
||||
*/
|
||||
libtess.render.renderStrip_ = function(tess, e, size) {
|
||||
tess.callBeginOrBeginData(libtess.primitiveType.GL_TRIANGLE_STRIP);
|
||||
tess.callVertexOrVertexData(e.org.data);
|
||||
tess.callVertexOrVertexData(e.dst().data);
|
||||
|
||||
while (!libtess.render.marked_(e.lFace)) {
|
||||
e.lFace.marked = true;
|
||||
--size;
|
||||
e = e.dPrev();
|
||||
tess.callVertexOrVertexData(e.org.data);
|
||||
if (libtess.render.marked_(e.lFace)) {
|
||||
break;
|
||||
}
|
||||
|
||||
e.lFace.marked = true;
|
||||
--size;
|
||||
e = e.oNext;
|
||||
tess.callVertexOrVertexData(e.dst().data);
|
||||
}
|
||||
|
||||
libtess.assert(size === 0);
|
||||
tess.callEndOrEndData();
|
||||
};
|
||||
|
||||
/**
|
||||
* Just add the triangle to a triangle list, so we can render all
|
||||
* the separate triangles at once.
|
||||
* @private
|
||||
* @param {libtess.GluTesselator} tess [description]
|
||||
* @param {libtess.GluHalfEdge} e [description]
|
||||
* @param {number} size [description]
|
||||
*/
|
||||
libtess.render.renderTriangle_ = function(tess, e, size) {
|
||||
libtess.assert(size === 1);
|
||||
// NOTE(bckenny): AddToTrail(e.lFace, tess.lonelyTriList) macro
|
||||
e.lFace.trail = tess.lonelyTriList;
|
||||
tess.lonelyTriList = e.lFace;
|
||||
e.lFace.marked = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* We want to find the largest triangle fan or strip of unmarked faces
|
||||
* which includes the given face fOrig. There are 3 possible fans
|
||||
* passing through fOrig (one centered at each vertex), and 3 possible
|
||||
* strips (one for each CCW permutation of the vertices). Our strategy
|
||||
* is to try all of these, and take the primitive which uses the most
|
||||
* triangles (a greedy approach).
|
||||
* @private
|
||||
* @param {libtess.GluTesselator} tess [description]
|
||||
* @param {libtess.GluFace} fOrig [description]
|
||||
*/
|
||||
libtess.render.renderMaximumFaceGroup_ = function(tess, fOrig) {
|
||||
var e = fOrig.anEdge;
|
||||
|
||||
// TODO(bckenny): see faceCount comments from below. should probably create
|
||||
// two here and pass one in and compare against the other to find max
|
||||
// maybe doesnt matter since so short lived
|
||||
var max = new libtess.FaceCount(1, e, libtess.render.renderTriangle_);
|
||||
|
||||
var newFace;
|
||||
if (!tess.flagBoundary) {
|
||||
newFace = libtess.render.maximumFan_(e);
|
||||
if (newFace.size > max.size) {
|
||||
max = newFace;
|
||||
}
|
||||
newFace = libtess.render.maximumFan_(e.lNext);
|
||||
if (newFace.size > max.size) {
|
||||
max = newFace;
|
||||
}
|
||||
newFace = libtess.render.maximumFan_(e.lPrev());
|
||||
if (newFace.size > max.size) {
|
||||
max = newFace;
|
||||
}
|
||||
|
||||
newFace = libtess.render.maximumStrip_(e);
|
||||
if (newFace.size > max.size) {
|
||||
max = newFace;
|
||||
}
|
||||
newFace = libtess.render.maximumStrip_(e.lNext);
|
||||
if (newFace.size > max.size) {
|
||||
max = newFace;
|
||||
}
|
||||
newFace = libtess.render.maximumStrip_(e.lPrev());
|
||||
if (newFace.size > max.size) {
|
||||
max = newFace;
|
||||
}
|
||||
}
|
||||
|
||||
max.render(tess, max.eStart, max.size);
|
||||
};
|
||||
|
||||
/**
|
||||
* Now we render all the separate triangles which could not be
|
||||
* grouped into a triangle fan or strip.
|
||||
* @private
|
||||
* @param {libtess.GluTesselator} tess [description]
|
||||
* @param {libtess.GluFace} head [description]
|
||||
*/
|
||||
libtess.render.renderLonelyTriangles_ = function(tess, head) {
|
||||
// TODO(bckenny): edgeState needs to be boolean, but != on first call
|
||||
// force edge state output for first vertex
|
||||
var edgeState = -1;
|
||||
|
||||
var f = head;
|
||||
|
||||
tess.callBeginOrBeginData(libtess.primitiveType.GL_TRIANGLES);
|
||||
|
||||
for(; f !== null; f = f.trail) {
|
||||
// Loop once for each edge (there will always be 3 edges)
|
||||
var e = f.anEdge;
|
||||
do {
|
||||
if (tess.flagBoundary) {
|
||||
// Set the "edge state" to true just before we output the
|
||||
// first vertex of each edge on the polygon boundary.
|
||||
var newState = !e.rFace().inside ? 1 : 0; // TODO(bckenny): total hack to get edgeState working. fix me.
|
||||
if (edgeState !== newState) {
|
||||
edgeState = newState;
|
||||
// TODO(bckenny): edgeState should be boolean now
|
||||
tess.callEdgeFlagOrEdgeFlagData(!!edgeState);
|
||||
}
|
||||
}
|
||||
tess.callVertexOrVertexData(e.org.data);
|
||||
|
||||
e = e.lNext;
|
||||
} while (e !== f.anEdge);
|
||||
}
|
||||
|
||||
tess.callEndOrEndData();
|
||||
};
|
||||
|
||||
/**
|
||||
* If check==false, we compute the polygon normal and place it in norm[].
|
||||
* If check==true, we check that each triangle in the fan from v0 has a
|
||||
* consistent orientation with respect to norm[]. If triangles are
|
||||
* consistently oriented CCW, return 1; if CW, return -1; if all triangles
|
||||
* are degenerate return 0; otherwise (no consistent orientation) return
|
||||
* render.SIGN_INCONSISTENT_.
|
||||
* @private
|
||||
* @param {libtess.GluTesselator} tess [description]
|
||||
* @param {Array.<number>} norm [description]
|
||||
* @param {boolean} check [description]
|
||||
* @return {number} int
|
||||
*/
|
||||
libtess.render.computeNormal_ = function(tess, norm, check) {
|
||||
/* Find the polygon normal. It is important to get a reasonable
|
||||
* normal even when the polygon is self-intersecting (eg. a bowtie).
|
||||
* Otherwise, the computed normal could be very tiny, but perpendicular
|
||||
* to the true plane of the polygon due to numerical noise. Then all
|
||||
* the triangles would appear to be degenerate and we would incorrectly
|
||||
* decompose the polygon as a fan (or simply not render it at all).
|
||||
*
|
||||
* We use a sum-of-triangles normal algorithm rather than the more
|
||||
* efficient sum-of-trapezoids method (used in checkOrientation()
|
||||
* in normal.js). This lets us explicitly reverse the signed area
|
||||
* of some triangles to get a reasonable normal in the self-intersecting
|
||||
* case.
|
||||
*/
|
||||
if (!check) {
|
||||
norm[0] = norm[1] = norm[2] = 0;
|
||||
}
|
||||
|
||||
// indexes into tess.cache to replace pointers
|
||||
// TODO(bckenny): refactor to be more straightforward
|
||||
var v0 = 0;
|
||||
var vn = v0 + tess.cacheCount;
|
||||
var vc = v0 + 1;
|
||||
var vert0 = tess.cache[v0];
|
||||
var vertc = tess.cache[vc];
|
||||
|
||||
var xc = vertc.coords[0] - vert0.coords[0];
|
||||
var yc = vertc.coords[1] - vert0.coords[1];
|
||||
var zc = vertc.coords[2] - vert0.coords[2];
|
||||
|
||||
var sign = 0;
|
||||
while (++vc < vn) {
|
||||
vertc = tess.cache[vc];
|
||||
var xp = xc;
|
||||
var yp = yc;
|
||||
var zp = zc;
|
||||
xc = vertc.coords[0] - vert0.coords[0];
|
||||
yc = vertc.coords[1] - vert0.coords[1];
|
||||
zc = vertc.coords[2] - vert0.coords[2];
|
||||
|
||||
// Compute (vp - v0) cross (vc - v0)
|
||||
var n = [0, 0, 0]; // TODO(bckenny): better init?
|
||||
n[0] = yp*zc - zp*yc;
|
||||
n[1] = zp*xc - xp*zc;
|
||||
n[2] = xp*yc - yp*xc;
|
||||
|
||||
var dot = n[0]*norm[0] + n[1]*norm[1] + n[2]*norm[2];
|
||||
if (!check) {
|
||||
// Reverse the contribution of back-facing triangles to get
|
||||
// a reasonable normal for self-intersecting polygons (see above)
|
||||
if (dot >= 0) {
|
||||
norm[0] += n[0];
|
||||
norm[1] += n[1];
|
||||
norm[2] += n[2];
|
||||
} else {
|
||||
norm[0] -= n[0];
|
||||
norm[1] -= n[1];
|
||||
norm[2] -= n[2];
|
||||
}
|
||||
} else if (dot !== 0) {
|
||||
// Check the new orientation for consistency with previous triangles
|
||||
if (dot > 0) {
|
||||
if (sign < 0) {
|
||||
return libtess.render.SIGN_INCONSISTENT_;
|
||||
}
|
||||
sign = 1;
|
||||
} else {
|
||||
if (sign > 0) {
|
||||
return libtess.render.SIGN_INCONSISTENT_;
|
||||
}
|
||||
sign = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sign;
|
||||
};
|
||||
70
src/libtess.js/render/FaceCount.js
Normal file
70
src/libtess.js/render/FaceCount.js
Normal file
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* 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 Eric Veach, July 1994
|
||||
* @author Brendan Kenny
|
||||
*/
|
||||
|
||||
// require libtess
|
||||
// require libtess.GluHalfEdge
|
||||
// require libtess.GluTesselator
|
||||
/*global libtess */
|
||||
|
||||
// TODO(bckenny): Used only in private functions of render.js
|
||||
|
||||
/**
|
||||
* This structure remembers the information we need about a primitive
|
||||
* to be able to render it later, once we have determined which
|
||||
* primitive is able to use the most triangles.
|
||||
*
|
||||
* @constructor
|
||||
* @param {number} size [description]
|
||||
* @param {libtess.GluHalfEdge} eStart [description]
|
||||
* @param {!function(libtess.GluTesselator, libtess.GluHalfEdge, number)} renderFunction [description]
|
||||
*/
|
||||
libtess.FaceCount = function(size, eStart, renderFunction) {
|
||||
/**
|
||||
* The number of triangles used.
|
||||
* @type {number}
|
||||
*/
|
||||
this.size = size;
|
||||
|
||||
/**
|
||||
* The edge where this primitive starts.
|
||||
* @type {libtess.GluHalfEdge}
|
||||
*/
|
||||
this.eStart = eStart;
|
||||
|
||||
/**
|
||||
* The routine to render this primitive.
|
||||
* @type {!function(libtess.GluTesselator, libtess.GluHalfEdge, number)}
|
||||
*/
|
||||
this.render = renderFunction;
|
||||
};
|
||||
1449
src/libtess.js/sweep.js
Normal file
1449
src/libtess.js/sweep.js
Normal file
File diff suppressed because it is too large
Load Diff
116
src/libtess.js/sweep/ActiveRegion.js
Normal file
116
src/libtess.js/sweep/ActiveRegion.js
Normal file
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* 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 Eric Veach, July 1994
|
||||
* @author Brendan Kenny
|
||||
*/
|
||||
|
||||
|
||||
// require libtess
|
||||
// require libtess.DictNode
|
||||
// require libtess.GluHalfEdge
|
||||
/*global libtess */
|
||||
|
||||
// TODO(bckenny): apparently only visible outside of sweep for debugging routines.
|
||||
// find out if we can hide
|
||||
|
||||
/**
|
||||
* For each pair of adjacent edges crossing the sweep line, there is
|
||||
* an ActiveRegion to represent the region between them. The active
|
||||
* regions are kept in sorted order in a dynamic dictionary. As the
|
||||
* sweep line crosses each vertex, we update the affected regions.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
libtess.ActiveRegion = function() {
|
||||
// TODO(bckenny): I *think* eUp and nodeUp could be passed in as constructor params
|
||||
|
||||
/**
|
||||
* upper edge, directed right to left
|
||||
* @type {libtess.GluHalfEdge}
|
||||
*/
|
||||
this.eUp = null;
|
||||
|
||||
/**
|
||||
* dictionary node corresponding to eUp
|
||||
* @type {libtess.DictNode}
|
||||
*/
|
||||
this.nodeUp = null;
|
||||
|
||||
/**
|
||||
* used to determine which regions are inside the polygon
|
||||
* @type {number}
|
||||
*/
|
||||
this.windingNumber = 0;
|
||||
|
||||
/**
|
||||
* is this region inside the polygon?
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.inside = false;
|
||||
|
||||
/**
|
||||
* marks fake edges at t = +/-infinity
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.sentinel = false;
|
||||
|
||||
/**
|
||||
* Marks regions where the upper or lower edge has changed, but we haven't
|
||||
* checked whether they intersect yet.
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.dirty = false;
|
||||
|
||||
/**
|
||||
* marks temporary edges introduced when we process a "right vertex" (one
|
||||
* without any edges leaving to the right)
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.fixUpperEdge = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* [regionBelow description]
|
||||
* @return {libtess.ActiveRegion} [description]
|
||||
*/
|
||||
libtess.ActiveRegion.prototype.regionBelow = function() {
|
||||
// TODO(bckenny): better typing? or is cast unavoidable
|
||||
return /** @type {libtess.ActiveRegion} */ (this.nodeUp.getPred().getKey());
|
||||
};
|
||||
|
||||
/**
|
||||
* [regionAbove description]
|
||||
* @return {libtess.ActiveRegion} [description]
|
||||
*/
|
||||
libtess.ActiveRegion.prototype.regionAbove = function() {
|
||||
// TODO(bckenny): better typing? or is cast unavoidable
|
||||
return /** @type {libtess.ActiveRegion} */ (this.nodeUp.getSucc().getKey());
|
||||
};
|
||||
193
src/libtess.js/tessmono.js
Normal file
193
src/libtess.js/tessmono.js
Normal file
@@ -0,0 +1,193 @@
|
||||
/**
|
||||
* 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 Eric Veach, July 1994
|
||||
* @author Brendan Kenny
|
||||
*/
|
||||
|
||||
// require libtess
|
||||
// require libtess.geom
|
||||
// require libtess.GluFace
|
||||
// require libtess.GluMesh
|
||||
/*global libtess */
|
||||
|
||||
libtess.tessmono = function() {
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* 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).
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user