我正在研究一个涉及贝塞尔曲线的项目,我很难找到一个值t,它在[0,1]范围内,对应于贝塞尔曲线上的特定位置。我设置了一个测试,其中我将增量值 t (特别是以0.001到1的增量)并将它们放在贝塞尔曲线的x和y参数方程中,然后我用它减去该值。真值,如果它在 x 和 y 的小阈值范围内,我找到了一个合适的 t 。不幸的是, t 似乎没有一个与所需坐标相对应的值。这意味着循环实际上完成而没有成功完成for循环中的条件。
应该在曲线上的坐标是(75,-2.26384401)。我把我的代码放在下面,显示控制点的坐标和真正的 x 和 y 坐标。任何帮助将不胜感激。谢谢!
int _tmain(int argc, _TCHAR* argv[])
{
float P0[2] = { 55, -11.105 };
float P1[2] = { 72.569, -11.105 };
float P2[2] = { 50.217, 1.396 };
float P3[2] = { 100, 1.396 };
float t = 1.0F;
float int_t = t / 1000; // intervals
float x;
float y;
float tx = 75.0000000F; // where it should be in x
float ty = -2.26384401F; // where it should be in y
float epsilon = 0.01;
float final_t;
for (float i = 0; i < t; i += int_t)
{
final_t = i;
x = powf(1.0F - i, 3.0F)*P0[0] + 3.0F*powf(1.0F - i, 2.0F)*i*P1[0] +
3.0F*(1.0F - i)*powf(i, 2.0F)*P2[0] + powf(i, 3.0F)*P3[0]; // x(t)
y = powf(1.0F - i, 3.0F)*P0[1] + 3.0F*powf(1.0F - i, 2.0F)*i*P1[1] +
3.0F*(1.0F - i)*powf(i, 2.0F)*P2[1] + powf(i, 3.0F)*P3[1]; // y(t)
// for my testing
cout << "---------------------------------" << endl;
cout << "i = " << i << endl;
cout << "x = " << x << endl;
cout << "y = " << y << endl;
cout << "---------------------------------" << endl;
if ((fabsf(x - tx) <= epsilon) && (fabsf(y - ty) <= epsilon))
break;
}
cout << endl;
cout << "x = " << x << endl;
cout << "y = " << y << endl;
cout << "t = " << final_t << endl;
return 0;
}
答案 0 :(得分:1)
您的问题是输入点Pt(tx,ty)
根本不在 BEZIER (P0,P1,P2,P3)
曲线上。所以曲线的距离总是大得多然后是你的小epsilon,无论你找到t
的匹配程度如何......
这是它的样子:
BEZIER 曲线为灰色。绿色X
是输入(tx,ty)
点,红色+
位于曲线t=0.753
上的最近点
这里的 C ++ / VCL 代码我这样做:
//---------------------------------------------------------------------------
#include <math.h>
#include "approx.h"
double P0[2] = { 55, -11.105 };
double P1[2] = { 72.569, -11.105 };
double P2[2] = { 50.217, 1.396 };
double P3[2] = { 100, 1.396 };
double Pt[2] = { 75.0000000,-2.26384401 };
//---------------------------------------------------------------------------
void BEZIER_getxy(double *p0,double *p1,double *p2,double *p3,double &x,double &y,double t) // return x,y of point on BEZIER curve for t
{
double a0,a1,a2,a3,tt,ttt;
tt=t*t; ttt=tt*t;
a0= ( p0[0]);
a1= (3.0*p1[0])-(3.0*p0[0]);
a2= (3.0*p2[0])-(6.0*p1[0])+(3.0*p0[0]);
a3=( p3[0])-(3.0*p2[0])+(3.0*p1[0])-( p0[0]);
x=a0+(a1*t)+(a2*tt)+(a3*ttt);
a0= ( p0[1]);
a1= (3.0*p1[1])-(3.0*p0[1]);
a2= (3.0*p2[1])-(6.0*p1[1])+(3.0*p0[1]);
a3=( p3[1])-(3.0*p2[1])+(3.0*p1[1])-( p0[1]);
y=a0+(a1*t)+(a2*tt)+(a3*ttt);
}
//---------------------------------------------------------------------------
void BEZIER_gett (double *p0,double *p1,double *p2,double *p3,double x,double y,double &t) // return t which is closest to (x,y)
{
double e,xx,yy;
approx at;
for (at.init(0.0,1.0,0.1,3,&e);!at.done;at.step()) // search t
{
BEZIER_getxy(p0,p1,p2,p3,xx,yy,at.a);
xx-=x; xx*=xx;
yy-=y; yy*=yy;
e=xx+yy; // error is distance between points ^ 2
}
t=at.aa;
}
//---------------------------------------------------------------------------
void BEZIER_draw (double *p0,double *p1,double *p2,double *p3,TCanvas *can,double x0,double y0,double zoom) // just render curve with VCL/GDI
{
int e;
double x,y,t;
BEZIER_getxy(p0,p1,p2,p3,x,y,0.0);
x=x0+(x*zoom);
y=y0-(y*zoom);
can->MoveTo(x,y);
for (e=1,t=0.0;e;t+=0.02)
{
if (t>=1.0) { e=0; t=1.0; }
BEZIER_getxy(p0,p1,p2,p3,x,y,t);
x=x0+(x*zoom);
y=y0-(y*zoom);
can->LineTo(x,y);
}
}
//---------------------------------------------------------------------------
void TMain::draw() // this is just my window redraw routine
{
if (!_redraw) return;
// clear buffer
bmp->Canvas->Brush->Color=clBlack;
bmp->Canvas->FillRect(TRect(0,0,xs,ys));
double x0=-40.0,y0=170.0,zoom=3.0; // view
double x,y,w=10,t;
// whole BEZIER curve (Gray curve)
bmp->Canvas->Pen->Color=clDkGray;
BEZIER_draw(P0,P1,P2,P3,bmp->Canvas,x0,y0,zoom);
// input point Pt (Green X)
bmp->Canvas->Pen->Color=0x0000FF00;
x=x0+(Pt[0]*zoom);
y=y0-(Pt[1]*zoom);
bmp->Canvas->MoveTo(x-w,y-w);
bmp->Canvas->LineTo(x+w,y+w);
bmp->Canvas->MoveTo(x+w,y-w);
bmp->Canvas->LineTo(x-w,y+w);
// closest point (Red +)
bmp->Canvas->Pen->Color=clRed;
BEZIER_gett (P0,P1,P2,P3,Pt[0],Pt[1],t);
BEZIER_getxy(P0,P1,P2,P3,x,y,t);
x=x0+(x*zoom);
y=y0-(y*zoom);
bmp->Canvas->MoveTo(x-w,y);
bmp->Canvas->LineTo(x+w,y);
bmp->Canvas->MoveTo(x,y-w);
bmp->Canvas->LineTo(x,y+w);
Caption=t;
// render backbuffer
Main->Canvas->Draw(0,0,bmp);
_redraw=false;
}
//---------------------------------------------------------------------------
由于我懒得调试你的代码,我用于 BEZIER 三次解算器我的approx
这个相关的 QA :
在搜索本身中,我使用步骤t=<0.0,1.0>
和0.1
递归设置3
的搜索参数,导致0.1/10^(3-1)
最终精度t
,即0.001
1}}作为int_t
步骤,但解决方案位于30
步骤而非您1000
为了补救您的代码,请记住x,y,t
与tx,ty
的最小距离,而不仅仅是distance<=epsilon
答案 1 :(得分:0)
简单的方法是使用三次解算器。
现在求解x并求解y。 如果该点真的在曲线上,您将获得相同的t值。可能它有点偏离,所以替换ts,然后选择最接近的。
系数
DX = m_P0.x;
CX = 3.0f * ( m_P1.x - m_P0.x );
BX = ( 3.0f * m_P2.x ) - ( 6.0f * m_P1.x ) + ( 3.0f * m_P0.x );
AX = m_P3.x - ( 3.0f * m_P2.x ) + ( 3.0f * m_P1.x ) - m_P0.x;
为y做同样的事。
现在解决立方AXt ^ 3 + BXt ^ 2 + CXt + DX - px = 0并找到三个 根源为t。 对y执行相同操作,并找到t的三个根。
现在我们有6个根。有些可能很复杂,所以请忽略。因此可能在0 - epsilon,1 + epsilon(允许一点倾斜)之外,所以忽略。如果点px,py正好在曲线上,则t值中的两个将在0-1上相同,这就是你的答案。 实际上,这一点有点过时了。因此,将所有(最多6个)t值替换回贝塞尔曲线并获得x,y。至少一个x,y对应该非常接近px,py,除了可能非常接近尖点的点。
如果你有两个关闭点并且它们之间的间隔是px py,你可以进一步细化你的答案。