For example regular shapes (ovals) and non regular shapes - polygons etc. How would you go about detecting mouseEnter/Exit and mouseUp/Down events?
Math, you can use the Point in polygon aproach.
Or, you can implement a Flood Fill, starting from your point, if the flood touches the edge, the point was outside.
for circles the distance from center to mouse click.
or rebuild the non-rectangular with smaller rectangle areas if you not need a perfect shape.
Math if you can.
Alternatively, if the shape is in a picture with an alpha channel you could possibly hit-test on the bounding rectangle and then check if the pixel is not transparent.
For a polygon, there are several methods to test whether a given point is inside the shape (see there: https://en.wikipedia.org/wiki/Point_in_polygon).
One of them is to “walk” from the leftmost side of the page (for x=0 to Point.x) in a horizontal way (y=Point.Y) and check, for each step point, whether you cross a side of the polygon (there are functions to test whether a given point is on a given line). You count how many times you’ve crossed a side of the polygon from 0 to the given point’s x coordinate: if it’s an odd number, the point is in the polygon; otherwise, it’s out.
I’m using this in one of my apps. The concept may look to be a slow approach (loop thru all pixels on the line and check for each side of the polygon), but in reality, it’s really efficient.
If you are drawing the shapes yourself in a canvas, I use a quite simple approch. I subclass the canvas and add a property “Shape as picture” to it. When I draw a polygon, an oval or anything else into the canvas, I draw it also to the shape, but always in pure black (&c00000000).
In the MouseDown event I do the following:
if Shape.RGBSurface.Pixel(x,y).Red=0 then Return true Return False
If you use it for a custom control with a specific background shape (like a button with circular end caps) you can just draw the shape as filled surface once at the beginning of you paint event into your shape property and you can then even draw transparent or semi-transparent content into your canvas.
To extend this concept, you could use a different color on the special picture for each distinct object, so you can check the color to know which one was clicked. (be careful to draw on the special picture without anti-aliasing)
Thank you all for your replies - very useful. The simple approach by Pascal is interesting as potentially it can accommodate any shape. I presume you hide the black mask underneath the image you want to test and then let mouse events fall through to test the colour of the mask. This would be a little difficult in my application as I am using transparent objects.
In the end I’ve done a modified version of that suggested by Arnoud. Instead of iterating along a line and testing to see if the point lies on a line, it follows a simpler approach of algebraically generating intersection points of pairs of lines. I think the algebra is easier than determining if each point is on a line which is subject to rounding errors.
- IF within the rectangle defined by the polygon THEN
- Draw lines from the mouse point North, South, East and West to the edges of the rectangle.
- Count the number of lines of the polygon that get intersected by the NSEW lines respectively. Use simple geometry to solve if the lines intersect within their range.
- IF any of the counts are even, THEN the mouse point is outside the polygon. ELSE all are odd, and it is inside.
It seems effective.
Happy to share code if needed.
Make sure you handle cases when the polygon bleeds off an edge. If any of your “lines” hits the edge and it’s still within the polygon, your “all odd” calculation will fail.
Yes, thanks, I first check the extremes of the polygon prior to setting the limits of the lines.
I love puzzles and thinkers, but is there any programmatic reason testing the alpha of the clicked pixel would be slower or inefficient?
I ask because I may be looking at click detection soon and am curious about both sides of this idea.
From my point of view no, it should be the fastest, but it won’t work if you want to detect clicks inside transparent objects.
What about having another image with a simple one bit mask, just loaded but not shown, when the user clicks, you just take the coordinates and check against the mask, not the visible image.
Been a long time since I have been able to create a 1-bit depth image in Xojo.
That and a 256 color indexed image would still be very useful to me.
But Apple, Xojo and most of the rest of the world seem to feel that I need 32 bits per pixel.
I dont need to waste that much memory