Como posso saber se um caminho fechado contém um determinado ponto ?

? Tom Seago @ | Original: StackOverFlow
---

No Android, eu tenho um objeto Path que acontece que eu sei define um caminho fechado, e eu preciso descobrir se um determinado ponto está contido dentro do caminho . O que eu estava esperando era algo ao longo das linhas de

path.contains ( int x, int y )

mas isso não parece existir.

O motivo específico que eu estou olhando para isso é porque eu tenho uma coleção de formas na tela definidos como caminhos, e eu quero descobrir qual o usuário clicou. Se há uma maneira melhor de estar se aproximando este como o uso de diferentes elementos de interface do usuário, em vez de fazê-lo " da maneira mais difícil " eu, eu estou aberto a sugestões.

Estou aberto a escrever um algoritmo de mim mesmo se eu precisar, mas isso significa que a pesquisa diferente, eu acho.

---

Top 5 Responder

1Brian @

O android.graphics.Path class não tem um tal método. A classe de lona tem uma região de corte que pode ser definido como um caminho, não há nenhuma maneira de testá-lo de encontro a um ponto . Você pode tentar Canvas.quickReject, testando contra um único retângulo ponto (ou um 1x1 Rect ) . Eu não sei se isso realmente vá de encontro ao caminho ou apenas o retângulo envolvente, no entanto.

A classe Região claramente apenas mantém o controle do retângulo que contém .

Você pode considerar o desenho cada uma de suas regiões em um 8-bit camada alpha Bitmap com cada Path preenchido em seu próprio valor "cor" ( certifique-se de anti -aliasing está desativado no seu Paint ) . Isso cria uma espécie de máscara para cada caminho cheio com um índice para o caminho que encheu. Em seguida, você pode simplesmente usar o valor do pixel como um índice para a sua lista de caminhos.

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); 
    }

Em seguida, procure pontos ,

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

Certifique-se de verificar a existência de 255 (no caminho ), se há pontos não preenchidas .

2Randy Findley @

Aqui está o que eu fiz e ele parece funcionar :

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));

Agora você pode usar os region.contians ( x, y ) de método.

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

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

** Atualização em 6/7/2010 ** O método region.setPath fará com que meu aplicativo para curso ( nenhuma mensagem de aviso ), se o rectF é muito grande. Aqui está a minha solução :

// 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.
}
3Jesse Wilson @

Http://www.opensource.apple.com/source/WebCore/WebCore-658.28/platform/graphics/skia/SkiaUtils.cpp do WebKit tem um C ++ solução alternativa para bug do 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<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;
}
4Cal Hinshaw @

Eu sei que estou um pouco atrasado para a festa, mas eu gostaria de resolver este problema através de pensar nisso como determinar se um ponto está em um polígono.

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

A matemática computa mais lentamente quando você está olhando para curvas Bézier em vez de segmentos de linha, mas desenho de um raio a partir do ponto ainda funciona.