Refactored code, integrated base code (DCEL)

This commit is contained in:
Rory Healy 2021-09-11 22:47:44 +10:00
parent aa2690dbd3
commit b1697a0c30
19 changed files with 1487 additions and 227 deletions

View file

@ -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

View file

@ -19,6 +19,13 @@
#endif
#ifndef MATH_HEADER
#define MATH_HEADER
#include <math.h>
#endif
#ifndef COMMON_HEADER
#define COMMON_HEADER

BIN
common.o

Binary file not shown.

1083
dcel.c

File diff suppressed because it is too large Load diff

253
dcel.h
View file

@ -1,32 +1,261 @@
#ifndef GEOMETRY_HEADER
#include "geometry.h"
#endif
#ifndef STDIO_HEADER
#include <stdio.h>
#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

BIN
dcel.o

Binary file not shown.

View file

@ -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;
}

View file

@ -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

Binary file not shown.

BIN
input.o

Binary file not shown.

BIN
main.o

Binary file not shown.

View file

@ -1,6 +0,0 @@
y = 0.000000 * (x - 145.600000) + -34.700000
y = 0.000000 * (x - 145.600000) + -35.200000
x = 147.100000
x = 147.100000
x = 147.600000
y = 1.000000 * (x - 147.600000) + -33.200000

97
towers.c Normal file
View file

@ -0,0 +1,97 @@
#ifndef TOWERS_HEADER
#include "towers.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);
}

26
towers.h Normal file
View file

@ -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

BIN
towers.o Normal file

Binary file not shown.

138
voronoi.c
View file

@ -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);

View file

@ -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);

BIN
voronoi.o

Binary file not shown.

BIN
voronoi2

Binary file not shown.