我一直致力于一个项目,我使用Bezier路径绘制我需要的曲线。我项目中的每个基本形状都包含三个端对端排列的三次贝塞尔曲线,以便斜坡与它们相遇的地方相匹配。
我需要解决的基本问题是由三条贝塞尔曲线组成的复合曲线是否与自身相交。在考虑了一段时间后,我发现考虑到曲线的约束,我可以将任务简化为其他东西:
三条贝塞尔曲线路径中的每一条曲线的曲率应相对于其邻接的曲线在曲率方向上相反。换句话说,应该存在一个拐点,其中一个贝塞尔曲线与另一个贝塞尔曲线相邻。如果不是这种情况,我想拒绝生成曲线的参数集并选择不同的集合。
无论如何,我的基本问题是如何检测曲线是否存在相互邻接的拐点。
在图示中,使用不同的颜色显示三条贝塞尔曲线中的每一条。左侧黑色曲线在与它们相遇的点处的红色曲线的相反方向上弯曲,但右侧黑色曲线在相同方向上弯曲。有一个拐点,红色和左黑色曲线相遇,但没有红色和右黑色曲线相遇的地方。
编辑: 下面,我添加了另一张图片,显示了包围Bezier路径的多边形。多边形的交叉线(如黑色曲线所示)测试拐点,而不是环路。我猜测可以通过检查包围多边形是否相交来测试一条与另一条曲线相交的曲线,如红色和蓝色曲线所示。
P.S。由于存在一些关于约束的问题,我将在此列出其中一些:
答案 0 :(得分:3)
曲率方程式适度简单。你只需要曲率的符号,所以你可以跳过一点数学。您基本上对一阶和二阶导数的交叉积的符号感兴趣。
这种简化只有在曲线平滑连接时才有效。如果没有相同的切线,则需要进行更复杂的测试。
曲线P曲率的符号:
ax = P[1].x - P[0].x; // a = P1 - P0
ay = P[1].y - P[0].y;
bx = P[2].x - P[1].x - ax; // b = P2 - P1 - a
by = P[2].y - P[1].y - ay;
cx = P[3].x - P[2].x - bx*2 - ax; // c = P3 - P2 - 2b - a
cy = P[3].y - P[2].y - by*2 - ay;
bc = bx*cy - cx*by;
ac = ax*cy - cx*ay;
ab = ax*by - bx*ay;
r = ab + ac*t + bc*t*t;
注意,r是一阶和二阶导数的叉积,符号表示曲率方向。计算左曲线上t = 1处的r和右曲线上t = 0处的r。如果产品是负数,则存在拐点。
如果你有曲线U,V和W,其中U是左边的黑色,V是中间的红色,W是右边的黑色,然后计算每个的bc,ac和ab。如果两个连接都是拐点,则以下测试将为真:
(Uab+Uac+Ubc)*(Vab) < 0 && (Vab+Vac+Vbc)*(Wab) < 0
曲率方程有一个我忽略的分母。它不会影响曲率的符号,如果曲线是直线,则只会为零。
数学总结:
// start with the classic bezier curve equation
P = (1-t)^3*P0 + 3*(1-t)^2*t*P1 + 3*(1-t)*t^2*P2 + t^3*P3
// convert to polynomial
P = P0 + 3*t*(P1 - P0) + 3*t^2*(P2 - 2*P1 + P0) + t^3*(P3 - 3*P2 + 3*P1 - P0)
// rename the terms to a,b,c
P = P0 + 3at + 3btt + cttt
// find the first and second derivatives
P' = 3a + 6bt + 3ctt
P" = 6b + 6ct
// and the cross product after some reduction
P' x P" = ab + act + bctt
答案 1 :(得分:0)
检查贝塞尔曲线是否具有双点或自相交的确定性方法之一是计算逆方程并评估根,因为贝塞尔曲线的反演方程是在自交点处始终为零。 详见本例T.W.Sederberg –course notes。然后,为了检测两个贝塞尔曲线(问题中的红色和黑色)是否相互交叉,有一些方法,binary subdivision(更容易实现)和bezier clipping(效率和代码的非常好的平衡)复杂性),隐式化(不值得)。
但更有效的方法可能是将曲线细分为小线段并找到交点。 Here is one way to do it。特别是当你想知道路径是否自相交,而不是准确的交点时。
如果你对分段贝塞尔曲线的CV的位置(或上面提到的@Pomax的poly-bezier)有明确的假设,你可以试试你在问题中提到的基于曲率的方法。
以下是与问题类似的路径上的曲率图。在类似的情况下,它似乎可以为您提供您正在寻找的解决方案。此外,如果有一个尖点,似乎快速和脏方法仍然有效!
答案 2 :(得分:0)
假设您有4个点:P0,P1,P2和P3,它们描述了一个三次贝塞尔曲线(简称 cBc )。 P0和P3是起点和终点,P1和P2是方向点。
当P0,P1和P2共线时, cBc 在P0处有一个拐点。
当P1,P2和P3共线时, cBC 在P3处有一个拐点。
说明
1)使用矢量积(分别为P0P1 x P1P2和P1P2 x P2P3)检查共线性。结果应该是零长度矢量。
2)避免P0,P1,P2和P3全部共线的情况, cBc 它们在常识中创建了不是曲线。< / p>
推论。
当P1 = P2时, cBc 两侧都有拐点。
答案 3 :(得分:0)
@ Victor Engel:谢谢你的澄清。现在解决方案更加容易。
简而言之,看看两条曲线的扭矩。如果他们拉在一起,那么&#34;拐点&#34;如果他们遭到反对,他们就会发生弯曲&#34;继续进行。
备注:单词&#34; inflexion&#34; 和&#34;曲率&#34; 在这里比 严格的数学意义 ,因此使用撇号。
和以前一样,一组点 P0,P1,P2,P3 定义了第一个三次贝塞尔曲线, Q0,Q1,Q2 和 Q3 < / strong>第二个。 此外,4个载体: a,b,u,w 将是有用的。
a = P2P3
b = P1P2
u = Q0Q1
v = Q1Q2
我将省略C1连续性检查,它已经完成了。 (这意味着 P3 = Q0 和 a x u = 0)
最后, Tp 和 Tq 扭矩将会出现。
Tp = b x a
(&#34; x&#34;表示矢量积,但平面上的 Tp 被视为简单数字,而不是矢量。}
if Tp=0 {very rarely vectors a, b can be parallel.}
b = P0P1
Tp = b x a
if Tp=0
No! It's can't be! Who straightened the curve???
STOP
endif
endif
Tq = u x v
if Tq=0 {also vectors u, v can be parallel.}
v = Q2Q3
Tq = u x v
if Tq=0
Oh no! What happened to my curve???
STOP
endif
endif
现在进行最后的测试:
if Tp*Tq < 0 then Houston! We have AN "INFLEXION"!
else WE CONTINUE THE TURN!