From 60862d95cc09f3c46ab0556b088ceb47f52a291a Mon Sep 17 00:00:00 2001 From: Taylor Bockman Date: Sun, 26 Jan 2020 16:50:49 -0800 Subject: [PATCH] init chapter 1 --- .clang-format | 15 +++ .gitignore | 117 ++++++++++++++++++++ .idea/.name | 1 + .idea/codeStyles/codeStyleConfig.xml | 5 + .idea/compginc.iml | 2 + .idea/misc.xml | 13 +++ .idea/modules.xml | 8 ++ .idea/vcs.xml | 6 + CMakeLists.txt | 15 +++ README.md | 26 +++++ include/error.h | 6 + include/math.h | 146 +++++++++++++++++++++++++ include/vertex.h | 77 +++++++++++++ src/main.c | 64 +++++++++++ src/math.c | 206 +++++++++++++++++++++++++++++++++++ src/vertex.c | 101 +++++++++++++++++ 16 files changed, 808 insertions(+) create mode 100644 .clang-format create mode 100644 .gitignore create mode 100644 .idea/.name create mode 100644 .idea/codeStyles/codeStyleConfig.xml create mode 100644 .idea/compginc.iml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 CMakeLists.txt create mode 100644 README.md create mode 100644 include/error.h create mode 100644 include/math.h create mode 100644 include/vertex.h create mode 100644 src/main.c create mode 100644 src/math.c create mode 100644 src/vertex.c diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..b5b8085 --- /dev/null +++ b/.clang-format @@ -0,0 +1,15 @@ +AlignConsecutiveMacros: 'true' +AlignConsecutiveAssignments: 'true' +AlignTrailingComments: 'true' +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: None +AllowShortLoopsOnASingleLine: 'false' +BreakBeforeBraces: Linux +ColumnLimit: '80' +IndentWidth: '4' +PointerAlignment: Right +SpaceBeforeParens: ControlStatements +SpacesInCStyleCastParentheses: 'false' +TabWidth: '4' +UseTab: Never diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f0d323b --- /dev/null +++ b/.gitignore @@ -0,0 +1,117 @@ +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +cmake-build-debug + +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +bin/ \ No newline at end of file diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..854dc21 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +cgc \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..a55e7a1 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/compginc.iml b/.idea/compginc.iml new file mode 100644 index 0000000..f08604b --- /dev/null +++ b/.idea/compginc.iml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..2db4407 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..2f39955 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..27e4b12 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.15) +project(cgc C) + +file(GLOB SOURCE "src/*.c") +file(GLOB INCLUDE "include/*.h") + +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + +include_directories("include") + +set(CMAKE_C_STANDARD 99) + +add_executable(cgc ${SOURCE} ${INCLUDE}) \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..7f38014 --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +# Computational Geometry in C, 2nd Edition + +This is the project I use for storing all of the code in the book _Computational Geometry in C, 2nd Edition_ by +Joseph O'Rourke. + +Much of the code is copied verbatim, however I took the time to fix a few memory leaks and made other small changes +to make the code more maintainable across different chapters. In particular, I wrote the code without an assumption +of a global vertex pointer in order to make debugging functions significantly easier. I also added a few +freeing functions that were not featured in the book. + +## Some Notes + +There are a handful of instances where I am using constant pointers or double pointers (pointers to pointers) that +I need to go back and fix. Additionally, Valgrind does not work on OS X right now (1/26/2020) so I cannot verify +the code is leak-free. + +## Compilation and Running + +To compile the code: + +```sh +cd +cmake . +make +./bin/cgc +``` \ No newline at end of file diff --git a/include/error.h b/include/error.h new file mode 100644 index 0000000..5622527 --- /dev/null +++ b/include/error.h @@ -0,0 +1,6 @@ +#ifndef _CGC_ERROR_H +#define _CGC_ERROR_H + +#define EXIT_FAILURE 1 + +#endif // _CGC_ERROR_H \ No newline at end of file diff --git a/include/math.h b/include/math.h new file mode 100644 index 0000000..9a87a92 --- /dev/null +++ b/include/math.h @@ -0,0 +1,146 @@ +#ifndef CGC_MATH_H +#define CGC_MATH_H + +#include "vertex.h" +#define X 0 +#define Y 1 +#define Z 2 + +/** + * Given 3 vertices, calculates the two dimensional area of the triangle + * represented by those shapes. + * + * Integers are used here for simplicity. Doubles would be nice too but + * for now the lack of rounding error in integers is sufficient for the + * purpose. + * + * @param a The first vertex. + * @param b The second vertex. + * @param c The third vertex. + * @return The integer area of the polygon. + */ +int area2(const struct Vertex *a, const struct Vertex *b, + const struct Vertex *c); + +/** + * Given the head of a counter-clockwise indexed polygon area_poly_2 + * calculates the area of the polygon given. + * + * @param head The first vertex. + * @return The area. + */ +int area_poly_2(const struct Vertex *head); + +/** + * Determines if vertex c is to the left of the line segment ab. Note the + * area of the shape made by the 3 vertices must have c to the left to have + * positive area. + * @param a Vertex a. + * @param b Vertex b. + * @param c Vertex c. + * @return True if it is to the left, false otherwise. + */ +bool left(const struct Vertex *a, const struct Vertex *b, + const struct Vertex *c); + +/** + * Determines if vertex c is to the left or collinear with the line segment ab. + * @param a Vertex a. + * @param b Vertex b. + * @param c Vertex c. + * @return True if it is to the left or collinear, false otherwise. + */ +bool left_on(const struct Vertex *a, const struct Vertex *b, + const struct Vertex *c); + +/** + * Determines if the vertex c is collinear with line segment ab. + * @param a Vertex a. + * @param b Vertex b. + * @param c Vertex c. + * @return True if it is collinear with ab, false otherwise. + */ +bool collinear(const struct Vertex *a, const struct Vertex *b, + const struct Vertex *c); + +/** + * Determines if the line segments ab and cd are intersecting properly. + * @param a Vertex a. + * @param b Vertex b. + * @param c Vertex c. + * @param d Vertex d. + * @return True if they intersect properly, false otherwise. + */ +bool intersect_prop(const struct Vertex *a, const struct Vertex *b, + const struct Vertex *c, const struct Vertex *d); + +/** + * Determines the betweenness of c. + * @param a Vertex a. + * @param b Vertex b. + * @param c Vertex c. + * @return True if c is between ab. + */ +bool between(const struct Vertex *a, const struct Vertex *b, + const struct Vertex *c); + +/** + * Determines if two line segments, ab and cd, intersect. + * @param a Vertex a. + * @param b Vertex b. + * @param c Vertex c. + * @param d Vertex d. + * @return True if they intersect, false otherwise. + */ +bool intersect(const struct Vertex *a, const struct Vertex *b, + const struct Vertex *c, const struct Vertex *d); + +/** + * Finds a diagonal of line segment ab for the polygon represented by the start + * vertex `head`. + * + * @param head The first vertex of a counter-clockwise labeled polygon. + * @param a Vertex a. + * @param b Vertex b. + * @return True if ab is a valid diagonal, false otherwise. + */ +bool diagonalie(const struct Vertex *head, const struct Vertex *a, + const struct Vertex *b); + +/** + * Determines if one vector b lines inside the cone created by vertices a and + * b. + * @param a Vertex a. + * @param b Vertex b. + * @return True if it lies in the cone, false otherwise. + */ +bool in_cone(const struct Vertex *a, const struct Vertex *b); + +/** + * Determines if ab is a diagonal in the polygon represented by the + * counter-clockwise ordered vertex list head. + * + * @param head The head of the polygon's vertex list. + * @param a Vertex a. + * @param b Vertex b. + * @return True if ab is a diagonal, false otherwise. + */ +bool diagonal(const struct Vertex *head, const struct Vertex *a, + const struct Vertex *b); + +/** + * Triangulates a given polygon and prints the results to screen. + * + * Note this currently destructively modifies the polygon. Each ear + * removed is freed as a result of the algorithm pointing around it in + * the circularly linked list. The book does not talk about memory management + * and on large polygons this is a massive memory leak. + * + * Future versions of the function should operate on copies and/or non + * destructively produce results. + * + * @param head The start vertex of a polygon. + */ +void triangulate(const struct Vertex *head); + +#endif // CGC_MATH_H diff --git a/include/vertex.h b/include/vertex.h new file mode 100644 index 0000000..cbd56ff --- /dev/null +++ b/include/vertex.h @@ -0,0 +1,77 @@ +#ifndef _CGC_VERTEX_H +#define _CGC_VERTEX_H + +#include +#include +#include + +struct Vertex { + int number; + int coordinates[2]; + bool ear; + struct Vertex *next; + struct Vertex *prev; +}; + +/** + * Initializes a new vertex struct. + * @param number The number of the vertex for identification. + * @param x X coordinate. + * @param y Y coordinate. + * @return An initialized Vertex struct. + */ +struct Vertex *new_vertex(int number, int x, int y); + +/** + * Adds a vertex to a parent vertex under the assumption the parent vertex + * is the LAST vertex in the list. The reason this is necessary is because + * the list of vertices is circular, and as such the last element points + * back to the head of the list. The list will be modified in place. + * + * @param head The head of the polygon's vertex list. + * @param parent The parent vertex to add the new one to. + * @param child The child vertex. + */ +void add_vertex(struct Vertex **head, struct Vertex **parent, + struct Vertex **child); + +/** + * Frees a single vertex. This does not free it's next and previous pointers. + * @param vertex The vertex to free. + */ +void free_vertex(struct Vertex *vertex); + +/** + * Frees an entire polygon given it's head (first) vertex. + * @param head The start of the counter-clockwise ordered polygon. + */ +void free_polygon(struct Vertex **head); + +/** + * Pretty prints a vertex. + * @param vertex The vertex. + */ +void print_vertex(const struct Vertex *vertex); + +/** + * Pretty prints a polygon. + * @param head The first point in the polygon. + */ +void print_polygon(const struct Vertex *head); + +/** + * Traverses the polygon represented by the starting point `head` and + * sets the ear parameter correctly. + * + * @param head + */ +void init_ear(struct Vertex *head); + +/** + * Counts the number of vertices in a polygon. + * @param head The start vertex of the polygon. + * @return The count of vertices. + */ +int vertex_count(const struct Vertex *head); + +#endif // _CGC_VERTEX_H \ No newline at end of file diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..3330750 --- /dev/null +++ b/src/main.c @@ -0,0 +1,64 @@ +#include + +#include "vertex.h" +#include "math.h" + +void chapter1() { + // Initialize our chapter 1 polygon + struct Vertex *v0 = new_vertex(0, 0, 0); + struct Vertex *v1 = new_vertex(1, 10, 7); + struct Vertex *v2 = new_vertex(2, 12, 3); + struct Vertex *v3 = new_vertex(3, 20, 8); + struct Vertex *v4 = new_vertex(4, 13, 17); + struct Vertex *v5 = new_vertex(5, 10, 12); + struct Vertex *v6 = new_vertex(6, 12, 14); + struct Vertex *v7 = new_vertex(7, 14, 9); + struct Vertex *v8 = new_vertex(8, 8, 10); + struct Vertex *v9 = new_vertex(9, 6, 14); + struct Vertex *v10 = new_vertex(10, 10, 15); + struct Vertex *v11 = new_vertex(11, 7, 18); + struct Vertex *v12 = new_vertex(12, 0, 16); + struct Vertex *v13 = new_vertex(13, 1, 13); + struct Vertex *v14 = new_vertex(14, 3, 15); + struct Vertex *v15 = new_vertex(15, 5, 8); + struct Vertex *v16 = new_vertex(16, -2, 9); + struct Vertex *v17 = new_vertex(17, 5, 5); + + // TODO: You may be able to change this back to normal as long as you're + // using -> to set the value on the struct. This is interpreted + // as (*thing).xyz so you are doing too much work on pointers + // in this. + add_vertex(&v0, &v0, &v1); + add_vertex(&v0, &v1, &v2); + add_vertex(&v0, &v2, &v3); + add_vertex(&v0, &v3, &v4); + add_vertex(&v0, &v4, &v5); + add_vertex(&v0, &v5, &v6); + add_vertex(&v0, &v6, &v7); + add_vertex(&v0, &v7, &v8); + add_vertex(&v0, &v8, &v9); + add_vertex(&v0, &v9, &v10); + add_vertex(&v0, &v10, &v11); + add_vertex(&v0, &v11, &v12); + add_vertex(&v0, &v12, &v13); + add_vertex(&v0, &v13, &v14); + add_vertex(&v0, &v14, &v15); + add_vertex(&v0, &v15, &v16); + add_vertex(&v0, &v16, &v17); + + init_ear(v0); + + print_polygon(v0); + + triangulate(v0); + + //print_polygon(v0); + + free_polygon(&v0); +} + +int main() +{ + chapter1(); + return 0; +} diff --git a/src/math.c b/src/math.c new file mode 100644 index 0000000..c4973f1 --- /dev/null +++ b/src/math.c @@ -0,0 +1,206 @@ +#include "math.h" +#include + +int area2(const struct Vertex *a, const struct Vertex *b, + const struct Vertex *c) +{ + return ((b->coordinates[X] - a->coordinates[X]) * + (c->coordinates[Y] - a->coordinates[Y])) - + ((c->coordinates[X] - a->coordinates[X]) * + (b->coordinates[Y] - a->coordinates[Y])); +} + +int area_poly_2(const struct Vertex *head) +{ + int sum = 0; + struct Vertex *a = head->next; + + do { + sum += area2(head, a, a->next); + a = a->next; + } while (a->next != head); + + return sum; +} + +bool left(const struct Vertex *a, const struct Vertex *b, + const struct Vertex *c) +{ + return area2(a, b, c) > 0; +} + +bool left_on(const struct Vertex *a, const struct Vertex *b, + const struct Vertex *c) +{ + return area2(a, b, c) >= 0; +} + +bool collinear(const struct Vertex *a, const struct Vertex *b, + const struct Vertex *c) +{ + return area2(a, b, c) == 0; +} + +bool intersect_prop(const struct Vertex *a, const struct Vertex *b, + const struct Vertex *c, const struct Vertex *d) +{ + if (collinear(a, b, c) || collinear(a, b, d) || collinear(c, d, a) || + collinear(c, d, b)) { + return false; + } + + // The arguments are negated to insure they are 0/1 + return !left(a, b, c) ^ !left(a, b, d) && !left(c, d, a) ^ !left(c, d, b); +} + +bool between(const struct Vertex *a, const struct Vertex *b, + const struct Vertex *c) +{ + if (!collinear(a, b, c)) { + return false; + } + + // if ab is not vertical, check betweenness on x, else on y. + if (a->coordinates[X] != b->coordinates[X]) { + return ((a->coordinates[X] <= c->coordinates[X]) && + (c->coordinates[X] <= b->coordinates[X])) || + ((a->coordinates[X] >= c->coordinates[X]) && + (c->coordinates[X] >= b->coordinates[X])); + } else { + return ((a->coordinates[Y] <= c->coordinates[Y]) && + (c->coordinates[Y] <= b->coordinates[Y])) || + ((a->coordinates[Y] >= c->coordinates[Y]) && + (c->coordinates[Y] >= b->coordinates[Y])); + } +} + +bool intersect(const struct Vertex *a, const struct Vertex *b, + const struct Vertex *c, const struct Vertex *d) +{ + // First check for proper intersection - the intersection that you + // normally think of (two lines forming a kind of cross). + if (intersect_prop(a, b, c, d)) { + return true; + } else if (between(a, b, c) || between(a, b, d) || between(c, d, a) || + between(c, d, b)) { + + // Improper intersection - where an endpoint of one line + // segment is intersected on another line segment. + return true; + } else { + return false; + } +} + +bool diagonalie(const struct Vertex *head, const struct Vertex *a, + const struct Vertex *b) +{ + struct Vertex *c; + struct Vertex *c1; + + c = head; + + // For each edge (c, c1) of the polygon represented by head + do { + c1 = c->next; + + /* Skip the edge (c, c1) if it is incident with (a, b). + In this case we are testing first to make sure the chosen points + aren't the same points as our test edge ab (trivial case of + incidence) and then testing to make sure there is no intersection + (normal idea of incidence of two line segments). + */ + if ((c != a) && (c1 != a) && (c != b) && (c1 != b) && + intersect(a, b, c, c1)) { + return false; + } + + c = c->next; + } while (c != head); + + return true; +} + +bool in_cone(const struct Vertex *a, const struct Vertex *b) +{ + struct Vertex *a0; + struct Vertex *a1; + + a1 = a->next; + a0 = a->prev; + + // If a is a convex vertex... + if (left_on(a, a1, a0)) { + return left(a, b, a0) && left(b, a, a1); + } + + // Otherwise a is reflex... + // The result is a0 and a1 will be "further outside" of a, so the + // diagonal formed by a0a1 is external to the cone. + return !(left_on(a, b, a1) && left_on(b, a, a0)); +} + +bool diagonal(const struct Vertex *head, const struct Vertex *a, + const struct Vertex *b) +{ + // Performance wise in_cone is called first because it is a constant + // time operation that, when it fails, prevents a call to the more + // potentially performance heavy looping in diagonalie. + return in_cone(a, b) && in_cone(b, a) && diagonalie(head, a, b); +} + +void triangulate(const struct Vertex *head) +{ + // 5 consecutive vertices + struct Vertex *v0; + struct Vertex *v1; + struct Vertex *v2; + struct Vertex *v3; + struct Vertex *v4; + + int n = vertex_count(head); + + // Each step of the outer loop removes one ear + while (n > 3) { + // Inner loop searches for an ear. + v2 = head; + do { + if (v2->ear) { + // Found an ear, fill the variables + v3 = v2->next; + v4 = v3->next; + v1 = v2->prev; + v0 = v1->prev; + + // Print the diagonal + printf("DIAGONAL: (%d, %d)\n", v1->number, v3->number); + + // Update the earity of the remaining diagonal endpoints + v1->ear = diagonal(head, v0, v3); + v3->ear = diagonal(head, v1, v4); + + // Cut off the ear v2. + v1->next = v3; + v3->prev = v1; + head = v3; + n--; + + break; + } + + if(v2 -> ear) { + struct Vertex *temp = v2->next; + + // Since we have pointed around v2 (ear removal) there is no + // way to reach it anymore. As a result a free call is necessary + // to clean it up. + free_vertex(v2); + + v2 = temp; + } else { + v2 = v2->next; + } + + } while (v2 != head); + } +} diff --git a/src/vertex.c b/src/vertex.c new file mode 100644 index 0000000..01fb98f --- /dev/null +++ b/src/vertex.c @@ -0,0 +1,101 @@ +#include "vertex.h" +#include "math.h" + +struct Vertex *new_vertex(int number, int x, int y) +{ + struct Vertex *newv = (struct Vertex *)malloc(sizeof(struct Vertex)); + + if (newv == NULL) { + printf("Out of memory"); + exit(EXIT_FAILURE); + } + + newv->number = number; + newv->coordinates[0] = x; + newv->coordinates[1] = y; + newv->ear = false; + + return newv; +} + +void add_vertex(struct Vertex **head, struct Vertex **parent, + struct Vertex **child) +{ + (*parent)->next = *child; + (*child)->prev = *parent; + (*child)->next = *head; + (*head)->prev = *child; +}; + +void free_vertex(struct Vertex *vertex) +{ + if (vertex != NULL) { + free(vertex); + } +} + +void free_polygon(struct Vertex **head) +{ + if(head == NULL) { + return; + } + + while(*head != NULL) { + struct Vertex **temp = head; + (*head) = (*head)->next; + free(*temp); + *temp = NULL; + } +} + +void print_vertex(const struct Vertex *vertex) +{ + printf("VERTEX {number: %d x: %d y: %d ear: %d}\n", vertex->number, + vertex->coordinates[X], vertex->coordinates[Y], vertex->ear); +} + +void print_polygon(const struct Vertex *head) { + const struct Vertex *v = head->next; + + printf("-------"); + while(v != head) { + print_vertex(v); + v = v->next; + } +} + +void init_ear(struct Vertex *head) +{ + // 3 consecutive vertices + struct Vertex *v0; + struct Vertex *v1; + struct Vertex *v2; + + // Initialize v1->ear for all vertices + v1 = head; + + do { + v2 = v1->next; + v0 = v1->prev; + + // If v0v2 is a diagonal, v1 is an ear by definition. + v1->ear = diagonal(head, v0, v2); + + // Move one point over and run it again. + v1 = v1->next; + } while (v1 != head); +} + + +int vertex_count(const struct Vertex *head) +{ + int sum = 1; + const struct Vertex *v = head->next; + + while(v != head) { + sum++; + v = v->next; + } + + return sum; +}