From b1697a0c30e953ac805e291957e4111af71fc609 Mon Sep 17 00:00:00 2001 From: Rory Healy Date: Sat, 11 Sep 2021 22:47:44 +1000 Subject: [PATCH] Refactored code, integrated base code (DCEL) --- Makefile | 8 +- common.h | 7 + common.o | Bin 6536 -> 6616 bytes dcel.c | 1083 +++++++++++++++++++++++++++++++++++++++++++++++++++- dcel.h | 253 +++++++++++- dcel.o | Bin 7352 -> 42528 bytes geometry.c | 56 --- geometry.h | 19 - geometry.o | Bin 4600 -> 0 bytes input.o | Bin 8752 -> 8832 bytes main.o | Bin 7288 -> 7328 bytes output.txt | 6 - towers.c | 97 +++++ towers.h | 26 ++ towers.o | Bin 0 -> 9848 bytes voronoi.c | 138 ++----- voronoi.h | 21 +- voronoi.o | Bin 16920 -> 15384 bytes voronoi2 | Bin 34256 -> 63952 bytes 19 files changed, 1487 insertions(+), 227 deletions(-) delete mode 100644 geometry.c delete mode 100644 geometry.h delete mode 100644 geometry.o create mode 100644 towers.c create mode 100644 towers.h create mode 100644 towers.o diff --git a/Makefile b/Makefile index c786681..e725bfa 100644 --- a/Makefile +++ b/Makefile @@ -16,14 +16,14 @@ # gcc -Wall -Wextra -Werror -pedantic -g -o main.o main.c -c # Link command: -voronoi2: common.o geometry.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 geometry.o common.o +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 -lm common.o: common.c gcc -Wall -Wextra -Werror -pedantic -g -o common.o common.c -c -geometry.o: geometry.c - gcc -Wall -Wextra -Werror -pedantic -g -o geometry.o geometry.c -c +towers.o: towers.c + gcc -Wall -Wextra -Werror -pedantic -g -o towers.o towers.c -c dcel.o: dcel.c gcc -Wall -Wextra -Werror -pedantic -g -o dcel.o dcel.c -c diff --git a/common.h b/common.h index 7709843..8527956 100644 --- a/common.h +++ b/common.h @@ -19,6 +19,13 @@ #endif +#ifndef MATH_HEADER +#define MATH_HEADER + +#include + +#endif + #ifndef COMMON_HEADER #define COMMON_HEADER diff --git a/common.o b/common.o index 734d90fe2cbc13637b2445741bbe533d8901260f..d255b408f06158bb9091861590835434d41b065e 100644 GIT binary patch delta 830 zcmZ9IK}Zx)7{}+E8C}=} z{skR^Z3u$wAnQm(blF>Vlf8J!+Djx-PL=*fKB;XRn|b&7H7GxHux6jPx~lUF(Gt#Os@<~-cW%tzOkQLz zn#z-W$xd0)qob^0cxf|wRnMqz2Ng${T_$ch)6~Q#C!kl@cLMocMUPgLw*P^rxRevJ z$#hAh*RYriXc@7ptJug*n3jbT?rHg%1G`$_AtG1M9+ureK4VtoSvxb<(R77uF}NTfW7X<_Y0>g4R*; zCg?gAJt1v&jB=uLmv4v_Z7`kE=vQp0|7>F03+8S!d#-sta`vUBKj!yY(ey*sS2cZ_ z_wP~Z7gx75dY|_{HT^H2Zt-h%?)9hRvy!#`Uqq8hd^|&R3aK78kPby3@cA=0urVh($>IXp0ateh-TNIGse~hcu(& RA>Bh;)jbqsNHs)qhPn)QfHWCm5DYUY1gQQ4j6GE!w zzBa_1*cgK0vKA-NBApUJ)^A~csR=AZsFGON%XfmhrWc_->`sbTdJo_?-{g(Wjmxh*wOJJQhP%D6gcmkRWI1^ z-7w7Iz9Z3ci&%52s?D^@+a7M&#%D*W*Gw-NbO6r{X%81&sSa2(YiQbtHQnUyl0i?P zqiH^unb!@vinn_HD9DrQG2gLgXwISLNqT}S-UMC7vR3}$P0q06@RkqQ(ex4fZyNL+ zQa$GtN|kVYirI>x*=BFY=wIi01D%Sz`o^F!*B_1k7oPUR==Zrk5Q#W8AZjG(@dD8) z{2s2C+;VnZ595BFZXos}9lL%cURLnUZ_+$!G7|e0%*bYG{Ya*^@l4jKhg3%Tch!PO gd@5lkXyR^AL@#KNi!_L65;YakE@B;9=&I9y0M?>=$p8QV diff --git a/dcel.c b/dcel.c index 52af33b..9ba0348 100644 --- a/dcel.c +++ b/dcel.c @@ -2,22 +2,21 @@ * * Created by Rory Healy (healyr@student.unimelb.edu.au) * Created on 25th August 2021 - * Last modified 9th September 2021 + * Last modified 11th September 2021 * - * Contains functions for the DCEL data structure, including creating, + * Contains functions for the DCEL data structure, including initialisation, * splitting an edge, and identifying towers in faces. * + * Contains several functions and structures from Grady Fitzpatrick's Base + * Code on the Ed Discussion Forum. + * */ #ifndef DCEL_HEADER #include "dcel.h" #endif -#ifndef COMMON_HEADER -#include "common.h" -#endif - -/* Reads the polygon file and stores the information in the vertices array. */ +/* Deprecated by Grady's sample solution */ vertex_t **readPolygon(vertex_t **vertices, FILE *polygonFile, \ int *numVertices) { double currentX, currentY; @@ -47,3 +46,1073 @@ void freeVertices(vertex_t **vertices, int numVertices) { } free(vertices); } + +bisector_t *getBisector(vertex_t *pointA, vertex_t *pointB) { + bisector_t *newBisector = malloc(sizeof(*newBisector)); + checkNullPointer(newBisector); + + double midpointX = (pointA->x + pointB->x) / 2; + double midpointY = (pointA->y + pointB->y) / 2; + + newBisector->midX = midpointX; + newBisector->midY = midpointY; + + /* 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. + */ + newBisector->slope = 0; + newBisector->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. + */ + newBisector->isSlopeInfinite = 1; + + /* Not actually zero, just a placeholder to prevent + * accidental errors + */ + newBisector->slope = 0; + } else { + /* The line segment AB has a non-zero and finite gradient, + * so the gradient of the bisector can be calculated. + */ + newBisector->isSlopeInfinite = 0; + newBisector->slope = -1 / \ + ((pointB->y - pointA->y) / (pointB->x - pointA->x)); + } + + return newBisector; +} + +/* 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) + +double dist_2D(double x1, double y1, double x2, double y2) { + return sqrt(pow(x2 - x1, 2) + pow(y2 - y1, 2)); +} + +double inv_dist_2D(double x1, double y1, double m, double c1, double d) { + /* Solve sqrt((y - y1)^2 + (x - x1)^2) = d^2 for x by substituting + * y = mx + c1, which gives an equation for x + * Using this, you can find a point that lies on the line y = mx + c1 + * that is of distance d away from (x1, y1). + */ + double a = m * m + 1; + double b = 2 * c1 * m - 2 * x1 - 2 * m * y1; + double c = c1 * c1 + x1 * x1 + y1 * y1 - 2 * c1 * y1 - d * d; + return (-b + sqrt(b * b - 4 * a * c)) / (2 * a); +} + +vertex_t *getBisectorPoint(double distance, bisector_t *b) { + vertex_t *returnPoint = malloc(sizeof(*returnPoint)); + if (b->isSlopeInfinite) { + returnPoint->x = b->midX; + returnPoint->y = b->midY + distance; + } else if (b->slope == 0) { + returnPoint->x = b->midX + distance; + returnPoint->y = b->midY; + } else { + /* Find the y-intercept of the bisector, use it to find the general + * form of the bisector in the form of y = mx + c + */ + double c = b->midY - b->slope * b->midX; + returnPoint->x = inv_dist_2D(b->midX, b->midY, b->slope, c, distance); + returnPoint->y = b->slope * returnPoint->x + c; + } + + /* Due to floating-point precision, there may be a situation where the + * distance between the bisector midpoint and returnPoint is slightly less + * than distance, but unless getBisectorPoint needs to guarentee that this + * distance is greater than distance, it shouldn't be an issue. + */ + + return returnPoint; +} + +/* +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; + } +} + +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; + } + } +} + +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; + } +} + +enum intersectType parallelIntersects(double heSx, double heSy, \ + double heEx, double heEy, double bSx, double bSy, \ + double bEx, double bEy, double *x, double *y) { + if (!collinear(heSx, heSy, heEx, heEy, bSx, bSy)) { + /* Parallel, no intersection so don't set (x, y) */ + return DOESNT_INTERSECT; + } + /* bS between heS and heE */ + if (between(heSx, heSy, heEx, heEy, bSx, bSy)) { + *x = bSx; + *y = bSy; + return SAME_LINE_OVERLAP; + } + /* bE between heS and heE */ + if (between(heSx, heSy, heEx, heEy, bEx, bEy)) { + *x = bEx; + *y = bEy; + return SAME_LINE_OVERLAP; + } + /* heS between bS and bE */ + if (between(bSx, bSy, bEx, bEy, heSx, heSy)) { + *x = heSx; + *y = heSy; + return SAME_LINE_OVERLAP; + } + /* heE between bS and bE */ + if (between(bSx, bSy, bEx, bEy, heEx, heEy)) { + *x = heEx; + *y = heEy; + return SAME_LINE_OVERLAP; + } + + return DOESNT_INTERSECT; +} + +enum intersectType intersects(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 */ + double bSx = b->startX; + double bSy = b->startY; + double bEx = b->endX; + double bEy = b->endY; + + /* Fill in segment. */ + // min-length not used here? + printf("In intersets(): minlength = %lf\n", minLength); + + /* 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; + } +} + +char *getIntersectionString(intersection_t *intersection) { + /* + + + + FILL IN + + + + */ + if (!intersection) { + return NULL; + } + char *returnString = NULL; + if (0 <= 0) { + /* Find out memory needed. */ + int stringLength = snprintf(returnString, 0, + "From Edge %d (%lf, %lf) to Edge %d (%lf, %lf)", + 0, 0.0, 0.0, + 0, 0.0, 0.0); + returnString = malloc(sizeof(*returnString) * (stringLength + 1)); + checkNullPointer(returnString); + sprintf(returnString, "From Edge %d (%lf, %lf) to Edge %d (%lf, %lf)", + 0, 0.0, 0.0, + 0, 0.0, 0.0); + } else { + /* Find out memory needed. */ + int stringLength = snprintf(returnString, 0, + "From Edge %d (%lf, %lf) to Edge %d (%lf, %lf)", + 0, 0.0, 0.0, + 0, 0.0, 0.0); + returnString = malloc(sizeof(*returnString) * (stringLength + 1)); + checkNullPointer(returnString); + sprintf(returnString, "From Edge %d (%lf, %lf) to Edge %d (%lf, %lf)", + 0, 0.0, 0.0, + 0, 0.0, 0.0); + } + return returnString; +} + +void freeIntersection(intersection_t *intersection) { + if (!intersection) { + return; + } + free(intersection); +} + +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; +} + +int getDCELPointCount(DCEL_t *dcel) { + if (!dcel) { + return 0; + } + return dcel->verticesUsed; +} + +double getDCELVertexX(DCEL_t *dcel, int vertex) { + return (dcel->vertices)[vertex].x; +} + +double getDCELVertexY(DCEL_t *dcel, int vertex) { + return (dcel->vertices)[vertex].y; +} + +int getDCELEdgeCount(DCEL_t *dcel) { + if (!dcel) { + return 0; + } + return dcel->edgesUsed; +} + +int getDCELEdgeVertexStart(DCEL_t *dcel, int edge) { + if (!dcel) { + return 0; + } + return (dcel->edges)[edge].halfEdge->startVertex; +} + +int getDCELEdgeVertexEnd(DCEL_t *dcel, int edge) { + if (!dcel) { + return 0; + } + return (dcel->edges)[edge].halfEdge->endVertex; +} + +int getDCELEdgeVertexPairStart(DCEL_t *dcel, int edge) { + if (!dcel) { + return 0; + } + return (dcel->edges)[edge].halfEdge->twin->startVertex; +} + +int getDCELEdgeVertexPairEnd(DCEL_t *dcel, int edge) { + if (!dcel) { + return 0; + } + return (dcel->edges)[edge].halfEdge->twin->endVertex; +} + +int DCELhasEdge(DCEL_t *dcel, int edge) { + if ((dcel->edges)[edge].halfEdge->face != NOFACE) { + return 1; + } else { + return 0; + } +} + +int DCELhasEdgePair(DCEL_t *dcel, int edge) { + if ((dcel->edges)[edge].halfEdge->face == NOFACE) { + return 0; + } + if ((dcel->edges)[edge].halfEdge->twin) { + return 1; + } else { + return 0; + } +} + +intersection_t *getIntersection(bisector_t *b, DCEL_t *dcel, int face, + double minLength) { + // FILL IN + printf("In getIntersection(): bisector start: %lf, dcel numfaces = %d" \ + "face = %d, minLength = %lf\n", b->startX, dcel->facesAllocated, \ + face, minLength); + return NULL; +} + +double getDiameter(DCEL_t *dcel, int faceIndex) { + // FILL IN + printf("in getDiamter(): faceIndex = %d, dcel faces = %d\n", \ + faceIndex, dcel->facesAllocated); + return NODIAMETER; +} + +void incrementalVoronoi(DCEL_t *dcel, tower_t *tower) { + // FILL IN + printf("In incrementalVoronoi(): tower id = %s, dcel faces = %d\n", \ + tower->id, dcel->facesAllocated); +} diff --git a/dcel.h b/dcel.h index cabca01..d13827a 100644 --- a/dcel.h +++ b/dcel.h @@ -1,32 +1,261 @@ -#ifndef GEOMETRY_HEADER -#include "geometry.h" -#endif - -#ifndef STDIO_HEADER -#include +#ifndef TOWERS_HEADER +#include "towers.h" #endif #ifndef DCEL_HEADER #define DCEL_HEADER -typedef struct halfEdge { - struct halfEdge *previous; - struct halfEdge *next; - struct halfEdge *twin; +/* Here is the Base Code from Grady, with some alterations by me */ + +enum intersectType { + DOESNT_INTERSECT = 0, // Doesn't intersect + INTERSECT = 1, // Intersects + SAME_LINE_OVERLAP = 2, // Lines are the same + ENDS_OVERLAP = 3 // Intersects at exactly one point (endpoint) +}; + +typedef struct vertex { + double x; + double y; +} vertex_t; + +typedef struct bisector { + double startX; + double startY; + double endX; + double endY; + + /* This refers to the midpoint of the line segment AB, not the midpoint + * of the bisector itself. + */ + double midX; + double midY; + + int isSlopeInfinite; + double slope; +} bisector_t; + +typedef struct halfEdgeLabel { + struct halfEdgeLabel *previous; + struct halfEdgeLabel *next; + struct halfEdgeLabel *twin; int face; int edge; + int startVertex; + int endVertex; } halfEdge_t; typedef struct edge { - halfEdge_t halfEdge; + halfEdge_t *halfEdge; } edge_t; typedef struct face { - halfEdge_t start; + halfEdge_t *start; + tower_t *tower; } face_t; +typedef struct split { + int startEdge; + int endEdge; + int verticesSpecified; + vertex_t startPoint; + vertex_t endPoint; +} split_t; + +typedef struct intersection { + vertex_t intersectionPoint; +} intersection_t; + +typedef struct DCEL { + edge_t *edges; + int edgesUsed; + int edgesAllocated; + + face_t *faces; + int facesUsed; + int facesAllocated; + + vertex_t *vertices; + int verticesUsed; + int verticesAllocated; +} DCEL_t; + +/* 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. + */ +void ensureSpaceForVertex(DCEL_t *dcel); + +/* 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. + */ +void ensureSpaceForFace(DCEL_t *dcel); + +/* Add an edge from the startVertex index vertex to the endVertex index. + * Only fills one half-edge as other half-edges will always be added + * through geometry construction. + */ +void addEdge(DCEL_t *dcel, int startVertex, int endVertex); + +/* Add a face to the DCEL given using the given halfEdge and sets the face. */ +void addFace(DCEL_t *dcel, halfEdge_t *he); + +/* Reads the polygon from the given file. */ +DCEL_t *readPolygonFile(char *polygonfileName); + +/* Reads the next split from the given file. */ +split_t *readNextSplit(FILE *splitfile); + +/* 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); + +/* Frees the given bisector. */ +void freeBisector(bisector_t *bisector); + +/* 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) + +/* Gets the intersection between the given bisector and the given DCEL + * for the given face. + */ +intersection_t *getIntersection(bisector_t *b, DCEL_t *dcel, \ + int face, double minLength); + +/* Gets the string for the given intersection. */ +char *getIntersectionString(intersection_t *intersection); + +/* Frees a given intersection. */ +void freeIntersection(intersection_t *intersection); + +/* Applies a given split to the DCEL. */ +void applySplit(split_t *split, DCEL_t *dcel); + +/* Frees the given DCEL */ +void freeDCEL(DCEL_t *dcel); + +/* Gets the number of faces in the DCEL. */ +int getFaceCount(DCEL_t *dcel); + +/* 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 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); + +/* Returns the number of vertices in the DCEL. */ +int getDCELPointCount(DCEL_t *dcel); + +/* Get x value of given vertex in DCEL. */ +double getDCELVertexX(DCEL_t *dcel, int vertex); + +/* Get y value of given vertex in DCEL. */ +double getDCELVertexY(DCEL_t *dcel, int vertex); + +/* Returns the number of edges in the DCEL. */ +int getDCELEdgeCount(DCEL_t *dcel); + +/* Get start vertex of given edge in DCEL. */ +int getDCELEdgeVertexStart(DCEL_t *dcel, int edge); + +/* Get end vertex of given edge in DCEL. */ +int getDCELEdgeVertexEnd(DCEL_t *dcel, int edge); + +/* Get start vertex of paired given edge in DCEL. */ +int getDCELEdgeVertexPairStart(DCEL_t *dcel, int edge); + +/* Get end vertex of paired given edge in DCEL. */ +int getDCELEdgeVertexPairEnd(DCEL_t *dcel, int edge); + +/* Check if the DCEL has the given edge. */ +int DCELhasEdge(DCEL_t *dcel, int edge); + +/* Check if the DCEL has a pair for the given edge. */ +int DCELhasEdgePair(DCEL_t *dcel, int edge); + +/* 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 *getBisector(vertex_t *pointA, vertex_t *pointB); + +/* Euclidian distance between two 2D points */ +double dist_2D(double x1, double y1, double x2, double y2); + +/* Returns a value for x that ensures the point (x, y) is at least d (distance) + * away from (x1, y1) whilst ensuring (x, y) is on the line y = mx + c1. + */ +double inv_dist_2D(double x1, double y1, double m, double c, double d); + +/* Returns a point at least distance away from the midpoint of the bisector given */ +vertex_t *getBisectorPoint(double distance, bisector_t *b); + +/* O'Rourke's 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. + */ +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. + */ +enum intersectType parallelIntersects(double heSx, double heSy, \ + double heEx, double heEy, double bSx, double bSy, \ + double bEx, double bEy, double *x, double *y); + +/* Tests if */ +enum intersectType intersects(halfEdge_t *he, bisector_t *b, \ + DCEL_t *dcel, double minLength, double *x, double *y); + #endif diff --git a/dcel.o b/dcel.o index 88feeaf856fd518146eab4412821c9420a839f97..aca2613661e378d5ea3080a60ef625dbcc94bec5 100644 GIT binary patch literal 42528 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�U)pP?|wdruYo{ymlRdo}W`8h%O*IfY(&Mh(Vb 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 ax0qC(_aU1s5zRjPn34LvR~TtK|NjM)S?YNJ literal 7352 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 diff --git a/geometry.c b/geometry.c deleted file mode 100644 index ea75e12..0000000 --- a/geometry.c +++ /dev/null @@ -1,56 +0,0 @@ -/* geometry.c - * - * Created by Rory Healy (healyr@student.unimelb.edu.au) - * Created on 9th September 2021 - * Last modified 9th September 2021 - * - * A small library of functions used in the geometric construction of a - * Voronoi diagram. - * - */ - -#ifndef GEOMETRY_HEADER -#include "geometry.h" -#endif - -#ifndef COMMON_HEADER -#include "common.h" -#endif - -bisector_t getBisector(vertex_t *pointA, vertex_t *pointB) { - bisector_t newBisector; - double midpointX = (pointA->x + pointB->x) / 2; - double midpointY = (pointA->y + pointB->y) / 2; - - newBisector.x = midpointX; - newBisector.y = midpointY; - - /* 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. - */ - newBisector.slope = 0; - newBisector.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. - */ - newBisector.isSlopeInfinite = 1; - - /* Not actually zero, just a placeholder to prevent - * accidental errors - */ - newBisector.slope = 0; - } else { - /* Slope of the line segment AB */ - double segmentSlope = \ - (pointB->y - pointA->y) / (pointB->x - pointA->x); - - /* Calculate orthogonal slope */ - newBisector.isSlopeInfinite = 0; - newBisector.slope = -1 / segmentSlope; - } - - return newBisector; -} diff --git a/geometry.h b/geometry.h deleted file mode 100644 index f199c17..0000000 --- a/geometry.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef GEOMETRY_HEADER -#define GEOMETRY_HEADER - -typedef struct vertex { - double x; - double y; -} vertex_t; - -typedef struct bisector { - int isSlopeInfinite; - double slope; - double x; - double y; -} bisector_t; - -/* Calculates and returns the equation of a bisector of two points */ -bisector_t getBisector(vertex_t *vertexA, vertex_t *vertexB); - -#endif \ No newline at end of file diff --git a/geometry.o b/geometry.o deleted file mode 100644 index 5e75cfe7c709b6d43210cfa908550865093c81cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4600 zcmbVOVQdsd6rSC^ZMWRDckPwZT7nm$DuLU!KoC&sK|Q$=Fd#N5K_u7f_U>Z4S9kZI zC6b!{P#R*=OGqHbL@_Zzqe+easEGm%i4xSrs6UAQAtoj$F;@H`Dha+f`{sHb?iVk) zo%en7zIktEXJ>9Qx#wwBQJ64=^{_=GSjNUfi++N;3D&}vv%kWt{_Rgq|B{+MF_)Sy z&ZegO&!{j>%@Eem?C@3YO5=OPKZ$X#5f`SF~05&FX zla$f;#&Fo2Ki-PZV$*Q=k4qSI6X+ zaHJCA83HO71Ps4Xe3Fna=PIQ+^d73o#B7x}Ux#_uA*(b$guYgKdy?=bAD^9E#`~8? za0Ioq&xb9%Zml#&-77d&ONk&xqwVn<=EryP)l^F<#zMvbb}5ZYM?*BIHMBQ0DowSK z6)<{K(HermZNbNbTZ2jz%Bg3X;lsY@78p9Ph6RH?AYx2!VOoRegGGkWGQ`kY3WWtQ zOy$Wo(fdeSUlbE2TF>lk!z6Wjg;H&0Y}qD>2nM0pCRq4T7}EEFhOLJmf8&fVFiwb@ zSXzNa`yn_Q>x8GG{MS$qPkv0mk}E806-F)3nJ_aLT}C-XOn-va`H28%QAK~y=+W0r z>8q#o&~5-xecg<{dImw%Xnso%pVL=P>Bf}aenIaj=`AJf4A1Bt7xeZCy?IuT&FHN& zy1855Hl=Tv(j$F($7wxQ)Z4G|Mem$}jGip590qHS%@TDIln zGXT?0CbxZj%jWpzO|f`379UQ#6Lu!&6zoFLjTh}>d3z}CrJ-CGI$bZFc`-ib6g%H^ zyH&a%C-&ZJntw>%)1mq2Y!$wGevOrnswEnY@vC(ShxbKOMLp%Oy!^3dmh2Kd9nkQ@ z#kIS0*S7;mg2{Y@rYd-Txj-C0&!NRLlppcXqG3H~$m4y`L;6M}!36(?58^n-~aj&BS_(aT4T|&pX2OdPEp& z2M^y*qRjY?K}TXabc3bEDJZ=Xm+6T|hxcO+=Be!GE_#{`P4C*1`E z6(+|XhCZ3Y@ZU(^RiQ;!jvWB5YW;M@3O&|e2S2&~m#T~j6nlU^`i~G)n4EtE`l{uB zh4PDa!u&|d`A-0cV~RS#<@Ocj|5{2YplDA*bT$1M`k-)sSDMg?wgjERuZ;Vg^pza? za_l+a@IIFJ?2h&p%?F^T5e3$ sijxAn<~RKRCu(Iq?kV}(N67-|MIPkPxmo_>lGg17G?WAOHXW diff --git a/input.o b/input.o index 325dbb47f8f935acdcbeb60b68960fe5217e05f2..a46796f0a8ae85dd177e258f6564522d68593110 100644 GIT binary patch delta 1013 zcmZ9KL1+^}6o&W3#AIV7t~NWF*rbUdMp2?5#DlPuYSTqgDqd8iRJ2|yv1malqM;xJ z6&rrUn^Z*9gV?wSJt`^^JeZ1Lp%z;_ND;9;m7a9MzVt8y`yTuK|Ga;O-N^$t4vyyn zMyj^sUWl%GZ$gbgG#m^XYK0>~+>UG^3ojzO=!iEPq2SuHrI9m3=e*fAi$a4#rGd-d zpY@L@U>M$=*rAY|r{+$>u>593RS&DDf~9srk7369^c$EFWQBI2M0lsPLhE-NKYCO&Fl6%wE9DJ8kc5D9D z97p3n`7}p;Zm<4a-E2JB>{v=|vMy*_t6tycWujisO@)cBp)Xx98|{AnG^*(W?MFTB z&_$S?4n2jd>I5pPPNUxGn7KCJdkR*jKx1$-PT*M#wGJB-nH)``o^hxFGwaYITvdBf TQFRXWtYhx$@Hf9hHP!haL57Ro delta 938 zcmZ9KOK1~e6oq?}hGuLrhMM_%Q}eJOq=g!~G0QRdh=K~bG7E|6LWF`YbR!DBHc~+o z4=%DPjUbAvDdWP85ML`7HX>pdg2hcm7cDL5(m&3Pi#ssqGUq%0y>pqlamYW|C`9~S zOH&VHG#7MY%MmQZvlPY4_%!Vge#U7q*c{nGfwTP)1%Bd4j6c!9zbhG*fo--9+4~lf zDMJgGOx5^{u)`|z4egZSHE~ZVx{j{xFohqfB2UYdt12^!8Q1V!B((1;dK`7PNLSEw zEBs6{?^Nb7He5sR;g}jfm&~wC>+j;>rZMb^yGPLmmQ3a7S#hh1Zc5K{%6~2XCFRee zJXYb$;yzLIE!ty6YNBiP0*DPQgSp7LXt-8Fy7u2ZiaHz4OP5JSKcH#pHK7L-{f<|b zE{c0b(GE83^gnRYMZPWUh06TG$#jW7DmN}ay4j?M?|@!v#+pp_XSx`=kRI1A;y#vmBPI@HFoPPV6*;b6T;CDCW}9S@AoA4Wx!&yi zB|M_Z*3L>ahb!$X9crKOW1Jm2sk7X5d;P~TL}zyUk_$8HDADt&CABB$*U&;Rpdm*v zsG*BsNJ9_7ehvEw4rmx4ctOJm!H9;#8W!Axn#Rc*)i6OYwsQbGJxTCVb{OFd!QrH8 z`y4?*Ly6#(;5<6>1V=LC2xWrTn%_XUKycI_!)%MbrS9J?HouPaIa$YdC~cWwN}fPx zg$pWs!>F%1;ABNI#d@N`c7l$3o$|w-|w2H9fX~qs^+3|R*cE`diSk#Ws z5FScV@Emrw1Vt|=cUn$*DA*qi1_capp0t8sIB?R{vNz>=r{~U|Dpl{Nz7*A&pwAV* zL#6qhXs^nQLtTOTm^&5VH~_sZvhX57!)@Z8Up4d+_NB%#S{m_V96t$hi;PYg?gc(| z7S^#=$#5C1vY}V`)K`YC?naFYbcZtfo}kf5g}DmFP!?_&?jrN1pLpWyra)6nlU$Zw`uxivL5=^Xivq|i3w3Tkfejr-4wI&g3gjiE8 z+E!&-g{CM<(jypAa7Doi77Y8K>4PL#^+6wu6cQgRiuz!ozSvSQo|$uQgt?HJ`ThUr zoH_T-Y+BVTeN$!oN@2F^FCqG&Xm>YV$HFI~276J~)#S!CIeKzjTezBSl0}Rts-~zD zefr>GOwpvPL}r5L(V_LbCAE&ZGdS9msjDnIz3$DnWw}i>EN5eUT+MnPXH(*|S`$<> zqzU>o6bY&tHW2h{C=(25=ph)?&_{5WhB`rALDSksG^DLv1b1tAm|#nxhMf)*+?(5q zFh;OVw>?47&~TVwyLSxV;RwO~nLP-{33jCRBfLtm({0{|2HzCE zV@d5r9d-I~C$gN@l%CvXt~iCZ3iU+-Z5v=Eb&t<0z~@fz(=xb+VAbsuBy5KvbRRI>gV?ru9)&|` z*%%>M&3%@6%+O!vtQQP@iuom2#A=gN{7uE7HC4!p%k_=tHUh@1=t z40nZJW)ymJa_AYuJ<0r*q5s1CzM+r6hdBwYoEwCfI&6D4?{f|=V;A#A-tX*w3)kmm z;|Ih2hu!d#gf-XmCk@{`)XS{@3Ma7IHKW?UA#+ri^(X6rp&#cDUT$B^FA1@sJtyNr zY=(tmB{7kr)`md@74aZcgPItHqd5Kub2y%ct2kOv?WiRj{>2W$iH?e>!(2zrKH$Nk nSAcY>EXJT(s) 0) { + /* Check if there 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) { + /* Stores the current CSV field for the current line */ + char *token = strtok(lineBuffer, ","); + size_t tokenLength; + + 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. + */ + case 0: + tower->id = malloc(sizeof(char) * tokenLength + 1); + checkNullPointer(tower->id); + strcpy(tower->id, token); + tower->id[tokenLength] = '\0'; + break; + case 1: + tower->postcode = malloc(sizeof(char) * tokenLength + 1); + checkNullPointer(tower->postcode); + strcpy(tower->postcode, token); + tower->postcode[tokenLength] = '\0'; + break; + case 2: + tower->population = strtol(token, NULL, BASE_10); + break; + case 3: + tower->manager = malloc(sizeof(char) * tokenLength + 1); + checkNullPointer(tower->manager); + strcpy(tower->manager, token); + tower->manager[tokenLength] = '\0'; + break; + case 4: + tower->x = strtod(token, NULL); + break; + case 5: + tower->y = strtod(token, NULL); + break; + } + token = strtok(NULL, ","); + } +} + +void freeTowers(tower_t **towers, int numTowers) { + for (int i = 0; i < numTowers; i++) { + free(towers[i]->id); + free(towers[i]->manager); + free(towers[i]->postcode); + free(towers[i]); + } + free(towers); +} diff --git a/towers.h b/towers.h new file mode 100644 index 0000000..2e9a696 --- /dev/null +++ b/towers.h @@ -0,0 +1,26 @@ +#ifndef COMMON_HEADER +#include "common.h" +#endif + +#ifndef TOWERS_HEADER +#define TOWERS_HEADER + +typedef struct tower { + char *id; + char *postcode; + char *manager; + int population; + double x; + double y; +} tower_t; + +/* 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); + +#endif diff --git a/towers.o b/towers.o new file mode 100644 index 0000000000000000000000000000000000000000..04f55abc1666ad5e478016eeeb97b1303d5bb9df GIT binary patch literal 9848 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 blkQ(fg;YG~xkfjW>0hMyPZm{0PuKq+T`J{) literal 0 HcmV?d00001 diff --git a/voronoi.c b/voronoi.c index c012bb0..78ef41f 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 9th September 2021 + * Last modified 11th September 2021 * * Contains functions involving the generation of the Voronoi diagram, and * running each stage of the assignment. @@ -17,101 +17,9 @@ #include "voronoi.h" #endif -#define BASE_10 10 -#define MAX_CSV_ENTRY_LEN 512 -#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 */ - 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) { - /* Check if there 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) { - /* Stores the current CSV field for the current line */ - char *token = strtok(lineBuffer, ","); - size_t tokenLength; - - 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. - */ - case 0: - tower->id = malloc(sizeof(char) * tokenLength + 1); - checkNullPointer(tower->id); - strcpy(tower->id, token); - tower->id[tokenLength] = '\0'; - break; - case 1: - tower->postcode = malloc(sizeof(char) * tokenLength + 1); - checkNullPointer(tower->postcode); - strcpy(tower->postcode, token); - tower->postcode[tokenLength] = '\0'; - break; - case 2: - tower->population = strtol(token, NULL, BASE_10); - break; - case 3: - tower->manager = malloc(sizeof(char) * tokenLength + 1); - checkNullPointer(tower->manager); - strcpy(tower->manager, token); - tower->manager[tokenLength] = '\0'; - break; - case 4: - tower->x = strtod(token, NULL); - break; - case 5: - tower->y = strtod(token, NULL); - break; - } - token = strtok(NULL, ","); - } -} - -void freeTowers(tower_t **towers, int numTowers) { - for (int i = 0; i < numTowers; i++) { - free(towers[i]->id); - free(towers[i]->manager); - free(towers[i]->postcode); - free(towers[i]); - } - free(towers); -} - vertex_t **readPoints(vertex_t **points, FILE *pointsFile, int *numPoints) { - /* Current length of a single points line */ - size_t lineBufferSize = 50; + /* 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)); @@ -148,6 +56,16 @@ vertex_t **readPoints(vertex_t **points, FILE *pointsFile, int *numPoints) { return points; } +void stage1PrintBisector(bisector_t *bisector, FILE *outputFile) { + if (bisector->isSlopeInfinite) { + /* Cannot print infinite slope, so print only x-intercept */ + fprintf(outputFile, "x = %lf\n", bisector->midX); + } else { + fprintf(outputFile, "y = %lf * (x - %lf) + %lf\n", \ + bisector->slope, bisector->midX, bisector->midY); + } +} + void freePoints(vertex_t **points, int numPoints) { for (int i = 0; i < numPoints; i++) { free(points[i]); @@ -168,17 +86,12 @@ void stage1(char *pointsFileName, char *outputFileName) { /* For each pair, calculate and print the bisector to outputFile */ for (int i = 0; i < numPoints; i += 2) { - bisector_t currBisector = getBisector(points[i], points[i + 1]); - - /* Cannot print infinite slope, so print only x-intercept */ - if (currBisector.isSlopeInfinite) { - fprintf(outputFile, "x = %lf\n", currBisector.x); - } else { - fprintf(outputFile, "y = %lf * (x - %lf) + %lf\n", \ - currBisector.slope, currBisector.x, currBisector.y); - } + bisector_t *currBisector = getBisector(points[i], points[i + 1]); + stage1PrintBisector(currBisector, outputFile); + free(currBisector); } + /* Clean up */ freePoints(points, numPoints); fclose(pointsFile); fclose(outputFile); @@ -190,8 +103,21 @@ void stage2(char *pointsFileName, char *polygonFileName, char *outputFileName) { polygonFile = safeFileOpen(&polygonFile, polygonFileName, "r"); outputFile = safeFileOpen(&outputFile, outputFileName, "w"); + /* Points are given as pairs, so initialise 2 points */ + vertex_t **points = malloc(sizeof(*points) * 2); + checkNullPointer(points); + int numPoints = 0; + points = readPoints(points, pointsFile, &numPoints); + + /* Construct the DCEL from the polygon file */ + DCEL_t *dcel = readPolygonFile(polygonFileName); + + /* Calculate and print intersections to the output file */ + /* Clean up */ + freePoints(points, numPoints); + freeDCEL(dcel); fclose(pointsFile); fclose(polygonFile); fclose(outputFile); @@ -205,6 +131,7 @@ void stage3(char *dataFileName, char *polygonFileName, char *outputFileName) { + /* Clean up */ fclose(dataFile); fclose(polygonFile); fclose(outputFile); @@ -217,7 +144,8 @@ void stage4(char *dataFileName, char *polygonFileName, char *outputFileName) { outputFile = safeFileOpen(&outputFile, outputFileName, "w"); - + + /* Clean up */ fclose(dataFile); fclose(polygonFile); fclose(outputFile); diff --git a/voronoi.h b/voronoi.h index 905c1b8..8f059ac 100644 --- a/voronoi.h +++ b/voronoi.h @@ -5,27 +5,12 @@ #ifndef VORONOI_HEADER #define VORONOI_HEADER -typedef struct tower { - char *id; - char *postcode; - char *manager; - int population; - double x; - double y; -} tower_t; - -/* 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); - /* Reads a points file and stores the information in the points array */ vertex_t **readPoints(vertex_t **points, FILE *pointsFile, int *numPoints); +/* Prints bisectors to an output file */ +void stage1PrintBisector(bisector_t *bisector, FILE *outputFile); + /* Frees all points from a points array */ void freePoints(vertex_t **points, int numPoints); diff --git a/voronoi.o b/voronoi.o index 707d2cc7a57608821c2dc9f1e5d43e9f6a687335..4b32af21370979d2b2f98ede11da0fdb97c1df4d 100644 GIT binary patch literal 15384 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 literal 16920 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 diff --git a/voronoi2 b/voronoi2 index 9096ef93b097b522e87ba37a406423ddfcfdce12..a1adcbe65b075574874aac7b7252f5b83714411d 100755 GIT binary patch literal 63952 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 SNj~;#C4ajyRDoCd@Bam|(npv8 literal 34256 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