diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..6a73ec0 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + "files.associations": { + "voronoi.h": "c", + "stdio.h": "c", + "dcel.h": "c", + "common.h": "c", + "string.h": "c" + } +} \ No newline at end of file diff --git a/Makefile b/Makefile index 32c22e8..3b7b5e2 100755 --- a/Makefile +++ b/Makefile @@ -1,2 +1,15 @@ -voronoi1 : voronoi.c - gcc -Wall -Wextra -Werror -pedantic -g -o voronoi1 voronoi.c +voronoi1: main + gcc -Wall -Wextra -Werror -pedantic -g -o voronoi1 main.o voronoi.o dcel.o common.o + rm *.o + +main: voronoi + gcc -Wall -Wextra -Werror -pedantic -g -o main.o main.c -c + +voronoi: dcel + gcc -Wall -Wextra -Werror -pedantic -g -o voronoi.o voronoi.c -c + +dcel: common + gcc -Wall -Wextra -Werror -pedantic -g -o dcel.o dcel.c -c + +common: + gcc -Wall -Wextra -Werror -pedantic -g -o common.o common.c -c diff --git a/common.c b/common.c new file mode 100644 index 0000000..a324131 --- /dev/null +++ b/common.c @@ -0,0 +1,53 @@ +/* common.c + * + * Created by Rory Healy (healyr@student.unimelb.edu.au) + * Created on 25th August 2021 + * Last modified 25th August 2021 + * + * Contains functions for general use throughout other files. + * + */ + +#ifndef COMMON_HEADER +#include "common.h" +#endif + +#define ARGC_CORRECT_LEN 4 +#define MEMORY_ALLOCATION_ERROR "Error: Cannot allocate memory.\n" +#define OPEN_FILE_ERROR "Error: Unable to open file %s\n" +#define NUM_FILE_ERROR "Error: Incorrect number of inputs (3 required).\n" + +/* Checks if a given pointer is null. Used for malloc() and realloc(). */ +void checkNullPointer(void *ptr) { + if (!ptr) { + fputs(MEMORY_ALLOCATION_ERROR, stderr); + exit(EXIT_FAILURE); + } +} + +/* Checks the validity of the command line input arguments */ +void checkInputArgs(int argc, char **argv, FILE **datasetFile, \ + FILE **polygonFile, FILE **outputFile) { + if (argc != ARGC_CORRECT_LEN) { + fputs(NUM_FILE_ERROR, stderr); + exit(EXIT_FAILURE); + } + + *datasetFile = fopen(argv[1], "r"); + if (*datasetFile == NULL) { + fprintf(stderr, OPEN_FILE_ERROR, argv[1]); + exit(EXIT_FAILURE); + } + + *polygonFile = fopen(argv[2], "r"); + if (*polygonFile == NULL) { + fprintf(stderr, OPEN_FILE_ERROR, argv[2]); + exit(EXIT_FAILURE); + } + + *outputFile = fopen(argv[3], "w"); + if (*outputFile == NULL) { + fprintf(stderr, OPEN_FILE_ERROR, argv[3]); + exit(EXIT_FAILURE); + } +} diff --git a/common.h b/common.h new file mode 100644 index 0000000..3ac77a7 --- /dev/null +++ b/common.h @@ -0,0 +1,23 @@ +#ifndef STDIO_HEADER + +#define STDIO_HEADER +#include + +#endif + +#ifndef STDLIB_HEADER + +#define STDLIB_HEADER +#include + +#endif + +#ifndef COMMON_HEADER + +#define COMMON_HEADER + +void checkNullPointer(void *ptr); +void checkInputArgs(int argc, char **argv, FILE **datasetFile, \ + FILE **polygonFile, FILE **outputFile); + +#endif diff --git a/dcel.c b/dcel.c new file mode 100644 index 0000000..a2ab3be --- /dev/null +++ b/dcel.c @@ -0,0 +1,68 @@ +/* dcel.c + * + * Created by Rory Healy (healyr@student.unimelb.edu.au) + * Created on 25th August 2021 + * Last modified 25th August 2021 + * + * Contains functions for the DCEL data structure, including creating, + * splitting an edge, and identifying towers in faces. + * + */ + +#ifndef DCEL_HEADER +#include "dcel.h" +#endif + +#ifndef COMMON_HEADER +#include "common.h" +#endif + +struct halfEdge { + halfEdge_t *previous; + halfEdge_t *next; + halfEdge_t *twin; + int face; + int edge; +}; + +struct vertex { + double x; + double y; +}; + +struct edge { + halfEdge_t halfEdge; +}; + +struct face { + halfEdge_t start; +}; + +/* Reads the polygon file and stores the information in the vertices array. */ +vertex_t **readPolygon(vertex_t **vertices, FILE *polygonFile, int *numVertices) { + double currentX, currentY; + int maxSizeVertices = 1; + while ((fscanf(polygonFile, "%lf %lf", ¤tX, ¤tY)) != EOF) { + /* Check if there enough space in the towers array */ + if (*numVertices == maxSizeVertices) { + maxSizeVertices *= 2; + vertex_t **temp = realloc(vertices, maxSizeVertices * sizeof(*vertices)); + checkNullPointer(temp); + vertices = temp; + } + + /* The current vertex being filled in with information */ + vertices[*numVertices] = malloc(sizeof(*vertices[*numVertices])); + vertices[*numVertices]->x = currentX; + vertices[*numVertices]->y = currentY; + *numVertices += 1; + } + return vertices; +} + +void freeVertices(vertex_t **vertices, int numVertices) { + for (int i = 0; i < numVertices; i++) { + free(vertices[i]); + } + free(vertices); +} diff --git a/dcel.h b/dcel.h new file mode 100644 index 0000000..5066490 --- /dev/null +++ b/dcel.h @@ -0,0 +1,20 @@ +#ifndef STDIO_HEADER + +#define STDIO_HEADER +#include + +#endif + +#ifndef DCEL_HEADER + +#define DCEL_HEADER + +typedef struct halfEdge halfEdge_t; +typedef struct vertex vertex_t; +typedef struct edge edge_t; +typedef struct face face_t; + +vertex_t **readPolygon(vertex_t **vertices, FILE *polygonFile, \ + int *numVertices); +void freeVertices(vertex_t **vertices, int numVertices); +#endif diff --git a/main.c b/main.c new file mode 100644 index 0000000..79de3ab --- /dev/null +++ b/main.c @@ -0,0 +1,64 @@ +/* main.c + * + * Created by Rory Healy (healyr@student.unimelb.edu.au) + * Created on 25th August 2021 + * Last modified 25th August 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. + * + * To run the program type: + * ./voronoi1 dataset_file polygon_file output_file < splits + * + * Options: + * dataset_file required Path to the dataset file (CSV) + * polygon_file required Path to the polygon file (txt) + * output_file required Output solution file + * splits optional Pairs of integers to split the edges + * + */ + +#ifndef DCEL_HEADER +#include "dcel.h" +#endif + +#ifndef COMMON_HEADER +#include "common.h" +#endif + +#ifndef VORONOI_HEADER +#include "voronoi.h" +#endif + +int main(int argc, char **argv) { + /* Input and output files */ + FILE *datasetFile = NULL, *polygonFile = NULL, *outputFile = NULL; + checkInputArgs(argc, argv, &datasetFile, &polygonFile, &outputFile); + + /* Stores information about the towers given in dataset file */ + tower_t **towers = malloc(sizeof(*towers)); + checkNullPointer(towers); + int numTowers = 0; + towers = readTowers(towers, datasetFile, &numTowers); + + /* Stores information about the vertices in the polygon file */ + vertex_t **vertices = malloc(sizeof(*vertices)); + checkNullPointer(vertices); + int numVertices = 0; + vertices = readPolygon(vertices, polygonFile, &numVertices); + + /* Create DCEL structure from vertices */ + + /* Check for splits, split the polygon if needed */ + + /* Counts towers in each polygon, outputs to outputFile */ + + /* Cleaning up data */ + freeTowers(towers, numTowers); + freeVertices(vertices, numVertices); + fclose(datasetFile); + fclose(polygonFile); + fclose(outputFile); + return 0; +} diff --git a/voronoi.c b/voronoi.c index c76b593..0d02e27 100755 --- a/voronoi.c +++ b/voronoi.c @@ -1,165 +1,122 @@ /* voronoi.c -* -* Created by Rory Healy (healyr@student.unimelb.edu.au) -* 12th August 2021 -* -*/ - -#include -#include -#include -#include "voronoi.h" - -#define OPEN_FILE_ERROR "Error: Unable to open file %s\n" -#define NUM_FILE_ERROR "Error: Incorrect number of inputs (3 required).\n" -#define MEMORY_ALLOCATION_ERROR "Error: Cannot allocate memory.\n" - -#define ARGC_CORRECT_LEN 4 -#define NUM_CSV_FIELDS 6 -#define MAX_FIELD_LEN 128 -#define MAX_CSV_ENTRY_LEN 512 - -int main(int argc, char **argv) { - // /* Input and output files */ - FILE *dataset = NULL, *polygonData = NULL, *output = NULL; - checkInputArgs(argc, argv, &dataset, &polygonData, &output); - - /* Stores information about the watchtowers given in dataset file */ - watchtower_t **watchtowers = malloc(sizeof(*watchtowers)); - checkNullPointer(watchtowers); - int numWatchtowers = 0; - watchtowers = readWatchtowers(watchtowers, dataset, &numWatchtowers); - - /* Cleaning up data */ - for (int i = 0; i < numWatchtowers; i++) { - free(watchtowers[i]->id); - free(watchtowers[i]->manager); - free(watchtowers[i]->postcode); - free(watchtowers[i]); - } - - free(watchtowers); - fclose(dataset); - fclose(polygonData); - fclose(output); - return 0; -} - -/* Checks the validity of the command line input arguments */ -void checkInputArgs(int argc, char **argv, FILE **datasetFile, \ - FILE **polygonFile, FILE **outputFile) { - if (argc != ARGC_CORRECT_LEN) { - fputs(NUM_FILE_ERROR, stderr); - exit(EXIT_FAILURE); - } - - *datasetFile = fopen(argv[1], "r"); - if (*datasetFile == NULL) { - fprintf(stderr, OPEN_FILE_ERROR, argv[1]); - exit(EXIT_FAILURE); - } - - *polygonFile = fopen(argv[2], "r"); - if (*polygonFile == NULL) { - fprintf(stderr, OPEN_FILE_ERROR, argv[2]); - exit(EXIT_FAILURE); - } - - *outputFile = fopen(argv[3], "w"); - if (*outputFile == NULL) { - fprintf(stderr, OPEN_FILE_ERROR, argv[3]); - exit(EXIT_FAILURE); - } -} - -/* Reads the CSV file and stores the information in the watchtowers array. - * Returns the number of watchtowers read. + * + * Created by Rory Healy (healyr@student.unimelb.edu.au) + * Created on 12th August 2021 + * Last modified 25th August 2021 + * + * Contains functions involving the reading of the dataset CSV file. + * */ -watchtower_t** readWatchtowers(watchtower_t **watchtowers, FILE* datasetFile, \ - int *numWatchtowers) { + +#ifndef COMMON_HEADER +#include "common.h" +#endif + +#ifndef VORONOI_HEADER +#include "voronoi.h" +#endif + +#include + +#define BASE_10 10 +#define MAX_CSV_ENTRY_LEN 512 +#define MAX_FIELD_LEN 128 +#define NUM_CSV_FIELDS 6 + +struct tower { + char *id; + char *postcode; + char *manager; + int population; + double x; + double y; +}; + +/* Reads the CSV file and stores the information in the towers array. */ +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 = (char *) malloc(lineBufferSize * sizeof(char)); + char *lineBuffer = malloc(lineBufferSize * sizeof(char)); checkNullPointer(lineBuffer); - int maxSizeWatchtowers = 1; + 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 watchtowers array */ - if (*numWatchtowers == maxSizeWatchtowers) { - maxSizeWatchtowers *= 2; - watchtower_t **temp = realloc(watchtowers, \ - maxSizeWatchtowers * sizeof(*watchtowers)); + /* Check if there enough space in the towers array */ + if (*numTowers == maxSizetowers) { + maxSizetowers *= 2; + tower_t **temp = realloc(towers, maxSizetowers * sizeof(*towers)); checkNullPointer(temp); - watchtowers = temp; + towers = temp; } /* The current tower being filled in with information */ - watchtowers[*numWatchtowers] = malloc(sizeof(*watchtowers[*numWatchtowers])); + towers[*numTowers] = malloc(sizeof(*towers[*numTowers])); - /* Stores the current CSV field for the current line */ - char *token = strtok(lineBuffer, ","); - size_t tokenLength; - - /* Read the line in to currTower */ - for (int i = 0; i < NUM_CSV_FIELDS; i++) { - tokenLength = strlen(token); - switch(i) { - /* Case 0, 1, and 3 deal with strings in the CSV - * Case 2 deals with an integer - * Case 4 and 5 deal with doubles - * Each case is handled seperately to fill in the - * watchtower with no space wasted. - */ - case 0: - watchtowers[*numWatchtowers]->id = (char *) malloc( \ - sizeof(char) * tokenLength + 1); - checkNullPointer(watchtowers[*numWatchtowers]->id); - strcpy(watchtowers[*numWatchtowers]->id, token); - watchtowers[*numWatchtowers]->id[tokenLength] = '\0'; - break; - case 1: - watchtowers[*numWatchtowers]->postcode = (char *) malloc( \ - sizeof(char) * tokenLength + 1); - checkNullPointer(watchtowers[*numWatchtowers]->postcode); - strcpy(watchtowers[*numWatchtowers]->postcode, token); - watchtowers[*numWatchtowers]->postcode[tokenLength] = '\0'; - break; - case 3: - watchtowers[*numWatchtowers]->manager = (char *) malloc( \ - sizeof(char) * tokenLength + 1); - checkNullPointer(watchtowers[*numWatchtowers]->manager); - strcpy(watchtowers[*numWatchtowers]->manager, token); - watchtowers[*numWatchtowers]->manager[tokenLength] = '\0'; - break; - case 2: - watchtowers[*numWatchtowers]->population = \ - strtol(token, NULL, 10); - break; - case 4: - watchtowers[*numWatchtowers]->x = strtod(token, NULL); - break; - case 5: - watchtowers[*numWatchtowers]->y = strtod(token, NULL); - break; - } - token = strtok(NULL, ","); - } - - *numWatchtowers += 1; + readCurrentLine(lineBuffer, towers[*numTowers]); + *numTowers += 1; } - free(lineBuffer); - return watchtowers; + return towers; } -void checkNullPointer(void *ptr) { - if (!ptr) { - fputs(MEMORY_ALLOCATION_ERROR, stderr); - exit(EXIT_FAILURE); - } +/* Takes a line from the CSV and converts the data into a tower_t format */ +void readCurrentLine(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/voronoi.h b/voronoi.h index cb34dcd..10113b1 100755 --- a/voronoi.h +++ b/voronoi.h @@ -1,14 +1,25 @@ -typedef struct Watchtower { - char *id; - char *postcode; - char *manager; - int population; - double x; - double y; -} watchtower_t; +#ifndef STDIO_HEADER -void checkInputArgs(int argc, char **argv, FILE **datasetFile, \ - FILE **polygonFile, FILE **outputFile); -watchtower_t** readWatchtowers(watchtower_t **watchtowers, FILE* datasetFile, \ - int *numWatchtowers); -void checkNullPointer(void *ptr); +#define STDIO_HEADER +#include + +#endif + +#ifndef STDLIB_HEADER + +#define STDLIB_HEADER +#include + +#endif + +#ifndef VORONOI_HEADER + +#define VORONOI_HEADER + +typedef struct tower tower_t; + +tower_t** readTowers(tower_t **towers, FILE* datasetFile, int *numTowers); +void readCurrentLine(char* lineBuffer, tower_t* tower); +void freeTowers(tower_t **towers, int numTowers); + +#endif diff --git a/voronoi1 b/voronoi1 index 41afb53..0c4f7ae 100755 Binary files a/voronoi1 and b/voronoi1 differ