You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

1757 lines
44 KiB

(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.concaveman = f()}})(function(){var define,module,exports;return (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<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
'use strict';
var rbush = require('rbush');
var convexHull = require('monotone-convex-hull-2d');
var Queue = require('tinyqueue');
var pointInPolygon = require('point-in-polygon');
var orient = require('robust-orientation')[3];
module.exports = concaveman;
function concaveman(points, concavity, lengthThreshold) {
// a relative measure of concavity; higher value means simpler hull
concavity = Math.max(0, concavity === undefined ? 2 : concavity);
// when a segment goes below this length threshold, it won't be drilled down further
lengthThreshold = lengthThreshold || 0;
// start with a convex hull of the points
var hull = fastConvexHull(points);
// index the points with an R-tree
var tree = rbush(16, ['[0]', '[1]', '[0]', '[1]']).load(points);
// turn the convex hull into a linked list and populate the initial edge queue with the nodes
var queue = [];
for (var i = 0, last; i < hull.length; i++) {
var p = hull[i];
tree.remove(p);
last = insertNode(p, last);
queue.push(last);
}
// index the segments with an R-tree (for intersection checks)
var segTree = rbush(16, ['.minX', '.minY', '.maxX', '.maxY']);
for (i = 0; i < queue.length; i++) segTree.insert(updateBBox(queue[i]));
var sqConcavity = concavity * concavity;
var sqLenThreshold = lengthThreshold * lengthThreshold;
// process edges one by one
while (queue.length) {
var node = queue.shift();
var a = node.p;
var b = node.next.p;
// skip the edge if it's already short enough
var sqLen = getSqDist(a, b);
if (sqLen < sqLenThreshold) continue;
var maxSqLen = sqLen / sqConcavity;
// find the best connection point for the current edge to flex inward to
p = findCandidate(tree, node.prev.p, a, b, node.next.next.p, maxSqLen, segTree);
// if we found a connection and it satisfies our concavity measure
if (p && Math.min(getSqDist(p, a), getSqDist(p, b)) <= maxSqLen) {
// connect the edge endpoints through this point and add 2 new edges to the queue
queue.push(node);
queue.push(insertNode(p, node));
// update point and segment indexes
tree.remove(p);
segTree.remove(node);
segTree.insert(updateBBox(node));
segTree.insert(updateBBox(node.next));
}
}
// convert the resulting hull linked list to an array of points
node = last;
var concave = [];
do {
concave.push(node.p);
node = node.next;
} while (node !== last);
concave.push(node.p);
return concave;
}
function findCandidate(tree, a, b, c, d, maxDist, segTree) {
var queue = new Queue(null, compareDist);
var node = tree.data;
// search through the point R-tree with a depth-first search using a priority queue
// in the order of distance to the edge (b, c)
while (node) {
for (var i = 0; i < node.children.length; i++) {
var child = node.children[i];
var dist = node.leaf ? sqSegDist(child, b, c) : sqSegBoxDist(b, c, child.bbox);
if (dist > maxDist) continue; // skip the node if it's farther than we ever need
queue.push({
node: child,
dist: dist
});
}
while (queue.length && !queue.peek().node.children) {
var item = queue.pop();
var p = item.node;
// skip all points that are as close to adjacent edges (a,b) and (c,d),
// and points that would introduce self-intersections when connected
var d0 = sqSegDist(p, a, b);
var d1 = sqSegDist(p, c, d);
if (item.dist < d0 && item.dist < d1 &&
noIntersections(b, p, segTree) &&
noIntersections(c, p, segTree)) return p;
}
node = queue.pop();
if (node) node = node.node;
}
return null;
}
function compareDist(a, b) {
return a.dist - b.dist;
}
// square distance from a segment bounding box to the given one
function sqSegBoxDist(a, b, bbox) {
if (inside(a, bbox) || inside(b, bbox)) return 0;
var d1 = sqSegSegDist(a[0], a[1], b[0], b[1], bbox[0], bbox[1], bbox[2], bbox[1]);
if (d1 === 0) return 0;
var d2 = sqSegSegDist(a[0], a[1], b[0], b[1], bbox[0], bbox[1], bbox[0], bbox[3]);
if (d2 === 0) return 0;
var d3 = sqSegSegDist(a[0], a[1], b[0], b[1], bbox[2], bbox[1], bbox[2], bbox[3]);
if (d3 === 0) return 0;
var d4 = sqSegSegDist(a[0], a[1], b[0], b[1], bbox[0], bbox[3], bbox[2], bbox[3]);
if (d4 === 0) return 0;
return Math.min(d1, d2, d3, d4);
}
function inside(a, bbox) {
return a[0] >= bbox[0] &&
a[0] <= bbox[2] &&
a[1] >= bbox[1] &&
a[1] <= bbox[3];
}
// check if the edge (a,b) doesn't intersect any other edges
function noIntersections(a, b, segTree) {
var minX = Math.min(a[0], b[0]);
var minY = Math.min(a[1], b[1]);
var maxX = Math.max(a[0], b[0]);
var maxY = Math.max(a[1], b[1]);
var edges = segTree.search([minX, minY, maxX, maxY]);
for (var i = 0; i < edges.length; i++) {
if (intersects(edges[i].p, edges[i].next.p, a, b)) return false;
}
return true;
}
// check if the edges (p1,q1) and (p2,q2) intersect
function intersects(p1, q1, p2, q2) {
return p1 !== q2 && q1 !== p2 &&
orient(p1, q1, p2) > 0 !== orient(p1, q1, q2) > 0 &&
orient(p2, q2, p1) > 0 !== orient(p2, q2, q1) > 0;
}
// update the bounding box of a node's edge
function updateBBox(node) {
var p1 = node.p;
var p2 = node.next.p;
node.minX = Math.min(p1[0], p2[0]);
node.minY = Math.min(p1[1], p2[1]);
node.maxX = Math.max(p1[0], p2[0]);
node.maxY = Math.max(p1[1], p2[1]);
return node;
}
// speed up convex hull by filtering out points inside quadrilateral formed by 4 extreme points
function fastConvexHull(points) {
var left = points[0];
var top = points[0];
var right = points[0];
var bottom = points[0];
// find the leftmost, rightmost, topmost and bottommost points
for (var i = 0; i < points.length; i++) {
var p = points[i];
if (p[0] < left[0]) left = p;
if (p[0] > right[0]) right = p;
if (p[1] < top[1]) top = p;
if (p[1] > bottom[1]) bottom = p;
}
// filter out points that are inside the resulting quadrilateral
var cull = [left, top, right, bottom];
var filtered = cull.slice();
for (i = 0; i < points.length; i++) {
if (!pointInPolygon(points[i], cull)) filtered.push(points[i]);
}
// get convex hull around the filtered points
var indices = convexHull(filtered);
// return the hull as array of points (rather than indices)
var hull = [];
for (i = 0; i < indices.length; i++) hull.push(filtered[indices[i]]);
return hull;
}
// create a new node in a doubly linked list
function insertNode(p, prev) {
var node = {
p: p,
prev: null,
next: null,
minX: 0,
minY: 0,
maxX: 0,
maxY: 0
};
if (!prev) {
node.prev = node;
node.next = node;
} else {
node.next = prev.next;
node.prev = prev;
prev.next.prev = node;
prev.next = node;
}
return node;
}
// square distance between 2 points
function getSqDist(p1, p2) {
var dx = p1[0] - p2[0],
dy = p1[1] - p2[1];
return dx * dx + dy * dy;
}
// square distance from a point to a segment
function sqSegDist(p, p1, p2) {
var x = p1[0],
y = p1[1],
dx = p2[0] - x,
dy = p2[1] - y;
if (dx !== 0 || dy !== 0) {
var t = ((p[0] - x) * dx + (p[1] - y) * dy) / (dx * dx + dy * dy);
if (t > 1) {
x = p2[0];
y = p2[1];
} else if (t > 0) {
x += dx * t;
y += dy * t;
}
}
dx = p[0] - x;
dy = p[1] - y;
return dx * dx + dy * dy;
}
// segment to segment distance, ported from http://geomalgorithms.com/a07-_distance.html by Dan Sunday
function sqSegSegDist(x0, y0, x1, y1, x2, y2, x3, y3) {
var ux = x1 - x0;
var uy = y1 - y0;
var vx = x3 - x2;
var vy = y3 - y2;
var wx = x0 - x2;
var wy = y0 - y2;
var a = ux * ux + uy * uy;
var b = ux * vx + uy * vy;
var c = vx * vx + vy * vy;
var d = ux * wx + uy * wy;
var e = vx * wx + vy * wy;
var D = a * c - b * b;
var sc, sN, tc, tN;
var sD = D;
var tD = D;
if (D === 0) {
sN = 0;
sD = 1;
tN = e;
tD = c;
} else {
sN = b * e - c * d;
tN = a * e - b * d;
if (sN < 0) {
sN = 0;
tN = e;
tD = c;
} else if (sN > sD) {
sN = sD;
tN = e + b;
tD = c;
}
}
if (tN < 0.0) {
tN = 0.0;
if (-d < 0.0) sN = 0.0;
else if (-d > a) sN = sD;
else {
sN = -d;
sD = a;
}
} else if (tN > tD) {
tN = tD;
if ((-d + b) < 0.0) sN = 0;
else if (-d + b > a) sN = sD;
else {
sN = -d + b;
sD = a;
}
}
sc = sN === 0 ? 0 : sN / sD;
tc = tN === 0 ? 0 : tN / tD;
var cx = (1 - sc) * x0 + sc * x1;
var cy = (1 - sc) * y0 + sc * y1;
var cx2 = (1 - tc) * x2 + tc * x3;
var cy2 = (1 - tc) * y2 + tc * y3;
var dx = cx2 - cx;
var dy = cy2 - cy;
return dx * dx + dy * dy;
}
},{"monotone-convex-hull-2d":2,"point-in-polygon":3,"rbush":4,"robust-orientation":5,"tinyqueue":9}],2:[function(require,module,exports){
'use strict'
module.exports = monotoneConvexHull2D
var orient = require('robust-orientation')[3]
function monotoneConvexHull2D(points) {
var n = points.length
if(n < 3) {
var result = new Array(n)
for(var i=0; i<n; ++i) {
result[i] = i
}
if(n === 2 &&
points[0][0] === points[1][0] &&
points[0][1] === points[1][1]) {
return [0]
}
return result
}
//Sort point indices along x-axis
var sorted = new Array(n)
for(var i=0; i<n; ++i) {
sorted[i] = i
}
sorted.sort(function(a,b) {
var d = points[a][0]-points[b][0]
if(d) {
return d
}
return points[a][1] - points[b][1]
})
//Construct upper and lower hulls
var lower = [sorted[0], sorted[1]]
var upper = [sorted[0], sorted[1]]
for(var i=2; i<n; ++i) {
var idx = sorted[i]
var p = points[idx]
//Insert into lower list
var m = lower.length
while(m > 1 && orient(
points[lower[m-2]],
points[lower[m-1]],
p) <= 0) {
m -= 1
lower.pop()
}
lower.push(idx)
//Insert into upper list
m = upper.length
while(m > 1 && orient(
points[upper[m-2]],
points[upper[m-1]],
p) >= 0) {
m -= 1
upper.pop()
}
upper.push(idx)
}
//Merge lists together
var result = new Array(upper.length + lower.length - 2)
var ptr = 0
for(var i=0, nl=lower.length; i<nl; ++i) {
result[ptr++] = lower[i]
}
for(var j=upper.length-2; j>0; --j) {
result[ptr++] = upper[j]
}
//Return result
return result
}
},{"robust-orientation":5}],3:[function(require,module,exports){
module.exports = function (point, vs) {
// ray-casting algorithm based on
// http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
var x = point[0], y = point[1];
var inside = false;
for (var i = 0, j = vs.length - 1; i < vs.length; j = i++) {
var xi = vs[i][0], yi = vs[i][1];
var xj = vs[j][0], yj = vs[j][1];
var intersect = ((yi > y) != (yj > y))
&& (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
if (intersect) inside = !inside;
}
return inside;
};
},{}],4:[function(require,module,exports){
/*
(c) 2015, Vladimir Agafonkin
RBush, a JavaScript library for high-performance 2D spatial indexing of points and rectangles.
https://github.com/mourner/rbush
*/
(function () {
'use strict';
function rbush(maxEntries, format) {
// jshint newcap: false, validthis: true
if (!(this instanceof rbush)) return new rbush(maxEntries, format);
// max entries in a node is 9 by default; min node fill is 40% for best performance
this._maxEntries = Math.max(4, maxEntries || 9);
this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
if (format) {
this._initFormat(format);
}
this.clear();
}
rbush.prototype = {
all: function () {
return this._all(this.data, []);
},
search: function (bbox) {
var node = this.data,
result = [],
toBBox = this.toBBox;
if (!intersects(bbox, node.bbox)) return result;
var nodesToSearch = [],
i, len, child, childBBox;
while (node) {
for (i = 0, len = node.children.length; i < len; i++) {
child = node.children[i];
childBBox = node.leaf ? toBBox(child) : child.bbox;
if (intersects(bbox, childBBox)) {
if (node.leaf) result.push(child);
else if (contains(bbox, childBBox)) this._all(child, result);
else nodesToSearch.push(child);
}
}
node = nodesToSearch.pop();
}
return result;
},
collides: function (bbox) {
var node = this.data,
toBBox = this.toBBox;
if (!intersects(bbox, node.bbox)) return false;
var nodesToSearch = [],
i, len, child, childBBox;
while (node) {
for (i = 0, len = node.children.length; i < len; i++) {
child = node.children[i];
childBBox = node.leaf ? toBBox(child) : child.bbox;
if (intersects(bbox, childBBox)) {
if (node.leaf || contains(bbox, childBBox)) return true;
nodesToSearch.push(child);
}
}
node = nodesToSearch.pop();
}
return false;
},
load: function (data) {
if (!(data && data.length)) return this;
if (data.length < this._minEntries) {
for (var i = 0, len = data.length; i < len; i++) {
this.insert(data[i]);
}
return this;
}
// recursively build the tree with the given data from stratch using OMT algorithm
var node = this._build(data.slice(), 0, data.length - 1, 0);
if (!this.data.children.length) {
// save as is if tree is empty
this.data = node;
} else if (this.data.height === node.height) {
// split root if trees have the same height
this._splitRoot(this.data, node);
} else {
if (this.data.height < node.height) {
// swap trees if inserted one is bigger
var tmpNode = this.data;
this.data = node;
node = tmpNode;
}
// insert the small tree into the large tree at appropriate level
this._insert(node, this.data.height - node.height - 1, true);
}
return this;
},
insert: function (item) {
if (item) this._insert(item, this.data.height - 1);
return this;
},
clear: function () {
this.data = {
children: [],
height: 1,
bbox: empty(),
leaf: true
};
return this;
},
remove: function (item) {
if (!item) return this;
var node = this.data,
bbox = this.toBBox(item),
path = [],
indexes = [],
i, parent, index, goingUp;
// depth-first iterative tree traversal
while (node || path.length) {
if (!node) { // go up
node = path.pop();
parent = path[path.length - 1];
i = indexes.pop();
goingUp = true;
}
if (node.leaf) { // check current node
index = node.children.indexOf(item);
if (index !== -1) {
// item found, remove the item and condense tree upwards
node.children.splice(index, 1);
path.push(node);
this._condense(path);
return this;
}
}
if (!goingUp && !node.leaf && contains(node.bbox, bbox)) { // go down
path.push(node);
indexes.push(i);
i = 0;
parent = node;
node = node.children[0];
} else if (parent) { // go right
i++;
node = parent.children[i];
goingUp = false;
} else node = null; // nothing found
}
return this;
},
toBBox: function (item) { return item; },
compareMinX: function (a, b) { return a[0] - b[0]; },
compareMinY: function (a, b) { return a[1] - b[1]; },
toJSON: function () { return this.data; },
fromJSON: function (data) {
this.data = data;
return this;
},
_all: function (node, result) {
var nodesToSearch = [];
while (node) {
if (node.leaf) result.push.apply(result, node.children);
else nodesToSearch.push.apply(nodesToSearch, node.children);
node = nodesToSearch.pop();
}
return result;
},
_build: function (items, left, right, height) {
var N = right - left + 1,
M = this._maxEntries,
node;
if (N <= M) {
// reached leaf level; return leaf
node = {
children: items.slice(left, right + 1),
height: 1,
bbox: null,
leaf: true
};
calcBBox(node, this.toBBox);
return node;
}
if (!height) {
// target height of the bulk-loaded tree
height = Math.ceil(Math.log(N) / Math.log(M));
// target number of root entries to maximize storage utilization
M = Math.ceil(N / Math.pow(M, height - 1));
}
node = {
children: [],
height: height,
bbox: null,
leaf: false
};
// split the items into M mostly square tiles
var N2 = Math.ceil(N / M),
N1 = N2 * Math.ceil(Math.sqrt(M)),
i, j, right2, right3;
multiSelect(items, left, right, N1, this.compareMinX);
for (i = left; i <= right; i += N1) {
right2 = Math.min(i + N1 - 1, right);
multiSelect(items, i, right2, N2, this.compareMinY);
for (j = i; j <= right2; j += N2) {
right3 = Math.min(j + N2 - 1, right2);
// pack each entry recursively
node.children.push(this._build(items, j, right3, height - 1));
}
}
calcBBox(node, this.toBBox);
return node;
},
_chooseSubtree: function (bbox, node, level, path) {
var i, len, child, targetNode, area, enlargement, minArea, minEnlargement;
while (true) {
path.push(node);
if (node.leaf || path.length - 1 === level) break;
minArea = minEnlargement = Infinity;
for (i = 0, len = node.children.length; i < len; i++) {
child = node.children[i];
area = bboxArea(child.bbox);
enlargement = enlargedArea(bbox, child.bbox) - area;
// choose entry with the least area enlargement
if (enlargement < minEnlargement) {
minEnlargement = enlargement;
minArea = area < minArea ? area : minArea;
targetNode = child;
} else if (enlargement === minEnlargement) {
// otherwise choose one with the smallest area
if (area < minArea) {
minArea = area;
targetNode = child;
}
}
}
node = targetNode;
}
return node;
},
_insert: function (item, level, isNode) {
var toBBox = this.toBBox,
bbox = isNode ? item.bbox : toBBox(item),
insertPath = [];
// find the best node for accommodating the item, saving all nodes along the path too
var node = this._chooseSubtree(bbox, this.data, level, insertPath);
// put the item into the node
node.children.push(item);
extend(node.bbox, bbox);
// split on node overflow; propagate upwards if necessary
while (level >= 0) {
if (insertPath[level].children.length > this._maxEntries) {
this._split(insertPath, level);
level--;
} else break;
}
// adjust bboxes along the insertion path
this._adjustParentBBoxes(bbox, insertPath, level);
},
// split overflowed node into two
_split: function (insertPath, level) {
var node = insertPath[level],
M = node.children.length,
m = this._minEntries;
this._chooseSplitAxis(node, m, M);
var splitIndex = this._chooseSplitIndex(node, m, M);
var newNode = {
children: node.children.splice(splitIndex, node.children.length - splitIndex),
height: node.height,
bbox: null,
leaf: false
};
if (node.leaf) newNode.leaf = true;
calcBBox(node, this.toBBox);
calcBBox(newNode, this.toBBox);
if (level) insertPath[level - 1].children.push(newNode);
else this._splitRoot(node, newNode);
},
_splitRoot: function (node, newNode) {
// split root node
this.data = {
children: [node, newNode],
height: node.height + 1,
bbox: null,
leaf: false
};
calcBBox(this.data, this.toBBox);
},
_chooseSplitIndex: function (node, m, M) {
var i, bbox1, bbox2, overlap, area, minOverlap, minArea, index;
minOverlap = minArea = Infinity;
for (i = m; i <= M - m; i++) {
bbox1 = distBBox(node, 0, i, this.toBBox);
bbox2 = distBBox(node, i, M, this.toBBox);
overlap = intersectionArea(bbox1, bbox2);
area = bboxArea(bbox1) + bboxArea(bbox2);
// choose distribution with minimum overlap
if (overlap < minOverlap) {
minOverlap = overlap;
index = i;
minArea = area < minArea ? area : minArea;
} else if (overlap === minOverlap) {
// otherwise choose distribution with minimum area
if (area < minArea) {
minArea = area;
index = i;
}
}
}
return index;
},
// sorts node children by the best axis for split
_chooseSplitAxis: function (node, m, M) {
var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX,
compareMinY = node.leaf ? this.compareMinY : compareNodeMinY,
xMargin = this._allDistMargin(node, m, M, compareMinX),
yMargin = this._allDistMargin(node, m, M, compareMinY);
// if total distributions margin value is minimal for x, sort by minX,
// otherwise it's already sorted by minY
if (xMargin < yMargin) node.children.sort(compareMinX);
},
// total margin of all possible split distributions where each node is at least m full
_allDistMargin: function (node, m, M, compare) {
node.children.sort(compare);
var toBBox = this.toBBox,
leftBBox = distBBox(node, 0, m, toBBox),
rightBBox = distBBox(node, M - m, M, toBBox),
margin = bboxMargin(leftBBox) + bboxMargin(rightBBox),
i, child;
for (i = m; i < M - m; i++) {
child = node.children[i];
extend(leftBBox, node.leaf ? toBBox(child) : child.bbox);
margin += bboxMargin(leftBBox);
}
for (i = M - m - 1; i >= m; i--) {
child = node.children[i];
extend(rightBBox, node.leaf ? toBBox(child) : child.bbox);
margin += bboxMargin(rightBBox);
}
return margin;
},
_adjustParentBBoxes: function (bbox, path, level) {
// adjust bboxes along the given tree path
for (var i = level; i >= 0; i--) {
extend(path[i].bbox, bbox);
}
},
_condense: function (path) {
// go through the path, removing empty nodes and updating bboxes
for (var i = path.length - 1, siblings; i >= 0; i--) {
if (path[i].children.length === 0) {
if (i > 0) {
siblings = path[i - 1].children;
siblings.splice(siblings.indexOf(path[i]), 1);
} else this.clear();
} else calcBBox(path[i], this.toBBox);
}
},
_initFormat: function (format) {
// data format (minX, minY, maxX, maxY accessors)
// uses eval-type function compilation instead of just accepting a toBBox function
// because the algorithms are very sensitive to sorting functions performance,
// so they should be dead simple and without inner calls
// jshint evil: true
var compareArr = ['return a', ' - b', ';'];
this.compareMinX = new Function('a', 'b', compareArr.join(format[0]));
this.compareMinY = new Function('a', 'b', compareArr.join(format[1]));
this.toBBox = new Function('a', 'return [a' + format.join(', a') + '];');
}
};
// calculate node's bbox from bboxes of its children
function calcBBox(node, toBBox) {
node.bbox = distBBox(node, 0, node.children.length, toBBox);
}
// min bounding rectangle of node children from k to p-1
function distBBox(node, k, p, toBBox) {
var bbox = empty();
for (var i = k, child; i < p; i++) {
child = node.children[i];
extend(bbox, node.leaf ? toBBox(child) : child.bbox);
}
return bbox;
}
function empty() { return [Infinity, Infinity, -Infinity, -Infinity]; }
function extend(a, b) {
a[0] = Math.min(a[0], b[0]);
a[1] = Math.min(a[1], b[1]);
a[2] = Math.max(a[2], b[2]);
a[3] = Math.max(a[3], b[3]);
return a;
}
function compareNodeMinX(a, b) { return a.bbox[0] - b.bbox[0]; }
function compareNodeMinY(a, b) { return a.bbox[1] - b.bbox[1]; }
function bboxArea(a) { return (a[2] - a[0]) * (a[3] - a[1]); }
function bboxMargin(a) { return (a[2] - a[0]) + (a[3] - a[1]); }
function enlargedArea(a, b) {
return (Math.max(b[2], a[2]) - Math.min(b[0], a[0])) *
(Math.max(b[3], a[3]) - Math.min(b[1], a[1]));
}
function intersectionArea(a, b) {
var minX = Math.max(a[0], b[0]),
minY = Math.max(a[1], b[1]),
maxX = Math.min(a[2], b[2]),
maxY = Math.min(a[3], b[3]);
return Math.max(0, maxX - minX) *
Math.max(0, maxY - minY);
}
function contains(a, b) {
return a[0] <= b[0] &&
a[1] <= b[1] &&
b[2] <= a[2] &&
b[3] <= a[3];
}
function intersects(a, b) {
return b[0] <= a[2] &&
b[1] <= a[3] &&
b[2] >= a[0] &&
b[3] >= a[1];
}
// sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
// combines selection algorithm with binary divide & conquer approach
function multiSelect(arr, left, right, n, compare) {
var stack = [left, right],
mid;
while (stack.length) {
right = stack.pop();
left = stack.pop();
if (right - left <= n) continue;
mid = left + Math.ceil((right - left) / n / 2) * n;
select(arr, left, right, mid, compare);
stack.push(left, mid, mid, right);
}
}
// Floyd-Rivest selection algorithm:
// sort an array between left and right (inclusive) so that the smallest k elements come first (unordered)
function select(arr, left, right, k, compare) {
var n, i, z, s, sd, newLeft, newRight, t, j;
while (right > left) {
if (right - left > 600) {
n = right - left + 1;
i = k - left + 1;
z = Math.log(n);
s = 0.5 * Math.exp(2 * z / 3);
sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (i - n / 2 < 0 ? -1 : 1);
newLeft = Math.max(left, Math.floor(k - i * s / n + sd));
newRight = Math.min(right, Math.floor(k + (n - i) * s / n + sd));
select(arr, newLeft, newRight, k, compare);
}
t = arr[k];
i = left;
j = right;
swap(arr, left, k);
if (compare(arr[right], t) > 0) swap(arr, left, right);
while (i < j) {
swap(arr, i, j);
i++;
j--;
while (compare(arr[i], t) < 0) i++;
while (compare(arr[j], t) > 0) j--;
}
if (compare(arr[left], t) === 0) swap(arr, left, j);
else {
j++;
swap(arr, j, right);
}
if (j <= k) left = j + 1;
if (k <= j) right = j - 1;
}
}
function swap(arr, i, j) {
var tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
// export as AMD/CommonJS module or global variable
if (typeof define === 'function' && define.amd) define('rbush', function () { return rbush; });
else if (typeof module !== 'undefined') module.exports = rbush;
else if (typeof self !== 'undefined') self.rbush = rbush;
else window.rbush = rbush;
})();
},{}],5:[function(require,module,exports){
"use strict"
var twoProduct = require("two-product")
var robustSum = require("robust-sum")
var robustScale = require("robust-scale")
var robustSubtract = require("robust-subtract")
var NUM_EXPAND = 5
var EPSILON = 1.1102230246251565e-16
var ERRBOUND3 = (3.0 + 16.0 * EPSILON) * EPSILON
var ERRBOUND4 = (7.0 + 56.0 * EPSILON) * EPSILON
function cofactor(m, c) {
var result = new Array(m.length-1)
for(var i=1; i<m.length; ++i) {
var r = result[i-1] = new Array(m.length-1)
for(var j=0,k=0; j<m.length; ++j) {
if(j === c) {
continue
}
r[k++] = m[i][j]
}
}
return result
}
function matrix(n) {
var result = new Array(n)
for(var i=0; i<n; ++i) {
result[i] = new Array(n)
for(var j=0; j<n; ++j) {
result[i][j] = ["m", j, "[", (n-i-1), "]"].join("")
}
}
return result
}
function sign(n) {
if(n & 1) {
return "-"
}
return ""
}
function generateSum(expr) {
if(expr.length === 1) {
return expr[0]
} else if(expr.length === 2) {
return ["sum(", expr[0], ",", expr[1], ")"].join("")
} else {
var m = expr.length>>1
return ["sum(", generateSum(expr.slice(0, m)), ",", generateSum(expr.slice(m)), ")"].join("")
}
}
function determinant(m) {
if(m.length === 2) {
return [["sum(prod(", m[0][0], ",", m[1][1], "),prod(-", m[0][1], ",", m[1][0], "))"].join("")]
} else {
var expr = []
for(var i=0; i<m.length; ++i) {
expr.push(["scale(", generateSum(determinant(cofactor(m, i))), ",", sign(i), m[0][i], ")"].join(""))
}
return expr
}
}
function orientation(n) {
var pos = []
var neg = []
var m = matrix(n)
var args = []
for(var i=0; i<n; ++i) {
if((i&1)===0) {
pos.push.apply(pos, determinant(cofactor(m, i)))
} else {
neg.push.apply(neg, determinant(cofactor(m, i)))
}
args.push("m" + i)
}
var posExpr = generateSum(pos)
var negExpr = generateSum(neg)
var funcName = "orientation" + n + "Exact"
var code = ["function ", funcName, "(", args.join(), "){var p=", posExpr, ",n=", negExpr, ",d=sub(p,n);\
return d[d.length-1];};return ", funcName].join("")
var proc = new Function("sum", "prod", "scale", "sub", code)
return proc(robustSum, twoProduct, robustScale, robustSubtract)
}
var orientation3Exact = orientation(3)
var orientation4Exact = orientation(4)
var CACHED = [
function orientation0() { return 0 },
function orientation1() { return 0 },
function orientation2(a, b) {
return b[0] - a[0]
},
function orientation3(a, b, c) {
var l = (a[1] - c[1]) * (b[0] - c[0])
var r = (a[0] - c[0]) * (b[1] - c[1])
var det = l - r
var s
if(l > 0) {
if(r <= 0) {
return det
} else {
s = l + r
}
} else if(l < 0) {
if(r >= 0) {
return det
} else {
s = -(l + r)
}
} else {
return det
}
var tol = ERRBOUND3 * s
if(det >= tol || det <= -tol) {
return det
}
return orientation3Exact(a, b, c)
},
function orientation4(a,b,c,d) {
var adx = a[0] - d[0]
var bdx = b[0] - d[0]
var cdx = c[0] - d[0]
var ady = a[1] - d[1]
var bdy = b[1] - d[1]
var cdy = c[1] - d[1]
var adz = a[2] - d[2]
var bdz = b[2] - d[2]
var cdz = c[2] - d[2]
var bdxcdy = bdx * cdy
var cdxbdy = cdx * bdy
var cdxady = cdx * ady
var adxcdy = adx * cdy
var adxbdy = adx * bdy
var bdxady = bdx * ady
var det = adz * (bdxcdy - cdxbdy)
+ bdz * (cdxady - adxcdy)
+ cdz * (adxbdy - bdxady)
var permanent = (Math.abs(bdxcdy) + Math.abs(cdxbdy)) * Math.abs(adz)
+ (Math.abs(cdxady) + Math.abs(adxcdy)) * Math.abs(bdz)
+ (Math.abs(adxbdy) + Math.abs(bdxady)) * Math.abs(cdz)
var tol = ERRBOUND4 * permanent
if ((det > tol) || (-det > tol)) {
return det
}
return orientation4Exact(a,b,c,d)
}
]
function slowOrient(args) {
var proc = CACHED[args.length]
if(!proc) {
proc = CACHED[args.length] = orientation(args.length)
}
return proc.apply(undefined, args)
}
function generateOrientationProc() {
while(CACHED.length <= NUM_EXPAND) {
CACHED.push(orientation(CACHED.length))
}
var args = []
var procArgs = ["slow"]
for(var i=0; i<=NUM_EXPAND; ++i) {
args.push("a" + i)
procArgs.push("o" + i)
}
var code = [
"function getOrientation(", args.join(), "){switch(arguments.length){case 0:case 1:return 0;"
]
for(var i=2; i<=NUM_EXPAND; ++i) {
code.push("case ", i, ":return o", i, "(", args.slice(0, i).join(), ");")
}
code.push("}var s=new Array(arguments.length);for(var i=0;i<arguments.length;++i){s[i]=arguments[i]};return slow(s);}return getOrientation")
procArgs.push(code.join(""))
var proc = Function.apply(undefined, procArgs)
module.exports = proc.apply(undefined, [slowOrient].concat(CACHED))
for(var i=0; i<=NUM_EXPAND; ++i) {
module.exports[i] = CACHED[i]
}
}
generateOrientationProc()
},{"robust-scale":6,"robust-subtract":7,"robust-sum":8,"two-product":10}],6:[function(require,module,exports){
"use strict"
var twoProduct = require("two-product")
var twoSum = require("two-sum")
module.exports = scaleLinearExpansion
function scaleLinearExpansion(e, scale) {
var n = e.length
if(n === 1) {
var ts = twoProduct(e[0], scale)
if(ts[0]) {
return ts
}
return [ ts[1] ]
}
var g = new Array(2 * n)
var q = [0.1, 0.1]
var t = [0.1, 0.1]
var count = 0
twoProduct(e[0], scale, q)
if(q[0]) {
g[count++] = q[0]
}
for(var i=1; i<n; ++i) {
twoProduct(e[i], scale, t)
var pq = q[1]
twoSum(pq, t[0], q)
if(q[0]) {
g[count++] = q[0]
}
var a = t[1]
var b = q[1]
var x = a + b
var bv = x - a
var y = b - bv
q[1] = x
if(y) {
g[count++] = y
}
}
if(q[1]) {
g[count++] = q[1]
}
if(count === 0) {
g[count++] = 0.0
}
g.length = count
return g
}
},{"two-product":10,"two-sum":11}],7:[function(require,module,exports){
"use strict"
module.exports = robustSubtract
//Easy case: Add two scalars
function scalarScalar(a, b) {
var x = a + b
var bv = x - a
var av = x - bv
var br = b - bv
var ar = a - av
var y = ar + br
if(y) {
return [y, x]
}
return [x]
}
function robustSubtract(e, f) {
var ne = e.length|0
var nf = f.length|0
if(ne === 1 && nf === 1) {
return scalarScalar(e[0], -f[0])
}
var n = ne + nf
var g = new Array(n)
var count = 0
var eptr = 0
var fptr = 0
var abs = Math.abs
var ei = e[eptr]
var ea = abs(ei)
var fi = -f[fptr]
var fa = abs(fi)
var a, b
if(ea < fa) {
b = ei
eptr += 1
if(eptr < ne) {
ei = e[eptr]
ea = abs(ei)
}
} else {
b = fi
fptr += 1
if(fptr < nf) {
fi = -f[fptr]
fa = abs(fi)
}
}
if((eptr < ne && ea < fa) || (fptr >= nf)) {
a = ei
eptr += 1
if(eptr < ne) {
ei = e[eptr]
ea = abs(ei)
}
} else {
a = fi
fptr += 1
if(fptr < nf) {
fi = -f[fptr]
fa = abs(fi)
}
}
var x = a + b
var bv = x - a
var y = b - bv
var q0 = y
var q1 = x
var _x, _bv, _av, _br, _ar
while(eptr < ne && fptr < nf) {
if(ea < fa) {
a = ei
eptr += 1
if(eptr < ne) {
ei = e[eptr]
ea = abs(ei)
}
} else {
a = fi
fptr += 1
if(fptr < nf) {
fi = -f[fptr]
fa = abs(fi)
}
}
b = q0
x = a + b
bv = x - a
y = b - bv
if(y) {
g[count++] = y
}
_x = q1 + x
_bv = _x - q1
_av = _x - _bv
_br = x - _bv
_ar = q1 - _av
q0 = _ar + _br
q1 = _x
}
while(eptr < ne) {
a = ei
b = q0
x = a + b
bv = x - a
y = b - bv
if(y) {
g[count++] = y
}
_x = q1 + x
_bv = _x - q1
_av = _x - _bv
_br = x - _bv
_ar = q1 - _av
q0 = _ar + _br
q1 = _x
eptr += 1
if(eptr < ne) {
ei = e[eptr]
}
}
while(fptr < nf) {
a = fi
b = q0
x = a + b
bv = x - a
y = b - bv
if(y) {
g[count++] = y
}
_x = q1 + x
_bv = _x - q1
_av = _x - _bv
_br = x - _bv
_ar = q1 - _av
q0 = _ar + _br
q1 = _x
fptr += 1
if(fptr < nf) {
fi = -f[fptr]
}
}
if(q0) {
g[count++] = q0
}
if(q1) {
g[count++] = q1
}
if(!count) {
g[count++] = 0.0
}
g.length = count
return g
}
},{}],8:[function(require,module,exports){
"use strict"
module.exports = linearExpansionSum
//Easy case: Add two scalars
function scalarScalar(a, b) {
var x = a + b
var bv = x - a
var av = x - bv
var br = b - bv
var ar = a - av
var y = ar + br
if(y) {
return [y, x]
}
return [x]
}
function linearExpansionSum(e, f) {
var ne = e.length|0
var nf = f.length|0
if(ne === 1 && nf === 1) {
return scalarScalar(e[0], f[0])
}
var n = ne + nf
var g = new Array(n)
var count = 0
var eptr = 0
var fptr = 0
var abs = Math.abs
var ei = e[eptr]
var ea = abs(ei)
var fi = f[fptr]
var fa = abs(fi)
var a, b
if(ea < fa) {
b = ei
eptr += 1
if(eptr < ne) {
ei = e[eptr]
ea = abs(ei)
}
} else {
b = fi
fptr += 1
if(fptr < nf) {
fi = f[fptr]
fa = abs(fi)
}
}
if((eptr < ne && ea < fa) || (fptr >= nf)) {
a = ei
eptr += 1
if(eptr < ne) {
ei = e[eptr]
ea = abs(ei)
}
} else {
a = fi
fptr += 1
if(fptr < nf) {
fi = f[fptr]
fa = abs(fi)
}
}
var x = a + b
var bv = x - a
var y = b - bv
var q0 = y
var q1 = x
var _x, _bv, _av, _br, _ar
while(eptr < ne && fptr < nf) {
if(ea < fa) {
a = ei
eptr += 1
if(eptr < ne) {
ei = e[eptr]
ea = abs(ei)
}
} else {
a = fi
fptr += 1
if(fptr < nf) {
fi = f[fptr]
fa = abs(fi)
}
}
b = q0
x = a + b
bv = x - a
y = b - bv
if(y) {
g[count++] = y
}
_x = q1 + x
_bv = _x - q1
_av = _x - _bv
_br = x - _bv
_ar = q1 - _av
q0 = _ar + _br
q1 = _x
}
while(eptr < ne) {
a = ei
b = q0
x = a + b
bv = x - a
y = b - bv
if(y) {
g[count++] = y
}
_x = q1 + x
_bv = _x - q1
_av = _x - _bv
_br = x - _bv
_ar = q1 - _av
q0 = _ar + _br
q1 = _x
eptr += 1
if(eptr < ne) {
ei = e[eptr]
}
}
while(fptr < nf) {
a = fi
b = q0
x = a + b
bv = x - a
y = b - bv
if(y) {
g[count++] = y
}
_x = q1 + x
_bv = _x - q1
_av = _x - _bv
_br = x - _bv
_ar = q1 - _av
q0 = _ar + _br
q1 = _x
fptr += 1
if(fptr < nf) {
fi = f[fptr]
}
}
if(q0) {
g[count++] = q0
}
if(q1) {
g[count++] = q1
}
if(!count) {
g[count++] = 0.0
}
g.length = count
return g
}
},{}],9:[function(require,module,exports){
'use strict';
module.exports = TinyQueue;
function TinyQueue(data, compare) {
if (!(this instanceof TinyQueue)) return new TinyQueue(data, compare);
this.data = data || [];
this.length = this.data.length;
this.compare = compare || defaultCompare;
if (data) for (var i = Math.floor(this.length / 2); i >= 0; i--) this._down(i);
}
function defaultCompare(a, b) {
return a < b ? -1 : a > b ? 1 : 0;
}
TinyQueue.prototype = {
push: function (item) {
this.data.push(item);
this.length++;
this._up(this.length - 1);
},
pop: function () {
var top = this.data[0];
this.data[0] = this.data[this.length - 1];
this.length--;
this.data.pop();
this._down(0);
return top;
},
peek: function () {
return this.data[0];
},
_up: function (pos) {
var data = this.data,
compare = this.compare;
while (pos > 0) {
var parent = Math.floor((pos - 1) / 2);
if (compare(data[pos], data[parent]) < 0) {
swap(data, parent, pos);
pos = parent;
} else break;
}
},
_down: function (pos) {
var data = this.data,
compare = this.compare,
len = this.length;
while (true) {
var left = 2 * pos + 1,
right = left + 1,
min = pos;
if (left < len && compare(data[left], data[min]) < 0) min = left;
if (right < len && compare(data[right], data[min]) < 0) min = right;
if (min === pos) return;
swap(data, min, pos);
pos = min;
}
}
};
function swap(data, i, j) {
var tmp = data[i];
data[i] = data[j];
data[j] = tmp;
}
},{}],10:[function(require,module,exports){
"use strict"
module.exports = twoProduct
var SPLITTER = +(Math.pow(2, 27) + 1.0)
function twoProduct(a, b, result) {
var x = a * b
var c = SPLITTER * a
var abig = c - a
var ahi = c - abig
var alo = a - ahi
var d = SPLITTER * b
var bbig = d - b
var bhi = d - bbig
var blo = b - bhi
var err1 = x - (ahi * bhi)
var err2 = err1 - (alo * bhi)
var err3 = err2 - (ahi * blo)
var y = alo * blo - err3
if(result) {
result[0] = y
result[1] = x
return result
}
return [ y, x ]
}
},{}],11:[function(require,module,exports){
"use strict"
module.exports = fastTwoSum
function fastTwoSum(a, b, result) {
var x = a + b
var bv = x - a
var av = x - bv
var br = b - bv
var ar = a - av
if(result) {
result[0] = ar + br
result[1] = x
return result
}
return [ar+br, x]
}
},{}]},{},[1])(1)
});