New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

hull.js

Package Overview
Dependencies
Maintainers
1
Versions
18
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

hull.js - npm Package Compare versions

Comparing version 0.2.7 to 0.2.8

debug/data/geoNorvay600k.js

2

bower.json
{
"name": "hull-js",
"version": "0.2.7",
"version": "0.2.8",
"description": "JavaScript library that builds concave hulls (shapes) by set of points",

@@ -5,0 +5,0 @@ "homepage": "https://github.com/AndriiHeonia/hull",

@@ -8,6 +8,9 @@ var horse13k = require('./data/horse13k.js'),

owl58k = require('./data/owl58k.js'),
owl102k = require('./data/owl102k.js');
owl102k = require('./data/owl102k.js'),
geoNorvay600k = require('./data/geoNorvay600k.js');
var hull = require('../src/hull');
// concavity = 20
console.time('hull(horse13k, 20)');

@@ -47,4 +50,51 @@ hull(horse13k, 20);

console.log('\n');
// master:
// concavity = 10
console.time('hull(horse13k, 10)');
hull(horse13k, 10);
console.timeEnd('hull(horse13k, 10)');
console.time('hull(horse26k, 10)');
hull(horse26k, 10);
console.timeEnd('hull(horse26k, 10)');
console.time('hull(horse52k, 10)');
hull(horse52k, 10);
console.timeEnd('hull(horse52k, 10)');
console.time('hull(horse131k, 10)');
hull(horse131k, 10);
console.timeEnd('hull(horse131k, 10)');
console.log('\n');
console.time('hull(owl15k, 10)');
hull(owl15k, 10);
console.timeEnd('hull(owl15k, 10)');
console.time('hull(owl30k, 10)');
hull(owl30k, 10);
console.timeEnd('hull(owl30k, 10)');
console.time('hull(owl58k, 10)');
hull(owl58k, 10);
console.timeEnd('hull(owl58k, 10)');
console.time('hull(owl102k, 10)');
hull(owl102k, 10);
console.timeEnd('hull(owl102k, 10)');
console.log('\n');
// geoNorvay600k, concavity = 0.2
console.time('hull(geoNorvay600k, 0.5)');
hull(geoNorvay600k, 0.5);
console.timeEnd('hull(geoNorvay600k, 0.5)');
// version 0.1.0:
// hull(horse13k, 20): 1613ms

@@ -62,18 +112,52 @@ // hull(horse26k, 20): 2318ms

// speedup:
// hull(horse13k, 20): 188ms
// hull(horse26k, 20): 189ms
// hull(horse52k, 20): 306ms
// hull(horse131k, 20): 653ms
// version 0.2.0:
// hull(owl15k, 20): 43ms
// hull(owl30k, 20): 112ms
// hull(owl58k, 20): 153ms
// hull(owl102k, 20): 219ms
// hull(horse13k, 20): 187ms
// hull(horse26k, 20): 191ms
// hull(horse52k, 20): 305ms
// hull(horse131k, 20): 657ms
//
// hull(owl15k, 20): 39ms
// hull(owl30k, 20): 68ms
// hull(owl58k, 20): 120ms
// hull(owl102k, 20): 201ms
//
// hull(horse13k, 10): 90634ms
// hull(horse26k, 10): 22178ms
// hull(horse52k, 10): 560ms
// hull(horse131k, 10): 1077ms
//
// hull(owl15k, 10): 61973ms
// hull(owl30k, 10): 7250ms
// hull(owl58k, 10): 181ms
// hull(owl102k, 10): 315ms
// ------------------------------
// Horse: 8.6x, 12.3x, 18.3x, 30.2x
// Owl: 12x, 7.4x, 13.3x, 19.7x
// Hull.js 0.2. Now it's ~20x faster (on 100K points) than previous triangulation based version.
// Hull.js 0.2. Now it's ~20x faster (on 100K points) than previous triangulation based version.
// ------------------------------
// version 0.2.8:
// hull(horse13k, 20): 234ms
// hull(horse26k, 20): 237ms
// hull(horse52k, 20): 329ms
// hull(horse131k, 20): 953ms
// hull(owl15k, 20): 48ms
// hull(owl30k, 20): 66ms
// hull(owl58k, 20): 120ms
// hull(owl102k, 20): 209ms
// hull(horse13k, 10): 1176ms
// hull(horse26k, 10): 690ms
// hull(horse52k, 10): 899ms
// hull(horse131k, 10): 1341ms
// hull(owl15k, 10): 448ms
// hull(owl30k, 10): 228ms
// hull(owl58k, 10): 211ms
// hull(owl102k, 10): 309ms
// hull(geoNorvay600k, 0.5): 116772ms
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.hull=e()}}(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){
function Grid(points) {
var _cells = [];
function _cross(o, a, b) {
return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]);
}
function _upperTangent(pointset) {
var lower = [];
for (var l = 0; l < pointset.length; l++) {
while (lower.length >= 2 && (_cross(lower[lower.length - 2], lower[lower.length - 1], pointset[l]) <= 0)) {
lower.pop();
}
lower.push(pointset[l]);
}
lower.pop();
return lower;
}
function _lowerTangent(pointset) {
var reversed = pointset.reverse(),
upper = [];
for (var u = 0; u < reversed.length; u++) {
while (upper.length >= 2 && (_cross(upper[upper.length - 2], upper[upper.length - 1], reversed[u]) <= 0)) {
upper.pop();
}
upper.push(reversed[u]);
}
upper.pop();
return upper;
}
// pointset has to be sorted by X
function convex(pointset) {
var convex;
upper = _upperTangent(pointset);
lower = _lowerTangent(pointset);
convex = lower.concat(upper);
convex.push(pointset[0]);
return convex;
}
module.exports = convex;
},{}],2:[function(require,module,exports){
module.exports = {
toXy: function(pointset, format) {
if (format === undefined) {
return pointset;
}
return pointset.map(function(pt) {
/*jslint evil: true */
var _getXY = new Function('pt', 'return [pt' + format[0] + ',' + 'pt' + format[1] + '];');
return _getXY(pt);
});
},
fromXy: function(pointset, format) {
if (format === undefined) {
return pointset;
}
return pointset.map(function(pt) {
/*jslint evil: true */
var _getObj = new Function('pt', 'var o = {}; o' + format[0] + '= pt[0]; o' + format[1] + '= pt[1]; return o;');
return _getObj(pt);
});
}
}
},{}],3:[function(require,module,exports){
function Grid(points, cellSize) {
this._cells = [];
this._cellSize = cellSize;
points.forEach(function(point) {

@@ -9,18 +77,34 @@ var cellXY = this.point2CellXY(point),

y = cellXY[1];
if (_cells[x] === undefined) {
_cells[x] = [];
if (this._cells[x] === undefined) {
this._cells[x] = [];
}
if (_cells[x][y] === undefined) {
_cells[x][y] = [];
if (this._cells[x][y] === undefined) {
this._cells[x][y] = [];
}
_cells[x][y].push(point);
this._cells[x][y].push(point);
}, this);
}
this.cellPoints = function(x, y) { // (Number, Number) -> Array
return (_cells[x] !== undefined && _cells[x][y] !== undefined) ? _cells[x][y] : [];
};
Grid.prototype = {
cellPoints: function(x, y) { // (Number, Number) -> Array
return (this._cells[x] !== undefined && this._cells[x][y] !== undefined) ? this._cells[x][y] : [];
},
this.removePoint = function(point) { // (Array) -> Array
rangePoints: function(bbox) { // (Array) -> Array
var tlCellXY = this.point2CellXY([bbox[0], bbox[1]]),
brCellXY = this.point2CellXY([bbox[2], bbox[3]]),
points = [];
for (var x = tlCellXY[0]; x <= brCellXY[0]; x++) {
for (var y = tlCellXY[1]; y <= brCellXY[1]; y++) {
points = points.concat(this.cellPoints(x, y));
}
}
return points;
},
removePoint: function(point) { // (Array) -> Array
var cellXY = this.point2CellXY(point),
cell = _cells[cellXY[0]][cellXY[1]],
cell = this._cells[cellXY[0]][cellXY[1]],
pointIdxInCell;

@@ -38,32 +122,16 @@

return cell;
};
}
},
Grid.prototype = {
point2CellXY: function(point) { // (Array) -> Array
var x = parseInt(point[0] / Grid.CELL_SIZE),
y = parseInt(point[1] / Grid.CELL_SIZE);
var x = parseInt(point[0] / this._cellSize),
y = parseInt(point[1] / this._cellSize);
return [x, y];
},
rangePoints: function(bbox) { // (Array) -> Array
var tlCellXY = this.point2CellXY([bbox[0], bbox[1]]),
brCellXY = this.point2CellXY([bbox[2], bbox[3]]),
points = [];
for (var x = tlCellXY[0]; x <= brCellXY[0]; x++) {
for (var y = tlCellXY[1]; y <= brCellXY[1]; y++) {
points = points.concat(this.cellPoints(x, y));
}
}
return points;
},
addBorder2Bbox: function(bbox, border) { // (Array, Number) -> Array
extendBbox: function(bbox, scaleFactor) { // (Array, Number) -> Array
return [
bbox[0] - (border * Grid.CELL_SIZE),
bbox[1] - (border * Grid.CELL_SIZE),
bbox[2] + (border * Grid.CELL_SIZE),
bbox[3] + (border * Grid.CELL_SIZE)
bbox[0] - (scaleFactor * this._cellSize),
bbox[1] - (scaleFactor * this._cellSize),
bbox[2] + (scaleFactor * this._cellSize),
bbox[3] + (scaleFactor * this._cellSize)
];

@@ -73,12 +141,10 @@ }

function grid(points) {
return new Grid(points);
function grid(points, cellSize) {
return new Grid(points, cellSize);
}
Grid.CELL_SIZE = 10;
module.exports = grid;
},{}],2:[function(require,module,exports){
},{}],4:[function(require,module,exports){
/*
(c) 2014-2015, Andrii Heonia
(c) 2014-2016, Andrii Heonia
Hull.js, a JavaScript library for concave hull generation by set of points.

@@ -92,31 +158,11 @@ https://github.com/AndriiHeonia/hull

var grid = require('./grid.js');
var formatUtil = require('./format.js');
var convexHull = require('./convex.js');
function _formatToXy(pointset, format) {
if (format === undefined) {
return pointset;
}
return pointset.map(function(pt) {
/*jslint evil: true */
var _getXY = new Function('pt', 'return [pt' + format[0] + ',' + 'pt' + format[1] + '];');
return _getXY(pt);
});
}
function _xyToFormat(pointset, format) {
if (format === undefined) {
return pointset;
}
return pointset.map(function(pt) {
/*jslint evil: true */
var _getObj = new Function('pt', 'var o = {}; o' + format[0] + '= pt[0]; o' + format[1] + '= pt[1]; return o;');
return _getObj(pt);
});
}
function _sortByX(pointset) {
return pointset.sort(function(a, b) {
if (a[0] == b[0]) {
return a[1] - b[1];
} else {
return a[0] - b[0];
return a[1] - b[1];
} else {
return a[0] - b[0];
}

@@ -126,41 +172,2 @@ });

function _getMaxY(pointset) {
var maxY = -Infinity;
for (var i = pointset.length - 1; i >= 0; i--) {
if (pointset[i][1] > maxY) {
maxY = pointset[i][1];
}
}
return maxY;
}
function _upperTangent(pointset) {
var lower = [];
for (var l = 0; l < pointset.length; l++) {
while (lower.length >= 2 && (_cross(lower[lower.length - 2], lower[lower.length - 1], pointset[l]) <= 0)) {
lower.pop();
}
lower.push(pointset[l]);
}
lower.pop();
return lower;
}
function _lowerTangent(pointset) {
var reversed = pointset.reverse(),
upper = [];
for (var u = 0; u < reversed.length; u++) {
while (upper.length >= 2 && (_cross(upper[upper.length - 2], upper[upper.length - 1], reversed[u]) <= 0)) {
upper.pop();
}
upper.push(reversed[u]);
}
upper.pop();
return upper;
}
function _cross(o, a, b) {
return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]);
}
function _sqLength(a, b) {

@@ -194,24 +201,35 @@ return Math.pow(b[0] - a[0], 2) + Math.pow(b[1] - a[1], 2);

function _bBoxAround(edge, boxSize) {
var minX, maxX, minY, maxY;
function _occupiedArea(pointset) {
var minX = Infinity,
minY = Infinity,
maxX = -Infinity,
maxY = -Infinity;
if (edge[0][0] < edge[1][0]) {
minX = edge[0][0] - boxSize;
maxX = edge[1][0] + boxSize;
} else {
minX = edge[1][0] - boxSize;
maxX = edge[0][0] + boxSize;
for (var i = pointset.length - 1; i >= 0; i--) {
if (pointset[i][0] < minX) {
minX = pointset[i][0];
}
if (pointset[i][1] < minY) {
minY = pointset[i][1];
}
if (pointset[i][0] > maxX) {
maxX = pointset[i][0];
}
if (pointset[i][1] > maxY) {
maxY = pointset[i][1];
}
}
if (edge[0][1] < edge[1][1]) {
minY = edge[0][1] - boxSize;
maxY = edge[1][1] + boxSize;
} else {
minY = edge[1][1] - boxSize;
maxY = edge[0][1] + boxSize;
}
return [
maxX - minX, // width
maxY - minY // height
];
}
function _bBoxAround(edge) {
return [
minX, minY, // tl
maxX, maxY // br
Math.min(edge[0][0], edge[1][0]), // left
Math.min(edge[0][1], edge[1][1]), // top
Math.max(edge[0][0], edge[1][0]), // right
Math.max(edge[0][1], edge[1][1]) // bottom
];

@@ -243,8 +261,10 @@ }

function _concave(convex, maxSqEdgeLen, maxSearchBBoxSize, grid) {
function _concave(convex, maxSqEdgeLen, maxSearchArea, grid, edgeSkipList) {
var edge,
border,
bBoxSize,
keyInSkipList,
scaleFactor,
midPoint,
bBoxAround,
bBoxAround,
bBoxWidth,
bBoxHeight,
midPointInserted = false;

@@ -254,15 +274,22 @@

edge = [convex[i], convex[i + 1]];
keyInSkipList = edge[0].join() + ',' + edge[1].join();
if (_sqLength(edge[0], edge[1]) < maxSqEdgeLen) { continue; }
if (_sqLength(edge[0], edge[1]) < maxSqEdgeLen ||
edgeSkipList[keyInSkipList] === true) { continue; }
border = 0;
bBoxSize = MIN_SEARCH_BBOX_SIZE;
bBoxAround = _bBoxAround(edge, bBoxSize);
scaleFactor = 0;
bBoxAround = _bBoxAround(edge);
do {
bBoxAround = grid.addBorder2Bbox(bBoxAround, border);
bBoxSize = bBoxAround[2] - bBoxAround[0];
bBoxAround = grid.extendBbox(bBoxAround, scaleFactor);
bBoxWidth = bBoxAround[2] - bBoxAround[0];
bBoxHeight = bBoxAround[3] - bBoxAround[1];
midPoint = _midPoint(edge, grid.rangePoints(bBoxAround), convex);
border++;
} while (midPoint === null && maxSearchBBoxSize > bBoxSize);
scaleFactor++;
} while (midPoint === null && (maxSearchArea[0] > bBoxWidth || maxSearchArea[1] > bBoxHeight));
if (bBoxWidth >= maxSearchArea[0] && bBoxHeight >= maxSearchArea[1]) {
edgeSkipList[keyInSkipList] = true;
}
if (midPoint !== null) {

@@ -276,3 +303,3 @@ convex.splice(i + 1, 0, midPoint);

if (midPointInserted) {
return _concave(convex, maxSqEdgeLen, maxSearchBBoxSize, grid);
return _concave(convex, maxSqEdgeLen, maxSearchArea, grid, edgeSkipList);
}

@@ -284,5 +311,8 @@

function hull(pointset, concavity, format) {
var lower, upper, convex,
var convex,
concave,
innerPoints,
maxSearchBBoxSize,
occupiedArea,
maxSearchArea,
cellSize,
maxEdgeLen = concavity || 20;

@@ -294,22 +324,28 @@

pointset = _sortByX(_formatToXy(pointset, format));
upper = _upperTangent(pointset);
lower = _lowerTangent(pointset);
convex = lower.concat(upper);
convex.push(pointset[0]);
pointset = _sortByX(formatUtil.toXy(pointset, format));
occupiedArea = _occupiedArea(pointset);
maxSearchArea = [
occupiedArea[0] * MAX_SEARCH_BBOX_SIZE_PERCENT,
occupiedArea[1] * MAX_SEARCH_BBOX_SIZE_PERCENT
];
maxSearchBBoxSize = Math.max(pointset[pointset.length - 1][0], _getMaxY(convex)) * MAX_SEARCH_BBOX_SIZE_PERCENT;
convex = convexHull(pointset);
innerPoints = pointset.filter(function(pt) {
return convex.indexOf(pt) < 0;
});
cellSize = Math.ceil(1 / (pointset.length / (occupiedArea[0] * occupiedArea[1])));
concave = _concave(
convex, Math.pow(maxEdgeLen, 2),
maxSearchArea, grid(innerPoints, cellSize), {});
return _xyToFormat(_concave(convex, Math.pow(maxEdgeLen, 2), maxSearchBBoxSize, grid(innerPoints)), format);
return formatUtil.fromXy(concave, format);
}
var MAX_CONCAVE_ANGLE_COS = Math.cos(90 / (180 / Math.PI)); // angle = 90 deg
var MIN_SEARCH_BBOX_SIZE = 5;
var MAX_SEARCH_BBOX_SIZE_PERCENT = 0.8;
var MAX_SEARCH_BBOX_SIZE_PERCENT = 0.6;
module.exports = hull;
},{"./grid.js":1,"./intersect.js":3}],3:[function(require,module,exports){
},{"./convex.js":1,"./format.js":2,"./grid.js":3,"./intersect.js":5}],5:[function(require,module,exports){
function ccw(x1, y1, x2, y2, x3, y3) {

@@ -325,2 +361,3 @@ var cw = ((y3 - y1) * (x2 - x1)) - ((y2 - y1) * (x3 - x1));

x4 = seg2[1][0], y4 = seg2[1][1];
return ccw(x1, y1, x3, y3, x4, y4) !== ccw(x2, y2, x3, y3, x4, y4) && ccw(x1, y1, x2, y2, x3, y3) !== ccw(x1, y1, x2, y2, x4, y4);

@@ -330,3 +367,3 @@ }

module.exports = intersect;
},{}]},{},[2])(2)
},{}]},{},[4])(4)
});
{
"name": "hull.js",
"version": "0.2.7",
"version": "0.2.8",
"description": "JavaScript library that builds concave hulls (shapes) by set of points",

@@ -20,2 +20,3 @@ "homepage": "https://github.com/AndriiHeonia/hull",

"devDependencies": {
"nodemon": "~1.9.0",
"mocha": "~1.18.2",

@@ -26,2 +27,3 @@ "jshint": "~2.5.0",

"scripts": {
"watch": "./node_modules/nodemon/bin/nodemon.js --watch src --exec \"./node_modules/browserify/bin/cmd.js ./src/hull.js --standalone hull > ./dist/hull.js\"",
"test": "./node_modules/jshint/bin/jshint src/intersect.js && ./node_modules/jshint/bin/jshint src/grid.js && ./node_modules/jshint/bin/jshint src/hull.js && ./node_modules/.bin/mocha -R spec && ./node_modules/browserify/bin/cmd.js ./src/hull.js --standalone hull > ./dist/hull.js"

@@ -34,2 +36,2 @@ },

"license": "BSD"
}
}

