Verifying Mouse Position Within Triangle - Python

884 views Asked by At

**This is part of programming course, and the modules we are asked to use are not generally used otherwise. I will do my best to explain my code (though it is pretty self-explanatory)

EDIT: If you're curious, I must use Myro, and the code I use to get mouse click coordinates is: mouseX, mouseY = win.getMouse() # "win" refers to a Window object

I am creating "buttons" that when clicked with perform some form of action. I have three different shapes I use: rectangles, circles, and triangles.

For the rectangle:

# The numbers used in this example are the coordinates on an XY plane
# mouseX refers to the X coord of a recent mouse click; mouseY on the Y axis
if mouseX >= 70 and mouseX <= 120:
        if mouseY >= 10 and mouseY <= 35:
            print("rectangle button clicked")

For the circle, I got help from this question, and ended up with this code:

# mouseX and mouseY, same as rectangle example
if sqrt((mouseX-660)**2 + (mouseY-270)**2) <= 30:
        print("circle button clicked")

The last shape I am attempting to work with is a triangle. I'm not sure how I would go about ensuring mouseX and mouseY are within the coords for the shape. I'm rather terrible at math, but I'm assuming there is some formula that can be used (e.g. the circle example). Thanks much.

3

There are 3 answers

0
Aaron On BEST ANSWER

I found the answer as part of this question, written in C (I believe). I've rewritten the code into Python and will leave it here for others.

The code:

def sign(p1, p2, p3): #all Points
    return (p1.getX() - p3.getX()) * (p2.getY() - p3.getY()) - (p2.getX() - p3.getX()) * (p1.getY() - p3.getY())

def inTriangle(pt, v1, v2, v3): #all Points, pt = mouse, v = triangle vertex
    b1 = sign(pt, v1, v2) < 0.0
    b2 = sign(pt, v2, v3) < 0.0
    b3 = sign(pt, v3, v1) < 0.0
    return ((b1 == b2) and (b2 == b3))

Testing:

mouseX, mouseY = win.getMouse() #Myro module, win = Window object
a = Point(662,200)
b = Point(1,1)
print(inTriangle(a, Point(661,156), Point(633,217), Point(688,218))) #TRUE!
print(inTriangle(b, Point(661,156), Point(633,217), Point(688,218))) #FALSE!
triangle = [Point(661, 156), Point(633, 217), Point(688, 218)]
if inTriangle(Point(mouseX, mouseY), triangle[0], triangle[1], triangle[2]):
    print("clicked up")
1
pacholik On

Using cross products - has to be all positive or all negative.

def line_point_pos(start, end, point):
    """right: negative, left: positive, onedge: zero"""
    A = end[0]-start[0], end[1]-start[1]
    B = point[0]-start[0], point[1]-start[1]
    # return z-component of cross product
    return A[0]*B[1] - A[1]*B[0]

def in_polygon(vertices, point, onedges=True):
    """find out if the point is inside the polygon"""
    edges = zip(vertices, vertices[1:] + [vertices[0]])

    zs = [line_point_pos(s, e, point) for s,e in edges]
    if 0 in zs:
        return onedges
    return all(z>0 for z in zs) or all(z<0 for z in zs)

triangle = [(1, 1), (10, 1), (5, 10)]
a = 5, 10
print(in_polygon(triangle, a))      # True
3
AudioBubble On

Define your triangle and mouse coordinates as such:

enter image description here

And define the 2D cross-product of two vectors u and v:

enter image description here

This is positive if the vector u lies on the right of v, and negative if on the left.

So the four conditions you are looking for are:

enter image description here

... for a mouse hit.

(An alternative method involving the dot-product is available, but it involves square roots which are rather inefficient)

Apologies for the lack of code - I hate Python (yeah, I said it). But this should provide the math you need to implement it.