Custom contains method for shapes

Hi,
I’m currently trying to create a “custom” version of the contains method, that is implemented for shapes of the type path. The main reason I’m not using the path shape is, that I would like to map a texture onto my shape, which is possible for paths, but seems quite tedious (especially if the shape needs to be rotateable and moveable). A suggestion on one of my previous posts, was that I could reuse the implementation of the contains method, and convert it to work with my shapes.

I have found the implementation of the contains method through the github here, I will paste in the code below for your convenience.

public boolean contains(float x, float y) {
    if (family == PATH) {
      PVector p = new PVector(x, y);
      if (matrix != null) {
        // apply the inverse transformation matrix to the point coordinates
        PMatrix inverseCoords = matrix.get();
        // TODO why is this called twice? [fry 190724]
        // commit was https://github.com/processing/processing/commit/027fc7a4f8e8d0a435366eae754304eea282512a
        inverseCoords.invert();  // maybe cache this?
        inverseCoords.invert();  // maybe cache this?
        inverseCoords.mult(new PVector(x, y), p);
      }

      // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
      boolean c = false;
      for (int i = 0, j = vertexCount-1; i < vertexCount; j = i++) {
        if (((vertices[i][Y] > p.y) != (vertices[j][Y] > p.y)) &&
            (p.x <
                (vertices[j][X]-vertices[i][X]) *
                (y-vertices[i][Y]) /
                (vertices[j][1]-vertices[i][Y]) +
                vertices[i][X])) {
          c = !c;
        }
      }
      return c;

    } else if (family == GROUP) {
      // If this is a group, loop through children until we find one that
      // contains the supplied coordinates. If a child does not support
      // contains() throw a warning and continue.
      for (int i = 0; i < childCount; i++) {
        if (children[i].contains(x, y)) return true;
      }
      return false;

    } else {
      // https://github.com/processing/processing/issues/1280
      throw new IllegalArgumentException("The contains() method is only implemented for paths.");
    }
  }

At first glance it seemed quite easy, just remove the if statement, that checked if it was a path shape, and convert the rest to work inside my code.

Heres my contains method (pA is a reference to the PApplet object):

public boolean contains(float x, float y) {
		PVector p = new PVector(x, y);
		boolean c = false;
		for (int i = 0, j = shape.getVertexCount()-1; i < shape.getVertexCount(); j = i++) {
			if (((shape.getVertex(i).y > p.y) != (shape.getVertex(j).y > p.y)) &&
				(p.x <
				(shape.getVertex(j).x-shape.getVertex(i).x) *
				(shape.getVertex(i).y) /
				(shape.getVertex(j).y-shape.getVertex(i).y) +
				shape.getVertex(i).x)) {
				
				c = !c;
			}
		}
		return c;
	}

It has worked (sort of), theres multiple quirks, where it returns true, even if the point im checking is not within the shape, which unfortunately makes it very inconsistent.

If any of you have some prior experience with creating a custom contains method, or can figure out where my attempt at copying the implemented version went wrong, please dont hesitate, any help will be highly appreciated!

Heres my shape initialization (if it can be of any help):

shape = pA.createShape();
	    shape.beginShape();
	    	shape.noStroke();
	    	shape.textureMode(PConstants.NORMAL);
	    	shape.texture(texture);
	        for (int i = 0; i < vertices.length-1; i++) {
	        	shape.vertex(origin.x + vertices[i].x, origin.y + vertices[i].y, Math.signum(vertices[i].x + Math.abs(vertices[i].x)), Math.signum(vertices[i].y + Math.abs(vertices[i].y)));
	        }
	    shape.endShape(PApplet.CLOSE);

If you need more information, feel free to ask :slight_smile:

Best
Frinkle

I found a valid solution, following this.

public boolean contains(float x, float y) {
		Point2D test = new Point2D.Float(x,y);
		int i;
		int j;
		boolean result = false;
		for (i = 0, j = shape.getVertexCount() - 1; i < shape.getVertexCount(); j = i++) {
		  if ((shape.getVertex(i).y > test.getY()) != (shape.getVertex(j).y > test.getY()) &&
		      (test.getX() < (shape.getVertex(j).x - shape.getVertex(i).x) * (test.getY() - shape.getVertex(i).y) / (shape.getVertex(j).y-shape.getVertex(i).y) + shape.getVertex(i).x)) {
		    result = !result;
		  }
		    
		}
		return result;
	}