From 7af561eccb7b4210e4e8233d53876b7af5607234 Mon Sep 17 00:00:00 2001 From: pacien Date: Mon, 18 Dec 2017 17:20:35 +0100 Subject: Force doc rebuild Signed-off-by: pacien --- makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/makefile b/makefile index 33706f3..906516f 100644 --- a/makefile +++ b/makefile @@ -57,8 +57,8 @@ clean-bin: .PHONY: api-doc clean-api-doc api-doc: - naturaldocs --project $(DOC_DIR)/gen/ --output HTML $(DOC_DIR)/api/ \ - --input $(INCLUDE_DIR) --input $(DOC_DIR)/topics/ + naturaldocs --rebuild --project $(DOC_DIR)/gen/ --output HTML $(DOC_DIR)/api/ \ + --input $(INCL_DIR) --input $(DOC_DIR)/topics/ $(RM) $(DOC_DIR)/gen/Menu.txt clean-api-doc: -- cgit v1.2.3 From a767c658cb603de9ec9f0577627b9b32cbf82b2b Mon Sep 17 00:00:00 2001 From: pacien Date: Fri, 22 Dec 2017 01:53:55 +0100 Subject: Simplify and add geom. and matrix utility functions Signed-off-by: pacien --- include/common/geom.h | 54 +++++++++++++++++++++++++++++++++++++++++ include/common/matrix.h | 51 --------------------------------------- include/morpher/matrix.h | 41 +++++++++++++++++++++++++++++++ src/common/geom.c | 19 +++++++++++++++ src/common/matrix.c | 63 ------------------------------------------------ src/morpher/matrix.c | 16 ++++++++++++ test/common/matrix.c | 21 ---------------- test/morpher/matrix.c | 19 +++++++++++++++ 8 files changed, 149 insertions(+), 135 deletions(-) delete mode 100644 include/common/matrix.h create mode 100644 include/morpher/matrix.h delete mode 100644 src/common/matrix.c create mode 100644 src/morpher/matrix.c delete mode 100644 test/common/matrix.c create mode 100644 test/morpher/matrix.c diff --git a/include/common/geom.h b/include/common/geom.h index a843c76..b3564a5 100644 --- a/include/common/geom.h +++ b/include/common/geom.h @@ -38,6 +38,46 @@ typedef struct { CartesianVector origin, target; } CartesianMapping; + +/** + * Function: m + * Shorthand for an identity mapping. + * + * Parameters: + * x - the x-coordinate + * y - the y-coordinate + * + * Returns: + * A cartesian identity mapping + */ +CartesianMapping m(int x, int y); + +/** + * Function: v + * Shorthand for a vector. + * + * Parameters: + * x - the x-coordinate + * y - the y-coordinate + * + * Returns: + * An integer vector + */ +CartesianVector v(int x, int y); + +/** + * Function: mappings_equals + * Compares two cartesian mappings. + * + * Parameters: + * m1 - the first mapping + * m2 - the second mapping + * + * Returns: + * T(m1 is equal to m2) + */ +bool mappings_equals(CartesianMapping m1, CartesianMapping m2); + /** * Function: vector_equals * Compares two cartesian vectors. @@ -51,4 +91,18 @@ typedef struct { */ bool vector_equals(CartesianVector v1, CartesianVector v2); +/** + * Function: triangle_area + * Computes the area of a triangle. + * + * Parameters: + * v1 - first vertex + * v2 - second vertex + * v3 - third vertex + * + * Returns: + * The area of the triangle spawned by the three supplied vertices + */ +IntVector triangle_area(CartesianVector v1, CartesianVector v2, CartesianVector v3); + #endif diff --git a/include/common/matrix.h b/include/common/matrix.h deleted file mode 100644 index fe4a12a..0000000 --- a/include/common/matrix.h +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef UPEM_MORPHING_MATRIX -#define UPEM_MORPHING_MATRIX - -/** - * File: matrix.h - * Matrices representation and useful operations. - * - * See also: - * The film - */ - -#include "geom.h" - -/** - * Struct: IntSquareMatrix - * Represents a square integer matrix. - * - * Fields: - * **elements - NULL-terminated array of element pointers - * dim - dimension - */ -typedef struct { - IntVector **elements; - IntVector dim; -} IntSquareMatrix; - -/** - * Function: matrix_int_det - * Computes and returns the determinant of a square integer matrix. - * - * Parameters: - * *matrix - pointer to input matrix - * - * Returns: - * The integer determinant - */ -IntVector matrix_int_det(IntSquareMatrix *matrix); - -/** - * Function: matrix_reshape - * Reshapes a flat vector into a bi-dimensional row pointer array. - * - * Parameters: - * **bi_dim - pointer to the result row array - * *flat - flat vector - * width - number of elements per row - * height - number of rows - */ -void matrix_reshape(IntVector **bi_dim, IntVector *flat, int width, int height); - -#endif diff --git a/include/morpher/matrix.h b/include/morpher/matrix.h new file mode 100644 index 0000000..8118727 --- /dev/null +++ b/include/morpher/matrix.h @@ -0,0 +1,41 @@ +#ifndef UPEM_MORPHING_MATRIX +#define UPEM_MORPHING_MATRIX + +/** + * File: matrix.h + * Determinant operations. + * + * See also: + * The film + */ + +#include "common/geom.h" + +/** + * Function: matrix_int_det2 + * Computes and returns the determinant of a square integer matrix of size 2. + * + * Parameters: + * uij - element at the i-th row and j-th column, counting from 1 + * + * Returns: + * The integer determinant + */ +IntVector matrix_int_det2(IntVector u11, IntVector u12, + IntVector u21, IntVector u22); + +/** + * Function: matrix_int_det3 + * Computes and returns the determinant of a square integer matrix of size 3. + * + * Parameters: + * uij - element at the i-th row and j-th column, counting from 1 + * + * Returns: + * The integer determinant + */ +IntVector matrix_int_det3(IntVector u11, IntVector u12, IntVector u13, + IntVector u21, IntVector u22, IntVector u23, + IntVector u31, IntVector u32, IntVector u33); + +#endif diff --git a/src/common/geom.c b/src/common/geom.c index 7abb422..219270f 100644 --- a/src/common/geom.c +++ b/src/common/geom.c @@ -1,5 +1,24 @@ #include "common/geom.h" +#include "morpher/matrix.h" + +CartesianMapping m(int x, int y) { + return (CartesianMapping) {{x, y}, + {x, y}}; +} + +CartesianVector v(int x, int y) { + return (CartesianVector) {x, y}; +} + +bool mappings_equals(CartesianMapping m1, CartesianMapping m2) { + return vector_equals(m1.origin, m2.origin) && vector_equals(m1.target, m2.target); +} bool vector_equals(CartesianVector v1, CartesianVector v2) { return v1.x == v2.x && v1.y == v2.y; } + +IntVector triangle_area(CartesianVector v1, CartesianVector v2, CartesianVector v3) { + return matrix_int_det2(v1.x - v3.x, v2.x - v3.x, + v1.y - v3.y, v2.y - v3.y); +} diff --git a/src/common/matrix.c b/src/common/matrix.c deleted file mode 100644 index 60f9afb..0000000 --- a/src/common/matrix.c +++ /dev/null @@ -1,63 +0,0 @@ -#include "common/matrix.h" -#include -#include -#include -#include "common/mem.h" - -static inline IntSquareMatrix *matrix_without_row(IntSquareMatrix *target, IntSquareMatrix *origin, - IntVector omitted_row) { - int origin_row, target_row; - - for (origin_row = 0, target_row = 0; origin_row < origin->dim; ++origin_row) - if (origin_row != omitted_row) - target->elements[target_row++] = origin->elements[origin_row]; - - return target; -} - -static inline IntVector det_dev_sign(IntVector row, IntVector col) { - assert(row > 0 && col > 0); - return ((row + col) % 2 == 0) ? 1 : -1; -} - -static inline IntVector det_reduce(IntSquareMatrix *matrix) { - IntSquareMatrix sub_matrix; - int row; - IntVector det = 0; - - assert(matrix->dim > 2); - - sub_matrix.dim = matrix->dim - 1; - sub_matrix.elements = malloc_or_die(sub_matrix.dim * sizeof(IntVector *)); - - for (row = 0; row < matrix->dim; ++row) - det += matrix->elements[row][matrix->dim - 1] - * det_dev_sign(row + 1, matrix->dim) - * matrix_int_det(matrix_without_row(&sub_matrix, matrix, row)); - - - free(sub_matrix.elements); - return det; -} - -IntVector matrix_int_det(IntSquareMatrix *matrix) { - assert(matrix->dim > 0); - switch (matrix->dim) { - case 1: - return matrix->elements[0][0]; - - case 2: - return matrix->elements[0][0] * matrix->elements[1][1] - matrix->elements[0][1] * matrix->elements[1][0]; - - default: - return det_reduce(matrix); - } -} - -void matrix_reshape(IntVector **bi_dim, IntVector *flat, int width, int height) { - int row; - assert(width > 0 && height > 0); - - for (row = 0; row < height; ++row) - bi_dim[row] = flat + row * width; -} diff --git a/src/morpher/matrix.c b/src/morpher/matrix.c new file mode 100644 index 0000000..2fe1193 --- /dev/null +++ b/src/morpher/matrix.c @@ -0,0 +1,16 @@ +#include "morpher/matrix.h" + +IntVector matrix_int_det2(IntVector u11, IntVector u12, + IntVector u21, IntVector u22) { + + return u11 * u22 - u12 * u21; +} + +IntVector matrix_int_det3(IntVector u11, IntVector u12, IntVector u13, + IntVector u21, IntVector u22, IntVector u23, + IntVector u31, IntVector u32, IntVector u33) { + + return u11 * matrix_int_det2(u22, u23, u32, u33) + - u21 * matrix_int_det2(u12, u13, u32, u33) + + u31 * matrix_int_det2(u12, u13, u22, u23); +} diff --git a/test/common/matrix.c b/test/common/matrix.c deleted file mode 100644 index 6d85304..0000000 --- a/test/common/matrix.c +++ /dev/null @@ -1,21 +0,0 @@ -#include "common/matrix.h" -#include - -static void test_matrix_int_det() { - IntSquareMatrix matrix; - IntVector *elements[3]; - - matrix_reshape(elements, (IntVector[]) {-2, +2, -3, - -1, +1, +3, - +2, +0, -1}, 3, 3); - - matrix.dim = 3; - matrix.elements = elements; - - assert(matrix_int_det(&matrix) == 18); -} - -int main(int argc, char **argv) { - test_matrix_int_det(); - return 0; -} diff --git a/test/morpher/matrix.c b/test/morpher/matrix.c new file mode 100644 index 0000000..0c96fab --- /dev/null +++ b/test/morpher/matrix.c @@ -0,0 +1,19 @@ +#include "morpher/matrix.h" +#include + +static void test_matrix_int_det2() { + assert(matrix_int_det2(5, 7, + 2, 3) == 1); +} + +static void test_matrix_int_det3() { + assert(matrix_int_det3(-2, +2, -3, + -1, +1, +3, + +2, +0, -1) == 18); +} + +int main(int argc, char **argv) { + test_matrix_int_det2(); + test_matrix_int_det3(); + return 0; +} -- cgit v1.2.3 From 553dc4a5b272a1828940e72a07b1c9a7210be464 Mon Sep 17 00:00:00 2001 From: pacien Date: Fri, 22 Dec 2017 01:57:03 +0100 Subject: Add triangle map and quadrilateral representation and common operations Signed-off-by: pacien --- include/morpher/quadrilateral.h | 39 +++++++++++++ include/morpher/trianglemap.h | 123 ++++++++++++++++++++++++++++++++++++++++ src/morpher/quadrilateral.c | 58 +++++++++++++++++++ src/morpher/trianglemap.c | 64 +++++++++++++++++++++ test/morpher/quadrilateral.c | 71 +++++++++++++++++++++++ test/morpher/trianglemap.c | 71 +++++++++++++++++++++++ 6 files changed, 426 insertions(+) create mode 100644 include/morpher/quadrilateral.h create mode 100644 include/morpher/trianglemap.h create mode 100644 src/morpher/quadrilateral.c create mode 100644 src/morpher/trianglemap.c create mode 100644 test/morpher/quadrilateral.c create mode 100644 test/morpher/trianglemap.c diff --git a/include/morpher/quadrilateral.h b/include/morpher/quadrilateral.h new file mode 100644 index 0000000..c8cf3a1 --- /dev/null +++ b/include/morpher/quadrilateral.h @@ -0,0 +1,39 @@ +#ifndef UPEM_MORPHING_QUADRILATERAL +#define UPEM_MORPHING_QUADRILATERAL + +/** + * File: quadrilateral.h + * Operations on quadrilaterals formed by adjacent triangles pairs. + */ + +#include +#include "common/geom.h" +#include "morpher/trianglemap.h" + +/** + * Function: quadrilateral_flip_diagonal + * Flips the diagonal of a quadrilateral formed by two triangles sharing a common edge, + * using a positive rotation-like transform inverting both references. + * This transforms keeps the positive orientation of the vertices. + * Pointers to surrounding triangles are updated accordingly. + * + * Parameters: + * *t1 - the first triangle + * *t2 - the second triangle + */ +void quadrilateral_flip_diagonal(TriangleMap *t1, TriangleMap *t2); + +/** + * Function: quadrilateral_is_delaunay + * Tells whether the quadrilateral formed by the two supplied adjacent triangle forms a Delaunay triangulation. + * + * Parameters: + * *t1 - first triangle + * *t2 - second triangle adjacent to the first one + * + * Returns: + * T(t1 and t2 are a Delaunay triangulation in the quadrilateral formed by the twos) + */ +bool quadrilateral_is_delaunay(TriangleMap *t1, TriangleMap *t2); + +#endif diff --git a/include/morpher/trianglemap.h b/include/morpher/trianglemap.h new file mode 100644 index 0000000..05e7b87 --- /dev/null +++ b/include/morpher/trianglemap.h @@ -0,0 +1,123 @@ +#ifndef UPEM_MORPHING_TRIANGLEMAP +#define UPEM_MORPHING_TRIANGLEMAP + +/** + * File: trianglemap.h + * Represents a triangle map in a triangulation, in a plane oriented left to right and top to bottom. + */ + +#include +#include "common/geom.h" + +/** + * Struct: TriangleMap + * Represents a triangle mapping. + * + * Fields: + * vertices[] - array of vertices + * *neighbors[] - array of neighboring triangles ordered by the edges spawned by the vertices + * *next - pointer to another triangle for linear browsing, or NULL + */ +typedef struct _TriangleMap { + CartesianMapping vertices[3]; + struct _TriangleMap *neighbors[3]; + struct _TriangleMap *next; +} TriangleMap; + +/** + * Function: trianglemap_create + * Creates a TriangleMap, instantiating it on the heap. + * + * Parameters: + * vertex1 - first vertex + * vertex2 - second vertex + * vertex3 - third vertex + * + * Returns: + * A pointer to the newly created triangle + */ +TriangleMap *trianglemap_create(CartesianMapping v1, CartesianMapping v2, CartesianMapping v3); + +/** + * Function: trianglemap_destroy + * Destroys a triangle, freeing its dynamically allocated resources. + * Also returns the next triangle for convenience. + * Does not update surrounding triangles. + * + * Parameters: + * *t - pointer to the triangle to destroy + * + * Returns: + * A pointer to the next linear triangle + */ +TriangleMap *trianglemap_destroy(TriangleMap *t); + +/** + * Function: trianglemap_to + * Returns a pointer to the current or the closest adjacent neighbour TriangleMap + * minimizing the distance to the targeted position. + * + * Parameters: + * *t - the origin triangle + * v - the target position vector + * + * Returns: + * A pointer to the current or an immediate neighbour TriangleMap + */ +TriangleMap *trianglemap_to(TriangleMap *t, CartesianVector v); + +/** + * Function: trianglemap_find_common_edge + * Finds the index of the commonly shared edge in the neighbourhood of t. + * + * Parameters: + * *t - the base triangle + * *neighbor - the neighbour to search for + * + * Returns: + * The index of the common neighbour in the listing of t. + */ +int trianglemap_find_common_edge(TriangleMap *t, TriangleMap *neighbor); + +/** + * Function: trianglemap_set_neighbors + * Updates a triangle neighbors. + * Vertices must be given in a positively oriented (trigonometric) order. + * Neighbours must be given in the same order as the vertices. + * + * Parameters: + * *t - the triangle to modify + * *n1 - first neighbour + * *n2 - second neighbour + * *n3 - third neighbour + * *next - linear neighbour + */ +void trianglemap_set_neighbors(TriangleMap *t, TriangleMap *n1, TriangleMap *n2, TriangleMap *n3, TriangleMap *next); + +/** + * Function: triangle_replace_neighbor + * Substitutes a given neighbour in a neighbourhood. + * + * Parameters: + * *t - the base triangle + * *old - the neighbour to replace + * *new - the new neighbour + */ +void trianglemap_replace_neighbor(TriangleMap *t, TriangleMap *old, TriangleMap *new); + +/** + * Function: trianglemap_split + * Splits a triangle into three sub-triangles at the supplied center vertex, updating the surrounding triangles. + * The first triangle resulting from the split is returned, with the two others chained as linear neighbours. + * Those generated triangles each have one of the original surrounding neighbour as first element in their listings. + * + * Parameters: + * *t - the triangle to split + * v - the new vertex to add + * + * Returns: + * The first generated TriangleMap + */ +TriangleMap *trianglemap_split(TriangleMap *t, CartesianMapping v); + +#endif diff --git a/src/morpher/quadrilateral.c b/src/morpher/quadrilateral.c new file mode 100644 index 0000000..b9e740b --- /dev/null +++ b/src/morpher/quadrilateral.c @@ -0,0 +1,58 @@ +#include "morpher/quadrilateral.h" +#include "common/geom.h" +#include "morpher/matrix.h" + +static inline IntVector p2(IntVector n) { + return n * n; +} + +static inline bool in_circumcircle(TriangleMap *t, CartesianVector v) { + CartesianVector a = t->vertices[0].origin, b = t->vertices[1].origin, c = t->vertices[2].origin; + IntVector v2 = p2(v.x) + p2(v.y); + return matrix_int_det3(a.x - v.x, a.y - v.y, p2(a.x) + p2(a.y) - v2, + b.x - v.x, b.y - v.y, p2(b.x) + p2(b.y) - v2, + c.x - v.x, c.y - v.y, p2(c.x) + p2(c.y) - v2) > 0; +} + +static inline void rotate_vertices(TriangleMap *t1, TriangleMap *t2, int e1, int e2) { + CartesianMapping quad[] = { + t1->vertices[(e1 + 1) % 3], + t1->vertices[(e1 + 2) % 3], + t2->vertices[(e2 + 1) % 3], + t2->vertices[(e2 + 2) % 3] + }; + + t1->vertices[(e1 + 1) % 3] = quad[1]; + t1->vertices[(e1 + 2) % 3] = quad[2]; + t1->vertices[(e1 + 3) % 3] = quad[3]; + t2->vertices[(e2 + 1) % 3] = quad[3]; + t2->vertices[(e2 + 2) % 3] = quad[0]; + t2->vertices[(e2 + 3) % 3] = quad[1]; +} + +static inline void rotate_neighbors(TriangleMap *t1, TriangleMap *t2, int e1, int e2) { + TriangleMap *neighbors[] = { + t1->neighbors[(e1 + 1) % 3], + t1->neighbors[(e1 + 2) % 3], + t2->neighbors[(e2 + 1) % 3], + t2->neighbors[(e2 + 2) % 3] + }; + + t1->neighbors[(e1 + 1) % 3] = neighbors[1]; + t1->neighbors[(e1 + 2) % 3] = neighbors[2]; + t2->neighbors[(e2 + 1) % 3] = neighbors[3]; + t2->neighbors[(e2 + 2) % 3] = neighbors[0]; + trianglemap_replace_neighbor(t1->neighbors[(e1 + 2) % 3], t2, t1); + trianglemap_replace_neighbor(t2->neighbors[(e2 + 2) % 3], t1, t2); +} + +void quadrilateral_flip_diagonal(TriangleMap *t1, TriangleMap *t2) { + int e1 = trianglemap_find_common_edge(t1, t2), e2 = trianglemap_find_common_edge(t2, t1); + rotate_vertices(t1, t2, e1, e2); + rotate_neighbors(t1, t2, e1, e2); +} + +bool quadrilateral_is_delaunay(TriangleMap *t1, TriangleMap *t2) { + return in_circumcircle(t1, t2->vertices[(trianglemap_find_common_edge(t2, t1) + 2) % 3].origin) && + in_circumcircle(t2, t1->vertices[(trianglemap_find_common_edge(t1, t2) + 2) % 3].origin); +} diff --git a/src/morpher/trianglemap.c b/src/morpher/trianglemap.c new file mode 100644 index 0000000..45f4ade --- /dev/null +++ b/src/morpher/trianglemap.c @@ -0,0 +1,64 @@ +#include "morpher/trianglemap.h" +#include +#include +#include "common/geom.h" +#include "common/mem.h" + +TriangleMap *trianglemap_create(CartesianMapping v1, CartesianMapping v2, CartesianMapping v3) { + TriangleMap *triangle = malloc_or_die(sizeof(TriangleMap)); + triangle->vertices[0] = v1; + triangle->vertices[1] = v2; + triangle->vertices[2] = v3; + return triangle; +} + +TriangleMap *trianglemap_destroy(TriangleMap *t) { + TriangleMap *next = t->next; + free(t); + return next; +} + +TriangleMap *trianglemap_to(TriangleMap *t, CartesianVector v) { + int edge; + + for (edge = 0; edge < 3; ++edge) + if (triangle_area(t->vertices[edge].origin, t->vertices[(edge + 1) % 3].origin, v) >= 0) + return t->neighbors[edge]; + + return t; +} + +int trianglemap_find_common_edge(TriangleMap *t, TriangleMap *neighbor) { + int edge; + for (edge = 0; t->neighbors[edge] != neighbor && edge < 3; ++edge); + assert(t->neighbors[edge] == neighbor && neighbor != NULL); + return edge; +} + +void trianglemap_set_neighbors(TriangleMap *t, TriangleMap *n1, TriangleMap *n2, TriangleMap *n3, TriangleMap *next) { + t->neighbors[0] = n1; + t->neighbors[1] = n2; + t->neighbors[2] = n3; + t->next = next; +} + +void trianglemap_replace_neighbor(TriangleMap *t, TriangleMap *old, TriangleMap *new) { + if (t != NULL) t->neighbors[trianglemap_find_common_edge(t, old)] = new; +} + +TriangleMap *trianglemap_split(TriangleMap *t, CartesianMapping v) { + assert(trianglemap_to(t, v.origin) == t); + + TriangleMap *triangle2 = trianglemap_create(t->vertices[1], t->vertices[2], v); + TriangleMap *triangle3 = trianglemap_create(t->vertices[2], t->vertices[0], v); + t->vertices[2] = v; + + trianglemap_replace_neighbor(t->neighbors[1], t, triangle2); + trianglemap_replace_neighbor(t->neighbors[2], t, triangle3); + + trianglemap_set_neighbors(triangle3, t->neighbors[2], t, triangle2, t->next); + trianglemap_set_neighbors(triangle2, t->neighbors[1], triangle3, t, triangle3); + trianglemap_set_neighbors(t, t->neighbors[0], triangle2, triangle3, triangle2); + + return t; +} diff --git a/test/morpher/quadrilateral.c b/test/morpher/quadrilateral.c new file mode 100644 index 0000000..0ab0e4b --- /dev/null +++ b/test/morpher/quadrilateral.c @@ -0,0 +1,71 @@ +#include "morpher/quadrilateral.h" +#include +#include + +static inline bool neighbors_equals(TriangleMap *neighbors[], + TriangleMap *n1, TriangleMap *n2, TriangleMap *n3) { + return neighbors[0] == n1 && neighbors[1] == n2 && neighbors[2] == n3; +} + +static inline bool vertices_equals(CartesianMapping maps[], + CartesianMapping m1, CartesianMapping m2, CartesianMapping m3) { + return mappings_equals(maps[0], m1) && mappings_equals(maps[1], m2) && mappings_equals(maps[2], m3); +} + +/* + * - + * / \ t + * --- + * r |/| l + * --- + */ +static void test_quadrilateral_flip_diagonal() { + TriangleMap *t = trianglemap_create(m(50, 0), m(0, 100), m(100, 100)); + TriangleMap *r = trianglemap_create(m(100, 100), m(0, 100), m(100, 200)); + TriangleMap *l = trianglemap_create(m(0, 100), m(0, 200), m(100, 200)); + trianglemap_set_neighbors(t, NULL, r, NULL, r); + trianglemap_set_neighbors(r, t, l, NULL, l); + trianglemap_set_neighbors(l, NULL, NULL, r, NULL); + quadrilateral_flip_diagonal(r, l); + + assert(vertices_equals(t->vertices, m(50, 0), m(0, 100), m(100, 100))); + assert(vertices_equals(r->vertices, m(0, 100), m(0, 200), m(100, 100))); + assert(vertices_equals(l->vertices, m(0, 200), m(100, 200), m(100, 100))); + assert(neighbors_equals(t->neighbors, NULL, r, NULL)); + assert(neighbors_equals(r->neighbors, NULL, l, t)); + assert(neighbors_equals(l->neighbors, NULL, NULL, r)); + + while (t != NULL) t = trianglemap_destroy(t); +} + +static void test_quadrilateral_is_delaunay() { + { + TriangleMap *l = trianglemap_create(m(0, 0), m(0, 3), m(3, 3)); + TriangleMap *r = trianglemap_create(m(0, 0), m(3, 3), m(2, 1)); + trianglemap_set_neighbors(l, NULL, NULL, r, r); + trianglemap_set_neighbors(r, l, NULL, NULL, NULL); + + assert(!quadrilateral_is_delaunay(l, r)); + + trianglemap_destroy(l); + trianglemap_destroy(r); + } + + { + TriangleMap *l = trianglemap_create(m(0, 0), m(0, 3), m(3, 3)); + TriangleMap *r = trianglemap_create(m(0, 0), m(3, 3), m(4, -1)); + trianglemap_set_neighbors(l, NULL, NULL, r, r); + trianglemap_set_neighbors(r, l, NULL, NULL, NULL); + + assert(quadrilateral_is_delaunay(l, r)); + + trianglemap_destroy(l); + trianglemap_destroy(r); + } +} + +int main(int argc, char **argv) { + test_quadrilateral_flip_diagonal(); + test_quadrilateral_is_delaunay(); + return 0; +} diff --git a/test/morpher/trianglemap.c b/test/morpher/trianglemap.c new file mode 100644 index 0000000..608f3c9 --- /dev/null +++ b/test/morpher/trianglemap.c @@ -0,0 +1,71 @@ +#include "morpher/trianglemap.h" +#include +#include +#include +#include "common/geom.h" + +static inline bool neighbors_equals(TriangleMap *neighbors[], + TriangleMap *n1, TriangleMap *n2, TriangleMap *n3) { + return neighbors[0] == n1 && neighbors[1] == n2 && neighbors[2] == n3; +} + +static inline bool vertices_equals(CartesianMapping maps[], + CartesianMapping m1, CartesianMapping m2, CartesianMapping m3) { + return mappings_equals(maps[0], m1) && mappings_equals(maps[1], m2) && mappings_equals(maps[2], m3); +} + +/* + * - + * / \ t + * --- + * l |\| r + * --- + */ +static inline TriangleMap *create_test_map() { + TriangleMap *t = trianglemap_create(m(50, 0), m(0, 100), m(100, 100)); + TriangleMap *r = trianglemap_create(m(100, 100), m(0, 100), m(100, 200)); + TriangleMap *l = trianglemap_create(m(0, 100), m(0, 200), m(100, 200)); + trianglemap_set_neighbors(t, NULL, r, NULL, r); + trianglemap_set_neighbors(r, t, l, NULL, l); + trianglemap_set_neighbors(l, NULL, NULL, r, NULL); + return t; +} + +static inline void free_map(TriangleMap *t) { + while (t != NULL) t = trianglemap_destroy(t); +} + +static void test_triangle_to() { + TriangleMap *t = create_test_map(); + + assert(trianglemap_to(t, v(80, 80)) == t); + assert(trianglemap_to(t, v(0, 0)) == t->neighbors[0]); + assert(trianglemap_to(t, v(55, 170)) == t->neighbors[1]); + + free_map(t); +} + +static void test_triangle_split() { + TriangleMap *t = create_test_map(); + TriangleMap *r = t->next; + TriangleMap *l = r->next; + TriangleMap *r1 = trianglemap_split(r, m(90, 110)); + TriangleMap *r2 = r1->next; + TriangleMap *r3 = r2->next; + + assert(vertices_equals(r1->vertices, m(100, 100), m(0, 100), m(90, 110))); + assert(vertices_equals(r2->vertices, m(0, 100), m(100, 200), m(90, 110))); + assert(vertices_equals(r3->vertices, m(100, 200), m(100, 100), m(90, 110))); + assert(neighbors_equals(r1->neighbors, t, r2, r3)); + assert(neighbors_equals(r2->neighbors, l, r3, r1)); + assert(neighbors_equals(r3->neighbors, NULL, r1, r2)); + assert(t->neighbors[1] == r1 && l->neighbors[2] == r2); + + free_map(t); +} + +int main(int argc, char **argv) { + test_triangle_to(); + test_triangle_split(); + return 0; +} -- cgit v1.2.3 From 6802eedbdde0e5a42bb7b6fc251b3123c295666f Mon Sep 17 00:00:00 2001 From: pacien Date: Fri, 22 Dec 2017 13:50:34 +0100 Subject: Rename function accurately Signed-off-by: pacien --- src/morpher/quadrilateral.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/morpher/quadrilateral.c b/src/morpher/quadrilateral.c index b9e740b..9671116 100644 --- a/src/morpher/quadrilateral.c +++ b/src/morpher/quadrilateral.c @@ -6,7 +6,7 @@ static inline IntVector p2(IntVector n) { return n * n; } -static inline bool in_circumcircle(TriangleMap *t, CartesianVector v) { +static inline bool not_in_circumcircle(TriangleMap *t, CartesianVector v) { CartesianVector a = t->vertices[0].origin, b = t->vertices[1].origin, c = t->vertices[2].origin; IntVector v2 = p2(v.x) + p2(v.y); return matrix_int_det3(a.x - v.x, a.y - v.y, p2(a.x) + p2(a.y) - v2, @@ -53,6 +53,6 @@ void quadrilateral_flip_diagonal(TriangleMap *t1, TriangleMap *t2) { } bool quadrilateral_is_delaunay(TriangleMap *t1, TriangleMap *t2) { - return in_circumcircle(t1, t2->vertices[(trianglemap_find_common_edge(t2, t1) + 2) % 3].origin) && - in_circumcircle(t2, t1->vertices[(trianglemap_find_common_edge(t1, t2) + 2) % 3].origin); + return not_in_circumcircle(t1, t2->vertices[(trianglemap_find_common_edge(t2, t1) + 2) % 3].origin) && + not_in_circumcircle(t2, t1->vertices[(trianglemap_find_common_edge(t1, t2) + 2) % 3].origin); } -- cgit v1.2.3 From 39cbe5d0d7db78f0d2808abea5562db84d03a07e Mon Sep 17 00:00:00 2001 From: pacien Date: Sat, 23 Dec 2017 21:52:22 +0100 Subject: Refactor quadrilateral tests Signed-off-by: pacien --- test/morpher/quadrilateral.c | 47 ++++++++++++++++++-------------------------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/test/morpher/quadrilateral.c b/test/morpher/quadrilateral.c index 0ab0e4b..963d1d6 100644 --- a/test/morpher/quadrilateral.c +++ b/test/morpher/quadrilateral.c @@ -20,17 +20,19 @@ static inline bool vertices_equals(CartesianMapping maps[], * --- */ static void test_quadrilateral_flip_diagonal() { - TriangleMap *t = trianglemap_create(m(50, 0), m(0, 100), m(100, 100)); - TriangleMap *r = trianglemap_create(m(100, 100), m(0, 100), m(100, 200)); - TriangleMap *l = trianglemap_create(m(0, 100), m(0, 200), m(100, 200)); + CartesianMapping A = m(50, 0), B = m(0, 100), C = m(100, 100), D = m(0, 200), E = m(100, 200); + + TriangleMap *t = trianglemap_create(A, B, C); + TriangleMap *r = trianglemap_create(C, B, E); + TriangleMap *l = trianglemap_create(B, D, E); trianglemap_set_neighbors(t, NULL, r, NULL, r); trianglemap_set_neighbors(r, t, l, NULL, l); trianglemap_set_neighbors(l, NULL, NULL, r, NULL); quadrilateral_flip_diagonal(r, l); - assert(vertices_equals(t->vertices, m(50, 0), m(0, 100), m(100, 100))); - assert(vertices_equals(r->vertices, m(0, 100), m(0, 200), m(100, 100))); - assert(vertices_equals(l->vertices, m(0, 200), m(100, 200), m(100, 100))); + assert(vertices_equals(t->vertices, A, B, C)); + assert(vertices_equals(r->vertices, B, D, C)); + assert(vertices_equals(l->vertices, D, E, C)); assert(neighbors_equals(t->neighbors, NULL, r, NULL)); assert(neighbors_equals(r->neighbors, NULL, l, t)); assert(neighbors_equals(l->neighbors, NULL, NULL, r)); @@ -38,34 +40,23 @@ static void test_quadrilateral_flip_diagonal() { while (t != NULL) t = trianglemap_destroy(t); } -static void test_quadrilateral_is_delaunay() { - { - TriangleMap *l = trianglemap_create(m(0, 0), m(0, 3), m(3, 3)); - TriangleMap *r = trianglemap_create(m(0, 0), m(3, 3), m(2, 1)); - trianglemap_set_neighbors(l, NULL, NULL, r, r); - trianglemap_set_neighbors(r, l, NULL, NULL, NULL); - - assert(!quadrilateral_is_delaunay(l, r)); - - trianglemap_destroy(l); - trianglemap_destroy(r); - } +static void test_quadrilateral_is_delaunay(CartesianMapping A, CartesianMapping B, CartesianMapping C, + CartesianMapping D, bool expected) { - { - TriangleMap *l = trianglemap_create(m(0, 0), m(0, 3), m(3, 3)); - TriangleMap *r = trianglemap_create(m(0, 0), m(3, 3), m(4, -1)); - trianglemap_set_neighbors(l, NULL, NULL, r, r); - trianglemap_set_neighbors(r, l, NULL, NULL, NULL); + TriangleMap *l = trianglemap_create(A, B, C); + TriangleMap *r = trianglemap_create(A, C, D); + trianglemap_set_neighbors(l, NULL, NULL, r, r); + trianglemap_set_neighbors(r, l, NULL, NULL, NULL); - assert(quadrilateral_is_delaunay(l, r)); + assert(quadrilateral_is_delaunay(l, r) == expected); - trianglemap_destroy(l); - trianglemap_destroy(r); - } + trianglemap_destroy(l); + trianglemap_destroy(r); } int main(int argc, char **argv) { test_quadrilateral_flip_diagonal(); - test_quadrilateral_is_delaunay(); + test_quadrilateral_is_delaunay(m(0, 0), m(0, 3), m(3, 3), m(2, 1), false); + test_quadrilateral_is_delaunay(m(0, 0), m(0, 3), m(3, 3), m(4, -1), true); return 0; } -- cgit v1.2.3 From cdbae7a5e7515ba50ae21f15929a086fc40fcae3 Mon Sep 17 00:00:00 2001 From: pacien Date: Sun, 24 Dec 2017 00:07:53 +0100 Subject: Implement Delaunay propagation Signed-off-by: pacien --- include/morpher/quadrilateral.h | 11 +++++++++++ include/morpher/trianglemap.h | 10 ++++++++++ src/morpher/quadrilateral.c | 17 +++++++++++++++-- src/morpher/trianglemap.c | 7 ++++++- test/morpher/quadrilateral.c | 26 +++++++++++++++++++++++++- 5 files changed, 67 insertions(+), 4 deletions(-) diff --git a/include/morpher/quadrilateral.h b/include/morpher/quadrilateral.h index c8cf3a1..8691263 100644 --- a/include/morpher/quadrilateral.h +++ b/include/morpher/quadrilateral.h @@ -36,4 +36,15 @@ void quadrilateral_flip_diagonal(TriangleMap *t1, TriangleMap *t2); */ bool quadrilateral_is_delaunay(TriangleMap *t1, TriangleMap *t2); +/** + * Function: quadrilateral_propagate_delaunay + * Ensures that the quadrilateral spawned by the given triangles fulfills the Delaunay criterion, + * flipping the diagonal if necessary and propagating the changes to the neighbouring triangles. + * + * Parameters: + * *start - the starting triangle + * *neighbor - a neighboring triangle + */ +void quadrilateral_propagate_delaunay(TriangleMap *start, TriangleMap *neighbor); + #endif diff --git a/include/morpher/trianglemap.h b/include/morpher/trianglemap.h index 05e7b87..0bb6a1d 100644 --- a/include/morpher/trianglemap.h +++ b/include/morpher/trianglemap.h @@ -105,6 +105,16 @@ void trianglemap_set_neighbors(TriangleMap *t, TriangleMap *n1, TriangleMap *n2, */ void trianglemap_replace_neighbor(TriangleMap *t, TriangleMap *old, TriangleMap *new); +/** + * Function: trianglemap_foreach_neighbor + * Executes the given function for each existing (non-NULL) neighbour of the supplied triangle. + * + * Parameters: + * *t - the base triangle + * *f - the function + */ +void trianglemap_foreach_neighbor(TriangleMap *t, void (*f)(TriangleMap *current, TriangleMap *neighbor)); + /** * Function: trianglemap_split * Splits a triangle into three sub-triangles at the supplied center vertex, updating the surrounding triangles. diff --git a/src/morpher/quadrilateral.c b/src/morpher/quadrilateral.c index 9671116..af77f0f 100644 --- a/src/morpher/quadrilateral.c +++ b/src/morpher/quadrilateral.c @@ -1,5 +1,6 @@ #include "morpher/quadrilateral.h" -#include "common/geom.h" +#include +#include #include "morpher/matrix.h" static inline IntVector p2(IntVector n) { @@ -47,12 +48,24 @@ static inline void rotate_neighbors(TriangleMap *t1, TriangleMap *t2, int e1, in } void quadrilateral_flip_diagonal(TriangleMap *t1, TriangleMap *t2) { - int e1 = trianglemap_find_common_edge(t1, t2), e2 = trianglemap_find_common_edge(t2, t1); + int e1, e2; + assert(t1 != NULL && t2 != NULL); + e1 = trianglemap_find_common_edge(t1, t2); + e2 = trianglemap_find_common_edge(t2, t1); rotate_vertices(t1, t2, e1, e2); rotate_neighbors(t1, t2, e1, e2); } bool quadrilateral_is_delaunay(TriangleMap *t1, TriangleMap *t2) { + assert(t1 != NULL && t2 != NULL); return not_in_circumcircle(t1, t2->vertices[(trianglemap_find_common_edge(t2, t1) + 2) % 3].origin) && not_in_circumcircle(t2, t1->vertices[(trianglemap_find_common_edge(t1, t2) + 2) % 3].origin); } + +void quadrilateral_propagate_delaunay(TriangleMap *start, TriangleMap *neighbor) { + assert(start != NULL && neighbor != NULL); + if (!quadrilateral_is_delaunay(start, neighbor)) { + quadrilateral_flip_diagonal(start, neighbor); + trianglemap_foreach_neighbor(neighbor, quadrilateral_propagate_delaunay); + } +} diff --git a/src/morpher/trianglemap.c b/src/morpher/trianglemap.c index 45f4ade..01b66c9 100644 --- a/src/morpher/trianglemap.c +++ b/src/morpher/trianglemap.c @@ -1,7 +1,6 @@ #include "morpher/trianglemap.h" #include #include -#include "common/geom.h" #include "common/mem.h" TriangleMap *trianglemap_create(CartesianMapping v1, CartesianMapping v2, CartesianMapping v3) { @@ -46,6 +45,12 @@ void trianglemap_replace_neighbor(TriangleMap *t, TriangleMap *old, TriangleMap if (t != NULL) t->neighbors[trianglemap_find_common_edge(t, old)] = new; } +void trianglemap_foreach_neighbor(TriangleMap *t, void (*f)(TriangleMap *, TriangleMap *)) { + int cursor; + assert(t != NULL); + for (cursor = 0; cursor < 3; ++cursor) if (t->neighbors[cursor] != NULL) f(t, t->neighbors[cursor]); +} + TriangleMap *trianglemap_split(TriangleMap *t, CartesianMapping v) { assert(trianglemap_to(t, v.origin) == t); diff --git a/test/morpher/quadrilateral.c b/test/morpher/quadrilateral.c index 963d1d6..b278e2f 100644 --- a/test/morpher/quadrilateral.c +++ b/test/morpher/quadrilateral.c @@ -21,7 +21,6 @@ static inline bool vertices_equals(CartesianMapping maps[], */ static void test_quadrilateral_flip_diagonal() { CartesianMapping A = m(50, 0), B = m(0, 100), C = m(100, 100), D = m(0, 200), E = m(100, 200); - TriangleMap *t = trianglemap_create(A, B, C); TriangleMap *r = trianglemap_create(C, B, E); TriangleMap *l = trianglemap_create(B, D, E); @@ -54,9 +53,34 @@ static void test_quadrilateral_is_delaunay(CartesianMapping A, CartesianMapping trianglemap_destroy(r); } +static void test_quadrilateral_propagate_delaunay() { + CartesianMapping A = m(0, 0), B = m(0, 10), C = m(10, 10), D = m(10, 0), E = m(4, 6), F = m(8, 7); + TriangleMap *l = trianglemap_create(A, B, C); + TriangleMap *r = trianglemap_create(A, C, D); + trianglemap_set_neighbors(l, NULL, NULL, r, r); + trianglemap_set_neighbors(r, l, NULL, NULL, NULL); + trianglemap_split(l, E); + trianglemap_split(r, F); + + trianglemap_foreach_neighbor(r, quadrilateral_propagate_delaunay); + + { + TriangleMap *triangle; + int neighbor_index; + for (triangle = l; triangle != NULL; triangle = triangle->next) + for (neighbor_index = 0; neighbor_index < 3; ++neighbor_index) + if (triangle->neighbors[neighbor_index] != NULL) + assert(quadrilateral_is_delaunay(triangle, triangle->neighbors[neighbor_index])); + } + + trianglemap_destroy(l); + trianglemap_destroy(r); +} + int main(int argc, char **argv) { test_quadrilateral_flip_diagonal(); test_quadrilateral_is_delaunay(m(0, 0), m(0, 3), m(3, 3), m(2, 1), false); test_quadrilateral_is_delaunay(m(0, 0), m(0, 3), m(3, 3), m(4, -1), true); + test_quadrilateral_propagate_delaunay(); return 0; } -- cgit v1.2.3 From 2a9ca610acdd24ebf1a215f779bf2a5d9f80d9cf Mon Sep 17 00:00:00 2001 From: pacien Date: Sun, 24 Dec 2017 15:35:54 +0100 Subject: Move Delaunay propagation function Signed-off-by: pacien --- include/morpher/quadrilateral.h | 11 ----------- include/morpher/trianglemap.h | 10 ++++++++++ src/morpher/quadrilateral.c | 8 -------- src/morpher/trianglemap.c | 13 +++++++++++++ test/morpher/quadrilateral.c | 25 ------------------------- test/morpher/trianglemap.c | 28 ++++++++++++++++++++++++++-- 6 files changed, 49 insertions(+), 46 deletions(-) diff --git a/include/morpher/quadrilateral.h b/include/morpher/quadrilateral.h index 8691263..c8cf3a1 100644 --- a/include/morpher/quadrilateral.h +++ b/include/morpher/quadrilateral.h @@ -36,15 +36,4 @@ void quadrilateral_flip_diagonal(TriangleMap *t1, TriangleMap *t2); */ bool quadrilateral_is_delaunay(TriangleMap *t1, TriangleMap *t2); -/** - * Function: quadrilateral_propagate_delaunay - * Ensures that the quadrilateral spawned by the given triangles fulfills the Delaunay criterion, - * flipping the diagonal if necessary and propagating the changes to the neighbouring triangles. - * - * Parameters: - * *start - the starting triangle - * *neighbor - a neighboring triangle - */ -void quadrilateral_propagate_delaunay(TriangleMap *start, TriangleMap *neighbor); - #endif diff --git a/include/morpher/trianglemap.h b/include/morpher/trianglemap.h index 0bb6a1d..f690a37 100644 --- a/include/morpher/trianglemap.h +++ b/include/morpher/trianglemap.h @@ -115,6 +115,16 @@ void trianglemap_replace_neighbor(TriangleMap *t, TriangleMap *old, TriangleMap */ void trianglemap_foreach_neighbor(TriangleMap *t, void (*f)(TriangleMap *current, TriangleMap *neighbor)); +/** + * Function: trianglemap_propagate_delaunay + * Ensures that the quadrilateral spawned by the given triangles and its neighbours fulfill the Delaunay criterion, + * flipping the diagonal if necessary and propagating the changes to the neighbouring triangles. + * + * Parameters: + * *t - the starting triangle + */ +void trianglemap_propagate_delaunay(TriangleMap *t); + /** * Function: trianglemap_split * Splits a triangle into three sub-triangles at the supplied center vertex, updating the surrounding triangles. diff --git a/src/morpher/quadrilateral.c b/src/morpher/quadrilateral.c index af77f0f..9c73dfa 100644 --- a/src/morpher/quadrilateral.c +++ b/src/morpher/quadrilateral.c @@ -61,11 +61,3 @@ bool quadrilateral_is_delaunay(TriangleMap *t1, TriangleMap *t2) { return not_in_circumcircle(t1, t2->vertices[(trianglemap_find_common_edge(t2, t1) + 2) % 3].origin) && not_in_circumcircle(t2, t1->vertices[(trianglemap_find_common_edge(t1, t2) + 2) % 3].origin); } - -void quadrilateral_propagate_delaunay(TriangleMap *start, TriangleMap *neighbor) { - assert(start != NULL && neighbor != NULL); - if (!quadrilateral_is_delaunay(start, neighbor)) { - quadrilateral_flip_diagonal(start, neighbor); - trianglemap_foreach_neighbor(neighbor, quadrilateral_propagate_delaunay); - } -} diff --git a/src/morpher/trianglemap.c b/src/morpher/trianglemap.c index 01b66c9..6658901 100644 --- a/src/morpher/trianglemap.c +++ b/src/morpher/trianglemap.c @@ -1,8 +1,17 @@ #include "morpher/trianglemap.h" #include #include +#include "morpher/quadrilateral.h" #include "common/mem.h" +static void propagate_delaunay(TriangleMap *start, TriangleMap *neighbor) { + assert(start != NULL && neighbor != NULL); + if (!quadrilateral_is_delaunay(start, neighbor)) { + quadrilateral_flip_diagonal(start, neighbor); + trianglemap_foreach_neighbor(neighbor, propagate_delaunay); + } +} + TriangleMap *trianglemap_create(CartesianMapping v1, CartesianMapping v2, CartesianMapping v3) { TriangleMap *triangle = malloc_or_die(sizeof(TriangleMap)); triangle->vertices[0] = v1; @@ -51,6 +60,10 @@ void trianglemap_foreach_neighbor(TriangleMap *t, void (*f)(TriangleMap *, Trian for (cursor = 0; cursor < 3; ++cursor) if (t->neighbors[cursor] != NULL) f(t, t->neighbors[cursor]); } +void trianglemap_propagate_delaunay(TriangleMap *t) { + trianglemap_foreach_neighbor(t, propagate_delaunay); +} + TriangleMap *trianglemap_split(TriangleMap *t, CartesianMapping v) { assert(trianglemap_to(t, v.origin) == t); diff --git a/test/morpher/quadrilateral.c b/test/morpher/quadrilateral.c index b278e2f..31b22d6 100644 --- a/test/morpher/quadrilateral.c +++ b/test/morpher/quadrilateral.c @@ -53,34 +53,9 @@ static void test_quadrilateral_is_delaunay(CartesianMapping A, CartesianMapping trianglemap_destroy(r); } -static void test_quadrilateral_propagate_delaunay() { - CartesianMapping A = m(0, 0), B = m(0, 10), C = m(10, 10), D = m(10, 0), E = m(4, 6), F = m(8, 7); - TriangleMap *l = trianglemap_create(A, B, C); - TriangleMap *r = trianglemap_create(A, C, D); - trianglemap_set_neighbors(l, NULL, NULL, r, r); - trianglemap_set_neighbors(r, l, NULL, NULL, NULL); - trianglemap_split(l, E); - trianglemap_split(r, F); - - trianglemap_foreach_neighbor(r, quadrilateral_propagate_delaunay); - - { - TriangleMap *triangle; - int neighbor_index; - for (triangle = l; triangle != NULL; triangle = triangle->next) - for (neighbor_index = 0; neighbor_index < 3; ++neighbor_index) - if (triangle->neighbors[neighbor_index] != NULL) - assert(quadrilateral_is_delaunay(triangle, triangle->neighbors[neighbor_index])); - } - - trianglemap_destroy(l); - trianglemap_destroy(r); -} - int main(int argc, char **argv) { test_quadrilateral_flip_diagonal(); test_quadrilateral_is_delaunay(m(0, 0), m(0, 3), m(3, 3), m(2, 1), false); test_quadrilateral_is_delaunay(m(0, 0), m(0, 3), m(3, 3), m(4, -1), true); - test_quadrilateral_propagate_delaunay(); return 0; } diff --git a/test/morpher/trianglemap.c b/test/morpher/trianglemap.c index 608f3c9..a00f89b 100644 --- a/test/morpher/trianglemap.c +++ b/test/morpher/trianglemap.c @@ -1,8 +1,7 @@ #include "morpher/trianglemap.h" #include #include -#include -#include "common/geom.h" +#include "morpher/quadrilateral.h" static inline bool neighbors_equals(TriangleMap *neighbors[], TriangleMap *n1, TriangleMap *n2, TriangleMap *n3) { @@ -64,8 +63,33 @@ static void test_triangle_split() { free_map(t); } +static void test_trianglemap_propagate_delaunay() { + CartesianMapping A = m(0, 0), B = m(0, 10), C = m(10, 10), D = m(10, 0), E = m(4, 6), F = m(8, 7); + TriangleMap *l = trianglemap_create(A, B, C); + TriangleMap *r = trianglemap_create(A, C, D); + trianglemap_set_neighbors(l, NULL, NULL, r, r); + trianglemap_set_neighbors(r, l, NULL, NULL, NULL); + trianglemap_split(l, E); + trianglemap_split(r, F); + + trianglemap_propagate_delaunay(r); + + { + TriangleMap *triangle; + int neighbor_index; + for (triangle = l; triangle != NULL; triangle = triangle->next) + for (neighbor_index = 0; neighbor_index < 3; ++neighbor_index) + if (triangle->neighbors[neighbor_index] != NULL) + assert(quadrilateral_is_delaunay(triangle, triangle->neighbors[neighbor_index])); + } + + trianglemap_destroy(l); + trianglemap_destroy(r); +} + int main(int argc, char **argv) { test_triangle_to(); test_triangle_split(); + test_trianglemap_propagate_delaunay(); return 0; } -- cgit v1.2.3 From 36d472870a7617d1a7863c81411c0033bbc247ab Mon Sep 17 00:00:00 2001 From: pacien Date: Sun, 24 Dec 2017 18:42:20 +0100 Subject: Refactor trianglemap tests Signed-off-by: pacien --- test/morpher/trianglemap.c | 57 +++++++++++++++++++--------------------------- 1 file changed, 23 insertions(+), 34 deletions(-) diff --git a/test/morpher/trianglemap.c b/test/morpher/trianglemap.c index a00f89b..986e406 100644 --- a/test/morpher/trianglemap.c +++ b/test/morpher/trianglemap.c @@ -13,54 +13,44 @@ static inline bool vertices_equals(CartesianMapping maps[], return mappings_equals(maps[0], m1) && mappings_equals(maps[1], m2) && mappings_equals(maps[2], m3); } -/* - * - - * / \ t - * --- - * l |\| r - * --- - */ -static inline TriangleMap *create_test_map() { - TriangleMap *t = trianglemap_create(m(50, 0), m(0, 100), m(100, 100)); - TriangleMap *r = trianglemap_create(m(100, 100), m(0, 100), m(100, 200)); - TriangleMap *l = trianglemap_create(m(0, 100), m(0, 200), m(100, 200)); +static void test_trianglemap_to() { + CartesianMapping A = m(50, 0), B = m(0, 100), C = m(100, 100), D = m(0, 200), E = m(100, 200); + TriangleMap *t = trianglemap_create(A, B, C); + TriangleMap *r = trianglemap_create(C, B, E); + TriangleMap *l = trianglemap_create(B, D, E); trianglemap_set_neighbors(t, NULL, r, NULL, r); trianglemap_set_neighbors(r, t, l, NULL, l); trianglemap_set_neighbors(l, NULL, NULL, r, NULL); - return t; -} - -static inline void free_map(TriangleMap *t) { - while (t != NULL) t = trianglemap_destroy(t); -} - -static void test_triangle_to() { - TriangleMap *t = create_test_map(); assert(trianglemap_to(t, v(80, 80)) == t); assert(trianglemap_to(t, v(0, 0)) == t->neighbors[0]); assert(trianglemap_to(t, v(55, 170)) == t->neighbors[1]); - free_map(t); + while (t != NULL) t = trianglemap_destroy(t); } -static void test_triangle_split() { - TriangleMap *t = create_test_map(); - TriangleMap *r = t->next; - TriangleMap *l = r->next; - TriangleMap *r1 = trianglemap_split(r, m(90, 110)); +static void test_trianglemap_split() { + CartesianMapping A = m(50, 0), B = m(0, 100), C = m(100, 100), D = m(0, 200), E = m(100, 200), F = m(90, 110); + TriangleMap *t = trianglemap_create(A, B, C); + TriangleMap *r = trianglemap_create(C, B, E); + TriangleMap *l = trianglemap_create(B, D, E); + trianglemap_set_neighbors(t, NULL, r, NULL, r); + trianglemap_set_neighbors(r, t, l, NULL, l); + trianglemap_set_neighbors(l, NULL, NULL, r, NULL); + + TriangleMap *r1 = trianglemap_split(r, F); TriangleMap *r2 = r1->next; TriangleMap *r3 = r2->next; - assert(vertices_equals(r1->vertices, m(100, 100), m(0, 100), m(90, 110))); - assert(vertices_equals(r2->vertices, m(0, 100), m(100, 200), m(90, 110))); - assert(vertices_equals(r3->vertices, m(100, 200), m(100, 100), m(90, 110))); + assert(vertices_equals(r1->vertices, C, B, F)); + assert(vertices_equals(r2->vertices, B, E, F)); + assert(vertices_equals(r3->vertices, E, C, F)); assert(neighbors_equals(r1->neighbors, t, r2, r3)); assert(neighbors_equals(r2->neighbors, l, r3, r1)); assert(neighbors_equals(r3->neighbors, NULL, r1, r2)); assert(t->neighbors[1] == r1 && l->neighbors[2] == r2); - free_map(t); + while (t != NULL) t = trianglemap_destroy(t); } static void test_trianglemap_propagate_delaunay() { @@ -83,13 +73,12 @@ static void test_trianglemap_propagate_delaunay() { assert(quadrilateral_is_delaunay(triangle, triangle->neighbors[neighbor_index])); } - trianglemap_destroy(l); - trianglemap_destroy(r); + while (l != NULL) l = trianglemap_destroy(l); } int main(int argc, char **argv) { - test_triangle_to(); - test_triangle_split(); + test_trianglemap_to(); + test_trianglemap_split(); test_trianglemap_propagate_delaunay(); return 0; } -- cgit v1.2.3 From 45b6f6b2edf2d8b604c8daa1b8019ce4de1e99ea Mon Sep 17 00:00:00 2001 From: pacien Date: Tue, 26 Dec 2017 13:49:10 +0100 Subject: Make triangle boundaries inclusive Signed-off-by: pacien --- src/morpher/trianglemap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/morpher/trianglemap.c b/src/morpher/trianglemap.c index 6658901..e2f3eb9 100644 --- a/src/morpher/trianglemap.c +++ b/src/morpher/trianglemap.c @@ -30,7 +30,7 @@ TriangleMap *trianglemap_to(TriangleMap *t, CartesianVector v) { int edge; for (edge = 0; edge < 3; ++edge) - if (triangle_area(t->vertices[edge].origin, t->vertices[(edge + 1) % 3].origin, v) >= 0) + if (triangle_area(t->vertices[edge].origin, t->vertices[(edge + 1) % 3].origin, v) > 0) return t->neighbors[edge]; return t; -- cgit v1.2.3 From 45d01bc6d74ac1982c974f1b891b0ad96b878f3a Mon Sep 17 00:00:00 2001 From: pacien Date: Tue, 26 Dec 2017 16:30:12 +0100 Subject: Detail doc Signed-off-by: pacien --- include/morpher/trianglemap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/morpher/trianglemap.h b/include/morpher/trianglemap.h index f690a37..3dc5f96 100644 --- a/include/morpher/trianglemap.h +++ b/include/morpher/trianglemap.h @@ -14,7 +14,7 @@ * Represents a triangle mapping. * * Fields: - * vertices[] - array of vertices + * vertices[] - array of vertices mappings, positively ordered * *neighbors[] - array of neighboring triangles ordered by the edges spawned by the vertices * *next - pointer to another triangle for linear browsing, or NULL */ -- cgit v1.2.3 From 2f74af6a1069b9d662676e3d2cbbc671a67574b5 Mon Sep 17 00:00:00 2001 From: pacien Date: Tue, 26 Dec 2017 18:16:55 +0100 Subject: Implement morphing functions and adapt blender Signed-off-by: pacien --- include/blender/blender.h | 2 +- include/morpher/morphing.h | 58 ++++++++++++++++++++++++++++++++++++++++++++++ src/blender/blender.c | 5 ++-- src/morpher/morphing.c | 44 +++++++++++++++++++++++++++++++++++ test/blender/blender.c | 8 +++---- 5 files changed, 109 insertions(+), 8 deletions(-) create mode 100644 include/morpher/morphing.h create mode 100644 src/morpher/morphing.c diff --git a/include/blender/blender.h b/include/blender/blender.h index 8e89208..26ff802 100644 --- a/include/blender/blender.h +++ b/include/blender/blender.h @@ -8,7 +8,7 @@ #include "common/time.h" #include "blender/canvas.h" -#include "morpher/morpher.h" +#include "morpher/morphing.h" /** * Function: blender_blend_canvas diff --git a/include/morpher/morphing.h b/include/morpher/morphing.h new file mode 100644 index 0000000..028cd87 --- /dev/null +++ b/include/morpher/morphing.h @@ -0,0 +1,58 @@ +#ifndef UPEM_MORPHING_MORPHING +#define UPEM_MORPHING_MORPHING + +/** + * File: morphing.h + * Coordinate mapping for morphing transforms. + */ + +#include "common/geom.h" +#include "common/time.h" +#include "morpher/trianglemap.h" + +/** + * Struct: Morphing + * Represents an abstract coordinate transform from a source to a destination coordinate matrix, + * constrained by a given set of points. + * + * Fields: + * dim - dimension in pixels + * *first - the first triangle in the linked list + * *center - the center triangle + */ +typedef struct { + CartesianVector dim; + TriangleMap *first, *center; +} Morphing; + +/** + * Function: morphing_init + * Initialises a morphing. + * + * Parameters: + * width - coordinate matrix width in pixels + * height - coordinate matrix height in pixels + */ +Morphing *morphing_create(IntVector width, IntVector height); + +/** + * Function: morphing_free + * Frees any resources allocated to a morphing. + * + * Parameters: + * *m - pointer to the morphing to destroy + */ +void morphing_destroy(Morphing *m); + +/** + * Function: morphing_add_constraint + * Adds a constraint point to a morphing. + * + * Parameters: + * *m - pointer to the morphing to alter + * origin - constraint point coordinates on the origin matrix + * destination - constraint point coordinates on the target matrix + */ +void morphing_add_constraint(Morphing *m, CartesianVector origin, CartesianVector destination); + +#endif diff --git a/src/blender/blender.c b/src/blender/blender.c index 99abedd..08cafa4 100644 --- a/src/blender/blender.c +++ b/src/blender/blender.c @@ -1,7 +1,6 @@ #include "blender/blender.h" #include #include -#include "morpher/morpher.h" static inline ColorComponent blend_components(ColorComponent origin, ColorComponent target, TimeVector frame) { // https://www.youtube.com/watch?v=LKnqECcg6Gw @@ -21,7 +20,7 @@ void blender_blend_canvas(Canvas *canvas, Canvas *source, Canvas *target, Morphi CartesianMapping mapping; Color pixel; - dim = morpher_get_dim(morphing); + dim = morphing->dim; assert(dim.x > 0 && dim.y > 0); assert(vector_equals(dim, canvas_get_dim(canvas))); @@ -33,7 +32,7 @@ void blender_blend_canvas(Canvas *canvas, Canvas *source, Canvas *target, Morphi point.x = flat_dim % dim.y; point.y = flat_dim / dim.y; - mapping = morpher_get_point_mapping(morphing, point, frame); + mapping = (CartesianMapping) {point, point}; pixel = blend_colors(canvas_get_pixel(source, mapping.origin), canvas_get_pixel(target, mapping.target), frame); canvas_set_pixel(canvas, point, pixel); } diff --git a/src/morpher/morphing.c b/src/morpher/morphing.c new file mode 100644 index 0000000..2ab22d0 --- /dev/null +++ b/src/morpher/morphing.c @@ -0,0 +1,44 @@ +#include "morpher/morphing.h" +#include "common/mem.h" + +static inline TriangleMap *init_trianglemap(IntVector width, IntVector height) { + TriangleMap *bottom_left = trianglemap_create(m(0, 0), m(0, height), m(width, height)); + TriangleMap *top_right = trianglemap_create(m(0, 0), m(width, height), m(width, 0)); + trianglemap_set_neighbors(bottom_left, NULL, NULL, top_right, top_right); + trianglemap_set_neighbors(top_right, bottom_left, NULL, NULL, NULL); + return bottom_left; +} + +static inline TriangleMap *find_triangle(TriangleMap *start, CartesianVector target) { + TriangleMap *t = trianglemap_to(start, target); + return t == start ? t : find_triangle(t, target); +} + +static inline void update_center(Morphing *m) { + m->center = find_triangle(m->center, v(m->dim.x / 2, m->dim.y / 2)); +} + +static inline void ensure_delaunay_neighborhood(TriangleMap *t) { + trianglemap_propagate_delaunay(t); + trianglemap_propagate_delaunay(t->next); + trianglemap_propagate_delaunay(t->next->next); +} + +Morphing *morphing_create(IntVector width, IntVector height) { + Morphing *m = malloc_or_die(sizeof(Morphing)); + m->dim = (CartesianVector) {width, height}; + m->first = init_trianglemap(width, height); + m->center = m->first; + return m; +} + +void morphing_destroy(Morphing *m) { + while (m->first != NULL) m->first = trianglemap_destroy(m->first); +} + +void morphing_add_constraint(Morphing *m, CartesianVector origin, CartesianVector destination) { + TriangleMap *target = find_triangle(m->center, origin); + TriangleMap *split = trianglemap_split(target, (CartesianMapping) {origin, destination}); + ensure_delaunay_neighborhood(split); + update_center(m); +} diff --git a/test/blender/blender.c b/test/blender/blender.c index bf16dc6..f42322f 100644 --- a/test/blender/blender.c +++ b/test/blender/blender.c @@ -2,11 +2,11 @@ #include static void test_canvas_blending() { - Morphing morphing; + Morphing *morphing; Canvas origin, target, result; CartesianVector sample_point = {13, 17}; - morpher_init(&morphing, 64, 64); + morphing = morphing_create(64, 64); canvas_init(&origin, 64, 64); canvas_init(&target, 64, 64); canvas_init(&result, 64, 64); @@ -14,13 +14,13 @@ static void test_canvas_blending() { canvas_set_pixel(&origin, sample_point, (Color) {{0xFF, 0xED, 0x00, 0x00}}); canvas_set_pixel(&target, sample_point, (Color) {{0x00, 0x47, 0xAB, 0x00}}); - blender_blend_canvas(&result, &origin, &target, &morphing, 0.125); + blender_blend_canvas(&result, &origin, &target, morphing, 0.125); assert(color_equals(canvas_get_pixel(&result, sample_point), (Color) {{0xEE, 0xDF, 0x3C, 0x00}})); canvas_free(&result); canvas_free(&target); canvas_free(&origin); - morpher_free(&morphing); + morphing_destroy(morphing); } int main(int argc, char **argv) { -- cgit v1.2.3 From facf2d3b8fcce3407b456330221de9840d308447 Mon Sep 17 00:00:00 2001 From: pacien Date: Tue, 26 Dec 2017 18:48:42 +0100 Subject: Add missing free Signed-off-by: pacien --- src/morpher/morphing.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/morpher/morphing.c b/src/morpher/morphing.c index 2ab22d0..f6e9387 100644 --- a/src/morpher/morphing.c +++ b/src/morpher/morphing.c @@ -1,4 +1,5 @@ #include "morpher/morphing.h" +#include #include "common/mem.h" static inline TriangleMap *init_trianglemap(IntVector width, IntVector height) { @@ -34,6 +35,7 @@ Morphing *morphing_create(IntVector width, IntVector height) { void morphing_destroy(Morphing *m) { while (m->first != NULL) m->first = trianglemap_destroy(m->first); + free(m); } void morphing_add_constraint(Morphing *m, CartesianVector origin, CartesianVector destination) { -- cgit v1.2.3 From f5ff85f3c7e7d6bf11a423c497d2b3ce76cfafd8 Mon Sep 17 00:00:00 2001 From: pacien Date: Wed, 27 Dec 2017 01:11:18 +0100 Subject: Fix trianglemap initialisation pixel index Signed-off-by: pacien --- src/morpher/morphing.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/morpher/morphing.c b/src/morpher/morphing.c index f6e9387..7df3839 100644 --- a/src/morpher/morphing.c +++ b/src/morpher/morphing.c @@ -28,7 +28,7 @@ static inline void ensure_delaunay_neighborhood(TriangleMap *t) { Morphing *morphing_create(IntVector width, IntVector height) { Morphing *m = malloc_or_die(sizeof(Morphing)); m->dim = (CartesianVector) {width, height}; - m->first = init_trianglemap(width, height); + m->first = init_trianglemap(width - 1, height - 1); m->center = m->first; return m; } -- cgit v1.2.3