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