2D Line Intersection

From AntiHax

In the HalfLife engine, a lot of detections and counter measures require the use of TraceLines. This function is quite expensive however and will cause performance issues when used in large numbers.


To counter this, I have found the use of the old line intersection algorithm to be quite handy in to perform a fast 2d test. It is very important to remember that using this algorithm in 3D means it tests infinitely tall.


/* Faster Line Segment Intersection   */
/* Franklin Antonio                   */

/* return values */
#define DONT_INTERSECT 0
#define DO_INTERSECT   1
#define PARALLEL       2


/* The SAME_SIGNS macro assumes arithmetic where the exclusive-or    */
/* operation will work on sign bits.  This works for twos-complement,*/
/* and most other machine arithmetic.                                */
#define SAME_SIGNS( a, b ) \
	(((long) ((unsigned long) a ^ (unsigned long) b)) >= 0 )

// [Danni] Converted to REAL. The line intersection algorithm determines if
//         two lines collide by drawing a boundingbox around the two lines
//         and identify if any points are within another's box.

//         This is a constant time function, hence we can quickly test if 
//         a trace will hit a player in 2D space by stating players bbox 
//         widths are one line and the clients view angle is another.

//         Forewarning! Bounding boxes do not always fully engulf the model.
//         My personal preference is to make the bbox 3 times it's normal size.

int lines_intersect(float x1,float y1,float x2,float y2,float x3,float y3,
			float x4,float y4,float *x,float *y) {

	float Ax,Bx,Cx,Ay,By,Cy,d,e,f,num,offset;
	float x1lo,x1hi,y1lo,y1hi;

	Ax = x2-x1;
	Bx = x3-x4;

	if(Ax<0) {					/* X bound box test*/
		x1lo=(float)x2; x1hi=(float)x1;
	} else {
		x1hi=(float)x2; x1lo=(float)x1;
	}
	
	if(Bx>0) {
		if(x1hi < (float)x4 || (float)x3 < x1lo) return DONT_INTERSECT;
	} else {
		if(x1hi < (float)x3 || (float)x4 < x1lo) return DONT_INTERSECT;
	}

	Ay = y2-y1;
	By = y3-y4;

	if(Ay<0) {					/* Y bound box test*/
		y1lo=(float)y2; y1hi=(float)y1;
	} else {
		y1hi=(float)y2; y1lo=(float)y1;
	}

	if(By>0) {
		if(y1hi < (float)y4 || (float)y3 < y1lo) return DONT_INTERSECT;
	} else {
		if(y1hi < (float)y3 || (float)y4 < y1lo) return DONT_INTERSECT;
	}


Cx = x1-x3;
Cy = y1-y3;
d = By*Cx - Bx*Cy;					/* alpha numerator*/
f = Ay*Bx - Ax*By;					/* both denominator*/
if(f>0) {						/* alpha tests*/
  if(d<0 || d>f) return DONT_INTERSECT;
  } else {
  if(d>0 || d<f) return DONT_INTERSECT;
  }

e = Ax*Cy - Ay*Cx;					/* beta numerator*/
if(f>0) {						/* beta tests*/
  if(e<0 || e>f) return DONT_INTERSECT;
  } else {
  if(e>0 || e<f) return DONT_INTERSECT;
  }

/*compute intersection coordinates*/

if(f==0) return PARALLEL;
num = d*Ax;						/* numerator */
offset = SAME_SIGNS(num,f) ? f/2 : -f/2;		/* round direction*/
*x = x1 + (num+offset) / f;				/* intersection x */

num = d*Ay;
offset = SAME_SIGNS(num,f) ? f/2 : -f/2;
*y = y1 + (num+offset) / f;				/* intersection y */

return DO_INTERSECT;
}


bool UTIL_EntIntersect( Vector &vecStart, Vector &vecEnd,edict_t* source, 
			edict_t* target, int multiplier)
{
	
	int targetnum = ENTINDEX(target);
	int sourcenum = ENTINDEX(source);

	Vector mins = target->v.origin + (target->v.mins * multiplier);
	Vector maxs = target->v.origin + (target->v.maxs * multiplier);
	
	float x,y;
	if (lines_intersect(mins.x, mins.y, maxs.x, maxs.y,vecStart.x, vecStart.y, 
				vecEnd.x, vecEnd.y, &x, &y)) {
		return true;
	}
	return false;
}

An example of this code in use:

int scan_Simple (edict_t *source, edict_t *target) {
	
	UTIL_MakeVectors( source->v.v_angle ); 
	
	// A point far away in the aiming direction.
	Vector vecSrc = GetGunPosition(source);
	Vector vecDest = vecSrc + gpGlobals->v_forward * 8196;
	
	TraceResult tr;

	// Do a quick BBox 2d collision with 3x BBox
	if (UTIL_EntIntersect(vecDest, vecSrc, source, target, 3)) {
		// We hit em, now do a more expensive hitscan.
		TraceLine(vecSrc, vecDest, source, &tr); 
		//......
	}
}

References

Faster Line Segment Intersection