diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..14004e0 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "files.associations": { + "type_traits": "c" + } +} \ No newline at end of file diff --git a/Makefile b/Makefile index a96ce28..e196e78 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # Link command: voronoi2: common.o towers.o dcel.o voronoi.o input.o main.o - gcc -Wall -Wextra -Werror -pedantic -g -o voronoi2 main.o input.o voronoi.o dcel.o towers.o common.o + gcc -Wall -Wextra -Werror -pedantic -g -o voronoi2 main.o input.o voronoi.o dcel.o towers.o common.o -lm # Compilation commands common.o: common.c diff --git a/README.md b/README.md old mode 100644 new mode 100755 diff --git a/common.c b/common.c index d0bc6c3..3c7b41b 100644 --- a/common.c +++ b/common.c @@ -2,9 +2,9 @@ * * Created by Rory Healy (healyr@student.unimelb.edu.au) * Created on 25th August 2021 - * Last modified 9th September 2021 + * Last modified 14th September 2021 * - * Contains functions for general use throughout other files. + * Contains functions and headers for general use throughout other files. * */ diff --git a/dcel.c b/dcel.c index bc785a0..75626f7 100644 --- a/dcel.c +++ b/dcel.c @@ -2,10 +2,10 @@ * * Created by Rory Healy (healyr@student.unimelb.edu.au) * Created on 25th August 2021 - * Last modified 13th September 2021 + * Last modified 14th September 2021 * - * Contains functions for the DCEL data structure, including initialisation, - * splitting an edge, and identifying towers in faces. + * Contains functions for the DCEL data structure and related data structures + * (including points, intersections, and bisectors). * * Contains several functions and structures from Grady Fitzpatrick's Base * Code on the Ed Discussion Forum. @@ -16,106 +16,29 @@ #include "dcel.h" #endif -void freePoints(vertex_t **points, int numPoints) { - for (int i = 0; i < numPoints; i++) { - free(points[i]); - } - free(points); +#define INITIALVERTICES 4 +#define INITIALEDGES 4 +#define INITIALFACES 1 +#define NOVERTEX (-1) +#define NOEDGE (-1) + +#define DIR_UNDECIDED (0) +#define INSIDE (1) +#define OUTSIDE (-1) +#define NODIAMETER (-1) + +/* ----------------------- My points functions start ----------------------- */ +vertex_t *newPoint() { + vertex_t *newPoint = malloc(sizeof(*newPoint)); + checkNullPointer(newPoint); + return newPoint; } -void freeBisectors(bisector_t **bisectors, int numBisectors) { - for (int i = 0; i < numBisectors; i++) { - free(bisectors[i]->mid); - free(bisectors[i]); - } - free(bisectors); -} - -void freeIntersections(intersection_t **intersections, int numIntersections) { - for (int i = 0; i < numIntersections; i++) { - free(intersections[i]->fromPoint); - free(intersections[i]->toPoint); - free(intersections[i]); - } - free(intersections); -} - -bisector_t **getBisectors(bisector_t **bisectors, vertex_t **points, \ - int numBisectors) { - for (int i = 0; i < numBisectors; i++) { - bisectors[i] = malloc(sizeof(*bisectors[i])); - bisectors[i]->mid = malloc(sizeof(*bisectors[i]->mid)); - - /* Calculate midpoint of the two points */ - bisectors[i]->mid->x = (points[2 * i]->x + points[2 * i + 1]->x) / 2; - bisectors[i]->mid->y = (points[2 * i]->y + points[2 * i + 1]->y) / 2; - - /* Calculating bisector slope according to slope of AB */ - if (points[2 * i]->x == points[2 * i + 1]->x) { - /* The line segment AB has an infinite gradient, - * so the orthogonal line will have zero slope. - */ - bisectors[i]->slope = 0; - bisectors[i]->isSlopeInfinite = 0; - } else if (points[2 * i]->y == points[2 * i + 1]->y) { - /* The line segment AB has gradient of zero, so - * the orthogonal line will have an infinite slope. - */ - bisectors[i]->isSlopeInfinite = 1; - - /* Not actually zero, just a placeholder to prevent - * accidental errors - */ - bisectors[i]->slope = 0; - } else { - /* The line segment AB has a non-zero and finite gradient, - * so the gradient of the bisector can be calculated. - */ - bisectors[i]->isSlopeInfinite = 0; - bisectors[i]->slope = -1 / \ - ((points[2 * i + 1]->y - points[2 * i]->y) / \ - (points[2 * i + 1]->x - points[2 * i]->x)); - } - } - - 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; - } else { - /* 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; -} - -intersection_t *newIntersection() { - intersection_t *intersection = malloc(sizeof(*intersection)); - checkNullPointer(intersection); - - intersection->fromPoint = malloc(sizeof(*intersection->fromPoint)); - checkNullPointer(intersection->fromPoint); - - intersection->toPoint = malloc(sizeof(*intersection->toPoint)); - checkNullPointer(intersection->toPoint); - - return intersection; +vertex_t *getAPoint(double x, double y) { + vertex_t *point = newPoint(); + point->x = x; + point->y = y; + return point; } vertex_t **readPoints(vertex_t **points, FILE *pointsFile, int *numPoints) { @@ -157,224 +80,302 @@ vertex_t **readPoints(vertex_t **points, FILE *pointsFile, int *numPoints) { return points; } -/* Here on out is the base code from Grady, with some alterations from me */ - -#define INITIALVERTICES 4 -#define INITIALEDGES 4 -#define INITIALFACES 1 -#define NOVERTEX (-1) -#define NOEDGE (-1) - -#define DIR_UNDECIDED (0) -#define INSIDE (1) -#define OUTSIDE (-1) -#define NODIAMETER (-1) - -/* -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. -*/ - -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; +void freePoints(vertex_t **points, int numPoints) { + if (!points) { + return; } -} -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; + for (int i = 0; i < numPoints; i++) { + if (points[i]) { + free(points[i]); } } + free(points); } + +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); +} + +/* ------------------------ My points functions end ------------------------ */ + +/* -------------------- My intersection functions start -------------------- */ + +intersection_t *newIntersection() { + intersection_t *intersection = malloc(sizeof(*intersection)); + checkNullPointer(intersection); + + intersection->fromPoint = malloc(sizeof(*intersection->fromPoint)); + checkNullPointer(intersection->fromPoint); + + intersection->toPoint = malloc(sizeof(*intersection->toPoint)); + checkNullPointer(intersection->toPoint); + + return intersection; +} + +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; + } + currentHE = currentHE->next; + first = 0; + } + + return intersection; +} + +intersection_t **getIntersections(intersection_t **intersections, \ + int *numIntersections, bisector_t **bisectors, int numBisectors, \ + DCEL_t *dcel, int face, double minLength) { -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. + 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; + } + } + + return intersections; +} + +void freeIntersections(intersection_t **intersections, int numIntersections) { + if (!intersections) { + return; + } + + 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); +} + +/* --------------------- My intersection functions end --------------------- */ + +/* ---------------------- 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) { + + /* 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; + + /* 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)); + } + + return bisector; +} + +bisector_t **getBisectors(bisector_t **bisectors, vertex_t **points, \ + int numBisectors) { + + for (int i = 0; i < numBisectors; i++) { + bisectors[i] = newBisector(); + bisectors[i] = getABisector(bisectors[i], points[2 * i], \ + points[2 * i + 1]); + } + + 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; + } else { + /* 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 */ - if (areaSign(sx, sy, ex, ey, x, y) == 0) { - return 1; - } else { - return 0; + 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; + } + + return maxDiameter; } -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; -} +/* ------------------------- My DCEL functions end ------------------------- */ -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); - - /* Fill in segment. */ - - /* 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; - } -} +/* ------------------------------------------------------------------------- */ +/* */ +/* Here onwards are the functions from Grady's base code on Ed */ +/* */ +/* ------------------------------------------------------------------------- */ DCEL_t *newDCEL() { /* Setup DCEL. */ @@ -1011,83 +1012,208 @@ int inFace(DCEL_t *dcel, double x, double y, int faceIndex) { return 1; } -intersection_t **getIntersections(intersection_t **intersections, \ - int *numIntersections, bisector_t **bisectors, int numBisectors, \ - DCEL_t *dcel, int face, double minLength) { - int maxSizeIntersections = 1; +/* 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. + */ - for (int i = 0; i < numBisectors; i++) { - intersection_t *currentIntersection = NULL; +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; + } +} - /* 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, bisectors[i], dcel, minLength, &x, &y); - - switch (typeOfIntersection) { - case INTERSECT: - case SAME_LINE_OVERLAP: - case ENDS_OVERLAP: - if (!isIntersectionFound) { - /* First point of intersection */ - isIntersectionFound = 1; - currentIntersection = newIntersection(); - currentIntersection->fromPoint->x = x; - currentIntersection->fromPoint->y = y; - currentIntersection->fromEdge = currentHE->edge; - } else { - /* Second point of intersection */ - currentIntersection->toPoint->x = x; - currentIntersection->toPoint->y = y; - currentIntersection->toEdge = currentHE->edge; - } - break; - case DOESNT_INTERSECT: - default: - break; - } - currentHE = currentHE->next; - first = 0; +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; } - - /* If there is an intersection, add it to the array */ - if (currentIntersection != 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] = currentIntersection; - *numIntersections += 1; + } 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; } } - - return intersections; +} + +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; + } } -double getDiameter(DCEL_t *dcel, int faceIndex) { - // FILL IN - printf("in getDiamter(): faceIndex = %d, dcel faces = %d\n", \ - faceIndex, dcel->facesAllocated); - return NODIAMETER; +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; } -void incrementalVoronoi(DCEL_t *dcel, tower_t *tower) { - // FILL IN - printf("In incrementalVoronoi(): tower id = %s, dcel faces = %d\n", \ - tower->id, dcel->facesAllocated); +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; + } } diff --git a/dcel.h b/dcel.h index 25672ae..36bbc0e 100644 --- a/dcel.h +++ b/dcel.h @@ -2,10 +2,23 @@ #include "towers.h" #endif +#ifndef MATH_HEADER +#include +#endif + #ifndef DCEL_HEADER #define DCEL_HEADER -/* Here is the Base Code from Grady, with some alterations by me */ +/* -------- Definitions, enums, and structs from Grady's base code --------- */ + +/* Representation of no face */ +#define NOFACE (-1) + +/* Default face for intersections. */ +#define DEFAULT_FACE 0 + +/* Default minimum length for bisector in each direction */ +#define DEFAULTMINLENGTH (200) enum intersectType { DOESNT_INTERSECT = 0, // Doesn't intersect @@ -19,12 +32,6 @@ typedef struct vertex { double y; } vertex_t; -typedef struct bisector { - vertex_t *mid; - int isSlopeInfinite; - double slope; -} bisector_t; - typedef struct halfEdge { struct halfEdge *previous; struct halfEdge *next; @@ -53,13 +60,6 @@ typedef struct split { vertex_t endPoint; } split_t; -typedef struct intersection { - int fromEdge; - int toEdge; - vertex_t *fromPoint; - vertex_t *toPoint; -} intersection_t; - typedef struct DCEL { edge_t *edges; int edgesUsed; @@ -74,35 +74,114 @@ typedef struct DCEL { int verticesAllocated; } DCEL_t; +/* ------------------------------ My structs ------------------------------- */ + +typedef struct bisector { + vertex_t *mid; + int isSlopeInfinite; + double slope; +} bisector_t; + +typedef struct intersection { + int fromEdge; + int toEdge; + vertex_t *fromPoint; + vertex_t *toPoint; +} intersection_t; + +/* ----------------------- My points functions start ----------------------- */ + +/* Creates a new point */ +vertex_t *newPoint(); + +/* Gets a single point from an x-y pair */ +vertex_t *getAPoint(double x, double y); + +/* Reads a points file and stores the information in the points array */ +vertex_t **readPoints(vertex_t **points, FILE *pointsFile, int *numPoints); + +/* Frees an array of points */ +void freePoints(vertex_t **points, int numPoints); + +/* Returns the distance between two points */ +double distPoints(vertex_t *pointA, vertex_t *pointB); + +/* ------------------------ My points functions end ------------------------ */ + +/* -------------------- My intersection functions start -------------------- */ + +/* Create a new intersection */ +intersection_t *newIntersection(); + +/* Fills in a single intersection structure */ +intersection_t *getAnIntersection(intersection_t *intersection, DCEL_t *dcel, \ + bisector_t *bisector, int face, int minLength); + +/* Gets the intersection between the given bisector and the given DCEL + * for the given face. + */ +intersection_t **getIntersections(intersection_t **intersections, \ + int *numIntersections, bisector_t **bisectors, int numBisectors, \ + DCEL_t *dcel, int face, double minLength); + +/* Frees an array of intersections */ +void freeIntersections(intersection_t **intersections, int numIntersections); + +/* --------------------- My intersection functions end --------------------- */ + +/* ---------------------- My bisector functions start ---------------------- */ + +/* Creates a new bisector */ +bisector_t *newBisector(); + +/* Calculates and returns the equation of a bisector of two points */ +bisector_t *getABisector(bisector_t *bisector, vertex_t *pointA, \ + vertex_t *pointB); + +/* Returns a list of bisectors built from a list of points */ +bisector_t **getBisectors(bisector_t **bisectors, vertex_t **points, \ + int numPoints); +/* Returns a point at least distance away from the midpoint of the + * bisector given. If direction is 0, then the point is +distance away in the + * x-direction, otherwise -distance away in the x-direction. + */ +vertex_t *getBisectorPoint(double distance, bisector_t *b, int direction); + +/* Frees an array of bisectors */ +void freeBisectors(bisector_t **bisectors, int numBisectors); + +/* ----------------------- My bisector functions end ----------------------- */ + +/* ------------------------ My DCEL functions start ------------------------ */ + +/* Gets the diameter of the given face. */ +double getDiameter(DCEL_t *dcel, int faceIndex); + +/* ------------------------- My DCEL functions end ------------------------- */ + +/* ---------------------------------------------------------------------------- + * Here on out are the functions from Grady's base code on Ed + * ---------------------------------------------------------------------------- + */ + /* Allocate a new DCEL and return it. */ DCEL_t *newDCEL(); /* Allocate a new halfEdge and return it. */ halfEdge_t *newHalfEdge(); -/* Returns INSIDE if the points is on the INSIDE of the vector twin by the CW - * winding order, OUTSIDE if it is OUTSIDE by the CW winding order, and - * DIR_UNDECIDED if the point lies on the vector between the points v1 and v2. - */ -int getRelativeDir(double x, double y, vertex_t *v1, vertex_t *v2); - -/* Takes an established direction and a new direction, and returns 1 if the - * direction matches the decidedDirection or if the direction is undecided. - */ -int directionOrUndecided(int decidedDirection, int direction); - -/* Check there's space for another vertex in the DCEL, - * or increase the allocated space. +/* Check there's space for another vertex in the DCEL, or increase the + * allocated space. */ void ensureSpaceForVertex(DCEL_t *dcel); -/* Check there's space for another edge in the DCEL, - * or increase the allocated space. +/* Check there's space for another edge in the DCEL, or increase the + * allocated space. */ void ensureSpaceForEdge(DCEL_t *dcel); -/* Check there's space for another face in the DCEL, - * or increase the allocated space. +/* Check there's space for another face in the DCEL, or increase the + * allocated space. */ void ensureSpaceForFace(DCEL_t *dcel); @@ -121,24 +200,12 @@ DCEL_t *readPolygonFile(char *polygonfileName); /* Reads the next split from the given file. */ split_t *readNextSplit(FILE *splitfile); -/* Frees a given split. */ +/* Frees a given split. */ void freeSplit(split_t *split); /* Returns 1 if vertices are sufficiently close, 0 otherwise. */ int vertexMatch(vertex_t *v1, vertex_t *v2); -/* Gets the string for the given bisector equation. */ -char *getBisectorEquation(bisector_t *b); - -/* Representation of no face */ -#define NOFACE (-1) - -/* Default face for intersections. */ -#define DEFAULT_FACE 0 - -/* Default minimum length for bisector in each direction */ -#define DEFAULTMINLENGTH (200) - /* Applies a given split to the DCEL. */ void applySplit(split_t *split, DCEL_t *dcel); @@ -148,40 +215,37 @@ void freeDCEL(DCEL_t *dcel); /* Gets the number of faces in the DCEL. */ int getFaceCount(DCEL_t *dcel); +/* Returns INSIDE if the points is on the INSIDE of the vector twin by the CW + * winding order, OUTSIDE if it is OUTSIDE by the CW winding order, and + * DIR_UNDECIDED if the point lies on the vector between the points v1 and v2. + */ +int getRelativeDir(double x, double y, vertex_t *v1, vertex_t *v2); + +/* Takes an established direction and a new direction, and returns 1 if the + * direction matches the decidedDirection or if the direction is undecided. + */ +int directionOrUndecided(int decidedDirection, int direction); + /* Returns 1 if the given x,y point is inside the given face. */ int inFace(DCEL_t *dcel, double x, double y, int faceIndex); -/* Gets the intersection between the given bisector and the given DCEL - * for the given face. +/* ------------------- O'Rourke's intersection functions ------------------- */ + +/* Returns -1, 0 or 1, based on the area enclosed by the three points. + * 0 corresponds to no area enclosed */ -intersection_t **getIntersections(intersection_t **intersections, \ - int *numIntersections, bisector_t **bisectors, int numBisectors, \ - DCEL_t *dcel, int face, double minLength); - -/* Gets the diameter of the given face. */ -double getDiameter(DCEL_t *dcel, int faceIndex); - -/* Adds the watchtower to the Voronoi diagram represented by the given DCEL, - * applying required splits and setting the watchtower as required. - */ -void incrementalVoronoi(DCEL_t *dcel, tower_t *watchTower); - -/* O'Rourke's intersection functions */ -/* 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 point (x, y) is between (sx, sy) and (ex, ey) */ int between(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. + * 0 otherwise */ int collinear(double sx, double sy, double ex, double ey, double x, double y); /* Tests if the half edge and bisector are parallel and overlapping, or not - * intersecting. + * intersecting */ enum intersectType parallelIntersects(double heSx, double heSy, \ double heEx, double heEy, double bSx, double bSy, \ @@ -191,38 +255,4 @@ enum intersectType parallelIntersects(double heSx, double heSy, \ enum intersectType intersects(halfEdge_t *he, bisector_t *b, \ DCEL_t *dcel, double minLength, double *x, double *y); -/* My own functions */ - -/* Reads the polygon file and stores the information in the vertices array */ -vertex_t **readPolygon(vertex_t **vertices, FILE *polygonFile, \ - int *numVertices); - -/* Frees an array of vertices */ -void freeVertices(vertex_t **vertices, int numVertices); - -/* Calculates and returns the equation of a bisector of two points */ -bisector_t **getBisectors(bisector_t **bisectors, vertex_t **points, \ - int numPoints); - -/* Returns a point at least distance away from the midpoint of the - * bisector given. If direction is 0, then the point is +distance away in the - * x-direction, otherwise -distance away in the x-direction. - */ -vertex_t *getBisectorPoint(double distance, bisector_t *b, int direction); - -/* Create a new intersection */ -intersection_t *newIntersection(); - -/* Frees an array of bisectors */ -void freeBisectors(bisector_t **bisectors, int numBisectors); - -/* Frees an array of points */ -void freePoints(vertex_t **points, int numPoints); - -/* Frees an array of intersections */ -void freeIntersections(intersection_t **intersections, int numIntersections); - -/* Reads a points file and stores the information in the points array */ -vertex_t **readPoints(vertex_t **points, FILE *pointsFile, int *numPoints); - #endif diff --git a/dcel.o b/dcel.o index 78c1473..a271dd5 100644 Binary files a/dcel.o and b/dcel.o differ diff --git a/ed.supp b/ed.supp new file mode 100644 index 0000000..ff5a81e --- /dev/null +++ b/ed.supp @@ -0,0 +1,156 @@ +{ + + Memcheck:Cond + fun:runtime.adjustframe + fun:runtime.gentraceback + fun:runtime.copystack + fun:runtime.newstack + fun:runtime.morestack + fun:runtime.rt0_go +} +{ + + Memcheck:Cond + fun:runtime.adjustframe + fun:runtime.gentraceback + fun:runtime.copystack + fun:runtime.newstack + fun:runtime.morestack + fun:runtime.rt0_go +} +{ + + Memcheck:Cond + fun:runtime.adjustpointer + fun:runtime.adjustframe + fun:runtime.gentraceback + fun:runtime.copystack + fun:runtime.newstack + fun:runtime.morestack + fun:runtime.rt0_go +} +{ + + Memcheck:Cond + fun:runtime.adjustpointer + fun:runtime.adjustframe + fun:runtime.gentraceback + fun:runtime.copystack + fun:runtime.newstack + fun:runtime.morestack + fun:runtime.rt0_go +} +{ + + Memcheck:Leak + match-leak-kinds: possible + fun:calloc + fun:_dl_allocate_tls + fun:pthread_create@@GLIBC_2.2.5 + fun:_cgo_try_pthread_create + fun:x_cgo_sys_thread_create + fun:_rt0_amd64_lib + fun:call_init + fun:_dl_init + obj:/usr/lib/ld-2.33.so + obj:* + obj:* + obj:* +} +{ + + Memcheck:Leak + match-leak-kinds: possible + fun:calloc + fun:_dl_allocate_tls + fun:pthread_create@@GLIBC_2.2.5 + fun:_cgo_try_pthread_create + fun:_cgo_sys_thread_start + fun:runtime.asmcgocall + obj:* + fun:runtime.newm + fun:runtime.main.func1 + fun:runtime.systemstack + obj:/mnt/share/ed/libX11.so +} +{ + + Memcheck:Leak + match-leak-kinds: possible + fun:calloc + fun:_dl_allocate_tls + fun:pthread_create@@GLIBC_2.2.5 + fun:_cgo_try_pthread_create + fun:_cgo_sys_thread_start + fun:runtime.asmcgocall + obj:* + fun:runtime.newm + fun:runtime.startm + fun:runtime.newproc1 + fun:runtime.newproc.func1 + fun:runtime.systemstack +} +{ + + Memcheck:Leak + match-leak-kinds: possible + fun:calloc + fun:_dl_allocate_tls + fun:pthread_create@@GLIBC_2.2.5 + fun:_cgo_try_pthread_create + fun:_cgo_sys_thread_start + fun:runtime.asmcgocall + obj:* + fun:runtime.newm + fun:runtime.startm + fun:runtime.handoffp + fun:runtime.stoplockedm + fun:runtime.schedule +} +{ + + Memcheck:Leak + match-leak-kinds: possible + fun:calloc + fun:_dl_allocate_tls + fun:pthread_create@@GLIBC_2.2.5 + fun:_cgo_try_pthread_create + fun:_cgo_sys_thread_start + fun:runtime.asmcgocall + obj:* + fun:runtime.malg.func1 + fun:runtime.systemstack + obj:/mnt/share/ed/libX11.so + fun:runtime.rt0_go +} +{ + + Memcheck:Leak + match-leak-kinds: possible + fun:calloc + fun:_dl_allocate_tls + fun:pthread_create@@GLIBC_2.2.5 + fun:_cgo_try_pthread_create + fun:x_cgo_sys_thread_create + fun:_rt0_amd64_lib + fun:call_init + fun:_dl_init + obj:/usr/lib/ld-2.33.so +} +{ + + Memcheck:Leak + match-leak-kinds: possible + fun:calloc + fun:_dl_allocate_tls + fun:pthread_create@@GLIBC_2.2.5 + fun:_cgo_try_pthread_create + fun:_cgo_sys_thread_start + fun:runtime.asmcgocall + obj:* + obj:* + obj:* + obj:* + obj:* + obj:* +} diff --git a/input.c b/input.c index 1bec771..5a5927d 100644 --- a/input.c +++ b/input.c @@ -2,7 +2,7 @@ * * Created by Rory Healy (healyr@student.unimelb.edu.au) * Created on 8th September 2021 - * Last modified 9th September 2021 + * Last modified 14th September 2021 * * Contains functions for ensuring correct input arguments are given for * each stage of the voronoi2 program. Adapted from Grady's base code on Ed. @@ -13,10 +13,6 @@ #include "input.h" #endif -#ifndef COMMON_HEADER -#include "common.h" -#endif - #define STAGE_1_ARG_COUNT 4 #define STAGE_2_ARG_COUNT 5 #define STAGE_3_ARG_COUNT 5 diff --git a/input.h b/input.h index c8d5559..0d277ab 100644 --- a/input.h +++ b/input.h @@ -1,3 +1,7 @@ +#ifndef COMMON_HEADER +#include "common.h" +#endif + #ifndef INPUT_HEADER #define INPUT_HEADER diff --git a/input.o b/input.o index 325dbb4..a7d87f1 100644 Binary files a/input.o and b/input.o differ diff --git a/main.c b/main.c index 1a0c4f0..73b666f 100644 --- a/main.c +++ b/main.c @@ -2,14 +2,19 @@ * * Created by Rory Healy (healyr@student.unimelb.edu.au) * Created on 25th August 2021 - * Last modified 9th September 2021 + * Last modified 14th September 2021 * - * Lists the watchtowers that are in each face of the polygon. - * The polygon can be split using a pair of integers, which represent - * the edge numbers that should be connected. This comes from stdin. + * Stage 1: Computes and outputs equations for the bisectors of points + * Stage 2: Computes and outputs intersection points for bisectors against a + * given polygon + * Stage 3: Computes a Voronoi diagram using watchtowers as points and a + * polygon as the outer boundary. Outputs diameters of each Voronoi + * cell along with information about each watchtower. + * Stage 4: Same as Stage 4, but the watchtowers are sorted by ascending order + * by diameter. * * To see valid input arguments, run ./voronoi2 - * + * */ #ifndef VORONOI_HEADER @@ -25,6 +30,8 @@ int main(int argc, char **argv) { if (argc == 1) { printArgError(STAGE_ERROR, "Incorrect usage."); } + + /* Get stage, ensure correct input args */ enum stages stage = getStage(argv[1]); int expectedArgs = getExpectedArgs(stage); if (expectedArgs != argc) { diff --git a/main.o b/main.o index 4cceff8..8db26c3 100644 Binary files a/main.o and b/main.o differ diff --git a/output.txt b/output.txt index 5080ec9..a394812 100644 --- a/output.txt +++ b/output.txt @@ -1,6 +1 @@ -From Edge 0 (140.900000, -34.700000) to Edge 2 (150.000000, -34.700000) -From Edge 0 (140.900000, -35.200000) to Edge 2 (150.000000, -35.200000) -From Edge 1 (147.100000, -33.900000) to Edge 3 (147.100000, -39.200000) -From Edge 1 (147.100000, -33.900000) to Edge 3 (147.100000, -39.200000) -From Edge 1 (147.600000, -33.900000) to Edge 3 (147.600000, -39.200000) -From Edge 1 (146.900000, -33.900000) to Edge 3 (141.600000, -39.200000) +Watchtower ID: WT3765SHSPB, Postcode: 3765, Population Served: 3380, Watchtower Point of Contact Name: Eilene Horner, x: 145.362014, y: -37.818943, Diameter of Cell: 10.000000 diff --git a/towers.c b/towers.c index e9dc503..60527ce 100644 --- a/towers.c +++ b/towers.c @@ -2,7 +2,7 @@ * * Created by Rory Healy (healyr@student.unimelb.edu.au) * Created on 11th September 2021 - * Last modified 13th September 2021 + * Last modified 14th September 2021 * * Contains functions for reading a CSV of watchtower information into an * array of towers. @@ -18,38 +18,7 @@ #define MAX_FIELD_LEN 128 #define NUM_CSV_FIELDS 6 -tower_t **readTowers(tower_t **towers, FILE *datasetFile, int *numTowers) { - /* Maximum length of a single CSV line (+1 for null char in string) */ - size_t lineBufferSize = MAX_CSV_ENTRY_LEN + 1; - - /* Stores the current line from the CSV */ - char *lineBuffer = malloc(lineBufferSize * sizeof(*lineBuffer)); - checkNullPointer(lineBuffer); - - int maxSizetowers = 1; - - /* Discard the header line, then read the rest of the CSV */ - getline(&lineBuffer, &lineBufferSize, datasetFile); - while (getline(&lineBuffer, &lineBufferSize, datasetFile) > 0) { - /* Ensure there is enough space in the towers array */ - if (*numTowers == maxSizetowers) { - maxSizetowers *= 2; - tower_t **temp = realloc(towers, maxSizetowers * sizeof(*towers)); - checkNullPointer(temp); - towers = temp; - } - - /* The current tower being filled in with information */ - towers[*numTowers] = malloc(sizeof(*towers[*numTowers])); - - readCurrentTower(lineBuffer, towers[*numTowers]); - *numTowers += 1; - } - free(lineBuffer); - return towers; -} - -void readCurrentTower(char *lineBuffer, tower_t *tower) { +void readATower(char *lineBuffer, tower_t *tower) { /* Stores the current CSV field for the current line */ char *token = strtok(lineBuffer, ","); size_t tokenLength; @@ -57,11 +26,8 @@ void readCurrentTower(char *lineBuffer, tower_t *tower) { for (int i = 0; i < NUM_CSV_FIELDS; i++) { tokenLength = strlen(token); switch(i) { - /* Case 0, 1, and 3 deal with strings - * Case 2 deals with an integer - * Case 4 and 5 deal with doubles - * Each case is handled seperately to fill in the - * tower with no space wasted. + /* Cases represent each field in the CSV, and as such are handled + * differently. */ case 0: tower->id = malloc(sizeof(char) * tokenLength + 1); @@ -95,12 +61,55 @@ void readCurrentTower(char *lineBuffer, tower_t *tower) { } } +tower_t **readTowers(tower_t **towers, FILE *datasetFile, int *numTowers) { + /* Maximum length of a single CSV line (+1 for null char in string) */ + size_t lineBufferSize = MAX_CSV_ENTRY_LEN + 1; + + /* Stores the current line from the CSV */ + char *lineBuffer = malloc(lineBufferSize * sizeof(*lineBuffer)); + checkNullPointer(lineBuffer); + + int maxSizetowers = 1; + + /* Discard the header line, then read the rest of the CSV */ + getline(&lineBuffer, &lineBufferSize, datasetFile); + while (getline(&lineBuffer, &lineBufferSize, datasetFile) > 0) { + /* Ensure there is enough space in the towers array */ + if (*numTowers == maxSizetowers) { + maxSizetowers *= 2; + tower_t **temp = realloc(towers, maxSizetowers * sizeof(*towers)); + checkNullPointer(temp); + towers = temp; + } + + /* The current tower being filled in with information */ + towers[*numTowers] = malloc(sizeof(*towers[*numTowers])); + + readATower(lineBuffer, towers[*numTowers]); + *numTowers += 1; + } + free(lineBuffer); + return towers; +} + void freeTowers(tower_t **towers, int numTowers) { + if (!towers) { + return; + } + for (int i = 0; i < numTowers; i++) { - free(towers[i]->id); - free(towers[i]->manager); - free(towers[i]->postcode); - free(towers[i]); + if (towers[i]->id) { + free(towers[i]->id); + } + if (towers[i]->manager) { + free(towers[i]->manager); + } + if (towers[i]->postcode) { + free(towers[i]->postcode); + } + if (towers[i]) { + free(towers[i]); + } } free(towers); } diff --git a/towers.h b/towers.h index 2e9a696..967c903 100644 --- a/towers.h +++ b/towers.h @@ -14,12 +14,12 @@ typedef struct tower { double y; } tower_t; +/* Reads the current row from the CSV and converts it into a new tower */ +void readATower(char *lineBuffer, tower_t *tower); + /* Reads the CSV file and stores the information in the towers array */ tower_t **readTowers(tower_t **towers, FILE *datasetFile, int *numTowers); -/* Reads the current row from the CSV and converts it into a new tower */ -void readCurrentTower(char *lineBuffer, tower_t *tower); - /* Frees all towers in a towers array */ void freeTowers(tower_t **towers, int numTowers); diff --git a/towers.o b/towers.o index 32c15bb..f4723f3 100644 Binary files a/towers.o and b/towers.o differ diff --git a/voronoi.c b/voronoi.c index abeedd6..e97cc3c 100644 --- a/voronoi.c +++ b/voronoi.c @@ -2,7 +2,7 @@ * * Created by Rory Healy (healyr@student.unimelb.edu.au) * Created on 12th August 2021 - * Last modified 13th September 2021 + * Last modified 14th September 2021 * * Contains functions involving the generation of the Voronoi diagram, and * running each stage of the assignment. @@ -30,6 +30,58 @@ void stage2PrintIntersection(intersection_t *intersection, FILE *outputFile) { intersection->toPoint->x, intersection->toPoint->y); } +void stage34PrintTowers(tower_t *tower, FILE *outputFile, double diameter) { + fprintf(outputFile, "Watchtower ID: %s, Postcode: %s, Population " \ + "Served: %d, Watchtower Point of Contact Name: %s, x: %lf, y: %lf, " \ + "Diameter of Cell: %lf\n", tower->id, tower->postcode, \ + tower->population, tower->manager, tower->x, tower->y, diameter); +} + +int getFaceTowerIsIn(DCEL_t *dcel, tower_t *tower) { + for (int i = 0; i < getFaceCount(dcel); i++) { + if (inFace(dcel, tower->x, tower->y, i)) { + return i; + } + } + return -1; +} + +void incrementalVoronoi(DCEL_t *voronoi, tower_t *tower) { + int faceTowerIsIn = getFaceTowerIsIn(voronoi, tower); + if (faceTowerIsIn == -1) { + fprintf(stderr, "Error: Watchtower %s is outside the polygon.\n", \ + tower->id); + exit(EXIT_FAILURE); + } +} + +DCEL_t *newVoronoi(DCEL_t *voronoi, tower_t *towerA, tower_t *towerB) { + /* Get vertices from towers */ + vertex_t *pointA = getAPoint(towerA->x, towerA->y); + vertex_t *pointB = getAPoint(towerB->x, towerB->y); + + /* Create first bisector */ + bisector_t *bisector = newBisector(); + bisector = getABisector(bisector, pointA, pointB); + + /* Get intersection of bisector with the polygon */ + intersection_t *intersection = NULL; + intersection = getAnIntersection(intersection, voronoi, bisector, \ + DEFAULT_FACE, DEFAULTMINLENGTH); + + if (intersection) { + free(intersection->fromPoint); + free(intersection->toPoint); + free(intersection); + } + + free(bisector->mid); + free(bisector); + free(pointA); + free(pointB); + return voronoi; +} + void stage1(char *pointsFileName, char *outputFileName) { FILE *pointsFile = NULL, *outputFile = NULL; pointsFile = safeFileOpen(&pointsFile, pointsFileName, "r"); @@ -59,7 +111,9 @@ void stage1(char *pointsFileName, char *outputFileName) { fclose(outputFile); } -void stage2(char *pointsFileName, char *polygonFileName, char *outputFileName) { +void stage2(char *pointsFileName, char *polygonFileName, \ + char *outputFileName) { + FILE *pointsFile = NULL, *outputFile = NULL; pointsFile = safeFileOpen(&pointsFile, pointsFileName, "r"); outputFile = safeFileOpen(&outputFile, outputFileName, "w"); @@ -83,8 +137,8 @@ void stage2(char *pointsFileName, char *polygonFileName, char *outputFileName) { intersection_t **intersections = malloc(sizeof(*intersections)); checkNullPointer(intersections); int numIntersections = 0; - intersections = getIntersections(intersections, &numIntersections, bisectors, numBisectors, dcel, DEFAULT_FACE, \ - DEFAULTMINLENGTH); + intersections = getIntersections(intersections, &numIntersections, \ + bisectors, numBisectors, dcel, DEFAULT_FACE, DEFAULTMINLENGTH); for (int i = 0; i < numIntersections; i++) { stage2PrintIntersection(intersections[i], outputFile); @@ -105,9 +159,22 @@ void stage3(char *dataFileName, char *polygonFileName, char *outputFileName) { polygonFile = safeFileOpen(&polygonFile, polygonFileName, "r"); outputFile = safeFileOpen(&outputFile, outputFileName, "w"); + /* Read towers from the data file */ + tower_t **towers = malloc(sizeof(*towers)); + checkNullPointer(towers); + int numTowers = 0; + towers = readTowers(towers, dataFile, &numTowers); + /* Construct the DCEL from the polygon file */ + DCEL_t *dcel = readPolygonFile(polygonFileName); + /* Add each tower to the Voronoi DCEL */ + + /* Get diameters and output to outputFile */ + /* Clean up */ + freeDCEL(dcel); + freeTowers(towers, numTowers); fclose(dataFile); fclose(polygonFile); fclose(outputFile); @@ -119,9 +186,22 @@ void stage4(char *dataFileName, char *polygonFileName, char *outputFileName) { polygonFile = safeFileOpen(&polygonFile, polygonFileName, "r"); outputFile = safeFileOpen(&outputFile, outputFileName, "w"); + /* Read towers from the data file */ + tower_t **towers = malloc(sizeof(*towers)); + checkNullPointer(towers); + int numTowers = 0; + towers = readTowers(towers, dataFile, &numTowers); + /* Construct the DCEL from the polygon file */ + DCEL_t *dcel = readPolygonFile(polygonFileName); + + /* Add each tower to the Voronoi DCEL */ + + /* Get diameters and sort, then output to outputFile */ /* Clean up */ + freeDCEL(dcel); + freeTowers(towers, numTowers); fclose(dataFile); fclose(polygonFile); fclose(outputFile); diff --git a/voronoi.h b/voronoi.h index 7088143..0aaaecf 100644 --- a/voronoi.h +++ b/voronoi.h @@ -11,6 +11,20 @@ void stage1PrintBisector(bisector_t *bisector, FILE *outputFile); /* Prints intersections to an output file */ void stage2PrintIntersection(intersection_t *intersection, FILE *outputFile); +/* Prints the watchtower information to an output file */ +void stage34PrintTowers(tower_t *tower, FILE *outputFile, double diameter); + +/* Returns the face a tower is in */ +int getFaceTowerIsIn(DCEL_t *dcel, tower_t *tower); + +/* Adds the watchtower to the Voronoi diagram represented by the given DCEL, + * applying required splits and setting the watchtower as required. + */ +void incrementalVoronoi(DCEL_t *voronoi, tower_t *tower); + +/* Creates a new Voronoi structure */ +DCEL_t *newVoronoi(DCEL_t *voronoi, tower_t *towerA, tower_t *towerB); + /* Outputs the bisector equations from pointsFile into outputFile */ void stage1(char *pointsFileName, char *outputFileName); diff --git a/voronoi.o b/voronoi.o index bd23345..343e528 100644 Binary files a/voronoi.o and b/voronoi.o differ diff --git a/voronoi2 b/voronoi2 index ed784ff..440544b 100755 Binary files a/voronoi2 and b/voronoi2 differ