star-cpr

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

commit 6157fa54c6827b6af0b670f4880ba8a3b22e6e25
parent 0db8875deff65859fcb923ab813cdb596627856b
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Thu, 25 Aug 2016 13:24:38 +0200

Speed up the clip process

Do not clip the mesh triangles that does not intersect the clip polygon.

Diffstat:
Msrc/cpr_mesh.c | 160++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
1 file changed, 122 insertions(+), 38 deletions(-)

diff --git a/src/cpr_mesh.c b/src/cpr_mesh.c @@ -58,6 +58,7 @@ struct node { struct poly { struct darray_double coords; + double lower[2], upper[2]; }; struct hit { @@ -77,6 +78,27 @@ struct cpr_mesh { /******************************************************************************* * Helper functions ******************************************************************************/ +static INLINE int +aabb_is_degenerated(const double lower[2], const double upper[2]) +{ + ASSERT(lower && upper); + return lower[0] >= upper[0] || lower[1] >= upper[1]; +} + +static int +aabb_intersect + (const double lower0[2], + const double upper0[2], + const double lower1[2], + const double upper1[2]) +{ + ASSERT(lower0 && upper0 && lower1 && upper1); + ASSERT(!aabb_is_degenerated(lower0, upper0)); + ASSERT(!aabb_is_degenerated(lower1, upper1)); + return lower0[0] < upper1[0] && lower0[1] < upper1[1] + && lower1[0] < upper0[0] && lower1[1] < upper0[1]; +} + static int intersect_edges (double edge0[2][2], @@ -137,7 +159,7 @@ vertex_get_index ASSERT(v && coords && vertices); - /* Dirty trick. TODO comment this */ + /* Dirty trick. TODO comment this, or better, replace this! */ key.pos[0] = (float)v->pos[0]; key.pos[1] = (float)v->pos[1]; @@ -189,15 +211,21 @@ poly_setup(struct poly* poly, const struct cpr_polygon* desc) res = darray_double_resize(&poly->coords, desc->nvertices * 2/*#coords*/); if(res != RES_OK) goto error; + d2_splat(poly->lower, DBL_MAX); + d2_splat(poly->upper, DBL_MIN); FOR_EACH(ivert, 0, desc->nvertices) { double* pos = darray_double_data_get(&poly->coords) + ivert*2; desc->get_position(ivert, pos, desc->context); + d2_min(poly->lower, poly->lower, pos); + d2_max(poly->upper, poly->upper, pos); } exit: return res; error: darray_double_clear(&poly->coords); + d2_splat(poly->lower, DBL_MAX); + d2_splat(poly->upper, DBL_MIN); goto exit; } @@ -227,6 +255,52 @@ poly_setup_nodes(struct poly* poly, struct darray_node* nodes) return RES_OK; } +static void +triangle_compute_aabb + (const size_t triangle[3], + const struct cpr_mesh* mesh, + double lower[2], + double upper[2]) +{ + double pos[2]; + ASSERT(triangle && mesh && lower && upper); + CPR(mesh_get_position(mesh, triangle[0], pos)); + d2_set(lower, pos), d2_set(upper, pos); + CPR(mesh_get_position(mesh, triangle[1], pos)); + d2_min(lower, lower, pos), d2_max(upper, upper, pos); + CPR(mesh_get_position(mesh, triangle[2], pos)); + d2_min(lower, lower, pos), d2_max(upper, upper, pos); +} + +static res_T +triangle_register + (const size_t triangle[3], + const struct cpr_mesh* mesh, + struct darray_size_t* indices, + struct darray_double* coords, + struct htable_vertex* vertices) +{ + int i; + res_T res = RES_OK; + ASSERT(triangle && mesh && indices && coords && vertices); + + FOR_EACH(i, 0, 3) { + struct vertex v; + size_t id; + + CPR(mesh_get_position(mesh, triangle[i], v.pos)); + id = vertex_get_index(&v, coords, vertices); + res = darray_size_t_push_back(indices, &id); + if(res != RES_OK) goto error; + } +exit: + return res; +error: + /* Pop the already registered indices */ + darray_size_t_resize(indices, darray_size_t_size_get(indices) - (size_t)i); + goto exit; +} + static res_T triangle_setup_nodes(const size_t triangle[3], struct darray_node* nodes) { @@ -350,8 +424,10 @@ error: goto exit; } +/* Define if the vertices are inside the polygon and slightly move them if they + * roughly lie onto a polygon edge */ static void -vertices_setup_inside_poly_flag +preprocess_vertices (struct darray_double* coords, const struct darray_node* poly_nodes, const struct darray_double* poly_coords, @@ -682,11 +758,11 @@ cpr_mesh_clip(struct cpr_mesh* mesh, struct cpr_polygon* poly_desc) size_t itri, ntris, nverts; res_T res = RES_OK; - if(!mesh) { - res = RES_BAD_ARG; - goto error; - } + /* Directly return an error since the temporary data structures are still not + * initialized and thus could not be released by the exit block. */ + if(!mesh || !poly_desc) return RES_BAD_ARG; + /* Initialised the temporary data structures */ darray_node_init(mesh->allocator, &poly_nodes); darray_node_init(mesh->allocator, &cand_nodes); darray_node_init(mesh->allocator, &clip_nodes); @@ -697,60 +773,70 @@ cpr_mesh_clip(struct cpr_mesh* mesh, struct cpr_polygon* poly_desc) htable_vertex_init(mesh->allocator, &vertices); poly_init(mesh->allocator, &poly); - if(!poly_desc) { - res = RES_BAD_ARG; - goto error; - } - + /* Create the polygon object used to triangulated the clipped primitives */ res = polygon_create(NULL, &polygon); if(res != RES_OK) goto error; /* Setup the clip polygon */ res = poly_setup(&poly, poly_desc); if(res != RES_OK) goto error; + + /* Degenerated polygon => nothing to clip. + * TODO For CW polygon all the triangles must be clipped */ + if(aabb_is_degenerated(poly.lower, poly.upper)) goto exit; + + /* Build the polygon node list */ res = poly_setup_nodes(&poly, &poly_nodes); if(res != RES_OK) goto error; - /* Define which mesh vertices are inside the polygon */ + /* Preprocess the mesh vertices, i.e. define which mesh vertices are inside + * the polygon and avoid that a mesh vertex lies onto a polygon edge */ 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)); + preprocess_vertices(&mesh->coords, &poly_nodes, &poly.coords, + darray_char_data_get(&is_inside)); CPR(mesh_get_triangles_count(mesh, &ntris)); FOR_EACH(itri, 0, ntris) { + double lower[2], upper[2]; size_t ids[3]; + /* Fetch the current triangle */ CPR(mesh_get_indices(mesh, itri, ids)); + triangle_compute_aabb(ids, mesh, lower, upper); + if(aabb_is_degenerated(lower, upper)) continue; /* Skip degenerated prim */ + + if(!aabb_intersect(lower, upper, poly.lower, poly.upper)) { + /* TODO Discard the triangle if the clip polygon is CW oredered */ + res = triangle_register(ids, mesh, &indices, &coords, &vertices); + if(res != RES_OK) goto error; + continue; + } + + /* Build the node list of the mesh triangle */ res = triangle_setup_nodes(ids, &cand_nodes); if(res != RES_OK) goto error; + /* Make a copy of the polygon node list. This is necessary since the node + * list is going to be updated with respect to the candidate/polygon + * intersections */ res = darray_node_copy(&clip_nodes, &poly_nodes); if(res != RES_OK) goto error; + /* Insert into the candidate and clip node lists the intersection nodes */ res = setup_intersection_nodes (&cand_nodes, &mesh->coords, &clip_nodes, &poly.coords); if(res != RES_OK) goto error; #if 1 if(darray_node_size_get(&cand_nodes) == 3) { /* No intersection */ -#if 1 + /* If the first index is not inside */ if(!darray_char_data_get(&is_inside)[ids[0]]) { /* FIXME */ - struct vertex v[3]; - CPR(mesh_get_position(mesh, ids[0], v[0].pos)); - CPR(mesh_get_position(mesh, ids[1], v[1].pos)); - CPR(mesh_get_position(mesh, ids[2], v[2].pos)); - - ids[0] = vertex_get_index(v+0, &coords, &vertices); - ids[1] = vertex_get_index(v+1, &coords, &vertices); - ids[2] = vertex_get_index(v+2, &coords, &vertices); - - darray_size_t_push_back(&indices, ids + 0); - darray_size_t_push_back(&indices, ids + 1); + res = triangle_register(ids, mesh, &indices, &coords, &vertices); + if(res != RES_OK) goto error; darray_size_t_push_back(&indices, ids + 2); } -#endif } else #endif { @@ -777,17 +863,15 @@ cpr_mesh_clip(struct cpr_mesh* mesh, struct cpr_polygon* poly_desc) if(res != RES_OK) goto error; exit: - if(mesh) { - darray_node_release(&poly_nodes); - darray_node_release(&cand_nodes); - darray_node_release(&clip_nodes); - darray_char_release(&is_inside); - darray_size_t_release(&indices); - darray_double_release(&coords); - darray_size_t_release(&scratch); - htable_vertex_release(&vertices); - poly_release(&poly); - } + darray_node_release(&poly_nodes); + darray_node_release(&cand_nodes); + darray_node_release(&clip_nodes); + darray_char_release(&is_inside); + darray_size_t_release(&indices); + darray_double_release(&coords); + darray_size_t_release(&scratch); + htable_vertex_release(&vertices); + poly_release(&poly); if(polygon) POLYGON(ref_put(polygon)); return res;