判斷 點在曲線上 根據貝塞爾曲線上的點反算t值

2021-10-18 04:38:33 字數 3805 閱讀 6861

專案中使用的是二次貝塞爾曲線,所以本文也主要以二次貝塞爾曲線為講解重點。 要實現上述動畫,需要首先確定a點和b點在曲線上面的比例值ta和tb

最終的需求變成:「根據貝塞爾曲線上的點反算t值」。 大概有以下幾種方法。現假設貝塞爾曲線上的點為點p(後續會用到該點)。

分片迭代是一種近似的方法。我們知道,二次貝塞爾曲線的公式如下: b(t) = (1-t)2 p0 + 2t(1-t) p1 + t2 * p2 其中: $t in $[0,1],p0為二次貝塞爾曲線的起始點,p1為控制點,p2為終止點。

如果你對於上面的知識點不是很熟悉,建議學習貝塞爾曲線相關知識。推薦學習本人的專欄canvas高階高階, 裡面有專門的章節對貝塞爾曲線進行了全面詳細的講解。本文也是從該專欄的文章中摘錄並適當改編而成的。
從以上公式,我們可以得到,對於任意給定的比例值t,可以求出對應該比例值的點b(t)。分片迭代思路是:現在加設把範圍[0,1]平均分成n(比如100)等份,形成一系列的比例值t,對於每乙個t值,求取對應的點b(t) ,然後讓點b(t)和已知在貝塞爾曲線上的點p進行比較,如果點b(t)和點p之間的直線距離在一定的誤差範圍之內,則認為b(t)等於p,而此時的t值,就是我們要求的t值。 以下是主要**:

function computet(p0,p1,p2,p) 

t+= 0.001;

} return null;

}

上述分片迭代的方法,思路最簡單,最直觀。在精度要求不高的情況下是可以滿足的。而在精度要求高的時候,即**中的「特定誤差」值要很小,可能會出現函式返回值為null的情況,在精度要求高的時候要能夠計算出值,就要增加迭代次數,此時會極大增加效能消耗。比如上面**的迭代次數可能會變成10000甚至10000。

迭代方法同樣適用於三次貝塞爾曲線和更加高階的貝塞爾曲線。
上面提到在精度要求高的情況下,要得到正確結果,要極大的增加迭代次數,造成效能的極大消耗。 有沒有辦法既提高精度,又不大量增加迭代次數呢? 經過筆者的思考,發現是可以的。想想假設要求的t值在0.5附近,那麼我們只需要在0.5附近加大分片的數量,而不需要在其他地方(0.1~0.4,0.6~1.0)增加分片的數量。 應此公升級版本的思路就是,先用比較粗的分片初步確定t值的乙個大致範圍,再在該範圍之類,比較細的分片確定t值。注意這是個遞迴的過程,如果在第二次比較細的分片情況下,仍然不能確定t值,那麼就確定乙個t值的更小分範圍;重複上面過程,直到找到t值為止。 大致步驟如下:

下面是示例**:

function computet(p0, p1, p2, p,startt = 0,endt = 1) 

if (dst < 0.0001)

t += step;

} return computet(p0, p1, p2, p, mindistancet - step,mindistancet + step);

}

以上過程雖然增加了一定的迭代次數,但是是常量級別的增加,而非數量級別的增加,所以會極大提高效能。 比如目標t值在0.5附近,第一次通過100次迭代可以確定t值的範圍在0.4 ~ 0.6之間;然後進行第二次迭代,第二次迭代此次數仍然為100次,假設確定t值的範圍在0.51 ~ 0.53之間;然後進行第三次迭代,第三次迭代此次數仍然為100次,此時可以獲取t值為0.516,可以看出最多值迭代了300次。 假設總共經過第n次迭代,每次迭代次數為m,才找到t值,那麼總共的迭代次數是n * m。

該迭代方法同樣適用於三次貝塞爾曲線和更加高階的貝塞爾曲線。而且相對於未優化的版本,該方法的效能好了很多。是適合所有貝塞爾曲線的比較好的反算t值的方法。
二分法的思路是:

上述步驟有乙個難點: 如何判斷pm和目標點p的前後順序? 對於二次貝塞爾曲線,如下圖所示:

