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

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

问题描述

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



svg给了我这些参数(依据 here )。

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

现在我需要确定这些弧线之后的线。但我不明白如何将其转换为几何更可用。



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

$ SVG 椭圆弧真的很棘手,我花了一段时间来实现它(甚至在 SVG / strong>规格)。我在 C ++ 中得到了类似的结果:
$ b

// -------------------------------------------------- -------------------------
class svg_usek // svg_line类型的虚拟类
{
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 from line p0 - > p1 CW
{//扫描= 1弧从行p0-> p1 CCW
public:// larc如果| da | = PI
double x0,y0 ,X1,Y1,A,b,阿尔法; int sweep,larc;
double sx,sy,a0,a1,da,ang; // sx,sy通过ang
double cx,cy旋转中心; //真正的中心
void reset(){x0 = 0; Y0 = 0; X1 = 0; Y1 = 0;一个= 0; B = 0;阿尔法= 0;扫= FALSE; LARC = FALSE;计算(); }
double getl(double mx,double my);
// double getdt(double dl,double mx,double my);
double getdt(double dl,double mx,double my){int n;双dt; DT =除法(DL,getl(MX,MY)); N =地板(除(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;返回这个; }
// svg_ela * operator =(const svg_ela& a){... copy ... return this; }
};
// -------------------------------------------- -------------------------------
void svg_ela :: getpnt(double& x,double& y ,双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()
{
双斧,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 =除(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; //通过* = e转换成
;

sx = 0.5 *(ax + bx); // 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)//半弧不带larc且扫描不起作用改为a0,a1
{
db =(0.5 *(A0 + A1)) - atanxy(BX-AX,由-AY);
while(db <-pi)db + = pi2; // db <0 CCW ... sweep = 1
while(db> + pi)db- = pi2; // db> 0 CW ... sweep = 0
_sweep = 0; ((db <0.0)&&(!sweep))_sweep = 1;
。 ((db> 0.0)&&(sweep))_sweep = 1;

if(_sweep)
{
// a = 0; B = 0;
if(da> = 0.0)a1- = pi2;
if(da <0.0)a0- = pi2; ($ da

else if(larc)//大弧
{
if((da da)&(da> = 0.0)) A1- = PI2;如果((da> -pi)&(da< 0.0))a0- = pi2,则
;
}
else {//小圆弧
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;
}
// --------------------------------------- ------------------------------------
$ b $ atanxy(x,y) atan2(y,x )。你可以忽略class svg_usek 。使用 svg_ela 很简单,首先将SVG参数提供给它:


  • <$ code> 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); 其中 x,y 是返回的坐标, t = <0,1> 是输入参数。所有其他方法对你来说并不重要。要呈现您的ARC,请执行以下操作:

  svg_ela arc; //你的初始化弧在这里
int e; double x,y,t;
arc.getpnt(x,y,0.0);
Canvas-> MoveTo(x,y);如果(t> = 1.0)(t = 1.0; t = 1.0),则
为(e = 1,t = 0.0; e; t + = 0.02) E = 0; }
arc.getpnt(x,y,t);
Canvas-> LineTo(x,y);
}

不要忘记 SVG < g> <路径> 可以有变换矩阵,所以您应该在每个 svg_ela之后应用它们: :getpnt(x,y,t) call。



如果你感兴趣的东西如何工作 compute() 简单地说:


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

  2. 缩放空间,以便椭圆变为圆形。

  3. 计算圆的中心点

    center lies on与(x0,y0),(x1,y1)垂直的线,也位于其中点。距离由Pytagoras和 sweep larc 组合的方向计算。



  4. $ b

  5. $ b $ b
  6. 缩小到椭圆形

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

    希望它有一点帮助。


    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/].

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

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

    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 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.

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

    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.

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

    1. rotates the space so the ellipse semi-axises are axis aligned.
    2. scale the space so ellipse becomes circle.
    3. compute center point for circle

      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.

    4. scale back to ellipse

    5. rotate back

    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.

    Hope it helps a bit.

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

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