2017-01-08 7 views
1

Я пытаюсь преобразовать дугу SVG в ряд сегментов линии. Фоном является то, что я хочу нарисовать дугу, используя (reportlab) [http://www.reportlab.com/].Преобразование svg arc в строки

svg дает мне эти параметры (по сравнению с here).

ге, гу, х-ось вращение, большая дуга флаг, подметать флаг, дй, д

Теперь нужно определить строки, следующие за дуги. Но я не понимаю, как я могу преобразовать это в нечто геометрически более полезное.

Как определить центр дуги эллипса и его поворот?

+4

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

ответ

2

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

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);, где 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<g> и <path> может иметь преобразование матрицы, поэтому вы должны применять их после каждого svg_ela::getpnt(x,y,t) вызова.

Если вас интересует, как материал работает compute() просто:

  1. вращает пространство так Эллипс полуприцепы осях выровнены по оси.
  2. шкала пространство поэтому эллипс становится круг.
  3. вычислительный центр точка окружности

    центр лежит на линии, перпендикулярной к линии (x0,y0),(x1,y1), а также лежит на его середине.Расстояние вычисляется Pytagoras и направление от sweep и larc.

  4. масштаб обратно к эллипсу

  5. повернуть назад

Теперь мы имеем реальное положение центра, так и вычислить реальный конечной углы по отношению к нему. Теперь для каждой точки на эллипсе достаточно вычислить ее стандартным параметрическим уравнением эллипса и повернуть в нужное положение, что и делает getpnt(x,y,t).

Надеюсь, это поможет.

+0

@ Натан рад помочь ... вы не поверили бы, сколько времени потребовалось, чтобы закодировать мой декодер/кодировщик SVG до тех пор, пока Я получил это правильно для моих целей ... но это было много лет назад ... нет доступа к интернету и т. Д. ... только спецификации pdf и многие образцы (и не было 100% рабочего зрителя для справки в то время). btw просто пятнистый нетранслируемый комментарий ... 'body' означает' points' – Spektre

+0

О, я считаю, что это было много работы! Спасибо, что теперь я могу извлечь выгоду из вашей работы. – Nathan