2017-01-08 10 views
1

SVG弧を一連の線分に変換しようとしています。背景は、(reportlab)[http://www.reportlab.com/]]を使って円弧を描きたいということです。SVG弧を線に変換する

svgは私にこれらのパラメータを与えます(hereに従います)。

RX、RY、X軸回り、大円弧フラグ、スイープフラグ、DX、DY

今はこの円弧次の行を決定する必要があります。しかし、私はこれをどのように幾何学的により使いやすくすることができるのか分かりません。

どのように楕円弧の中心とその回転を決定できますか?

+4

https://www.w3.org/TR/SVG/implnote.html#ArcConversionCenterToEndpoint –

答えて

2

SVG楕円弧は本当にトリッキーであり、(でもSVGスペック以下)、それを実装するために私にしばらく時間がかかりました。

//--------------------------------------------------------------------------- 
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)と同じである:私はC++でこのようなものになってしまいました。クラスsvg_usekは無視できます。 svg_elaの使用がそれに単純な一供給SVGパラメータである:

  • x0,y0
  • x1,y1がエンドポイントである(前<path>素子からの)開始点である(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; // 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_ela::getpnt(x,y,t)呼び出しの後にそれらを適用する必要がありますので SVG <g><path>変換行列ができていることを忘れないでください。

ものは単にcompute()をどのように機能するか、あなたが興味を持っている場合:楕円半車軸が軸が揃っているので、

  1. はスペースを回転させます。
  2. 楕円が円になるように尺度を変えます。円

    中心ため

  3. 計算中心点は(x0,y0),(x1,y1)を線に垂直であり、また、その中間に位置するライン上に位置します。距離はPytagorasとsweeplarcの組み合わせから計算されます。

  4. を楕円形にする

  5. 規模バックは、今、私たちはそうも本当のエンドポイントはそれに対する相対角度を計算実中心位置を持っているバック

を回転させます。今度は楕円上の各点について、楕円の標準パラメトリック方程式で計算して、getpnt(x,y,t)が望む位置に回転すれば十分です。

少し助けてくれるといいですね。

+0

@Nathanは助けになることをうれしく思っています。私のSVGデコーダ/エンコーダをエンコードするまでには私は私の目的のためにそれを得た...しかしそれは数年前だった...インターネット等へのアクセスがない...ちょうどspecs pdfおよび多くのサンプル(およびその時の参照のための100%働く視聴者)。 btwはちょうど翻訳されていないコメントを見つけました... 'body'は' points'を意味します – Spektre

+0

ああ、私はそれがたくさんの仕事だったと信じています!私は今あなたの仕事から利益を得ることができて、ありがとう。 – Nathan