From b5f2cfa2f43b10aabdbb22ad72ef718064eafaa9 Mon Sep 17 00:00:00 2001 From: Rory Healy Date: Wed, 25 Aug 2021 22:24:41 +1000 Subject: [PATCH] Refactoring, and basic DCEL prelim structs --- .vscode/settings.json | 9 ++ Makefile | 17 ++- common.c | 53 +++++++++ common.h | 23 ++++ dcel.c | 68 ++++++++++++ dcel.h | 20 ++++ main.c | 64 +++++++++++ voronoi.c | 243 +++++++++++++++++------------------------- voronoi.h | 37 ++++--- voronoi1 | Bin 20848 -> 25288 bytes 10 files changed, 376 insertions(+), 158 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 common.c create mode 100644 common.h create mode 100644 dcel.c create mode 100644 dcel.h create mode 100644 main.c 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 41afb537f631b825827fba53fb62aecaee03eae8..0c4f7ae336dc3d5eb2dbcadab791a7fb3766616e 100755 GIT binary patch literal 25288 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 VuEnKivf1?)ZnY%taw)h__P^1}50U@?