218 lines
6.9 KiB
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;
|
|
}
|
|
}
|