Как я могу определить, содержит ли замкнутый путь заданную точку?
В Android у меня есть объект Path, который, как мне известно, определяет закрытый путь, и мне нужно выяснить, содержится ли данная точка в пути. То, на что я надеялся, было чем-то вроде
path.contains (int x, int y)
но это, похоже, не существует.
- Вычисление угла между линией, определяемой двумя точками
- Android: размер фонового изображения (в пикселях), который поддерживает все устройства
Конкретная причина, по которой я ищу это, состоит в том, что у меня есть набор фигур на экране, определенный как пути, и я хочу выяснить, на какой из них пользователь нажал. Если есть лучший способ приблизиться к этому, например, использовать разные элементы пользовательского интерфейса, а не делать это «трудным путем», я открыт для предложений.
Я открыт для написания алгоритма самостоятельно, если нужно, но это означает, что я предполагаю различные исследования.
Класс android.graphics.Path
не имеет такого метода. Класс Canvas имеет область отсечения, которая может быть установлена в путь, и нет возможности протестировать ее против точки. Вы можете попробовать Canvas.quickReject, тестирование на один прямоугольник точки (или 1×1 Rect
). Я не знаю, действительно ли это будет проверять путь или только прямоугольник.
Класс Region явно отслеживает только содержащий прямоугольник.
Вы можете рассмотреть возможность рисования каждого из ваших регионов в 8-битном битовом слое альфа-слоя с каждым 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
Затем найдите точки,
int pathIndex = lookup.getPixel(x, y); pathIndex >>>= 24;
Обязательно проверьте 255 (нет пути), если есть незаполненные точки.
Вот что я сделал и, похоже, работает:
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. }
** Обновление от 6/7/2010 ** Метод 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. }
У SkiaUtils от WebKit есть работа на C ++ для ошибки Randy Findley:
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(floorf(point.x() / scale)); int y = static_cast (floorf(point.y() / scale)); clip.setRect(x, y, x + 1, y + 1); bool contains = rgn.setPath(*path, clip); originalPath->setFillType(originalFillType); return contains; }
Я знаю, что я немного опаздываю на вечеринку, но я бы решил эту проблему, подумав об этом, как определить, находится ли точка в полигоне.
http://en.wikipedia.org/wiki/Point_in_polygon
Математика вычисляется медленнее, когда вы смотрите на сплайны Безье вместо сегментов линии, но рисование луча от точки все еще работает.
Для полноты я хочу сделать пару заметок здесь:
Начиная с API 19, существует операция пересечения для путей. Вы можете создать очень маленький квадратный путь вокруг своей тестовой точки, пересечь ее с помощью Path и посмотреть, пуст ли результат или нет.
Вы можете конвертировать маршруты в регионы и выполнять операцию contains () . Однако регионы работают в целых координатах, и я думаю, что они используют преобразованные (пиксельные) координаты, поэтому вам придется работать с этим. Я также подозреваю, что процесс преобразования является вычислительно интенсивным.
Алгоритм кросс-пересечения, который Ханс опубликовал, хорош и быстр, но вы должны быть очень осторожны для определенных угловых случаев, например, когда луч проходит непосредственно через вершину или пересекает горизонтальный край, или когда ошибка округления является проблемой , что всегда есть.
Метод числовой обмотки в значительной степени доказан дураком, но включает в себя множество триггеров и является дорогостоящим вычислительным.
В этой статье Дэн Воскресенье приводится гибридный алгоритм, который столь же точен, как и число обмотки, но как вычислительно простой, как алгоритм лучевого каста. Меня отпугнул, насколько он утончен.
См. https://stackoverflow.com/a/33974251/338479 для моего кода, который будет выполнять вычисление «точка-в-пути» для пути, состоящего из сегментов линии, дуг и кругов.