2021-09-08 21:48:00 +10:00
|
|
|
/* dcel.c
|
|
|
|
*
|
|
|
|
* Created by Rory Healy (healyr@student.unimelb.edu.au)
|
|
|
|
* Created on 25th August 2021
|
2021-09-15 00:06:04 +10:00
|
|
|
* Last modified 14th September 2021
|
2021-09-08 21:48:00 +10:00
|
|
|
*
|
2021-09-15 00:06:04 +10:00
|
|
|
* Contains functions for the DCEL data structure and related data structures
|
|
|
|
* (including points, intersections, and bisectors).
|
2021-09-08 21:48:00 +10:00
|
|
|
*
|
2021-09-11 22:47:44 +10:00
|
|
|
* Contains several functions and structures from Grady Fitzpatrick's Base
|
|
|
|
* Code on the Ed Discussion Forum.
|
|
|
|
*
|
2021-09-08 21:48:00 +10:00
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef DCEL_HEADER
|
|
|
|
#include "dcel.h"
|
|
|
|
#endif
|
|
|
|
|
2021-09-15 00:06:04 +10:00
|
|
|
#define INITIALVERTICES 4
|
|
|
|
#define INITIALEDGES 4
|
|
|
|
#define INITIALFACES 1
|
|
|
|
#define NOVERTEX (-1)
|
|
|
|
#define NOEDGE (-1)
|
2021-09-11 22:47:44 +10:00
|
|
|
|
2021-09-15 00:06:04 +10:00
|
|
|
#define DIR_UNDECIDED (0)
|
|
|
|
#define INSIDE (1)
|
|
|
|
#define OUTSIDE (-1)
|
|
|
|
#define NODIAMETER (-1)
|
2021-09-11 22:47:44 +10:00
|
|
|
|
2021-09-15 00:06:04 +10:00
|
|
|
/* ----------------------- My points functions start ----------------------- */
|
|
|
|
vertex_t *newPoint() {
|
|
|
|
vertex_t *newPoint = malloc(sizeof(*newPoint));
|
|
|
|
checkNullPointer(newPoint);
|
|
|
|
return newPoint;
|
2021-09-13 12:27:17 +10:00
|
|
|
}
|
|
|
|
|
2021-09-15 00:06:04 +10:00
|
|
|
vertex_t *getAPoint(double x, double y) {
|
|
|
|
vertex_t *point = newPoint();
|
|
|
|
point->x = x;
|
|
|
|
point->y = y;
|
|
|
|
return point;
|
2021-09-13 12:27:17 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
vertex_t **readPoints(vertex_t **points, FILE *pointsFile, int *numPoints) {
|
|
|
|
/* Initial size of buffer is 1 as getline() reallocs as needed */
|
|
|
|
size_t lineBufferSize = 1;
|
|
|
|
|
|
|
|
/* Stores the current line from the points file */
|
|
|
|
char *lineBuffer = malloc(lineBufferSize * sizeof(*lineBuffer));
|
|
|
|
checkNullPointer(lineBuffer);
|
|
|
|
|
|
|
|
/* Assuming that pointsFile is valid, there must be at least two points */
|
|
|
|
int maxSizePoints = 2;
|
|
|
|
|
|
|
|
while (getline(&lineBuffer, &lineBufferSize, pointsFile) > 0) {
|
|
|
|
/* Ensure there is enough space in the points array */
|
|
|
|
if (*numPoints == maxSizePoints) {
|
|
|
|
maxSizePoints *= 2;
|
|
|
|
vertex_t **temp = realloc(points, maxSizePoints * sizeof(*points));
|
|
|
|
checkNullPointer(temp);
|
|
|
|
points = temp;
|
|
|
|
}
|
|
|
|
|
|
|
|
double xCoordinateA, yCoordinateA, xCoordinateB, yCoordinateB;
|
|
|
|
sscanf(lineBuffer, "%lf %lf %lf %lf", &xCoordinateA, &yCoordinateA, \
|
|
|
|
&xCoordinateB, &yCoordinateB);
|
|
|
|
|
|
|
|
points[*numPoints] = malloc(sizeof(*points[*numPoints]));
|
|
|
|
points[*numPoints]->x = xCoordinateA;
|
|
|
|
points[*numPoints]->y = yCoordinateA;
|
|
|
|
*numPoints += 1;
|
|
|
|
|
|
|
|
points[*numPoints] = malloc(sizeof(*points[*numPoints]));
|
|
|
|
points[*numPoints]->x = xCoordinateB;
|
|
|
|
points[*numPoints]->y = yCoordinateB;
|
|
|
|
*numPoints += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
free(lineBuffer);
|
|
|
|
return points;
|
2021-09-11 22:47:44 +10:00
|
|
|
}
|
|
|
|
|
2021-09-15 00:06:04 +10:00
|
|
|
void freePoints(vertex_t **points, int numPoints) {
|
|
|
|
if (!points) {
|
|
|
|
return;
|
|
|
|
}
|
2021-09-11 22:47:44 +10:00
|
|
|
|
2021-09-15 00:06:04 +10:00
|
|
|
for (int i = 0; i < numPoints; i++) {
|
|
|
|
if (points[i]) {
|
|
|
|
free(points[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
free(points);
|
|
|
|
}
|
2021-09-11 22:47:44 +10:00
|
|
|
|
2021-09-15 00:06:04 +10:00
|
|
|
double distPoints(vertex_t *pointA, vertex_t *pointB) {
|
|
|
|
double a = pointB->x - pointA->x;
|
|
|
|
double b = pointB->y - pointA->y;
|
|
|
|
return sqrt(a * a + b * b);
|
|
|
|
}
|
2021-09-11 22:47:44 +10:00
|
|
|
|
2021-09-15 00:06:04 +10:00
|
|
|
/* ------------------------ My points functions end ------------------------ */
|
2021-09-11 22:47:44 +10:00
|
|
|
|
2021-09-15 00:06:04 +10:00
|
|
|
/* -------------------- My intersection functions start -------------------- */
|
2021-09-11 22:47:44 +10:00
|
|
|
|
2021-09-15 00:06:04 +10:00
|
|
|
intersection_t *newIntersection() {
|
|
|
|
intersection_t *intersection = malloc(sizeof(*intersection));
|
|
|
|
checkNullPointer(intersection);
|
2021-09-11 22:47:44 +10:00
|
|
|
|
2021-09-15 00:06:04 +10:00
|
|
|
intersection->fromPoint = malloc(sizeof(*intersection->fromPoint));
|
|
|
|
checkNullPointer(intersection->fromPoint);
|
|
|
|
|
|
|
|
intersection->toPoint = malloc(sizeof(*intersection->toPoint));
|
|
|
|
checkNullPointer(intersection->toPoint);
|
|
|
|
|
|
|
|
return intersection;
|
2021-09-11 22:47:44 +10:00
|
|
|
}
|
|
|
|
|
2021-09-15 00:06:04 +10:00
|
|
|
intersection_t *getAnIntersection(intersection_t *intersection, DCEL_t *dcel, \
|
|
|
|
bisector_t *bisector, int face, int minLength) {
|
|
|
|
|
|
|
|
/* Intersection coordinates */
|
|
|
|
double x, y;
|
|
|
|
|
|
|
|
/* Flag is raised when first intersection is found */
|
|
|
|
int isIntersectionFound = 0;
|
|
|
|
|
|
|
|
halfEdge_t *startHE = (dcel->faces)[face].start;
|
|
|
|
halfEdge_t *currentHE = startHE;
|
|
|
|
int startVertex = startHE->startVertex;
|
|
|
|
int first = 1;
|
|
|
|
|
|
|
|
/* Loop through the face until the starting vertex is reached */
|
|
|
|
while (first || currentHE->startVertex != startVertex) {
|
|
|
|
enum intersectType typeOfIntersection = \
|
|
|
|
intersects(currentHE, bisector, dcel, minLength, &x, &y);
|
|
|
|
|
|
|
|
switch (typeOfIntersection) {
|
|
|
|
case INTERSECT:
|
|
|
|
case SAME_LINE_OVERLAP:
|
|
|
|
case ENDS_OVERLAP:
|
|
|
|
if (!isIntersectionFound) {
|
|
|
|
/* First point of intersection */
|
|
|
|
isIntersectionFound = 1;
|
|
|
|
intersection = newIntersection();
|
|
|
|
intersection->fromPoint->x = x;
|
|
|
|
intersection->fromPoint->y = y;
|
|
|
|
intersection->fromEdge = currentHE->edge;
|
|
|
|
} else {
|
|
|
|
/* Second point of intersection */
|
|
|
|
intersection->toPoint->x = x;
|
|
|
|
intersection->toPoint->y = y;
|
|
|
|
intersection->toEdge = currentHE->edge;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case DOESNT_INTERSECT:
|
|
|
|
default:
|
|
|
|
break;
|
2021-09-11 22:47:44 +10:00
|
|
|
}
|
2021-09-15 00:06:04 +10:00
|
|
|
currentHE = currentHE->next;
|
|
|
|
first = 0;
|
2021-09-11 22:47:44 +10:00
|
|
|
}
|
2021-09-15 00:06:04 +10:00
|
|
|
|
|
|
|
return intersection;
|
2021-09-11 22:47:44 +10:00
|
|
|
}
|
2021-09-15 00:06:04 +10:00
|
|
|
|
|
|
|
intersection_t **getIntersections(intersection_t **intersections, \
|
|
|
|
int *numIntersections, bisector_t **bisectors, int numBisectors, \
|
|
|
|
DCEL_t *dcel, int face, double minLength) {
|
2021-09-11 22:47:44 +10:00
|
|
|
|
2021-09-15 00:06:04 +10:00
|
|
|
int maxSizeIntersections = 1;
|
|
|
|
|
|
|
|
for (int i = 0; i < numBisectors; i++) {
|
|
|
|
intersection_t *intersection = NULL;
|
|
|
|
intersection = getAnIntersection(intersection, dcel, bisectors[i], \
|
|
|
|
face, minLength);
|
|
|
|
|
|
|
|
/* If there is an intersection, add it to the array */
|
|
|
|
if (intersection != NULL) {
|
|
|
|
/* Ensure there is enough space in the array */
|
|
|
|
if (*numIntersections == maxSizeIntersections) {
|
|
|
|
maxSizeIntersections *= 2;
|
|
|
|
intersection_t **temp = realloc(intersections, \
|
|
|
|
maxSizeIntersections * sizeof(*intersections));
|
|
|
|
checkNullPointer(temp);
|
|
|
|
intersections = temp;
|
|
|
|
}
|
|
|
|
|
|
|
|
intersections[*numIntersections] = intersection;
|
|
|
|
*numIntersections += 1;
|
|
|
|
}
|
2021-09-11 22:47:44 +10:00
|
|
|
}
|
2021-09-15 00:06:04 +10:00
|
|
|
|
|
|
|
return intersections;
|
2021-09-11 22:47:44 +10:00
|
|
|
}
|
|
|
|
|
2021-09-15 00:06:04 +10:00
|
|
|
void freeIntersections(intersection_t **intersections, int numIntersections) {
|
|
|
|
if (!intersections) {
|
|
|
|
return;
|
2021-09-11 22:47:44 +10:00
|
|
|
}
|
|
|
|
|
2021-09-15 00:06:04 +10:00
|
|
|
for (int i = 0; i < numIntersections; i++) {
|
|
|
|
if (intersections[i]->fromPoint) {
|
|
|
|
free(intersections[i]->fromPoint);
|
|
|
|
}
|
|
|
|
if (intersections[i]->toPoint) {
|
|
|
|
free(intersections[i]->toPoint);
|
|
|
|
}
|
|
|
|
if (intersections[i]) {
|
|
|
|
free(intersections[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
free(intersections);
|
2021-09-11 22:47:44 +10:00
|
|
|
}
|
|
|
|
|
2021-09-15 00:06:04 +10:00
|
|
|
/* --------------------- My intersection functions end --------------------- */
|
2021-09-13 12:27:17 +10:00
|
|
|
|
2021-09-15 00:06:04 +10:00
|
|
|
/* ---------------------- My bisector functions start ---------------------- */
|
|
|
|
|
|
|
|
bisector_t *newBisector() {
|
|
|
|
bisector_t *newBisector = malloc(sizeof(*newBisector));
|
|
|
|
checkNullPointer(newBisector);
|
|
|
|
newBisector->mid = malloc(sizeof(*newBisector->mid));
|
|
|
|
checkNullPointer(newBisector->mid);
|
|
|
|
return newBisector;
|
|
|
|
}
|
|
|
|
|
|
|
|
bisector_t *getABisector(bisector_t *bisector, vertex_t *pointA, \
|
|
|
|
vertex_t *pointB) {
|
2021-09-11 22:47:44 +10:00
|
|
|
|
2021-09-15 00:06:04 +10:00
|
|
|
/* Calculate midpoint of the two points */
|
|
|
|
bisector->mid->x = (pointA->x + pointB->x) / 2;
|
|
|
|
bisector->mid->y = (pointA->y + pointB->y) / 2;
|
|
|
|
|
|
|
|
/* Calculating bisector slope according to slope of AB */
|
|
|
|
if (pointA->x == pointB->x) {
|
|
|
|
/* The line segment AB has an infinite gradient,
|
|
|
|
* so the orthogonal line will have zero slope.
|
|
|
|
*/
|
|
|
|
bisector->slope = 0;
|
|
|
|
bisector->isSlopeInfinite = 0;
|
|
|
|
} else if (pointA->y == pointB->y) {
|
|
|
|
/* The line segment AB has gradient of zero, so
|
|
|
|
* the orthogonal line will have an infinite slope.
|
|
|
|
*/
|
|
|
|
bisector->isSlopeInfinite = 1;
|
2021-09-13 12:27:17 +10:00
|
|
|
|
2021-09-15 00:06:04 +10:00
|
|
|
/* Not actually zero, just a placeholder to prevent
|
|
|
|
* accidental errors
|
|
|
|
*/
|
|
|
|
bisector->slope = 0;
|
|
|
|
} else {
|
|
|
|
/* The line segment AB has a non-zero and finite gradient,
|
|
|
|
* so the gradient of the bisector can be calculated.
|
|
|
|
*/
|
|
|
|
bisector->isSlopeInfinite = 0;
|
|
|
|
bisector->slope = -1 / ((pointB->y - pointA->y) / \
|
|
|
|
(pointB->x - pointA->x));
|
|
|
|
}
|
2021-09-11 22:47:44 +10:00
|
|
|
|
2021-09-15 00:06:04 +10:00
|
|
|
return bisector;
|
|
|
|
}
|
2021-09-11 22:47:44 +10:00
|
|
|
|
2021-09-15 00:06:04 +10:00
|
|
|
bisector_t **getBisectors(bisector_t **bisectors, vertex_t **points, \
|
|
|
|
int numBisectors) {
|
2021-09-11 22:47:44 +10:00
|
|
|
|
2021-09-15 00:06:04 +10:00
|
|
|
for (int i = 0; i < numBisectors; i++) {
|
|
|
|
bisectors[i] = newBisector();
|
|
|
|
bisectors[i] = getABisector(bisectors[i], points[2 * i], \
|
|
|
|
points[2 * i + 1]);
|
2021-09-11 22:47:44 +10:00
|
|
|
}
|
|
|
|
|
2021-09-15 00:06:04 +10:00
|
|
|
return bisectors;
|
|
|
|
}
|
|
|
|
|
|
|
|
vertex_t *getBisectorPoint(double distance, bisector_t *b, int direction) {
|
|
|
|
vertex_t *returnPoint = malloc(sizeof(*returnPoint));
|
|
|
|
|
|
|
|
if (b->isSlopeInfinite) {
|
|
|
|
/* Vertical line - just add vertical distance */
|
|
|
|
returnPoint->x = b->mid->x;
|
|
|
|
returnPoint->y = b->mid->y + (distance * direction);
|
|
|
|
} else if (b->slope == 0) {
|
|
|
|
/* Horizontal line - just add horizontal distance */
|
|
|
|
returnPoint->x = b->mid->x + (distance * direction);
|
|
|
|
returnPoint->y = b->mid->y;
|
2021-09-11 22:47:44 +10:00
|
|
|
} else {
|
2021-09-15 00:06:04 +10:00
|
|
|
/* Not horizontal or vertical - add distance to x, then find
|
|
|
|
* y-intercept of the bisector, and plug it in to y = mx + c to find
|
|
|
|
* the corresponding y-value.
|
|
|
|
*/
|
|
|
|
double c = b->mid->y - b->slope * b->mid->x;
|
|
|
|
returnPoint->x = b->mid->x + (distance * direction);
|
|
|
|
returnPoint->y = b->slope * returnPoint->x + c;
|
|
|
|
}
|
|
|
|
|
|
|
|
return returnPoint;
|
|
|
|
}
|
|
|
|
|
|
|
|
void freeBisectors(bisector_t **bisectors, int numBisectors) {
|
|
|
|
if (!bisectors) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < numBisectors; i++) {
|
|
|
|
if (bisectors[i]->mid) {
|
|
|
|
free(bisectors[i]->mid);
|
|
|
|
}
|
|
|
|
if (bisectors[i]) {
|
|
|
|
free(bisectors[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
free(bisectors);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ----------------------- My bisector functions end ----------------------- */
|
|
|
|
|
|
|
|
/* ------------------------ My DCEL functions start ------------------------ */
|
|
|
|
|
|
|
|
double getDiameter(DCEL_t *dcel, int faceIndex) {
|
|
|
|
/* firstHE and firstVertex refer to the first HE/vertex from the face used
|
|
|
|
* to control when the loop stops
|
|
|
|
*
|
|
|
|
* startHE and startVertex refer to the starting point used to
|
|
|
|
* advance the new starting vertex after a complete cycle
|
|
|
|
*
|
|
|
|
* currentHE and currentVertex refer to the current HE/vertex being used to
|
|
|
|
* compute the diameter
|
|
|
|
*/
|
|
|
|
halfEdge_t *firstHE = (dcel->faces)[faceIndex].start;
|
|
|
|
halfEdge_t *startHE = firstHE;
|
|
|
|
halfEdge_t *currentHE = startHE;
|
|
|
|
int firstVertex = firstHE->startVertex;
|
|
|
|
int startVertex = firstVertex;
|
|
|
|
int currentVertex = startVertex;
|
|
|
|
int first = 1;
|
|
|
|
double currentDiameter = NODIAMETER, maxDiameter = NODIAMETER;
|
|
|
|
|
|
|
|
while (first || !(currentHE->startVertex == firstVertex && \
|
|
|
|
startHE->startVertex == firstVertex)) {
|
|
|
|
first = 0;
|
|
|
|
|
|
|
|
/* Compute currentDiameter, compare to maxDiameter */
|
|
|
|
currentDiameter = distPoints(&(dcel->vertices)[startVertex], \
|
|
|
|
&(dcel->vertices)[currentVertex]);
|
|
|
|
if (currentDiameter >= maxDiameter) {
|
|
|
|
maxDiameter = currentDiameter;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Advance current HE, update currentVertex */
|
|
|
|
currentHE = currentHE->next;
|
|
|
|
currentVertex = currentHE->startVertex;
|
|
|
|
|
|
|
|
/* When the starting vertex is reached, move the startHE to the next HE
|
|
|
|
* and update the vertices.
|
|
|
|
*/
|
|
|
|
if (currentVertex == startVertex) {
|
|
|
|
startHE = startHE->next;
|
|
|
|
currentHE = startHE;
|
|
|
|
startVertex = startHE->startVertex;
|
|
|
|
currentVertex = startVertex;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (currentDiameter == NODIAMETER) {
|
|
|
|
return NODIAMETER;
|
2021-09-11 22:47:44 +10:00
|
|
|
}
|
2021-09-15 00:06:04 +10:00
|
|
|
|
|
|
|
return maxDiameter;
|
2021-09-11 22:47:44 +10:00
|
|
|
}
|
|
|
|
|
2021-09-15 00:06:04 +10:00
|
|
|
/* ------------------------- My DCEL functions end ------------------------- */
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
/* */
|
|
|
|
/* Here onwards are the functions from Grady's base code on Ed */
|
|
|
|
/* */
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
|
2021-09-11 22:47:44 +10:00
|
|
|
DCEL_t *newDCEL() {
|
|
|
|
/* Setup DCEL. */
|
|
|
|
DCEL_t *dcel = malloc(sizeof(*dcel));
|
|
|
|
checkNullPointer(dcel);
|
|
|
|
|
|
|
|
dcel->edges = NULL;
|
|
|
|
dcel->edgesUsed = 0;
|
|
|
|
dcel->edgesAllocated = 0;
|
|
|
|
|
|
|
|
dcel->faces = NULL;
|
|
|
|
dcel->facesUsed = 0;
|
|
|
|
dcel->facesAllocated = 0;
|
|
|
|
|
|
|
|
dcel->vertices = NULL;
|
|
|
|
dcel->verticesUsed = 0;
|
|
|
|
dcel->verticesAllocated = 0;
|
|
|
|
|
|
|
|
return dcel;
|
|
|
|
}
|
|
|
|
|
|
|
|
halfEdge_t *newHalfEdge() {
|
|
|
|
halfEdge_t *he = malloc(sizeof(*he));
|
|
|
|
checkNullPointer(he);
|
|
|
|
he->startVertex = NOVERTEX;
|
|
|
|
he->endVertex = NOVERTEX;
|
|
|
|
he->next = NULL;
|
|
|
|
he->previous = NULL;
|
|
|
|
he->twin = NULL;
|
|
|
|
he->face = NOFACE;
|
|
|
|
he->edge = NOEDGE;
|
|
|
|
return he;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ensureSpaceForVertex(DCEL_t *dcel) {
|
|
|
|
if (!(dcel->vertices)) {
|
|
|
|
dcel->vertices = malloc(sizeof(*dcel->vertices) * INITIALVERTICES);
|
|
|
|
checkNullPointer(dcel->vertices);
|
|
|
|
dcel->verticesAllocated = INITIALVERTICES;
|
|
|
|
} else if ((dcel->verticesUsed + 1) > dcel->verticesAllocated) {
|
|
|
|
dcel->vertices = realloc(dcel->vertices, \
|
|
|
|
sizeof(*dcel->vertices) * dcel->verticesAllocated * 2);
|
|
|
|
checkNullPointer(dcel->vertices);
|
|
|
|
dcel->verticesAllocated = dcel->verticesAllocated * 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ensureSpaceForEdge(DCEL_t *dcel) {
|
|
|
|
if (!(dcel->edges)) {
|
|
|
|
dcel->edges = malloc(sizeof(*dcel->edges) * INITIALEDGES);
|
|
|
|
checkNullPointer(dcel->edges);
|
|
|
|
dcel->edgesAllocated = INITIALEDGES;
|
|
|
|
} else if ((dcel->edgesUsed + 1) > dcel->edgesAllocated) {
|
|
|
|
dcel->edges = realloc(dcel->edges, \
|
|
|
|
sizeof(*dcel->edges) * dcel->edgesAllocated * 2);
|
|
|
|
checkNullPointer(dcel->edges);
|
|
|
|
dcel->edgesAllocated = dcel->edgesAllocated * 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ensureSpaceForFace(DCEL_t *dcel) {
|
|
|
|
if (!(dcel->faces)) {
|
|
|
|
dcel->faces = malloc(sizeof(*dcel->faces) * INITIALFACES);
|
|
|
|
checkNullPointer(dcel->faces);
|
|
|
|
dcel->facesAllocated = INITIALFACES;
|
|
|
|
} else if ((dcel->facesUsed + 1) > dcel->facesAllocated) {
|
|
|
|
dcel->faces = realloc(dcel->faces, \
|
|
|
|
sizeof(*dcel->faces) * dcel->facesAllocated * 2);
|
|
|
|
checkNullPointer(dcel->faces);
|
|
|
|
dcel->facesAllocated = dcel->facesAllocated * 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void addEdge(DCEL_t *dcel, int startVertex, int endVertex) {
|
|
|
|
ensureSpaceForEdge(dcel);
|
|
|
|
|
|
|
|
int newEdge = dcel->edgesUsed;
|
|
|
|
|
|
|
|
halfEdge_t *newHE = newHalfEdge();
|
|
|
|
newHE->startVertex = startVertex;
|
|
|
|
newHE->endVertex = endVertex;
|
|
|
|
// newHE->next = NULL;
|
|
|
|
// newHE->previous = NULL;
|
|
|
|
// newHE->twin = NULL;
|
|
|
|
// newHE->face = NOFACE;
|
|
|
|
newHE->edge = newEdge;
|
|
|
|
|
|
|
|
(dcel->edges)[newEdge].halfEdge = newHE;
|
|
|
|
|
|
|
|
dcel->edgesUsed = dcel->edgesUsed + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void addFace(DCEL_t *dcel, halfEdge_t *he) {
|
|
|
|
ensureSpaceForFace(dcel);
|
|
|
|
(dcel->faces)[dcel->facesUsed].start = he;
|
|
|
|
/* Set the face in the half-edges. */
|
|
|
|
he->face = dcel->facesUsed;
|
|
|
|
|
|
|
|
(dcel->faces)[dcel->facesUsed].tower = NULL;
|
|
|
|
|
|
|
|
halfEdge_t *current = he->next;
|
|
|
|
while(current != he) {
|
|
|
|
current->face = dcel->facesUsed;
|
|
|
|
current = current->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
dcel->facesUsed = dcel->facesUsed + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
DCEL_t *readPolygonFile(char *polygonfileName) {
|
|
|
|
DCEL_t *dcel = newDCEL();
|
|
|
|
|
|
|
|
FILE *polygonFile = fopen(polygonfileName, "r");
|
|
|
|
checkNullPointer(polygonFile);
|
|
|
|
double x;
|
|
|
|
double y;
|
|
|
|
|
|
|
|
int startVertex = NOVERTEX;
|
|
|
|
int endVertex = NOVERTEX;
|
|
|
|
|
|
|
|
/* Used to finish off the polygon in the first face. */
|
|
|
|
int firstVertex = NOVERTEX;
|
|
|
|
int firstEdge = NOEDGE;
|
|
|
|
|
|
|
|
while(fscanf(polygonFile, "%lf %lf", &x, &y) == 2) {
|
|
|
|
ensureSpaceForVertex(dcel);
|
|
|
|
|
|
|
|
(dcel->vertices)[dcel->verticesUsed].x = x;
|
|
|
|
(dcel->vertices)[dcel->verticesUsed].y = y;
|
|
|
|
dcel->verticesUsed = dcel->verticesUsed + 1;
|
|
|
|
|
|
|
|
if (startVertex == NOVERTEX) {
|
|
|
|
startVertex = dcel->verticesUsed - 1;
|
|
|
|
firstVertex = startVertex;
|
|
|
|
} else if (endVertex == NOVERTEX) {
|
|
|
|
/* First edge */
|
|
|
|
endVertex = dcel->verticesUsed - 1;
|
|
|
|
firstEdge = dcel->edgesUsed;
|
|
|
|
addEdge(dcel, startVertex, endVertex);
|
|
|
|
} else {
|
|
|
|
/* Start from last vertex. */
|
|
|
|
startVertex = endVertex;
|
|
|
|
endVertex = dcel->verticesUsed - 1;
|
|
|
|
addEdge(dcel, startVertex, endVertex);
|
|
|
|
|
|
|
|
/* Connect last edge added to newest edge */
|
|
|
|
((dcel->edges)[dcel->edgesUsed - 2].halfEdge)->next = \
|
|
|
|
(dcel->edges)[dcel->edgesUsed - 1].halfEdge;
|
|
|
|
|
|
|
|
/* Connect newest edge to last edge added */
|
|
|
|
((dcel->edges)[dcel->edgesUsed - 1].halfEdge)->previous = \
|
|
|
|
(dcel->edges)[dcel->edgesUsed - 2].halfEdge;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (firstEdge == NOEDGE) {
|
|
|
|
fputs("Error: Unable to create DCEL structure for polygon.\n", stderr);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Finalise polygon by adding edge back to first vertex. */
|
|
|
|
int finalEdge = dcel->edgesUsed;
|
|
|
|
addEdge(dcel, endVertex, firstVertex);
|
|
|
|
|
|
|
|
/* Connect previous edge to this edge. */
|
|
|
|
((dcel->edges)[dcel->edgesUsed - 2].halfEdge)->next = \
|
|
|
|
(dcel->edges)[dcel->edgesUsed - 1].halfEdge;
|
|
|
|
|
|
|
|
/* Connect newest edge to last edge added */
|
|
|
|
((dcel->edges)[dcel->edgesUsed - 1].halfEdge)->previous = \
|
|
|
|
(dcel->edges)[dcel->edgesUsed - 2].halfEdge;
|
|
|
|
|
|
|
|
/* Connect final edge back to start edge. */
|
|
|
|
((dcel->edges)[finalEdge].halfEdge)->next = \
|
|
|
|
(dcel->edges)[firstEdge].halfEdge;
|
|
|
|
|
|
|
|
/* Connect start edge back to final edge. */
|
|
|
|
((dcel->edges)[firstEdge].halfEdge)->previous = \
|
|
|
|
(dcel->edges)[finalEdge].halfEdge;
|
|
|
|
|
|
|
|
/* Add face to DCEL - could be any edge we constructed,
|
|
|
|
* so may as well be the first.
|
|
|
|
*/
|
|
|
|
addFace(dcel, (dcel->edges)[firstEdge].halfEdge);
|
|
|
|
if (polygonFile) {
|
|
|
|
fclose(polygonFile);
|
|
|
|
}
|
|
|
|
|
|
|
|
return dcel;
|
|
|
|
}
|
|
|
|
|
|
|
|
split_t *readNextSplit(FILE *splitfile) {
|
|
|
|
int firstEdge;
|
|
|
|
int secondEdge;
|
|
|
|
if (fscanf(splitfile, "%d %d", &firstEdge, &secondEdge) != 2) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
split_t *split = malloc(sizeof(*split));
|
|
|
|
split->startEdge = firstEdge;
|
|
|
|
split->endEdge = secondEdge;
|
|
|
|
split->verticesSpecified = 0;
|
|
|
|
return split;
|
|
|
|
}
|
|
|
|
|
|
|
|
void freeSplit(split_t *split) {
|
|
|
|
if (split) {
|
|
|
|
free(split);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int vertexMatch(vertex_t *v1, vertex_t *v2) {
|
|
|
|
if (v1->x != v2->x) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (v1->y != v2->y) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void applySplit(split_t *split, DCEL_t *dcel) {
|
|
|
|
int isAdjacent;
|
|
|
|
double midpointX;
|
|
|
|
double midpointY;
|
|
|
|
halfEdge_t *startHE;
|
|
|
|
halfEdge_t *endHE;
|
|
|
|
halfEdge_t *newJoinHE;
|
|
|
|
halfEdge_t *newJoinHEPair;
|
|
|
|
halfEdge_t *newStartHEToMid;
|
|
|
|
halfEdge_t *newStartHEToMidPair;
|
|
|
|
halfEdge_t *newMidHEToEnd;
|
|
|
|
halfEdge_t *newMidHEToEndPair;
|
|
|
|
/* Temporary holders for old twin edges */
|
|
|
|
halfEdge_t *oldStartPairPrev;
|
|
|
|
halfEdge_t *oldEndPairNext;
|
|
|
|
/* Temporary holder for old pairs */
|
|
|
|
halfEdge_t *oldStartPair;
|
|
|
|
halfEdge_t *oldEndPair;
|
|
|
|
|
|
|
|
int newVertexMidStart;
|
|
|
|
int newVertexMidEnd;
|
|
|
|
/* The vertex representing the end of the original starting edge */
|
|
|
|
int oldVertexStart;
|
|
|
|
/* The vertex representing the start of the original ending edge */
|
|
|
|
int oldVertexEnd;
|
|
|
|
|
|
|
|
/* Each split creates exactly 3 edges, so we can set up space for these now. */
|
|
|
|
int joinEdge;
|
|
|
|
int newStartEdge;
|
|
|
|
int newEndEdge;
|
|
|
|
|
|
|
|
ensureSpaceForEdge(dcel);
|
|
|
|
joinEdge = dcel->edgesUsed;
|
|
|
|
dcel->edgesUsed = dcel->edgesUsed + 1;
|
|
|
|
|
|
|
|
ensureSpaceForEdge(dcel);
|
|
|
|
newStartEdge = dcel->edgesUsed;
|
|
|
|
dcel->edgesUsed = dcel->edgesUsed + 1;
|
|
|
|
|
|
|
|
ensureSpaceForEdge(dcel);
|
|
|
|
newEndEdge = dcel->edgesUsed;
|
|
|
|
dcel->edgesUsed = dcel->edgesUsed + 1;
|
|
|
|
|
|
|
|
/* Get vertices for MidStart and MidEnd */
|
|
|
|
ensureSpaceForVertex(dcel);
|
|
|
|
newVertexMidStart = dcel->verticesUsed;
|
|
|
|
dcel->verticesUsed = dcel->verticesUsed + 1;
|
|
|
|
|
|
|
|
ensureSpaceForVertex(dcel);
|
|
|
|
newVertexMidEnd = dcel->verticesUsed;
|
|
|
|
dcel->verticesUsed = dcel->verticesUsed + 1;
|
|
|
|
|
|
|
|
/* Work out what half-edges we need to use. */
|
|
|
|
startHE = (dcel->edges)[split->startEdge].halfEdge;
|
|
|
|
endHE = (dcel->edges)[split->endEdge].halfEdge;
|
|
|
|
|
|
|
|
/* Set midpoint of start */
|
|
|
|
double startX = (dcel->vertices)[startHE->startVertex].x;
|
|
|
|
double startY = (dcel->vertices)[startHE->startVertex].y;
|
|
|
|
double endX = (dcel->vertices)[startHE->endVertex].x;
|
|
|
|
double endY = (dcel->vertices)[startHE->endVertex].y;
|
|
|
|
if (split->verticesSpecified) {
|
|
|
|
/* See if vertex needs to be reused */
|
|
|
|
if (vertexMatch(&(dcel->vertices)[startHE->endVertex],
|
|
|
|
&split->startPoint)) {
|
|
|
|
newVertexMidStart = startHE->endVertex;
|
|
|
|
} else if (vertexMatch(&(dcel->vertices)[startHE->startVertex],
|
|
|
|
&split->startPoint)) {
|
|
|
|
newVertexMidStart = startHE->startVertex;
|
|
|
|
} else {
|
|
|
|
(dcel->vertices)[newVertexMidStart].x = split->startPoint.x;
|
|
|
|
(dcel->vertices)[newVertexMidStart].y = split->startPoint.y;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
(dcel->vertices)[newVertexMidStart].x = (startX + endX) / 2.0;
|
|
|
|
(dcel->vertices)[newVertexMidStart].y = (startY + endY) / 2.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Set midpoint of end */
|
|
|
|
startX = (dcel->vertices)[endHE->startVertex].x;
|
|
|
|
startY = (dcel->vertices)[endHE->startVertex].y;
|
|
|
|
endX = (dcel->vertices)[endHE->endVertex].x;
|
|
|
|
endY = (dcel->vertices)[endHE->endVertex].y;
|
|
|
|
if (split->verticesSpecified) {
|
|
|
|
/* See if vertex needs to be reused */
|
|
|
|
if (vertexMatch(&(dcel->vertices)[endHE->startVertex],
|
|
|
|
&split->endPoint)) {
|
|
|
|
newVertexMidEnd = endHE->startVertex;
|
|
|
|
} else if (vertexMatch(&(dcel->vertices)[endHE->endVertex],
|
|
|
|
&split->endPoint)) {
|
|
|
|
newVertexMidEnd = endHE->endVertex;
|
|
|
|
} else {
|
|
|
|
(dcel->vertices)[newVertexMidEnd].x = split->endPoint.x;
|
|
|
|
(dcel->vertices)[newVertexMidEnd].y = split->endPoint.y;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
(dcel->vertices)[newVertexMidEnd].x = (startX + endX) / 2.0;
|
|
|
|
(dcel->vertices)[newVertexMidEnd].y = (startY + endY) / 2.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Get point halfway between both midpoints */
|
|
|
|
double x1 = (dcel->vertices)[newVertexMidStart].x;
|
|
|
|
double x2 = (dcel->vertices)[newVertexMidEnd].x;
|
|
|
|
double y1 = (dcel->vertices)[newVertexMidStart].y;
|
|
|
|
double y2 = (dcel->vertices)[newVertexMidEnd].y;
|
|
|
|
midpointX = (x1 + x2) / 2.0;
|
|
|
|
midpointY = (y1 + y2) / 2.0;
|
|
|
|
|
|
|
|
/* Work out whether on correct side. */
|
|
|
|
vertex_t *v1 = &((dcel->vertices)[startHE->startVertex]);
|
|
|
|
vertex_t *v2 = &((dcel->vertices)[startHE->endVertex]);
|
|
|
|
if (getRelativeDir(midpointX, midpointY, v1, v2) == OUTSIDE) {
|
|
|
|
startHE = startHE->twin;
|
|
|
|
}
|
|
|
|
v1 = &((dcel->vertices)[endHE->startVertex]);
|
|
|
|
v2 = &((dcel->vertices)[endHE->endVertex]);
|
|
|
|
if (getRelativeDir(midpointX, midpointY, v1, v2) == OUTSIDE) {
|
|
|
|
endHE = endHE->twin;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Work out whether edges are adjacent. */
|
|
|
|
if (startHE->next == endHE) {
|
|
|
|
isAdjacent = 1;
|
|
|
|
} else {
|
|
|
|
isAdjacent = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Store old previous and next from start and end edges for convenience */
|
|
|
|
halfEdge_t *oldEndPrev = endHE->previous;
|
|
|
|
halfEdge_t *oldStartNext = startHE->next;
|
|
|
|
oldVertexEnd = endHE->startVertex;
|
|
|
|
oldVertexStart = startHE->endVertex;
|
|
|
|
|
|
|
|
/* Update vertices of endHE and startHE */
|
|
|
|
endHE->startVertex = newVertexMidEnd;
|
|
|
|
startHE->endVertex = newVertexMidStart;
|
|
|
|
|
|
|
|
/* Add bridging edges */
|
|
|
|
newJoinHE = newHalfEdge();
|
|
|
|
|
|
|
|
newJoinHE->startVertex = newVertexMidStart;
|
|
|
|
newJoinHE->endVertex = newVertexMidEnd;
|
|
|
|
newJoinHE->next = endHE;
|
|
|
|
endHE->previous = newJoinHE;
|
|
|
|
newJoinHE->previous = startHE;
|
|
|
|
startHE->next = newJoinHE;
|
|
|
|
newJoinHE->twin = NULL; // Will be set later
|
|
|
|
/* joinHE is same face as startHE and endHE */
|
|
|
|
newJoinHE->face = startHE->face;
|
|
|
|
newJoinHE->edge = joinEdge;
|
|
|
|
|
|
|
|
/* Set joinEdge to relevant halfEdge */
|
|
|
|
(dcel->edges)[joinEdge].halfEdge = newJoinHE;
|
|
|
|
|
|
|
|
newJoinHEPair = newHalfEdge();
|
|
|
|
/* twin is in opposite direction. */
|
|
|
|
newJoinHEPair->startVertex = newVertexMidEnd;
|
|
|
|
newJoinHEPair->endVertex = newVertexMidStart;
|
|
|
|
newJoinHEPair->next = NULL; // Will join to new HEs
|
|
|
|
newJoinHEPair->previous = NULL; // Will join to new HEs
|
|
|
|
newJoinHEPair->twin = newJoinHE;
|
|
|
|
newJoinHE->twin = newJoinHEPair;
|
|
|
|
newJoinHEPair->face = NOFACE; // Will be new face set later
|
|
|
|
newJoinHEPair->edge = joinEdge;
|
|
|
|
|
|
|
|
/* Set up what we can of new edges */
|
|
|
|
newStartHEToMid = newHalfEdge();
|
|
|
|
newStartHEToMid->startVertex = newVertexMidStart;
|
|
|
|
newStartHEToMid->endVertex = oldVertexStart;
|
|
|
|
newStartHEToMid->next = NULL; // Different setting based on adjacency, set below.
|
|
|
|
newStartHEToMid->previous = newJoinHEPair;
|
|
|
|
newJoinHEPair->next = newStartHEToMid;
|
|
|
|
newStartHEToMid->twin = NULL; // Will be set up later if needed.
|
|
|
|
newStartHEToMid->face = NOFACE; // Will be new face set later
|
|
|
|
newStartHEToMid->edge = newStartEdge;
|
|
|
|
|
|
|
|
/* Set newStartEdge to relevant halfEdge */
|
|
|
|
(dcel->edges)[newStartEdge].halfEdge = newStartHEToMid;
|
|
|
|
|
|
|
|
newMidHEToEnd = newHalfEdge();
|
|
|
|
newMidHEToEnd->startVertex = oldVertexEnd;
|
|
|
|
newMidHEToEnd->endVertex = newVertexMidEnd;
|
|
|
|
newMidHEToEnd->next = newJoinHEPair;
|
|
|
|
newJoinHEPair->previous = newMidHEToEnd;
|
|
|
|
newMidHEToEnd->previous = NULL; // Different setting based on adjacency, set below.
|
|
|
|
newMidHEToEnd->twin = NULL; // Will be set up later if needed.
|
|
|
|
newMidHEToEnd->face = NOFACE;
|
|
|
|
newMidHEToEnd->edge = newEndEdge;
|
|
|
|
|
|
|
|
/* Set newEndEdge to relevant halfEdge */
|
|
|
|
(dcel->edges)[newEndEdge].halfEdge = newMidHEToEnd;
|
|
|
|
|
|
|
|
/* If either start or end HEs have paired Half-Edges, we also need to split those. */
|
|
|
|
if (startHE->twin) {
|
|
|
|
oldStartPairPrev = startHE->twin->previous;
|
|
|
|
oldStartPair = startHE->twin;
|
|
|
|
|
|
|
|
newStartHEToMidPair = newHalfEdge();
|
|
|
|
/* Reverse of twin */
|
|
|
|
newStartHEToMidPair->startVertex = oldVertexStart;
|
|
|
|
newStartHEToMidPair->endVertex = newVertexMidStart;
|
|
|
|
newStartHEToMidPair->next = oldStartPair;
|
|
|
|
newStartHEToMidPair->previous = oldStartPairPrev;
|
|
|
|
startHE->twin->previous = newStartHEToMidPair;
|
|
|
|
oldStartPair->previous = newStartHEToMidPair;
|
|
|
|
oldStartPair->startVertex = newVertexMidStart;
|
|
|
|
oldStartPairPrev->next = newStartHEToMidPair;
|
|
|
|
newStartHEToMid->twin = newStartHEToMidPair;
|
|
|
|
newStartHEToMidPair->twin = newStartHEToMid;
|
|
|
|
newStartHEToMidPair->face = startHE->twin->face;
|
|
|
|
newStartHEToMidPair->edge = newStartEdge;
|
|
|
|
} else {
|
|
|
|
newStartHEToMidPair = NULL;
|
|
|
|
}
|
|
|
|
if (endHE->twin) {
|
|
|
|
oldEndPairNext = endHE->twin->next;
|
|
|
|
oldEndPair = endHE->twin;
|
|
|
|
|
|
|
|
newMidHEToEndPair = newHalfEdge();
|
|
|
|
newMidHEToEndPair->startVertex = newVertexMidEnd;
|
|
|
|
newMidHEToEndPair->endVertex = oldVertexEnd;
|
|
|
|
newMidHEToEndPair->next = oldEndPairNext; // endHE->twin ?
|
|
|
|
oldEndPair->next = newMidHEToEndPair;
|
|
|
|
oldEndPairNext->previous = newMidHEToEndPair; // Next?
|
|
|
|
oldEndPair->endVertex = newVertexMidEnd;
|
|
|
|
newMidHEToEndPair->previous = oldEndPair;
|
|
|
|
newMidHEToEnd->twin = newMidHEToEndPair;
|
|
|
|
newMidHEToEndPair->twin = newMidHEToEnd;
|
|
|
|
newMidHEToEndPair->face = endHE->twin->face;
|
|
|
|
newMidHEToEndPair->edge = newEndEdge;
|
|
|
|
} else {
|
|
|
|
newMidHEToEndPair = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set up remaining edges. */
|
|
|
|
if (isAdjacent) {
|
|
|
|
newStartHEToMid->next = newMidHEToEnd;
|
|
|
|
newMidHEToEnd->previous = newStartHEToMid;
|
|
|
|
} else {
|
|
|
|
/* Edges are old start and end edges (maybe the same edge). */
|
|
|
|
newStartHEToMid->next = oldStartNext;
|
|
|
|
oldStartNext->previous = newStartHEToMid;
|
|
|
|
newMidHEToEnd->previous = oldEndPrev;
|
|
|
|
oldEndPrev->next = newMidHEToEnd;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Setup new face. */
|
|
|
|
addFace(dcel, newJoinHEPair);
|
|
|
|
|
|
|
|
/* Check if face has overwritten other face */
|
|
|
|
int joinFace = startHE->face;
|
|
|
|
if ((dcel->faces)[joinFace].start->face != joinFace) {
|
|
|
|
(dcel->faces)[joinFace].start = startHE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void freeDCEL(DCEL_t *dcel) {
|
|
|
|
if (!dcel) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
int i;
|
|
|
|
if (dcel->edges) {
|
|
|
|
for(i = 0; i < dcel->edgesUsed; i++) {
|
|
|
|
if ((dcel->edges)[i].halfEdge) {
|
|
|
|
if (((dcel->edges)[i]).halfEdge->twin) {
|
|
|
|
/* Free if edge has two halves. */
|
|
|
|
free(((dcel->edges)[i]).halfEdge->twin);
|
|
|
|
}
|
|
|
|
free(((dcel->edges)[i]).halfEdge);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
free(dcel->edges);
|
|
|
|
}
|
|
|
|
if (dcel->faces) {
|
|
|
|
/* All edges are freed above, so no need to free each edge here. */
|
|
|
|
free(dcel->faces);
|
|
|
|
}
|
|
|
|
if (dcel->vertices) {
|
|
|
|
free(dcel->vertices);
|
|
|
|
}
|
|
|
|
free(dcel);
|
|
|
|
}
|
|
|
|
|
|
|
|
int getFaceCount(DCEL_t *dcel) {
|
|
|
|
if (!dcel) {
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
return dcel->facesUsed;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int getRelativeDir(double x, double y, vertex_t *v1, vertex_t *v2) {
|
|
|
|
/* Here we're doing a simple half-plane check against the vector v1->v2. */
|
|
|
|
double x1 = v1->x;
|
|
|
|
double x2 = v2->x;
|
|
|
|
double y1 = v1->y;
|
|
|
|
double y2 = v2->y;
|
|
|
|
if (x1 == x2 && y1 == y2) {
|
|
|
|
/* Same point. */
|
|
|
|
return DIR_UNDECIDED;
|
|
|
|
} else if (x1 == x2) {
|
|
|
|
/* y = c line */
|
|
|
|
/* Work out whether line is going up or down. */
|
|
|
|
if (y2 > y1) {
|
|
|
|
if (x > x1) {
|
|
|
|
return INSIDE;
|
|
|
|
} else if (x < x1) {
|
|
|
|
return OUTSIDE;
|
|
|
|
} else {
|
|
|
|
return DIR_UNDECIDED;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (x < x1) {
|
|
|
|
return INSIDE;
|
|
|
|
} else if (x > x1) {
|
|
|
|
return OUTSIDE;
|
|
|
|
} else {
|
|
|
|
return DIR_UNDECIDED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (y1 == y2) {
|
|
|
|
/* x = c line */
|
|
|
|
/* Work out whether line is going left or right. */
|
|
|
|
if (x2 > x1) {
|
|
|
|
if (y < y1) {
|
|
|
|
return INSIDE;
|
|
|
|
} else if (y > y1) {
|
|
|
|
return OUTSIDE;
|
|
|
|
} else {
|
|
|
|
return DIR_UNDECIDED;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (y > y1) {
|
|
|
|
return INSIDE;
|
|
|
|
} else if (y < y1) {
|
|
|
|
return OUTSIDE;
|
|
|
|
} else {
|
|
|
|
return DIR_UNDECIDED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
x1, x2, y1, y2 distinct, so see whether point being tested is
|
|
|
|
above or below gradient line.
|
|
|
|
*/
|
|
|
|
double m = (y2 - y1)/(x2 - x1);
|
|
|
|
double c = y1 - m*x1;
|
|
|
|
|
|
|
|
double predictedY = x * m + c;
|
|
|
|
double residual = y - predictedY;
|
|
|
|
|
|
|
|
/*
|
|
|
|
Being inside or outside the polygon depends on the direction
|
|
|
|
the half-edge is going.
|
|
|
|
*/
|
|
|
|
if (x2 > x1) {
|
|
|
|
if (residual < 0) {
|
|
|
|
return INSIDE;
|
|
|
|
} else if (residual > 0) {
|
|
|
|
return OUTSIDE;
|
|
|
|
} else {
|
|
|
|
return DIR_UNDECIDED;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (residual > 0) {
|
|
|
|
return INSIDE;
|
|
|
|
} else if (residual < 0) {
|
|
|
|
return OUTSIDE;
|
|
|
|
} else {
|
|
|
|
return DIR_UNDECIDED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int directionOrUndecided(int decidedDirection, int direction) {
|
|
|
|
if (direction == decidedDirection || direction == DIR_UNDECIDED) {
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int inFace(DCEL_t *dcel, double x, double y, int faceIndex) {
|
|
|
|
if (dcel->facesUsed < faceIndex || !(dcel->faces)[faceIndex].start) {
|
|
|
|
return OUTSIDE;
|
|
|
|
}
|
|
|
|
halfEdge_t *start = (dcel->faces)[faceIndex].start;
|
|
|
|
int first = 1;
|
|
|
|
int direction = DIR_UNDECIDED;
|
|
|
|
|
|
|
|
halfEdge_t *current = start;
|
|
|
|
while(start != current || first) {
|
|
|
|
if (direction == DIR_UNDECIDED) {
|
|
|
|
/* Doesn't matter where the point is until we find it on one side or the
|
|
|
|
other. */
|
|
|
|
direction = getRelativeDir(x, y, &(dcel->vertices)[current->startVertex],
|
|
|
|
&(dcel->vertices)[current->endVertex]);
|
|
|
|
} else {
|
|
|
|
if (!directionOrUndecided(direction,
|
|
|
|
getRelativeDir(x, y, &(dcel->vertices)[current->startVertex],
|
|
|
|
&(dcel->vertices)[current->endVertex]))) {
|
|
|
|
/* If the point is on the different side of any edge, it be inside
|
|
|
|
the face, because the face is convex. */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
current = current->next;
|
|
|
|
first = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2021-09-15 00:06:04 +10:00
|
|
|
/* 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/intersect
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
2021-09-11 22:47:44 +10:00
|
|
|
|
2021-09-15 00:06:04 +10:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2021-09-11 22:47:44 +10:00
|
|
|
|
2021-09-15 00:06:04 +10:00
|
|
|
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;
|
2021-09-13 12:27:17 +10:00
|
|
|
}
|
2021-09-15 00:06:04 +10:00
|
|
|
} 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;
|
2021-09-13 12:27:17 +10:00
|
|
|
}
|
2021-09-11 22:47:44 +10:00
|
|
|
}
|
2021-09-15 00:06:04 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
int collinear(double sx, double sy, double ex, double ey, double x, double y) {
|
|
|
|
/* If area of the parallelogram is 0, then the points
|
|
|
|
* are in the same line.
|
|
|
|
*/
|
|
|
|
if (areaSign(sx, sy, ex, ey, x, y) == 0) {
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
2021-09-11 22:47:44 +10:00
|
|
|
}
|
|
|
|
|
2021-09-15 00:06:04 +10:00
|
|
|
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;
|
2021-09-11 22:47:44 +10:00
|
|
|
}
|
|
|
|
|
2021-09-15 00:06:04 +10:00
|
|
|
enum intersectType intersects(halfEdge_t *he, bisector_t *b, \
|
|
|
|
DCEL_t *dcel, double minLength, double *x, double *y) {
|
|
|
|
|
|
|
|
/* Half-edge x, y twin */
|
|
|
|
double heSx = dcel->vertices[he->startVertex].x;
|
|
|
|
double heSy = dcel->vertices[he->startVertex].y;
|
|
|
|
double heEx = dcel->vertices[he->endVertex].x;
|
|
|
|
double heEy = dcel->vertices[he->endVertex].y;
|
|
|
|
|
|
|
|
/* Bisector x, y twin */
|
|
|
|
vertex_t *startPoint = getBisectorPoint(minLength, b, -1);
|
|
|
|
vertex_t *endPoint = getBisectorPoint(minLength, b, 1);
|
|
|
|
double bSx = startPoint->x;
|
|
|
|
double bSy = startPoint->y;
|
|
|
|
double bEx = endPoint->x;
|
|
|
|
double bEy = endPoint->y;
|
|
|
|
free(startPoint);
|
|
|
|
free(endPoint);
|
|
|
|
|
|
|
|
/* Parametric equation parameters */
|
|
|
|
double t1, t2;
|
|
|
|
|
|
|
|
/* Numerators for X and Y coordinate of intersection. */
|
|
|
|
double numeratorX, 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;
|
|
|
|
}
|
2021-09-11 22:47:44 +10:00
|
|
|
}
|