@@ -33,8 +33,8 @@ Hull.js - JavaScript library that builds concave hull by set of points.

<li>
<div>After that, the edges flex inward (according to `concavity` param). For example:</div>
<div>After that, the edges flex inward (according to the `concavity` param). For example:</div>
<div>
<img src="https://raw.githubusercontent.com/AndriiHeonia/hull/master/readme-imgs/2_1.png" />
`concavity = 80`
`concavity = 80`<br/>
<img src="https://raw.githubusercontent.com/AndriiHeonia/hull/master/readme-imgs/2_2.png" />
`concavity = 40`
`concavity = 40`<br/>
<img src="https://raw.githubusercontent.com/AndriiHeonia/hull/master/readme-imgs/2_3.png" />

@@ -47,4 +47,5 @@ `concavity = 20`

## Development
npm install # install dependencies
npm test # build dist file and run tests
npm install # install dependencies
npm test # build dist file and run tests
npm run-script watch # watch ./src dir and rebuild dist file

@@ -57,3 +58,3 @@ ## Contribute

* think about parallelisation of the calculations (on GPU or CPU);
* think about parallelisation (on GPU or CPU);
* think about holes;

@@ -66,2 +67,3 @@ * think about automatic `concavity` adjustment based on density.

* <a target="_blank" href="http://www.cs.jhu.edu/~misha/Fall05/09.13.05.pdf">Computational Geometry: Convex Hulls</a>;
* <a target="_blank" href="https://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain">Andrew's monotone chain convex hull algorithm</a>;
* <a target="_blank" href="http://bryceboe.com/2006/10/23/line-segment-intersection-algorithm/">Line Segment Intersection Algorithm</a>;

