diff --git a/README.org b/README.org index e11565f..c56e1b4 100644 --- a/README.org +++ b/README.org @@ -1,12 +1,14 @@ [[http://unmaintained.tech/][http://unmaintained.tech/badge.svg]] +*This is my 2017 library* but in 2023 I abandoned this and switched to a [[https://www.redblobgames.com/x/2312-dual-mesh/][newer library]]. + Dual mesh library for my polygon map generator projects (mapgen2, mapgen4). Feel free to use this, but it's not a stable library and I do make breaking changes. The create.js interface is the most likely to change in the future. This is a wrapper around [[https://mapbox.github.io/delaunator/][Delaunator]]. I wrote the [[https://mapbox.github.io/delaunator/][Delaunator Guide]] based on the code from this project. The code in the guide is easier to read and more general but less efficient than the code in this library. [[https://redblobgames.github.io/dual-mesh/][Documentation is here]], but it's a bit rough. See [[http://www.redblobgames.com/x/1721-voronoi-alternative/][my blog post about centroid polygons]] and [[http://www.redblobgames.com/x/1722-b-rep-triangle-meshes/][my blog post about the dual mesh data structure]] for the history. Those blog posts used the names “seeds, edges, triangles” but now I call them “regions, sides, triangles”, and I use “ghost” elements to eliminate the boundaries. -The naming convention is: =x_name_y= takes type x (r, s, t) as input and produces type y (r, s, t) as output. For example, =s_begin_r= is a function that takes a side (s) as input and returns a region (r), and could be called ~r = mesh.s_begin_r(s)~. +The naming convention is: =x_name_y= takes type x (r, s, t) as input and produces type y (r, s, t) as output. For example, =s_begin_r= is a function that takes a side (s) as input and returns a region (r), and could be called ~r = mesh.s_begin_r(s)~. In 2023 [[https://www.redblobgames.com/x/2312-dual-mesh/][I changed conventions]] to use =y_from_x=, putting the output first. For efficiency, the accessors never allocate new arrays, but take a parameter where the result should be written: diff --git a/docs/_bundle.js b/docs/_bundle.js index be68555..de4666b 100644 --- a/docs/_bundle.js +++ b/docs/_bundle.js @@ -1,1853 +1,1514 @@ -(function(){function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o - * License: Apache v2.0 - * - * Generate a random triangle mesh for the area 0 <= x <= 1000, 0 <= y <= 1000 - * - * This program runs on the command line (node) - */ - -'use strict'; - -let Delaunator = require('delaunator'); // ISC licensed -let TriangleMesh = require('./'); - -function s_next_s(s) { return (s % 3 == 2) ? s-2 : s+1; } - - -function checkPointInequality({_r_vertex, _triangles, _halfedges}) { - // TODO: check for collinear vertices. Around each red point P if - // there's a point Q and R both connected to it, and the angle P→Q and - // the angle P→R are 180° apart, then there's collinearity. This would - // indicate an issue with poisson disc point selection. -} - - -function checkTriangleInequality({_r_vertex, _triangles, _halfedges}) { - // check for skinny triangles - const badAngleLimit = 30; - let summary = new Array(badAngleLimit).fill(0); - let count = 0; - for (let s = 0; s < _triangles.length; s++) { - let r0 = _triangles[s], - r1 = _triangles[s_next_s(s)], - r2 = _triangles[s_next_s(s_next_s(s))]; - let p0 = _r_vertex[r0], - p1 = _r_vertex[r1], - p2 = _r_vertex[r2]; - let d0 = [p0[0]-p1[0], p0[1]-p1[1]]; - let d2 = [p2[0]-p1[0], p2[1]-p1[1]]; - let dotProduct = d0[0] * d2[0] + d0[1] + d2[1]; - let angleDegrees = 180 / Math.PI * Math.acos(dotProduct); - if (angleDegrees < badAngleLimit) { - summary[angleDegrees|0]++; - count++; +"use strict"; +(() => { + var __getOwnPropNames = Object.getOwnPropertyNames; + var __commonJS = (cb, mod) => function __require() { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; + }; + + // index.js + var require_dual_mesh = __commonJS({ + "index.js"(exports, module) { + "use strict"; + var TriangleMesh = class _TriangleMesh { + static s_to_t(s) { + return s / 3 | 0; } - } - // NOTE: a much faster test would be the ratio of the inradius to - // the circumradius, but as I'm generating these offline, I'm not - // worried about speed right now - - // TODO: consider adding circumcenters of skinny triangles to the point set - if (count > 0) { - console.log(' bad angles:', summary.join(" ")); - } -} - - -function checkMeshConnectivity({_r_vertex, _triangles, _halfedges}) { - // 1. make sure each side's opposite is back to itself - // 2. make sure region-circulating starting from each side works - let ghost_r = _r_vertex.length - 1, out_s = []; - for (let s0 = 0; s0 < _triangles.length; s0++) { - if (_halfedges[_halfedges[s0]] !== s0) { - console.log(`FAIL _halfedges[_halfedges[${s0}]] !== ${s0}`); + static s_prev_s(s) { + return s % 3 === 0 ? s + 2 : s - 1; } - let s = s0, count = 0; - out_s.length = 0; - do { - count++; out_s.push(s); - s = s_next_s(_halfedges[s]); - if (count > 100 && _triangles[s0] !== ghost_r) { - console.log(`FAIL to circulate around region with start side=${s0} from region ${_triangles[s0]} to ${_triangles[s_next_s(s0)]}, out_s=${out_s}`); - break; + static s_next_s(s) { + return s % 3 === 2 ? s - 2 : s + 1; + } + /** + * Constructor takes partial mesh information and fills in the rest; the + * partial information is generated in create.js or in fromDelaunator. + */ + constructor({ numBoundaryRegions, numSolidSides, _r_vertex, _triangles, _halfedges }) { + Object.assign(this, { + numBoundaryRegions, + numSolidSides, + _r_vertex, + _triangles, + _halfedges + }); + this._t_vertex = []; + this._update(); + } + /** + * Update internal data structures from Delaunator + */ + update(points, delaunator) { + this._r_vertex = points; + this._triangles = delaunator.triangles; + this._halfedges = delaunator.halfedges; + this._update(); + } + /** + * Update internal data structures to match the input mesh. + * + * Use if you have updated the triangles/halfedges with Delaunator + * and want the dual mesh to match the updated data. Note that + * this DOES not update boundary regions or ghost elements. + */ + _update() { + let { _triangles, _halfedges, _r_vertex, _t_vertex } = this; + this.numSides = _triangles.length; + this.numRegions = _r_vertex.length; + this.numSolidRegions = this.numRegions - 1; + this.numTriangles = this.numSides / 3; + this.numSolidTriangles = this.numSolidSides / 3; + if (this._t_vertex.length < this.numTriangles) { + const numOldTriangles = _t_vertex.length; + const numNewTriangles = this.numTriangles - numOldTriangles; + _t_vertex = _t_vertex.concat(new Array(numNewTriangles)); + for (let t = numOldTriangles; t < this.numTriangles; t++) { + _t_vertex[t] = [0, 0]; } - } while (s !== s0); - } -} - - -/* - * Add vertices evenly along the boundary of the mesh; - * use a slight curve so that the Delaunay triangulation - * doesn't make long thing triangles along the boundary. - * These points also prevent the Poisson disc generator - * from making uneven points near the boundary. - */ -function addBoundaryPoints(spacing, size) { - let N = Math.ceil(size/spacing); - let points = []; - for (let i = 0; i <= N; i++) { - let t = (i + 0.5) / (N + 1); - let w = size * t; - let offset = Math.pow(t - 0.5, 2); - points.push([offset, w], [size-offset, w]); - points.push([w, offset], [w, size-offset]); - } - return points; -} - - -function addGhostStructure({_r_vertex, _triangles, _halfedges}) { - const numSolidSides = _triangles.length; - const ghost_r = _r_vertex.length; - - let numUnpairedSides = 0, firstUnpairedEdge = -1; - let r_unpaired_s = []; // seed to side - // TODO: get these from the delaunator hull - for (let s = 0; s < numSolidSides; s++) { - if (_halfedges[s] === -1) { - numUnpairedSides++; - r_unpaired_s[_triangles[s]] = s; - firstUnpairedEdge = s; + this._t_vertex = _t_vertex; + } + this._r_in_s = new Int32Array(this.numRegions); + for (let s = 0; s < _triangles.length; s++) { + let endpoint = _triangles[_TriangleMesh.s_next_s(s)]; + if (this._r_in_s[endpoint] === 0 || _halfedges[s] === -1) { + this._r_in_s[endpoint] = s; + } + } + for (let s = 0; s < _triangles.length; s += 3) { + let t = s / 3, a = _r_vertex[_triangles[s]], b = _r_vertex[_triangles[s + 1]], c = _r_vertex[_triangles[s + 2]]; + if (this.s_ghost(s)) { + let dx = b[0] - a[0], dy = b[1] - a[1]; + let scale = 10 / Math.sqrt(dx * dx + dy * dy); + _t_vertex[t][0] = 0.5 * (a[0] + b[0]) + dy * scale; + _t_vertex[t][1] = 0.5 * (a[1] + b[1]) - dx * scale; + } else { + _t_vertex[t][0] = (a[0] + b[0] + c[0]) / 3; + _t_vertex[t][1] = (a[1] + b[1] + c[1]) / 3; + } + } } - } - - let r_newvertex = _r_vertex.concat([[500, 500]]); - let s_newstart_r = new Int32Array(numSolidSides + 3 * numUnpairedSides); - s_newstart_r.set(_triangles); - let s_newopposite_s = new Int32Array(numSolidSides + 3 * numUnpairedSides); - s_newopposite_s.set(_halfedges); - - for (let i = 0, s = firstUnpairedEdge; - i < numUnpairedSides; - i++, s = r_unpaired_s[s_newstart_r[s_next_s(s)]]) { - - // Construct a ghost side for s - let ghost_s = numSolidSides + 3 * i; - s_newopposite_s[s] = ghost_s; - s_newopposite_s[ghost_s] = s; - s_newstart_r[ghost_s] = s_newstart_r[s_next_s(s)]; - - // Construct the rest of the ghost triangle - s_newstart_r[ghost_s + 1] = s_newstart_r[s]; - s_newstart_r[ghost_s + 2] = ghost_r; - let k = numSolidSides + (3 * i + 4) % (3 * numUnpairedSides); - s_newopposite_s[ghost_s + 2] = k; - s_newopposite_s[k] = ghost_s + 2; - } - - return { - numSolidSides, - _r_vertex: r_newvertex, - _triangles: s_newstart_r, - _halfedges: s_newopposite_s - }; -} - - - -/** - * Build a dual mesh from points, with ghost triangles around the exterior. - * - * The builder assumes 0 ≤ x < 1000, 0 ≤ y < 1000 - * - * Options: - * - To have equally spaced points added around the 1000x1000 boundary, - * pass in boundarySpacing > 0 with the spacing value. If using Poisson - * disc points, I recommend 1.5 times the spacing used for Poisson disc. - * - * Phases: - * - Your own set of points - * - Poisson disc points - * - * The mesh generator runs some sanity checks but does not correct the - * generated points. - * - * Examples: - * - * Build a mesh with poisson disc points and a boundary: - * - * new MeshBuilder({boundarySpacing: 150}) - * .addPoisson(Poisson, 100) - * .create() - */ -class MeshBuilder { - /** If boundarySpacing > 0 there will be a boundary added around the 1000x1000 area */ - constructor ({boundarySpacing=0} = {}) { - let boundaryPoints = boundarySpacing > 0 ? addBoundaryPoints(boundarySpacing, 1000) : []; - this.points = boundaryPoints; - this.numBoundaryRegions = boundaryPoints.length; - } - - /** Points should be [x, y] */ - addPoints(newPoints) { - this.points.push.apply(this.points, newPoints); - return this; - } - - /** Pass in the constructor from the poisson-disk-sampling module */ - addPoisson(Poisson, spacing, random=Math.random) { - let generator = new Poisson([1000, 1000], spacing, undefined, undefined, random); - this.points.forEach(p => generator.addPoint(p)); - this.points = generator.fill(); - return this; - } - - /** Build and return a TriangleMesh */ - create(runChecks=false) { - // TODO: use Float32Array instead of this, so that we can - // construct directly from points read in from a file - let delaunator = Delaunator.from(this.points); - let graph = { - _r_vertex: this.points, + /** + * Construct a DualMesh from a Delaunator object, without any + * additional boundary regions. + */ + static fromDelaunator(points, delaunator) { + return new _TriangleMesh({ + numBoundaryRegions: 0, + numSolidSides: delaunator.triangles.length, + _r_vertex: points, _triangles: delaunator.triangles, _halfedges: delaunator.halfedges - }; - - if (runChecks) { - checkPointInequality(graph); - checkTriangleInequality(graph); + }); } - - graph = addGhostStructure(graph); - graph.numBoundaryRegions = this.numBoundaryRegions; - if (runChecks) { - checkMeshConnectivity(graph); + r_x(r) { + return this._r_vertex[r][0]; } - - return new TriangleMesh(graph); - } -} - - -module.exports = MeshBuilder; - -},{"./":3,"delaunator":4}],2:[function(require,module,exports){ -/* - * From https://github.com/redblobgames/dual-mesh - * Copyright 2017 Red Blob Games - * License: Apache v2.0 - */ -'use strict'; - -let DualMesh = require('../'); -let MeshBuilder = require('../create'); -let Poisson = require('poisson-disk-sampling'); - -const seeds1 = [ - [250, 30], [100, 260], [400, 260], [550, 30] -]; - -const seeds2 = [ - [320, 170], [220, 270], [400, 270], - [530, 50], [100, 80], [300, 30], - [50, 220], [550, 240], -]; - -let G0 = new MeshBuilder({boundarySpacing: 75}) - .addPoisson(Poisson, 50) - .create(); -let G1 = new MeshBuilder() - .addPoints(seeds1) - .create(); -let G2 = new MeshBuilder() - .addPoints(seeds2) - .create(); - - -function interpolate(p, q, t) { - return [p[0] * (1-t) + q[0] * t, p[1] * (1-t) + q[1] * t]; -} - -function extrapolate_from_center(p, center) { - let dx = p[0] - center[0], dy = p[1] - center[1]; - return [center[0] + dx*5, center[1] + dy*5]; -} - -/** Label placed near a reference point. */ -Vue.component('a-label', { - props: ['at', 'dx', 'dy'], - template: '' -}); - -Vue.component('a-side-black-edges', { - props: ['graph', 'alpha'], - template: ` - - - -`, - methods: { - b_side: function(s) { - const alpha = this.alpha || 0.0; - let begin = this.graph.r_pos([], this.graph.s_begin_r(s)); - let end = this.graph.r_pos([], this.graph.s_end_r(s)); - if (this.graph.r_ghost(this.graph.s_begin_r(s))) { - begin = extrapolate_from_center(end, [300, 150]); - } else if (this.graph.r_ghost(this.graph.s_end_r(s))) { - end = extrapolate_from_center(begin, [300, 150]); - } - let center = this.graph.t_pos([], this.graph.s_inner_t(s)); - begin = interpolate(begin, center, alpha); - end = interpolate(end, center, alpha); - return `M ${begin} L ${end}`; - }, - } -}); - -Vue.component('a-side-white-edges', { - props: ['graph', 'alpha'], - template: ` - - - -`, - methods: { - w_side: function(s) { - const alpha = this.alpha || 0.0; - let begin = this.graph.t_pos([], this.graph.s_inner_t(s)); - let end = this.graph.t_pos([], this.graph.s_outer_t(s)); - let center = this.graph.r_pos([], this.graph.s_begin_r(s)); - begin = interpolate(begin, center, alpha); - end = interpolate(end, center, alpha); - return `M ${begin} L ${end}`; - }, - } -}); - -Vue.component('a-side-labels', { - props: ['graph'], - template: ` - - - s{{s}} - - -`, - methods: {interpolate}, -}); - -Vue.component('a-region-points', { - props: ['graph', 'hover', 'radius'], - template: ` - - - -`, -}); - -Vue.component('a-region-labels', { - props: ['graph'], - template: ` - - - r{{r}} - - -`, -}); - -Vue.component('a-triangle-points', { - props: ['graph', 'hover', 'radius'], - template: ` - - - -`, -}); - -Vue.component('a-triangle-labels', { - props: ['graph'], - template: ` - - - t{{t}} - - -`, -}); - -function makeDiagram(selector, graph) { - new Vue({ - el: selector, - data: { - graph: Object.freeze(graph), - highlight: '', - }, - computed: { - highlightId: function() { - return parseInt(this.highlight.slice(1)); - }, - }, - methods: { - hover: function(label) { - this.highlight = label; - }, - format_array: function(label, array) { - return array.map((x) => (x === null || x < 0)? '(null)' : label+x).join(" "); - }, + r_y(r) { + return this._r_vertex[r][1]; } - }); -} - -for (let diagram of document.querySelectorAll("div.diagram-g0")) { - makeDiagram(diagram, G0); -} -for (let diagram of document.querySelectorAll("div.diagram-g1")) { - makeDiagram(diagram, G1); -} -for (let diagram of document.querySelectorAll("div.diagram-g2")) { - makeDiagram(diagram, G2); -} - -},{"../":3,"../create":1,"poisson-disk-sampling":9}],3:[function(require,module,exports){ -/* - * From https://github.com/redblobgames/dual-mesh - * Copyright 2017 Red Blob Games - * License: Apache v2.0 - */ - -'use strict'; - -/** - * Represent a triangle-polygon dual mesh with: - * - Regions (r) - * - Sides (s) - * - Triangles (t) - * - * Each element has an id: - * - 0 <= r < numRegions - * - 0 <= s < numSides - * - 0 <= t < numTriangles - * - * Naming convention: x_name_y takes x (r, s, t) as input and produces - * y (r, s, t) as output. If the output isn't a mesh index (r, s, t) - * then the _y suffix is omitted. - * - * A side is directed. If two triangles t0, t1 are adjacent, there will - * be two sides representing the boundary, one for t0 and one for t1. These - * can be accessed with s_inner_t and s_outer_t. - * - * A side also represents the boundary between two regions. If two regions - * r0, r1 are adjacent, there will be two sides representing the boundary, - * s_begin_r and s_end_r. - * - * Each side will have a pair, accessed with s_opposite_s. - * - * The mesh has no boundaries; it wraps around the "back" using a - * "ghost" region. Some regions are marked as the boundary; these are - * connected to the ghost region. Ghost triangles and ghost sides - * connect these boundary regions to the ghost region. Elements that - * aren't "ghost" are called "solid". - */ -class TriangleMesh { - static s_to_t(s) { return (s/3) | 0; } - static s_prev_s(s) { return (s % 3 == 0) ? s+2 : s-1; } - static s_next_s(s) { return (s % 3 == 2) ? s-2 : s+1; } - - /** - * constructor takes partial mesh information and fills in the rest; the - * partial information is generated in create.js or in deserialize.js - */ - constructor ({numBoundaryRegions, numSolidSides, _r_vertex, _triangles, _halfedges}) { - Object.assign(this, {numBoundaryRegions, numSolidSides, - _r_vertex, _triangles, _halfedges}); - - this.numSides = _triangles.length; - this.numRegions = _r_vertex.length; - this.numSolidRegions = this.numRegions - 1; - this.numTriangles = this.numSides / 3; - this.numSolidTriangles = this.numSolidSides / 3; - - // Construct an index for finding sides connected to a region - this._r_any_s = new Int32Array(this.numRegions); - for (let s = 0; s < _triangles.length; s++) { - this._r_any_s[_triangles[s]] = this._r_any_s[_triangles[s]] || s; + t_x(r) { + return this._t_vertex[r][0]; } - - // Construct triangle coordinates - this._t_vertex = new Array(this.numTriangles); - for (let s = 0; s < _triangles.length; s += 3) { - let a = _r_vertex[_triangles[s]], - b = _r_vertex[_triangles[s+1]], - c = _r_vertex[_triangles[s+2]]; - if (this.s_ghost(s)) { - // ghost triangle center is just outside the unpaired side - let dx = b[0]-a[0], dy = b[1]-a[1]; - this._t_vertex[s/3] = [a[0] + 0.5*(dx+dy), a[1] + 0.5*(dy-dx)]; - } else { - // solid triangle center is at the centroid - this._t_vertex[s/3] = [(a[0] + b[0] + c[0])/3, - (a[1] + b[1] + c[1])/3]; - } + t_y(r) { + return this._t_vertex[r][1]; } + r_pos(out, r) { + out.length = 2; + out[0] = this.r_x(r); + out[1] = this.r_y(r); + return out; + } + t_pos(out, t) { + out.length = 2; + out[0] = this.t_x(t); + out[1] = this.t_y(t); + return out; + } + s_begin_r(s) { + return this._triangles[s]; + } + s_end_r(s) { + return this._triangles[_TriangleMesh.s_next_s(s)]; + } + s_inner_t(s) { + return _TriangleMesh.s_to_t(s); + } + s_outer_t(s) { + return _TriangleMesh.s_to_t(this._halfedges[s]); + } + s_next_s(s) { + return _TriangleMesh.s_next_s(s); + } + s_prev_s(s) { + return _TriangleMesh.s_prev_s(s); + } + s_opposite_s(s) { + return this._halfedges[s]; + } + t_circulate_s(out_s, t) { + out_s.length = 3; + for (let i = 0; i < 3; i++) { + out_s[i] = 3 * t + i; + } + return out_s; + } + t_circulate_r(out_r, t) { + out_r.length = 3; + for (let i = 0; i < 3; i++) { + out_r[i] = this._triangles[3 * t + i]; + } + return out_r; + } + t_circulate_t(out_t, t) { + out_t.length = 3; + for (let i = 0; i < 3; i++) { + out_t[i] = this.s_outer_t(3 * t + i); + } + return out_t; + } + r_circulate_s(out_s, r) { + const s0 = this._r_in_s[r]; + let incoming = s0; + out_s.length = 0; + do { + out_s.push(this._halfedges[incoming]); + let outgoing = _TriangleMesh.s_next_s(incoming); + incoming = this._halfedges[outgoing]; + } while (incoming !== -1 && incoming !== s0); + return out_s; + } + r_circulate_r(out_r, r) { + const s0 = this._r_in_s[r]; + let incoming = s0; + out_r.length = 0; + do { + out_r.push(this.s_begin_r(incoming)); + let outgoing = _TriangleMesh.s_next_s(incoming); + incoming = this._halfedges[outgoing]; + } while (incoming !== -1 && incoming !== s0); + return out_r; + } + r_circulate_t(out_t, r) { + const s0 = this._r_in_s[r]; + let incoming = s0; + out_t.length = 0; + do { + out_t.push(_TriangleMesh.s_to_t(incoming)); + let outgoing = _TriangleMesh.s_next_s(incoming); + incoming = this._halfedges[outgoing]; + } while (incoming !== -1 && incoming !== s0); + return out_t; + } + ghost_r() { + return this.numRegions - 1; + } + s_ghost(s) { + return s >= this.numSolidSides; + } + r_ghost(r) { + return r === this.numRegions - 1; + } + t_ghost(t) { + return this.s_ghost(3 * t); + } + s_boundary(s) { + return this.s_ghost(s) && s % 3 === 0; + } + r_boundary(r) { + return r < this.numBoundaryRegions; + } + }; + module.exports = TriangleMesh; } - - r_x(r) { return this._r_vertex[r][0]; } - r_y(r) { return this._r_vertex[r][1]; } - t_x(r) { return this._t_vertex[r][0]; } - t_y(r) { return this._t_vertex[r][1]; } - r_pos(out, r) { out.length = 2; out[0] = this.r_x(r); out[1] = this.r_y(r); return out; } - t_pos(out, t) { out.length = 2; out[0] = this.t_x(t); out[1] = this.t_y(t); return out; } - - s_begin_r(s) { return this._triangles[s]; } - s_end_r(s) { return this._triangles[TriangleMesh.s_next_s(s)]; } - - s_inner_t(s) { return TriangleMesh.s_to_t(s); } - s_outer_t(s) { return TriangleMesh.s_to_t(this._halfedges[s]); } - - s_next_s(s) { return TriangleMesh.s_next_s(s); } - s_prev_s(s) { return TriangleMesh.s_prev_s(s); } - - s_opposite_s(s) { return this._halfedges[s]; } - - t_circulate_s(out_s, t) { out_s.length = 3; for (let i = 0; i < 3; i++) { out_s[i] = 3*t + i; } return out_s; } - t_circulate_r(out_r, t) { out_r.length = 3; for (let i = 0; i < 3; i++) { out_r[i] = this._triangles[3*t+i]; } return out_r; } - t_circulate_t(out_t, t) { out_t.length = 3; for (let i = 0; i < 3; i++) { out_t[i] = this.s_outer_t(3*t+i); } return out_t; } - - r_circulate_s(out_s, r) { - const s0 = this._r_any_s[r]; - let s = s0; - out_s.length = 0; - do { - out_s.push(s); - s = TriangleMesh.s_next_s(this._halfedges[s]); - } while (s != s0); - return out_s; - } - - r_circulate_r(out_r, r) { - const s0 = this._r_any_s[r]; - let s = s0; - out_r.length = 0; - do { - out_r.push(this.s_end_r(s)); - s = TriangleMesh.s_next_s(this._halfedges[s]); - } while (s != s0); - return out_r; - } - - r_circulate_t(out_t, r) { - const s0 = this._r_any_s[r]; - let s = s0; - out_t.length = 0; - do { - out_t.push(TriangleMesh.s_to_t(s)); - s = TriangleMesh.s_next_s(this._halfedges[s]); - } while (s != s0); - return out_t; - } - - ghost_r() { return this.numRegions - 1; } - s_ghost(s) { return s >= this.numSolidSides; } - r_ghost(r) { return r == this.numRegions - 1; } - t_ghost(t) { return this.s_ghost(3 * t); } - s_boundary(s) { return this.s_ghost(s) && (s % 3 == 0); } - r_boundary(r) { return r < this.numBoundaryRegions; } -} - -module.exports = TriangleMesh; - -},{}],4:[function(require,module,exports){ -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : - typeof define === 'function' && define.amd ? define(factory) : - (global.Delaunator = factory()); -}(this, (function () { 'use strict'; - - Delaunator.from = function (points, getX, getY) { - if (!getX) getX = defaultGetX; - if (!getY) getY = defaultGetY; - - var n = points.length; - var coords = new Float64Array(n * 2); - - for (var i = 0; i < n; i++) { + }); + + // node_modules/delaunator/delaunator.js + var require_delaunator = __commonJS({ + "node_modules/delaunator/delaunator.js"(exports, module) { + (function(global, factory) { + typeof exports === "object" && typeof module !== "undefined" ? module.exports = factory() : typeof define === "function" && define.amd ? define(factory) : (global = global || self, global.Delaunator = factory()); + })(exports, function() { + "use strict"; + var EPSILON = Math.pow(2, -52); + var EDGE_STACK = new Uint32Array(512); + var Delaunator = function Delaunator2(coords) { + var n = coords.length >> 1; + if (n > 0 && typeof coords[0] !== "number") { + throw new Error("Expected coords to contain numbers."); + } + this.coords = coords; + var maxTriangles = Math.max(2 * n - 5, 0); + this._triangles = new Uint32Array(maxTriangles * 3); + this._halfedges = new Int32Array(maxTriangles * 3); + this._hashSize = Math.ceil(Math.sqrt(n)); + this._hullPrev = new Uint32Array(n); + this._hullNext = new Uint32Array(n); + this._hullTri = new Uint32Array(n); + this._hullHash = new Int32Array(this._hashSize).fill(-1); + this._ids = new Uint32Array(n); + this._dists = new Float64Array(n); + this.update(); + }; + Delaunator.from = function from(points, getX, getY) { + if (getX === void 0) getX = defaultGetX; + if (getY === void 0) getY = defaultGetY; + var n = points.length; + var coords = new Float64Array(n * 2); + for (var i = 0; i < n; i++) { var p = points[i]; coords[2 * i] = getX(p); coords[2 * i + 1] = getY(p); - } - - return new Delaunator(coords); - }; - - function Delaunator(coords) { - if (!ArrayBuffer.isView(coords)) throw new Error('Expected coords to be a typed array.'); - - var minX = Infinity; - var minY = Infinity; - var maxX = -Infinity; - var maxY = -Infinity; - - var n = coords.length >> 1; - var ids = this.ids = new Uint32Array(n); - - this.coords = coords; - - for (var i = 0; i < n; i++) { + } + return new Delaunator(coords); + }; + Delaunator.prototype.update = function update() { + var ref = this; + var coords = ref.coords; + var hullPrev = ref._hullPrev; + var hullNext = ref._hullNext; + var hullTri = ref._hullTri; + var hullHash = ref._hullHash; + var n = coords.length >> 1; + var minX = Infinity; + var minY = Infinity; + var maxX = -Infinity; + var maxY = -Infinity; + for (var i = 0; i < n; i++) { var x = coords[2 * i]; var y = coords[2 * i + 1]; - if (x < minX) minX = x; - if (y < minY) minY = y; - if (x > maxX) maxX = x; - if (y > maxY) maxY = y; - ids[i] = i; - } - - var cx = (minX + maxX) / 2; - var cy = (minY + maxY) / 2; - - var minDist = Infinity; - var i0, i1, i2; - - // pick a seed point close to the centroid - for (i = 0; i < n; i++) { - var d = dist(cx, cy, coords[2 * i], coords[2 * i + 1]); + if (x < minX) { + minX = x; + } + if (y < minY) { + minY = y; + } + if (x > maxX) { + maxX = x; + } + if (y > maxY) { + maxY = y; + } + this._ids[i] = i; + } + var cx = (minX + maxX) / 2; + var cy = (minY + maxY) / 2; + var minDist = Infinity; + var i0, i1, i2; + for (var i$1 = 0; i$1 < n; i$1++) { + var d = dist(cx, cy, coords[2 * i$1], coords[2 * i$1 + 1]); if (d < minDist) { - i0 = i; - minDist = d; + i0 = i$1; + minDist = d; } - } - - minDist = Infinity; - - // find the point closest to the seed - for (i = 0; i < n; i++) { - if (i === i0) continue; - d = dist(coords[2 * i0], coords[2 * i0 + 1], coords[2 * i], coords[2 * i + 1]); - if (d < minDist && d > 0) { - i1 = i; - minDist = d; + } + var i0x = coords[2 * i0]; + var i0y = coords[2 * i0 + 1]; + minDist = Infinity; + for (var i$2 = 0; i$2 < n; i$2++) { + if (i$2 === i0) { + continue; } - } - - var minRadius = Infinity; - - // find the third point which forms the smallest circumcircle with the first two - for (i = 0; i < n; i++) { - if (i === i0 || i === i1) continue; - - var r = circumradius( - coords[2 * i0], coords[2 * i0 + 1], - coords[2 * i1], coords[2 * i1 + 1], - coords[2 * i], coords[2 * i + 1]); - + var d$1 = dist(i0x, i0y, coords[2 * i$2], coords[2 * i$2 + 1]); + if (d$1 < minDist && d$1 > 0) { + i1 = i$2; + minDist = d$1; + } + } + var i1x = coords[2 * i1]; + var i1y = coords[2 * i1 + 1]; + var minRadius = Infinity; + for (var i$3 = 0; i$3 < n; i$3++) { + if (i$3 === i0 || i$3 === i1) { + continue; + } + var r = circumradius(i0x, i0y, i1x, i1y, coords[2 * i$3], coords[2 * i$3 + 1]); if (r < minRadius) { - i2 = i; - minRadius = r; + i2 = i$3; + minRadius = r; } - } - - if (minRadius === Infinity) { - throw new Error('No Delaunay triangulation exists for this input.'); - } - - // swap the order of the seed points for counter-clockwise orientation - if (area(coords[2 * i0], coords[2 * i0 + 1], - coords[2 * i1], coords[2 * i1 + 1], - coords[2 * i2], coords[2 * i2 + 1]) < 0) { - - var tmp = i1; + } + var i2x = coords[2 * i2]; + var i2y = coords[2 * i2 + 1]; + if (minRadius === Infinity) { + for (var i$4 = 0; i$4 < n; i$4++) { + this._dists[i$4] = coords[2 * i$4] - coords[0] || coords[2 * i$4 + 1] - coords[1]; + } + quicksort(this._ids, this._dists, 0, n - 1); + var hull = new Uint32Array(n); + var j = 0; + for (var i$5 = 0, d0 = -Infinity; i$5 < n; i$5++) { + var id = this._ids[i$5]; + if (this._dists[id] > d0) { + hull[j++] = id; + d0 = this._dists[id]; + } + } + this.hull = hull.subarray(0, j); + this.triangles = new Uint32Array(0); + this.halfedges = new Uint32Array(0); + return; + } + if (orient(i0x, i0y, i1x, i1y, i2x, i2y)) { + var i$6 = i1; + var x$1 = i1x; + var y$1 = i1y; i1 = i2; - i2 = tmp; - } - - var i0x = coords[2 * i0]; - var i0y = coords[2 * i0 + 1]; - var i1x = coords[2 * i1]; - var i1y = coords[2 * i1 + 1]; - var i2x = coords[2 * i2]; - var i2y = coords[2 * i2 + 1]; - - var center = circumcenter(i0x, i0y, i1x, i1y, i2x, i2y); - this._cx = center.x; - this._cy = center.y; - - // sort the points by distance from the seed triangle circumcenter - quicksort(ids, coords, 0, ids.length - 1, center.x, center.y); - - // initialize a hash table for storing edges of the advancing convex hull - this._hashSize = Math.ceil(Math.sqrt(n)); - this._hash = []; - for (i = 0; i < this._hashSize; i++) this._hash[i] = null; - - // initialize a circular doubly-linked list that will hold an advancing convex hull - var e = this.hull = insertNode(coords, i0); - this._hashEdge(e); - e.t = 0; - e = insertNode(coords, i1, e); - this._hashEdge(e); - e.t = 1; - e = insertNode(coords, i2, e); - this._hashEdge(e); - e.t = 2; - - var maxTriangles = 2 * n - 5; - var triangles = this.triangles = new Uint32Array(maxTriangles * 3); - var halfedges = this.halfedges = new Int32Array(maxTriangles * 3); - - this.trianglesLen = 0; - - this._addTriangle(i0, i1, i2, -1, -1, -1); - - var xp, yp; - for (var k = 0; k < ids.length; k++) { - i = ids[k]; - x = coords[2 * i]; - y = coords[2 * i + 1]; - - // skip duplicate points - if (x === xp && y === yp) continue; - xp = x; - yp = y; - - // skip seed triangle points - if ((x === i0x && y === i0y) || - (x === i1x && y === i1y) || - (x === i2x && y === i2y)) continue; - - // find a visible edge on the convex hull using edge hash - var startKey = this._hashKey(x, y); - var key = startKey; - var start; - do { - start = this._hash[key]; - key = (key + 1) % this._hashSize; - } while ((!start || start.removed) && key !== startKey); - - e = start; - while (area(x, y, e.x, e.y, e.next.x, e.next.y) >= 0) { - e = e.next; - if (e === start) { - throw new Error('Something is wrong with the input points.'); - } + i1x = i2x; + i1y = i2y; + i2 = i$6; + i2x = x$1; + i2y = y$1; + } + var center = circumcenter(i0x, i0y, i1x, i1y, i2x, i2y); + this._cx = center.x; + this._cy = center.y; + for (var i$7 = 0; i$7 < n; i$7++) { + this._dists[i$7] = dist(coords[2 * i$7], coords[2 * i$7 + 1], center.x, center.y); + } + quicksort(this._ids, this._dists, 0, n - 1); + this._hullStart = i0; + var hullSize = 3; + hullNext[i0] = hullPrev[i2] = i1; + hullNext[i1] = hullPrev[i0] = i2; + hullNext[i2] = hullPrev[i1] = i0; + hullTri[i0] = 0; + hullTri[i1] = 1; + hullTri[i2] = 2; + hullHash.fill(-1); + hullHash[this._hashKey(i0x, i0y)] = i0; + hullHash[this._hashKey(i1x, i1y)] = i1; + hullHash[this._hashKey(i2x, i2y)] = i2; + this.trianglesLen = 0; + this._addTriangle(i0, i1, i2, -1, -1, -1); + for (var k = 0, xp = void 0, yp = void 0; k < this._ids.length; k++) { + var i$8 = this._ids[k]; + var x$2 = coords[2 * i$8]; + var y$2 = coords[2 * i$8 + 1]; + if (k > 0 && Math.abs(x$2 - xp) <= EPSILON && Math.abs(y$2 - yp) <= EPSILON) { + continue; } - - var walkBack = e === start; - - // add the first triangle from the point - var t = this._addTriangle(e.i, i, e.next.i, -1, -1, e.t); - - e.t = t; // keep track of boundary triangles on the hull - e = insertNode(coords, i, e); - - // recursively flip triangles from the point until they satisfy the Delaunay condition - e.t = this._legalize(t + 2); - if (e.prev.prev.t === halfedges[t + 1]) { - e.prev.prev.t = t + 2; + xp = x$2; + yp = y$2; + if (i$8 === i0 || i$8 === i1 || i$8 === i2) { + continue; } - - // walk forward through the hull, adding more triangles and flipping recursively - var q = e.next; - while (area(x, y, q.x, q.y, q.next.x, q.next.y) < 0) { - t = this._addTriangle(q.i, i, q.next.i, q.prev.t, -1, q.t); - q.prev.t = this._legalize(t + 2); - this.hull = removeNode(q); - q = q.next; + var start = 0; + for (var j$1 = 0, key = this._hashKey(x$2, y$2); j$1 < this._hashSize; j$1++) { + start = hullHash[(key + j$1) % this._hashSize]; + if (start !== -1 && start !== hullNext[start]) { + break; + } } - - if (walkBack) { - // walk backward from the other side, adding more triangles and flipping - q = e.prev; - while (area(x, y, q.prev.x, q.prev.y, q.x, q.y) < 0) { - t = this._addTriangle(q.prev.i, i, q.i, -1, q.t, q.prev.t); - this._legalize(t + 2); - q.prev.t = t; - this.hull = removeNode(q); - q = q.prev; - } + start = hullPrev[start]; + var e = start, q = void 0; + while (q = hullNext[e], !orient(x$2, y$2, coords[2 * e], coords[2 * e + 1], coords[2 * q], coords[2 * q + 1])) { + e = q; + if (e === start) { + e = -1; + break; + } } - - // save the two new edges in the hash table - this._hashEdge(e); - this._hashEdge(e.prev); - } - - // trim typed triangle mesh arrays - this.triangles = triangles.subarray(0, this.trianglesLen); - this.halfedges = halfedges.subarray(0, this.trianglesLen); - } - - Delaunator.prototype = { - - _hashEdge: function (e) { - this._hash[this._hashKey(e.x, e.y)] = e; - }, - - _hashKey: function (x, y) { - var dx = x - this._cx; - var dy = y - this._cy; - // use pseudo-angle: a measure that monotonically increases - // with real angle, but doesn't require expensive trigonometry - var p = 1 - dx / (Math.abs(dx) + Math.abs(dy)); - return Math.floor((2 + (dy < 0 ? -p : p)) / 4 * this._hashSize); - }, - - _legalize: function (a) { - var triangles = this.triangles; - var coords = this.coords; - var halfedges = this.halfedges; - + if (e === -1) { + continue; + } + var t = this._addTriangle(e, i$8, hullNext[e], -1, -1, hullTri[e]); + hullTri[i$8] = this._legalize(t + 2); + hullTri[e] = t; + hullSize++; + var n$1 = hullNext[e]; + while (q = hullNext[n$1], orient(x$2, y$2, coords[2 * n$1], coords[2 * n$1 + 1], coords[2 * q], coords[2 * q + 1])) { + t = this._addTriangle(n$1, i$8, q, hullTri[i$8], -1, hullTri[n$1]); + hullTri[i$8] = this._legalize(t + 2); + hullNext[n$1] = n$1; + hullSize--; + n$1 = q; + } + if (e === start) { + while (q = hullPrev[e], orient(x$2, y$2, coords[2 * q], coords[2 * q + 1], coords[2 * e], coords[2 * e + 1])) { + t = this._addTriangle(q, i$8, e, -1, hullTri[e], hullTri[q]); + this._legalize(t + 2); + hullTri[q] = t; + hullNext[e] = e; + hullSize--; + e = q; + } + } + this._hullStart = hullPrev[i$8] = e; + hullNext[e] = hullPrev[n$1] = i$8; + hullNext[i$8] = n$1; + hullHash[this._hashKey(x$2, y$2)] = i$8; + hullHash[this._hashKey(coords[2 * e], coords[2 * e + 1])] = e; + } + this.hull = new Uint32Array(hullSize); + for (var i$9 = 0, e$1 = this._hullStart; i$9 < hullSize; i$9++) { + this.hull[i$9] = e$1; + e$1 = hullNext[e$1]; + } + this.triangles = this._triangles.subarray(0, this.trianglesLen); + this.halfedges = this._halfedges.subarray(0, this.trianglesLen); + }; + Delaunator.prototype._hashKey = function _hashKey(x, y) { + return Math.floor(pseudoAngle(x - this._cx, y - this._cy) * this._hashSize) % this._hashSize; + }; + Delaunator.prototype._legalize = function _legalize(a) { + var ref = this; + var triangles = ref._triangles; + var halfedges = ref._halfedges; + var coords = ref.coords; + var i = 0; + var ar = 0; + while (true) { var b = halfedges[a]; - var a0 = a - a % 3; + ar = a0 + (a + 2) % 3; + if (b === -1) { + if (i === 0) { + break; + } + a = EDGE_STACK[--i]; + continue; + } var b0 = b - b % 3; - var al = a0 + (a + 1) % 3; - var ar = a0 + (a + 2) % 3; var bl = b0 + (b + 2) % 3; - var p0 = triangles[ar]; var pr = triangles[a]; var pl = triangles[al]; var p1 = triangles[bl]; - var illegal = inCircle( - coords[2 * p0], coords[2 * p0 + 1], - coords[2 * pr], coords[2 * pr + 1], - coords[2 * pl], coords[2 * pl + 1], - coords[2 * p1], coords[2 * p1 + 1]); - + coords[2 * p0], + coords[2 * p0 + 1], + coords[2 * pr], + coords[2 * pr + 1], + coords[2 * pl], + coords[2 * pl + 1], + coords[2 * p1], + coords[2 * p1 + 1] + ); if (illegal) { - triangles[a] = p1; - triangles[b] = p0; - - this._link(a, halfedges[bl]); - this._link(b, halfedges[ar]); - this._link(ar, bl); - - var br = b0 + (b + 1) % 3; - - this._legalize(a); - return this._legalize(br); + triangles[a] = p1; + triangles[b] = p0; + var hbl = halfedges[bl]; + if (hbl === -1) { + var e = this._hullStart; + do { + if (this._hullTri[e] === bl) { + this._hullTri[e] = a; + break; + } + e = this._hullPrev[e]; + } while (e !== this._hullStart); + } + this._link(a, hbl); + this._link(b, halfedges[ar]); + this._link(ar, bl); + var br = b0 + (b + 1) % 3; + if (i < EDGE_STACK.length) { + EDGE_STACK[i++] = br; + } + } else { + if (i === 0) { + break; + } + a = EDGE_STACK[--i]; } - - return ar; - }, - - _link: function (a, b) { - this.halfedges[a] = b; - if (b !== -1) this.halfedges[b] = a; - }, - - // add a new triangle given vertex indices and adjacent half-edge ids - _addTriangle: function (i0, i1, i2, a, b, c) { - var t = this.trianglesLen; - - this.triangles[t] = i0; - this.triangles[t + 1] = i1; - this.triangles[t + 2] = i2; - - this._link(t, a); - this._link(t + 1, b); - this._link(t + 2, c); - - this.trianglesLen += 3; - - return t; - } - }; - - function dist(ax, ay, bx, by) { - var dx = ax - bx; - var dy = ay - by; - return dx * dx + dy * dy; - } - - function area(px, py, qx, qy, rx, ry) { - return (qy - py) * (rx - qx) - (qx - px) * (ry - qy); - } - - function inCircle(ax, ay, bx, by, cx, cy, px, py) { - ax -= px; - ay -= py; - bx -= px; - by -= py; - cx -= px; - cy -= py; - - var ap = ax * ax + ay * ay; - var bp = bx * bx + by * by; - var cp = cx * cx + cy * cy; - - return ax * (by * cp - bp * cy) - - ay * (bx * cp - bp * cx) + - ap * (bx * cy - by * cx) < 0; - } - - function circumradius(ax, ay, bx, by, cx, cy) { - bx -= ax; - by -= ay; - cx -= ax; - cy -= ay; - - var bl = bx * bx + by * by; - var cl = cx * cx + cy * cy; - - if (bl === 0 || cl === 0) return Infinity; - - var d = bx * cy - by * cx; - if (d === 0) return Infinity; - - var x = (cy * bl - by * cl) * 0.5 / d; - var y = (bx * cl - cx * bl) * 0.5 / d; - - return x * x + y * y; - } - - function circumcenter(ax, ay, bx, by, cx, cy) { - bx -= ax; - by -= ay; - cx -= ax; - cy -= ay; - - var bl = bx * bx + by * by; - var cl = cx * cx + cy * cy; - - var d = bx * cy - by * cx; - - var x = (cy * bl - by * cl) * 0.5 / d; - var y = (bx * cl - cx * bl) * 0.5 / d; - - return { - x: ax + x, - y: ay + y + } + return ar; }; - } - - // create a new node in a doubly linked list - function insertNode(coords, i, prev) { - var node = { - i: i, - x: coords[2 * i], - y: coords[2 * i + 1], - t: 0, - prev: null, - next: null, - removed: false + Delaunator.prototype._link = function _link(a, b) { + this._halfedges[a] = b; + if (b !== -1) { + this._halfedges[b] = a; + } }; - - if (!prev) { - node.prev = node; - node.next = node; - - } else { - node.next = prev.next; - node.prev = prev; - prev.next.prev = node; - prev.next = node; + Delaunator.prototype._addTriangle = function _addTriangle(i0, i1, i2, a, b, c) { + var t = this.trianglesLen; + this._triangles[t] = i0; + this._triangles[t + 1] = i1; + this._triangles[t + 2] = i2; + this._link(t, a); + this._link(t + 1, b); + this._link(t + 2, c); + this.trianglesLen += 3; + return t; + }; + function pseudoAngle(dx, dy) { + var p = dx / (Math.abs(dx) + Math.abs(dy)); + return (dy > 0 ? 3 - p : 1 + p) / 4; } - return node; - } - - function removeNode(node) { - node.prev.next = node.next; - node.next.prev = node.prev; - node.removed = true; - return node.prev; - } - - function quicksort(ids, coords, left, right, cx, cy) { - var i, j, temp; - - if (right - left <= 20) { - for (i = left + 1; i <= right; i++) { - temp = ids[i]; - j = i - 1; - while (j >= left && compare(coords, ids[j], temp, cx, cy) > 0) ids[j + 1] = ids[j--]; - ids[j + 1] = temp; + function dist(ax, ay, bx, by) { + var dx = ax - bx; + var dy = ay - by; + return dx * dx + dy * dy; + } + function orientIfSure(px, py, rx, ry, qx, qy) { + var l = (ry - py) * (qx - px); + var r = (rx - px) * (qy - py); + return Math.abs(l - r) >= 33306690738754716e-32 * Math.abs(l + r) ? l - r : 0; + } + function orient(rx, ry, qx, qy, px, py) { + var sign = orientIfSure(px, py, rx, ry, qx, qy) || orientIfSure(rx, ry, qx, qy, px, py) || orientIfSure(qx, qy, px, py, rx, ry); + return sign < 0; + } + function inCircle(ax, ay, bx, by, cx, cy, px, py) { + var dx = ax - px; + var dy = ay - py; + var ex = bx - px; + var ey = by - py; + var fx = cx - px; + var fy = cy - py; + var ap = dx * dx + dy * dy; + var bp = ex * ex + ey * ey; + var cp = fx * fx + fy * fy; + return dx * (ey * cp - bp * fy) - dy * (ex * cp - bp * fx) + ap * (ex * fy - ey * fx) < 0; + } + function circumradius(ax, ay, bx, by, cx, cy) { + var dx = bx - ax; + var dy = by - ay; + var ex = cx - ax; + var ey = cy - ay; + var bl = dx * dx + dy * dy; + var cl = ex * ex + ey * ey; + var d = 0.5 / (dx * ey - dy * ex); + var x = (ey * bl - dy * cl) * d; + var y = (dx * cl - ex * bl) * d; + return x * x + y * y; + } + function circumcenter(ax, ay, bx, by, cx, cy) { + var dx = bx - ax; + var dy = by - ay; + var ex = cx - ax; + var ey = cy - ay; + var bl = dx * dx + dy * dy; + var cl = ex * ex + ey * ey; + var d = 0.5 / (dx * ey - dy * ex); + var x = ax + (ey * bl - dy * cl) * d; + var y = ay + (dx * cl - ex * bl) * d; + return { x, y }; + } + function quicksort(ids, dists, left, right) { + if (right - left <= 20) { + for (var i = left + 1; i <= right; i++) { + var temp = ids[i]; + var tempDist = dists[temp]; + var j = i - 1; + while (j >= left && dists[ids[j]] > tempDist) { + ids[j + 1] = ids[j--]; + } + ids[j + 1] = temp; } - } else { - var median = (left + right) >> 1; - i = left + 1; - j = right; - swap(ids, median, i); - if (compare(coords, ids[left], ids[right], cx, cy) > 0) swap(ids, left, right); - if (compare(coords, ids[i], ids[right], cx, cy) > 0) swap(ids, i, right); - if (compare(coords, ids[left], ids[i], cx, cy) > 0) swap(ids, left, i); - - temp = ids[i]; + } else { + var median = left + right >> 1; + var i$1 = left + 1; + var j$1 = right; + swap(ids, median, i$1); + if (dists[ids[left]] > dists[ids[right]]) { + swap(ids, left, right); + } + if (dists[ids[i$1]] > dists[ids[right]]) { + swap(ids, i$1, right); + } + if (dists[ids[left]] > dists[ids[i$1]]) { + swap(ids, left, i$1); + } + var temp$1 = ids[i$1]; + var tempDist$1 = dists[temp$1]; while (true) { - do i++; while (compare(coords, ids[i], temp, cx, cy) < 0); - do j--; while (compare(coords, ids[j], temp, cx, cy) > 0); - if (j < i) break; - swap(ids, i, j); + do { + i$1++; + } while (dists[ids[i$1]] < tempDist$1); + do { + j$1--; + } while (dists[ids[j$1]] > tempDist$1); + if (j$1 < i$1) { + break; + } + swap(ids, i$1, j$1); } - ids[left + 1] = ids[j]; - ids[j] = temp; - - if (right - i + 1 >= j - left) { - quicksort(ids, coords, i, right, cx, cy); - quicksort(ids, coords, left, j - 1, cx, cy); + ids[left + 1] = ids[j$1]; + ids[j$1] = temp$1; + if (right - i$1 + 1 >= j$1 - left) { + quicksort(ids, dists, i$1, right); + quicksort(ids, dists, left, j$1 - 1); } else { - quicksort(ids, coords, left, j - 1, cx, cy); - quicksort(ids, coords, i, right, cx, cy); + quicksort(ids, dists, left, j$1 - 1); + quicksort(ids, dists, i$1, right); } + } } + function swap(arr, i, j) { + var tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } + function defaultGetX(p) { + return p[0]; + } + function defaultGetY(p) { + return p[1]; + } + return Delaunator; + }); } - - function compare(coords, i, j, cx, cy) { - var d1 = dist(coords[2 * i], coords[2 * i + 1], cx, cy); - var d2 = dist(coords[2 * j], coords[2 * j + 1], cx, cy); - return (d1 - d2) || (coords[2 * i] - coords[2 * j]) || (coords[2 * i + 1] - coords[2 * j + 1]); - } - - function swap(arr, i, j) { - var tmp = arr[i]; - arr[i] = arr[j]; - arr[j] = tmp; - } - - function defaultGetX(p) { - return p[0]; - } - function defaultGetY(p) { - return p[1]; - } - - return Delaunator; - -}))); - -},{}],5:[function(require,module,exports){ -"use strict" - -function iota(n) { - var result = new Array(n) - for(var i=0; i - * @license MIT - */ - -// The _isBuffer check is for Safari 5-7 support, because it's missing -// Object.prototype.constructor. Remove this eventually -module.exports = function (obj) { - return obj != null && (isBuffer(obj) || isSlowBuffer(obj) || !!obj._isBuffer) -} - -function isBuffer (obj) { - return !!obj.constructor && typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj) -} - -// For Node v0.10 support. Remove this eventually. -function isSlowBuffer (obj) { - return typeof obj.readFloatLE === 'function' && typeof obj.slice === 'function' && isBuffer(obj.slice(0, 0)) -} - -},{}],7:[function(require,module,exports){ -module.exports = moore - -function moore(range, dims) { - dims = dims || 2 - range = range || 1 - return recurse([], [], 0) - - function recurse(array, temp, d) { - if (d === dims-1) { - for (var i = -range; i <= range; i += 1) { - if (i || temp.some(function(n) { - return n - })) array.push(temp.concat(i)) + }); + + // create.js + var require_create = __commonJS({ + "create.js"(exports, module) { + "use strict"; + var Delaunator = require_delaunator(); + var TriangleMesh = require_dual_mesh(); + function s_next_s(s) { + return s % 3 == 2 ? s - 2 : s + 1; } - } else { - for (var i = -range; i <= range; i += 1) { - recurse(array, temp.concat(i), d+1) + function checkPointInequality({ _r_vertex, _triangles, _halfedges }) { } - } - return array - } -} - -},{}],8:[function(require,module,exports){ -var iota = require("iota-array") -var isBuffer = require("is-buffer") - -var hasTypedArrays = ((typeof Float64Array) !== "undefined") - -function compare1st(a, b) { - return a[0] - b[0] -} - -function order() { - var stride = this.stride - var terms = new Array(stride.length) - var i - for(i=0; iMath.abs(this.stride[1]))?[1,0]:[0,1]}})") - } else if(dimension === 3) { - code.push( -"var s0=Math.abs(this.stride[0]),s1=Math.abs(this.stride[1]),s2=Math.abs(this.stride[2]);\ -if(s0>s1){\ -if(s1>s2){\ -return [2,1,0];\ -}else if(s0>s2){\ -return [1,2,0];\ -}else{\ -return [1,0,2];\ -}\ -}else if(s0>s2){\ -return [2,0,1];\ -}else if(s2>s1){\ -return [0,1,2];\ -}else{\ -return [0,2,1];\ -}}})") + function checkTriangleInequality({ _r_vertex, _triangles, _halfedges }) { + const badAngleLimit = 30; + let summary = new Array(badAngleLimit).fill(0); + let count = 0; + for (let s = 0; s < _triangles.length; s++) { + let r0 = _triangles[s], r1 = _triangles[s_next_s(s)], r2 = _triangles[s_next_s(s_next_s(s))]; + let p0 = _r_vertex[r0], p1 = _r_vertex[r1], p2 = _r_vertex[r2]; + let d0 = [p0[0] - p1[0], p0[1] - p1[1]]; + let d2 = [p2[0] - p1[0], p2[1] - p1[1]]; + let dotProduct = d0[0] * d2[0] + d0[1] + d2[1]; + let angleDegrees = 180 / Math.PI * Math.acos(dotProduct); + if (angleDegrees < badAngleLimit) { + summary[angleDegrees | 0]++; + count++; + } + } + if (count > 0) { + console.log(" bad angles:", summary.join(" ")); + } } - } else { - code.push("ORDER})") - } - } - - //view.set(i0, ..., v): - code.push( -"proto.set=function "+className+"_set("+args.join(",")+",v){") - if(useGetters) { - code.push("return this.data.set("+index_str+",v)}") - } else { - code.push("return this.data["+index_str+"]=v}") - } - - //view.get(i0, ...): - code.push("proto.get=function "+className+"_get("+args.join(",")+"){") - if(useGetters) { - code.push("return this.data.get("+index_str+")}") - } else { - code.push("return this.data["+index_str+"]}") - } - - //view.index: - code.push( - "proto.index=function "+className+"_index(", args.join(), "){return "+index_str+"}") - - //view.hi(): - code.push("proto.hi=function "+className+"_hi("+args.join(",")+"){return new "+className+"(this.data,"+ - indices.map(function(i) { - return ["(typeof i",i,"!=='number'||i",i,"<0)?this.shape[", i, "]:i", i,"|0"].join("") - }).join(",")+","+ - indices.map(function(i) { - return "this.stride["+i + "]" - }).join(",")+",this.offset)}") - - //view.lo(): - var a_vars = indices.map(function(i) { return "a"+i+"=this.shape["+i+"]" }) - var c_vars = indices.map(function(i) { return "c"+i+"=this.stride["+i+"]" }) - code.push("proto.lo=function "+className+"_lo("+args.join(",")+"){var b=this.offset,d=0,"+a_vars.join(",")+","+c_vars.join(",")) - for(var i=0; i=0){\ -d=i"+i+"|0;\ -b+=c"+i+"*d;\ -a"+i+"-=d}") - } - code.push("return new "+className+"(this.data,"+ - indices.map(function(i) { - return "a"+i - }).join(",")+","+ - indices.map(function(i) { - return "c"+i - }).join(",")+",b)}") - - //view.step(): - code.push("proto.step=function "+className+"_step("+args.join(",")+"){var "+ - indices.map(function(i) { - return "a"+i+"=this.shape["+i+"]" - }).join(",")+","+ - indices.map(function(i) { - return "b"+i+"=this.stride["+i+"]" - }).join(",")+",c=this.offset,d=0,ceil=Math.ceil") - for(var i=0; i=0){c=(c+this.stride["+i+"]*i"+i+")|0}else{a.push(this.shape["+i+"]);b.push(this.stride["+i+"])}") - } - code.push("var ctor=CTOR_LIST[a.length+1];return ctor(this.data,a,b,c)}") - - //Add return statement - code.push("return function construct_"+className+"(data,shape,stride,offset){return new "+className+"(data,"+ - indices.map(function(i) { - return "shape["+i+"]" - }).join(",")+","+ - indices.map(function(i) { - return "stride["+i+"]" - }).join(",")+",offset)}") - - //Compile procedure - var procedure = new Function("CTOR_LIST", "ORDER", code.join("\n")) - return procedure(CACHED_CONSTRUCTORS[dtype], order) -} - -function arrayDType(data) { - if(isBuffer(data)) { - return "buffer" - } - if(hasTypedArrays) { - switch(Object.prototype.toString.call(data)) { - case "[object Float64Array]": - return "float64" - case "[object Float32Array]": - return "float32" - case "[object Int8Array]": - return "int8" - case "[object Int16Array]": - return "int16" - case "[object Int32Array]": - return "int32" - case "[object Uint8Array]": - return "uint8" - case "[object Uint16Array]": - return "uint16" - case "[object Uint32Array]": - return "uint32" - case "[object Uint8ClampedArray]": - return "uint8_clamped" - } - } - if(Array.isArray(data)) { - return "array" - } - return "generic" -} - -var CACHED_CONSTRUCTORS = { - "float32":[], - "float64":[], - "int8":[], - "int16":[], - "int32":[], - "uint8":[], - "uint16":[], - "uint32":[], - "array":[], - "uint8_clamped":[], - "buffer":[], - "generic":[] -} - -;(function() { - for(var id in CACHED_CONSTRUCTORS) { - CACHED_CONSTRUCTORS[id].push(compileConstructor(id, -1)) - } -}); - -function wrappedNDArrayCtor(data, shape, stride, offset) { - if(data === undefined) { - var ctor = CACHED_CONSTRUCTORS.array[0] - return ctor([]) - } else if(typeof data === "number") { - data = [data] - } - if(shape === undefined) { - shape = [ data.length ] - } - var d = shape.length - if(stride === undefined) { - stride = new Array(d) - for(var i=d-1, sz=1; i>=0; --i) { - stride[i] = sz - sz *= shape[i] + function checkMeshConnectivity({ _r_vertex, _triangles, _halfedges }) { + let ghost_r = _r_vertex.length - 1, out_s = []; + for (let s0 = 0; s0 < _triangles.length; s0++) { + if (_halfedges[_halfedges[s0]] !== s0) { + console.log(`FAIL _halfedges[_halfedges[${s0}]] !== ${s0}`); + } + let s = s0, count = 0; + out_s.length = 0; + do { + count++; + out_s.push(s); + s = s_next_s(_halfedges[s]); + if (count > 100 && _triangles[s0] !== ghost_r) { + console.log(`FAIL to circulate around region with start side=${s0} from region ${_triangles[s0]} to ${_triangles[s_next_s(s0)]}, out_s=${out_s}`); + break; + } + } while (s !== s0); + } + } + function addBoundaryPoints(spacing, size) { + let N = Math.ceil(size / spacing); + let points = []; + for (let i = 0; i <= N; i++) { + let t = (i + 0.5) / (N + 1); + let w = size * t; + let offset = Math.pow(t - 0.5, 2); + points.push([offset, w], [size - offset, w]); + points.push([w, offset], [w, size - offset]); + } + return points; + } + function addGhostStructure({ _r_vertex, _triangles, _halfedges }) { + const numSolidSides = _triangles.length; + const ghost_r = _r_vertex.length; + let numUnpairedSides = 0, firstUnpairedEdge = -1; + let r_unpaired_s = []; + for (let s = 0; s < numSolidSides; s++) { + if (_halfedges[s] === -1) { + numUnpairedSides++; + r_unpaired_s[_triangles[s]] = s; + firstUnpairedEdge = s; + } + } + let r_newvertex = _r_vertex.concat([[500, 500]]); + let s_newstart_r = new Int32Array(numSolidSides + 3 * numUnpairedSides); + s_newstart_r.set(_triangles); + let s_newopposite_s = new Int32Array(numSolidSides + 3 * numUnpairedSides); + s_newopposite_s.set(_halfedges); + for (let i = 0, s = firstUnpairedEdge; i < numUnpairedSides; i++, s = r_unpaired_s[s_newstart_r[s_next_s(s)]]) { + let ghost_s = numSolidSides + 3 * i; + s_newopposite_s[s] = ghost_s; + s_newopposite_s[ghost_s] = s; + s_newstart_r[ghost_s] = s_newstart_r[s_next_s(s)]; + s_newstart_r[ghost_s + 1] = s_newstart_r[s]; + s_newstart_r[ghost_s + 2] = ghost_r; + let k = numSolidSides + (3 * i + 4) % (3 * numUnpairedSides); + s_newopposite_s[ghost_s + 2] = k; + s_newopposite_s[k] = ghost_s + 2; + } + return { + numSolidSides, + _r_vertex: r_newvertex, + _triangles: s_newstart_r, + _halfedges: s_newopposite_s + }; + } + var MeshBuilder2 = class { + /** If boundarySpacing > 0 there will be a boundary added around the 1000x1000 area */ + constructor({ boundarySpacing = 0 } = {}) { + let boundaryPoints = boundarySpacing > 0 ? addBoundaryPoints(boundarySpacing, 1e3) : []; + this.points = boundaryPoints; + this.numBoundaryRegions = boundaryPoints.length; + } + /** Points should be [x, y] */ + addPoints(newPoints) { + for (let p of newPoints) { + this.points.push(p); + } + return this; + } + /** Points will be [x, y] */ + getNonBoundaryPoints() { + return this.points.slice(this.numBoundaryRegions); + } + /** (used for more advanced mixing of different mesh types) */ + clearNonBoundaryPoints() { + this.points.splice(this.numBoundaryRegions, this.points.length); + return this; + } + /** Pass in the constructor from the poisson-disk-sampling module */ + addPoisson(Poisson2, spacing, random = Math.random) { + let generator = new Poisson2({ + shape: [1e3, 1e3], + minDistance: spacing + }, random); + this.points.forEach((p) => generator.addPoint(p)); + this.points = generator.fill(); + return this; + } + /** Build and return a TriangleMesh */ + create(runChecks = false) { + let delaunator = Delaunator.from(this.points); + let graph = { + _r_vertex: this.points, + _triangles: delaunator.triangles, + _halfedges: delaunator.halfedges + }; + if (runChecks) { + checkPointInequality(graph); + checkTriangleInequality(graph); + } + graph = addGhostStructure(graph); + graph.numBoundaryRegions = this.numBoundaryRegions; + if (runChecks) { + checkMeshConnectivity(graph); + } + return new TriangleMesh(graph); + } + }; + module.exports = MeshBuilder2; } - } - if(offset === undefined) { - offset = 0 - for(var i=0; i 0; dimension--) { + stride[dimension - 1] = totalLength; + totalLength = totalLength * gridShape[dimension - 1]; + } + return { + stride, + data: new Uint32Array(totalLength) + }; + } + function tinyNDArrayOfArray(gridShape) { + var dimensions = gridShape.length, totalLength = 1, stride = new Array(dimensions), data = [], dimension, index; + for (dimension = dimensions; dimension > 0; dimension--) { + stride[dimension - 1] = totalLength; + totalLength = totalLength * gridShape[dimension - 1]; + } + for (index = 0; index < totalLength; index++) { + data.push([]); + } + return { + stride, + data + }; } + module.exports = { + integer: tinyNDArrayOfInteger, + array: tinyNDArrayOfArray + }; } - } - var dtype = arrayDType(data) - var ctor_list = CACHED_CONSTRUCTORS[dtype] - while(ctor_list.length <= d+1) { - ctor_list.push(compileConstructor(dtype, ctor_list.length-1)) - } - var ctor = ctor_list[d+1] - return ctor(data, shape, stride, offset) -} - -module.exports = wrappedNDArrayCtor - -},{"iota-array":5,"is-buffer":6}],9:[function(require,module,exports){ -"use strict"; - -module.exports = require('./src/poisson-disk-sampling'); - -},{"./src/poisson-disk-sampling":11}],10:[function(require,module,exports){ -"use strict"; - -module.exports = function euclideanDistanceN (point1, point2) { - var result = 0, - i = 0; - - for (; i < point1.length; i++) { - result += Math.pow(point1[i] - point2[i], 2); + }); + + // node_modules/poisson-disk-sampling/src/sphere-random.js + var require_sphere_random = __commonJS({ + "node_modules/poisson-disk-sampling/src/sphere-random.js"(exports, module) { + "use strict"; + module.exports = sampleSphere; + function sampleSphere(d, rng) { + var v = new Array(d), d2 = Math.floor(d / 2) << 1, r2 = 0, rr, r, theta, h, i; + for (i = 0; i < d2; i += 2) { + rr = -2 * Math.log(rng()); + r = Math.sqrt(rr); + theta = 2 * Math.PI * rng(); + r2 += rr; + v[i] = r * Math.cos(theta); + v[i + 1] = r * Math.sin(theta); + } + if (d % 2) { + var x = Math.sqrt(-2 * Math.log(rng())) * Math.cos(2 * Math.PI * rng()); + v[d - 1] = x; + r2 += Math.pow(x, 2); + } + h = 1 / Math.sqrt(r2); + for (i = 0; i < d; ++i) { + v[i] *= h; + } + return v; + } } - - return Math.sqrt(result); -}; - -},{}],11:[function(require,module,exports){ -"use strict"; - -var zeros = require('zeros'), - moore = require('moore'), - euclideanDistanceN = require('./euclidean-distance'), - sphereRandom = require('./sphere-random'); - -/** - * Get the neighbourhood ordered by distance, including the origin point - * @param {int} dimensionNumber Number of dimensions - * @returns {Array} Neighbourhood - */ -var getNeighbourhood = function getNeighbourhood (dimensionNumber) { - var neighbourhood = moore(2, dimensionNumber), - origin = [], - dimension; - - for (dimension = 0; dimension < dimensionNumber; dimension++) { - origin.push(0); + }); + + // node_modules/moore/index.js + var require_moore = __commonJS({ + "node_modules/moore/index.js"(exports, module) { + module.exports = function moore(range, dimensions) { + range = range || 1; + dimensions = dimensions || 2; + var size = range * 2 + 1; + var length = Math.pow(size, dimensions) - 1; + var neighbors = new Array(length); + for (var i = 0; i < length; i++) { + var neighbor = neighbors[i] = new Array(dimensions); + var index = i < length / 2 ? i : i + 1; + for (var dimension = 1; dimension <= dimensions; dimension++) { + var value = index % Math.pow(size, dimension); + neighbor[dimension - 1] = value / Math.pow(size, dimension - 1) - range; + index -= value; + } + } + return neighbors; + }; } - - neighbourhood.push(origin); - - // sort by ascending distance to optimize proximity checks - // see point 5.1 in Parallel Poisson Disk Sampling by Li-Yi Wei, 2008 - // http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.460.3061&rank=1 - neighbourhood.sort(function (n1, n2) { - var squareDist1 = 0, - squareDist2 = 0; - - for (var dimension = 0; dimension < dimensionNumber; dimension++) { - squareDist1 += Math.pow(n1[dimension], 2); - squareDist2 += Math.pow(n2[dimension], 2); + }); + + // node_modules/poisson-disk-sampling/src/neighbourhood.js + var require_neighbourhood = __commonJS({ + "node_modules/poisson-disk-sampling/src/neighbourhood.js"(exports, module) { + "use strict"; + var moore = require_moore(); + function getNeighbourhood(dimensionNumber) { + var neighbourhood = moore(2, dimensionNumber), origin = [], dimension; + neighbourhood = neighbourhood.filter(function(n) { + var dist = 0; + for (var d = 0; d < dimensionNumber; d++) { + dist += Math.pow(Math.max(0, Math.abs(n[d]) - 1), 2); + } + return dist < dimensionNumber; + }); + for (dimension = 0; dimension < dimensionNumber; dimension++) { + origin.push(0); } - - if (squareDist1 < squareDist2) { + neighbourhood.push(origin); + neighbourhood.sort(function(n1, n2) { + var squareDist1 = 0, squareDist2 = 0, dimension2; + for (dimension2 = 0; dimension2 < dimensionNumber; dimension2++) { + squareDist1 += Math.pow(n1[dimension2], 2); + squareDist2 += Math.pow(n2[dimension2], 2); + } + if (squareDist1 < squareDist2) { return -1; - } else if(squareDist1 > squareDist2) { + } else if (squareDist1 > squareDist2) { return 1; - } else { + } else { return 0; + } + }); + return neighbourhood; + } + var neighbourhoodCache = {}; + function getNeighbourhoodMemoized(dimensionNumber) { + if (!neighbourhoodCache[dimensionNumber]) { + neighbourhoodCache[dimensionNumber] = getNeighbourhood(dimensionNumber); } - }); - - return neighbourhood; -}; - - -/** - * PoissonDiskSampling constructor - * @param {Array} shape Shape of the space - * @param {float} minDistance Minimum distance between each points - * @param {float} [maxDistance] Maximum distance between each points, defaults to minDistance * 2 - * @param {int} [maxTries] Number of times the algorithm has to try to place a point in the neighbourhood of another points, defaults to 30 - * @param {function|null} [rng] RNG function, defaults to Math.random - * @constructor - */ -var PoissonDiskSampling = function PoissonDiskSampling (shape, minDistance, maxDistance, maxTries, rng) { - maxDistance = maxDistance || minDistance * 2; - - this.shape = shape; - this.dimension = this.shape.length; - this.minDistance = minDistance; - this.deltaDistance = maxDistance - minDistance; - this.cellSize = minDistance / Math.sqrt(this.dimension); - this.maxTries = maxTries || 30; - this.rng = rng || Math.random; - - this.neighbourhood = getNeighbourhood(this.dimension); - - this.currentPoint = null; - this.processList = []; - this.samplePoints = []; - - // cache grid - - this.gridShape = []; - - for (var i = 0; i < this.dimension; i++) { - this.gridShape.push(Math.ceil(shape[i] / this.cellSize)); - } - - this.grid = zeros(this.gridShape, 'uint32'); //will store references to samplePoints -}; - -PoissonDiskSampling.prototype.shape = null; -PoissonDiskSampling.prototype.dimension = null; -PoissonDiskSampling.prototype.minDistance = null; -PoissonDiskSampling.prototype.deltaDistance = null; -PoissonDiskSampling.prototype.cellSize = null; -PoissonDiskSampling.prototype.maxTries = null; -PoissonDiskSampling.prototype.rng = null; -PoissonDiskSampling.prototype.neighbourhood = null; - -PoissonDiskSampling.prototype.currentPoint = null; -PoissonDiskSampling.prototype.processList = null; -PoissonDiskSampling.prototype.samplePoints = null; -PoissonDiskSampling.prototype.gridShape = null; -PoissonDiskSampling.prototype.grid = null; - -/** - * Add a totally random point in the grid - * @returns {Array} The point added to the grid - */ -PoissonDiskSampling.prototype.addRandomPoint = function () { - var point = new Array(this.dimension); - - for (var i = 0; i < this.dimension; i++) { - point[i] = this.rng() * this.shape[i]; + return neighbourhoodCache[dimensionNumber]; + } + module.exports = getNeighbourhoodMemoized; } - - return this.directAddPoint(point); -}; - -/** - * Add a given point to the grid - * @param {Array} point Point - * @returns {Array|null} The point added to the grid, null if the point is out of the bound or not of the correct dimension - */ -PoissonDiskSampling.prototype.addPoint = function (point) { - var dimension, - valid = true; - - if (point.length === this.dimension) { - for (dimension = 0; dimension < this.dimension && valid; dimension++) { - valid = (point[dimension] >= 0 && point[dimension] <= this.shape[dimension]); + }); + + // node_modules/poisson-disk-sampling/src/implementations/fixed-density.js + var require_fixed_density = __commonJS({ + "node_modules/poisson-disk-sampling/src/implementations/fixed-density.js"(exports, module) { + "use strict"; + var tinyNDArray = require_tiny_ndarray().integer; + var sphereRandom = require_sphere_random(); + var getNeighbourhood = require_neighbourhood(); + function squaredEuclideanDistance(point1, point2) { + var result = 0, i = 0; + for (; i < point1.length; i++) { + result += Math.pow(point1[i] - point2[i], 2); } - } else { - valid = false; - } - - return valid ? this.directAddPoint(point) : null; -}; - -/** - * Add a given point to the grid, without any check - * @param {Array} point Point - * @returns {Array} The point added to the grid - * @protected - */ -PoissonDiskSampling.prototype.directAddPoint = function (point) { - var internalArrayIndex = 0, - stride = this.grid.stride, - dimension; - - this.processList.push(point); - this.samplePoints.push(point); - - for (dimension = 0; dimension < this.dimension; dimension++) { - internalArrayIndex += ((point[dimension] / this.cellSize) | 0) * stride[dimension]; - } - - this.grid.data[internalArrayIndex] = this.samplePoints.length; // store the point reference - - return point; -}; - -/** - * Check whether a given point is in the neighbourhood of existing points - * @param {Array} point Point - * @returns {boolean} Whether the point is in the neighbourhood of another point - * @protected - */ -PoissonDiskSampling.prototype.inNeighbourhood = function (point) { - var dimensionNumber = this.dimension, - stride = this.grid.stride, - neighbourIndex, - internalArrayIndex, - dimension, - currentDimensionValue, - existingPoint; - - for (neighbourIndex = 0; neighbourIndex < this.neighbourhood.length; neighbourIndex++) { - internalArrayIndex = 0; - - for (dimension = 0; dimension < dimensionNumber; dimension++) { - currentDimensionValue = ((point[dimension] / this.cellSize) | 0) + this.neighbourhood[neighbourIndex][dimension]; - - if (currentDimensionValue >= 0 && currentDimensionValue < this.gridShape[dimension]) { - internalArrayIndex += currentDimensionValue * stride[dimension]; - } + return result; + } + var epsilon = 2e-14; + function FixedDensityPDS(options, rng) { + if (typeof options.distanceFunction === "function") { + throw new Error("PoissonDiskSampling: Tried to instantiate the fixed density implementation with a distanceFunction"); } - - if (this.grid.data[internalArrayIndex] !== 0) { + this.shape = options.shape; + this.minDistance = options.minDistance; + this.maxDistance = options.maxDistance || options.minDistance * 2; + this.maxTries = Math.ceil(Math.max(1, options.tries || 30)); + this.rng = rng || Math.random; + this.dimension = this.shape.length; + this.squaredMinDistance = this.minDistance * this.minDistance; + this.minDistancePlusEpsilon = this.minDistance + epsilon; + this.deltaDistance = Math.max(0, this.maxDistance - this.minDistancePlusEpsilon); + this.cellSize = this.minDistance / Math.sqrt(this.dimension); + this.neighbourhood = getNeighbourhood(this.dimension); + this.currentPoint = null; + this.processList = []; + this.samplePoints = []; + this.gridShape = []; + for (var i = 0; i < this.dimension; i++) { + this.gridShape.push(Math.ceil(this.shape[i] / this.cellSize)); + } + this.grid = tinyNDArray(this.gridShape); + } + FixedDensityPDS.prototype.shape = null; + FixedDensityPDS.prototype.dimension = null; + FixedDensityPDS.prototype.minDistance = null; + FixedDensityPDS.prototype.maxDistance = null; + FixedDensityPDS.prototype.minDistancePlusEpsilon = null; + FixedDensityPDS.prototype.squaredMinDistance = null; + FixedDensityPDS.prototype.deltaDistance = null; + FixedDensityPDS.prototype.cellSize = null; + FixedDensityPDS.prototype.maxTries = null; + FixedDensityPDS.prototype.rng = null; + FixedDensityPDS.prototype.neighbourhood = null; + FixedDensityPDS.prototype.currentPoint = null; + FixedDensityPDS.prototype.processList = null; + FixedDensityPDS.prototype.samplePoints = null; + FixedDensityPDS.prototype.gridShape = null; + FixedDensityPDS.prototype.grid = null; + FixedDensityPDS.prototype.addRandomPoint = function() { + var point = new Array(this.dimension); + for (var i = 0; i < this.dimension; i++) { + point[i] = this.rng() * this.shape[i]; + } + return this.directAddPoint(point); + }; + FixedDensityPDS.prototype.addPoint = function(point) { + var dimension, valid = true; + if (point.length === this.dimension) { + for (dimension = 0; dimension < this.dimension && valid; dimension++) { + valid = point[dimension] >= 0 && point[dimension] <= this.shape[dimension]; + } + } else { + valid = false; + } + return valid ? this.directAddPoint(point) : null; + }; + FixedDensityPDS.prototype.directAddPoint = function(point) { + var internalArrayIndex = 0, stride = this.grid.stride, dimension; + this.processList.push(point); + this.samplePoints.push(point); + for (dimension = 0; dimension < this.dimension; dimension++) { + internalArrayIndex += (point[dimension] / this.cellSize | 0) * stride[dimension]; + } + this.grid.data[internalArrayIndex] = this.samplePoints.length; + return point; + }; + FixedDensityPDS.prototype.inNeighbourhood = function(point) { + var dimensionNumber = this.dimension, stride = this.grid.stride, neighbourIndex, internalArrayIndex, dimension, currentDimensionValue, existingPoint; + for (neighbourIndex = 0; neighbourIndex < this.neighbourhood.length; neighbourIndex++) { + internalArrayIndex = 0; + for (dimension = 0; dimension < dimensionNumber; dimension++) { + currentDimensionValue = (point[dimension] / this.cellSize | 0) + this.neighbourhood[neighbourIndex][dimension]; + if (currentDimensionValue < 0 || currentDimensionValue >= this.gridShape[dimension]) { + internalArrayIndex = -1; + break; + } + internalArrayIndex += currentDimensionValue * stride[dimension]; + } + if (internalArrayIndex !== -1 && this.grid.data[internalArrayIndex] !== 0) { existingPoint = this.samplePoints[this.grid.data[internalArrayIndex] - 1]; - - if (euclideanDistanceN(point, existingPoint) < this.minDistance) { - return true; + if (squaredEuclideanDistance(point, existingPoint) < this.squaredMinDistance) { + return true; } + } } - } - - return false; -}; - -/** - * Try to generate a new point in the grid, returns null if it wasn't possible - * @returns {Array|null} The added point or null - */ -PoissonDiskSampling.prototype.next = function () { - var tries, - angle, - distance, - currentPoint, - newPoint, - inShape, - i; - - while (this.processList.length > 0) { - if (this.currentPoint === null) { + return false; + }; + FixedDensityPDS.prototype.next = function() { + var tries, angle, distance, currentPoint, newPoint, inShape, i; + while (this.processList.length > 0) { + if (this.currentPoint === null) { this.currentPoint = this.processList.shift(); - } - - currentPoint = this.currentPoint; - - for (tries = 0; tries < this.maxTries; tries++) { + } + currentPoint = this.currentPoint; + for (tries = 0; tries < this.maxTries; tries++) { inShape = true; - distance = this.minDistance + this.deltaDistance * this.rng(); - + distance = this.minDistancePlusEpsilon + this.deltaDistance * this.rng(); if (this.dimension === 2) { - angle = this.rng() * Math.PI * 2; - newPoint = [ - Math.cos(angle), - Math.sin(angle) - ]; + angle = this.rng() * Math.PI * 2; + newPoint = [ + Math.cos(angle), + Math.sin(angle) + ]; } else { - newPoint = sphereRandom(this.dimension, this.rng); + newPoint = sphereRandom(this.dimension, this.rng); } - for (i = 0; inShape && i < this.dimension; i++) { - newPoint[i] = currentPoint[i] + newPoint[i] * distance; - inShape = (newPoint[i] >= 0 && newPoint[i] <= this.shape[i] - 1) + newPoint[i] = currentPoint[i] + newPoint[i] * distance; + inShape = newPoint[i] >= 0 && newPoint[i] < this.shape[i]; } - if (inShape && !this.inNeighbourhood(newPoint)) { - return this.directAddPoint(newPoint); + return this.directAddPoint(newPoint); } - } - - if (tries >= this.maxTries) { + } + if (tries === this.maxTries) { this.currentPoint = null; + } } + return null; + }; + FixedDensityPDS.prototype.fill = function() { + if (this.samplePoints.length === 0) { + this.addRandomPoint(); + } + while (this.next()) { + } + return this.samplePoints; + }; + FixedDensityPDS.prototype.getAllPoints = function() { + return this.samplePoints; + }; + FixedDensityPDS.prototype.getAllPointsWithDistance = function() { + throw new Error("PoissonDiskSampling: getAllPointsWithDistance() is not available in fixed-density implementation"); + }; + FixedDensityPDS.prototype.reset = function() { + var gridData = this.grid.data, i = 0; + for (i = 0; i < gridData.length; i++) { + gridData[i] = 0; + } + this.samplePoints = []; + this.currentPoint = null; + this.processList.length = 0; + }; + module.exports = FixedDensityPDS; } - - return null; -}; - -/** - * Automatically fill the grid, adding a random point to start the process if needed. - * Will block the thread, probably best to use it in a web worker or child process. - * @returns {Array[]} Sample points - */ -PoissonDiskSampling.prototype.fill = function () { - if (this.samplePoints.length === 0) { - this.addRandomPoint(); - } - - while(this.next()) {} - - return this.samplePoints; -}; - -/** - * Get all the points in the grid. - * @returns {Array[]} Sample points - */ -PoissonDiskSampling.prototype.getAllPoints = function () { - return this.samplePoints; -}; - -/** - * Reinitialize the grid as well as the internal state - */ -PoissonDiskSampling.prototype.reset = function () { - var gridData = this.grid.data, - i = 0; - - // reset the cache grid - for (i = 0; i < gridData.length; i++) { - gridData[i] = 0; + }); + + // node_modules/poisson-disk-sampling/src/implementations/variable-density.js + var require_variable_density = __commonJS({ + "node_modules/poisson-disk-sampling/src/implementations/variable-density.js"(exports, module) { + "use strict"; + var tinyNDArray = require_tiny_ndarray().array; + var sphereRandom = require_sphere_random(); + var getNeighbourhood = require_neighbourhood(); + function euclideanDistance(point1, point2) { + var result = 0, i = 0; + for (; i < point1.length; i++) { + result += Math.pow(point1[i] - point2[i], 2); + } + return Math.sqrt(result); + } + var epsilon = 2e-14; + function VariableDensityPDS(options, rng) { + if (typeof options.distanceFunction !== "function") { + throw new Error("PoissonDiskSampling: Tried to instantiate the variable density implementation without a distanceFunction"); + } + this.shape = options.shape; + this.minDistance = options.minDistance; + this.maxDistance = options.maxDistance || options.minDistance * 2; + this.maxTries = Math.ceil(Math.max(1, options.tries || 30)); + this.distanceFunction = options.distanceFunction; + this.bias = Math.max(0, Math.min(1, options.bias || 0)); + this.rng = rng || Math.random; + this.dimension = this.shape.length; + this.minDistancePlusEpsilon = this.minDistance + epsilon; + this.deltaDistance = Math.max(0, this.maxDistance - this.minDistancePlusEpsilon); + this.cellSize = this.maxDistance / Math.sqrt(this.dimension); + this.neighbourhood = getNeighbourhood(this.dimension); + this.currentPoint = null; + this.currentDistance = 0; + this.processList = []; + this.samplePoints = []; + this.sampleDistance = []; + this.gridShape = []; + for (var i = 0; i < this.dimension; i++) { + this.gridShape.push(Math.ceil(this.shape[i] / this.cellSize)); + } + this.grid = tinyNDArray(this.gridShape); + } + VariableDensityPDS.prototype.shape = null; + VariableDensityPDS.prototype.dimension = null; + VariableDensityPDS.prototype.minDistance = null; + VariableDensityPDS.prototype.maxDistance = null; + VariableDensityPDS.prototype.minDistancePlusEpsilon = null; + VariableDensityPDS.prototype.deltaDistance = null; + VariableDensityPDS.prototype.cellSize = null; + VariableDensityPDS.prototype.maxTries = null; + VariableDensityPDS.prototype.distanceFunction = null; + VariableDensityPDS.prototype.bias = null; + VariableDensityPDS.prototype.rng = null; + VariableDensityPDS.prototype.neighbourhood = null; + VariableDensityPDS.prototype.currentPoint = null; + VariableDensityPDS.prototype.currentDistance = null; + VariableDensityPDS.prototype.processList = null; + VariableDensityPDS.prototype.samplePoints = null; + VariableDensityPDS.prototype.sampleDistance = null; + VariableDensityPDS.prototype.gridShape = null; + VariableDensityPDS.prototype.grid = null; + VariableDensityPDS.prototype.addRandomPoint = function() { + var point = new Array(this.dimension); + for (var i = 0; i < this.dimension; i++) { + point[i] = this.rng() * this.shape[i]; + } + return this.directAddPoint(point); + }; + VariableDensityPDS.prototype.addPoint = function(point) { + var dimension, valid = true; + if (point.length === this.dimension) { + for (dimension = 0; dimension < this.dimension && valid; dimension++) { + valid = point[dimension] >= 0 && point[dimension] <= this.shape[dimension]; + } + } else { + valid = false; + } + return valid ? this.directAddPoint(point) : null; + }; + VariableDensityPDS.prototype.directAddPoint = function(point) { + var internalArrayIndex = 0, stride = this.grid.stride, pointIndex = this.samplePoints.length, dimension; + this.processList.push(pointIndex); + this.samplePoints.push(point); + this.sampleDistance.push(this.distanceFunction(point)); + for (dimension = 0; dimension < this.dimension; dimension++) { + internalArrayIndex += (point[dimension] / this.cellSize | 0) * stride[dimension]; + } + this.grid.data[internalArrayIndex].push(pointIndex); + return point; + }; + VariableDensityPDS.prototype.inNeighbourhood = function(point) { + var dimensionNumber = this.dimension, stride = this.grid.stride, neighbourIndex, internalArrayIndex, dimension, currentDimensionValue, existingPoint, existingPointDistance; + var pointDistance = this.distanceFunction(point); + for (neighbourIndex = 0; neighbourIndex < this.neighbourhood.length; neighbourIndex++) { + internalArrayIndex = 0; + for (dimension = 0; dimension < dimensionNumber; dimension++) { + currentDimensionValue = (point[dimension] / this.cellSize | 0) + this.neighbourhood[neighbourIndex][dimension]; + if (currentDimensionValue < 0 || currentDimensionValue >= this.gridShape[dimension]) { + internalArrayIndex = -1; + break; + } + internalArrayIndex += currentDimensionValue * stride[dimension]; + } + if (internalArrayIndex !== -1 && this.grid.data[internalArrayIndex].length > 0) { + for (var i = 0; i < this.grid.data[internalArrayIndex].length; i++) { + existingPoint = this.samplePoints[this.grid.data[internalArrayIndex][i]]; + existingPointDistance = this.sampleDistance[this.grid.data[internalArrayIndex][i]]; + var minDistance = Math.min(existingPointDistance, pointDistance); + var maxDistance = Math.max(existingPointDistance, pointDistance); + var dist = minDistance + (maxDistance - minDistance) * this.bias; + if (euclideanDistance(point, existingPoint) < this.minDistance + this.deltaDistance * dist) { + return true; + } + } + } + } + return false; + }; + VariableDensityPDS.prototype.next = function() { + var tries, angle, distance, currentPoint, currentDistance, newPoint, inShape, i; + while (this.processList.length > 0) { + if (this.currentPoint === null) { + var sampleIndex = this.processList.shift(); + this.currentPoint = this.samplePoints[sampleIndex]; + this.currentDistance = this.sampleDistance[sampleIndex]; + } + currentPoint = this.currentPoint; + currentDistance = this.currentDistance; + for (tries = 0; tries < this.maxTries; tries++) { + inShape = true; + distance = this.minDistancePlusEpsilon + this.deltaDistance * (currentDistance + (1 - currentDistance) * this.bias); + if (this.dimension === 2) { + angle = this.rng() * Math.PI * 2; + newPoint = [ + Math.cos(angle), + Math.sin(angle) + ]; + } else { + newPoint = sphereRandom(this.dimension, this.rng); + } + for (i = 0; inShape && i < this.dimension; i++) { + newPoint[i] = currentPoint[i] + newPoint[i] * distance; + inShape = newPoint[i] >= 0 && newPoint[i] < this.shape[i]; + } + if (inShape && !this.inNeighbourhood(newPoint)) { + return this.directAddPoint(newPoint); + } + } + if (tries === this.maxTries) { + this.currentPoint = null; + } + } + return null; + }; + VariableDensityPDS.prototype.fill = function() { + if (this.samplePoints.length === 0) { + this.addRandomPoint(); + } + while (this.next()) { + } + return this.samplePoints; + }; + VariableDensityPDS.prototype.getAllPoints = function() { + return this.samplePoints; + }; + VariableDensityPDS.prototype.getAllPointsWithDistance = function() { + var result = new Array(this.samplePoints.length), i = 0, dimension = 0, point; + for (i = 0; i < this.samplePoints.length; i++) { + point = new Array(this.dimension + 1); + for (dimension = 0; dimension < this.dimension; dimension++) { + point[dimension] = this.samplePoints[i][dimension]; + } + point[this.dimension] = this.sampleDistance[i]; + result[i] = point; + } + return result; + }; + VariableDensityPDS.prototype.reset = function() { + var gridData = this.grid.data, i = 0; + for (i = 0; i < gridData.length; i++) { + gridData[i] = []; + } + this.samplePoints = []; + this.currentPoint = null; + this.processList.length = 0; + }; + module.exports = VariableDensityPDS; } - - // new array for the samplePoints as it is passed by reference to the outside - this.samplePoints = []; - - // reset the internal state - this.currentPoint = null; - this.processList.length = 0; -}; - -module.exports = PoissonDiskSampling; - -},{"./euclidean-distance":10,"./sphere-random":12,"moore":7,"zeros":13}],12:[function(require,module,exports){ -"use strict"; - -// sphere-random module by Mikola Lysenko under the MIT License -// waiting for https://github.com/scijs/sphere-random/pull/1 to be merged - -module.exports = sampleSphere; - -var defaultRng = Math.random; - -/** - * @param {int} d Dimensions - * @param {Function} [rng] - * @returns {Array} - */ -function sampleSphere(d, rng) { - var v = new Array(d), - d2 = Math.floor(d/2) << 1, - r2 = 0.0, - rr, - r, - theta, - h, - i; - - rng = rng || defaultRng; - - for (i = 0; i < d2; i += 2) { - rr = -2.0 * Math.log(rng()); - r = Math.sqrt(rr); - theta = 2.0 * Math.PI * rng(); - - r2+= rr; - v[i] = r * Math.cos(theta); - v[i+1] = r * Math.sin(theta); + }); + + // node_modules/poisson-disk-sampling/src/poisson-disk-sampling.js + var require_poisson_disk_sampling = __commonJS({ + "node_modules/poisson-disk-sampling/src/poisson-disk-sampling.js"(exports, module) { + "use strict"; + var FixedDensityPDS = require_fixed_density(); + var VariableDensityPDS = require_variable_density(); + function PoissonDiskSampling(options, rng) { + this.shape = options.shape; + if (typeof options.distanceFunction === "function") { + this.implementation = new VariableDensityPDS(options, rng); + } else { + this.implementation = new FixedDensityPDS(options, rng); + } + } + PoissonDiskSampling.prototype.implementation = null; + PoissonDiskSampling.prototype.addRandomPoint = function() { + return this.implementation.addRandomPoint(); + }; + PoissonDiskSampling.prototype.addPoint = function(point) { + return this.implementation.addPoint(point); + }; + PoissonDiskSampling.prototype.next = function() { + return this.implementation.next(); + }; + PoissonDiskSampling.prototype.fill = function() { + return this.implementation.fill(); + }; + PoissonDiskSampling.prototype.getAllPoints = function() { + return this.implementation.getAllPoints(); + }; + PoissonDiskSampling.prototype.getAllPointsWithDistance = function() { + return this.implementation.getAllPointsWithDistance(); + }; + PoissonDiskSampling.prototype.reset = function() { + this.implementation.reset(); + }; + module.exports = PoissonDiskSampling; } - - if (d % 2) { - var x = Math.sqrt(-2.0 * Math.log(rng())) * Math.cos(2.0 * Math.PI * rng()); - v[d - 1] = x; - r2+= Math.pow(x, 2); + }); + + // docs/diagrams.js + var DualMesh = require_dual_mesh(); + var MeshBuilder = require_create(); + var Poisson = require_poisson_disk_sampling(); + var seeds1 = [ + [250, 30], + [100, 260], + [400, 260], + [550, 30] + ]; + var seeds2 = [ + [320, 170], + [220, 270], + [400, 270], + [530, 50], + [100, 80], + [300, 30], + [50, 220], + [550, 240] + ]; + var G0 = new MeshBuilder({ boundarySpacing: 75 }).addPoisson(Poisson, 50).create(); + var G1 = new MeshBuilder().addPoints(seeds1).create(); + var G2 = new MeshBuilder().addPoints(seeds2).create(); + function interpolate(p, q, t) { + return [p[0] * (1 - t) + q[0] * t, p[1] * (1 - t) + q[1] * t]; + } + function extrapolate_from_center(p, center) { + let dx = p[0] - center[0], dy = p[1] - center[1]; + return [center[0] + dx * 5, center[1] + dy * 5]; + } + Vue.component("a-label", { + props: ["at", "dx", "dy"], + template: '' + }); + Vue.component("a-side-black-edges", { + props: ["graph", "alpha"], + template: ` + + + +`, + methods: { + b_side: function(s) { + const alpha = this.alpha || 0; + let begin = this.graph.r_pos([], this.graph.s_begin_r(s)); + let end = this.graph.r_pos([], this.graph.s_end_r(s)); + if (this.graph.r_ghost(this.graph.s_begin_r(s))) { + begin = extrapolate_from_center(end, [300, 150]); + } else if (this.graph.r_ghost(this.graph.s_end_r(s))) { + end = extrapolate_from_center(begin, [300, 150]); + } + let center = this.graph.t_pos([], this.graph.s_inner_t(s)); + begin = interpolate(begin, center, alpha); + end = interpolate(end, center, alpha); + return `M ${begin} L ${end}`; + } } - - h = 1.0 / Math.sqrt(r2); - - for (i=0; i + + +`, + methods: { + w_side: function(s) { + const alpha = this.alpha || 0; + let begin = this.graph.t_pos([], this.graph.s_inner_t(s)); + let end = this.graph.t_pos([], this.graph.s_outer_t(s)); + let center = this.graph.r_pos([], this.graph.s_begin_r(s)); + begin = interpolate(begin, center, alpha); + end = interpolate(end, center, alpha); + return `M ${begin} L ${end}`; + } } - - return v; -} - -},{}],13:[function(require,module,exports){ -"use strict" - -var ndarray = require("ndarray") - -function dtypeToType(dtype) { - switch(dtype) { - case 'uint8': - return Uint8Array; - case 'uint16': - return Uint16Array; - case 'uint32': - return Uint32Array; - case 'int8': - return Int8Array; - case 'int16': - return Int16Array; - case 'int32': - return Int32Array; - case 'float': - case 'float32': - return Float32Array; - case 'double': - case 'float64': - return Float64Array; - case 'uint8_clamped': - return Uint8ClampedArray; - case 'generic': - case 'buffer': - case 'data': - case 'dataview': - return ArrayBuffer; - case 'array': - return Array; + }); + Vue.component("a-side-labels", { + props: ["graph"], + template: ` + + + s{{s}} + + +`, + methods: { interpolate } + }); + Vue.component("a-region-points", { + props: ["graph", "hover", "radius"], + template: ` + + + +` + }); + Vue.component("a-region-labels", { + props: ["graph"], + template: ` + + + r{{r}} + + +` + }); + Vue.component("a-triangle-points", { + props: ["graph", "hover", "radius"], + template: ` + + + +` + }); + Vue.component("a-triangle-labels", { + props: ["graph"], + template: ` + + + t{{t}} + + +` + }); + function makeDiagram(selector, graph) { + new Vue({ + el: selector, + data: { + graph: Object.freeze(graph), + highlight: "" + }, + computed: { + highlightId: function() { + return parseInt(this.highlight.slice(1)); + } + }, + methods: { + hover: function(label) { + this.highlight = label; + }, + format_array: function(label, array) { + return array.map((x) => x === null || x < 0 ? "(null)" : label + x).join(" "); + } + } + }); } -} - -module.exports = function zeros(shape, dtype) { - dtype = dtype || 'float64'; - var sz = 1; - for(var i=0; idist/dual-mesh.js", "test": "node tests.js", - "docs": "watchify docs/diagrams.js -o docs/_bundle.js" + "docs": "esbuild --bundle docs/diagrams.js >docs/_bundle.js" } } diff --git a/rollup.config.js b/rollup.config.js deleted file mode 100644 index 0d0c2d8..0000000 --- a/rollup.config.js +++ /dev/null @@ -1,18 +0,0 @@ -/* based on https://github.com/rollup/rollup-starter-lib/blob/master/rollup.config.js */ - -import resolve from '@rollup/plugin-node-resolve'; -import commonjs from '@rollup/plugin-commonjs'; -import pkg from './package.json'; - -export default [ - // browser-friendly UMD build - { - input: 'index.js', - output: { - name: 'DualMesh', - file: pkg.browser, - format: 'umd' - }, - plugins: [ resolve(), commonjs() ] - } -]; diff --git a/yarn.lock b/yarn.lock index 890aa37..fd2549f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,66 +2,6 @@ # yarn lockfile v1 -"@rollup/plugin-commonjs@^11": - version "11.1.0" - resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-11.1.0.tgz#60636c7a722f54b41e419e1709df05c7234557ef" - integrity sha512-Ycr12N3ZPN96Fw2STurD21jMqzKwL9QuFhms3SD7KKRK7oaXUsBU9Zt0jL/rOPHiPYisI21/rXGO3jr9BnLHUA== - dependencies: - "@rollup/pluginutils" "^3.0.8" - commondir "^1.0.1" - estree-walker "^1.0.1" - glob "^7.1.2" - is-reference "^1.1.2" - magic-string "^0.25.2" - resolve "^1.11.0" - -"@rollup/plugin-node-resolve@^7": - version "7.1.3" - resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-7.1.3.tgz#80de384edfbd7bfc9101164910f86078151a3eca" - integrity sha512-RxtSL3XmdTAE2byxekYLnx+98kEUOrPHF/KRVjLH+DEIHy6kjIw7YINQzn+NXiH/NTrQLAwYs0GWB+csWygA9Q== - dependencies: - "@rollup/pluginutils" "^3.0.8" - "@types/resolve" "0.0.8" - builtin-modules "^3.1.0" - is-module "^1.0.0" - resolve "^1.14.2" - -"@rollup/pluginutils@^3.0.8": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b" - integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg== - dependencies: - "@types/estree" "0.0.39" - estree-walker "^1.0.1" - picomatch "^2.2.2" - -"@types/estree@*": - version "0.0.46" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.46.tgz#0fb6bfbbeabd7a30880504993369c4bf1deab1fe" - integrity sha512-laIjwTQaD+5DukBZaygQ79K1Z0jb1bPEMRrkXSLjtCcZm+abyp5YbrqpSLzD42FwWW6gK/aS4NYpJ804nG2brg== - -"@types/estree@0.0.39": - version "0.0.39" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" - integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== - -"@types/node@*": - version "14.14.31" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.31.tgz#72286bd33d137aa0d152d47ec7c1762563d34055" - integrity sha512-vFHy/ezP5qI0rFgJ7aQnjDXwAMrG0KqqIH7tQG5PPv3BWBayOPIQNBjVc/P6hhdZfMx51REc6tfDNXHUio893g== - -"@types/resolve@0.0.8": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194" - integrity sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ== - dependencies: - "@types/node" "*" - -acorn@^7.1.0: - version "7.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" - integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== - array-filter@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-1.0.0.tgz#baf79e62e6ef4c2a4c0b831232daffec251f9d83" @@ -87,11 +27,6 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -builtin-modules@^3.1.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.2.0.tgz#45d5db99e7ee5e6bc4f362e008bf917ab5049887" - integrity sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA== - call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" @@ -100,11 +35,6 @@ call-bind@^1.0.0, call-bind@^1.0.2: function-bind "^1.1.1" get-intrinsic "^1.0.2" -commondir@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" - integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= - concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -198,11 +128,6 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" -estree-walker@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700" - integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg== - for-each@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" @@ -234,7 +159,7 @@ get-intrinsic@^1.0.1, get-intrinsic@^1.0.2, get-intrinsic@^1.1.0: has "^1.0.3" has-symbols "^1.0.1" -glob@^7.1.2, glob@^7.1.6: +glob@^7.1.6: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== @@ -312,11 +237,6 @@ is-map@^2.0.1, is-map@^2.0.2: resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127" integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg== -is-module@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" - integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE= - is-negative-zero@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" @@ -327,13 +247,6 @@ is-number-object@^1.0.4: resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.4.tgz#36ac95e741cf18b283fc1ddf5e83da798e3ec197" integrity sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw== -is-reference@^1.1.2: - version "1.2.1" - resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7" - integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ== - dependencies: - "@types/estree" "*" - is-regex@^1.1.1, is-regex@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.2.tgz#81c8ebde4db142f2cf1c53fc86d6a45788266251" @@ -385,13 +298,6 @@ isarray@^2.0.5: resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== -magic-string@^0.25.2: - version "0.25.7" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" - integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA== - dependencies: - sourcemap-codec "^1.4.4" - minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" @@ -454,11 +360,6 @@ path-parse@^1.0.6: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== -picomatch@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" - integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== - poisson-disk-sampling@^2: version "2.2.2" resolved "https://registry.yarnpkg.com/poisson-disk-sampling/-/poisson-disk-sampling-2.2.2.tgz#d326f6be7e1704e1d54e6a024bd44a3fb33120e5" @@ -474,14 +375,6 @@ regexp.prototype.flags@^1.3.0: call-bind "^1.0.2" define-properties "^1.1.3" -resolve@^1.11.0, resolve@^1.14.2: - version "1.20.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" - integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== - dependencies: - is-core-module "^2.2.0" - path-parse "^1.0.6" - resolve@^2.0.0-next.3: version "2.0.0-next.3" resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.3.tgz#d41016293d4a8586a39ca5d9b5f15cbea1f55e46" @@ -497,15 +390,6 @@ resumer@^0.0.0: dependencies: through "~2.3.4" -rollup@^1.0.0: - version "1.32.1" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-1.32.1.tgz#4480e52d9d9e2ae4b46ba0d9ddeaf3163940f9c4" - integrity sha512-/2HA0Ec70TvQnXdzynFffkjA6XN+1e2pEv/uKS5Ulca40g2L7KuOE3riasHoNVHOsFD5KKZgDsMk1CP3Tw9s+A== - dependencies: - "@types/estree" "*" - "@types/node" "*" - acorn "^7.1.0" - side-channel@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" @@ -515,11 +399,6 @@ side-channel@^1.0.3: get-intrinsic "^1.0.2" object-inspect "^1.9.0" -sourcemap-codec@^1.4.4: - version "1.4.8" - resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" - integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== - string.prototype.trim@^1.2.3: version "1.2.4" resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.4.tgz#6014689baf5efaf106ad031a5fa45157666ed1bd"