light-chart
Version:
Charts for mobile visualization.
197 lines (178 loc) • 4.45 kB
JavaScript
const Vector2 = require('./vector2');
const start = Vector2.create();
const end = Vector2.create();
const extremity = Vector2.create();
function getCubicBezierXYatT(startPt, controlPt1, controlPt2, endPt, T) {
const x = CubicN(T, startPt.x, controlPt1.x, controlPt2.x, endPt.x);
const y = CubicN(T, startPt.y, controlPt1.y, controlPt2.y, endPt.y);
return ({
x,
y
});
}
// cubic helper formula at T distance
function CubicN(T, a, b, c, d) {
const t2 = T * T;
const t3 = t2 * T;
return a + (-a * 3 + T * (3 * a - a * T)) * T + (3 * b + T * (-6 * b + b * 3 * T)) * T + (c * 3 - c * 3 * T) * t2 + d * t3;
}
function cubicBezierBounds(c) {
let minX = Infinity;
let maxX = -Infinity;
let minY = Infinity;
let maxY = -Infinity;
const s = {
x: c[0],
y: c[1]
};
const c1 = {
x: c[2],
y: c[3]
};
const c2 = {
x: c[4],
y: c[5]
};
const e = {
x: c[6],
y: c[7]
};
for (let t = 0; t < 100; t++) {
const pt = getCubicBezierXYatT(s, c1, c2, e, t / 100);
if (pt.x < minX) {
minX = pt.x;
}
if (pt.x > maxX) {
maxX = pt.x;
}
if (pt.y < minY) {
minY = pt.y;
}
if (pt.y > maxY) {
maxY = pt.y;
}
}
return {
minX,
minY,
maxX,
maxY
};
}
module.exports = {
getBBoxFromPoints(points, lineWidth) {
if (points.length === 0) {
return;
}
let p = points[0];
let left = p.x;
let right = p.x;
let top = p.y;
let bottom = p.y;
const len = points.length;
for (let i = 1; i < len; i++) {
p = points[i];
left = Math.min(left, p.x);
right = Math.max(right, p.x);
top = Math.min(top, p.y);
bottom = Math.max(bottom, p.y);
}
lineWidth = (lineWidth / 2) || 0;
return {
minX: left - lineWidth,
minY: top - lineWidth,
maxX: right + lineWidth,
maxY: bottom + lineWidth
};
},
getBBoxFromLine(x0, y0, x1, y1, lineWidth) {
lineWidth = (lineWidth / 2) || 0;
return {
minX: Math.min(x0, x1) - lineWidth,
minY: Math.min(y0, y1) - lineWidth,
maxX: Math.max(x0, x1) + lineWidth,
maxY: Math.max(y0, y1) + lineWidth
};
},
getBBoxFromArc(x, y, r, startAngle, endAngle, anticlockwise) {
const diff = Math.abs(startAngle - endAngle);
if (diff % (Math.PI * 2) < 1e-4 && diff > 1e-4) {
// Is a circle
return {
minX: x - r,
minY: y - r,
maxX: x + r,
maxY: y + r
};
}
start[0] = Math.cos(startAngle) * r + x;
start[1] = Math.sin(startAngle) * r + y;
end[0] = Math.cos(endAngle) * r + x;
end[1] = Math.sin(endAngle) * r + y;
const min = [ 0, 0 ];
const max = [ 0, 0 ];
Vector2.min(min, start, end);
Vector2.max(max, start, end);
// Thresh to [0, Math.PI * 2]
startAngle = startAngle % (Math.PI * 2);
if (startAngle < 0) {
startAngle = startAngle + Math.PI * 2;
}
endAngle = endAngle % (Math.PI * 2);
if (endAngle < 0) {
endAngle = endAngle + Math.PI * 2;
}
if (startAngle > endAngle && !anticlockwise) {
endAngle += Math.PI * 2;
} else if (startAngle < endAngle && anticlockwise) {
startAngle += Math.PI * 2;
}
if (anticlockwise) {
const tmp = endAngle;
endAngle = startAngle;
startAngle = tmp;
}
for (let angle = 0; angle < endAngle; angle += Math.PI / 2) {
if (angle > startAngle) {
extremity[0] = Math.cos(angle) * r + x;
extremity[1] = Math.sin(angle) * r + y;
Vector2.min(min, extremity, min);
Vector2.max(max, extremity, max);
}
}
return {
minX: min[0],
minY: min[1],
maxX: max[0],
maxY: max[1]
};
},
getBBoxFromBezierGroup(points, lineWidth) {
let minX = Infinity;
let maxX = -Infinity;
let minY = Infinity;
let maxY = -Infinity;
for (let i = 0, len = points.length; i < len; i++) {
const bbox = cubicBezierBounds(points[i]);
if (bbox.minX < minX) {
minX = bbox.minX;
}
if (bbox.maxX > maxX) {
maxX = bbox.maxX;
}
if (bbox.minY < minY) {
minY = bbox.minY;
}
if (bbox.maxY > maxY) {
maxY = bbox.maxY;
}
}
lineWidth = (lineWidth / 2) || 0;
return {
minX: minX - lineWidth,
minY: minY - lineWidth,
maxX: maxX + lineWidth,
maxY: maxY + lineWidth
};
}
};