star-cpr

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

commit 339d4f77b30d883e15469dee20bd3de935715a77
parent 91a905be96b8839148fb4f56f931968c15302dd4
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Thu,  9 Feb 2023 18:37:59 +0100

Add a device to the lib to hold precision

Diffstat:
Mcmake/CMakeLists.txt | 6+++++-
Msrc/scpr.h | 106+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Msrc/scpr_c.h | 63+++++++++++++--------------------------------------------------
Asrc/scpr_device.c | 189+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/scpr_mesh.c | 60+++++++++++++++++++++++++++++++++++-------------------------
Msrc/scpr_polygon.c | 64++++++++++++++++++++++++++++++++++++++++------------------------
Msrc/test_scpr_clip.c | 35++++++++++++++++++++++++-----------
Asrc/test_scpr_device.c | 108+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_scpr_mesh.c | 12+++++++++---
Msrc/test_scpr_offset.c | 126+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Msrc/test_scpr_polygon.c | 51++++++++++++++++++++++++++++++++++++++++++---------
Msrc/test_scpr_utils.h | 14+++++---------
12 files changed, 668 insertions(+), 166 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -51,7 +51,10 @@ set(VERSION_MINOR 2) set(VERSION_PATCH 0) set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) -set(SCPR_FILES_SRC scpr_mesh.c scpr_polygon.c) +set(SCPR_FILES_SRC + scpr_device.c + scpr_mesh.c + scpr_polygon.c) set(SCPR_FILES_INC_API scpr.h) set(SCPR_FILES_INC scpr_c.h) set(SCPR_FILES_DOC COPYING README.md) @@ -93,6 +96,7 @@ if(NOT NO_TEST) add_test(${_name} ${_name}) endfunction() new_test(test_scpr_clip) + new_test(test_scpr_device) 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 @@ -51,25 +51,102 @@ enum scpr_join_type { /* Forward declaration */ struct mem_allocator; -/* Opaque 2D closed polygon data type */ +/* Forward declaration of the star-cpr opaque data types. These data types are + * ref counted. Once created the caller implicitly owns the created data, i.e. + * its reference counter is set to 1. The scpr_<TYPE>_ref_<get|put> functions + * get or release a reference on the data, i.e. they increment or decrement the + * reference counter, respectively. When this counter reaches 0, the object is + * silently destroyed and cannot be used anymore. */ +struct scpr_device; struct scpr_polygon; -/* Opaque 2D mesh data type */ struct scpr_mesh; +/* Input arguments of the scpr_device_create function */ +struct scpr_device_create_args { + struct logger* logger; /* NULL <=> default logger */ + struct mem_allocator* allocator; /* NULL <=> default allocator */ + int verbosity_level; + int precision; /* Number of decimal place preserved; in [0 8] */ +}; +#define SCPR_DEVICE_CREATE_ARGS_DEFAULT__ { \ + NULL, NULL, 1, 6 \ +} +static const struct scpr_device_create_args SCPR_DEVICE_CREATE_ARGS_DEFAULT = + SCPR_DEVICE_CREATE_ARGS_DEFAULT__; + BEGIN_DECLS -/* Polygons can be made of any number of paths. +/******************************************************************************* + * star-scpr Device. It is an handle toward the Stardis library. It manages the + * star-scpr resources. + ******************************************************************************/ +SCPR_API res_T +scpr_device_create + (const struct scpr_device_create_args* args, + struct scpr_device** dev); + +SCPR_API res_T +scpr_device_ref_get + (struct scpr_device* dev); + +SCPR_API res_T +scpr_device_ref_put + (struct scpr_device* dev); + +/* Get the range for polygon and mesh vertices. + * The range depends on the precision parameter of the device and is + * [-2^(46-precision) + 2^(46-precision)]. */ +SCPR_API res_T +scpr_device_get_range + (const struct scpr_device* dev, + double range[2]); + +/* Check if values are in range */ +SCPR_API res_T +scpr_device_in_range + (const struct scpr_device* dev, + const double* values, + const size_t count, + int* in_range); + +/* Scale a set of values. + * Internal representation for vertices in star-cpr is in interger format. + * The process of converting reals to integers is named scaling (and the reverse + * process is named unscaling). */ +SCPR_API res_T +scpr_device_scale + (const struct scpr_device* dev, + const double* values, + const size_t count, + int64_t* scaled); + +/* Unscale a set of values. + * Internal representation for vertices in star-cpr is in interger format. + * The process of converting reals to integers is named scaling (and the reverse + * process is named unscaling). */ +SCPR_API res_T +scpr_device_unscale + (const struct scpr_device* dev, + const int64_t* values, + const size_t count, + double* unscaled); + +/******************************************************************************* + * Type of polygons, as manipulated by star-cpr. + * Polygons can be made of any number of paths and are subject to a range limit + * and a precision (see device). * Polygons inside/outside regions are defined by their winding numbers * considering the Even-Odd convention. - * E.g. a CCW path inside a CW one defines a hole. */ + * E.g. a CCW path inside a CW one defines a hole. + ******************************************************************************/ SCPR_API res_T scpr_polygon_create - (struct mem_allocator* allocator, /* May be NULL <=> Use default allocator */ + (struct scpr_device* dev, struct scpr_polygon** polygon); SCPR_API res_T scpr_polygon_create_copy - (struct mem_allocator* allocator, /* May be NULL <=> Use default allocator */ + (struct scpr_device* dev, const struct scpr_polygon* src_polygon, struct scpr_polygon** polygon); @@ -81,13 +158,7 @@ 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 allowed range is [-1e12 +1e12], and vertex coordinates are truncated - * approximately past the 6th decimal place. It is an error to use out-of-range - * values. - * Also, the number of components and vertices can be changed due to a +/* The number of components and vertices can be changed due to a * simplification process and one should not take for granted that the number of * components and vertices stay as provided at construction time. * The actual counts and coordinates should be retrieved using the appropriate @@ -133,9 +204,14 @@ scpr_polygon_eq const struct scpr_polygon* polygon2, int* is_eq); +/******************************************************************************* + * Type of meshes, as manipulated by star-cpr. + * Meshes can be made of any number of triangles and are subject to a range + * limit and a precision (see device). + ******************************************************************************/ SCPR_API res_T scpr_mesh_create - (struct mem_allocator* allocator, /* May be NULL <=> Use default allocator */ + (struct scpr_device* dev, struct scpr_mesh** mesh); SCPR_API res_T @@ -146,8 +222,6 @@ SCPR_API res_T scpr_mesh_ref_put (struct scpr_mesh* mesh); -/* In a way similar to polygons, meshes have their vertices coordinates range - * limited to [-1e12 1e12] and truncated past the 6th decimal place. */ SCPR_API res_T scpr_mesh_setup_indexed_vertices (struct scpr_mesh* mesh, diff --git a/src/scpr_c.h b/src/scpr_c.h @@ -49,21 +49,6 @@ goto error; \ } -/* 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/4 +INT64_MAX/4] (that is approximately - * [-2.3E18 + 2.3E18]). - * Increasing precision by 1 adds 1 more decimal place to the coordinate - * precision and divides the coordinate range by 10. */ -#define PRECISION 6 - -/* Maximum range is somewhat arbitrarily set at [-1e12 + 1e12]. - * The rationale is that beyond this range accuracy decreases sharply. */ -#define RANGE_MAX 1e12 - struct mem_allocator; struct vertex { int64_t pos[2]; }; @@ -78,12 +63,23 @@ vertex_eq(const struct vertex* a, const struct vertex* b) #define HTABLE_KEY_FUNCTOR_EQ vertex_eq #include <rsys/hash_table.h> +struct scpr_device { + int precision; + double scale; + double inv_scale; + double range[2]; + + ref_T ref; + struct mem_allocator* allocator; + struct logger* logger; +}; + struct scpr_polygon { Clipper2Lib::Paths64 paths; int64_t lower[2], upper[2]; /* Polygon AABB */ ref_T ref; - struct mem_allocator* allocator; + struct scpr_device* device; }; struct scpr_mesh { @@ -91,40 +87,7 @@ struct scpr_mesh { struct darray_size_t indices; ref_T ref; - struct mem_allocator* allocator; + struct scpr_device* device; }; -res_T INLINE -round_and_set_vertex - (const double in[2], - int64_t out[2]) -{ - /* to optimize scaling / descaling precision - * set the scale to a power of double's radix (2) (#25) */ - double scale = pow(2, (int)log2(pow(10, PRECISION)) + 1); - ASSERT(in && out); - /* Check value is in-range */ - if(in[0] < -RANGE_MAX || in[0] > RANGE_MAX - || in[1] < -RANGE_MAX || in[1] > RANGE_MAX) - { - return RES_BAD_ARG; - } - out[0] = std::llround(in[0] * scale); - out[1] = std::llround(in[1] * scale); - return RES_OK; -} - -void INLINE -get_vertex - (const int64_t in[2], - double out[2]) -{ - /* to optimize scaling / descaling precision - * set the scale to a power of double's radix (2) (#25) */ - double inv_scale = pow(2, -(int)log2(pow(10, PRECISION)) - 1); - ASSERT(in && out); - out[0] = (double)in[0] * inv_scale; - out[1] = (double)in[1] * inv_scale; -} - #endif diff --git a/src/scpr_device.c b/src/scpr_device.c @@ -0,0 +1,189 @@ +/* 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/>. */ + +#include "scpr.h" +#include "scpr_c.h" + +#include <new> +#include <polygon.h> +#include <rsys/mem_allocator.h> +#include <rsys/logger.h> +#include <rsys/rsys.h> +#include <rsys/double2.h> + +#undef PI +#include <clipper2/clipper.h> + +#include <math.h> + +#ifdef OS_UNIX + /* On UNIX assume a VT100-like terminal emulator */ + #define MSG_INFO_PREFIX "star-cpr (\x1b[1m\x1b[32minfo\x1b[0m): " + #define MSG_ERROR_PREFIX "star-cpr (\x1b[1m\x1b[31merror\x1b[0m): " + #define MSG_WARNING_PREFIX "star-cpr (\x1b[1m\x1b[33mwarning\x1b[0m): " +#else + #define MSG_INFO_PREFIX "star-cpr (info): " + #define MSG_ERROR_PREFIX "star-cpr (error): " + #define MSG_WARNING_PREFIX "star-cpr (warning): " +#endif + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static void +device_release(ref_T* ref) +{ + struct scpr_device* device; + struct mem_allocator* allocator; + ASSERT(ref); + device = CONTAINER_OF(ref, struct scpr_device, ref); + allocator = device->allocator; + MEM_RM(allocator, device); +} + +int INLINE +is_in_range + (const double x, + const double range[2]) +{ + ASSERT(range); + return range[0] <= x && x <= range[1]; +} + +/******************************************************************************* + * Exported functions + ******************************************************************************/ +res_T +scpr_device_create + (const struct scpr_device_create_args* args, + struct scpr_device** out_device) +{ + struct scpr_device* device = NULL; + struct mem_allocator* allocator = NULL; + res_T res = RES_OK; + + if(!args || !out_device || args->precision < 0 || args->precision > 8) { + res = RES_BAD_ARG; + goto error; + } + + allocator = args->allocator ? args->allocator : &mem_default_allocator; + device = (struct scpr_device*)MEM_CALLOC(allocator, 1, sizeof(struct scpr_device)); + if(!device) { + res = RES_MEM_ERR; + goto error; + } + ref_init(&device->ref); + device->allocator = allocator; + device->logger = args->logger ? args->logger : LOGGER_DEFAULT; + device->precision = args->precision; + /* to optimize scaling / descaling precision + * set the scale to a power of double's radix (2) (#25) */ + device->scale = pow(2, (int)log2(pow(10, device->precision)) + 1); + device->inv_scale = 1/device->scale; + device->range[1] = pow(2, 46 - device->precision); + device->range[0] = -device->range[1]; + +exit: + if(out_device) *out_device = device; + return res; + +error: + if(device) { + SCPR(device_ref_put(device)); + device = NULL; + } + goto exit; +} + +res_T +scpr_device_ref_get + (struct scpr_device* device) +{ + if(!device) return RES_BAD_ARG; + ref_get(&device->ref); + return RES_OK; +} + +res_T +scpr_device_ref_put + (struct scpr_device* device) +{ + if(!device) return RES_BAD_ARG; + ref_put(&device->ref, device_release); + return RES_OK; +} + +res_T +scpr_device_get_range + (const struct scpr_device* dev, + double range[2]) +{ + if(!dev || !range) return RES_BAD_ARG; + d2_set(range, dev->range); + return RES_OK; +} + +res_T +scpr_device_in_range + (const struct scpr_device* dev, + const double* values, + const size_t count, + int* in_range) + +{ + size_t i; + int in = 1; + if(!dev || !values || !in_range) return RES_BAD_ARG; + for(i = 0; i < count; i++) { + if(!is_in_range(values[i], dev->range)) { + in = 0; + break; + } + } + *in_range = in; + return RES_OK; +} + +res_T +scpr_device_scale + (const struct scpr_device* dev, + const double* values, + const size_t count, + int64_t* scaled) +{ + size_t i; + if(!dev || !values || !scaled) return RES_BAD_ARG; + for(i = 0; i < count; i++) { + if(!is_in_range(values[i], dev->range)) return RES_BAD_ARG; + scaled[i] = std::llround(values[i] * dev->scale); + } + return RES_OK; +} + +res_T +scpr_device_unscale + (const struct scpr_device* dev, + const int64_t* values, + const size_t count, + double* unscaled) +{ + size_t i; + if(!dev || !values || !unscaled) return RES_BAD_ARG; + for(i = 0; i < count; i++) { + unscaled[i] = (double)values[i] * dev->inv_scale; + } + return RES_OK; +} diff --git a/src/scpr_mesh.c b/src/scpr_mesh.c @@ -140,11 +140,12 @@ error: static res_T register_paths - (const Clipper2Lib::Paths64& paths, + (struct scpr_device* dev, + const Clipper2Lib::Paths64& paths, struct darray_int64* coords, /* Vertex buffer */ struct darray_size_t* indices, /* Index buffer */ struct htable_vertex* vertices, /* Map a vertex to its index */ - struct polygon* polygon) /* Use to triangulate the clipped polygons */ + struct polygon* polygon) /* Used to triangulate the clipped polygons */ { size_t ivert; size_t ipath; @@ -173,7 +174,7 @@ register_paths pos64[0] = paths[ipath][ivert].x; pos64[1] = paths[ipath][ivert].y; - get_vertex(pos64, posd); + SCPR(device_unscale(dev, pos64, 2, posd)); fpos[0] = (float)posd[0], fpos[1] = (float)posd[1]; ERR(polygon_vertex_add(polygon, fpos)); @@ -187,7 +188,7 @@ register_paths POLYGON(vertex_get(polygon, ids[ivert], fpos)); pos[0] = (double)fpos[0]; pos[1] = (double)fpos[1]; - ERR(round_and_set_vertex(pos, pos64)); + SCPR(device_scale(dev, pos, 2, pos64)); ERR(register_vertex(pos64, coords, indices, vertices)); } } @@ -199,7 +200,10 @@ error: } static void -mesh_compute_aabb(const struct scpr_mesh* mesh, int64_t lower[2], int64_t upper[2]) +mesh_compute_aabb + (const struct scpr_mesh* mesh, + int64_t lower[2], + int64_t upper[2]) { size_t itri, ntris, i; @@ -214,7 +218,7 @@ mesh_compute_aabb(const struct scpr_mesh* mesh, int64_t lower[2], int64_t upper[ double pos[2]; int64_t pos64[2]; SCPR(mesh_get_position(mesh, ids[ivert], pos)); - CHK(RES_OK == round_and_set_vertex(pos, pos64)); + SCPR(device_scale(mesh->device, pos, 2, pos64)); for(i = 0; i < 2; i++) { if(lower[i] > pos64[i]) lower[i] = pos64[i]; if(upper[i] < pos64[i]) upper[i] = pos64[i]; @@ -227,39 +231,43 @@ static void mesh_release(ref_T* ref) { struct scpr_mesh* mesh; + struct mem_allocator* allocator; ASSERT(ref); mesh = CONTAINER_OF(ref, struct scpr_mesh, ref); + allocator = mesh->device->allocator; + SCPR(device_ref_put(mesh->device)); darray_int64_release(&mesh->coords); darray_size_t_release(&mesh->indices); - MEM_RM(mesh->allocator, mesh); + MEM_RM(allocator, mesh); } /******************************************************************************* * Exported functions ******************************************************************************/ res_T -scpr_mesh_create(struct mem_allocator* mem_allocator, struct scpr_mesh** out_mesh) +scpr_mesh_create + (struct scpr_device* dev, + struct scpr_mesh** out_mesh) { struct scpr_mesh* mesh = NULL; - struct mem_allocator* allocator = NULL; res_T res = RES_OK; - if(!out_mesh) { + if(!dev || !out_mesh) { res = RES_BAD_ARG; goto error; } - allocator = mem_allocator ? mem_allocator : &mem_default_allocator; mesh = (struct scpr_mesh*) - MEM_CALLOC(allocator, 1, sizeof(struct scpr_mesh)); + MEM_CALLOC(dev->allocator, 1, sizeof(struct scpr_mesh)); if(!mesh) { res = RES_MEM_ERR; goto error; } - darray_int64_init(mesh->allocator, &mesh->coords); - darray_size_t_init(mesh->allocator, &mesh->indices); ref_init(&mesh->ref); - mesh->allocator = allocator; + mesh->device = dev; + SCPR(device_ref_get(dev)); + darray_int64_init(dev->allocator, &mesh->coords); + darray_size_t_init(dev->allocator, &mesh->indices); exit: if(out_mesh) *out_mesh = mesh; @@ -316,7 +324,7 @@ scpr_mesh_setup_indexed_vertices FOR_EACH(i, 0, nverts) { double posd[2]; get_position(i, posd, data); - ERR(round_and_set_vertex(posd, pos + i*2)); + ERR(scpr_device_scale(mesh->device, posd, 2, pos + i*2)); } /* Fetch mesh indices */ @@ -386,7 +394,7 @@ scpr_mesh_get_position if(ivert >= nverts) return RES_BAD_ARG; pos64[0] = darray_int64_cdata_get(&mesh->coords)[i+0]; pos64[1] = darray_int64_cdata_get(&mesh->coords)[i+1]; - get_vertex(pos64, pos); + SCPR(device_unscale(mesh->device, pos64, 2, pos)); return RES_OK; } @@ -407,6 +415,7 @@ scpr_mesh_clip Clipper2Lib::Path64 tmp; Clipper2Lib::ClipType clip_type; /* Type of clipping to perform */ size_t itri, ntris, ivert; + struct mem_allocator* allocator; int i; res_T res = RES_OK; @@ -414,11 +423,12 @@ scpr_mesh_clip if(!mesh || !poly_desc || (unsigned)op >= SCPR_OPERATIONS_COUNT__) return RES_BAD_ARG; + allocator = mesh->device->allocator; clip_type = scpr_operation_to_clip_type(op); - darray_int64_init(mesh->allocator, &coords); - darray_size_t_init(mesh->allocator, &indices); - htable_vertex_init(mesh->allocator, &vertices); + darray_int64_init(allocator, &coords); + darray_size_t_init(allocator, &indices); + htable_vertex_init(allocator, &vertices); if(aabb_is_degenerated(poly_desc->lower, poly_desc->upper)) goto exit; @@ -432,7 +442,7 @@ scpr_mesh_clip } /* Create the polygon structure used to triangulate the clipped polygons */ - ERR(polygon_create(mesh->allocator, &polygon)); + ERR(polygon_create(allocator, &polygon)); /* Clip the triangles of the mesh */ SCPR(mesh_get_triangles_count(mesh, &ntris)); @@ -446,9 +456,9 @@ scpr_mesh_clip SCPR(mesh_get_position(mesh, ids[0], tri[0])); SCPR(mesh_get_position(mesh, ids[1], tri[1])); SCPR(mesh_get_position(mesh, ids[2], tri[2])); - ERR(round_and_set_vertex(tri[0], tri64[0])); - ERR(round_and_set_vertex(tri[1], tri64[1])); - ERR(round_and_set_vertex(tri[2], tri64[2])); + SCPR(device_scale(mesh->device, tri[0], 2, tri64[0])); + SCPR(device_scale(mesh->device, tri[1], 2, tri64[1])); + SCPR(device_scale(mesh->device, tri[2], 2, tri64[2])); triangle_compute_aabb(tri64, lower, upper); /* Do not clip triangles that do not intersect the clip AABB */ @@ -477,7 +487,7 @@ scpr_mesh_clip clipper.Execute(clip_type, Clipper2Lib::FillRule::EvenOdd, output); /* Register the resulting clipped polygons */ - ERR(register_paths (output, &coords, &indices, &vertices, polygon)); + ERR(register_paths(mesh->device, output, &coords, &indices, &vertices, polygon)); } ERR(darray_int64_copy_and_clear(&mesh->coords, &coords)); diff --git a/src/scpr_polygon.c b/src/scpr_polygon.c @@ -18,6 +18,7 @@ #include <new> #include <polygon.h> +#include <rsys/logger.h> #include <rsys/mem_allocator.h> #include <rsys/rsys.h> #include <rsys/double2.h> @@ -47,11 +48,14 @@ static void polygon_release(ref_T* ref) { struct scpr_polygon* polygon; + struct mem_allocator* allocator; ASSERT(ref); polygon = CONTAINER_OF(ref, struct scpr_polygon, ref); + allocator = polygon->device->allocator; + SCPR(device_ref_put(polygon->device)); /* Call destructor for paths */ polygon->paths.Clipper2Lib::Paths64::~Paths64(); - MEM_RM(polygon->allocator, polygon); + MEM_RM(allocator, polygon); } static int @@ -110,27 +114,26 @@ one_path_is_eq ******************************************************************************/ res_T scpr_polygon_create - (struct mem_allocator* mem_allocator, + (struct scpr_device* dev, struct scpr_polygon** out_polygon) { struct scpr_polygon* polygon = NULL; - struct mem_allocator* allocator = NULL; res_T res = RES_OK; - if(!out_polygon) { + if(!dev || !out_polygon) { res = RES_BAD_ARG; goto error; } - allocator = mem_allocator ? mem_allocator : &mem_default_allocator; polygon = (struct scpr_polygon*) - MEM_CALLOC(allocator, 1, sizeof(struct scpr_polygon)); + MEM_CALLOC(dev->allocator, 1, sizeof(struct scpr_polygon)); if(!polygon) { res = RES_MEM_ERR; goto error; } ref_init(&polygon->ref); - polygon->allocator = allocator; + polygon->device = dev; + SCPR(device_ref_get(dev)); /* Allocate paths the C++ way (placement new) */ new (&polygon->paths) Clipper2Lib::PathsD; polygon->lower[0] = polygon->lower[1] = INT64_MAX; @@ -176,10 +179,9 @@ scpr_polygon_setup_indexed_vertices void* data) { size_t c; - /* to optimize scaling / descaling precision - * set the scale to a power of double's radix (2) (#25) */ - double inv_scale = pow(2, -(int)log2(pow(10, PRECISION)) - 1); + struct scpr_device* dev; Clipper2Lib::Paths64 paths; + int changedc, changedv = 0; res_T res = RES_OK; if(!polygon || !get_nverts || !get_position || !data) { @@ -187,6 +189,7 @@ scpr_polygon_setup_indexed_vertices goto error; } + dev = polygon->device; TRY(paths.resize(ncomponents)); FOR_EACH(c, 0, ncomponents) { @@ -203,7 +206,7 @@ scpr_polygon_setup_indexed_vertices Clipper2Lib::Point64 pt; get_position(c, i, tmp, data); /* Check range and truncate precision to ensure further consistency */ - ERR(round_and_set_vertex(tmp, tmp64)); + ERR(scpr_device_scale(dev, tmp, 2, tmp64)); pt.x = tmp64[0]; pt.y = tmp64[1]; paths[c][i] = pt; @@ -211,8 +214,19 @@ scpr_polygon_setup_indexed_vertices } /* Merge vertices, ... */ - TRY(paths = Clipper2Lib::SimplifyPaths(paths, inv_scale)); + TRY(paths = Clipper2Lib::SimplifyPaths(paths, dev->inv_scale)); polygon->paths = std::move(paths); + changedc = (ncomponents != polygon->paths.size()); + for(c = 0; !changedv && c < paths.size(); c++) { + size_t nv; + get_nverts(c, &nv, data); + changedv |= (nv != polygon->paths[c].size()); + } + if(changedc || changedv) { + /* TODO: emit a warning log */ + logger_print(dev->logger, LOG_WARNING, + "Polygon has been simplified. Original counts are no longer valid.\n"); + } /* Build bounding box */ polygon->lower[0] = polygon->lower[1] = INT64_MAX; @@ -245,7 +259,7 @@ error: res_T scpr_polygon_create_copy - (struct mem_allocator* allocator, + (struct scpr_device* dev, const struct scpr_polygon* src_polygon, struct scpr_polygon** out_polygon) { @@ -253,11 +267,11 @@ scpr_polygon_create_copy int i; res_T res = RES_OK; - if(!src_polygon || !out_polygon) { + if(!dev || !src_polygon || !out_polygon) { res = RES_BAD_ARG; goto error; } - ERR(scpr_polygon_create(allocator, &copy)); + ERR(scpr_polygon_create(dev, &copy)); copy->paths = src_polygon->paths; for(i = 0; i < 2; i++) { @@ -318,7 +332,7 @@ scpr_polygon_get_position pt = &polygon->paths[icomponent][i]; pos64[0] = pt->x; pos64[1] = pt->y; - get_vertex(pos64, pos); + SCPR(device_unscale(polygon->device, pos64, 2, pos)); return RES_OK; } @@ -330,11 +344,10 @@ scpr_offset_polygon { size_t c; Clipper2Lib::Path64 tmp; + struct scpr_device* dev; Clipper2Lib::JoinType cjt; Clipper2Lib::Paths64 polygon; - /* to optimize scaling / descaling precision - * set the scale to a power of double's radix (2) (#25) */ - double scale = pow(2, (int)log2(pow(10, PRECISION)) + 1); + int64_t offset64; res_T res = RES_OK; if(!poly_desc) { @@ -342,12 +355,16 @@ scpr_offset_polygon goto error; } + dev = poly_desc->device; + /* Check offset polygon will be in-range */ if(offset > 0) { int i; + double range[2]; + SCPR(device_get_range(dev, range)); for(i = 0; i < 2; i++) { - if((double)poly_desc->lower[i] - offset < -RANGE_MAX - || (double)poly_desc->upper[i] + offset > RANGE_MAX) + if((double)poly_desc->lower[i] - offset < range[0] + || (double)poly_desc->upper[i] + offset > range[1]) { res = RES_BAD_ARG; goto error; @@ -366,9 +383,10 @@ scpr_offset_polygon goto error; } + ERR(scpr_device_scale(dev, &offset, 1, &offset64)); cjt = scpr_join_type_to_clipper_join_type(join_type); TRY(poly_desc->paths = Clipper2Lib::InflatePaths(poly_desc->paths, - offset * scale, cjt, Clipper2Lib::EndType::Polygon)); + (double)offset64, cjt, Clipper2Lib::EndType::Polygon)); /* Rebuild AABB */ poly_desc->lower[0] = poly_desc->lower[1] = INT64_MAX; @@ -440,5 +458,3 @@ exit: error: goto exit; } - - diff --git a/src/test_scpr_clip.c b/src/test_scpr_clip.c @@ -51,13 +51,15 @@ dump_obj(FILE* stream, const struct scpr_mesh* mesh) static void test_triangle - (struct mem_allocator* allocator, + (struct scpr_device* dev, + struct mem_allocator* allocator, struct scpr_mesh* mesh) { const double triangle_pos1[] = { 0, 0, 0, 1, 1, 0 }; - const double triangle_pos2[] = { 0e20, 0e20, 0e20, 1e20, 1e20, 0e20 }; + double triangle_pos2[] = { 0, 0, 0, 1e20, 1e20, 0}; /* To be replaced */ const size_t triangle_ids[] = { 0, 1, 2 }; double** clip_pos; + double range[2]; size_t nverts[] = { 3 }; size_t ncomps = 1; const double clip_pos0[] = { -1.0, 0.25, 1.0, 0.75, 1, 0.25 }; @@ -66,6 +68,10 @@ test_triangle struct mesh_context mctx; size_t ntris; + /* Set out-of-range value in triangle_pos2 */ + SCPR(device_get_range(dev, range)); + triangle_pos2[3] = triangle_pos2[4] = range[1] + 1; + clip_pos = (double**)MEM_CALLOC(allocator, ncomps, sizeof(*clip_pos)); *clip_pos = (double*)MEM_CALLOC(allocator, nverts[0], 2*sizeof(**clip_pos)); memcpy(*clip_pos, clip_pos0, 2*nverts[0]*sizeof(**clip_pos)); @@ -73,7 +79,7 @@ test_triangle pctx.coords = clip_pos; pctx.nverts = nverts; pctx.ncomps = ncomps; - OK(scpr_polygon_create(allocator, &poly)); + OK(scpr_polygon_create(dev, &poly)); OK(scpr_polygon_setup_indexed_vertices(poly, ncomps, pget_nverts, pget_pos, &pctx)); /* Check out-of-range */ @@ -109,7 +115,8 @@ test_triangle static void test_quad - (struct mem_allocator* allocator, + (struct scpr_device* dev, + struct mem_allocator* allocator, struct scpr_mesh* mesh) { const double quad_pos[] = { 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0 }; @@ -129,7 +136,7 @@ test_quad pctx.coords = clip_pos; pctx.nverts = nverts; pctx.ncomps = ncomps; - OK(scpr_polygon_create(allocator, &poly)); + OK(scpr_polygon_create(dev, &poly)); OK(scpr_polygon_setup_indexed_vertices(poly, ncomps, pget_nverts, pget_pos, &pctx)); mctx.coords = quad_pos; @@ -150,7 +157,8 @@ test_quad static void test_disk - (struct mem_allocator* allocator, + (struct scpr_device* dev, + struct mem_allocator* allocator, struct scpr_mesh* mesh) { double** clip_pos; @@ -220,7 +228,7 @@ test_disk pctx.coords = clip_pos; pctx.nverts = nverts; pctx.ncomps = ncomps; - OK(scpr_polygon_create(allocator, &poly)); + OK(scpr_polygon_create(dev, &poly)); OK(scpr_polygon_setup_indexed_vertices(poly, ncomps, pget_nverts, pget_pos, &pctx)); mctx.coords = pos; @@ -245,18 +253,23 @@ 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; struct scpr_mesh* mesh; (void)argc, (void)argv; mem_init_proxy_allocator(&allocator, &mem_default_allocator); - OK(scpr_mesh_create(&allocator, &mesh)); + args.allocator = &allocator; + OK(scpr_device_create(&args, &dev)); + OK(scpr_mesh_create(dev, &mesh)); - test_triangle(&allocator, mesh); - test_quad(&allocator, mesh); - test_disk(&allocator, mesh); + test_triangle(dev, &allocator, mesh); + test_quad(dev, &allocator, mesh); + test_disk(dev, &allocator, mesh); OK(scpr_mesh_ref_put(mesh)); + OK(scpr_device_ref_put(dev)); check_memory_allocator(&allocator); mem_shutdown_proxy_allocator(&allocator); diff --git a/src/test_scpr_device.c b/src/test_scpr_device.c @@ -0,0 +1,108 @@ +/* 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 <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 r[2]; + const double d[5] = { 0, 1, 2, 3, 4}; + double tmpd[5]; + const int64_t i64[5] = { 0, 1, 2, 3, 4}; + int64_t tmp64[5]; + int in; + (void)argc, (void)argv; + + mem_init_proxy_allocator(&allocator, &mem_default_allocator); + + args.allocator = &allocator; + + BAD(scpr_device_create(NULL, NULL)); + BAD(scpr_device_create(&args, NULL)); + BAD(scpr_device_create(NULL, &dev)); + + args.precision = -1; + BAD(scpr_device_create(&args, &dev)); + + args.precision = SCPR_DEVICE_CREATE_ARGS_DEFAULT.precision; + OK(scpr_device_create(&args, &dev)); + + BAD(scpr_device_ref_get(NULL)); + OK(scpr_device_ref_get(dev)); + + BAD(scpr_device_ref_put(NULL)); + OK(scpr_device_ref_put(dev)); + + BAD(scpr_device_get_range(NULL, NULL)); + BAD(scpr_device_get_range(NULL, r)); + BAD(scpr_device_get_range(dev, NULL)); + OK(scpr_device_get_range(dev, r)); + + BAD(scpr_device_in_range(NULL, NULL, 5, NULL)); + BAD(scpr_device_in_range(NULL, NULL, 5, &in)); + BAD(scpr_device_in_range(NULL, d, 5, NULL)); + BAD(scpr_device_in_range(dev, NULL, 5, NULL)); + BAD(scpr_device_in_range(NULL, d, 5, &in)); + BAD(scpr_device_in_range(dev, NULL, 5, &in)); + BAD(scpr_device_in_range(dev, d, 5, NULL)); + OK(scpr_device_in_range(dev, d, 5, &in)); + CHK(in); + /* With out_of_range value */ + memcpy(tmpd, d, sizeof(d)); + tmpd[3] = r[1] + 1; + OK(scpr_device_in_range(dev, tmpd, 5, &in)); + CHK(!in); + + BAD(scpr_device_scale(NULL, NULL, 5, NULL)); + BAD(scpr_device_scale(NULL, NULL, 5, tmp64)); + BAD(scpr_device_scale(NULL, d, 5, NULL)); + BAD(scpr_device_scale(dev, NULL, 5, NULL)); + BAD(scpr_device_scale(NULL, d, 5, tmp64)); + BAD(scpr_device_scale(dev, NULL, 5, tmp64)); + BAD(scpr_device_scale(dev, d, 5, NULL)); + OK(scpr_device_scale(dev, d, 5, tmp64)); + /* With out_of_range value */ + memcpy(tmpd, d, sizeof(d)); + tmpd[3] = r[1] + 1; + BAD(scpr_device_scale(dev, tmpd, 5, tmp64)); + + BAD(scpr_device_unscale(NULL, NULL, 5, NULL)); + BAD(scpr_device_unscale(NULL, NULL, 5, tmpd)); + BAD(scpr_device_unscale(NULL, i64, 5, NULL)); + BAD(scpr_device_unscale(dev, NULL, 5, NULL)); + BAD(scpr_device_unscale(NULL, i64, 5, tmpd)); + BAD(scpr_device_unscale(dev, NULL, 5, tmpd)); + BAD(scpr_device_unscale(dev, i64, 5, NULL)); + OK(scpr_device_unscale(dev, i64, 5, tmpd)); + + OK(scpr_device_ref_put(dev)); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHK(mem_allocated_size() == 0); + return 0; +} + diff --git a/src/test_scpr_mesh.c b/src/test_scpr_mesh.c @@ -48,6 +48,8 @@ main(int argc, char** argv) size_t ids[3]; double pos[2]; size_t i, n; + struct scpr_device_create_args args = SCPR_DEVICE_CREATE_ARGS_DEFAULT; + struct scpr_device* dev; struct mem_allocator allocator; struct mesh_context ctx; struct scpr_mesh* mesh; @@ -55,9 +57,12 @@ main(int argc, char** argv) mem_init_proxy_allocator(&allocator, &mem_default_allocator); + args.allocator = &allocator; + OK(scpr_device_create(&args, &dev)); + BAD(scpr_mesh_create(NULL, NULL)); - BAD(scpr_mesh_create(&allocator, NULL)); - OK(scpr_mesh_create(NULL, &mesh)); + BAD(scpr_mesh_create(dev, NULL)); + OK(scpr_mesh_create(dev, &mesh)); BAD(scpr_mesh_ref_get(NULL)); OK(scpr_mesh_ref_get(mesh)); @@ -65,7 +70,7 @@ main(int argc, char** argv) OK(scpr_mesh_ref_put(mesh)); OK(scpr_mesh_ref_put(mesh)); - OK(scpr_mesh_create(&allocator, &mesh)); + OK(scpr_mesh_create(dev, &mesh)); ctx.coords = coords; ctx.nverts = nverts; @@ -188,6 +193,7 @@ main(int argc, char** argv) } OK(scpr_mesh_ref_put(mesh)); + OK(scpr_device_ref_put(dev)); check_memory_allocator(&allocator); mem_shutdown_proxy_allocator(&allocator); diff --git a/src/test_scpr_offset.c b/src/test_scpr_offset.c @@ -35,35 +35,102 @@ test_single(void) 2.0, 2.0, 2.0, -1.0 }; - const double coords2[] = { + double coords2[] = { 0.12345678901234, 0.0, 0.0, 1.0, - 1.0, 1000000000000, + 1.0, 1000000000000, /* To be replaced */ 1.0, 0.0 }; - const double coords2_reverse[] = { + double coords2_reverse[] = { 0.12345678901234, 0.0, 1.0, 0.0, - 1.0, 1000000000000, + 1.0, 1000000000000, /* To be replaced */ 0.0, 1.0 }; + double coords3[112] = { + 1454.65, 1289.72, + 1455.91, 1303.38, + 1449.01, 1304.09, + 1449.82, 1316.24, + 1450.17, 1321.71, + 1447.09, 1322.02, + 1447.46, 1327.61, + 1447.90, 1333.96, + 1448.08, 1336.94, + 1448.34, 1341.45, + 1449.84, 1364.03, + 1450.32, 1371.41, + 1450.68, 1376.57, + 1450.96, 1381.84, + 1451.19, 1385.23, + 1451.32, 1387.00, + 1457.16, 1386.79, + 1456.88, 1381.42, + 1464.75, 1380.91, + 1464.39, 1375.95, + 1467.66, 1375.64, + 1470.14, 1375.43, + 1470.05, 1372.20, + 1467.48, 1372.41, + 1467.30, 1370.27, + 1467.48, 1370.27, + 1465.33, 1340.27, + 1478.67, 1339.29, + 1478.40, 1337.46, + 1472.30, 1337.88, + 1472.22, 1337.17, + 1469.30, 1337.38, + 1469.11, 1335.14, + 1454.35, 1336.08, + 1454.17, 1333.45, + 1459.21, 1333.14, + 1460.72, 1333.03, + 1460.45, 1330.30, + 1458.94, 1330.40, + 1458.50, 1324.54, + 1460.17, 1324.33, + 1459.91, 1320.99, + 1461.94, 1320.78, + 1461.66, 1315.12, + 1461.48, 1312.48, + 1460.34, 1312.59, + 1459.17, 1298.72, + 1525.39, 1290.04, + 1528.51, 1314.63, + 1543.99, 1312.59, + 1540.24, 1281.82, + 1528.96, 1283.36, + 1524.59, 1283.96, + 1524.86, 1286.19, + 1522.73, 1286.50, + 1521.85, 1280.52 + }; double** coords; + double range[2]; size_t nverts[] = { 4 }; - size_t ncomps = 1; + size_t count, ncomps = 1; struct mem_allocator allocator; struct polygon_context ctx; struct scpr_polygon* polygon; struct scpr_polygon* expected; + struct scpr_device_create_args args = SCPR_DEVICE_CREATE_ARGS_DEFAULT; + struct scpr_device* dev; int eq; mem_init_proxy_allocator(&allocator, &mem_default_allocator); + args.allocator = &allocator; + OK(scpr_device_create(&args, &dev)); + + /* Set barely in-range value in coords2 */ + SCPR(device_get_range(dev, range)); + coords2[5] = coords2_reverse[5] = range[1]; coords = (double**)MEM_CALLOC(&allocator, ncomps, sizeof(*coords)); *coords = (double*)MEM_CALLOC(&allocator, nverts[0], 2*sizeof(**coords)); memcpy(*coords, coords0, 2*nverts[0]*sizeof(**coords)); - OK(scpr_polygon_create(&allocator, &polygon)); - OK(scpr_polygon_create(&allocator, &expected)); + OK(scpr_polygon_create(dev, &polygon)); + OK(scpr_polygon_create(dev, &expected)); ctx.coords = coords; ctx.nverts = nverts; @@ -76,7 +143,7 @@ test_single(void) OK(scpr_offset_polygon(polygon, 0, SCPR_JOIN_MITER)); OK(scpr_polygon_eq(polygon, expected, &eq)); CHK(eq); - CHK(check_stability(polygon)); + CHK(check_stability(dev, polygon)); /* Offset 1 */ memcpy(*coords, coords1, 2*nverts[0]*sizeof(**coords)); @@ -85,7 +152,7 @@ test_single(void) OK(scpr_offset_polygon(polygon, 1, SCPR_JOIN_MITER)); OK(scpr_polygon_eq(polygon, expected, &eq)); CHK(eq); - CHK(check_stability(polygon)); + CHK(check_stability(dev, polygon)); /* Offset -1: back to original polygon */ memcpy(*coords, coords0, 2*nverts[0]*sizeof(**coords)); @@ -94,14 +161,14 @@ test_single(void) OK(scpr_offset_polygon(polygon, -1, SCPR_JOIN_MITER)); OK(scpr_polygon_eq(polygon, expected, &eq)); CHK(eq); - CHK(check_stability(polygon)); + CHK(check_stability(dev, polygon)); /* Non representable offset: truncation will ensure stability */ OK(scpr_offset_polygon(polygon, 0.123456789, SCPR_JOIN_MITER)); OK(scpr_offset_polygon(expected, 0.123457, SCPR_JOIN_MITER)); OK(scpr_polygon_eq(polygon, expected, &eq)); CHK(eq); - CHK(check_stability(polygon)); + CHK(check_stability(dev, polygon)); /* Offset -5: empty polygon */ ncomps = 0; @@ -111,7 +178,7 @@ test_single(void) OK(scpr_offset_polygon(polygon, -5, SCPR_JOIN_MITER)); OK(scpr_polygon_eq(polygon, expected, &eq)); CHK(eq); - CHK(check_stability(polygon)); + CHK(check_stability(dev, polygon)); /* Check coordinates barely in-range */ memcpy(*coords, coords2, 2*nverts[0]*sizeof(**coords)); @@ -126,7 +193,7 @@ test_single(void) OK(scpr_offset_polygon(polygon, 0, SCPR_JOIN_MITER)); OK(scpr_polygon_eq(polygon, expected, &eq)); CHK(eq); - CHK(check_stability(polygon)); + CHK(check_stability(dev, polygon)); /* Check non-effect of CW/CCW */ memcpy(*coords, coords2_reverse, 2*nverts[0]*sizeof(**coords)); @@ -134,13 +201,26 @@ test_single(void) OK(scpr_offset_polygon(polygon, 0, SCPR_JOIN_MITER)); OK(scpr_polygon_eq(polygon, expected, &eq)); CHK(eq); - CHK(check_stability(polygon)); + CHK(check_stability(dev, polygon)); /* Check out of range after offset being detected */ BAD(scpr_offset_polygon(polygon, 1, SCPR_JOIN_MITER)); + /* Check a polygon that used to produce a wrong result */ + ctx.nverts[0] = 56; + coords[0] = (double*)MEM_REALLOC(&allocator, coords[0], nverts[0]*2*sizeof(**coords)); + memcpy(*coords, coords3, 2 * ctx.nverts[0] * sizeof(**coords)); + OK(scpr_polygon_setup_indexed_vertices(polygon, ncomps, pget_nverts, pget_pos, &ctx)); + OK(scpr_offset_polygon(polygon, -0.2, SCPR_JOIN_MITER)); + OK(scpr_polygon_get_components_count(polygon, &count)); + CHK(count == 1); + OK(scpr_polygon_get_vertices_count(polygon, 0, &count)); + /* Vertice count should remain unchanged */ + CHK(count == ctx.nverts[0]); + /* Cleanup */ OK(scpr_polygon_ref_put(polygon)); + OK(scpr_device_ref_put(dev)); OK(scpr_polygon_ref_put(expected)); MEM_RM(&allocator, *coords); @@ -191,9 +271,13 @@ test_double(void) struct polygon_context ctx; struct scpr_polygon* polygon; struct scpr_polygon* expected; + struct scpr_device_create_args args = SCPR_DEVICE_CREATE_ARGS_DEFAULT; + struct scpr_device* dev; int eq; mem_init_proxy_allocator(&allocator, &mem_default_allocator); + args.allocator = &allocator; + OK(scpr_device_create(&args, &dev)); coords = (double**)MEM_CALLOC(&allocator, ncomps, sizeof(*coords)); *coords = (double*)MEM_CALLOC(&allocator, nverts[0], 2*sizeof(**coords)); @@ -201,8 +285,8 @@ test_double(void) memcpy(*coords, coords0, 2*nverts[0]*sizeof(**coords)); memcpy(*(coords+1), coords1, 2*nverts[1]*sizeof(**coords)); - OK(scpr_polygon_create(&allocator, &polygon)); - OK(scpr_polygon_create(&allocator, &expected)); + OK(scpr_polygon_create(dev, &polygon)); + OK(scpr_polygon_create(dev, &expected)); ctx.coords = coords; ctx.nverts = nverts; @@ -254,6 +338,7 @@ test_double(void) CHK(eq); OK(scpr_polygon_ref_put(polygon)); + OK(scpr_device_ref_put(dev)); OK(scpr_polygon_ref_put(expected)); MEM_RM(&allocator, *coords); @@ -305,9 +390,13 @@ test_internal(void) struct polygon_context ctx; struct scpr_polygon* polygon; struct scpr_polygon* expected; + struct scpr_device_create_args args = SCPR_DEVICE_CREATE_ARGS_DEFAULT; + struct scpr_device* dev; int eq; mem_init_proxy_allocator(&allocator, &mem_default_allocator); + args.allocator = &allocator; + OK(scpr_device_create(&args, &dev)); coords = (double**)MEM_CALLOC(&allocator, ncomps, sizeof(*coords)); *coords = (double*)MEM_CALLOC(&allocator, nverts[0], 2*sizeof(**coords)); @@ -315,8 +404,8 @@ test_internal(void) memcpy(*coords, coords0, 2*nverts[0]*sizeof(**coords)); memcpy(*(coords+1), coords1, 2*nverts[1]*sizeof(**coords)); - OK(scpr_polygon_create(&allocator, &polygon)); - OK(scpr_polygon_create(&allocator, &expected)); + OK(scpr_polygon_create(dev, &polygon)); + OK(scpr_polygon_create(dev, &expected)); ctx.coords = coords; ctx.nverts = nverts; @@ -375,6 +464,7 @@ test_internal(void) OK(scpr_polygon_ref_put(polygon)); OK(scpr_polygon_ref_put(expected)); + OK(scpr_device_ref_put(dev)); MEM_RM(&allocator, *coords); MEM_RM(&allocator, *(coords+1)); diff --git a/src/test_scpr_polygon.c b/src/test_scpr_polygon.c @@ -38,24 +38,27 @@ main(int argc, char** argv) 1.0, 1.0, 0.0, 1.0 }; - const double coords1[] = { + double coords1[] = { 0.0, 0.0, 0.5, 0.0, 0.0, 1.0, 1.0, 1.0, - 1.0, 1000000000001, + 1.0, 1000000000001, /* To be replaced */ 1.0, 0.0 }; double** coords; size_t nverts[] = { 6 }; size_t ncomps = 1; - double pos[2]; + double pos[2], range[2]; size_t i, c, n; struct mem_allocator allocator; struct polygon_context ctx; struct scpr_polygon* polygon; struct scpr_polygon* copy; - int eq; + struct scpr_device* dev; + struct scpr_device_create_args args = SCPR_DEVICE_CREATE_ARGS_DEFAULT; + int eq, in; + double low[2] = {0, 0}, up[2] = {1, 1}; (void)argc, (void)argv; mem_init_proxy_allocator(&allocator, &mem_default_allocator); @@ -64,10 +67,18 @@ main(int argc, char** argv) *coords = (double*)MEM_CALLOC(&allocator, nverts[0], 2*sizeof(**coords)); memcpy(*coords, coords0, 2*nverts[0]*sizeof(**coords)); + args.allocator = &allocator; + args.precision = SCPR_DEVICE_CREATE_ARGS_DEFAULT.precision; + OK(scpr_device_create(&args, &dev)); + + /* Set out-of-range value in coords1 */ + SCPR(device_get_range(dev, range)); + coords1[9] = range[1] + 1; + BAD(scpr_polygon_create(NULL, NULL)); - BAD(scpr_polygon_create(&allocator, NULL)); - OK(scpr_polygon_create(NULL, &polygon)); - OK(scpr_polygon_create(NULL, &copy)); + BAD(scpr_polygon_create(dev, NULL)); + OK(scpr_polygon_create(dev, &polygon)); + OK(scpr_polygon_create(dev, &copy)); OK(scpr_polygon_ref_put(copy)); BAD(scpr_polygon_ref_get(NULL)); @@ -76,7 +87,7 @@ main(int argc, char** argv) OK(scpr_polygon_ref_put(polygon)); OK(scpr_polygon_ref_put(polygon)); - OK(scpr_polygon_create(&allocator, &polygon)); + OK(scpr_polygon_create(dev, &polygon)); OK(scpr_polygon_get_components_count(polygon, &n)); CHK(n == 0); @@ -111,6 +122,27 @@ main(int argc, char** argv) CHK(n == sizeof(coords0_reduced)/(2*sizeof(*coords0_reduced))); #undef SETUP + BAD(scpr_polygon_in_bbox(NULL, NULL, NULL, NULL)); + BAD(scpr_polygon_in_bbox(NULL, NULL, NULL, &in)); + BAD(scpr_polygon_in_bbox(NULL, NULL, up, NULL)); + BAD(scpr_polygon_in_bbox(NULL, NULL, up, &in)); + BAD(scpr_polygon_in_bbox(NULL, low, NULL, NULL)); + BAD(scpr_polygon_in_bbox(NULL, low, NULL, &in)); + BAD(scpr_polygon_in_bbox(NULL, low, up, NULL)); + BAD(scpr_polygon_in_bbox(NULL, low, up, &in)); + BAD(scpr_polygon_in_bbox(polygon, NULL, NULL, NULL)); + BAD(scpr_polygon_in_bbox(polygon, NULL, NULL, &in)); + BAD(scpr_polygon_in_bbox(polygon, NULL, up, NULL)); + BAD(scpr_polygon_in_bbox(polygon, NULL, up, &in)); + BAD(scpr_polygon_in_bbox(polygon, low, NULL, NULL)); + BAD(scpr_polygon_in_bbox(polygon, low, NULL, &in)); + OK(scpr_polygon_in_bbox(polygon, low, up, &in)); + CHK(in); + /* With smaller box */ + up[0] = 0.5; + OK(scpr_polygon_in_bbox(polygon, low, up, &in)); + CHK(!in); + BAD(scpr_polygon_get_components_count(NULL, NULL)); BAD(scpr_polygon_get_components_count(polygon, NULL)); BAD(scpr_polygon_get_components_count(NULL, &n)); @@ -165,7 +197,7 @@ main(int argc, char** argv) BAD(scpr_polygon_create_copy(NULL, NULL, NULL)); BAD(scpr_polygon_create_copy(NULL, NULL, &copy)); BAD(scpr_polygon_create_copy(NULL, polygon, NULL)); - OK(scpr_polygon_create_copy(NULL, polygon, &copy)); + OK(scpr_polygon_create_copy(dev, polygon, &copy)); OK(scpr_polygon_eq(polygon, copy, &eq)); CHK(eq); @@ -174,6 +206,7 @@ main(int argc, char** argv) BAD(scpr_offset_polygon(NULL, 0, SCPR_JOIN_MITER)); OK(scpr_offset_polygon(polygon, 0, SCPR_JOIN_MITER)); + OK(scpr_device_ref_put(dev)); OK(scpr_polygon_ref_put(polygon)); OK(scpr_polygon_ref_put(copy)); diff --git a/src/test_scpr_utils.h b/src/test_scpr_utils.h @@ -101,14 +101,12 @@ check_memory_allocator(struct mem_allocator* allocator) INLINE int check_stability - (const struct scpr_polygon* polygon) + (struct scpr_device* dev, + const struct scpr_polygon* polygon) { res_T res = RES_OK; - /* to optimize scaling / descaling precision - * set the scale to a power of double's radix (2) (#25) */ - double scale = pow(2, (int)log2(pow(10, 6)) + 1); size_t i, j, ccount, vcount; - ASSERT(polygon); + ASSERT(dev && polygon); ERR(scpr_polygon_get_components_count(polygon, &ccount)); @@ -118,10 +116,8 @@ check_stability double pt[2], tmp[2]; int64_t tmp2[2]; ERR(scpr_polygon_get_position(polygon, i, j, pt)); - tmp2[0] = llround(pt[0] * scale); - tmp2[1] = llround(pt[1] * scale); - tmp[0] = (double)tmp2[0] / scale; - tmp[1] = (double)tmp2[1] / scale; + ERR(scpr_device_scale(dev, pt, 2, tmp2)); + ERR(scpr_device_unscale(dev, tmp2, 2, tmp)); if(tmp[0] != pt[0] || tmp[1] != pt[1]) { res = RES_BAD_ARG; goto error;