Browse Source

init chapter 1

master
Taylor Bockman 4 years ago
commit
60862d95cc
  1. 15
      .clang-format
  2. 117
      .gitignore
  3. 1
      .idea/.name
  4. 5
      .idea/codeStyles/codeStyleConfig.xml
  5. 2
      .idea/compginc.iml
  6. 13
      .idea/misc.xml
  7. 8
      .idea/modules.xml
  8. 6
      .idea/vcs.xml
  9. 15
      CMakeLists.txt
  10. 26
      README.md
  11. 6
      include/error.h
  12. 146
      include/math.h
  13. 77
      include/vertex.h
  14. 64
      src/main.c
  15. 206
      src/math.c
  16. 101
      src/vertex.c

15
.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

117
.gitignore vendored

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

1
.idea/.name

@ -0,0 +1 @@
cgc

5
.idea/codeStyles/codeStyleConfig.xml

@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

2
.idea/compginc.iml

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<module classpath="CMake" type="CPP_MODULE" version="4" />

13
.idea/misc.xml

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
<component name="CidrRootsConfiguration">
<sourceRoots>
<file path="$PROJECT_DIR$/include" />
<file path="$PROJECT_DIR$/src" />
</sourceRoots>
</component>
<component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" />
</component>
</project>

8
.idea/modules.xml

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/compginc.iml" filepath="$PROJECT_DIR$/.idea/compginc.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

15
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})

26
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 <directory of repository>
cmake .
make
./bin/cgc
```

6
include/error.h

@ -0,0 +1,6 @@
#ifndef _CGC_ERROR_H
#define _CGC_ERROR_H
#define EXIT_FAILURE 1
#endif // _CGC_ERROR_H

146
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

77
include/vertex.h

@ -0,0 +1,77 @@
#ifndef _CGC_VERTEX_H
#define _CGC_VERTEX_H
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
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

64
src/main.c

@ -0,0 +1,64 @@
#include <stdio.h>
#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;
}

206
src/math.c

@ -0,0 +1,206 @@
#include "math.h"
#include <vertex.h>
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);
}
}

101
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;
}
Loading…
Cancel
Save