Two rectangles, A and B overlap if either
- A is entirely inside B
-
- All corners of A are in B
- B is entirely inside A
-
- All corners of B are in A
- At least one side of A intersects with at least one side of B
(These conditions are actually sufficient for any convex shapes, but only easy to calculate for convex polygons, and particularly so for rectangles)
Thus, if we can turn the GUI objects into rectangles, then we can solve this problem.
Defining a rectangle by four corners, a b c
and d
(as Vector2s) we can write a simple function for determining if another point x
is in the interior of the rectangle.
01 | function inInterior(a,b,c,d, x) |
04 | local alongBA = BA.unit:dot(x - a) |
05 | local alongDA = DA.unit:dot(x - a) |
07 | alongBA > = 0 and alongDA > = 0 |
08 | and alongBA < = BA.magnitude |
09 | and alongDA < = DA.magnitude |
The idea of this function is that using dot products, we define x
in terms of the cartesian space defined by the rectangle's orientation, and then check that the coordinate is within the axis-aligned rectangle defined by the rectangle's width and height
Now, to determine if two segments are intersecting, we can check that each endpoint is on the opposite side of the other segment.
03 | function opposite(a,b, c,d) |
04 | local N = Vector 2. new( b.y - a.y, a.x - b.x ) |
10 | return DA + CA < math.abs(DA) + math.abs(CA) |
So to check if two segments are intersecting, just apply this:
1 | function intersecting(a,b,c,d) |
3 | opposite(a,b, c,d) and opposite(c,d, a,b) |
To determine if two rectangles are intersecting, then, we just need to check this pairwise on all of the sides:
1 | function rectangleIntersecting(a,b,c,d, e,f,g,h) |
To determine if one rectangle is in the other,
1 | function rectangleInside(a, b, c, d, e, f, g, h) |
4 | and inInterior(a,b,c,d, f) |
5 | and inInterior(a,b,c,d, g) |
6 | and inInterior(a,b,c,d, h) |
Putting it all together, to determine if two rectangles are colliding,
1 | function rectanglesColliding(a,b,c,d, e,f,g,h) |
3 | rectangleInside(a,b,c,d, e,f,g,h) |
4 | or rectangleInside(e,f,g,h, a,b,c,d) |
8 | return rectangleIntersecting(a,b,c,d, e,f,g,h) |
The only remaining step is to get the corners from a GUI. This is relatively easy if the rectangle is given in terms of offset only.
(Use something like x +- w/2 * cos(rotation)
and y +- h/2 * sin(rotation)
)
For now, though, I will leave that as an exercise to the reader.