其中,p0為起始點,p2為終止點,p1為控制點。 二次貝塞爾曲線有如下特點: 線段(p1,p0)、(p1,p2)和曲線相切,這也就意味著曲線一定在三角形(p0,p1,p2)之內,而且二次貝塞爾曲線本身不會自身相交,所有我們可以有如下結論,

對於曲線上面的點a,直線(p1,a)和線段(p0,p1)相交於點a;對於曲線上面的點b,直線(p1,b)和線段(p0,p1)相交於點b。點a和點b的先後順序與點a和點b的先後順序是一致的,而直線上面的點(a和b)的前後順序是容易判斷的。 也就是說如果點a在點b的前面,則點a也在點b的前面,反之亦然。如下圖所示:

有了以上的結論,我們就找到了判斷pm和目標點p的前後順序的方法。

如果你對上述結論不熟悉,建議學習貝塞爾曲線的相關知識,推薦學習本人的專欄canvas高階高階, 裡面有專門的章節對貝塞爾曲線進行了全面詳細的講解。本文也是從該專欄的文章中摘錄並適當改編而成的。
有了這個方法,加上前面描述的二分查詢的步驟,可以得出示例**如下:

function computet2(p0,p1,p2,p,startt = 0,endt = 1) 

//求交點:

var inter1 = segmentsintr(p0,p2,p1,p);

var inter2 = segmentsintr(p0,p2,p1,halfpoint);

var r1 = interpolationrate(p0,inter1,p2),

r2 = interpolationrate(p0,inter2,p2);

if(r1 > r2)else

return computet2(p0,p1,p2,p,startt,endt);

}

前面說過,貝塞爾曲線的公式如下: b(t) = (1-t)2 p0 + 2t(1-t) p1 + t2 * p2 其中: $t in $[0,1],p0為二次貝塞爾曲線的起始點,p1為控制點,p2為終止點。 分別表示成x和y的方程,則可以表示如下:

實際上就是兩個變數t的二次元方程,取上面任意乙個方程,帶入相關的值解方程,方程的解即為我們要求的目標t值。

整理方程: xp = (1-t)2 xp0

+ 2t(1-t) xp1 + t2 xp2,可以得出二次方程如下: (xp2

+ xp0

- 2 xp1 ) t2

+ 2(xp1 - xp0) t + (xp0

- xp) = 0。 我們已知二次方程的: at2 + b * t + c = 0的解為:

應此令:

需要注意的是,二次方程的解可能會有兩個。如果求出的解有兩個怎麼辦呢。 首先我們知道貝塞爾曲線的t值的範圍是$t in $[0,1],所以如果有兩個解:

下面是示例**,其中函式equation2用於解曲線的方程:

function computet(p0,p1,p2,p) elseelse

}else

}}function equation2(z0,z1,z2,zp) else ;

}else if(tt1 <= 1 && tt1>= 0)else

} return tt;

}

從效能方面來說:

從通用性來說,分片迭代的方式是適合任意階的貝塞爾曲線。但是考慮到效能問題所以分片迭代的優化版是通用性最好的求解方法。

判斷點在直線的哪

2.2.1 下面開始程式的設計 由於本部分需要判斷空間多邊形的拓撲關係,現在約定凸多邊形的邊界和內部,凸多邊形用頂點座標的逆時針方向序列確定。凸多邊形 p q 的頂點序列為 p1 p2 pn 和q1 q2 qn 為了簡單,假設 p邊界上不包含 q的頂點,q的邊界上不包含 p的頂點。這使得p 和 q或...

javascript 判斷點在麵內,點在圓內

點在多邊形內常用的演算法就是使用射線法,作為筆記直接上 判斷乙個點是否在多邊形內部 param points 多邊形座標集合 param testpoint 測試點座標 返回true為真,false為假 function insidepolygon points,testpoint return i...

貝塞爾曲線上一點

計算bezier曲線上的點,可用bezier曲線方程,但使用de casteljau提出的遞推算法則要簡單得多。如圖3.1.10所示,設p0 p02 p 2是一條拋物線上順序三個不同的點。過p0和p 2點的兩切線交於p1 點,在p02 點的切線交p0p 1和p2p 1於p01 和p11,則如下比例成...