star-cpr

Clip 2D meshes with 2D polygons
git clone git://git.meso-star.fr/star-cpr.git
Log | Files | Refs | README | LICENSE

commit 80506a9f82f64073dbe9a1ed72e77ed205f6d709
parent 818e40e24d75578d1aeb2153aa5e804bb9018836
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Tue, 23 Aug 2016 16:43:36 +0200

Simplify the setup of the Weiler & Atherton algorithm

Do not pre-compute the polygon/mesh edge intersections.

Diffstat:
Msrc/cpr_mesh.c | 454+++++++++++++++++++++++++++++++++----------------------------------------------
1 file changed, 192 insertions(+), 262 deletions(-)

diff --git a/src/cpr_mesh.c b/src/cpr_mesh.c @@ -51,7 +51,7 @@ struct node { #define DARRAY_DATA struct node #include <rsys/dynamic_array.h> -struct polygon { +struct poly { struct darray_double coords; }; @@ -70,24 +70,70 @@ struct cpr_mesh { }; /******************************************************************************* - * Helper function + * Helper functions ******************************************************************************/ +static int +intersect_edges + (double edge0[2][2], + double edge1[2][2], + struct hit* hit) +{ + double org0[2], org1[2]; + double dir0[2], dir1[2]; + double tmp[2]; + double a, b; + int i; + + /* Express the edges as org<0|1> + t<0|1>*dir<0|1> */ + d2_set(org0, edge0[0]); + d2_set(org1, edge1[0]); + d2_sub(dir0, edge0[1], edge0[0]); + d2_sub(dir1, edge1[1], edge1[0]); + + /* Solve org0 + t0*dir0 = org1 + t1*dir1 + * (org0 + t0*dir0) x dir1 = (org1 + t1*dir1) x dir1 + * org0 x dir1 + t0*(dir0 x dir1) = org1 x dir1 + * t0 = (org1 - org0) x dir1 / (dir0 x dir1) */ + d2_sub(tmp, org1, org0); + b = d2_cross(dir0, dir1); + if(b == 0) return 0; /* Edges are colinear => i.e. no intersection */ + a = d2_cross(tmp, dir1); + hit->t0 = a / b; + + /* Compute the hit position */ + d2_muld(hit->pos, dir0, hit->t0); + d2_add(hit->pos, org0, hit->pos); + + /* Compute the paremtric hit coordinate on the edge1 */ + i = dir1[0] != 0 ? 0 : 1; + hit->t1 = (hit->pos[i] - org1[i]) / dir1[i]; + +#ifndef NDEBUG + d2_muld(tmp, dir1, hit->t1); + d2_add(tmp, org1, tmp); + ASSERT(d2_eq_eps(hit->pos, tmp, 1.e-6)); +#endif + + /* Ensure that the hit lie in the edge boundaries */ + return hit->t0 >= 0.0 && hit->t0 <= 1.0 && hit->t1 >= 0.0 && hit->t1 <= 1.0; +} + static INLINE void -polygon_init(struct mem_allocator* allocator, struct polygon* poly) +poly_init(struct mem_allocator* allocator, struct poly* poly) { ASSERT(allocator && poly); darray_double_init(allocator, &poly->coords); } static INLINE void -polygon_release(struct polygon* poly) +poly_release(struct poly* poly) { ASSERT(poly); darray_double_release(&poly->coords); } static res_T -polygon_setup(struct polygon* poly, const struct cpr_polygon* desc) +poly_setup(struct poly* poly, const struct cpr_polygon* desc) { size_t ivert; res_T res = RES_OK; @@ -114,7 +160,7 @@ error: } static res_T -polygon_setup_nodes(struct polygon* poly, struct darray_node* nodes) +poly_setup_nodes(struct poly* poly, struct darray_node* nodes) { size_t nverts; size_t ivert; @@ -125,7 +171,7 @@ polygon_setup_nodes(struct polygon* poly, struct darray_node* nodes) nverts = darray_double_size_get(&poly->coords)/2/*#coords*/; res = darray_node_resize(nodes, nverts); - if(res != RES_OK) goto error; + if(res != RES_OK) return res; FOR_EACH(ivert, 0, nverts) { struct node* node = darray_node_data_get(nodes) + ivert; @@ -136,224 +182,122 @@ polygon_setup_nodes(struct polygon* poly, struct darray_node* nodes) node->t = -1.0; } -exit: - return res; -error: - darray_node_clear(nodes); - goto exit; + return RES_OK; } static res_T -mesh_setup_edge_nodes - (struct cpr_mesh* mesh, - struct darray_node* nodes, - struct htable_edge* edges) +triangle_setup_nodes(const size_t triangle[3], struct darray_node* nodes) { - size_t itri, ntris; + size_t ivert; res_T res = RES_OK; - ASSERT(mesh && nodes); + ASSERT(triangle && nodes); darray_node_clear(nodes); - htable_edge_clear(edges); - - CPR(mesh_get_triangles_count(mesh, &ntris)); - - FOR_EACH(itri, 0, ntris) { - struct edge edge; - size_t iedge; - size_t ids[3]; - - CPR(mesh_get_indices(mesh, itri, ids)); - FOR_EACH(iedge, 0, 3) { - struct node* node; - size_t inode; - edge.v0 = ids[iedge]; - edge.v1 = ids[(iedge + 1)%3]; - if(edge.v0 > edge.v1) SWAP(size_t, edge.v0, edge.v1); + res = darray_node_resize(nodes, 3); + if(res != RES_OK) return res; - if(htable_edge_find(edges, &edge)) continue; - - inode = darray_node_size_get(nodes); - - res = htable_edge_set(edges, &edge, &inode); - if(res != RES_OK) goto error; - res = darray_node_resize(nodes, inode + 2); - if(res != RES_OK) goto error; - - node = darray_node_data_get(nodes) + inode; - node[0].next = inode + 1; - node[0].prev = SIZE_MAX; - node[0].link = SIZE_MAX; - node[0].id = edge.v0; - node[0].t = -1.0; - - node[1].next = SIZE_MAX; - node[1].prev = inode; - node[1].link = SIZE_MAX; - node[1].id = edge.v1; - node[1].t = -1.0; - } + FOR_EACH(ivert, 0, 3) { + struct node* node = darray_node_data_get(nodes) + ivert; + node->next = ivert + 1; + node->prev = ivert - 1; + node->link = SIZE_MAX; + node->id = triangle[ivert]; + node->t = -1.0; } - -exit: - return res; -error: - darray_node_clear(nodes); - goto exit; -} - -static int -intersect_edges - (double edge0[2][2], - double edge1[2][2], - struct hit* hit) -{ - double org0[2], org1[2]; - double dir0[2], dir1[2]; - double tmp[2]; - double a, b; - int i; - - /* Express the edges as org<0|1> + t<0|1>*dir<0|1> */ - d2_set(org0, edge0[0]); - d2_set(org1, edge1[0]); - d2_sub(dir0, edge0[1], edge0[0]); - d2_sub(dir1, edge1[1], edge1[0]); - - /* Solve org0 + t0*dir0 = org1 + t1*dir1 - * (org0 + t0*dir0) x dir1 = (org1 + t1*dir1) x dir1 - * org0 x dir1 + t0*(dir0 x dir1) = org1 x dir1 - * t0 = (org1 - org0) x dir1 / (dir0 x dir1) */ - d2_sub(tmp, org1, org0); - b = d2_cross(dir0, dir1); - if(b == 0) return 0; /* Edges are colinear => i.e. no intersection */ - a = d2_cross(tmp, dir1); - hit->t0 = a / b; - - /* Compute the hit position */ - d2_muld(hit->pos, dir0, hit->t0); - d2_add(hit->pos, org0, hit->pos); - - /* Compute the paremtric hit coordinate on the edge1 */ - i = dir1[0] != 0 ? 0 : 1; - hit->t1 = (hit->pos[i] - org1[i]) / dir1[i]; - -#ifndef NDEBUG - d2_muld(tmp, dir1, hit->t1); - d2_add(tmp, org1, tmp); - ASSERT(d2_eq_eps(hit->pos, tmp, 1.e-6)); -#endif - - /* Ensure that the hit lie in the edge boundaries */ - return hit->t0 >= 0.0 && hit->t0 <= 1.0 && hit->t1 >= 0.0 && hit->t1 <= 1.0; + darray_node_data_get(nodes)[0].prev = 2; + darray_node_data_get(nodes)[2].next = 0; + return RES_OK; } static res_T -compute_hit_nodes - (struct darray_node* mesh_nodes, - struct darray_double* mesh_coords, - struct darray_node* poly_nodes, - struct darray_double* poly_coords) +setup_intersection_nodes + (struct darray_node* nodes0, + struct darray_double* coords0, + struct darray_node* nodes1, + struct darray_double* coords1) { - size_t nmesh_edges, npoly_edges; - size_t imesh_edge, ipoly_edge; + size_t inode0, nnodes0; + size_t inode1, nnodes1; res_T res = RES_OK; - ASSERT(mesh_nodes && mesh_coords && poly_nodes && poly_coords); + ASSERT(nodes0 && coords0 && nodes1 && coords1); - nmesh_edges = darray_node_size_get(mesh_nodes)/2; - npoly_edges = darray_node_size_get(poly_nodes); + nnodes0 = darray_node_size_get(nodes0); + nnodes1 = darray_node_size_get(nodes1); - FOR_EACH(imesh_edge, 0, nmesh_edges) { - struct node* mesh_node = darray_node_data_get(mesh_nodes) + imesh_edge; - double mesh_edge[2][2]; + #define POS(Array) darray_double_data_get(Array) + #define NODES(Array) darray_node_data_get(Array) + FOR_EACH(inode0, 0, nnodes0) { + double edge0[2][2]; - /* Fetch the coordinates of the edge0 vertices */ - d2_set(mesh_edge[0], darray_double_data_get(mesh_coords) + mesh_node[0].id*2); - d2_set(mesh_edge[1], darray_double_data_get(mesh_coords) + mesh_node[1].id*2); + /* Fetch the coordinates of the edge0 */ + d2_set(edge0[0], POS(coords0) + NODES(nodes0)[inode0].id*2); + d2_set(edge0[1], POS(coords0) + NODES(nodes0)[(inode0+1)%nnodes0].id*2); - FOR_EACH(ipoly_edge, 0, npoly_edges) { - const size_t ipoly_node0 = ipoly_edge; - const size_t ipoly_node1 = (ipoly_edge + 1) % npoly_edges; - struct node* poly_node0; - struct node* poly_node1; + FOR_EACH(inode1, 0, nnodes1) { struct node* node; - struct node* mesh_hit_node; - struct node* poly_hit_node; struct hit hit; - size_t inode; - size_t imesh_hit_node, ipoly_hit_node; - size_t imesh_hit_pos, ipoly_hit_pos; - double poly_edge[2][2]; - - poly_node0 = darray_node_data_get(poly_nodes) + ipoly_node0; - poly_node1 = darray_node_data_get(poly_nodes) + ipoly_node1; + double edge1[2][2]; + size_t ihit_node0, ihit_node1; + size_t ihit_pos; - /* Fetch the coordinates of the edge1 vertices */ - d2_set(poly_edge[0], darray_double_data_get(poly_coords) + poly_node0->id*2); - d2_set(poly_edge[1], darray_double_data_get(poly_coords) + poly_node1->id*2); + /* Fetch the coordinates of the edge1 */ + d2_set(edge1[0], POS(coords1) + NODES(nodes1)[inode1].id*2); + d2_set(edge1[1], POS(coords1) + NODES(nodes1)[(inode1+1)%nnodes1].id*2); - /* Find the intersection between the 2 edges */ - if(!intersect_edges(mesh_edge, poly_edge, &hit)) - continue; + /* Find the edge intersection */ + if(!intersect_edges(edge0, edge1, &hit)) continue; - /* Allocate the hit node in the mesh node list */ - imesh_hit_node = darray_node_size_get(mesh_nodes); - res = darray_node_resize(mesh_nodes, imesh_hit_node + 1); + /* Allocate the hit node into the node list 0 */ + ihit_node0 = darray_node_size_get(nodes0); + res = darray_node_resize(nodes0, ihit_node0 + 1); if(res != RES_OK) goto error; - mesh_hit_node = darray_node_data_get(mesh_nodes) + imesh_hit_node; - /* Allocate the hit node in the polygon node list */ - ipoly_hit_node = darray_node_size_get(poly_nodes); - res = darray_node_resize(poly_nodes, ipoly_hit_node + 1); + /* Allocate the hit node into the node list 1 */ + ihit_node1 = darray_node_size_get(nodes1); + res = darray_node_resize(nodes1, ihit_node1 + 1); if(res != RES_OK) goto error; - poly_hit_node = darray_node_data_get(poly_nodes) + ipoly_hit_node; - /* Register the hit position in the mesh vertex buffer */ - imesh_hit_pos = darray_double_size_get(mesh_coords); - res = darray_double_resize(mesh_coords, imesh_hit_pos + 2); + /* Register the hit position into the coords0 */ + ihit_pos = darray_double_size_get(coords0); + res = darray_double_resize(coords0, ihit_pos + 2); if(res != RES_OK) goto error; - d2_set(darray_double_data_get(mesh_coords) + imesh_hit_pos, hit.pos); - imesh_hit_pos /= 2/*#coords per vertex*/; + d2_set(POS(coords0) + ihit_pos, hit.pos); + ihit_pos /= 2/* #coords per vertex */; - /* Register the hit position in the vertex buffer 1 */ - ipoly_hit_pos = darray_double_size_get(poly_coords); - res = darray_double_resize(poly_coords, ipoly_hit_pos + 2); - if(res != RES_OK) goto error; - d2_set(darray_double_data_get(poly_coords) + ipoly_hit_pos, hit.pos); - ipoly_hit_pos /= 2/*#coords per vertex*/; - - /* Insert the hit node in the mesh node list */ - mesh_node = darray_node_data_get(mesh_nodes) + imesh_edge*2; - node = darray_node_data_get(mesh_nodes) + mesh_node[0].next; - while(node->link != SIZE_MAX && node->t <= hit.t0) { - node = darray_node_data_get(mesh_nodes) + node->next; + /* + * Note that the hit position is not registered in the coords1 array + */ + + /* Insert the hit node into the node list 0 */ + node = NODES(nodes0) + NODES(nodes0)[inode0].next; + while(node->link != SIZE_MAX && node->t < hit.t0) { + node = NODES(nodes0) + node->next; } - inode = (size_t)(node - darray_node_data_get(mesh_nodes)); - mesh_hit_node->next = inode; - mesh_hit_node->prev = node->prev; - mesh_hit_node->link = ipoly_hit_node; /* Hit id in the poly node list */ - mesh_hit_node->id = imesh_hit_pos; - mesh_hit_node->t = hit.t0; - darray_node_data_get(mesh_nodes)[node->prev].next = imesh_hit_node; - node->prev = imesh_hit_node; - - /* Insert the hit node in the poly node list 1 */ - poly_node0 = darray_node_data_get(poly_nodes) + ipoly_node0; - node = darray_node_data_get(poly_nodes) + poly_node0->next; - while(node->link != SIZE_MAX && node->t <= hit.t1) { - node = darray_node_data_get(poly_nodes) + node->next; + NODES(nodes0)[ihit_node0].next = (size_t)(node - NODES(nodes0)); + NODES(nodes0)[ihit_node0].prev = node->prev; + NODES(nodes0)[ihit_node0].link = ihit_node1; + NODES(nodes0)[ihit_node0].id = ihit_pos; + NODES(nodes0)[ihit_node0].t = hit.t0; + NODES(nodes0)[node->prev].next = ihit_node0; + node->prev = ihit_node0; + + /* Inser the hit node into the node list 1 */ + node = NODES(nodes1) + NODES(nodes1)[inode1].next; + while(node->link != SIZE_MAX && node->t < hit.t1) { + node = NODES(nodes1) + node->next; } - inode = (size_t)(node - darray_node_data_get(poly_nodes)); - poly_hit_node->next = inode; - poly_hit_node->prev = node->prev; - poly_hit_node->link = imesh_hit_node; /* Hit id in the mesh node list */ - poly_hit_node->id = ipoly_hit_pos; - poly_hit_node->t = hit.t1; - darray_node_data_get(poly_nodes)[node->prev].next = ipoly_hit_node; - node->prev = ipoly_hit_node; + NODES(nodes1)[ihit_node1].next = (size_t)(node - NODES(nodes1)); + NODES(nodes1)[ihit_node1].prev = node->prev; + NODES(nodes1)[ihit_node1].link = ihit_node0; + NODES(nodes1)[ihit_node1].id = ihit_pos; + NODES(nodes1)[ihit_node1].t = hit.t1; + NODES(nodes1)[node->prev].next = ihit_node1; + node->prev = ihit_node1; } } + #undef POS + #undef NODES exit: return res; @@ -362,7 +306,7 @@ error: } static void -vertices_setup_inside_polygon_flag +vertices_setup_inside_poly_flag (const struct darray_double* coords, const struct darray_node* poly_nodes, const struct darray_double* poly_coords, @@ -393,69 +337,51 @@ vertices_setup_inside_polygon_flag } static res_T -mesh_setup_candidate_nodes - (const struct cpr_mesh* mesh, - const size_t itri, - struct htable_edge* edges, - const struct darray_node* mesh_nodes, - struct darray_node* poly_nodes, - struct darray_node* cand_nodes) +clip + (const struct darray_node* cand_nodes, + const struct darray_node* clip_nodes, + const char* is_inside, + struct darray_size_t* output) { - const struct node* msh_nodes; - size_t ids[3]; - size_t iedge; - size_t icand_node_last; + enum { CAND, CLIP }; + size_t icand_node, ncand_nodes; + const struct node* node_lists[2]; res_T res = RES_OK; + ASSERT(cand_nodes && clip_nodes && is_inside && output); - ASSERT(mesh_nodes && cand_nodes); - ASSERT(itri < darray_node_size_get(mesh_nodes) / - (3/*#edge per triangle*/*2/*#node per edge*/)); - - darray_node_clear(cand_nodes); - CPR(mesh_get_indices(mesh, itri, ids)); + ncand_nodes = darray_node_size_get(cand_nodes); + node_lists[CAND] = darray_node_cdata_get(cand_nodes); + node_lists[CLIP] = darray_node_cdata_get(clip_nodes); - msh_nodes = darray_node_cdata_get(mesh_nodes); + FOR_EACH(icand_node, 0, ncand_nodes) { + size_t ilist = CAND; + const struct node* n0 = node_lists[CAND] + icand_node; + const struct node* n1 = node_lists[CAND] + node_lists[CAND][icand_node].next; + const struct node* node_start; + const struct node* node; - FOR_EACH(iedge, 0, 3) { - struct edge edge; - size_t imesh_node; - int flip_edge; + if(n0->link != SIZE_MAX || n1->link == SIZE_MAX || is_inside[n1->id]) + continue; - edge.v0 = ids[iedge], edge.v1 = ids[(iedge+1)%3]; - if((flip_edge = edge.v0 > edge.v1)) SWAP(size_t, edge.v0, edge.v1); - - imesh_node = *htable_edge_find(edges, &edge) + (size_t)flip_edge; + darray_size_t_clear(output); + node_start = node = n1; do { - struct node* cand_node; - const size_t icand_node = darray_node_size_get(cand_nodes); - - res = darray_node_resize(cand_nodes, icand_node + 1); - if(res != RES_OK) goto error; - cand_node = darray_node_data_get(cand_nodes) + icand_node; - - cand_node->prev = icand_node - 1; - cand_node->next = icand_node + 1; - cand_node->link = msh_nodes[imesh_node].link; - cand_node->id = msh_nodes[imesh_node].id; - cand_node->t = msh_nodes[imesh_node].t; - if(cand_node->link) { - darray_node_data_get(poly_nodes)[cand_node->link].link = - icand_node; - } - } while(flip_edge - ? msh_nodes[msh_nodes[imesh_node].prev].prev != SIZE_MAX - : msh_nodes[msh_nodes[imesh_node].next].next != SIZE_MAX); + res = darray_size_t_push_back(output, &node->id); + if(res != RES_OK) goto error; - icand_node_last = darray_node_size_get(cand_nodes) - 1; - darray_node_data_get(cand_nodes)[0].prev = icand_node_last; - darray_node_data_get(cand_nodes)[icand_node_last].next = 0; + node = node_lists[ilist] + node->next; + if(node->link != SIZE_MAX) { + ilist = !ilist; + node = node_lists[ilist] + node->link; + } + } while(node != node_start); } exit: return res; error: - darray_node_clear(cand_nodes); + darray_size_t_clear(output); goto exit; } @@ -624,56 +550,60 @@ cpr_mesh_get_position res_T cpr_mesh_clip(struct cpr_mesh* mesh, struct cpr_polygon* polygon) { - struct darray_node mesh_nodes; struct darray_node poly_nodes; struct darray_node cand_nodes; /* Candidate */ + struct darray_node clip_nodes; struct darray_char is_inside; - struct htable_edge edges; - struct polygon poly; - size_t itri, ntris; + struct poly poly; + size_t itri, ntris, nverts; res_T res = RES_OK; - darray_node_init(mesh->allocator, &mesh_nodes); darray_node_init(mesh->allocator, &poly_nodes); darray_node_init(mesh->allocator, &cand_nodes); + darray_node_init(mesh->allocator, &clip_nodes); darray_char_init(mesh->allocator, &is_inside); - htable_edge_init(mesh->allocator, &edges); - polygon_init(mesh->allocator, &poly); + poly_init(mesh->allocator, &poly); if(!mesh || !polygon) { res = RES_BAD_ARG; goto error; } - res = polygon_setup(&poly, polygon); + /* Setup the polygon */ + res = poly_setup(&poly, polygon); if(res != RES_OK) goto error; - res = polygon_setup_nodes(&poly, &poly_nodes); + res = poly_setup_nodes(&poly, &poly_nodes); if(res != RES_OK) goto error; - vertices_setup_inside_polygon_flag(&mesh->coords, &poly_nodes, - &poly.coords, darray_char_data_get(&is_inside)); - res = mesh_setup_edge_nodes(mesh, &mesh_nodes, &edges); - if(res != RES_OK) goto error; - res = compute_hit_nodes - (&mesh_nodes, &mesh->coords, &poly_nodes, &poly.coords); + /* Define which mesh vertices are inside the polygon */ + CPR(mesh_get_vertices_count(mesh, &nverts)); + res = darray_char_resize(&is_inside, nverts); if(res != RES_OK) goto error; + vertices_setup_inside_poly_flag(&mesh->coords, &poly_nodes, + &poly.coords, darray_char_data_get(&is_inside)); CPR(mesh_get_triangles_count(mesh, &ntris)); FOR_EACH(itri, 0, ntris) { - res = mesh_setup_candidate_nodes - (mesh, itri, &edges, &mesh_nodes, &poly_nodes, &cand_nodes); + size_t ids[3]; + + CPR(mesh_get_indices(mesh, itri, ids)); + res = triangle_setup_nodes(ids, &cand_nodes); + if(res != RES_OK) goto error; + + res = darray_node_copy(&clip_nodes, &poly_nodes); if(res != RES_OK) goto error; - /*TODO clip(&cand_nodes, &poly_nodes);*/ + res = setup_intersection_nodes + (&cand_nodes, &mesh->coords, &poly_nodes, &poly.coords); + if(res != RES_OK) goto error; } exit: - darray_node_release(&mesh_nodes); darray_node_release(&poly_nodes); darray_node_release(&cand_nodes); + darray_node_release(&clip_nodes); darray_char_release(&is_inside); - htable_edge_release(&edges); - polygon_release(&poly); + poly_release(&poly); return res; error: