1.問題描述:
最近在做一個watch上的電子寵物的項目,整個主功能界面是個圓形,然后每個功能(喂食,清潔,愉悅之類的)各自是圓上的一部分也就是個扇形。然后點擊每個扇形,出現對應的子功能界面,格式和主功能界面一致。
現在要實現點擊對應的扇形會出現對應的子功能界面,子功能界面和這個類似。換而言之就是點擊一下界面,需要判斷出接觸點落在哪個扇形內。
當時一個簡單的想法是在扇形區域內放置一個方形的按鈕,點擊后調用回調就可以,但是缺點是精確度不夠,因為按鈕不能覆蓋整個扇形,所以這種方法不可取。
於是需要實現點擊任意區域,都要能判斷到接觸點屬於哪個區域。
2.分析:
現在需求已經很明確,就是判斷接觸點位於圓內的哪個扇形內。以判斷一點是否在A0B扇形內為例。
通過和美術溝通得知,每個扇形在圓上的兩點坐標和圓心坐標是知道的。假設圓心坐標 O(0, 0), 上圖中A,B點坐標A(x1, y1), B(x2, y2) ,以及可以在點擊時得到的任一點坐標P(x, y) 。
現在討論(0 <= 角AOB <= 180)度的情況。
判斷點在扇形內要滿足以下三個條件:
1. 扇形的"start arm" 要逆時針旋轉經過P點。
2. 扇形的"end arm" 要順時針旋轉經過P點。
3. 點在圓內。
如下圖:
證明1 "start arm" 0A向量到OP向量是順時針旋轉:
1.)做“start arm” OA向量 "逆時針方向"轉過90度 的法向量(normal vector),則利用兩個垂直向量的 點乘 為 0 的性質 則該法向量可以表示為n1(-y1, x1).
2. )n1 和 OP 兩向量做點積 n1*OP = |n1|*|OP|*cosa (a為n1和OP間的夾角) ,如果點積的結果大於0,則表明0 < a < 90, 而OA和n1 夾角= 90. 這時可以證明0A向量到OP向量是順時針旋轉。
反之點積結果小於0,表示0A向量到OP向量是逆時針旋轉. 這里給出點積的百科: http://baike.baidu.com/view/2744555.htm
可以這樣理解,OA逆時針旋轉90度得到n1,倘若OP與n1的夾角小於90度,則OA逆時針旋轉到OP。
如下圖
接下來證明"end arm" 到OP是順時針旋轉:
1.)做“end arm” OB向量 "逆時針方向"轉過90度 的法向量(normal vector),則利用兩個垂直向量的 點乘 為 0 的性質 則該法向量可以表示為n2(-y2, x2).
2. )n2 和 OP 兩向量做點積 n2*OP = |n2|*|OP|*cosb (b為n1和OP間的夾角) ,如果點積的結果小於0,則表明90 < b < 180, 而OB和n2 夾角= 90. 這時可以證明0B向量到OP向量是順時針旋轉。
反之點積結果大於0,表示0B向量到OP向量是逆時針旋轉.
如下圖:
下面給出一個判斷扇形的"start arm" 或 “end arm” 到OP是順時針還是逆時針的函數:
function areClockWise(Varm, Vop)
{
return -Varm.y * Vop.x + Varm.x * Vop.y > 0;
}
說明:Varm的垂直向量表示為 (-Varm.y, Varm.x),所以上邊就是兩個向量的點積。
接下來判斷點P是否在圓內,這個就很簡單了
function isWithinRadius(v, radiusSquared)
{
return v.x*v.x + v.y*v.y <= radiusSquared;
}
那么三個的證明如上,下邊是總體的代碼,當然可以用任何語言實現:
function isInsideSector(point, center, sectorStart, sectorEnd, radiusSquared) { //point即為任意一點,sectorStart即為"start arm", 同理sectorEnd. var relPoint = { x: point.x - center.x, y: point.y - center.y }; return !areClockwise(sectorStart, relPoint) && areClockwise(sectorEnd, relPoint) && isWithinRadius(relPoint, radiusSquared); }
說明:以上判斷扇形的sectorStart和sectorEnd為逆時針順序, 且(0 <= 角AOB <= 180)度的情況,如果任一點恰好和OA或OB重合,則需根據具體需求判斷算不算在扇形內.
原文地址: http://stackoverflow.com/questions/13652518/efficiently-find-points-inside-a-circle-sector
----------------------------------------------------------------------------------------------------------------分割線----------------------------------------------------------------------------------------------------------------------------
另一種思路:
本着盡量不用三角函數的原則,這里還可以通過比較斜率大小來判斷點是否在扇形內。在y軸的左側,斜率逆時針方向是遞增的,y軸右側,斜率也是遞增的,利用這個性質可以得出相應的方法。
當然,還需討論扇形包含y軸的情況。 本來沒發現上邊的方法之前,是要用這個思路實現的。
延伸:
1.如何利用上邊方法,判斷給出的兩個“arm”圍成的扇形角度是否大於180呢?
1)首先,做OA的反向延長線,交圓於點C,得到OC向量(-x1, -y1)。
2)如果OA和OB夾角大於180,則OC會用過逆時針旋轉轉到OB,這時用判斷“條件1”的步驟計算,若得到的點積大於0則表示 OA,OB所圍成的扇形大於180,反之則小於180.
2.當不知道OA,0B所圍成扇形是否大於180時,如何正確判斷點P是否在扇形中呢?
1)先用上邊的方法判斷OA,OB所圍成扇形是否>180.若大於180,這時只需判斷p點是否在OB,OA所謂稱的扇形(即star tarm = OB, end arm = OA)內即可。
水平有限,難免出錯,歡迎指正與指點。 希望通過閱讀本文能給讀者提供解決問題的思路。