@awayjs/renderer
Version:
Renderer for AwayJS
257 lines (256 loc) • 8.63 kB
JavaScript
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 };