網格選擇,顧名思義,就是把多邊形變成網格後選擇(此方法只適用於多邊形,若是曲線,我們就得將其分段)。
這樣,網格選擇就分成了兩步:
- 將多邊形分解爲多個三角形。
- 判斷鼠標點是否在三角形中。
來吧,我們先從最基礎的判斷鼠標點是否在三角形中開始說。
一、網格選擇與三角函數
我們可以用鼠標點和三角形其它頂點的夾角之和來判斷。
點D 在▲ABC 中:
∠ADB+∠BDC+∠CDA=360°
點D 不在▲ABC 中:
∠ADB+∠BDC+∠CDA<360°
接下來我們先說一下一下如何基於三個點計算夾角,如∠mon
先根據三個點畫一個角↓
poly.draw(ctx);
把∠mon 的頂點o 歸零:
m.x-=o.x
m.y-=o.y
n.x-=o.x
n.y-=o.y
根據餘弦定理,可以基於點m乘以點n 的點積,om的長度乘以on 的長度,計算∠mon 的餘弦值
根據反餘弦方法acos() 求∠mon,也就是下面的theta
const theta=Math.acos(cosTheta);
Math.acos() 可以自動把根據餘弦取得的弧度限制在[0,Math.PI]之內。
如果我們使用Math.atan2(y,x),會得到基於x 軸正方向的弧度;
而且y 值爲負的時候,atan2(y,x) 的值也是負數,這是不適合夾角求和的。
至於這裏面涉及的點積公式,這是個純數學的知識,大家先知道其用法即可。
感興趣的同學可以評論區扣1,我後面爲它再起一章:點積公式詳解
我們知道了一個夾角的求法之後,那就可以去求∠ADB+∠BDC+∠CDA 的夾角和了。
其和若小於360°,那就在三角之外,否則在三角之中。
我把這樣的方法封裝在了Vector2d 類裏:
注:0.01 是一個用於容差的數。
電腦對浮點數的運算不是絕對精確的,所以我沒有直接用Math.PI*2===sum來做判斷;
而且是讓鼠標點只要靠近了三角形一定範圍,就算選擇上了。
p1.includedAngleTo(p2,p3) 是求∠p1p2p3 的方法:
p1.includedAngle(p2) 求的是角的頂點歸零後夾角:
dot() 就是點積方法:
length() 是求向量長度的方法
inTriangle() 的使用方法:
若上面的bool爲true,那就說明點在三角形中。
關於判斷判斷點位是否在三角形中的方法我們就說到這。
接下來,我們聊聊:圖形選擇-多邊形網格化
在說多邊形網格化之前,我們先畫個複雜點的多邊形出來。
準備一組用於逆時針繪圖的頂點:
基於這組頂點繪製一個多邊形:
二、圖形選擇的思路解析
接下來,我們就說一下網格化思路:
- 先判斷points 中的頂點數量,若等於3,那就直接將這三個點存儲起來。否則,下一步。
- 把多邊形裏的第一個點作爲起點
- 從起點連接下下個點,構成一個三角形
- 對上面的三角形做兩個判斷:
三角形是否包含了其它頂點
三角形是否爲凹三角形
若出現上面任何一種情況,就把起點的下一個點做爲起點,執行第1步。
若上面的情況都不存在:
那就把構成這個三角形的頂點存儲起來,把下一個點從points 中刪除,把下下個點做爲起點,執行第1步。
到這裏,網格化的思路就說完了,這是一個遞歸方法,最終效果如下:
接下來,咱們就說一下這整個邏輯中涉及的兩個知識點:
- 三角形是否包含了其它頂點。
這個方法我們上一章說過,遍歷其它頂點,逐一判斷即可,在此便不再贅述。
- 判斷三角形的凹凸。
首先我們在定點的時候要遵守一個規則,這裏是逆時針定點。
這樣我們在用叉乘的方法求三角形的面積的時候,面積爲正,那就是凹三角形;面積爲負,那就是凸三角形。
求三角形面積的方法:
這個求面積方法用到的是一個叉乘算法。
整體代碼的實現步驟
- 繪製多邊形
- 聲明用於存儲三角形的數組triangles[] 和起點
const triangles=[];
let start=0;
- 建立網格
len 是points 頂點長度
i0,i1,i2 是通過取餘的方法獲取三角形頂點的索引,因爲網格化可能會對points 循環遍歷。
len===3 是如果points 長度爲三,就直接將三角形頂點放到triangles 集合裏;
這也是個終止遞歸的條件,後面points 會不斷刪除三角形的第2個頂點,從而不斷變小。
area>=0||hasOtherPointInTriangle(p0,p1,p2) 便是思路4中的兩個情況:
若滿足其中之一,就把下一個頂點作爲參數,遞歸執行crtMesh(i1) 方法。
若都不滿足,就存儲三角形,衝points 中刪除三角形的第二個頂點,將三角形的第二個頂點作爲起點參數,執行crtMesh(i1) 方法。
好了,整個網格化的實現過程就是這樣。
三、多邊形怎麼選擇
要選擇多邊形的話,那就遍歷triangles裏的三角形即可,鼠標點只要在一個三角形中,那也就在多邊形中了。
最後我把上面的過程進行了封裝,建立了一個Mesh 類:
下滑查看全部代碼
☟☟☟
網格實例化和繪製方法:
圖形選擇方法:
效果:
寫 在 最 後
關於圖形選擇的三篇前端乾貨就講到這啦~
需要複習的小夥伴可以隨時去看~
=== 圖形模塊化2:https://blog.csdn.net/qq_30071415/article/details/106895989
=== 圖形模塊化1:
https://blog.csdn.net/qq_30071415/article/details/106895394
摘至開課吧前端團隊,閱讀後頗有收穫分享至此,希望對大家有所幫助~