star-cpr

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

commit 79114e9193d004c4d5b71783191cea0c9a03e8ef
parent ad87d8924defd730ac9fa1b557b61b9fa35dae06
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Thu,  2 Feb 2023 16:34:19 +0100

Improve tests: check that polygons are still representable after offsetting

Diffstat:
Mcmake/CMakeLists.txt | 6+++++-
Msrc/scpr_c.h | 16+++++++++++++++-
Msrc/scpr_polygon.c | 18++----------------
Msrc/test_scpr_clip.c | 14++++++++------
Msrc/test_scpr_mesh.c | 2++
Msrc/test_scpr_offset.c | 28++++++++++++++++++----------
Msrc/test_scpr_polygon.c | 6++++--
Msrc/test_scpr_utils.h | 48++++++++++++++++++++++++++++++++++++++++++++----
8 files changed, 98 insertions(+), 40 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -69,7 +69,7 @@ set_target_properties(scpr PROPERTIES target_link_libraries(scpr RSys Polygon Clipper2) if(CMAKE_COMPILER_IS_GNUCXX) - set_target_properties(scpr PROPERTIES COMPILE_FLAGS "-Wno-long-long -std=c++17") + set_target_properties(scpr PROPERTIES COMPILE_FLAGS "-std=c++17") endif() rcmake_setup_devel(scpr StarCPR ${VERSION} star/scpr_version.h) @@ -85,6 +85,10 @@ if(NOT NO_TEST) function(new_test _name) add_executable(${_name} ${SCPR_SOURCE_DIR}/${_name}.c) + set_source_files_properties(${SCPR_SOURCE_DIR}/${_name}.c PROPERTIES LANGUAGE C) + if(CMAKE_COMPILER_IS_GNUCXX) + set_target_properties(${name} PROPERTIES COMPILE_FLAGS "-std=c99") + endif() target_link_libraries(${_name} scpr RSys ${MATH_LIB}) add_test(${_name} ${_name}) endfunction() diff --git a/src/scpr_c.h b/src/scpr_c.h @@ -32,6 +32,19 @@ #define ERR(Expr) { if((res = (Expr)) != RES_OK) goto error; } (void)0 +#define TRY(Expr) \ + try { \ + (Expr); \ + } \ + catch(std::bad_alloc &e) { \ + res = RES_MEM_ERR; \ + goto error; \ + } \ + catch(...) { \ + res = RES_UNKNOWN_ERR; \ + goto error; \ + } + /* Sets the precision parameter, as expected by Clipper2. * Allowed range is [-8 +8]. * This parameter defines both the floating point precision for the coordinates @@ -48,7 +61,7 @@ struct mem_allocator; struct vertex { double pos[2]; }; static FINLINE int vertex_eq(const struct vertex* a, const struct vertex* b) -{ return d2_eq(a->pos, b->pos); } +{ ASSERT(a && b); return d2_eq(a->pos, b->pos); } /* Define the vertex to index hash table */ #define HTABLE_NAME vertex @@ -80,6 +93,7 @@ check_and_truncate_vertex double scale = pow(10, PRECISION); double tmp[2]; int64_t tmp2[2]; + ASSERT(pt); /* Truncate precision to ensure further consistency */ tmp2[0] = std::llround(pt[0] * scale); tmp2[1] = std::llround(pt[1] * scale); diff --git a/src/scpr_polygon.c b/src/scpr_polygon.c @@ -166,19 +166,6 @@ scpr_polygon_ref_put return RES_OK; } -#define TRY(Expr) \ - try { \ - (Expr); \ - } \ - catch(std::bad_alloc &e) { \ - res = RES_MEM_ERR; \ - goto error; \ - } \ - catch(...) { \ - res = RES_UNKNOWN_ERR; \ - goto error; \ - } - res_T scpr_polygon_setup_indexed_vertices (struct scpr_polygon* polygon, @@ -280,7 +267,6 @@ error: copy = NULL; goto exit; } -#undef TRY res_T scpr_polygon_get_components_count @@ -358,8 +344,8 @@ scpr_offset_polygon /* Some known problems when offset=0, not leaving polygon unchanged */ cjt = scpr_join_type_to_clipper_join_type(join_type); - poly_desc->paths = Clipper2Lib::InflatePaths(poly_desc->paths, offset, cjt, - Clipper2Lib::EndType::Polygon, 2, PRECISION); + TRY(poly_desc->paths = Clipper2Lib::InflatePaths(poly_desc->paths, offset, cjt, + Clipper2Lib::EndType::Polygon, 2, PRECISION)); /* Rebuild AABB */ d2_splat(poly_desc->lower, DBL_MAX); diff --git a/src/test_scpr_clip.c b/src/test_scpr_clip.c @@ -13,6 +13,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#define _POSIX_C_SOURCE 200112L + #include "scpr.h" #include "test_scpr_utils.h" @@ -64,8 +66,8 @@ test_triangle struct mesh_context mctx; size_t ntris; - clip_pos = MEM_CALLOC(allocator, ncomps, sizeof(*clip_pos)); - *clip_pos = MEM_CALLOC(allocator, nverts[0], 2*sizeof(**clip_pos)); + clip_pos = (double**)MEM_CALLOC(allocator, ncomps, sizeof(*clip_pos)); + *clip_pos = (double*)MEM_CALLOC(allocator, nverts[0], 2*sizeof(**clip_pos)); memcpy(*clip_pos, clip_pos0, 2*nverts[0]*sizeof(**clip_pos)); pctx.coords = clip_pos; @@ -121,8 +123,8 @@ test_quad struct polygon_context pctx; struct mesh_context mctx; - clip_pos = MEM_CALLOC(allocator, ncomps, sizeof(*clip_pos)); - *clip_pos = MEM_CALLOC(allocator, nverts[0], 2*sizeof(**clip_pos)); + clip_pos = (double**)MEM_CALLOC(allocator, ncomps, sizeof(*clip_pos)); + *clip_pos = (double*)MEM_CALLOC(allocator, nverts[0], 2*sizeof(**clip_pos)); memcpy(*clip_pos, clip_pos0, 2*nverts[0]*sizeof(**clip_pos)); pctx.coords = clip_pos; @@ -169,8 +171,8 @@ test_disk size_t* ids = NULL; size_t i, j; - clip_pos = MEM_CALLOC(allocator, ncomps, sizeof(*clip_pos)); - *clip_pos = MEM_CALLOC(allocator, nverts[0], 2*sizeof(**clip_pos)); + clip_pos = (double**)MEM_CALLOC(allocator, ncomps, sizeof(*clip_pos)); + *clip_pos = (double*)MEM_CALLOC(allocator, nverts[0], 2*sizeof(**clip_pos)); memcpy(*clip_pos, clip_pos0, 2*nverts[0]*sizeof(**clip_pos)); FOR_EACH(i, 0, ninternal_disks) { diff --git a/src/test_scpr_mesh.c b/src/test_scpr_mesh.c @@ -13,6 +13,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#define _POSIX_C_SOURCE 200112L + #include "scpr.h" #include "test_scpr_utils.h" diff --git a/src/test_scpr_offset.c b/src/test_scpr_offset.c @@ -13,6 +13,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#define _POSIX_C_SOURCE 200112L + #include "scpr.h" #include "test_scpr_utils.h" @@ -62,8 +64,8 @@ test_single(void) mem_init_proxy_allocator(&allocator, &mem_default_allocator); - coords = MEM_CALLOC(&allocator, ncomps, sizeof(*coords)); - *coords = MEM_CALLOC(&allocator, nverts[0], 2*sizeof(**coords)); + coords = (double**)MEM_CALLOC(&allocator, ncomps, sizeof(*coords)); + *coords = (double*)MEM_CALLOC(&allocator, nverts[0], 2*sizeof(**coords)); memcpy(*coords, coords0, 2*nverts[0]*sizeof(**coords)); CHK(scpr_polygon_create(&allocator, &polygon) == RES_OK); @@ -82,6 +84,7 @@ test_single(void) CHK(scpr_offset_polygon(polygon, 0, SCPR_JOIN_MITER) == RES_OK); CHK(scpr_polygon_eq(polygon, expected, &eq) == RES_OK); CHK(eq); + CHK(check_stability(polygon)); /* Offset 1 */ memcpy(*coords, coords1, 2*nverts[0]*sizeof(**coords)); @@ -91,6 +94,7 @@ test_single(void) CHK(scpr_offset_polygon(polygon, 1, SCPR_JOIN_MITER) == RES_OK); CHK(scpr_polygon_eq(polygon, expected, &eq) == RES_OK); CHK(eq); + CHK(check_stability(polygon)); /* Offset -1: back to original polygon */ memcpy(*coords, coords0, 2*nverts[0]*sizeof(**coords)); @@ -100,6 +104,7 @@ test_single(void) CHK(scpr_offset_polygon(polygon, -1, SCPR_JOIN_MITER) == RES_OK); CHK(scpr_polygon_eq(polygon, expected, &eq) == RES_OK); CHK(eq); + CHK(check_stability(polygon)); /* Offset -5: empty polygon */ ncomps = 0; @@ -110,8 +115,9 @@ test_single(void) CHK(scpr_offset_polygon(polygon, -5, SCPR_JOIN_MITER) == RES_OK); CHK(scpr_polygon_eq(polygon, expected, &eq) == RES_OK); CHK(eq); + CHK(check_stability(polygon)); - /* Check consistency with a non representable coordinate */ + /* Check coordinates barely in-range */ memcpy(*coords, coords2, 2*nverts[0]*sizeof(**coords)); ncomps = 1; @@ -126,6 +132,7 @@ test_single(void) CHK(scpr_offset_polygon(polygon, 0, SCPR_JOIN_MITER) == RES_OK); CHK(scpr_polygon_eq(polygon, expected, &eq) == RES_OK); CHK(eq); + CHK(check_stability(polygon)); /* Check non-effect of CW/CCW */ memcpy(*coords, coords2_reverse, 2*nverts[0]*sizeof(**coords)); @@ -133,10 +140,11 @@ test_single(void) == RES_OK); CHK(scpr_polygon_eq(polygon, expected, &eq) == RES_OK); CHK(eq); + CHK(check_stability(polygon)); /* Check out of range */ memcpy(*coords, coords3, 2*nverts[0]*sizeof(**coords)); - CHK(scpr_polygon_setup_indexed_vertices(expected, ncomps, pget_nverts, pget_pos, &ctx) + CHK(scpr_polygon_setup_indexed_vertices(polygon, ncomps, pget_nverts, pget_pos, &ctx) == RES_BAD_ARG); /* Cleanup */ @@ -195,9 +203,9 @@ test_double(void) mem_init_proxy_allocator(&allocator, &mem_default_allocator); - coords = MEM_CALLOC(&allocator, ncomps, sizeof(*coords)); - *coords = MEM_CALLOC(&allocator, nverts[0], 2*sizeof(**coords)); - *(coords+1) = MEM_CALLOC(&allocator, nverts[1], 2*sizeof(**coords)); + coords = (double**)MEM_CALLOC(&allocator, ncomps, sizeof(*coords)); + *coords = (double*)MEM_CALLOC(&allocator, nverts[0], 2*sizeof(**coords)); + *(coords+1) = (double*)MEM_CALLOC(&allocator, nverts[1], 2*sizeof(**coords)); memcpy(*coords, coords0, 2*nverts[0]*sizeof(**coords)); memcpy(*(coords+1), coords1, 2*nverts[1]*sizeof(**coords)); @@ -315,9 +323,9 @@ test_internal(void) mem_init_proxy_allocator(&allocator, &mem_default_allocator); - coords = MEM_CALLOC(&allocator, ncomps, sizeof(*coords)); - *coords = MEM_CALLOC(&allocator, nverts[0], 2*sizeof(**coords)); - *(coords+1) = MEM_CALLOC(&allocator, nverts[1], 2*sizeof(**coords)); + coords = (double**)MEM_CALLOC(&allocator, ncomps, sizeof(*coords)); + *coords = (double*)MEM_CALLOC(&allocator, nverts[0], 2*sizeof(**coords)); + *(coords+1) = (double*)MEM_CALLOC(&allocator, nverts[1], 2*sizeof(**coords)); memcpy(*coords, coords0, 2*nverts[0]*sizeof(**coords)); memcpy(*(coords+1), coords1, 2*nverts[1]*sizeof(**coords)); diff --git a/src/test_scpr_polygon.c b/src/test_scpr_polygon.c @@ -13,6 +13,8 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#define _POSIX_C_SOURCE 200112L + #include "scpr.h" #include "test_scpr_utils.h" @@ -47,8 +49,8 @@ main(int argc, char** argv) mem_init_proxy_allocator(&allocator, &mem_default_allocator); - coords = MEM_CALLOC(&allocator, ncomps, sizeof(*coords)); - *coords = MEM_CALLOC(&allocator, nverts[0], 2*sizeof(**coords)); + coords = (double**)MEM_CALLOC(&allocator, ncomps, sizeof(*coords)); + *coords = (double*)MEM_CALLOC(&allocator, nverts[0], 2*sizeof(**coords)); memcpy(*coords, coords0, 2*nverts[0]*sizeof(**coords)); CHK(scpr_polygon_create(NULL, NULL) == RES_BAD_ARG); diff --git a/src/test_scpr_utils.h b/src/test_scpr_utils.h @@ -16,8 +16,12 @@ #ifndef TEST_CPR_UTILS_H #define TEST_CPR_UTILS_H +#define _POSIX_C_SOURCE 200112L + +#include "scpr.h" #include <rsys/mem_allocator.h> #include <stdio.h> +#include <math.h> struct polygon_context { double** coords; @@ -28,7 +32,7 @@ struct polygon_context { static INLINE void pget_nverts(const size_t icomp, size_t* nverts, void* context) { - const struct polygon_context* ctx = context; + const struct polygon_context* ctx = (const struct polygon_context*)context; CHK(nverts != NULL); CHK(context != NULL); CHK(icomp < ctx->ncomps); @@ -38,7 +42,7 @@ pget_nverts(const size_t icomp, size_t* nverts, void* context) static INLINE void pget_pos(const size_t icomp, const size_t ivert, double pos[2], void* context) { - const struct polygon_context* ctx = context; + const struct polygon_context* ctx = (const struct polygon_context*)context; CHK(pos != NULL); CHK(context != NULL); CHK(ctx->coords != NULL); @@ -58,7 +62,7 @@ struct mesh_context { static INLINE void mget_pos(const size_t ivert, double pos[2], void* context) { - const struct mesh_context* ctx = context; + const struct mesh_context* ctx = (const struct mesh_context*)context; CHK(pos != NULL); CHK(context != NULL); CHK(ctx->coords != NULL); @@ -70,7 +74,7 @@ mget_pos(const size_t ivert, double pos[2], void* context) static INLINE void mget_ids(const size_t itri, size_t ids[3], void* context) { - const struct mesh_context* ctx = context; + const struct mesh_context* ctx = (const struct mesh_context*)context; CHK(ids != NULL); CHK(context != NULL); CHK(ctx->indices != NULL); @@ -91,4 +95,40 @@ check_memory_allocator(struct mem_allocator* allocator) } } +INLINE int +check_stability + (const struct scpr_polygon* polygon) +{ + res_T res = RES_OK; + double scale = 1e6; + size_t i, j, ccount, vcount; + ASSERT(polygon); + + res = scpr_polygon_get_components_count(polygon, &ccount); + if(res != RES_OK) goto error; + + for(i = 0; i < ccount; i++) { + res = scpr_polygon_get_vertices_count(polygon, i, &vcount); + if(res != RES_OK) goto error; + for(j = 0; j < vcount; j++) { + double pt[2], tmp[2]; + int64_t tmp2[2]; + res = scpr_polygon_get_position(polygon, i, j, pt); + if(res != RES_OK) goto error; + tmp2[0] = llround(pt[0] * scale); + tmp2[1] = llround(pt[1] * scale); + tmp[0] = (double)tmp2[0] / scale; + tmp[1] = (double)tmp2[1] / scale; + if(tmp[0] != pt[0] || tmp[1] != pt[1]) { + res = RES_BAD_ARG; + goto error; + } + } + } +end: + return (res == RES_OK); +error: + goto end; +} + #endif /* TEST_CPR_UTILS_H */