commit 40a41800c8922250085749aae83262bbcf8665df
parent 6b83b2e0e44c90ef5b65946fae206d3a697391e3
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date: Mon, 3 Oct 2022 12:10:22 +0200
Allow polygons to have multiple connex components
Diffstat:
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 {