star-cpr

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

commit b38528debac9ddb735f6eacc99c27d26e119e758
parent 9d29d87dc27d1b2bf5b7f503f2bee5bb0a71e2c4
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Mon,  6 Nov 2023 16:46:52 +0100

BugFix in component in component test

Diffstat:
Mcmake/CMakeLists.txt | 1+
Msrc/scpr.h | 6+++---
Msrc/scpr_polygon.c | 63+++++++++++++++++++++++++++++++++------------------------------
Asrc/test_scpr_is_in.c | 97+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 134 insertions(+), 33 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -99,6 +99,7 @@ if(NOT NO_TEST) new_test(test_scpr_clip) new_test(test_scpr_device) new_test(test_scpr_intersector) + new_test(test_scpr_is_in) new_test(test_scpr_offset) new_test(test_scpr_mesh) new_test(test_scpr_polygon) diff --git a/src/scpr.h b/src/scpr.h @@ -268,9 +268,9 @@ scpr_is_vertex_in_component const double vertex[2], int* situation); /* +1: inside, 0: on, -1: outside */ -/* Check if a component is inside a component, given the 2 components do not - * overlap. - * Only meaningful for 2 simple polygons. */ +/* Check if a component is inside a component, given the 2 components are not + * equal and do not overlap (they can be adjoining). + * Only meaningful for simple components. */ SCPR_API res_T scpr_is_component_in_component (const struct scpr_polygon* polygon1, diff --git a/src/scpr_polygon.c b/src/scpr_polygon.c @@ -644,8 +644,10 @@ scpr_is_component_in_component int* c1_is_in_c2) { size_t i, p1sz; - Clipper2Lib::Point64 p0, c; + int is_in = -1; Clipper2Lib::PointInPolygonResult in; + const Clipper2Lib::Path64* comp1; + const Clipper2Lib::Path64* comp2; res_T res = RES_OK; if(!polygon1 || icomponent1 >= polygon1->paths.size() @@ -656,40 +658,41 @@ scpr_is_component_in_component goto error; } - /* First find a vertex that is inside polygon1 */ - p0 = polygon1->paths[icomponent1][0]; - p1sz = polygon1->paths[icomponent1].size(); - for(i = 2; i < p1sz; i++) { - Clipper2Lib::Point64 pi = polygon1->paths[icomponent1][i]; - if(p1sz == 3) { - /* Special case of a triangle: get the barycenter */ - Clipper2Lib::Point64 p1 = polygon1->paths[icomponent1][1]; - c = (p0 + p1 + pi) * 0.3333333; - } else { - c = (p0 + pi) * 0.5; - } - TRY(in = Clipper2Lib::PointInPolygon(c, polygon1->paths[icomponent1])); - if(in != Clipper2Lib::PointInPolygonResult::IsInside) - continue; - /* c is in polygon1; check its situation against polygon2 */ - TRY(in = Clipper2Lib::PointInPolygon(c, polygon2->paths[icomponent2])); + /* comp1 == comp2 is reported as bad arg. + * This API requires comp1 and comp2 to not overlap (they can be adjoining), + * this allows to exit early as soon as 1 vertex is either inside or outside */ + comp1 = &polygon1->paths[icomponent1]; + comp2 = &polygon2->paths[icomponent2]; + p1sz = comp1->size(); + for(i = 0; i < p1sz; i++) { + Clipper2Lib::Point64 p = (*comp1)[i]; + TRY(in = Clipper2Lib::PointInPolygon(p, (*comp2))); switch(in) { - case Clipper2Lib::PointInPolygonResult::IsOn: - /* The 2 components overlap! */ - res = RES_BAD_ARG; - goto error; case Clipper2Lib::PointInPolygonResult::IsOutside: - *c1_is_in_c2 = 0; - goto exit; + is_in = 0; + break; + case Clipper2Lib::PointInPolygonResult::IsOn: + /* Cannot decide based on this */ + break; case Clipper2Lib::PointInPolygonResult::IsInside: - *c1_is_in_c2 = 1; - goto exit; + is_in = 1; + break; + } + if(is_in >= 0) break; /* Early exit */ + } + if(is_in == -1) { + /* Every vertex of comp1 is on comp2: comp1 is either equal to comp2, or it + * is inside */ + int is_eq = path_is_eq(comp1, comp2); + if(!is_eq) { + is_in = 1; + } else { + res = RES_BAD_ARG; + goto error; } - break; } - /* Should not be there: component1 is either ill-formed or too thin to host - * a vertex in its inside! */ - res = RES_BAD_ARG; + + * c1_is_in_c2 = is_in; exit: return res; diff --git a/src/test_scpr_is_in.c b/src/test_scpr_is_in.c @@ -0,0 +1,97 @@ +/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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" + +#include <rsys/rsys.h> +#include <rsys/mem_allocator.h> + +#include <memory.h> + + +int +main(int argc, char** argv) +{ + struct mem_allocator allocator; + struct scpr_device_create_args args = SCPR_DEVICE_CREATE_ARGS_DEFAULT; + struct scpr_device* dev; + double c0[] = {0, 5, 8, 5, 8, 9, 0, 9 }; + double c1[] = {1, 1, 3, 1, 3, 9, 1, 9 }; + double c2[] = {3, 6, 5, 6, 5, 8, 4, 7, 3, 8 }; + size_t n0[] = { 4 }; + size_t n1[] = { 4 }; + size_t n2[] = { 5 }; + struct scpr_polygon* p0 = NULL; + struct scpr_polygon* p1 = NULL; + struct scpr_polygon* p2 = NULL; + struct polygon_context context; + double** pos; + int in; + (void)argc, (void)argv; + + mem_init_proxy_allocator(&allocator, &mem_default_allocator); + + pos = (double**)MEM_CALLOC(&allocator, 1, sizeof(*pos)); + *pos = (double*)MEM_CALLOC(&allocator, 10, sizeof(**pos)); + + args.allocator = &allocator; + args.precision = 2; + OK(scpr_device_create(&args, &dev)); + + OK(scpr_polygon_create(dev, &p0)); + OK(scpr_polygon_create(dev, &p1)); + OK(scpr_polygon_create(dev, &p2)); + + context.ncomps = 1; + context.coords = pos; + + context.nverts = n0; + memcpy(*pos, c0, 2*n0[0]*sizeof(**pos)); + OK(scpr_polygon_setup_indexed_vertices(p0, 1, pget_nverts, pget_pos, &context)); + + context.nverts = n1; + memcpy(*pos, c1, 2*n1[0]*sizeof(**pos)); + OK(scpr_polygon_setup_indexed_vertices(p1, 1, pget_nverts, pget_pos, &context)); + + context.nverts = n2; + memcpy(*pos, c2, 2*n2[0]*sizeof(**pos)); + OK(scpr_polygon_setup_indexed_vertices(p2, 1, pget_nverts, pget_pos, &context)); + + BAD(scpr_is_component_in_component(NULL, 0, NULL, 0, NULL)); + OK(scpr_is_component_in_component(p1, 0, p2, 0, &in)); + CHK(in == 0); + OK(scpr_is_component_in_component(p2, 0, p1, 0, &in)); + CHK(in == 0); + OK(scpr_is_component_in_component(p2, 0, p0, 0, &in)); + CHK(in == 1); + OK(scpr_is_component_in_component(p0, 0, p2, 0, &in)); + CHK(in == 0); + + OK(scpr_device_ref_put(dev)); + OK(scpr_polygon_ref_put(p0)); + OK(scpr_polygon_ref_put(p1)); + OK(scpr_polygon_ref_put(p2)); + + MEM_RM(&allocator, *pos); + MEM_RM(&allocator, pos); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHK(mem_allocated_size() == 0); + return 0; +}