star-cpr

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

commit dc8480d36df85762a7c85546cbc38a478f7d8684
parent 51b4479b86f195f7aa0e1fc97a25e851ad591db6
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Mon, 23 Jan 2023 16:34:46 +0100

Make coordinates truncation and range limits explicit

Diffstat:
Msrc/scpr.h | 8++++++++
Msrc/scpr_polygon.c | 68++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Msrc/test_scpr_offset.c | 34++++++++++++++++++++++++++++++++++
3 files changed, 90 insertions(+), 20 deletions(-)

diff --git a/src/scpr.h b/src/scpr.h @@ -81,6 +81,14 @@ SCPR_API res_T scpr_polygon_ref_put (struct scpr_polygon* polygon); +/* To ensure constant precision, vertice coordinates are truncated, and have a + * limited range. Range checking along with the associated truncation process + * occur at vertices setup. + * The range of the coordinates is [-INT64_MAX*10E-6 +INT64_MAX*10E-6], that is + * [-9223372036854.775807 +9223372036854.775807] or approximately + * [-9.2E12 +9.2E12], and vertex coordinates are truncated past the 6th decimal + * place. It is an error to use out-of-range values. + * Truncated coordinates can be retrieved using the appropriate getters. */ SCPR_API res_T scpr_polygon_setup_indexed_vertices (struct scpr_polygon* polygon, diff --git a/src/scpr_polygon.c b/src/scpr_polygon.c @@ -13,6 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "clipper2/clipper.core.h" #include "scpr.h" #include "scpr_c.h" @@ -20,10 +21,24 @@ #include <polygon.h> #include <rsys/mem_allocator.h> #include <rsys/rsys.h> +#include <rsys/double2.h> #undef PI #include <clipper2/clipper.h> +#include <math.h> + +/* Sets the precision parameter, as expected by Clipper2. + * Allowed range is [-8 +8]. + * This parameter defines both the floating point precision for the coordinates + * and the range of the coordinates. + * With a precision of 0, coordinates are truncated to the nearest integer and + * the coordinate range is [-INT64_MAX +INT64_MAX] (that is + * [-9223372036854775807 +9223372036854775807] or ~ [-9.2E18 + 9.2E18]). + * Increasing precision by 1 adds 1 more decimal place to the coordinate + * precision and divides the coordinate range by 10. */ +#define PRECISION 6 + /******************************************************************************* * Helper functions ******************************************************************************/ @@ -185,6 +200,7 @@ scpr_polygon_setup_indexed_vertices void* data) { size_t c; + double scale = pow(10, PRECISION); res_T res = RES_OK; if(!polygon || !get_nverts || !get_position || !data) { @@ -207,10 +223,23 @@ scpr_polygon_setup_indexed_vertices /* Fetch polygon positions for connex component c */ FOR_EACH(i, 0, nverts) { double tmp[2]; + int64_t tmp2[2]; Clipper2Lib::PointD pt; get_position(c, i, tmp, data); - pt.x = tmp[0]; - pt.y = tmp[1]; + /* Truncate precision to ensure further consistency */ + tmp2[0] = std::llround(tmp[0] * scale); + tmp2[1] = std::llround(tmp[1] * scale); + /* Store truncated vertex */ + pt.x = (double)tmp2[0] / scale; + pt.y = (double)tmp2[1] / scale; + if(fabs(tmp[0] - pt.x) > scale) { + res = RES_BAD_ARG; + goto error; + } + if(fabs(tmp[1] - pt.y) * scale > 1) { + res = RES_BAD_ARG; + goto error; + } polygon->paths[c][i] = pt; d2_min(polygon->lower, polygon->lower, tmp); @@ -314,6 +343,7 @@ scpr_offset_polygon { size_t c; Clipper2Lib::PathD tmp; + Clipper2Lib::JoinType cjt; Clipper2Lib::PathsD polygon; res_T res = RES_OK; @@ -334,24 +364,22 @@ scpr_offset_polygon } /* Some known problems when offset=0, not leaving polygon unchanged */ - if(offset != 0) { - Clipper2Lib::JoinType cjt = scpr_join_type_to_clipper_join_type(join_type); - poly_desc->paths = Clipper2Lib::InflatePaths(poly_desc->paths, offset, cjt, - Clipper2Lib::EndType::Polygon); - - /* Rebuild AABB */ - 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(); - - FOR_EACH(i, 0, nverts) { - double pos[2]; - ERR(scpr_polygon_get_position(poly_desc, c, i, pos)); - d2_min(poly_desc->lower, poly_desc->lower, pos); - d2_max(poly_desc->upper, poly_desc->upper, pos); - } + 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); + + /* Rebuild AABB */ + 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(); + + FOR_EACH(i, 0, nverts) { + double pos[2]; + ERR(scpr_polygon_get_position(poly_desc, c, i, pos)); + d2_min(poly_desc->lower, poly_desc->lower, pos); + d2_max(poly_desc->upper, poly_desc->upper, pos); } } diff --git a/src/test_scpr_offset.c b/src/test_scpr_offset.c @@ -33,6 +33,18 @@ test_single(void) 2.0, 2.0, 2.0, -1.0 }; + const double coords2[] = { + 0.12345678901234, 0.0, + 0.0, 1.0, + 1.0, 9223372036854, + 1.0, 0.0 + }; + const double coords3[] = { + 9223372036855, 0.0, + 0.0, 1.0, + 1.0, 9223372036854, + 1.0, 0.0 + }; double** coords; size_t nverts[] = { 4 }; size_t ncomps = 1; @@ -93,6 +105,28 @@ test_single(void) CHK(scpr_polygon_eq(polygon, expected, &eq) == RES_OK); CHK(eq); + /* Check consistency with a non representable coordinate */ + memcpy(*coords, coords2, 2*nverts[0]*sizeof(**coords)); + + ncomps = 1; + 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); + + /* 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) + == RES_BAD_ARG); + + /* Cleanup */ CHK(scpr_polygon_ref_put(polygon) == RES_OK); CHK(scpr_polygon_ref_put(expected) == RES_OK);