为贝塞尔曲线中的每个点绘制切线
Posted
技术标签:
【中文标题】为贝塞尔曲线中的每个点绘制切线【英文标题】:Drawing tangent lines for each point in bezier curve 【发布时间】:2013-10-26 09:46:02 【问题描述】:我设法绘制了一条贝塞尔曲线,例如:
glColor3f(0,1,0);
glBegin(GL_LINE_STRIP);
for (int i = 3; i < nPt; i+=3)
glColor3f(0,0,0);
for (float k = 0; k < NLINESEGMENT+1; k++)
float x = pow(1.0-k/NLINESEGMENT,3)*ptList[i-3].x +
3*(k/NLINESEGMENT)*pow(1.0-k/NLINESEGMENT, 2) * ptList[i-2].x +
3*(1.0-k/NLINESEGMENT)*pow(k/NLINESEGMENT, 2) * ptList[i-1].x +
pow(k/NLINESEGMENT, 3)*ptList[i].x;
float y = pow(1.0-k/NLINESEGMENT,3)*ptList[i-3].y +
3*(k/NLINESEGMENT)*pow(1.0-k/NLINESEGMENT, 2) * ptList[i-2].y +
3*(1.0-k/NLINESEGMENT)*pow(k/NLINESEGMENT, 2) * ptList[i-1].y +
pow(k/NLINESEGMENT, 3)*ptList[i].y;
glVertex2d(x,y);
glEnd();
现在我想为每个点添加切线箭头,我该怎么做?我得到了一个绘制箭头的函数。所以我相信我只需要旋转参考框架并画出那个箭头。但是我如何计算旋转?我想我需要微分方程,但问题仍然存在,我该如何使用它?
更新
每放置第 4 个点,就会绘制一条曲线。
我应该达到以下目标
Full Source
更新 2
好的,我尝试绘制切线,例如:
glColor3f(0,1,0);
for (int i = 3; i < nPt; i+=3)
for (int n = 0; n < NOBJECTONCURVE; n++)
float t = (float)n/NOBJECTONCURVE;
float x0 = points[i-3].x,
x1 = points[i-2].x,
x2 = points[i-1].x,
x3 = points[i].x;
float y0 = points[i-3].y,
y1 = points[i-2].y,
y2 = points[i-1].y,
y3 = points[i].y;
float x = pow(1.0-t, 3) * points[i-3].x +
3 * t * pow(1.0 - t, 2) * points[i-2].x +
3 * (1.0 - t) * pow(t, 2) * points[i-1].x +
pow(t, 3)*points[i].x;
float y = pow(1.0-t, 3) * points[i-3].y +
3 * t * pow(1.0 - t, 2) * points[i-2].y +
3 * (1.0 - t) * pow(t, 2) * points[i-1].y +
pow(t, 3)*points[i].y;
float dx = -3*(1-t)*x0 + 3*x1*((2*t)*(t-1)+pow((1-t),2)) + 3*x2*(2*t*(1-t)-pow(t,2)) + 3*pow(t,2)*x3;
float dy = -3*(1-t)*y0 + 3*y1*((2*t)*(t-1)+pow((1-t),2)) + 3*y2*(2*t*(1-t)-pow(t,2)) + 3*pow(t,2)*y3;
float angle = atan(dy/dx);
glPushMatrix();
glTranslatef(x, y, 0);
glRotatef(angle * 180 / 3.14159265, 0, 0, 1);
drawRightArrow();
glPopMatrix();
但正如您所见,切线似乎不正确,尤其是在贝塞尔曲线的中间?
【问题讨论】:
IMO在这类问题中截图你做了什么,可以让问题变得更好。 @JiewMeng Inx0, y0
切线角为ang=arctan(dy/dx)
。箭头的坐标(箭头长度l
)然后:(x0,y0) - (l * cos(ang), l * sin(ang))
。
@PetrBudnik:如果你之后再次将角度转换为矢量,为什么还要计算角度?
@NicoSchertler 因为角度与dx
和dy
无关,它们通常因点而异。
【参考方案1】:
因为我们不想打断线带,所以再做一个绘制箭头的循环是合理的。在这个循环中,我们可以跳过一些步骤,因为我们可能不希望每一步后面都有箭头。箭头可以绘制为“GL_LINES”原语。
为了便于阅读,我建议将内部循环中的参数t
定义为
float t = (float)k/NLINESEGMENT;
现在我们需要计算此时曲线相对于t
的导数。可以为每个坐标独立计算该导数。看起来这个问题是一个家庭作业,所以我把它留给你
float dx = ... //derivative of x-component with respect to t
float dy = ... //derivative of y-component with respect to t
我们还需要曲线点。理想情况下,您已将其保存在上一个循环中的数组或类似结构中。
所以我们可以画出箭头的基线(s
是自定义比例因子):
glVertex2d(x, y);
glVertex2d(x + s * dx, y + s * dy);
我们还需要实际的箭头。与箭头方向正交的向量是(-dy, dx)
。所以我们可以把方向和正交方向结合起来得到箭头:
glVertex2d(x + s * dx, y + s * dy);
glVertex2d(x + 0.9 * s * dx - 0.1 * dy, y + 0.9 * s * dy + 0.1 * dx);
glVertex2d(x + s * dx, y + s * dy);
glVertex2d(x + 0.9 * s * dx + 0.1 * dy, y + 0.9 * s * dy - 0.1 * dx);
这将产生一个 90° 箭头。您可以通过调整系数(0.9 和 0.1)来改变角度。
更新
您的导数更接近实际解决方案,但仍包含一些错误:
此外,在计算角度时,使用atan2
函数。这允许您获得大于PI/2
的角度:
float angle = atan2(dy,dx);
【讨论】:
dx
和 dy
,通常因点而异。在这种情况下,您的箭头将有不同的长度。除非你在 sqrt(dx * dx + dy * dy)
上标准化。
我打算让箭头的长度与导数的长度成正比。但这当然取决于任务。
这不会使它们与“导数长度”成比例。导数是dy/dx
(实际上是lim dy/dx with dx->0
)。对于dx = 1, dy = 0.5
和dx = 10000, dy = 5000
,它的值相同,等于0.5
。但是在这种情况下,您的箭头长度会相差 10000 倍。这就是为什么您必须以一种或另一种方式使用角度(如果您愿意,可以使用dy/dx = tan(ang)
)。
我说的是x
和y
相对于t
(dx/dt
和dy/dt
)的衍生物。 y
相对于x
的导数有点难以计算,并且可能包含不连续性(对于垂直线部分)。因此,使用它来显示切向量并不是一个好主意。此外,y
通常不能描述为x
的函数。对于给定的x
,可能有超过 1 个y
-value。
恕我直言,您不需要t
。您有一个代表函数的 离散 2D 点数组。您需要以数字方式在每个点中找到导数dy/dx
以获得切线。同样,该函数在分析上不可微分-它是一组离散点...对于dx = 0
,我们有无限导数,所以我们只画一条垂直线...此外,一个函数有多个@987654356 @ 对于给定的 x
不会使其不可微分。想想圆的方程。 除了,具有讽刺意味的是,对于具有单个 y
值的两个点,衍生产品无处不在。以上是关于为贝塞尔曲线中的每个点绘制切线的主要内容,如果未能解决你的问题,请参考以下文章