2010-04-08 101 views
10

在Android中,我有一個我碰巧知道的Path對象定義了一個封閉的路徑,我需要弄清楚路徑中是否包含給定的點。我希望的是沿如何判斷封閉路徑是否包含給定點?

path.contains線(INT X,int y)對

,但似乎不存在的東西。

我正在尋找這個的具體原因是因爲我有一個屏幕上的形狀集合定義爲路徑,我想弄清楚用戶點擊了哪一個。如果有更好的方法來處理這個問題,比如使用不同的用戶界面元素,而不是自己以「困難的方式」來做,我願意接受建議。

如果需要我可以自己寫一個算法,但這意味着不同的研究我猜。

回答

6

android.graphics.Path類沒有這樣的方法。 Canvas類確實有一個可以設置爲路徑的剪輯區域,但無法針對某個點進行測試。你可以嘗試Canvas.quickReject,測試單個點矩形(或1x1 Rect)。不過,我不知道是否真的會檢查路徑或只是包圍矩形。

Region類顯然只跟蹤包含矩形。

你可能會考慮每個區域的繪製與各Path填充它自己的「顏色」值的8位alpha層位圖(確保抗鋸齒在你Paint關閉)。這爲每個路徑創建了一種掩碼,用填充它的路徑的索引填充。然後,您可以將像素值用作路徑列表中的索引。

Bitmap lookup = Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8); 
//do this so that regions outside any path have a default 
//path index of 255 
lookup.eraseColor(0xFF000000); 

Canvas canvas = new Canvas(lookup); 
Paint paint = new Paint(); 

//these are defaults, you only need them if reusing a Paint 
paint.setAntiAlias(false); 
paint.setStyle(Paint.Style.FILL); 

for(int i=0;i<paths.size();i++) 
    { 
    paint.setColor(i<<24); // use only alpha value for color 0xXX000000 
    canvas.drawPath(paths.get(i), paint); 
    } 

再看看點,

int pathIndex = lookup.getPixel(x, y); 
pathIndex >>>= 24; 

一定要檢查255(沒有路徑),如果有空缺點。

+0

啊,好吧,我喜歡它。無論如何,額外的記憶沒有做任何有用的事! 我曾經遇到過的一個問題是,ALPHA_8不會再給我任何東西,只有0返回使用getPixel。我不得不放棄並使用ARGB_8888。我發現幾乎沒有關於ALPHA_8格式的文檔以及它的侷限性,但它確實在我這裏不起作用。 謝謝Brian。 – 2010-04-08 06:32:40

+0

鍛鍊記憶!整個Skia Android 2D框架都沒有記錄。對4倍內存需求感到遺憾,但至少Android屏幕非常小。 – Brian 2010-04-08 07:20:15

+0

@TomSeago嗨,我得到了同樣的問題,我也必須使用ARGB_8888,如果使用ALPHA_8,沒有任何回報! – John 2014-08-31 09:59:15

18

這是我沒有和它似乎工作:

RectF rectF = new RectF(); 
path.computeBounds(rectF, true); 
region = new Region(); 
region.setPath(path, new Region((int) rectF.left, (int) rectF.top, (int) rectF.right, (int) rectF.bottom)); 

現在你可以使用region.contains(x,y)方法。

Point point = new Point(); 
mapView.getProjection().toPixels(geoPoint, point); 

if (region.contains(point.x, point.y)) { 
    // Within the path. 
} 

**更新在2010年6月7日** 的region.setPath方法將導致我的應用程序崩潰(沒有警告消息)如果rectF太大。這裏是我的解決方案:

// Get the screen rect. If this intersects with the path's rect 
// then lets display this zone. The rectF will become the 
// intersection of the two rects. This will decrease the size therefor no more crashes. 
Rect drawableRect = new Rect(); 
mapView.getDrawingRect(drawableRect); 

if (rectF.intersects(drawableRect.left, drawableRect.top, drawableRect.right, drawableRect.bottom)) { 
    // ... Display Zone. 
} 
+0

