comp20003-project02/docs/intersection.c

218 lines
6.9 KiB
C

enum intersectType;
enum intersectType {
DOESNT_INTERSECT = 0, // Doesn't intersect
INTERSECT = 1, // Intersects
SAME_LINE_OVERLAP = 2, // Lines are the same
ENDS_OVERLAP = 3 // Intersects at exactly one point (endpoint)
};
/*
This intersection is based on code by Joseph O'Rourke and is provided for use in
COMP20003 Assignment 2.
The approach for intersections is:
- Use the bisector to construct a finite segment and test it against the half-edge.
- Use O'Rourke's segseg intersection (https://hydra.smith.edu/~jorourke/books/ftp.html)
to check if the values overlap.
*/
/*
Generates a segment with each end at least minLength away in each direction
from the bisector midpoint. Returns 1 if b intersects the given half-edge
on this segment, 0 otherwise. Sets the intersection point to the given x, y
positions.
*/
/* Returns -1, 0 or 1, based on the area enclosed by the three points. 0 corresponds
to no area enclosed.
*/
int areaSign(double sx, double sy, double ex, double ey, double x, double y);
/* Returns 1 if the point (x, y) is in the line from s(x, y) to e(x, y), 0 otherwise. */
int collinear(double sx, double sy, double ex, double ey, double x, double y);
int collinear(double sx, double sy, double ex, double ey, double x, double y){
/* Work out area of parallelogram - if it's 0, points are in the same line. */
if (areaSign(sx, sy, ex, ey, x, y) == 0){
return 1;
} else {
return 0;
}
}
int areaSign(double sx, double sy, double ex, double ey, double x, double y){
double areaSq;
/* |AB x AC|^2, squared area */
/* See https://mathworld.wolfram.com/CrossProduct.html */
areaSq = (ex - sx) * (y - sy) -
(x - sx) * (ey - sy);
if(areaSq > 0.0){
return 1;
} else if(areaSq == 0.0){
return 0;
} else {
return -1;
}
}
/* Returns 1 if point (x, y) is between (sx, sy) and (se, se) */
int between(double sx, double sy, double ex, double ey, double x, double y);
int between(double sx, double sy, double ex, double ey, double x, double y){
if(sx != ex){
/* If not vertical, check whether between x. */
if((sx <= x && x <= ex) || (sx >= x && x >= ex)){
return 1;
} else {
return 0;
}
} else {
/* Vertical, so can't check _between_ x-values. Check y-axis. */
if((sy <= y && y <= ey) || (sy >= y && y >= ey)){
return 1;
} else {
return 0;
}
}
}
enum intersectType parallelIntersects(double heSx, double heSy, double heEx, double heEy,
double bSx, double bSy, double bEx, double bEy, double *x, double *y);
enum intersectType parallelIntersects(double heSx, double heSy, double heEx, double heEy,
double bSx, double bSy, double bEx, double bEy, double *x, double *y){
if(!collinear(heSx, heSy, heEx, heEy, bSx, bSy)){
/* Parallel, no intersection so don't set (x, y) */
return DOESNT_INTERSECT;
}
/* bS between heS and heE */
if(between(heSx, heSy, heEx, heEy, bSx, bSy)){
*x = bSx;
*y = bSy;
return SAME_LINE_OVERLAP;
}
/* bE between heS and heE */
if(between(heSx, heSy, heEx, heEy, bEx, bEy)){
*x = bEx;
*y = bEy;
return SAME_LINE_OVERLAP;
}
/* heS between bS and bE */
if(between(bSx, bSy, bEx, bEy, heSx, heSy)){
*x = heSx;
*y = heSy;
return SAME_LINE_OVERLAP;
}
/* heE between bS and bE */
if(between(bSx, bSy, bEx, bEy, heEx, heEy)){
*x = heEx;
*y = heEy;
return SAME_LINE_OVERLAP;
}
return DOESNT_INTERSECT;
}
enum intersectType intersects( ... , double *x, double *y);
enum intersectType intersects( ... , double *x, double *y){
/* Half-edge x, y pair */
double heSx = ...;
double heSy = ...;
double heEx = ...;
double heEy = ...;
/* Bisector x, y pair */
double bSx = ...;
double bSy = ...;
double bEx = ...;
double bEy = ...;
/* Parametric equation parameters */
double t1;
double t2;
/* Numerators for X and Y coordinate of intersection. */
double numeratorX;
double numeratorY;
/* Denominators of intersection coordinates. */
double denominator;
/*
See http://www.cs.jhu.edu/~misha/Spring20/15.pdf
for explanation and intuition of the algorithm here.
x_1 = heSx, y_1 = heSy | p_1 = heS
x_2 = heEx, y_2 = heEy | q_1 = heE
x_3 = bSx , y_3 = bSy | p_2 = bS
x_4 = bEx , y_4 = bEy | q_2 = bE
----------------------------------------
So the parameters t1 and t2 are given by:
| t1 | | heEx - heSx bSx - bEx | -1 | bSx - heSx |
| | = | | | |
| t2 | | heEy - heSy bSy - bEy | | bSy - heSy |
Hence:
| t1 | 1 | bSy - bEy bEx - bSx | | bSx - heSx |
| | = --------- | | | |
| t2 | ad - bc | heSy - heEy heEx - heSx | | bSy - heSy |
where
a = heEx - heSx
b = bSx - bEx
c = heEy - heSy
d = bSy - bEy
*/
/* Here we calculate ad - bc */
denominator = heSx * (bEy - bSy) +
heEx * (bSy - bEy) +
bEx * (heEy - heSy) +
bSx * (heSy - heEy);
if(denominator == 0){
/* In this case the two are parallel */
return parallelIntersects(heSx, heSy, heEx, heEy, bSx, bSy, bEx, bEy, x, y);
}
/*
Here we calculate the top row.
| bSy - bEy bEx - bSx | | bSx - heSx |
| | | |
| | | bSy - heSy |
*/
numeratorX = heSx * (bEy - bSy) +
bSx * (heSy - bEy) +
bEx * (bSy - heSy);
/*
Here we calculate the bottom row.
| | | bSx - heSx |
| | | |
| heSy - heEy heEx - heSx | | bSy - heSy |
*/
numeratorY = -(heSx * (bSy - heEy) +
heEx * (heSy - bSy) +
bSx * (heEy - heSy));
/* Use parameters to convert to the intersection point */
t1 = numeratorX/denominator;
t2 = numeratorY/denominator;
*x = heSx + t1 * (heEx - heSx);
*y = heSy + t1 * (heEy - heSy);
/* Make final decision - if point is on segments, parameter values will be
between 0, the start of the line segment, and 1, the end of the line segment.
*/
if (0.0 < t1 && t1 < 1.0 && 0.0 < t2 && t2 < 1.0){
return INTERSECT;
} else if(t1 < 0.0 || 1.0 < t1 || t2 < 0.0 || 1.0 < t2){
/* s or t outside of line segment. */
return DOESNT_INTERSECT;
} else {
/*
((numeratorX == 0) || (numeratorY == 0) ||
(numeratorX == denominator) || (numeratorY == denominator))
*/
return ENDS_OVERLAP;
}
}