[SVG] 根据SVG椭圆弧路径参数计算中心点坐标、起始角度、结束角度的Javascript函数...
程序员文章站
2022-03-31 12:28:25
...
SVG spec 1.2以及之前的版本标准中,都只有一种绘制椭圆弧的方式,即以起点、终点、长半轴、短半轴、大小弧标记、顺逆时针方向标记、倾角为参数来确定一段弧。这个方法很强大并灵活,可绘制任意的椭圆弧。有时候我们需要计算圆弧的圆心和起始角度、结束角度,虽然标准官方文档给出了计算公式的描述,但是没有给出直接的代码。我根据标准文档以及网上的资料,写了一个Javascript函数来做这件事。
// svg : [A | a] (rx ry x-axis-rotation large-arc-flag sweep-flag x y)+
/* x1 y1 x2 y2 fA fS rx ry φ */
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 : convert(200,200,300,200,1,1,50,50,0,{})
function convert(x1, y1, x2, y2, fA, fS, rx, ry, phi) {
var cx,cy,theta1,delta_theta;
if( rx == 0.0 || ry == 0.0 ) return -1; // invalid arguments
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;
var rxry = rx * ry;
var rxy1_ = rx * y1_;
var ryx1_ = ry * x1_;
var sum_of_sq = rxy1_ * rxy1_ + ryx1_ * ryx1_; // sum of square
var coe = Math.sqrt( ( 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
theta1 = radian( 1.0, 0.0, xcr1, ycr1 );
// F6.5.6
delta_theta = radian( xcr1, ycr1, -xcr2, -ycr2 );
var PIx2 = Math.PI * 2.0;
while( delta_theta > PIx2 ) delta_theta -= PIx2;
while( delta_theta < 0.0 ) delta_theta += PIx2;
if( fS == false ) delta_theta -= PIx2;
var outputObj = { /* cx, cy, theta1, delta_theta */
cx : cx,
cy : cy,
theta1 : theta1,
delta_theta : delta_theta
}
console.dir(outputObj);
return outputObj;
}
SVG spec 1.2以及之前的版本标准中,都只有一种绘制椭圆弧的方式,即以起点、终点、长半轴、短半轴、大小弧标记、顺逆时针方向标记、倾角为参数来确定一段弧。这个方法很强大并灵活,可绘制任意的椭圆弧。有时候我们需要计算圆弧的圆心和起始角度、结束角度,虽然标准官方文档给出了计算公式的描述,但是没有给出直接的代码。我根据标准文档以及网上的资料,写了一个Javascript函数来做这件事。
// svg : [A | a] (rx ry x-axis-rotation large-arc-flag sweep-flag x y)+
/* x1 y1 x2 y2 fA fS rx ry φ */
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 : convert(200,200,300,200,1,1,50,50,0,{})
function convert(x1, y1, x2, y2, fA, fS, rx, ry, phi) {
var cx,cy,theta1,delta_theta;
if( rx == 0.0 || ry == 0.0 ) return -1; // invalid arguments
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;
var rxry = rx * ry;
var rxy1_ = rx * y1_;
var ryx1_ = ry * x1_;
var sum_of_sq = rxy1_ * rxy1_ + ryx1_ * ryx1_; // sum of square
var coe = Math.sqrt( ( 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
theta1 = radian( 1.0, 0.0, xcr1, ycr1 );
// F6.5.6
delta_theta = radian( xcr1, ycr1, -xcr2, -ycr2 );
var PIx2 = Math.PI * 2.0;
while( delta_theta > PIx2 ) delta_theta -= PIx2;
while( delta_theta < 0.0 ) delta_theta += PIx2;
if( fS == false ) delta_theta -= PIx2;
var outputObj = { /* cx, cy, theta1, delta_theta */
cx : cx,
cy : cy,
theta1 : theta1,
delta_theta : delta_theta
}
console.dir(outputObj);
return outputObj;
}