计算SVG弧的中心

Jam*_*mes 9 javascript math svg trigonometry

我有以下信息:

  • radiusX(rx)
  • radiusY(ry)
  • X1
  • Y1
  • X2
  • Y2

SVG规范允许您通过指定弧的半径,起点和终点来定义弧.还有其他选项,例如large-arc-flagsweep-flag这有助于确定要如何起点到达终点.更多细节在这里.

我不是数学倾向,所以理解这一切几乎是不可能的.

我想我正在寻找一个简单的等式,让我知道SVG的arc命令接受的所有参数的值centerXcenterY值.

任何帮助表示赞赏.

我搜索stackoverflow并没有任何答案似乎用简单的英语解释解决方案.

cui*_*ing 10

来自 W3C SVG 1.1 规范:从端点到中心参数化的转换

你可以看看详细的解释。

这是一个 javascript 实现。

// svg : [A | a] (rx ry x-axis-rotation large-arc-flag sweep-flag x y)+

function  radian( ux, uy, vx, vy ) {
    var  dot = ux * vx + uy * vy;
    var  mod = Math.sqrt( ( ux * ux + uy * uy ) * ( vx * vx + vy * vy ) );
    var  rad = Math.acos( dot / mod );
    if( ux * vy - uy * vx < 0.0 ) {
        rad = -rad;
    }
    return rad;
}
//conversion_from_endpoint_to_center_parameterization
//sample :  svgArcToCenterParam(200,200,50,50,0,1,1,300,200)
// x1 y1 rx ry ? fA fS x2 y2
function svgArcToCenterParam(x1, y1, rx, ry, phi, fA, fS, x2, y2) {
    var cx, cy, startAngle, deltaAngle, endAngle;
    var PIx2 = Math.PI * 2.0;

    if (rx < 0) {
        rx = -rx;
    }
    if (ry < 0) {
        ry = -ry;
    }
    if (rx == 0.0 || ry == 0.0) { // invalid arguments
        throw Error('rx and ry can not be 0');
    }

    var s_phi = Math.sin(phi);
    var c_phi = Math.cos(phi);
    var hd_x = (x1 - x2) / 2.0; // half diff of x
    var hd_y = (y1 - y2) / 2.0; // half diff of y
    var hs_x = (x1 + x2) / 2.0; // half sum of x
    var hs_y = (y1 + y2) / 2.0; // half sum of y

    // F6.5.1
    var x1_ = c_phi * hd_x + s_phi * hd_y;
    var y1_ = c_phi * hd_y - s_phi * hd_x;

    // F.6.6 Correction of out-of-range radii
    //   Step 3: Ensure radii are large enough
    var lambda = (x1_ * x1_) / (rx * rx) + (y1_ * y1_) / (ry * ry);
    if (lambda > 1) {
        rx = rx * Math.sqrt(lambda);
        ry = ry * Math.sqrt(lambda);
    }

    var rxry = rx * ry;
    var rxy1_ = rx * y1_;
    var ryx1_ = ry * x1_;
    var sum_of_sq = rxy1_ * rxy1_ + ryx1_ * ryx1_; // sum of square
    if (!sum_of_sq) {
        throw Error('start point can not be same as end point');
    }
    var coe = Math.sqrt(Math.abs((rxry * rxry - sum_of_sq) / sum_of_sq));
    if (fA == fS) { coe = -coe; }

    // F6.5.2
    var cx_ = coe * rxy1_ / ry;
    var cy_ = -coe * ryx1_ / rx;

    // F6.5.3
    cx = c_phi * cx_ - s_phi * cy_ + hs_x;
    cy = s_phi * cx_ + c_phi * cy_ + hs_y;

    var xcr1 = (x1_ - cx_) / rx;
    var xcr2 = (x1_ + cx_) / rx;
    var ycr1 = (y1_ - cy_) / ry;
    var ycr2 = (y1_ + cy_) / ry;

    // F6.5.5
    startAngle = radian(1.0, 0.0, xcr1, ycr1);

    // F6.5.6
    deltaAngle = radian(xcr1, ycr1, -xcr2, -ycr2);
    while (deltaAngle > PIx2) { deltaAngle -= PIx2; }
    while (deltaAngle < 0.0) { deltaAngle += PIx2; }
    if (fS == false || fS == 0) { deltaAngle -= PIx2; }
    endAngle = startAngle + deltaAngle;
    while (endAngle > PIx2) { endAngle -= PIx2; }
    while (endAngle < 0.0) { endAngle += PIx2; }

    var outputObj = { /* cx, cy, startAngle, deltaAngle */
        cx: cx,
        cy: cy,
        startAngle: startAngle,
        deltaAngle: deltaAngle,
        endAngle: endAngle,
        clockwise: (fS == true || fS == 1)
    }

    return outputObj;
}
Run Code Online (Sandbox Code Playgroud)

用法示例:

svg

<path d="M 0 100 A 60 60 0 0 0 100 0"/>
Run Code Online (Sandbox Code Playgroud)

js

var result = svgArcToCenterParam(0, 100, 60, 60, 0, 0, 0, 100, 0);
console.log(result);
/* will output:
{
    cx: 49.99999938964844,
    cy: 49.99999938964844,
    startAngle: 2.356194477985314,
    deltaAngle: -3.141592627780225,
    endAngle: 5.497787157384675,
    clockwise: false
}
*/
Run Code Online (Sandbox Code Playgroud)

  • 这是引用的 SVG 附录的一个很好的实现。不幸的是,它并不适用于所有情况。有一些(并不少见)边缘情况,其中计算 `coe` 时 sqrt 内的值为负。请参阅 http://svn.apache.org/repos/asf/xmlgraphics/batik/branches/svg11/sources/org/apache/batik/ext/awt/geom/ExtendedGeneralPath.java 中的 `computeArc`。请注意,如果值为负,则此实现将中心点设为 (0,0),这似乎对我有用。一个例子是 rx=ry=60 从 (0, 100) 到 (100, 0) 的弧。 (2认同)

MBo*_*MBo 2

我正在考虑 x 轴旋转 = 0 的情况。起点和终点的方程:

x1 = cx + rx * cos(起始角度)

y1 = cy + ry * sin(起始角度)

x2 = cx + rx * cos(结束角度)

y2 = cy + ry * sin(结束角度)

从方程对中排除角度可以得出:

ry^2*(x1-cx)^2+rx^2*(y1-cy)^2=rx^2*ry^2

ry^2*(x2-cx)^2+rx^2*(y2-cy)^2=rx^2*ry^2

该方程组可以通过手动或数学包(Maple、Mathematica 等)的帮助来解析求解 (cx, cy)。二次方程有两个解(由于大弧旗和扫旗组合)。