如果根據在屏幕上任點的一點,判斷他是否屬於已經畫在屏幕的哪條直線?


在屏幕中已經畫有無數條的線,線是沒有規則的任意曲線,但在程序中都是用取兩點畫直線逼近曲線這樣畫出來的.一條曲線是由若干個點構成的,然后在程序實現畫圖時每次取兩點畫直線.這些點的坐標都已經得知了.

現在問題是:用鼠標在屏幕上任點一點,要判斷這一點擊是否已經選中了哪條線?不知用什么方法來判斷比較好?
(我現在用了這樣一種方法:每次取兩點坐標,生成一個直線方程,然后再根據鼠標點的那一點和這個直線方程的距離來判斷,如果距離小於我給的一定的誤差范圍,就在該直線內.可這種方法我發現並不太好)

30 个解决方案

#1


你畫的曲線的點已經存儲了,可以逐一在里面檢索。不過這樣比較初級
你可以想想怎么樣檢索更合理,關鍵是要檢索

#2


我已經有了一個檢索的過程了,首先我已經判斷了鼠標點的點是否是屬於哪個曲線的我接矩形了,屬於那個外接矩形才去判斷是否在該線上的.

只是我最后用的點到線的距離的公式來計算並不是很精確的,有的時候點點在了線上,還說沒選中,有的時候,離線還有一點點距離,也選中了

#3


用三點(線段兩端點和被檢測點)所構成的三角形面積是否小於一個很小的值來判斷,不知道會不會好一些。

#4


用三角形以及兩段直線的距離都行不通的,關鍵是我要給它一個誤差范圍,如果那段線本身很短的時候(目標線很短),那誤差還能控制,如果目標線很長的時候,哪怕鼠標點的那點只是偏離目標線一點點,那個誤差就很大了

#5


這種情況不好處理,如果可能的話建議你重新,調整程序結夠,如過你對所話的線編輯比較多的話,更加要考慮考慮。我以前的做法是首先算出線的包容區域,然后利用指針在這個區域內找。

#6


多謝hardstudylulin(思過崖) 兄了

在我的應用中,所畫線是有很多很多的,不過不用做什么編輯的,只要做一個查詢功能就行了,也就是只要做判斷一下點是否在線上這一個操作.

老兄所指的先算出線的包容區域不知能否說詳細些,小弟不太明白啊,呵呵.

我現在的做法是:
      首先算出整個大曲線(由若干段直線構成)的外接矩形,鼠標點下去的時候,首先判斷該點是屬於哪幾個曲線的大外接矩形范圍內的,再到這幾條大曲線中去尋找
      尋找時,我再將大曲線分成若干段直線(都有坐標點的,曲線就是這樣畫來的),再將這一小段直線的外接矩形算出來,如果是屬於哪段直線的外接矩形,就生成該段直線的直線方程,再用點到直線的距離來判斷.

     這樣做基本的判斷選擇都還行,但在某些情況下,選擇的就不太精確了.這個問題搞得非常郁悶,請各位老兄幫幫忙

#7


我覺得不太精確應該是由於屏幕上的線段與該線段方程之間的誤差造成的。

#8


我認為下面的算法最精確的,用的是三點一線的行列式公式,完全不用出發,只用了乘和加
這是我的源程序,不想另外寫算法了,湊合着看吧,看不懂再說。
一點說明:error參數是精確度,最小為1(象素);
BOOL Line::PointOnLine(Point p,int error)
{
int x1=begin->GetX();
int y1=begin->GetY();
int x2=end->GetX();
int y2=end->GetY();
int x3=p.GetX();
int y3=p.GetY();
int X=x2*y1-x1*y2+x1*y3-x2*y3;
int Y=x2*y1-x1*y2-y1*x3+y2*x3;
if((abs(X-(y1-y2)*x3)<=error||abs(Y-(x1-x2)*y3)<=error)&&((x1-x3)*(x3-x2)>0||(y1-y3)*(y3-y2)>0))
return TRUE;
else
return FALSE; 
}

#9


我認為判斷出點在哪條線段上就夠了吧,你應該很容易的得知線段在哪條曲線上吧?

#10


