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()
をどのように機能するか、あなたが興味を持っている場合:楕円半車軸が軸が揃っているので、
- はスペースを回転させます。
- 楕円が円になるように尺度を変えます。円
中心ため
計算中心点は(x0,y0),(x1,y1)
を線に垂直であり、また、その中間に位置するライン上に位置します。距離はPytagorasとsweep
とlarc
の組み合わせから計算されます。
- を楕円形にする
規模バックは、今、私たちはそうも本当のエンドポイントはそれに対する相対角度を計算実中心位置を持っているバック
を回転させます。今度は楕円上の各点について、楕円の標準パラメトリック方程式で計算して、getpnt(x,y,t)
が望む位置に回転すれば十分です。
少し助けてくれるといいですね。
https://www.w3.org/TR/SVG/implnote.html#ArcConversionCenterToEndpoint –