polygon

Polygon triangulation
git clone git://git.meso-star.fr/polygon.git
Log | Files | Refs | README | LICENSE

commit ef5c91a352e199f4f54ea8fbf5c8330a5f4df7a7
parent d96b3066b4662949c00d23152352ce3b232f6a10
Author: vaplv <vaplv@free.fr>
Date:   Mon, 22 Sep 2014 16:27:12 +0200

Test and fix the polygon triangulation

Diffstat:
Msrc/polygon.c | 40++++++++++++++++++++++------------------
Msrc/test_polygon.c | 135++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
2 files changed, 150 insertions(+), 25 deletions(-)

diff --git a/src/polygon.c b/src/polygon.c @@ -140,10 +140,13 @@ node_is_an_ear float T[3], u; const uint32_t inode_concave = *htable_u32_iterator_key_get(&it); htable_u32_iterator_next(&it); + if(inode_concave == nodes[inode].next + || inode_concave == nodes[inode].prev) + continue; /* Project the concave vertex position onto the {prev, curr, next} triangle * plane and test if it lies into it */ f3_sub(T, nodes[inode_concave].pos, nodes[inode].pos); - u = f3_dot(T, E1) / det; + u = f3_dot(T, P) / det; if(u >= 0.f && u <= 1.f) { float Q[3], v; f3_cross(Q, T, E1); @@ -336,10 +339,8 @@ polygon_triangulate htable_u32_clear(&poly->vertices_concave); darray_u32_clear(&poly->triangle_ids); - if(poly->nvertices < 3) { /* Point or line */ - res = POLYGON_BAD_ARGUMENT; - goto error; - } + if(poly->nvertices < 3) /* Point or line */ + goto exit; if(poly->nvertices == 3) { /* Already a triangle */ FOR_EACH(ivert, 0, 3) { @@ -354,7 +355,7 @@ polygon_triangulate nodes = darray_vertex_node_data_get(&poly->pool); inode = poly->vertices; - FOR_EACH(ivert, 0, poly->nvertices) { /* Find the list of concave vertices */ + FOR_EACH(inode, 0, poly->nvertices) { /* Find the list of concave vertices */ node_normal_compute(poly, inode, normal); if(f3_dot(normal_convex, normal) <= 0.f) { const char dummy = 1; @@ -363,10 +364,11 @@ polygon_triangulate } } - /* "The Graham Scan Triangulates Simple Polygons */ + /* Implementation of the ear cutting algorithm "The Graham Scan Triangulates + * Simple Polygons */ inode = nodes[nodes[poly->vertices].next].next; while(inode != poly->vertices) { - if(!node_is_an_ear(poly, &poly->vertices_concave, inode)) { + if(!node_is_an_ear(poly, &poly->vertices_concave, nodes[inode].prev)) { inode = nodes[inode].next; } else { /* `Prev' is an ear */ const uint32_t iv0 = nodes[nodes[inode].prev].prev; @@ -401,16 +403,18 @@ polygon_triangulate } exit: - if(indices && nindices) { - *nindices = (uint32_t)darray_u32_size_get(&poly->triangle_ids); - *indices = *nindices ? darray_u32_cdata_get(&poly->triangle_ids) : NULL; - } - if(poly && poly->nvertices) { /* Restore the linked list */ - poly->vertices = 0; - nodes = darray_vertex_node_data_get(&poly->pool); - FOR_EACH(inode, 1, poly->nvertices) { - nodes[inode].prev = inode == 0 ? poly->nvertices - 1 : inode - 1; - nodes[inode].next = inode == poly->nvertices - 1 ? 0 : inode + 1; + if(poly) { + if(indices && nindices && poly) { + *nindices = (uint32_t)darray_u32_size_get(&poly->triangle_ids); + *indices = *nindices ? darray_u32_cdata_get(&poly->triangle_ids) : NULL; + } + if(poly->nvertices) { /* Restore the linked list */ + poly->vertices = 0; + nodes = darray_vertex_node_data_get(&poly->pool); + FOR_EACH(inode, 0, poly->nvertices) { + nodes[inode].prev = inode == 0 ? poly->nvertices - 1 : inode - 1; + nodes[inode].next = inode == poly->nvertices - 1 ? 0 : inode + 1; + } } } return res; diff --git a/src/test_polygon.c b/src/test_polygon.c @@ -14,32 +14,98 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "polygon.h" + #include <rsys/float3.h> #include <rsys/mem_allocator.h> +#include <string.h> + #define BARG POLYGON_BAD_ARGUMENT #define OK POLYGON_OK +static float /* Return the area of the triangulated polygon */ +check_triangulation + (struct polygon* poly, + const uint32_t* indices, + const uint32_t nindices, + const uint32_t nedges_contour) +{ + float area = 0.f; + uint32_t i; + /* List the counter edges retrieved after the polygon triangulation. This + * array must be indexed by the smallest index of the edge */ + char* contour; + ASSERT(poly && indices && nindices && nedges_contour); + + contour = mem_calloc(nedges_contour, sizeof(char)); + NCHECK(contour, NULL); + + memset(contour, 0, sizeof(contour)); + CHECK(nindices % 3, 0); + FOR_EACH(i, 0, nindices/3) { + const uint32_t* tri = indices + (i*3); + float e0[3], e1[3], N[3]; + float v0[3], v1[3], v2[3]; + float len; + CHECK(polygon_vertex_get(poly, tri[0], v0), OK); + CHECK(polygon_vertex_get(poly, tri[1], v1), OK); + CHECK(polygon_vertex_get(poly, tri[2], v2), OK); + /* Compute the triangle area and add it to the overall polygon area */ + f3_sub(e0, v1, v0); + f3_sub(e1, v2, v0); + len = f3_normalize(N, f3_cross(N, e0, e1)); + CHECK(eq_eps(len, 0.f, 1.e-6f), 0); + area += len * 0.5f; + /* Update the contour edge flag */ + if(tri[1] == (tri[0] + 1) % nedges_contour) { + CHECK(contour[tri[0]], 0); + contour[tri[0]] = 1; + } + if(tri[2] == (tri[1] + 1) % nedges_contour) { + CHECK(contour[tri[1]], 0); + contour[tri[1]] = 1; + } + if(tri[0] == (tri[2] + 1) % nedges_contour) { + CHECK(contour[tri[2]], 0); + contour[tri[2]] = 1; + } + } + FOR_EACH(i, 0, nedges_contour) /* All the contour edges may be found */ + CHECK(contour[i], 1); + + mem_free(contour); + return area; +} + int main(int argc, char** argv) { + /* Input polygon contour : + * 1-------------2 5---6 + * | | | | + * | 10----9 | | | + * | | | 3-----4 | + * | | | | + * 0---11 8-------------7 */ const float vertices[] = { 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, - 1.25f, 1.f, 0.f, - 1.25f, 0.25f, 0.f, - 1.75f, 0.25f, 0.f, + 1.f, 1.f, 0.f, + 1.f, 0.25f, 0.f, + 1.5f, 0.25f, 0.f, + 1.5f, 1.f, 0.f, 1.75f, 1.f, 0.f, - 2.f, 1.f, 0.f, - 2.f, 0.f, 0.f, - 1.f, 0.f, 0.f, - 1.f, 0.75f, 0.f, + 1.75f, 0.f, 0.f, + 0.75f, 0.f, 0.f, + 0.75f, 0.75f, 0.f, 0.25f, 0.75f, 0.f, 0.25f, 0.f, 0.f }; struct mem_allocator allocator_proxy; struct polygon* poly; + const uint32_t* indices; float pos[3]; + uint32_t nindices; uint32_t ivertex, nvertices; (void)argc, (void)argv; @@ -118,6 +184,60 @@ main(int argc, char** argv) CHECK(f3_eq_eps(pos, vertices + ivertex*3, 1.e-6f), 1); } + CHECK(polygon_triangulate(NULL, NULL, NULL), BARG); + CHECK(polygon_triangulate(poly, NULL, NULL), BARG); + CHECK(polygon_triangulate(NULL, &indices, NULL), BARG); + CHECK(polygon_triangulate(poly, &indices, NULL), BARG); + CHECK(polygon_triangulate(NULL, NULL, &nindices), BARG); + CHECK(polygon_triangulate(poly, NULL, &nindices), BARG); + CHECK(polygon_triangulate(NULL, &indices, &nindices), BARG); + + /* Check full triangulation */ + CHECK(polygon_triangulate(poly, &indices, &nindices), OK); + CHECK(nindices, 30); + CHECK(eq_eps(check_triangulation + (poly, indices, nindices, 12), 1.f, 1.e-6f), 1); + + /* After the triangulation the input polygon may be unchanged */ + CHECK(polygon_vertices_count_get(poly, &nvertices), OK); + CHECK(nvertices, sizeof(vertices)/sizeof(float[3])); + FOR_EACH(ivertex, 0, sizeof(vertices)/sizeof(float[3])) { + CHECK(polygon_vertex_get(poly, ivertex, pos), OK); + CHECK(f3_eq_eps(pos, vertices + ivertex*3, 1.e-6f), 1); + } + + /* Check that the input polygon can be retriangulated */ + CHECK(polygon_triangulate(poly, &indices, &nindices), OK); + CHECK(nindices, 30); + CHECK(eq_eps(check_triangulation + (poly, indices, nindices, 12), 1.f, 1.e-6f), 1); + + /* One can triangulate empty polygon */ + CHECK(polygon_clear(poly), OK); + CHECK(polygon_triangulate(poly, &indices, &nindices), OK); + CHECK(nindices, 0); + + /* Check the triangulation of an updated polygon */ + CHECK(polygon_vertex_add(poly, vertices + 0), OK); + CHECK(polygon_vertex_add(poly, vertices + 3), OK); + CHECK(polygon_triangulate(poly, &indices, &nindices), OK); + CHECK(nindices, 0); + CHECK(polygon_vertex_add(poly, vertices + 6), OK); + CHECK(polygon_triangulate(poly, &indices, &nindices), OK); + CHECK(nindices, 3); + CHECK(eq_eps(check_triangulation + (poly, indices, nindices, 3), 0.5f, 1.e-6f), 1); + CHECK(polygon_vertex_add(poly, vertices + 9), OK); + CHECK(polygon_vertex_add(poly, vertices + 12), OK); + CHECK(polygon_vertex_add(poly, vertices + 15), OK); + CHECK(polygon_vertex_add(poly, vertices + 18), OK); + CHECK(polygon_vertex_add(poly, vertices + 21), OK); + CHECK(polygon_vertex_add(poly, vertices + 21), OK); + CHECK(polygon_triangulate(poly, &indices, &nindices), OK); + CHECK(nindices, 18); + CHECK(eq_eps(check_triangulation + (poly, indices, nindices, 8), 1.375f, 1.e-6f), 1); + CHECK(polygon_ref_put(poly), OK); if(MEM_ALLOCATED_SIZE(&allocator_proxy)) { char dump[512]; @@ -129,3 +249,4 @@ main(int argc, char** argv) CHECK(mem_allocated_size(), 0); return 0; } +