star-cpr

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

commit 414ac1e7854377669ca39ca8eb3b69e0ab117a43
parent 6b83b2e0e44c90ef5b65946fae206d3a697391e3
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Mon,  3 Oct 2022 12:10:48 +0200

Merge branch 'feature-multi-components-polygons' into develop

Diffstat:
Msrc/scpr.h | 19+++++++++++++++++--
Msrc/scpr_c.h | 2+-
Msrc/scpr_mesh.c | 2+-
Msrc/scpr_polygon.c | 217++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Msrc/test_scpr_clip.c | 53+++++++++++++++++++++++++++++++++++++++++++----------
Msrc/test_scpr_offset.c | 205+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Msrc/test_scpr_polygon.c | 109++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Msrc/test_scpr_utils.h | 24++++++++++++++++++------
8 files changed, 497 insertions(+), 134 deletions(-)

diff --git a/src/scpr.h b/src/scpr.h @@ -74,8 +74,10 @@ scpr_polygon_ref_put SCPR_API res_T scpr_polygon_setup_indexed_vertices (struct scpr_polygon* polygon, - const size_t nverts, /* #vertices */ - void (*get_position)(const size_t ivert, double pos[2], void* ctx), + const size_t ncomponents, /* #connex components */ + void (*get_nverts)(const size_t icomponent, size_t *nverts, void* ctx), + void (*get_position) + (const size_t icomponent, const size_t ivert, double pos[2], void* ctx), void* data); /* Client data set as the last param of the callbacks */ SCPR_API res_T @@ -85,17 +87,30 @@ scpr_offset_polygon const enum scpr_join_type join_type); SCPR_API res_T +scpr_polygon_get_components_count + (const struct scpr_polygon* polygon, + size_t* ncomps); + +SCPR_API res_T scpr_polygon_get_vertices_count (const struct scpr_polygon* polygon, + const size_t icomponent, size_t* nverts); SCPR_API res_T scpr_polygon_get_position (const struct scpr_polygon* polygon, + const size_t icomponent, const size_t ivert, double position[2]); SCPR_API res_T +scpr_polygon_eq + (const struct scpr_polygon* polygon1, + const struct scpr_polygon* polygon2, + int* is_eq); + +SCPR_API res_T scpr_mesh_create (struct mem_allocator* allocator, /* May be NULL <=> Use default allocator */ struct scpr_mesh** mesh); diff --git a/src/scpr_c.h b/src/scpr_c.h @@ -43,7 +43,7 @@ vertex_eq(const struct vertex* a, const struct vertex* b) #include <rsys/hash_table.h> struct scpr_polygon { - Clipper2Lib::PathsD path; + Clipper2Lib::PathsD paths; double lower[2], upper[2]; /* Polygon AABB */ ref_T ref; diff --git a/src/scpr_mesh.c b/src/scpr_mesh.c @@ -460,7 +460,7 @@ scpr_mesh_clip /* Clip the polygon */ clipper.Clear(); clipper.AddSubject(cand_path); - clipper.AddClip(poly_desc->path); + clipper.AddClip(poly_desc->paths); clipper.Execute(clip_type, Clipper2Lib::FillRule::EvenOdd, output); /* Register the resulting clipped polygons */ diff --git a/src/scpr_polygon.c b/src/scpr_polygon.c @@ -45,10 +45,60 @@ polygon_release(ref_T* ref) struct scpr_polygon* polygon; ASSERT(ref); polygon = CONTAINER_OF(ref, struct scpr_polygon, ref); - polygon->path.clear(); + polygon->paths.clear(); MEM_RM(polygon->allocator, polygon); } +static int +path_is_eq + (const Clipper2Lib::PathD* p1, + const Clipper2Lib::PathD* p2) +{ + size_t i, first_vtx, sz; + int opposite_cw; + ASSERT(p1 && p2); + sz = p1->size(); + if(sz != p2->size()) { + return 0; + } + FOR_EACH(opposite_cw, 0, 2) { + FOR_EACH(first_vtx, 0, sz) { + int eq = 1; + FOR_EACH(i, 0, sz) { + size_t n; + if(opposite_cw) n = sz - 1 - (i + first_vtx) % sz; + else n = (i + first_vtx) % sz; + if((*p1)[i] != (*p2)[n]) { + eq = 0; + break; /* This opposite_cw/fstv failed: try next */ + } + } + /* Could prove p1 == p2 */ + if(eq) return 1; + } + } + return 0; +} + +static int +one_path_is_eq + (const Clipper2Lib::PathsD* pp, + const Clipper2Lib::PathD* p, + char* matched) +{ + size_t i, sz; + ASSERT(pp && p && matched); + sz = pp->size(); + FOR_EACH(i, 0, sz) { + if(matched[i]) continue; + if(path_is_eq(&pp->at(i), p)) { + matched[i] = 1; + return 1; + } + } + return 0; +} + /******************************************************************************* * Exported functions ******************************************************************************/ @@ -75,6 +125,8 @@ scpr_polygon_create } ref_init(&polygon->ref); polygon->allocator = allocator; + d2_splat(polygon->lower, DBL_MAX); + d2_splat(polygon->upper,-DBL_MAX); exit: if(out_polygon) *out_polygon = polygon; @@ -106,101 +158,115 @@ 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, - const size_t nverts, - void (*get_position)(const size_t ivert, double pos[2], void* ctx), + const size_t ncomponents, /* #connex components */ + void (*get_nverts)(const size_t icomponent, size_t *nverts, void* ctx), + void (*get_position) + (const size_t icomponent, const size_t ivert, double pos[2], void* ctx), void* data) { - size_t i; - Clipper2Lib::PathD path; + size_t c; res_T res = RES_OK; - if(!polygon || !nverts || !get_position || !data) { + if(!polygon || !get_nverts || !get_position || !data) { res = RES_BAD_ARG; goto error; } - try { - path.reserve(nverts); - } - catch(std::bad_alloc &e) { - res = RES_MEM_ERR; - goto error; - } - catch(...) { - res = RES_UNKNOWN_ERR; - goto error; - } + TRY(polygon->paths.resize(ncomponents)); d2_splat(polygon->lower, DBL_MAX); d2_splat(polygon->upper,-DBL_MAX); - /* Fetch polygon positions */ - FOR_EACH(i, 0, nverts) { - double tmp[2]; - Clipper2Lib::PointD pt; - get_position(i, tmp, data); - pt.x = tmp[0]; - pt.y = tmp[1]; - path.push_back(pt); - - d2_min(polygon->lower, polygon->lower, tmp); - d2_max(polygon->upper, polygon->upper, tmp); - } + FOR_EACH(c, 0, ncomponents) { + size_t i, nverts; - try { - polygon->path.push_back(path); - } - catch(std::bad_alloc &e) { - res = RES_MEM_ERR; - goto error; - } - catch(...) { - res = RES_UNKNOWN_ERR; - goto error; + /* Get count for connex component c */ + get_nverts(c, &nverts, data); + TRY(polygon->paths[c].resize(nverts)); + + /* Fetch polygon positions for connex component c */ + FOR_EACH(i, 0, nverts) { + double tmp[2]; + Clipper2Lib::PointD pt; + get_position(c, i, tmp, data); + pt.x = tmp[0]; + pt.y = tmp[1]; + polygon->paths[c][i] = pt; + + d2_min(polygon->lower, polygon->lower, tmp); + d2_max(polygon->upper, polygon->upper, tmp); + } } exit: return res; error: if(polygon) { - polygon->path.clear(); + polygon->paths.clear(); d2_splat(polygon->lower, DBL_MAX); d2_splat(polygon->upper,-DBL_MAX); } goto exit; } +#undef TRY + +res_T +scpr_polygon_get_components_count + (const struct scpr_polygon* polygon, + size_t* ncomps) +{ + if(!polygon || !ncomps) { + return RES_BAD_ARG; + } + *ncomps = polygon->paths.size(); + return RES_OK; +} res_T scpr_polygon_get_vertices_count (const struct scpr_polygon* polygon, + const size_t icomponent, size_t* nverts) { - if(!polygon || !nverts) return RES_BAD_ARG; - ASSERT(polygon->path.size() <= 1); - if(polygon->path.size() < 1) { - *nverts = 0; - } else { - *nverts = polygon->path[0].size(); + if(!polygon || !nverts || icomponent >= polygon->paths.size()) { + return RES_BAD_ARG; } + *nverts = polygon->paths[icomponent].size(); return RES_OK; } res_T scpr_polygon_get_position (const struct scpr_polygon* polygon, + const size_t icomponent, const size_t ivert, double pos[2]) { size_t nverts; const size_t i = ivert; const Clipper2Lib::PointD* pt; - if(!polygon || !pos) return RES_BAD_ARG; - SCPR(polygon_get_vertices_count(polygon, &nverts)); + if(!polygon || !pos || icomponent >= polygon->paths.size()) { + return RES_BAD_ARG; + } + SCPR(polygon_get_vertices_count(polygon, icomponent, &nverts)); if(ivert >= nverts) return RES_BAD_ARG; - pt = &polygon->path[0][i]; + pt = &polygon->paths[icomponent][i]; pos[0] = pt->x; pos[1] = pt->y; return RES_OK; @@ -212,7 +278,7 @@ scpr_offset_polygon const double offset, /* Can be either positive or negative */ const enum scpr_join_type join_type) { - size_t i, nverts; + size_t c; Clipper2Lib::PathD tmp; Clipper2Lib::PathsD polygon; Clipper2Lib::JoinType cjt; @@ -232,19 +298,19 @@ scpr_offset_polygon goto error; } cjt = scpr_join_type_to_clipper_join_type(join_type); - poly_desc->path = Clipper2Lib::InflatePaths(poly_desc->path, offset, cjt, + poly_desc->paths = Clipper2Lib::InflatePaths(poly_desc->paths, offset, cjt, Clipper2Lib::EndType::Polygon); /* Rebuild AABB */ - res = scpr_polygon_get_vertices_count(poly_desc, &nverts); - if(res != RES_OK) goto error; + d2_splat(poly_desc->lower, DBL_MAX); + d2_splat(poly_desc->upper,-DBL_MAX); + FOR_EACH(c, 0, poly_desc->paths.size()) { + size_t i, nverts; + nverts = poly_desc->paths[c].size(); - if(nverts > 0) { - d2_splat(poly_desc->lower, DBL_MAX); - d2_splat(poly_desc->upper,-DBL_MAX); FOR_EACH(i, 0, nverts) { double pos[2]; - res = scpr_polygon_get_position(poly_desc, i, pos); + res = scpr_polygon_get_position(poly_desc, c, i, pos); if(res != RES_OK) goto error; d2_min(poly_desc->lower, poly_desc->lower, pos); d2_max(poly_desc->upper, poly_desc->upper, pos); @@ -256,3 +322,40 @@ exit: error: goto exit; } + +res_T +scpr_polygon_eq + (const struct scpr_polygon* polygon1, + const struct scpr_polygon* polygon2, + int* is_eq) +{ + size_t i, sz; + char* matched = NULL; + res_T res = RES_OK; + + if(!polygon1 || !polygon2 || !is_eq) { + res = RES_BAD_ARG; + goto error; + } + + sz = polygon1->paths.size(); + if(sz != polygon2->paths.size()) { + *is_eq = 0; + goto exit; + } + + matched = (char*)calloc(sz, sizeof(*matched)); + FOR_EACH(i, 0, sz) { + if(!one_path_is_eq(&polygon1->paths, &polygon2->paths.at(i), matched)) { + *is_eq = 0; + goto exit; + } + } + *is_eq = 1; + +exit: + free(matched); + return res; +error: + goto exit; +} diff --git a/src/test_scpr_clip.c b/src/test_scpr_clip.c @@ -19,6 +19,8 @@ #include <rsys/math.h> #include <rsys/stretchy_array.h> +#include <memory.h> + static void dump_obj(FILE* stream, const struct scpr_mesh* mesh) { @@ -52,16 +54,24 @@ test_triangle { const double triangle_pos[] = { 0.0, 0.0, 0.0, 1.0, 1.0, 0.0 }; const size_t triangle_ids[] = { 0, 1, 2 }; - const double clip_pos[] = { -1.0, 0.25, 1.0, 0.75, 1, 0.25 }; + double** clip_pos; + size_t nverts[] = { 3 }; + size_t ncomps = 1; + const double clip_pos0[] = { -1.0, 0.25, 1.0, 0.75, 1, 0.25 }; struct scpr_polygon* poly; struct polygon_context pctx; 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)); + memcpy(*clip_pos, clip_pos0, 2*nverts[0]*sizeof(**clip_pos)); + pctx.coords = clip_pos; - pctx.nverts = sizeof(clip_pos)/(2*sizeof(double)); + pctx.nverts = nverts; + pctx.ncomps = ncomps; CHK(scpr_polygon_create(allocator, &poly) == RES_OK); - CHK(scpr_polygon_setup_indexed_vertices(poly, pctx.nverts, pget_pos, &pctx) + CHK(scpr_polygon_setup_indexed_vertices(poly, ncomps, pget_nverts, pget_pos, &pctx) == RES_OK); mctx.coords = triangle_pos; @@ -85,6 +95,8 @@ test_triangle CHK(scpr_mesh_get_triangles_count(mesh, &ntris) == RES_OK); CHK(ntris == 3); + MEM_RM(allocator, *clip_pos); + MEM_RM(allocator, clip_pos); CHK(scpr_polygon_ref_put(poly) == RES_OK); } @@ -95,15 +107,23 @@ test_quad { const double quad_pos[] = { 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0 }; const size_t quad_ids[] = { 0, 1, 3, 3, 1, 2 }; - const double clip_pos[] = { -0.25, 0.25, -0.25, 0.75, 1.25, 0.75, 1.25, 0.25 }; + double** clip_pos; + size_t nverts[] = { 4 }; + size_t ncomps = 1; + const double clip_pos0[] = { -0.25, 0.25, -0.25, 0.75, 1.25, 0.75, 1.25, 0.25 }; struct scpr_polygon* poly; 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)); + memcpy(*clip_pos, clip_pos0, 2*nverts[0]*sizeof(**clip_pos)); + pctx.coords = clip_pos; - pctx.nverts = sizeof(clip_pos)/(2*sizeof(double)); + pctx.nverts = nverts; + pctx.ncomps = ncomps; CHK(scpr_polygon_create(allocator, &poly) == RES_OK); - CHK(scpr_polygon_setup_indexed_vertices(poly, pctx.nverts, pget_pos, &pctx) + CHK(scpr_polygon_setup_indexed_vertices(poly, ncomps, pget_nverts, pget_pos, &pctx) == RES_OK); mctx.coords = quad_pos; @@ -117,6 +137,8 @@ test_quad /*dump_obj(stdout, mesh);*/ + MEM_RM(allocator, *clip_pos); + MEM_RM(allocator, clip_pos); CHK(scpr_polygon_ref_put(poly) == RES_OK); } @@ -125,7 +147,11 @@ test_disk (struct mem_allocator* allocator, struct scpr_mesh* mesh) { - const double clip[] = { -1.75, -1.75, 1.75, -1.75, 1.75, 1.75, -1.75, 1.75 }; + double** clip_pos; + size_t nverts[] = { 4 }; + size_t ncomps = 1; + const double clip_pos0[] + = { -1.75, -1.75, 1.75, -1.75, 1.75, 1.75, -1.75, 1.75 }; const size_t ninternal_disks = 10; const double radius = 2.5; const double internal_disk_step = radius / (double)ninternal_disks; @@ -137,6 +163,10 @@ 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)); + memcpy(*clip_pos, clip_pos0, 2*nverts[0]*sizeof(**clip_pos)); + FOR_EACH(i, 0, ninternal_disks) { const double r = (double)(i+1)*internal_disk_step; FOR_EACH(j, 0, nslices) { @@ -181,10 +211,11 @@ test_disk sa_push(ids, id1); } - pctx.coords = clip; - pctx.nverts = sizeof(clip)/(2*sizeof(double)); + pctx.coords = clip_pos; + pctx.nverts = nverts; + pctx.ncomps = ncomps; CHK(scpr_polygon_create(allocator, &poly) == RES_OK); - CHK(scpr_polygon_setup_indexed_vertices(poly, pctx.nverts, pget_pos, &pctx) + CHK(scpr_polygon_setup_indexed_vertices(poly, ncomps, pget_nverts, pget_pos, &pctx) == RES_OK); mctx.coords = pos; @@ -198,6 +229,8 @@ test_disk dump_obj(stdout, mesh); + MEM_RM(allocator, *clip_pos); + MEM_RM(allocator, clip_pos); sa_release(pos); sa_release(ids); CHK(scpr_polygon_ref_put(poly) == RES_OK); diff --git a/src/test_scpr_offset.c b/src/test_scpr_offset.c @@ -16,54 +16,219 @@ #include "scpr.h" #include "test_scpr_utils.h" -int -main(int argc, char** argv) +#include <memory.h> + +static void +test_single(void) { - const double coords[] = { + const double coords0[] = { 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0 }; - const size_t nverts = sizeof(coords)/(2*sizeof(double)); - double pos[2]; - size_t i; + const double coords1[] = { + -1.0, -1.0, + -1.0, 2.0, + 2.0, 2.0, + 2.0, -1.0 + }; + double** coords; + size_t nverts[] = { 4 }; + size_t ncomps = 1; struct mem_allocator allocator; struct polygon_context ctx; struct scpr_polygon* polygon; - (void)argc, (void)argv; + struct scpr_polygon* expected; + int eq; mem_init_proxy_allocator(&allocator, &mem_default_allocator); + coords = MEM_CALLOC(&allocator, ncomps, sizeof(*coords)); + *coords = MEM_CALLOC(&allocator, nverts[0], 2*sizeof(**coords)); + memcpy(*coords, coords0, 2*nverts[0]*sizeof(**coords)); + CHK(scpr_polygon_create(&allocator, &polygon) == RES_OK); + CHK(scpr_polygon_create(&allocator, &expected) == RES_OK); ctx.coords = coords; ctx.nverts = nverts; + ctx.ncomps = ncomps; + + CHK(scpr_polygon_setup_indexed_vertices(polygon, ncomps, pget_nverts, pget_pos, &ctx) + == RES_OK); + CHK(scpr_polygon_setup_indexed_vertices(expected, ncomps, pget_nverts, pget_pos, &ctx) + == RES_OK); + + /* Offset 0 = unchanged */ + CHK(scpr_offset_polygon(polygon, 0, SCPR_JOIN_MITER) == RES_OK); + CHK(scpr_polygon_eq(polygon, expected, &eq) == RES_OK); + CHK(eq); + + /* Offset 1 */ + memcpy(*coords, coords1, 2*nverts[0]*sizeof(**coords)); + CHK(scpr_polygon_setup_indexed_vertices(expected, ncomps, pget_nverts, pget_pos, &ctx) + == RES_OK); + + CHK(scpr_offset_polygon(polygon, 1, SCPR_JOIN_MITER) == RES_OK); + CHK(scpr_polygon_eq(polygon, expected, &eq) == RES_OK); + CHK(eq); + + /* Offset -1: back to original polygon */ + memcpy(*coords, coords0, 2*nverts[0]*sizeof(**coords)); + CHK(scpr_polygon_setup_indexed_vertices(expected, ncomps, pget_nverts, pget_pos, &ctx) + == RES_OK); + + CHK(scpr_offset_polygon(polygon, -1, SCPR_JOIN_MITER) == RES_OK); + CHK(scpr_polygon_eq(polygon, expected, &eq) == RES_OK); + CHK(eq); + + /* Offset -5: empty polygon */ + ncomps = 0; + ctx.ncomps = ncomps; + CHK(scpr_polygon_setup_indexed_vertices(expected, ncomps, pget_nverts, pget_pos, &ctx) + == RES_OK); + + CHK(scpr_offset_polygon(polygon, -5, SCPR_JOIN_MITER) == RES_OK); + CHK(scpr_polygon_eq(polygon, expected, &eq) == RES_OK); + CHK(eq); + + CHK(scpr_polygon_ref_put(polygon) == RES_OK); + CHK(scpr_polygon_ref_put(expected) == RES_OK); + + MEM_RM(&allocator, *coords); + MEM_RM(&allocator, coords); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHK(mem_allocated_size() == 0); +} + +static void +test_double(void) +{ + const double coords0[] = { + 0.0, 0.0, + 0.0, 1.0, + 1.0, 1.0, + 1.0, 0.0 + }; + const double coords1[] = { + 10.0, 0.0, + 10.0, 1.0, + 11.0, 1.0, + 11.0, 0.0 + }; + const double coords2[] = { + -1.0, -1.0, + -1.0, 2.0, + 2.0, 2.0, + 2.0, -1.0 + }; + const double coords3[] = { + 9.0, -1.0, + 9.0, 2.0, + 12.0, 2.0, + 12.0, -1.0 + }; + const double coords4[] = { + -4.5, -4.5, + -4.5, 5.5, + 15.5, 5.5, + 15.5, -4.5 + }; + double** coords; + size_t nverts[] = { 4, 4 }; + size_t ncomps = 2; + struct mem_allocator allocator; + struct polygon_context ctx; + struct scpr_polygon* polygon; + struct scpr_polygon* expected; + int eq; + 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)); + memcpy(*coords, coords0, 2*nverts[0]*sizeof(**coords)); + memcpy(*(coords+1), coords1, 2*nverts[1]*sizeof(**coords)); + + CHK(scpr_polygon_create(&allocator, &polygon) == RES_OK); + CHK(scpr_polygon_create(&allocator, &expected) == RES_OK); + + ctx.coords = coords; + ctx.nverts = nverts; + ctx.ncomps = ncomps; - CHK(scpr_polygon_setup_indexed_vertices(polygon, nverts, pget_pos, &ctx) + CHK(scpr_polygon_setup_indexed_vertices(polygon, ncomps, pget_nverts, pget_pos, &ctx) + == RES_OK); + CHK(scpr_polygon_setup_indexed_vertices(expected, ncomps, pget_nverts, pget_pos, &ctx) == RES_OK); + /* Offset 0 = unchanged */ CHK(scpr_offset_polygon(polygon, 0, SCPR_JOIN_MITER) == RES_OK); - for(i = 0; i < nverts; i++) { - CHK(scpr_polygon_get_position(polygon, i, pos) == RES_OK); - CHK(coords[2*i+0] == pos[0] && coords[2*i+1] == pos[1]); - } + CHK(scpr_polygon_eq(polygon, expected, &eq) == RES_OK); + CHK(eq); + + /* Offset 1 */ + memcpy(*coords, coords2, 2*nverts[0]*sizeof(**coords)); + memcpy(*(coords+1), coords3, 2*nverts[1]*sizeof(**coords)); + CHK(scpr_polygon_setup_indexed_vertices(expected, ncomps, pget_nverts, pget_pos, &ctx) + == RES_OK); CHK(scpr_offset_polygon(polygon, 1, SCPR_JOIN_MITER) == RES_OK); - for(i = 0; i < nverts; i++) { - double expected[2]; - CHK(scpr_polygon_get_position(polygon, i, pos) == RES_OK); - expected[0] = coords[2*i+0] != 0 ? 2 : -1; - expected[1] = coords[2*i+1] != 0 ? 2 : -1; - CHK(expected[0] == pos[0] && expected[1] == pos[1]); - } + CHK(scpr_polygon_eq(polygon, expected, &eq) == RES_OK); + CHK(eq); + + /* Offset -1: back to original polygon */ + memcpy(*coords, coords0, 2*nverts[0]*sizeof(**coords)); + memcpy(*(coords+1), coords1, 2*nverts[1]*sizeof(**coords)); + CHK(scpr_polygon_setup_indexed_vertices(expected, ncomps, pget_nverts, pget_pos, &ctx) + == RES_OK); + + CHK(scpr_offset_polygon(polygon, -1, SCPR_JOIN_MITER) == RES_OK); + CHK(scpr_polygon_eq(polygon, expected, &eq) == RES_OK); + CHK(eq); + + /* Offset 4.5: the 2 squares merge */ + ncomps = 1; + ctx.ncomps = ncomps; + memcpy(*coords, coords4, 2*nverts[0]*sizeof(**coords)); + CHK(scpr_polygon_setup_indexed_vertices(expected, ncomps, pget_nverts, pget_pos, &ctx) + == RES_OK); + + CHK(scpr_offset_polygon(polygon, 4.5, SCPR_JOIN_MITER) == RES_OK); + CHK(scpr_polygon_eq(polygon, expected, &eq) == RES_OK); + CHK(eq); + + /* Offset -5: empty polygon */ + ncomps = 0; + ctx.ncomps = ncomps; + CHK(scpr_polygon_setup_indexed_vertices(expected, ncomps, pget_nverts, pget_pos, &ctx) + == RES_OK); + + CHK(scpr_offset_polygon(polygon, -5, SCPR_JOIN_MITER) == RES_OK); + CHK(scpr_polygon_eq(polygon, expected, &eq) == RES_OK); + CHK(eq); CHK(scpr_polygon_ref_put(polygon) == RES_OK); + CHK(scpr_polygon_ref_put(expected) == RES_OK); + + MEM_RM(&allocator, *coords); + MEM_RM(&allocator, *(coords+1)); + MEM_RM(&allocator, coords); check_memory_allocator(&allocator); mem_shutdown_proxy_allocator(&allocator); CHK(mem_allocated_size() == 0); - return 0; } +int +main(int argc, char** argv) +{ + (void)argc; (void)argv; + test_single(); + test_double(); + return 0; +} diff --git a/src/test_scpr_polygon.c b/src/test_scpr_polygon.c @@ -16,10 +16,12 @@ #include "scpr.h" #include "test_scpr_utils.h" +#include <memory.h> + int main(int argc, char** argv) { - const double coords[] = { + double coords0[] = { 0.0, 0.0, 0.0, 0.5, 0.0, 1.0, @@ -30,16 +32,23 @@ main(int argc, char** argv) 1.0, 0.5, 1.0, 1.0 }; - const size_t nverts = sizeof(coords)/(2*sizeof(double)); + double** coords; + size_t nverts[] = { 9 }; + size_t ncomps = 1; double pos[2]; - size_t i, n; + size_t i, c, n; struct mem_allocator allocator; struct polygon_context ctx; struct scpr_polygon* polygon; + int eq; (void)argc, (void)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)); + memcpy(*coords, coords0, 2*nverts[0]*sizeof(**coords)); + CHK(scpr_polygon_create(NULL, NULL) == RES_BAD_ARG); CHK(scpr_polygon_create(&allocator, NULL) == RES_BAD_ARG); CHK(scpr_polygon_create(NULL, &polygon) == RES_OK); @@ -52,48 +61,71 @@ main(int argc, char** argv) CHK(scpr_polygon_create(&allocator, &polygon) == RES_OK); - CHK(scpr_polygon_get_vertices_count(polygon, &n) == RES_OK); + CHK(scpr_polygon_get_components_count(polygon, &n) == RES_OK); CHK(n == 0); ctx.coords = coords; ctx.nverts = nverts; + ctx.ncomps = ncomps; #define SETUP scpr_polygon_setup_indexed_vertices - CHK(SETUP(NULL, 0, NULL, NULL) == RES_BAD_ARG); - CHK(SETUP(polygon, 0, NULL, NULL) == RES_BAD_ARG); - CHK(SETUP(NULL, nverts, NULL, NULL) == RES_BAD_ARG); - CHK(SETUP(polygon, nverts, NULL, NULL) == RES_BAD_ARG); - CHK(SETUP(NULL, 0, pget_pos, NULL) == RES_BAD_ARG); - CHK(SETUP(polygon, 0, pget_pos, NULL) == RES_BAD_ARG); - CHK(SETUP(NULL, nverts, pget_pos, NULL) == RES_BAD_ARG); - CHK(SETUP(polygon, nverts, pget_pos, NULL) == RES_BAD_ARG); - CHK(SETUP(NULL, 0, NULL, &ctx) == RES_BAD_ARG); - CHK(SETUP(polygon, 0, NULL, &ctx) == RES_BAD_ARG); - CHK(SETUP(NULL, nverts, NULL, &ctx) == RES_BAD_ARG); - CHK(SETUP(polygon, nverts, NULL, &ctx) == RES_BAD_ARG); - CHK(SETUP(NULL, 0, pget_pos, &ctx) == RES_BAD_ARG); - CHK(SETUP(polygon, 0, pget_pos, &ctx) == RES_BAD_ARG); - CHK(SETUP(NULL, nverts, pget_pos, &ctx) == RES_BAD_ARG); - CHK(SETUP(polygon, nverts, pget_pos, &ctx) == RES_OK); + CHK(SETUP(NULL, ncomps, NULL, NULL, NULL) == RES_BAD_ARG); + CHK(SETUP(polygon, ncomps, NULL, NULL, NULL) == RES_BAD_ARG); + CHK(SETUP(NULL, ncomps, pget_nverts, NULL, NULL) == RES_BAD_ARG); + CHK(SETUP(polygon, ncomps, pget_nverts, NULL, NULL) == RES_BAD_ARG); + CHK(SETUP(NULL, ncomps, NULL, pget_pos, NULL) == RES_BAD_ARG); + CHK(SETUP(polygon, ncomps, NULL, pget_pos, NULL) == RES_BAD_ARG); + CHK(SETUP(NULL, ncomps, pget_nverts, pget_pos, NULL) == RES_BAD_ARG); + CHK(SETUP(polygon, ncomps, pget_nverts, pget_pos, NULL) == RES_BAD_ARG); + CHK(SETUP(NULL, ncomps, NULL, NULL, &ctx) == RES_BAD_ARG); + CHK(SETUP(polygon, ncomps, NULL, NULL, &ctx) == RES_BAD_ARG); + CHK(SETUP(NULL, ncomps, pget_nverts, NULL, &ctx) == RES_BAD_ARG); + CHK(SETUP(polygon, ncomps, pget_nverts, NULL, &ctx) == RES_BAD_ARG); + CHK(SETUP(NULL, ncomps, NULL, pget_pos, &ctx) == RES_BAD_ARG); + CHK(SETUP(polygon, ncomps, NULL, pget_pos, &ctx) == RES_BAD_ARG); + CHK(SETUP(NULL, ncomps, pget_nverts, pget_pos, &ctx) == RES_BAD_ARG); + CHK(SETUP(polygon, ncomps, pget_nverts, pget_pos, &ctx) == RES_OK); #undef SETUP - CHK(scpr_polygon_get_vertices_count(NULL, NULL) == RES_BAD_ARG); - CHK(scpr_polygon_get_vertices_count(polygon, NULL) == RES_BAD_ARG); - CHK(scpr_polygon_get_vertices_count(NULL, &n) == RES_BAD_ARG); - CHK(scpr_polygon_get_vertices_count(polygon, &n) == RES_OK); - CHK(n == nverts); - - CHK(scpr_polygon_get_position(NULL, nverts, NULL) == RES_BAD_ARG); - CHK(scpr_polygon_get_position(polygon, nverts, NULL) == RES_BAD_ARG); - CHK(scpr_polygon_get_position(NULL, 0, NULL) == RES_BAD_ARG); - CHK(scpr_polygon_get_position(polygon, 0, NULL) == RES_BAD_ARG); - CHK(scpr_polygon_get_position(NULL, nverts, pos) == RES_BAD_ARG); - CHK(scpr_polygon_get_position(polygon, nverts, pos) == RES_BAD_ARG); - CHK(scpr_polygon_get_position(NULL, 0, pos) == RES_BAD_ARG); - FOR_EACH(i, 0, nverts) { - CHK(scpr_polygon_get_position(polygon, i, pos) == RES_OK); - CHK(pos[0] == coords[i*2+0]); - CHK(pos[1] == coords[i*2+1]); + CHK(scpr_polygon_get_components_count(NULL, NULL) == RES_BAD_ARG); + CHK(scpr_polygon_get_components_count(polygon, NULL) == RES_BAD_ARG); + CHK(scpr_polygon_get_components_count(NULL, &n) == RES_BAD_ARG); + CHK(scpr_polygon_get_components_count(polygon, &n) == RES_OK); + CHK(n == ncomps); + + CHK(scpr_polygon_get_vertices_count(NULL, ncomps, NULL) == RES_BAD_ARG); + CHK(scpr_polygon_get_vertices_count(polygon, ncomps, NULL) == RES_BAD_ARG); + CHK(scpr_polygon_get_vertices_count(NULL, 0, NULL) == RES_BAD_ARG); + CHK(scpr_polygon_get_vertices_count(polygon, 0, NULL) == RES_BAD_ARG); + CHK(scpr_polygon_get_vertices_count(NULL, ncomps, &n) == RES_BAD_ARG); + CHK(scpr_polygon_get_vertices_count(polygon, ncomps, &n) == RES_BAD_ARG); + CHK(scpr_polygon_get_vertices_count(NULL, 0, &n) == RES_BAD_ARG); + CHK(scpr_polygon_get_vertices_count(polygon, 0, &n) == RES_OK); + CHK(n == nverts[0]); + + CHK(scpr_polygon_eq(NULL, NULL, NULL) == RES_BAD_ARG); + CHK(scpr_polygon_eq(polygon, NULL, NULL) == RES_BAD_ARG); + CHK(scpr_polygon_eq(NULL, polygon, NULL) == RES_BAD_ARG); + CHK(scpr_polygon_eq(polygon, polygon, NULL) == RES_BAD_ARG); + CHK(scpr_polygon_eq(NULL, NULL, &eq) == RES_BAD_ARG); + CHK(scpr_polygon_eq(polygon, NULL, &eq) == RES_BAD_ARG); + CHK(scpr_polygon_eq(polygon, 0, &eq) == RES_BAD_ARG); + CHK(scpr_polygon_eq(polygon, polygon, &eq) == RES_OK); + CHK(eq); + + CHK(scpr_polygon_get_position(NULL, ncomps, nverts[0], pos) == RES_BAD_ARG); + CHK(scpr_polygon_get_position(NULL, ncomps, nverts[0], pos) == RES_BAD_ARG); + CHK(scpr_polygon_get_position(NULL, ncomps, nverts[0], pos) == RES_BAD_ARG); + CHK(scpr_polygon_get_position(NULL, ncomps, nverts[0], pos) == RES_BAD_ARG); + CHK(scpr_polygon_get_position(NULL, ncomps, nverts[0], pos) == RES_BAD_ARG); + CHK(scpr_polygon_get_position(NULL, ncomps, nverts[0], pos) == RES_BAD_ARG); + CHK(scpr_polygon_get_position(NULL, ncomps, nverts[0], pos) == RES_BAD_ARG); + FOR_EACH(c, 0, ncomps) { + FOR_EACH(i, 0, nverts[c]) { + CHK(scpr_polygon_get_position(polygon, c, i, pos) == RES_OK); + CHK(pos[0] == coords[c][i*2+0]); + CHK(pos[1] == coords[c][i*2+1]); + } } CHK(scpr_offset_polygon(NULL, 0, SCPR_JOIN_TYPES_COUNT__) == RES_BAD_ARG); @@ -103,6 +135,9 @@ main(int argc, char** argv) CHK(scpr_polygon_ref_put(polygon) == RES_OK); + MEM_RM(&allocator, *coords); + MEM_RM(&allocator, coords); + check_memory_allocator(&allocator); mem_shutdown_proxy_allocator(&allocator); CHK(mem_allocated_size() == 0); diff --git a/src/test_scpr_utils.h b/src/test_scpr_utils.h @@ -20,20 +20,32 @@ #include <stdio.h> struct polygon_context { - const double* coords; - size_t nverts; + double** coords; + size_t* nverts; + size_t ncomps; }; static INLINE void -pget_pos(const size_t ivert, double pos[2], void* context) +pget_nverts(const size_t icomp, size_t* nverts, void* context) +{ + const struct polygon_context* ctx = context; + CHK(nverts != NULL); + CHK(context != NULL); + CHK(icomp < ctx->ncomps); + *nverts = ctx->nverts[icomp]; +} + +static INLINE void +pget_pos(const size_t icomp, const size_t ivert, double pos[2], void* context) { const struct polygon_context* ctx = context; CHK(pos != NULL); CHK(context != NULL); CHK(ctx->coords != NULL); - CHK(ivert < ctx->nverts); - pos[0] = ctx->coords[ivert*2 + 0]; - pos[1] = ctx->coords[ivert*2 + 1]; + CHK(icomp < ctx->ncomps); + CHK(ivert < ctx->nverts[icomp]); + pos[0] = ctx->coords[icomp][ivert*2 + 0]; + pos[1] = ctx->coords[icomp][ivert*2 + 1]; } struct mesh_context {