将 svg 弧线转换为线 [英] Converting an svg arc to lines

查看:17
本文介绍了将 svg 弧线转换为线的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to convert an SVG arc to a series of line segments. The background is, that I want to draw an arc using (reportlab)[http://www.reportlab.com/].

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

The svg gives me these parameters (accoring to here).

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

rx,ry,x-axis-rotation,large-arc-flag,sweep-flag,dx,dy

现在我需要确定这条弧线之后的线.但我不明白如何将其转换为更实用的几何图形.

Now I need to determine lines following this arcs. But I do not understand how I can convert this to something geometrical more usable.

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

How would I determine the center of the ellipse arc and its rotation?

推荐答案

SVG 椭圆弧真的很棘手,我花了一段时间来实现它(即使遵循 SVG眼镜).我最终在 C++ 中得到了类似的东西:

SVG elliptic arcs are really tricky and took me a while to implement it (even following the SVG specs). I ended up with something like this in 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;
    }
//---------------------------------------------------------------------------

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

The atanxy(x,y) is the same as atan2(y,x). You can ignore class svg_usek. Usage of svg_ela is simple first feed the SVG parameters to it:

  • x0,y0 是起点(从前一个 <path> 元素开始)
  • x1,y1 是端点 (x0+dx,y0+dy)
  • a,b 是你的 rx,ry
  • alfa 旋转角度[rad] 所以你需要从度数转换...
  • sweep,larc 属于你.
  • x0,y0 is start point (from previous <path> element)
  • x1,y1 is endpoint (x0+dx,y0+dy)
  • a,b are as yours rx,ry
  • alfa rotation angle [rad] so you need to convert from degrees...
  • sweep,larc are as yours.

然后调用 svg_ela::compute(); 将计算插值所需的所有变量.完成此初始化后,要从弧中获取任何点,只需调用 svg_ela::getpnt(x,y,t); 其中 x,y 是返回的坐标和t=<0,1> 是输入参数.所有其他方法对您来说都不重要.要渲染你的 ARC,只需这样做:

And then call svg_ela::compute(); that will compute all variables needed for interpolation. When this initialization is done then to obtain any point from the arc just call svg_ela::getpnt(x,y,t); where x,y is the returned coordinate and t=<0,1> is input parameter. All the other methods are not important for you. To render your ARC just do this:

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

不要忘记 SVG <g><path> 可以有变换矩阵,所以你应该在每个 svg_ela::getpnt(x,y,t) 调用.

Do not forget that SVG <g> and <path> can have transform matrices so you should apply them after each svg_ela::getpnt(x,y,t) call.

如果你对这些东西的工作原理感兴趣 compute() 只需:

If you are interested how the stuff works compute() simply:

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

  1. rotates the space so the ellipse semi-axises are axis aligned.

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

scale the space so ellipse becomes circle.

计算圆的中心点

中心位于垂直于线 (x0,y0),(x1,y1) 的线上,并且也位于其中点.距离由 Pytagoras 计算,方向来自 sweeplarc 组合.

center lies on line that is perpendicular to line (x0,y0),(x1,y1) and also lies on its midpoint. The distance is computed by Pytagoras and direction from sweep and larc combination.

缩放回椭圆

向后旋转

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

Now we have real center position so also compute the real endpoint angles relative to it. Now for each point on ellipse it is enough to compute it by standard parametric equation of ellipse and rotate to desired position which is what getpnt(x,y,t) does.

希望对你有所帮助.

这里相关的QA:

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

with some images explaining the math behind SVG arcs (using the same variable names as here)

这篇关于将 svg 弧线转换为线的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