TO  crazy_lazy_pig(瘋狂懶豬):

單從代碼上來看,你的確實算是很簡潔的了。抱歉的是,我對你的代碼看不懂啊。begin,end,GetX,GetY不知為什么東西,后面的幾個式子和條件判斷也看得不明白,老兄能否給點說明了,非常感謝!

#11


看了這么多回復,我都不好意思說我的想法了。
你這個要求對現在的CPU太苛刻了吧?
你看看AutoCAD是怎么做的。
一般情況下都是在曲線的轉折點用一個矩形構成一個熱點,然后你再看鼠標是否在該熱點中就行了。這樣算法簡單、速度快、用戶操作也明白。

#12


TO  flyhigh(一不小心):

如果一條直線稍長一點的話,那么構成的熱點的矩形會很大的吧,在屏幕上有些區域的點會屬於好多個那種矩形的,最終還是要用直線來判斷吧

#13


呵呵,實在是抱歉,我把我的程序直接貼出來了,太偷懶了:)
解釋一下:
Line——是我的一個類,實際上就是線段類
begin和end是Line(線段)的端點,GetX,GetY則是得到端點X,Y坐標的共有函數

其實你不用關心那么多,只要知道:
x1,y1以及x2,y2是線段兩端點的坐標,x3,y3是將要判斷是否是線段上的點。
若點(x3,y3)在線段上則返回TRUE,否則返回FALSE.

#14


至於算法嘛,說起來也是有些復雜的,昨天眼睛疼,就沒仔細講。如果你線性代數學過就簡單了。
具體原理就是利用三點P1(x1,y1)、P2(x2,y2)、P3(x3,y3)共線的行列式公式:
                 |  x1   y1   1 |
                 |  x2   y2   1 |=0
                 |  x3   y3   1 |
當然利用這個公式判斷點嚴格在線上是很容易的,但是如何判斷近似在線上呢?
我利用了一個小技巧,即:
      先使得 x3 變為未知數 x ,解出 x ,這樣我就得到在線上的一個點:(x,y3);
      同理,我也可以另 y3 未知,得到線上另外一個點:(x3,y);
這樣我就得到了一個直角三角形:
      (x3,y3)-(x,y3)-(x3,y) , 其中(x3,y3)是直角頂點(你最好是在紙上畫畫圖)
當這個直角三角形最小的一條直角邊長度小於給定誤差時,則可以確定其在直線上,
(需要說明的是我在作這一步判斷時又用了一個小技巧,即避免了除法,做法是解方程時並沒有把未知數的系數除過去,所以我得到了 X 和 Y ,因此我作差時也得把其他項都乘這個系數,這就是我判斷條件前半部分用了乘法的原因)。
應當注意的是這只能判斷是否在直線上,並無法得知是否在線段上,if語句中邏輯與符號后的東西就是作這一步工作,為什么就請你自己研究吧。

順便更正一下我的程序(多謝你的提問,使我發現了錯誤,這可是不可饒恕的錯誤,被老板發現是要挨罵的):
if((abs(X-(y1-y2)*x3)<=(y1-y2)*error||abs(Y-(x1-x2)*y3)<=(x1-x2)*error)&&((x1-x3)*(x3-x2)>0||(y1-y3)*(y3-y2)>0))

#15


還有一點需要說明,我的程序是要判斷點在線段“中”,也就是說判斷點是否在除掉了兩端點的線段上,你的程序應該包含兩端點的,所以  && 后的不等式應當用 >= 號

#16


多謝老兄了,我先揣摩揣摩了.

#17


已經將老兄的算法用到我的程序上去了,不過在實際點選線的操作過程中,感覺和我以前用的點到直線的距離的算法差不多的效果啊

#18


你的感覺是對的,應該是差不多效果,從數學上推斷(不考慮計算機精度)兩種方法是完全一樣的效果。我的算法只是稍微簡潔一點,而且在不用特殊高精度計算的情況下應該是精度最高的,即計算步驟少,減少了累積誤差,同時也加快了運算速度。當然憑借現在的計算機的性能,我們肉眼是感覺不到差別的。

#19


你可以看看hough變換!
因為所有在一條直線上的點都有相同的交點。這樣如果新點與所保存的交點重合就說明在這條直線上。