@@ -74,8 +76,10 @@ * <a target="_blank" href="http://allenchou.net/2013/07/cross-product-of-2d-vectors/">Game Math: "Cross Product" of 2D Vectors</a>;

### 0.2.8 — 01.04.2016
Added edgeSkipList to increase performance of the highly accurate shapes (with the small `concavity` number) + refactoring.
### 0.2.7 — 01.05.2015
Fix bower.json
Fixed bower.json
### 0.2.6 — 01.05.2015
Fix bower.json
Fixed bower.json
### 0.2.5 — 01.05.2015
Add Bower support
Bower support
### 0.2.4 — 23.03.2015

@@ -82,0 +86,0 @@ Minor fixes (copyrights)

@@ -1,3 +0,4 @@

function Grid(points) {
var _cells = [];
function Grid(points, cellSize) {
this._cells = [];
this._cellSize = cellSize;

@@ -8,18 +9,34 @@ points.forEach(function(point) {

y = cellXY[1];
if (_cells[x] === undefined) {
_cells[x] = [];
if (this._cells[x] === undefined) {
this._cells[x] = [];
}
if (_cells[x][y] === undefined) {
_cells[x][y] = [];
if (this._cells[x][y] === undefined) {
this._cells[x][y] = [];
}
_cells[x][y].push(point);
this._cells[x][y].push(point);
}, this);
}
this.cellPoints = function(x, y) { // (Number, Number) -> Array
return (_cells[x] !== undefined && _cells[x][y] !== undefined) ? _cells[x][y] : [];
};
Grid.prototype = {
cellPoints: function(x, y) { // (Number, Number) -> Array
return (this._cells[x] !== undefined && this._cells[x][y] !== undefined) ? this._cells[x][y] : [];
},
this.removePoint = function(point) { // (Array) -> Array
rangePoints: function(bbox) { // (Array) -> Array
var tlCellXY = this.point2CellXY([bbox[0], bbox[1]]),
brCellXY = this.point2CellXY([bbox[2], bbox[3]]),
points = [];
for (var x = tlCellXY[0]; x <= brCellXY[0]; x++) {
for (var y = tlCellXY[1]; y <= brCellXY[1]; y++) {
points = points.concat(this.cellPoints(x, y));
}
}
return points;
},
removePoint: function(point) { // (Array) -> Array
var cellXY = this.point2CellXY(point),
cell = _cells[cellXY[0]][cellXY[1]],
cell = this._cells[cellXY[0]][cellXY[1]],
pointIdxInCell;

@@ -37,32 +54,16 @@

return cell;
};
}
},
Grid.prototype = {
point2CellXY: function(point) { // (Array) -> Array
var x = parseInt(point[0] / Grid.CELL_SIZE),
y = parseInt(point[1] / Grid.CELL_SIZE);
var x = parseInt(point[0] / this._cellSize),
y = parseInt(point[1] / this._cellSize);
return [x, y];
},
rangePoints: function(bbox) { // (Array) -> Array
var tlCellXY = this.point2CellXY([bbox[0], bbox[1]]),
brCellXY = this.point2CellXY([bbox[2], bbox[3]]),
points = [];
for (var x = tlCellXY[0]; x <= brCellXY[0]; x++) {
for (var y = tlCellXY[1]; y <= brCellXY[1]; y++) {
points = points.concat(this.cellPoints(x, y));
}
}
return points;
},
addBorder2Bbox: function(bbox, border) { // (Array, Number) -> Array
extendBbox: function(bbox, scaleFactor) { // (Array, Number) -> Array
return [
bbox[0] - (border * Grid.CELL_SIZE),
bbox[1] - (border * Grid.CELL_SIZE),
bbox[2] + (border * Grid.CELL_SIZE),
bbox[3] + (border * Grid.CELL_SIZE)
bbox[0] - (scaleFactor * this._cellSize),
bbox[1] - (scaleFactor * this._cellSize),
bbox[2] + (scaleFactor * this._cellSize),
bbox[3] + (scaleFactor * this._cellSize)
];

@@ -72,8 +73,6 @@ }

function grid(points) {
return new Grid(points);
function grid(points, cellSize) {
return new Grid(points, cellSize);
}
Grid.CELL_SIZE = 10;
module.exports = grid;
/*
(c) 2014-2015, Andrii Heonia
(c) 2014-2016, Andrii Heonia
Hull.js, a JavaScript library for concave hull generation by set of points.

@@ -11,31 +11,11 @@ https://github.com/AndriiHeonia/hull

var grid = require('./grid.js');
var formatUtil = require('./format.js');
var convexHull = require('./convex.js');
function _formatToXy(pointset, format) {
if (format === undefined) {
return pointset;
}
return pointset.map(function(pt) {
/*jslint evil: true */
var _getXY = new Function('pt', 'return [pt' + format[0] + ',' + 'pt' + format[1] + '];');
return _getXY(pt);
});
}
function _xyToFormat(pointset, format) {
if (format === undefined) {
return pointset;
}
return pointset.map(function(pt) {
/*jslint evil: true */
var _getObj = new Function('pt', 'var o = {}; o' + format[0] + '= pt[0]; o' + format[1] + '= pt[1]; return o;');
return _getObj(pt);
});
}
function _sortByX(pointset) {
return pointset.sort(function(a, b) {
if (a[0] == b[0]) {
return a[1] - b[1];
} else {
return a[0] - b[0];
return a[1] - b[1];
} else {
return a[0] - b[0];
}

@@ -45,41 +25,2 @@ });

function _getMaxY(pointset) {
var maxY = -Infinity;
for (var i = pointset.length - 1; i >= 0; i--) {
if (pointset[i][1] > maxY) {
maxY = pointset[i][1];
}
}
return maxY;
}
function _upperTangent(pointset) {
var lower = [];
for (var l = 0; l < pointset.length; l++) {
while (lower.length >= 2 && (_cross(lower[lower.length - 2], lower[lower.length - 1], pointset[l]) <= 0)) {
lower.pop();
}
lower.push(pointset[l]);
}
lower.pop();
return lower;
}
function _lowerTangent(pointset) {
var reversed = pointset.reverse(),
upper = [];
for (var u = 0; u < reversed.length; u++) {
while (upper.length >= 2 && (_cross(upper[upper.length - 2], upper[upper.length - 1], reversed[u]) <= 0)) {
upper.pop();
}
upper.push(reversed[u]);
}
upper.pop();
return upper;
}
function _cross(o, a, b) {
return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]);
}
function _sqLength(a, b) {

@@ -113,24 +54,35 @@ return Math.pow(b[0] - a[0], 2) + Math.pow(b[1] - a[1], 2);

function _bBoxAround(edge, boxSize) {
var minX, maxX, minY, maxY;
function _occupiedArea(pointset) {
var minX = Infinity,
minY = Infinity,
maxX = -Infinity,
maxY = -Infinity;
if (edge[0][0] < edge[1][0]) {
minX = edge[0][0] - boxSize;
maxX = edge[1][0] + boxSize;
} else {
minX = edge[1][0] - boxSize;
maxX = edge[0][0] + boxSize;
for (var i = pointset.length - 1; i >= 0; i--) {
if (pointset[i][0] < minX) {
minX = pointset[i][0];
}
if (pointset[i][1] < minY) {
minY = pointset[i][1];
}
if (pointset[i][0] > maxX) {
maxX = pointset[i][0];
}
if (pointset[i][1] > maxY) {
maxY = pointset[i][1];
}
}
if (edge[0][1] < edge[1][1]) {
minY = edge[0][1] - boxSize;
maxY = edge[1][1] + boxSize;
} else {
minY = edge[1][1] - boxSize;
maxY = edge[0][1] + boxSize;
}
return [
maxX - minX, // width
maxY - minY // height
];
}
function _bBoxAround(edge) {
return [
minX, minY, // tl
maxX, maxY // br
Math.min(edge[0][0], edge[1][0]), // left
Math.min(edge[0][1], edge[1][1]), // top
Math.max(edge[0][0], edge[1][0]), // right
Math.max(edge[0][1], edge[1][1]) // bottom
];

@@ -162,8 +114,10 @@ }

function _concave(convex, maxSqEdgeLen, maxSearchBBoxSize, grid) {
function _concave(convex, maxSqEdgeLen, maxSearchArea, grid, edgeSkipList) {
var edge,
border,
bBoxSize,
keyInSkipList,
scaleFactor,
midPoint,
bBoxAround,
bBoxAround,
bBoxWidth,
bBoxHeight,
midPointInserted = false;

@@ -173,15 +127,22 @@

edge = [convex[i], convex[i + 1]];
keyInSkipList = edge[0].join() + ',' + edge[1].join();
if (_sqLength(edge[0], edge[1]) < maxSqEdgeLen) { continue; }
if (_sqLength(edge[0], edge[1]) < maxSqEdgeLen ||
edgeSkipList[keyInSkipList] === true) { continue; }
border = 0;
bBoxSize = MIN_SEARCH_BBOX_SIZE;
bBoxAround = _bBoxAround(edge, bBoxSize);
scaleFactor = 0;
bBoxAround = _bBoxAround(edge);
do {
bBoxAround = grid.addBorder2Bbox(bBoxAround, border);
bBoxSize = bBoxAround[2] - bBoxAround[0];
bBoxAround = grid.extendBbox(bBoxAround, scaleFactor);
bBoxWidth = bBoxAround[2] - bBoxAround[0];
bBoxHeight = bBoxAround[3] - bBoxAround[1];
midPoint = _midPoint(edge, grid.rangePoints(bBoxAround), convex);
border++;
} while (midPoint === null && maxSearchBBoxSize > bBoxSize);
scaleFactor++;
} while (midPoint === null && (maxSearchArea[0] > bBoxWidth || maxSearchArea[1] > bBoxHeight));
if (bBoxWidth >= maxSearchArea[0] && bBoxHeight >= maxSearchArea[1]) {
edgeSkipList[keyInSkipList] = true;
}
if (midPoint !== null) {

@@ -195,3 +156,3 @@ convex.splice(i + 1, 0, midPoint);

if (midPointInserted) {
return _concave(convex, maxSqEdgeLen, maxSearchBBoxSize, grid);
return _concave(convex, maxSqEdgeLen, maxSearchArea, grid, edgeSkipList);
}

@@ -203,5 +164,8 @@

function hull(pointset, concavity, format) {
var lower, upper, convex,
var convex,
concave,
innerPoints,
maxSearchBBoxSize,
occupiedArea,
maxSearchArea,
cellSize,
maxEdgeLen = concavity || 20;

@@ -213,20 +177,26 @@

pointset = _sortByX(_formatToXy(pointset, format));
upper = _upperTangent(pointset);
lower = _lowerTangent(pointset);
convex = lower.concat(upper);
convex.push(pointset[0]);
pointset = _sortByX(formatUtil.toXy(pointset, format));
occupiedArea = _occupiedArea(pointset);
maxSearchArea = [
occupiedArea[0] * MAX_SEARCH_BBOX_SIZE_PERCENT,
occupiedArea[1] * MAX_SEARCH_BBOX_SIZE_PERCENT
];
maxSearchBBoxSize = Math.max(pointset[pointset.length - 1][0], _getMaxY(convex)) * MAX_SEARCH_BBOX_SIZE_PERCENT;
convex = convexHull(pointset);
innerPoints = pointset.filter(function(pt) {
return convex.indexOf(pt) < 0;
});
cellSize = Math.ceil(1 / (pointset.length / (occupiedArea[0] * occupiedArea[1])));
concave = _concave(
convex, Math.pow(maxEdgeLen, 2),
maxSearchArea, grid(innerPoints, cellSize), {});
return _xyToFormat(_concave(convex, Math.pow(maxEdgeLen, 2), maxSearchBBoxSize, grid(innerPoints)), format);
return formatUtil.fromXy(concave, format);
}
var MAX_CONCAVE_ANGLE_COS = Math.cos(90 / (180 / Math.PI)); // angle = 90 deg
var MIN_SEARCH_BBOX_SIZE = 5;
var MAX_SEARCH_BBOX_SIZE_PERCENT = 0.8;
var MAX_SEARCH_BBOX_SIZE_PERCENT = 0.6;
module.exports = hull;

@@ -11,2 +11,3 @@ function ccw(x1, y1, x2, y2, x3, y3) {

x4 = seg2[1][0], y4 = seg2[1][1];
return ccw(x1, y1, x3, y3, x4, y4) !== ccw(x2, y2, x3, y3, x4, y4) && ccw(x1, y1, x2, y2, x3, y3) !== ccw(x1, y1, x2, y2, x4, y4);

@@ -13,0 +14,0 @@ }

@@ -20,3 +20,3 @@ var assert = require("assert"),

];
var g = grid(points);
var g = grid(points, 10);

@@ -66,8 +66,8 @@ module.exports = function() {

describe('addBorder2Bbox', function() {
describe('extendBbox', function() {
it('should increase bBox to 1 cell', function() {
assert.deepEqual(g.addBorder2Bbox([0, 0, 11, 11], 1), [-10, -10, 21, 21]);
assert.deepEqual(g.extendBbox([0, 0, 11, 11], 1), [-10, -10, 21, 21]);
});
it('should increase bBox to 2 cells', function() {
assert.deepEqual(g.addBorder2Bbox([0, 0, 11, 11], 2), [-20, -20, 31, 31]);
assert.deepEqual(g.extendBbox([0, 0, 11, 11], 2), [-20, -20, 31, 31]);
});

@@ -74,0 +74,0 @@ });

@@ -6,18 +6,18 @@ var assert = require("assert"),

it('should return correct hull of the "?" symbol', function() {
var vertices = [[162, 332], [182, 299], [141, 292], [158, 264], [141, 408], [160, 400], [177, 430], [151, 442], [155, 425], [134, 430], [126, 447], [139, 466], [160, 471], [167, 447], [182, 466], [192, 442], [187, 413], [173, 403], [168, 425], [153, 413], [179, 275], [163, 292], [134, 270], [143, 315], [177, 320], [163, 311], [162, 281], [182, 255], [141, 226], [156, 235], [173, 207], [187, 230], [204, 194], [165, 189], [145, 201], [158, 167], [190, 165], [206, 145], [179, 153], [204, 114], [221, 138], [243, 112], [248, 139], [177, 122], [179, 99], [196, 82], [219, 90], [240, 75], [218, 61], [228, 53], [211, 34], [197, 51], [179, 65], [155, 70], [165, 85], [134, 80], [124, 58], [153, 44], [173, 34], [192, 27], [156, 19], [119, 32], [128, 17], [138, 36], [100, 58], [112, 73], [100, 92], [78, 100], [83, 78], [61, 63], [80, 44], [100, 26], [60, 39], [43, 71], [34, 54], [32, 90], [53, 104], [60, 82], [66, 99], [247, 94], [187, 180], [221, 168]];
var expected = [[248,139], [221,168], [204,194], [187,230], [182,255], [182,299], [177,320], [162,332], [160,400], [173,403], [187,413], [192,442], [182,466], [160,471], [139,466], [126,447], [134,430], [141,408], [143,315], [141,292], [134,270], [141,226], [145,201], [158,167], [177,122], [179,99], [165,85], [134,80], [100,92], [78,100], [53,104], [32,90], [34,54], [60,39], [100,26], [128,17], [156,19], [192,27], [211,34], [228,53], [240,75], [247,94], [248,139]];
assert.deepEqual(hull(vertices, 50), expected);
var points = [[162, 332], [182, 299], [141, 292], [158, 264], [141, 408], [160, 400], [177, 430], [151, 442], [155, 425], [134, 430], [126, 447], [139, 466], [160, 471], [167, 447], [182, 466], [192, 442], [187, 413], [173, 403], [168, 425], [153, 413], [179, 275], [163, 292], [134, 270], [143, 315], [177, 320], [163, 311], [162, 281], [182, 255], [141, 226], [156, 235], [173, 207], [187, 230], [204, 194], [165, 189], [145, 201], [158, 167], [190, 165], [206, 145], [179, 153], [204, 114], [221, 138], [243, 112], [248, 139], [177, 122], [179, 99], [196, 82], [219, 90], [240, 75], [218, 61], [228, 53], [211, 34], [197, 51], [179, 65], [155, 70], [165, 85], [134, 80], [124, 58], [153, 44], [173, 34], [192, 27], [156, 19], [119, 32], [128, 17], [138, 36], [100, 58], [112, 73], [100, 92], [78, 100], [83, 78], [61, 63], [80, 44], [100, 26], [60, 39], [43, 71], [34, 54], [32, 90], [53, 104], [60, 82], [66, 99], [247, 94], [187, 180], [221, 168]];
var expected = [[248,139], [221,168], [204,194], [187,230], [182,255], [182,299], [177,320], [160,400], [173,403], [187,413], [192,442], [182,466], [160,471], [139,466], [126,447], [141,408], [162,332], [143,315], [141,292], [134,270], [141,226], [145,201], [158,167], [177,122], [179,99], [165,85], [134,80], [100,92], [78,100], [53,104], [32,90], [34,54], [60,39], [100,26], [128,17], [156,19], [192,27], [211,34], [228,53], [240,75], [247,94], [248,139]];
assert.deepEqual(hull(points, 50), expected);
});
it('should return only outer hull of the polygon with hole', function() {
var vertices = [[141, 408], [160, 400], [177, 430], [151, 442], [155, 425], [134, 430], [126, 447], [139, 466], [160, 471], [167, 447], [182, 466], [192, 442], [187, 413], [173, 403], [165, 430], [171, 430], [177, 437], [175, 443], [172, 444], [163, 448], [156, 447], [153, 438], [154, 431], [160, 428]];
var points = [[141, 408], [160, 400], [177, 430], [151, 442], [155, 425], [134, 430], [126, 447], [139, 466], [160, 471], [167, 447], [182, 466], [192, 442], [187, 413], [173, 403], [165, 430], [171, 430], [177, 437], [175, 443], [172, 444], [163, 448], [156, 447], [153, 438], [154, 431], [160, 428]];
var expected = [[192,442], [182,466], [160,471], [139,466], [126,447], [141,408], [160,400], [173,403], [187,413], [192,442]];
assert.deepEqual(hull(vertices, 50), expected);
assert.deepEqual(hull(points, 50), expected);
});
it('should return concave hull with lngs and lats', function() {
var vertices = [{lng:-0.206792373176235, lat:51.4911165465815 }, {lng:-0.207062672933557, lat:51.4915703125214 }, {lng:-0.207465840096923, lat:51.4912077781219 }, {lng:-0.210193421020222, lat:51.4918159814458 }, {lng:-0.214944392455692, lat:51.4929945001276 }, {lng:-0.208133371509344, lat:51.4910830915252 }, {lng:-0.214162055384851, lat:51.4905275966855 }, {lng:-0.208161917730384, lat:51.4903551232517 }, {lng:-0.209680931181673, lat:51.4901894811742 }, {lng:-0.212571431609104, lat:51.4903145141462 }, {lng:-0.216849005460861, lat:51.4921781720221 } ];
var expected = [{lng: -0.206792373176235, lat: 51.4911165465815 }, {lng: -0.207062672933557, lat: 51.4915703125214 }, {lng: -0.207465840096923, lat: 51.4912077781219 }, {lng: -0.210193421020222, lat: 51.4918159814458 }, {lng: -0.214944392455692, lat: 51.4929945001276 }, {lng: -0.216849005460861, lat: 51.4921781720221 }, {lng: -0.214162055384851, lat: 51.4905275966855 }, {lng: -0.212571431609104, lat: 51.4903145141462 }, {lng: -0.209680931181673, lat: 51.4901894811742 }, {lng: -0.208161917730384, lat: 51.4903551232517 }, {lng: -0.208133371509344, lat: 51.4910830915252 }, {lng: -0.206792373176235, lat: 51.4911165465815 }];
assert.deepEqual(hull(vertices, 0.0011, ['.lng', '.lat']), expected);
var points = [{lng:-0.206792373176235, lat:51.4911165465815 }, {lng:-0.207062672933557, lat:51.4915703125214 }, {lng:-0.207465840096923, lat:51.4912077781219 }, {lng:-0.210193421020222, lat:51.4918159814458 }, {lng:-0.214944392455692, lat:51.4929945001276 }, {lng:-0.208133371509344, lat:51.4910830915252 }, {lng:-0.214162055384851, lat:51.4905275966855 }, {lng:-0.208161917730384, lat:51.4903551232517 }, {lng:-0.209680931181673, lat:51.4901894811742 }, {lng:-0.212571431609104, lat:51.4903145141462 }, {lng:-0.216849005460861, lat:51.4921781720221}];
var expected = [{lng: -0.206792373176235, lat: 51.4911165465815 }, {lng: -0.207062672933557, lat: 51.4915703125214 }, {lng: -0.207465840096923, lat: 51.4912077781219 }, {lng: -0.210193421020222, lat: 51.4918159814458 }, {lng: -0.214944392455692, lat: 51.4929945001276 }, {lng: -0.216849005460861, lat: 51.4921781720221 }, {lng: -0.214162055384851, lat: 51.4905275966855 }, {lng: -0.212571431609104, lat: 51.4903145141462 }, {lng: -0.209680931181673, lat: 51.4901894811742 }, {lng: -0.208161917730384, lat: 51.4903551232517 }, {lng: -0.208133371509344, lat: 51.4910830915252 }, {lng: -0.206792373176235, lat: 51.4911165465815}];
assert.deepEqual(hull(points, 0.0011, ['.lng', '.lat']), expected);
});
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc