Calculate center of SVG arc

后端 未结 2 1710
伪装坚强ぢ
伪装坚强ぢ 2021-02-05 09:39

I have the following information:

  • radiusX (rx)
  • radiusY (ry)
  • x1
  • y1
  • x2
  • y2

The SVG spec allows you to defi

相关标签:
2条回答
  • 2021-02-05 10:09

    I'm considering the case of x-axis-rotation = 0. Equations for start and end points:

    x1 = cx + rx * cos(StartAngle)

    y1 = cy + ry * sin(StartAngle)

    x2 = cx + rx * cos(EndAngle)

    y2 = cy + ry * sin(EndAngle)

    Excluding angles from equation pairs gives us:

    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

    This equation system can be analytically solved for (cx, cy) by hands or with help of math packets (Maple, Mathematica etc). There are two solutions of quadratic equation (due to large-arc-flag and sweep-flag combination).

    0 讨论(0)
  • 2021-02-05 10:17

    From W3C SVG 1.1 spec: Conversion from endpoint to center parameterization

    You can take a look at the detailed explanation.

    This is a javascript implementation.

    // 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;
    }
    

    Usage example:

    svg

    <path d="M 0 100 A 60 60 0 0 0 100 0"/>
    

    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
    }
    */
    
    0 讨论(0)
提交回复
热议问题