#20


crazy_lazy_pig(瘋狂懶豬) 的方法實際上是將坐標(x0,y0)代入直線方程a*x+b*y+c=0的左邊部分,然后考察其值與右邊0的差異,如果小於幾個象素就認為是在線上(或者繼續進行更高精度的檢查).這種方法已經是單個比較的極限了,沒法改進

偶認為主要問題不在這里

設屏幕上有N條曲線,每條曲線平均分成k個直線段.那么要判斷(x0,y0)在不在某條線上就要比較大約N*k/2次,由於k一般相當大,這個次數是很多的

所以,要盡可能減少比較的次數

偶有個想法: 將屏幕分成A*B個小塊,對於曲線,由於作圖時要將曲線划分成細小的線段來完成,所以一定可以事先判斷出這些細小的線段大致屬於哪些塊中,我們就可以對這些細小線段與它的締屬塊建立索引關系,然后,要判斷(x0,y0)是否在線上時,先找出該點所在塊,只對該塊中的線段進行檢查,這樣可以省去許多比較次數了

#21


有道理,在進行兩個點以上的判斷時是有效多了,就是要多用一些資源了,不過憑現在的計算機性能,我們不怕浪費資源。

#22


在我的應用中是這樣處理了,給每條曲線設一個外接矩形,首先判斷那點是屬於哪條曲線的外接矩形,這樣就得到的就只有幾條曲線了,然后再給曲線內的每條直線(線段)設一外接矩形,然后再判斷點是在哪條線段的外接矩形內,然后再開始用點到直線的公式或者上面的老兄的公式進行計算,這樣一來,我覺得計算的次數還是不太多的.
我開始之所以很多線都不好選,呵呵,然后是因為我的線段的外接矩形沒有給一定誤差范圍,所以,哪怕點在了一根線的外面一點點,那條就被跳過去不比較了,弄得不好選,現在改了改好了很多了.

順便問問各位老兄:如果是判斷點是否屬於一個區域,這個區也是由若干個點畫起來的,又該如何處理了?

#23


"如果是判斷點是否屬於一個區域,這個區也是由若干個點畫起來的,又該如何處理了?"

如果是凸多邊形,辦法很簡單:
由該點向多邊形的各個頂點作直線.以順時針(或逆時針)的方向掃描各頂點,如果相鄰夾角都能夠在小於180度的范圍內掃描完畢,那么肯定是在內部,否則一定是在外部

凹多邊形的情況沒想出來

#24


當然要先出去正好在頂點的情況
如果是在邊上,則有一個是180度

#25


更正: "相鄰夾角"可以改為"最大夾角" ,在凸多邊形和同一旋轉方向求角度的情況下

#26


多謝樓上老兄了。
問題是我的區的范圍是亂七八糟的,我的應用是用在地圖上的,一個區的范圍就是一個地區的范圍,這個范圍也就是區的形狀是沒有任何規律的。

#27


哦,那就麻煩
偶先想到一個辦法: 先求該多邊形區域的最小外接凸多邊形,再找凹進去的部分,再按上面的辦法逐個判斷,不過也很麻煩,因為凹進去的部分有可能也有凹進去的....
看來你要先找一個辦法,將凹多邊形分成幾個凸多邊形....

#28


判斷多邊形內部點是有點麻煩了:(設判斷點為P)
1、作過P的水平線(或者豎直線)l ,求出 l 與多邊形的交點P1、P2、... 、Pn。
2、將這些點連同 P 排序,得到 P 的序號為 k 。
若 k 為奇數則在多邊形外,若為偶數則在多邊形內。

注:此算法規定多邊形的左邊沿為其“外部”,右邊沿為其“內部”。且為對上、下水平邊作定義(即水平邊的“內”、“外”性質不確定)。

#29


據考察,發現MFC類里有個CRgn類可以實現多變形的一些功能,包括你所要的

#30


多謝各位的相助了,我是用JAVA做的,發現JAVA里面將所有多邊形的點定義一個Polygon類,這個類有一個方法可以直接判斷坐標點是否在區內了,算了,自己就不去想了,結了.
再次感謝各位.

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com