在Android中,我有一个Path对象,我碰巧知道它定义了一个封闭的路径,我需要弄清楚路径中是否包含给定的点。我希望的是
path.contains(int x,int y)
但似乎并不存在。
我正在寻找这个的具体原因是因为我在屏幕上定义了一组形状定义为路径,我想弄清楚用户点击了哪一个。如果有更好的方法来接近这一点,比如使用不同的UI元素而不是自己“艰难”地做,那么我愿意接受建议。
如果必须的话,我愿意自己编写算法,但这意味着我猜不同的研究。
答案 0 :(得分: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日更新** 如果rectF太大,region.setPath方法将导致我的应用程序崩溃(没有警告消息)。这是我的解决方案:
// 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.
}
答案 1 :(得分:6)
android.graphics.Path
类没有这样的方法。 Canvas类确实有一个可以设置为路径的剪切区域,没有办法针对某个点测试它。您可以尝试Canvas.quickReject,针对单点矩形(或1x1 Rect
)进行测试。我不知道这是否真的会检查路径或仅仅是封闭的矩形。
Region类显然只跟踪包含的矩形。
您可以考虑将每个区域绘制为8位alpha图层位图,每个Path
填充自己的“颜色”值(确保在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(无路径)。
答案 2 :(得分:4)
WebKit的SkiaUtils为Randy Findley的bug提供了C ++解决方法:
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;
}
答案 3 :(得分:0)
我知道我在派对上有点迟了,但我会通过考虑确定点是否在多边形中来解决这个问题。
http://en.wikipedia.org/wiki/Point_in_polygon
当您查看Bezier样条而不是线段时,数学计算速度会更慢,但从该点绘制光线仍然有效。
答案 4 :(得分:0)
为了完整起见,我想在这里做几个笔记:
从API 19开始,路径有intersection operation。您可以在测试点周围创建一个非常小的方形路径,将其与路径相交,并查看结果是否为空。
您可以将路径转换为区域并执行contains()操作。但是区域以整数坐标工作,我认为它们使用变换(像素)坐标,因此您必须使用它。我还怀疑转换过程是计算密集型的。
Hans发布的边缘交叉算法很好而且快速,但是你必须非常小心某些角落情况,例如当光线直接穿过顶点,或者与水平边缘相交时,或者当圆角误差时这是一个问题,它始终是。
winding number方法非常简单,但涉及很多触发并且计算量很大。
This paper by Dan Sunday给出了一种混合算法,该算法与绕组数一样准确,但计算简单,如光线投射算法。它让我感到非常优雅。
请参阅https://stackoverflow.com/a/33974251/338479了解我的代码,该代码将对包含线段,圆弧和圆圈的路径进行路径点计算。