爲什麼我在使用上面的代碼時將region.getBounds()作爲0,0,0,0?它不適用於我的項目我在哪裏犯錯? – 2012-11-21 14:05:09

+3

不幸的是,如果路徑不是一個簡單的例子是一個三角形,一個矩形 - 三角形的邊界是一個矩形!所以,如果你,例如,在斜邊上方點擊,觸摸的點是靜止的在綁定!!也許更好的解決方案可以是這樣的:http://stackoverflow.com/questions/7044838/finding-points-contained-in-a-path-in-android – kinghomer 2012-12-19 17:03:37

+1

它的工作,但有時區域採取點是外封閉路徑 – Sameer 2013-01-21 07:49:58

4

WebKit的SkiaUtils有一個C++變通蘭迪芬德利的bug:

bool SkPathContainsPoint(SkPath* originalPath, const FloatPoint& point, SkPath::FillType ft) 
{ 
    SkRegion rgn; 
    SkRegion clip; 

    SkPath::FillType originalFillType = originalPath->getFillType(); 

    const SkPath* path = originalPath; 
    SkPath scaledPath; 
    int scale = 1; 

    SkRect bounds = originalPath->getBounds(); 

    // We can immediately return false if the point is outside the bounding rect 
    if (!bounds.contains(SkFloatToScalar(point.x()), SkFloatToScalar(point.y()))) 
     return false; 

    originalPath->setFillType(ft); 

    // Skia has trouble with coordinates close to the max signed 16-bit values 
    // If we have those, we need to scale. 
    // 
    // TODO: remove this code once Skia is patched to work properly with large 
    // values 
    const SkScalar kMaxCoordinate = SkIntToScalar(1<<15); 
    SkScalar biggestCoord = std::max(std::max(std::max(bounds.fRight, bounds.fBottom), -bounds.fLeft), -bounds.fTop); 

    if (biggestCoord > kMaxCoordinate) { 
     scale = SkScalarCeil(SkScalarDiv(biggestCoord, kMaxCoordinate)); 

     SkMatrix m; 
     m.setScale(SkScalarInvert(SkIntToScalar(scale)), SkScalarInvert(SkIntToScalar(scale))); 
     originalPath->transform(m, &scaledPath); 
     path = &scaledPath; 
    } 

    int x = static_cast<int>(floorf(point.x()/scale)); 
    int y = static_cast<int>(floorf(point.y()/scale)); 
    clip.setRect(x, y, x + 1, y + 1); 

    bool contains = rgn.setPath(*path, clip); 

    originalPath->setFillType(originalFillType); 
    return contains; 
} 
0

我知道我有點遲到了,但我會解決這個問題通過考慮它像確定一個點是否在一個多邊形中一樣。

http://en.wikipedia.org/wiki/Point_in_polygon

數學計算速度比較慢,當你看着貝塞爾樣,而不是線段,而是繪製光線從點仍然有效。

+3

聚會尚未結束我的朋友,世界仍在爲此而掙扎。你好,從2015年:) – 2015-07-29 05:21:21

0

爲了完整起見,我想打幾個音符在這裏:

由於API 19,對路徑的intersection operation。您可以在測試點周圍創建一條非常小的方形路徑,將其與路徑相交,並查看結果是否爲空。

您可以將路徑轉換爲區域並執行contains()操作。然而,區域工作在整數座標,我認爲他們使用轉換(像素)座標,所以你必須使用它。我也懷疑轉換過程是計算密集型的。

漢斯公佈的邊緣穿越算法既好又快,但對於某些邊角情況,例如當射線直接穿過頂點,或與水平邊緣相交時,或者舍入時,必須非常小心錯誤是一個始終存在的問題。

winding number方法是非常傻瓜的證明,但涉及大量的觸發,並且在計算上很昂貴。

This paper by Dan Sunday給出了一種混合算法,它與繞組編號一樣精確,但與光線投射算法一樣在計算上很簡單。它吹走了我多麼優雅。

請參閱https://stackoverflow.com/a/33974251/338479我的代碼,它將爲由線段,圓弧和圓組成的路徑執行點路徑計算。

相關問題