Come faccio a sapere se un percorso chiuso contiene un dato punto ?

? Tom Seago @ | Original: StackOverFlow

In Android, ho un oggetto Path che mi capita di conoscere definisce un percorso chiuso, e ho bisogno di capire se un dato punto è contenuto all'interno del percorso . Quello che speravo in stato qualcosa lungo le linee di

path.contains ( int x, int y )

ma che non sembra esistere .

La ragione specifica che sto cercando questo è perché ho ​​una collezione di forme sullo schermo definiti come percorsi, e voglio capire quale l' utente fa clic su . Se c'è un modo migliore per essere avvicinando questo come l'utilizzo di elementi dell'interfaccia utente differenti piuttosto che farlo " nel modo più duro " io, io sono aperto a suggerimenti .

Sono aperto a scrivere un algoritmo me se devo, ma questo significa ricerca diverso credo.

Top 5 Risposta

1Brian @

La classe android.graphics.Path non ha un tale metodo . La classe Canvas ha ancora una regione di ritaglio che può essere impostata su un percorso, non c'è modo di testare contro un punto . Si potrebbe provare Canvas.quickReject, test contro un singolo rettangolo punto ( o un 1x1 Rect ) . Non so se questo sarebbe davvero check contro il percorso o solo il rettangolo limite, però.

La classe Regione tiene chiaramente solo traccia del rettangolo che contiene .

Si potrebbe considerare l'elaborazione di ciascuna delle regioni in uno strato alpha Bitmap 8-bit con ogni Path compilato il proprio valore ' color' ( assicuratevi anti- aliasing è disattivato nel vostro Paint ) . Questo crea una sorta di maschera per ogni percorso riempito con un indice al percorso che riempito . Poi si potrebbe utilizzare il valore di pixel come un indice nella vostra lista di percorsi .

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

Poi cercare punti ,

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

Assicuratevi di controllare per 255 ( nessun percorso ) se ci sono punti vuoti .

2Randy Findley @

Ecco quello che ho fatto e che sembra funzionare :

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

Ora è possibile utilizzare i region.contians ( x, y ) metodo .

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

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

** Aggiornamento sulla 2010/06/07 ** Il metodo region.setPath causerà la mia app crash ( nessun messaggio di avviso ) se la rectF è troppo grande . Ecco la mia soluzione :

// 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 di WebKit ha un C ++ work-around per il bug di 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 @

So di essere un po 'in ritardo alla festa, ma vorrei risolvere questo problema pensando come determinare se un punto è in un poligono .

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

La matematica calcola più lentamente quando si sta guardando Bezier spline invece di segmenti di linea, ma disegnando un raggio dal punto funziona ancora .