@antv/util
Version:
> AntV 底层依赖的工具库,不建议在自己业务中使用。
143 lines • 5.79 kB
JavaScript
import { segmentLineFactory } from './segment-line-factory';
import { distanceSquareRoot } from './distance-square-root';
function angleBetween(v0, v1) {
var v0x = v0.x, v0y = v0.y;
var v1x = v1.x, v1y = v1.y;
var p = v0x * v1x + v0y * v1y;
var n = Math.sqrt((Math.pow(v0x, 2) + Math.pow(v0y, 2)) * (Math.pow(v1x, 2) + Math.pow(v1y, 2)));
var sign = v0x * v1y - v0y * v1x < 0 ? -1 : 1;
var angle = sign * Math.acos(p / n);
return angle;
}
/**
* Returns a {x,y} point at a given length, the total length and
* the minimum and maximum {x,y} coordinates of a C (cubic-bezier) segment.
* @see https://github.com/MadLittleMods/svg-curve-lib/blob/master/src/js/svg-curve-lib.js
*/
function getPointAtArcSegmentLength(x1, y1, RX, RY, angle, LAF, SF, x, y, t) {
var abs = Math.abs, sin = Math.sin, cos = Math.cos, sqrt = Math.sqrt, PI = Math.PI;
var rx = abs(RX);
var ry = abs(RY);
var xRot = ((angle % 360) + 360) % 360;
var xRotRad = xRot * (PI / 180);
if (x1 === x && y1 === y) {
return { x: x1, y: y1 };
}
if (rx === 0 || ry === 0) {
return segmentLineFactory(x1, y1, x, y, t).point;
}
var dx = (x1 - x) / 2;
var dy = (y1 - y) / 2;
var transformedPoint = {
x: cos(xRotRad) * dx + sin(xRotRad) * dy,
y: -sin(xRotRad) * dx + cos(xRotRad) * dy,
};
var radiiCheck = Math.pow(transformedPoint.x, 2) / Math.pow(rx, 2) + Math.pow(transformedPoint.y, 2) / Math.pow(ry, 2);
if (radiiCheck > 1) {
rx *= sqrt(radiiCheck);
ry *= sqrt(radiiCheck);
}
var cSquareNumerator = Math.pow(rx, 2) * Math.pow(ry, 2) - Math.pow(rx, 2) * Math.pow(transformedPoint.y, 2) - Math.pow(ry, 2) * Math.pow(transformedPoint.x, 2);
var cSquareRootDenom = Math.pow(rx, 2) * Math.pow(transformedPoint.y, 2) + Math.pow(ry, 2) * Math.pow(transformedPoint.x, 2);
var cRadicand = cSquareNumerator / cSquareRootDenom;
cRadicand = cRadicand < 0 ? 0 : cRadicand;
var cCoef = (LAF !== SF ? 1 : -1) * sqrt(cRadicand);
var transformedCenter = {
x: cCoef * ((rx * transformedPoint.y) / ry),
y: cCoef * (-(ry * transformedPoint.x) / rx),
};
var center = {
x: cos(xRotRad) * transformedCenter.x - sin(xRotRad) * transformedCenter.y + (x1 + x) / 2,
y: sin(xRotRad) * transformedCenter.x + cos(xRotRad) * transformedCenter.y + (y1 + y) / 2,
};
var startVector = {
x: (transformedPoint.x - transformedCenter.x) / rx,
y: (transformedPoint.y - transformedCenter.y) / ry,
};
var startAngle = angleBetween({ x: 1, y: 0 }, startVector);
var endVector = {
x: (-transformedPoint.x - transformedCenter.x) / rx,
y: (-transformedPoint.y - transformedCenter.y) / ry,
};
var sweepAngle = angleBetween(startVector, endVector);
if (!SF && sweepAngle > 0) {
sweepAngle -= 2 * PI;
}
else if (SF && sweepAngle < 0) {
sweepAngle += 2 * PI;
}
sweepAngle %= 2 * PI;
var alpha = startAngle + sweepAngle * t;
var ellipseComponentX = rx * cos(alpha);
var ellipseComponentY = ry * sin(alpha);
var point = {
x: cos(xRotRad) * ellipseComponentX - sin(xRotRad) * ellipseComponentY + center.x,
y: sin(xRotRad) * ellipseComponentX + cos(xRotRad) * ellipseComponentY + center.y,
};
// to be used later
// point.ellipticalArcStartAngle = startAngle;
// point.ellipticalArcEndAngle = startAngle + sweepAngle;
// point.ellipticalArcAngle = alpha;
// point.ellipticalArcCenter = center;
// point.resultantRx = rx;
// point.resultantRy = ry;
return point;
}
/**
* Returns a {x,y} point at a given length, the total length and
* the shape minimum and maximum {x,y} coordinates of an A (arc-to) segment.
*
* For better performance, it can skip calculate bbox or length in some scenario.
*/
export function segmentArcFactory(X1, Y1, RX, RY, angle, LAF, SF, X2, Y2, distance, options) {
var _a;
var _b = options.bbox, bbox = _b === void 0 ? true : _b, _c = options.length, length = _c === void 0 ? true : _c, _d = options.sampleSize, sampleSize = _d === void 0 ? 30 : _d;
var distanceIsNumber = typeof distance === 'number';
var x = X1;
var y = Y1;
var LENGTH = 0;
var prev = [x, y, LENGTH];
var cur = [x, y];
var t = 0;
var POINT = { x: 0, y: 0 };
var POINTS = [{ x: x, y: y }];
if (distanceIsNumber && distance <= 0) {
POINT = { x: x, y: y };
}
// bad perf when size > 100
for (var j = 0; j <= sampleSize; j += 1) {
t = j / sampleSize;
(_a = getPointAtArcSegmentLength(X1, Y1, RX, RY, angle, LAF, SF, X2, Y2, t), x = _a.x, y = _a.y);
if (bbox) {
POINTS.push({ x: x, y: y });
}
if (length) {
LENGTH += distanceSquareRoot(cur, [x, y]);
}
cur = [x, y];
if (distanceIsNumber && LENGTH >= distance && distance > prev[2]) {
var dv = (LENGTH - distance) / (LENGTH - prev[2]);
POINT = {
x: cur[0] * (1 - dv) + prev[0] * dv,
y: cur[1] * (1 - dv) + prev[1] * dv,
};
}
prev = [x, y, LENGTH];
}
if (distanceIsNumber && distance >= LENGTH) {
POINT = { x: X2, y: Y2 };
}
return {
length: LENGTH,
point: POINT,
min: {
x: Math.min.apply(null, POINTS.map(function (n) { return n.x; })),
y: Math.min.apply(null, POINTS.map(function (n) { return n.y; })),
},
max: {
x: Math.max.apply(null, POINTS.map(function (n) { return n.x; })),
y: Math.max.apply(null, POINTS.map(function (n) { return n.y; })),
},
};
}
//# sourceMappingURL=segment-arc-factory.js.map