将svg弧转换为线 [英] Converting an svg arc to lines
问题描述
我试图将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>
元素) -
a,b
是您的rx,ry
-
alfa
旋转角度[rad]
因此您需要从度数转换... -
sweep,larc
是您的。
x1,y1
是终点( x0 + dx,y0 + dy
) 然后调用 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()
简单地说:
- 旋转空间,以便椭圆半轴对齐。
- 缩放空间,以便椭圆变为圆形。
-
计算圆的中心点
center lies on与
(x0,y0),(x1,y1)
垂直的线,也位于其中点。距离由Pytagoras和sweep
和larc
组合的方向计算。 -
$ b $ b缩小到椭圆形
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 asatan2(y,x)
. You can ignore classsvg_usek
. Usage ofsvg_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 yoursrx,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 callsvg_ela::getpnt(x,y,t);
wherex,y
is the returned coordinate andt=<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 eachsvg_ela::getpnt(x,y,t)
call.If you are interested how the stuff works
compute()
simply:- rotates the space so the ellipse semi-axises are axis aligned.
- scale the space so ellipse becomes circle.
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 fromsweep
andlarc
combination.scale back to ellipse
- 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屋!
$ b