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:
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 */