UNPKG

polygon-tools

Version:
356 lines (309 loc) 13.9 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.Tesselator = exports.DEFAULT_OPTIONS = exports.GLU_TESS_WINDING_ABS_GEQ_TWO = exports.GLU_TESS_WINDING_NEGATIVE = exports.GLU_TESS_WINDING_POSITIVE = exports.GLU_TESS_WINDING_NONZERO = exports.GLU_TESS_WINDING_ODD = exports.GL_TRIANGLE_FAN = exports.GL_TRIANGLE_STRIP = exports.GL_TRIANGLES = exports.GL_LINE_LOOP = undefined; var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); exports.run = run; var _libtess = require('libtess'); var _polygon = require('./polygon'); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /** * @module tesselator */ var GL_LINE_LOOP = _libtess.primitiveType.GL_LINE_LOOP, GL_TRIANGLES = _libtess.primitiveType.GL_TRIANGLES, GL_TRIANGLE_STRIP = _libtess.primitiveType.GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN = _libtess.primitiveType.GL_TRIANGLE_FAN; exports.GL_LINE_LOOP = GL_LINE_LOOP; exports.GL_TRIANGLES = GL_TRIANGLES; exports.GL_TRIANGLE_STRIP = GL_TRIANGLE_STRIP; exports.GL_TRIANGLE_FAN = GL_TRIANGLE_FAN; var GLU_TESS_WINDING_ODD = _libtess.windingRule.GLU_TESS_WINDING_ODD, GLU_TESS_WINDING_NONZERO = _libtess.windingRule.GLU_TESS_WINDING_NONZERO, GLU_TESS_WINDING_POSITIVE = _libtess.windingRule.GLU_TESS_WINDING_POSITIVE, GLU_TESS_WINDING_NEGATIVE = _libtess.windingRule.GLU_TESS_WINDING_NEGATIVE, GLU_TESS_WINDING_ABS_GEQ_TWO = _libtess.windingRule.GLU_TESS_WINDING_ABS_GEQ_TWO; /** * Tesselator options. * @typedef {Object} TesselatorOptions * @property {Array} [polygons=[]] Array of polygons * @property {Array} [holes=[]] Array of holes * @property {Number} [vertexSize=2] Vertex size to use * @property {Number} [windingRule=GLU_TESS_WINDING_POSITIVE] Winding rule * @property {Boolean} [boundaryOnly=false] Whether to output boundaries only * @property {Array} [normal=null] Normal * @property {Boolean} [autoWinding=true] Whether to automatically set the correct winding on polygons */ exports.GLU_TESS_WINDING_ODD = GLU_TESS_WINDING_ODD; exports.GLU_TESS_WINDING_NONZERO = GLU_TESS_WINDING_NONZERO; exports.GLU_TESS_WINDING_POSITIVE = GLU_TESS_WINDING_POSITIVE; exports.GLU_TESS_WINDING_NEGATIVE = GLU_TESS_WINDING_NEGATIVE; exports.GLU_TESS_WINDING_ABS_GEQ_TWO = GLU_TESS_WINDING_ABS_GEQ_TWO; var DEFAULT_OPTIONS = exports.DEFAULT_OPTIONS = { polygons: [], holes: [], windingRule: GLU_TESS_WINDING_POSITIVE, boundaryOnly: false, normal: null, autoWinding: true }; var Tesselator = exports.Tesselator = function (_GluTesselator) { _inherits(Tesselator, _GluTesselator); function Tesselator() { var vsize = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 2; _classCallCheck(this, Tesselator); var _this = _possibleConstructorReturn(this, (Tesselator.__proto__ || Object.getPrototypeOf(Tesselator)).call(this)); _this._vsize = vsize; _this._current = []; _this._out = []; _this._primitiveType = 0; _this.gluTessCallback(_libtess.gluEnum.GLU_TESS_VERTEX_DATA, _this._vertex); _this.gluTessCallback(_libtess.gluEnum.GLU_TESS_BEGIN, _this._begin); _this.gluTessCallback(_libtess.gluEnum.GLU_TESS_END, _this._end); _this.gluTessCallback(_libtess.gluEnum.GLU_TESS_ERROR, _this._error); _this.gluTessCallback(_libtess.gluEnum.GLU_TESS_COMBINE, _this._combine); _this.gluTessCallback(_libtess.gluEnum.GLU_TESS_EDGE_FLAG, _this._edge); return _this; } _createClass(Tesselator, [{ key: 'start', value: function start(polygons, holes) { this._current = []; this._out = []; this.gluTessBeginPolygon(); var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = polygons[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var poly = _step.value; this.gluTessBeginContour(); var _iteratorNormalCompletion3 = true; var _didIteratorError3 = false; var _iteratorError3 = undefined; try { for (var _iterator3 = poly[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { var v = _step3.value; this.gluTessVertex(v, v); } } catch (err) { _didIteratorError3 = true; _iteratorError3 = err; } finally { try { if (!_iteratorNormalCompletion3 && _iterator3.return) { _iterator3.return(); } } finally { if (_didIteratorError3) { throw _iteratorError3; } } } this.gluTessEndContour(); } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } var _iteratorNormalCompletion2 = true; var _didIteratorError2 = false; var _iteratorError2 = undefined; try { for (var _iterator2 = holes[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { var _poly = _step2.value; this.gluTessBeginContour(); var _iteratorNormalCompletion4 = true; var _didIteratorError4 = false; var _iteratorError4 = undefined; try { for (var _iterator4 = _poly[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) { var _v = _step4.value; this.gluTessVertex(_v, _v); } } catch (err) { _didIteratorError4 = true; _iteratorError4 = err; } finally { try { if (!_iteratorNormalCompletion4 && _iterator4.return) { _iterator4.return(); } } finally { if (_didIteratorError4) { throw _iteratorError4; } } } this.gluTessEndContour(); } } catch (err) { _didIteratorError2 = true; _iteratorError2 = err; } finally { try { if (!_iteratorNormalCompletion2 && _iterator2.return) { _iterator2.return(); } } finally { if (_didIteratorError2) { throw _iteratorError2; } } } this.gluTessEndPolygon(); } }, { key: 'run', value: function run() { var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : DEFAULT_OPTIONS; var opts = Object.assign({}, DEFAULT_OPTIONS, options), polygons = opts.polygons, holes = opts.holes, autoWinding = opts.autoWinding, boundaryOnly = opts.boundaryOnly; if (!polygons || !polygons.length) { throw new Error('need at least a single polygon'); } if (autoWinding) { polygons = polygons.filter(function (p) { return Math.abs((0, _polygon.area)(p)) > 0; }).map(function (p) { if ((0, _polygon.is_cw)(p)) p.reverse(); return p; }); holes = holes.filter(function (p) { return Math.abs((0, _polygon.area)(p)) > 0; }).map(function (p) { if ((0, _polygon.is_ccw)(p)) p.reverse(); return p; }); } var _ref = opts.normal ? opts.normal : (0, _polygon.normal)(polygons[0], true), _ref2 = _slicedToArray(_ref, 3), nx = _ref2[0], ny = _ref2[1], nz = _ref2[2]; this.gluTessNormal(nx, ny, nz); this.gluTessProperty(_libtess.gluEnum.GLU_TESS_BOUNDARY_ONLY, boundaryOnly); this.gluTessProperty(_libtess.gluEnum.GLU_TESS_WINDING_RULE, opts.windingRule); this.start(polygons, holes); return this._out; } }, { key: '_begin', value: function _begin(type) { this._primitiveType = type; this._current = []; } }, { key: '_end_fan', value: function _end_fan() { var c = this._current.shift(), p1 = this._current.shift(); while (this._current.length) { var p2 = this._current.shift(); this._out.push(c, p1, p2); p1 = p2; } } }, { key: '_end_strip', value: function _end_strip() { var p1 = this._current.shift(), p2 = this._current.shift(); while (this._current.length) { var p3 = this._current.shift(); this._out.push(p1, p2, p3); p1 = p2; p2 = p3; } } }, { key: '_end', value: function _end() { switch (this._primitiveType) { case GL_TRIANGLE_FAN: this._end_fan(); break; case GL_TRIANGLE_STRIP: this._end_strip(); break; case GL_TRIANGLES: case GL_LINE_LOOP: default: this._out.push(this._current); break; } } }, { key: '_vertex', value: function _vertex(v) { this._current.push(v); } }, { key: '_edge', value: function _edge() {} }, { key: '_error', value: function _error(errno) { console.error('error number: ' + errno); } }, { key: '_combine', value: function _combine(v, data, w) { for (var i = 0; i < 4; ++i) { if (!data[i]) { data[i] = new Array(this._vsize); for (var j = 0; j < this._vsize; ++j) { data[i][j] = 0; } } } var r = new Array(this._vsize); for (var _i = 0; _i < this._vsize; ++_i) { r[_i] = data[0][_i] * w[0] + data[1][_i] * w[1] + data[2][_i] * w[2] + data[3][_i] * w[3]; } return r; } }]); return Tesselator; }(_libtess.GluTesselator); /** * Helper for triangulate * @private */ function to_triangles(data) { var result = []; for (var i = 0; i < data.length; i += 3) { result.push([data[i], data[i + 1], data[i + 2]]); } return result; } /** * Runs the tesselator * @see http://www.glprogramming.com/red/chapter11.html * * @param {TesselatorOptions} [options=TesselatorOptions] Options * * @returns {Array} */ function run() { var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : DEFAULT_OPTIONS; var tesselator = new Tesselator(options.vertexSize), result = tesselator.run(options); return options.boundaryOnly ? result : result.map(to_triangles); }