Files
NapCatQQ/.pnpm-store/v3/files/e4/bf7b2cd6e60917c446776ca0fb547a18bff2bd3e4af733bd9ad564009e9207d4b36c54649656a522a46687baadd1b22c3f47e463ece349830ab804862b25f7
T
TanTan e311ca3275
Build NapCat Artifacts / Type Check & Test (push) Waiting to run
docs: 仓库链接指向本镜像仓库
2026-11-13 09:25:58 +08:00

223 lines
6.1 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getArc;
var _bezierCubic2Q = _interopRequireDefault(require("../math/bezierCubic2Q2"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* @file 使用插值法获取椭圆弧度,以支持svg arc命令
* @author mengke01(kekee000@gmail.com)
*
* modify from:
* https://github.com/fontello/svgpath/blob/master/lib/a2c.js
* references:
* http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
*/
var TAU = Math.PI * 2;
function vectorAngle(ux, uy, vx, vy) {
// Calculate an angle between two vectors
var sign = ux * vy - uy * vx < 0 ? -1 : 1;
var umag = Math.sqrt(ux * ux + uy * uy);
var vmag = Math.sqrt(ux * ux + uy * uy);
var dot = ux * vx + uy * vy;
var div = dot / (umag * vmag);
if (div > 1 || div < -1) {
// rounding errors, e.g. -1.0000000000000002 can screw up this
div = Math.max(div, -1);
div = Math.min(div, 1);
}
return sign * Math.acos(div);
}
function correctRadii(midx, midy, rx, ry) {
// Correction of out-of-range radii
rx = Math.abs(rx);
ry = Math.abs(ry);
var Λ = midx * midx / (rx * rx) + midy * midy / (ry * ry);
if (Λ > 1) {
rx *= Math.sqrt(Λ);
ry *= Math.sqrt(Λ);
}
return [rx, ry];
}
function getArcCenter(x1, y1, x2, y2, fa, fs, rx, ry, sin_φ, cos_φ) {
// Convert from endpoint to center parameterization,
// see http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
// Step 1.
//
// Moving an ellipse so origin will be the middlepoint between our two
// points. After that, rotate it to line up ellipse axes with coordinate
// axes.
//
var x1p = cos_φ * (x1 - x2) / 2 + sin_φ * (y1 - y2) / 2;
var y1p = -sin_φ * (x1 - x2) / 2 + cos_φ * (y1 - y2) / 2;
var rx_sq = rx * rx;
var ry_sq = ry * ry;
var x1p_sq = x1p * x1p;
var y1p_sq = y1p * y1p;
// Step 2.
//
// Compute coordinates of the centre of this ellipse (cx', cy')
// in the new coordinate system.
//
var radicant = rx_sq * ry_sq - rx_sq * y1p_sq - ry_sq * x1p_sq;
if (radicant < 0) {
// due to rounding errors it might be e.g. -1.3877787807814457e-17
radicant = 0;
}
radicant /= rx_sq * y1p_sq + ry_sq * x1p_sq;
radicant = Math.sqrt(radicant) * (fa === fs ? -1 : 1);
var cxp = radicant * rx / ry * y1p;
var cyp = radicant * -ry / rx * x1p;
// Step 3.
//
// Transform back to get centre coordinates (cx, cy) in the original
// coordinate system.
//
var cx = cos_φ * cxp - sin_φ * cyp + (x1 + x2) / 2;
var cy = sin_φ * cxp + cos_φ * cyp + (y1 + y2) / 2;
// Step 4.
//
// Compute angles (θ1, Δθ).
//
var v1x = (x1p - cxp) / rx;
var v1y = (y1p - cyp) / ry;
var v2x = (-x1p - cxp) / rx;
var v2y = (-y1p - cyp) / ry;
var θ1 = vectorAngle(1, 0, v1x, v1y);
var Δθ = vectorAngle(v1x, v1y, v2x, v2y);
if (fs === 0 && Δθ > 0) {
Δθ -= TAU;
}
if (fs === 1 && Δθ < 0) {
Δθ += TAU;
}
return [cx, cy, θ1, Δθ];
}
function approximateUnitArc(θ1, Δθ) {
// Approximate one unit arc segment with bézier curves,
// see http://math.stackexchange.com/questions/873224/
// calculate-control-points-of-cubic-bezier-curve-approximating-a-part-of-a-circle
var α = 4 / 3 * Math.tan(Δθ / 4);
var x1 = Math.cos(θ1);
var y1 = Math.sin(θ1);
var x2 = Math.cos(θ1 + Δθ);
var y2 = Math.sin(θ1 + Δθ);
return [x1, y1, x1 - y1 * α, y1 + x1 * α, x2 + y2 * α, y2 - x2 * α, x2, y2];
}
function a2c(x1, y1, x2, y2, fa, fs, rx, ry, φ) {
var sin_φ = Math.sin(φ * TAU / 360);
var cos_φ = Math.cos(φ * TAU / 360);
// Make sure radii are valid
//
var x1p = cos_φ * (x1 - x2) / 2 + sin_φ * (y1 - y2) / 2;
var y1p = -sin_φ * (x1 - x2) / 2 + cos_φ * (y1 - y2) / 2;
if (x1p === 0 && y1p === 0) {
// we're asked to draw line to itself
return [];
}
if (rx === 0 || ry === 0) {
// one of the radii is zero
return [];
}
var radii = correctRadii(x1p, y1p, rx, ry);
rx = radii[0];
ry = radii[1];
// Get center parameters (cx, cy, θ1, Δθ)
//
var cc = getArcCenter(x1, y1, x2, y2, fa, fs, rx, ry, sin_φ, cos_φ);
var result = [];
var θ1 = cc[2];
var Δθ = cc[3];
// Split an arc to multiple segments, so each segment
// will be less than τ/4 (= 90°)
//
var segments = Math.max(Math.ceil(Math.abs(Δθ) / (TAU / 4)), 1);
Δθ /= segments;
for (var i = 0; i < segments; i++) {
result.push(approximateUnitArc(θ1, Δθ));
θ1 += Δθ;
}
// We have a bezier approximation of a unit circle,
// now need to transform back to the original ellipse
//
return result.map(function (curve) {
for (var _i = 0; _i < curve.length; _i += 2) {
var x = curve[_i + 0];
var y = curve[_i + 1];
// scale
x *= rx;
y *= ry;
// rotate
var xp = cos_φ * x - sin_φ * y;
var yp = sin_φ * x + cos_φ * y;
// translate
curve[_i + 0] = xp + cc[0];
curve[_i + 1] = yp + cc[1];
}
return curve;
});
}
/**
* 获取椭圆弧度
*
* @param {number} rx 椭圆长半轴
* @param {number} ry 椭圆短半轴
* @param {number} angle 旋转角度
* @param {number} largeArc 是否大圆弧
* @param {number} sweep 是否延伸圆弧
* @param {Object} p0 分割点1
* @param {Object} p1 分割点2
* @return {Array} 分割后的路径
*/
function getArc(rx, ry, angle, largeArc, sweep, p0, p1) {
var result = a2c(p0.x, p0.y, p1.x, p1.y, largeArc, sweep, rx, ry, angle);
var path = [];
if (result.length) {
path.push({
x: result[0][0],
y: result[0][1],
onCurve: true
});
// 将三次曲线转换成二次曲线
result.forEach(function (c) {
var q2Array = (0, _bezierCubic2Q.default)({
x: c[0],
y: c[1]
}, {
x: c[2],
y: c[3]
}, {
x: c[4],
y: c[5]
}, {
x: c[6],
y: c[7]
});
q2Array[0][2].onCurve = true;
path.push(q2Array[0][1]);
path.push(q2Array[0][2]);
if (q2Array[1]) {
q2Array[1][2].onCurve = true;
path.push(q2Array[1][1]);
path.push(q2Array[1][2]);
}
});
}
return path;
}