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:
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);