将 svg 圆弧转换为直线

Nat*_*han 3 geometry svg

我正在尝试将 SVG 弧转换为一系列线段。背景是,我想使用 (reportlab)[ http://www.reportlab.com/]绘制弧线。

svg 给了我这些参数(根据这里)。

rx,ry,x 轴旋转,大弧标志,扫描标志,dx,dy

现在我需要确定这条弧线后面的线。但我不明白如何将其转换为更有用的几何形状。

如何确定椭圆弧的中心及其旋转?

Spe*_*tre 6

SVG椭圆弧确实很棘手,我花了一些时间来实现它(即使遵循SVG规范)。我最终在C++中得到了这样的结果:

//---------------------------------------------------------------------------
class svg_usek  // virtual class for svg_line types
    {
public:
    int pat;                // svg::pat[] index
    virtual void reset(){};
    virtual double getl (double mx,double my){ return 1.0; };
    virtual double getdt(double dl,double mx,double my){ return 0.1; };
    virtual void getpnt(double &x,double &y,double t){};
    virtual void compute(){};
    virtual void getcfg(AnsiString &nam,AnsiString &dtp,AnsiString &val){};
    virtual void setcfg(AnsiString &nam,AnsiString &dtp,AnsiString &val,int &an,int &ad,int &av){};
    };
//---------------------------------------------------------------------------
class svg_ela:public svg_usek       // sweep = 0 arc goes from line p0->p1 CW
    {                               // sweep = 1 arc goes from line p0->p1 CCW
public:                             // larc is unused if |da|=PI
    double x0,y0,x1,y1,a,b,alfa; int sweep,larc;
    double sx,sy,a0,a1,da,ang;      // sx,sy rotated center by ang
    double cx,cy;                   // real center
    void reset() { x0=0; y0=0; x1=0; y1=0; a=0; b=0; alfa=0; sweep=false; larc=false; compute(); }
    double getl (double mx,double my);
//  double getdt(double dl,double mx,double my);
    double getdt(double dl,double mx,double my) { int n; double dt; dt=divide(dl,getl(mx,my)); n=floor(divide(1.0,dt)); if (n<1) n=1; return divide(1.0,n); }
    void getpnt(double &x,double &y,double t);
    void compute();
    void getcfg(AnsiString &nam,AnsiString &dtp,AnsiString &val);
    void setcfg(AnsiString &nam,AnsiString &dtp,AnsiString &val,int &an,int &ad,int &av);
    svg_ela()       {}
    svg_ela(svg_ela& a) { *this=a; }
    ~svg_ela()  {}
    svg_ela* operator = (const svg_ela *a) { *this=*a; return this; }
    //svg_ela* operator = (const svg_ela &a) { ...copy... return this; }
    };
//---------------------------------------------------------------------------
void svg_ela::getpnt(double &x,double &y,double t)
    {
    double c,s,xx,yy;
    t=a0+(da*t);
    xx=sx+a*cos(t);
    yy=sy+b*sin(t);
    c=cos(-ang);
    s=sin(-ang);
    x=xx*c-yy*s;
    y=xx*s+yy*c;
    }
//---------------------------------------------------------------------------
void svg_ela::compute()
    {
    double  ax,ay,bx,by;            // body
    double  vx,vy,l,db;
    int     _sweep;
    double  c,s,e;

    ang=pi-alfa;
    _sweep=sweep;
    if (larc) _sweep=!_sweep;

    e=divide(a,b);
    c=cos(ang);
    s=sin(ang);
    ax=x0*c-y0*s;
    ay=x0*s+y0*c;
    bx=x1*c-y1*s;
    by=x1*s+y1*c;

    ay*=e;                  // transform to circle
    by*=e;

    sx=0.5*(ax+bx);         // mid point between A,B
    sy=0.5*(ay+by);
    vx=(ay-by);
    vy=(bx-ax);
    l=divide(a*a,(vx*vx)+(vy*vy))-0.25;
    if (l<0) l=0;
    l=sqrt(l);
    vx*=l;
    vy*=l;

    if (_sweep)
        {
        sx+=vx;
        sy+=vy;
        }
    else{
        sx-=vx;
        sy-=vy;
        }

    a0=atanxy(ax-sx,ay-sy);
    a1=atanxy(bx-sx,by-sy);
//  ay=divide(ay,e);
//  by=divide(by,e);
    sy=divide(sy,e);


    da=a1-a0;
    if (fabs(fabs(da)-pi)<=_acc_zero_ang)       // half arc is without larc and sweep is not working instead change a0,a1
        {
        db=(0.5*(a0+a1))-atanxy(bx-ax,by-ay);
        while (db<-pi) db+=pi2;     // db<0 CCW ... sweep=1
        while (db>+pi) db-=pi2;     // db>0  CW ... sweep=0
        _sweep=0;
        if ((db<0.0)&&(!sweep)) _sweep=1;
        if ((db>0.0)&&( sweep)) _sweep=1;
        if (_sweep)
            {
//          a=0; b=0;
            if (da>=0.0) a1-=pi2;
            if (da< 0.0) a0-=pi2;
            }
        }
    else if (larc)              // big arc
        {
        if ((da< pi)&&(da>=0.0)) a1-=pi2;
        if ((da>-pi)&&(da< 0.0)) a0-=pi2;
        }
    else{                       // small arc
        if (da>+pi) a1-=pi2;
        if (da<-pi) a0-=pi2;
        }
    da=a1-a0;

    // realny stred
    c=cos(+ang);
    s=sin(+ang);
    cx=sx*c-sy*s;
    cy=sx*s+sy*c;
    }
//---------------------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)

atanxy(x,y)相同atan2(y,x)。你可以忽略类svg_usek。的用法svg_ela很简单,首先将 SVG 参数提供给它:

  • x0,y0是起点(从前一个<path>元素开始)
  • x1,y1是端点 ( x0+dx,y0+dy)
  • a,b 和你一样rx,ry
  • alfa 旋转角度[rad],所以你需要从度数转换...
  • sweep,larc和你的一样。

然后调用svg_ela::compute();它将计算插值所需的所有变量。初始化完成后,要获取弧上的任何点,只需调用svg_ela::getpnt(x,y,t);wherex,y是返回的坐标,并且t=<0,1>是输入参数。所有其他方法对您来说都不重要。要渲染 ARC,只需执行以下操作:

svg_ela arc; // your initialized arc here
int e; double x,y,t;
arc.getpnt(x,y,0.0);
Canvas->MoveTo(x,y);
for (e=1,t=0.0;e;t+=0.02)
 {
 if (t>=1.0) { t=1.0; e=0; }
 arc.getpnt(x,y,t);
 Canvas->LineTo(x,y);
 }
Run Code Online (Sandbox Code Playgroud)

不要忘记SVG <g><path>可以具有变换矩阵,因此您应该在每次svg_ela::getpnt(x,y,t)调用后应用它们。

如果您对这些东西如何简单地工作感兴趣compute()

  1. 旋转空间,使椭圆半轴轴对齐。

  2. 缩放空间,使椭圆变成圆形。

  3. 计算圆的中心点

    中心位于与线垂直的线上(x0,y0),(x1,y1),并且位于其中点。距离由毕达哥拉斯和方向sweep以及larc组合来计算。

  4. 缩放回椭圆

  5. 向后旋转

现在我们有了真实的中心位置,因此还可以计算相对于它的真实端点角度。现在,对于椭圆上的每个点,通过椭圆的标准参数方程来计算它并旋转到所需的位置就足够了getpnt(x,y,t)

希望它能有所帮助。

这里相关的质量检查:

一些图像解释了 SVG 弧线背后的数学原理(使用与此处相同的变量名称)