UNPKG

d3plus-shape

Version:

Fancy SVG shapes for visualizations

120 lines (101 loc) 3.33 kB
import pointDistanceSquared from "./pointDistanceSquared"; /** @desc square distance from a point to a segment @param {Array} point @param {Array} segmentAnchor1 @param {Array} segmentAnchor2 @private */ function getSqSegDist(p, p1, p2) { var x = p1[0], y = p1[1]; var 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; } /** @desc basic distance-based simplification @param {Array} polygon @param {Number} sqTolerance @private */ function simplifyRadialDist(poly, sqTolerance) { var point, prevPoint = poly[0]; var newPoints = [prevPoint]; for (var i = 1, len = poly.length; i < len; i++) { point = poly[i]; if (pointDistanceSquared(point, prevPoint) > sqTolerance) { newPoints.push(point); prevPoint = point; } } if (prevPoint !== point) newPoints.push(point); return newPoints; } /** @param {Array} polygon @param {Number} first @param {Number} last @param {Number} sqTolerance @param {Array} simplified @private */ function simplifyDPStep(poly, first, last, sqTolerance, simplified) { var index, maxSqDist = sqTolerance; for (var i = first + 1; i < last; i++) { var sqDist = getSqSegDist(poly[i], poly[first], poly[last]); if (sqDist > maxSqDist) { index = i; maxSqDist = sqDist; } } if (maxSqDist > sqTolerance) { if (index - first > 1) simplifyDPStep(poly, first, index, sqTolerance, simplified); simplified.push(poly[index]); if (last - index > 1) simplifyDPStep(poly, index, last, sqTolerance, simplified); } } /** @desc simplification using Ramer-Douglas-Peucker algorithm @param {Array} polygon @param {Number} sqTolerance @private */ function simplifyDouglasPeucker(poly, sqTolerance) { var last = poly.length - 1; var simplified = [poly[0]]; simplifyDPStep(poly, 0, last, sqTolerance, simplified); simplified.push(poly[last]); return simplified; } /** @function largestRect @desc Simplifies the points of a polygon using both the Ramer-Douglas-Peucker algorithm and basic distance-based simplification. Adapted to an ES6 module from the excellent [Simplify.js](http://mourner.github.io/simplify-js/). @author Vladimir Agafonkin @param {Array} poly An Array of points that represent a polygon. @param {Number} [tolerance = 1] Affects the amount of simplification (in the same metric as the point coordinates). @param {Boolean} [highestQuality = false] Excludes distance-based preprocessing step which leads to highest quality simplification but runs ~10-20 times slower. */ export default (function (poly) { var tolerance = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1; var highestQuality = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; if (poly.length <= 2) return poly; var sqTolerance = tolerance * tolerance; poly = highestQuality ? poly : simplifyRadialDist(poly, sqTolerance); poly = simplifyDouglasPeucker(poly, sqTolerance); return poly; });