barriers: Replace complex intersection test with simpler math

Since barriers are axis-aligned, we can do the intersection test with
simple interpolation rather than line-segment intersection. This also
helps us out in the future when we want the barriers to extend to be
rays and lines rather than just segments.

Signed-off-by: Jasper St. Pierre <jstpierre@mecheye.net>
Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
This commit is contained in:
Jasper St. Pierre 2012-11-27 14:29:52 -05:00 committed by Peter Hutterer
parent 6401317bdc
commit 0a5a0d7c24

View File

@ -140,6 +140,9 @@ barrier_is_blocking_direction(const struct PointerBarrier * barrier,
return (barrier->directions & direction) != direction;
}
#define T(v, a, b) (((float)v) - (a)) / ((b) - (a))
#define F(t, a, b) ((t) * ((a) - (b)) + (a))
/**
* Test if the movement vector x1/y1 x2/y2 is intersecting with the
* barrier. A movement vector with the startpoint or endpoint adjacent to
@ -157,67 +160,40 @@ BOOL
barrier_is_blocking(const struct PointerBarrier * barrier,
int x1, int y1, int x2, int y2, double *distance)
{
BOOL rc = FALSE;
float ua, ub, ud;
int dir = barrier_get_direction(x1, y1, x2, y2);
/* Algorithm below doesn't handle edge cases well, hence the extra
* checks. */
if (barrier_is_vertical(barrier)) {
/* handle immediate barrier adjacency, moving away */
if (dir & BarrierPositiveX && x1 == barrier->x1)
float t, y;
t = T(barrier->x1, x1, x2);
if (t < 0 || t > 1)
return FALSE;
if (dir & BarrierNegativeX && x1 == (barrier->x1 - 1))
/* Edge case: moving away from barrier. */
if (x2 > x1 && t == 0)
return FALSE;
/* startpoint adjacent to barrier, moving towards -> block */
if (dir & BarrierPositiveX && x1 == (barrier->x1 - 1) && y1 >= barrier->y1 && y1 <= barrier->y2) {
*distance = 0;
return TRUE;
}
if (dir & BarrierNegativeX && x1 == barrier->x1 && y1 >= barrier->y1 && y1 <= barrier->y2) {
*distance = 0;
return TRUE;
}
y = F(t, y1, y2);
if (y < barrier->y1 || y > barrier->y2)
return FALSE;
*distance = sqrt((pow(y - y1, 2) + pow(barrier->x1 - x1, 2)));
return TRUE;
}
else {
/* handle immediate barrier adjacency, moving away */
if (dir & BarrierPositiveY && y1 == barrier->y1)
float t, x;
t = T(barrier->y1, y1, y2);
if (t < 0 || t > 1)
return FALSE;
if (dir & BarrierNegativeY && y1 == (barrier->y1 - 1))
/* Edge case: moving away from barrier. */
if (y2 > y1 && t == 0)
return FALSE;
/* startpoint adjacent to barrier, moving towards -> block */
if (dir & BarrierPositiveY && y1 == (barrier->y1 - 1) && x1 >= barrier->x1 && x1 <= barrier->x2) {
*distance = 0;
return TRUE;
}
if (dir & BarrierNegativeY && y1 == barrier->y1 && x1 >= barrier->x1 && x1 <= barrier->x2) {
*distance = 0;
return TRUE;
}
x = F(t, x1, x2);
if (x < barrier->x1 || x > barrier->x2)
return FALSE;
*distance = sqrt((pow(x - x1, 2) + pow(barrier->y1 - y1, 2)));
return TRUE;
}
/* not an edge case, compute distance */
ua = 0;
ud = (barrier->y2 - barrier->y1) * (x2 - x1) - (barrier->x2 -
barrier->x1) * (y2 - y1);
if (ud != 0) {
ua = ((barrier->x2 - barrier->x1) * (y1 - barrier->y1) -
(barrier->y2 - barrier->y1) * (x1 - barrier->x1)) / ud;
ub = ((x2 - x1) * (y1 - barrier->y1) -
(y2 - y1) * (x1 - barrier->x1)) / ud;
if (ua < 0 || ua > 1 || ub < 0 || ub > 1)
ua = 0;
}
if (ua > 0 && ua <= 1) {
double ix = barrier->x1 + ua * (barrier->x2 - barrier->x1);
double iy = barrier->y1 + ua * (barrier->y2 - barrier->y1);
*distance = sqrt(pow(x1 - ix, 2) + pow(y1 - iy, 2));
rc = TRUE;
}
return rc;
}
#define HIT_EDGE_EXTENTS 2