UNPKG

@awayjs/renderer

Version:
257 lines (256 loc) 8.63 kB
import { Box } from '@awayjs/core'; var FIXED_BASE = 1000; var PI = Math.PI; var PI2 = PI * 2; var PI_2 = PI / 2; var ConvexHull = /** @class */ (function () { function ConvexHull(obj) { this.ranges = null; this.edges = obj.edges; this.points = obj.points; this.middle = obj.middle; this._constructRanges(); } ConvexHull.prototype._constructRanges = function () { var edges = this.edges; var r = this.ranges = []; var range = 0; this.ranges[range] = { from: 0, to: 0 }; for (var i = 0; i < edges.length; i++) { if (edges[i].angle > (range + 1) * PI_2) { r[range].to = i - 1; r[++range] = { from: i, to: 0 }; } } r[range].to = edges.length - 1; }; ConvexHull.prototype.fetchEdge = function (angle) { var a = angle; while (a < 0) a += PI2; while (a >= PI2) a -= PI2; var edges = this.edges; var from = 0; var to = edges.length - 1; if (this.ranges) { var range = 3; if (a < PI2) { range = 0; } else if (a < PI) { range = 1; } else if (a < PI + PI_2) { range = 2; } from = this.ranges[range].from; to = this.ranges[range].to; } var minEdge = this.edges[from]; for (var i = from + 1; i <= to; i++) { if (edges[i].angle > a) { return minEdge; } minEdge = edges[i]; } return minEdge; }; ConvexHull.prototype.fetchPoint = function (index) { if (index < 0) index += this.points.length; return this.points[index % this.points.length]; }; ConvexHull.prototype.dispose = function () { this.edges = null; this.points = null; this.ranges = null; this.middle = null; }; return ConvexHull; }()); export { ConvexHull }; var ConvexHullUtils = /** @class */ (function () { function ConvexHullUtils() { } ConvexHullUtils.ccw = function (p1, p2, p3) { return (p2[0] - p1[0]) * (p3[1] - p1[1]) - (p2[1] - p1[1]) * (p3[0] - p1[0]); }; ConvexHullUtils.cmpPoints = function (a, b) { return (a[0] < b[0] || (Math.abs(a[0] - b[0]) === 0 && a[1] < b[1])) ? 1 : -1; }; ConvexHullUtils.cmpEdges = function (a, b) { return a.angle - b.angle; }; ConvexHullUtils.nearest = function (x0, y0, x1, y1) { var dx = (x0 - x1); (dx < 0) && (dx = -dx); var dy = (y0 - y1); (dy < 0) && (dy = -dy); return (dx + dy) < this.EPS; }; /** * @description Generate a hull by modified Graham scan, not required sorting by angle * @see https://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain */ ConvexHullUtils.generateHull = function (points) { var len = points.length; // sort by X and Y points.sort(this.cmpPoints); var p1 = points[0]; var p2 = points[points.length - 1]; // stack for selection var top = [p1]; var bottom = [p1]; var last = p1; for (var i = 1; i < len; i++) { var next = points[i]; if (this.nearest(last[0], last[1], next[0], next[1])) { continue; } last = next; var a = this.ccw(p1, next, p2); // pass for TOP part of shape if (i === len - 1 || a < 0) { while (top.length > 1 && this.ccw(top[top.length - 2], top[top.length - 1], next) >= 0) { top.length--; } top.push(next); } // pass for BOTTOM part of shape if (i === len - 1 || a > 0) { while (bottom.length > 1 && this.ccw(bottom[bottom.length - 2], bottom[bottom.length - 1], next) < 0) { bottom.length--; } bottom.push(next); } } if ((bottom.length + top.length) < 5) { // invalid hull, drop; return null; } // drop first point, because it same as in top bottom.shift(); // drop last point, because it same as in top if (bottom.length > 0) bottom.length--; // concat in reversed order var hull = top.concat(bottom.reverse()); var edges = new Array(hull.length); var hullLen = hull.length; var middle = [0, 0]; var atan2 = Math.atan2; for (var i = 0; i < hullLen; i++) { var p0 = hull[i]; var p1_1 = hull[(i + 1) % hullLen]; var angle = atan2(-(p1_1[1] - p0[1]), p1_1[0] - p0[0]); if (angle < 0) { angle += Math.PI * 2; } edges[i] = { a: i, b: (i + 1) % hullLen, angle: angle }; middle[0] += p0[0]; middle[1] += p0[1]; } middle[0] /= hullLen; middle[1] /= hullLen; // sort edges by angle, because 0 is left // TODO: fix ordering to avoid a sorting // but this is very fast when a data blocked sorted, // because ib v8 this used a TimSort edges.sort(this.cmpEdges); return new ConvexHull({ points: hull, edges: edges, middle: middle }); }; ConvexHullUtils.fromBuffer = function (buffer, dimension) { if (dimension === void 0) { dimension = 2; } // we should reconstuct this for [[0,0]] array to simplyfy sorting and math var len = buffer.length / dimension | 0; var points = new Array(len); for (var i = 0; i < len; i++) { // we not support 3D buffer points[i] = [buffer[i * dimension], buffer[i * dimension + 1]]; } return this.generateHull(points); }; ConvexHullUtils.fromAttribute = function (posAttrs, indexAttrs, step, count, offset) { if (indexAttrs === void 0) { indexAttrs = null; } if (step === void 0) { step = 1; } if (offset === void 0) { offset = 0; } var indices; var len; var positions; var stride = posAttrs.stride; if (indexAttrs) { len = count * indexAttrs.dimensions; indices = indexAttrs.get(count, offset); positions = posAttrs.get(posAttrs.count); } else { len = count; positions = posAttrs.get(count, offset); } var points = new Array(len / step); var p = 0; for (var i = 0; i < len; i += step) { var index = indices ? indices[i] * stride : i * stride; points[p] = [ positions[index], positions[index + 1] ]; p++; } return this.generateHull(points); }; ConvexHullUtils.createBox = function (simpleHull, matrix, target, cache) { // construct 2D bounds from hull without fast approximation becasue it not tested var rawData = matrix === null || matrix === void 0 ? void 0 : matrix._rawData; var minX = Infinity; var minY = Infinity; var maxX = -Infinity; var maxY = -Infinity; if (target) { minX = target.x; minY = target.y; maxX = minX + target.width; maxY = minY + target.height; } else { target = cache || new Box(); } for (var _i = 0, _a = simpleHull.points; _i < _a.length; _i++) { var p = _a[_i]; var x = p[0]; var y = p[1]; if (rawData) { var ox = x; var oy = y; x = ox * rawData[0] + oy * rawData[4] + rawData[12]; y = ox * rawData[1] + oy * rawData[5] + rawData[13]; } if (x < minX) minX = x; if (x > maxX) maxX = x; if (y < minY) minY = y; if (y > maxY) maxY = y; } target.x = minX; target.y = minY; target.width = maxX - minX; target.height = maxY - minY; target.z = target.depth = 0; return target; }; ConvexHullUtils.EPS = 1.0 / FIXED_BASE; return ConvexHullUtils; }()); export { ConvexHullUtils };