commit 58b26eeea2b9f865a10b67e9500e8c18f46be5c7
parent 531327dc13e560add3e1e9e9d5e791d70dbca8d5
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date: Wed, 1 Mar 2023 16:11:52 +0100
Merge branch 'release_0.3'
Diffstat:
| M | README.md | | | 12 | +++++++++++- |
| M | cmake/CMakeLists.txt | | | 18 | ++++++++++++++---- |
| D | cmake/ClipperConfig.cmake | | | 74 | -------------------------------------------------------------------------- |
| M | src/scpr.h | | | 193 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------- |
| M | src/scpr_c.h | | | 245 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- |
| A | src/scpr_device.c | | | 189 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/scpr_intersector.c | | | 802 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | src/scpr_mesh.c | | | 254 | +++++++++++++++++++++++++++++++++++++++++++++++-------------------------------- |
| M | src/scpr_polygon.c | | | 330 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------- |
| M | src/test_scpr_clip.c | | | 118 | +++++++++++++++++++++++++++++++++++++++++++++---------------------------------- |
| A | src/test_scpr_device.c | | | 108 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | src/test_scpr_intersector.c | | | 206 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| M | src/test_scpr_mesh.c | | | 213 | +++++++++++++++++++++++++++++++++++++++++-------------------------------------- |
| M | src/test_scpr_offset.c | | | 224 | +++++++++++++++++++++++++++++++++++++++++++------------------------------------ |
| M | src/test_scpr_polygon.c | | | 248 | +++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------- |
| M | src/test_scpr_utils.h | | | 47 | +++++++++++++++++++++++++++++++++++++++++++---- |
16 files changed, 2636 insertions(+), 645 deletions(-)
diff --git a/README.md b/README.md
@@ -11,7 +11,7 @@ clipping polygon.
The library uses [CMake](http://www.cmake.org) and the
[RCMake](https://gitlab.com/vaplv/rcmake/#tab-readme) package to build. It also
depends on the
-[Clipper](http://www.angusj.com/delphi/clipper.php),
+[Clipper2](https://github.com/AngusJohnson/Clipper2),
[Polygon](https://gitlab.Com/vaplv/polygon/#tab-readme) and
[RSys](https://gitlab.com/vaplv/rsys/#tab-readme) libraries.
@@ -22,6 +22,16 @@ project from the `cmake/CMakeLists.txt` file by appending to the
## Release notes
+### Version 0.3
+
+- Add functions to detect polygons intersections. At this stage, only
+ intersections of segments ares detected (neither "vertex on a segment", nor
+ "polygons sharing a vertex" are detected).
+- Add a device to hold library settings; this breaks the whole API.
+- Change internals to use integer representation for polygons.
+- Improve out-of-range detection.
+- Upgrade Clipper2 to 1.2.0
+
### Version 0.2
- Switch Clipper library to Clipper2. This increases requirements on compiler to
diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt
@@ -29,7 +29,7 @@ set(Clipper2_DIR ${_current_source_dir}/)
find_package(RCMake REQUIRED)
find_package(RSys 0.6 REQUIRED)
-find_package(Clipper2 1 REQUIRED)
+find_package(Clipper2 1.2 REQUIRED)
find_package(Polygon 0.0.5 REQUIRED)
include_directories(
@@ -47,11 +47,15 @@ rcmake_append_runtime_dirs(_runtime_dirs RSys Polygon Clipper2)
# Define targets
################################################################################
set(VERSION_MAJOR 0)
-set(VERSION_MINOR 2)
+set(VERSION_MINOR 3)
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_intersector.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)
@@ -69,7 +73,7 @@ set_target_properties(scpr PROPERTIES
target_link_libraries(scpr RSys Polygon Clipper2)
if(CMAKE_COMPILER_IS_GNUCXX)
- set_target_properties(scpr PROPERTIES COMPILE_FLAGS "-Wno-long-long -std=c++17")
+ set_target_properties(scpr PROPERTIES COMPILE_FLAGS "-std=c++17")
endif()
rcmake_setup_devel(scpr StarCPR ${VERSION} star/scpr_version.h)
@@ -85,10 +89,16 @@ if(NOT NO_TEST)
function(new_test _name)
add_executable(${_name} ${SCPR_SOURCE_DIR}/${_name}.c)
+ set_source_files_properties(${SCPR_SOURCE_DIR}/${_name}.c PROPERTIES LANGUAGE C)
+ if(CMAKE_COMPILER_IS_GNUCXX)
+ set_target_properties(${name} PROPERTIES COMPILE_FLAGS "-std=c99")
+ endif()
target_link_libraries(${_name} scpr RSys ${MATH_LIB})
add_test(${_name} ${_name})
endfunction()
new_test(test_scpr_clip)
+ new_test(test_scpr_device)
+ new_test(test_scpr_intersector)
new_test(test_scpr_offset)
new_test(test_scpr_mesh)
new_test(test_scpr_polygon)
diff --git a/cmake/ClipperConfig.cmake b/cmake/ClipperConfig.cmake
@@ -1,74 +0,0 @@
-# Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com)
-#
-# This software is a computer program whose purpose is to generate files
-# used to build the Star-3D library.
-#
-# This software is governed by the CeCILL license under French law and
-# abiding by the rules of distribution of free software. You can use,
-# modify and/or redistribute the software under the terms of the CeCILL
-# license as circulated by CEA, CNRS and INRIA at the following URL
-# "http://www.cecill.info".
-#
-# As a counterpart to the access to the source code and rights to copy,
-# modify and redistribute granted by the license, users are provided only
-# with a limited warranty and the software's author, the holder of the
-# economic rights, and the successive licensors have only limited
-# liability.
-#
-# In this respect, the user's attention is drawn to the risks associated
-# with loading, using, modifying and/or developing or reproducing the
-# software by the user in light of its specific status of free software,
-# that may mean that it is complicated to manipulate, and that also
-# therefore means that it is reserved for developers and experienced
-# professionals having in-depth computer knowledge. Users are therefore
-# encouraged to load and test the software's suitability as regards their
-# requirements in conditions enabling the security of their systems and/or
-# data to be ensured and, more generally, to use and operate it in the
-# same conditions as regards security.
-#
-# The fact that you are presently reading this means that you have had
-# knowledge of the CeCILL license and that you accept its terms.
-
-cmake_minimum_required(VERSION 3.1)
-
-# Try to find the Clipper2 devel. Once done this will define:
-# - Clipper2_FOUND: system has Clipper2
-# - Clipper2_INCLUDE_DIR: the include directory
-# - Clipper2: Link this to use Clipper2
-
-find_path(Clipper2_INCLUDE_DIR polyclipping/clipper.hpp)
-set(Clipper2_INCLUDE_DIR ${Clipper2_INCLUDE_DIR}/polyclipping/)
-
-unset(Clipper2_LIBRARY CACHE)
-unset(Clipper2_LIBRARY_DEBUG CACHE)
-unset(Clipper2_LIBRARY_RELWITHDEBINFO CACHE)
-unset(Clipper2_LIBRARY_MINSIZEREL CACHE)
-find_library(Clipper2_LIBRARY polyclipping
- DOC "Path to the clipper library used during release builds.")
-find_library(Clipper2_LIBRARY_DEBUG polyclipping-dbg
- DOC "Path to the clipper library used during debug builds.")
-
-# Create the imported library target
-if(CMAKE_HOST_WIN32)
- set(_property IMPORTED_IMPLIB)
-else(CMAKE_HOST_WIN32)
- set(_property IMPORTED_LOCATION)
- if(NOT Clipper2_LIBRARY_DEBUG) # Fallback lib
- get_property(_doc CACHE Clipper2_LIBRARY_DEBUG PROPERTY HELPSTRING)
- set(Clipper2_LIBRARY_DEBUG ${Clipper2_LIBRARY} CACHE PATH ${_doc} FORCE)
- endif()
-endif()
-
-add_library(Clipper2 SHARED IMPORTED)
-set_target_properties(Clipper2 PROPERTIES
- ${_property} ${Clipper2_LIBRARY_DEBUG}
- ${_property}_DEBUG ${Clipper2_LIBRARY_DEBUG}
- ${_property}_RELEASE ${Clipper2_LIBRARY})
-
-# Check the package
-include(FindPackageHandleStandardArgs)
-FIND_PACKAGE_HANDLE_STANDARD_ARGS(Clipper2 DEFAULT_MSG
- Clipper2_INCLUDE_DIR
- Clipper2_LIBRARY
- Clipper2_LIBRARY_DEBUG)
-
diff --git a/src/scpr.h b/src/scpr.h
@@ -51,25 +51,129 @@ 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;
+struct scpr_intersector;
+
+/* 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__;
+
+/* A struct to represent segments when calling user callbacks. */
+struct scpr_callback_segment {
+ struct scpr_polygon* polygon;
+ size_t component;
+ size_t first_vertex;
+ size_t last_vertex;
+};
+
+/* A struct holding callbacks that are called when carying an intersection check
+ * on a bunch of segments.
+ * If a callbacks is NULL, the corresponding events are unreported, but at least
+ * 1 callback must be non NULL.
+ * If a callback call returns non-zero, the whole scpr_intersector_check call
+ * exits whith a RES_BAD_ARG error status without further processing. */
+struct scpr_intersector_check_callbacks {
+ int(*simple_intersection)
+ (struct scpr_callback_segment* segment1,
+ struct scpr_callback_segment* segment2,
+ void* ctx);
+ int(*collinear_segments)
+ (struct scpr_callback_segment* segment1,
+ struct scpr_callback_segment* segment2,
+ void* ctx);
+};
+#define SCPR_INTERSECTOR_CHECK_CALLBACKS_NULL__ { NULL, NULL }
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,14 +185,11 @@ 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. */
+/* 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
+ * getters. */
SCPR_API res_T
scpr_polygon_setup_indexed_vertices
(struct scpr_polygon* polygon,
@@ -99,6 +200,13 @@ scpr_polygon_setup_indexed_vertices
void* data); /* Client data set as the last param of the callbacks */
SCPR_API res_T
+scpr_polygon_in_bbox
+ (struct scpr_polygon* polygon,
+ const double lower[2],
+ const double upper[2],
+ int* inside);
+
+SCPR_API res_T
scpr_offset_polygon
(struct scpr_polygon* polygon,
const double offset, /* Can be either positive or negative */
@@ -122,6 +230,8 @@ scpr_polygon_get_position
const size_t ivert,
double position[2]);
+/* Logical comparison for polygons.
+ * Component order and orientation are not considered. */
SCPR_API res_T
scpr_polygon_eq
(const struct scpr_polygon* polygon1,
@@ -129,8 +239,24 @@ scpr_polygon_eq
int* is_eq);
SCPR_API res_T
+scpr_polygon_dump_to_obj
+ (struct scpr_polygon* polygon,
+ FILE* stream);
+
+SCPR_API res_T
+scpr_polygon_dump_component_to_obj
+ (struct scpr_polygon* polygon,
+ const size_t icomponent,
+ FILE* stream);
+
+/*******************************************************************************
+ * 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
@@ -150,7 +276,7 @@ scpr_mesh_setup_indexed_vertices
void (*get_position)(const size_t ivert, double pos[2], void* ctx),
void* data); /* Client data set as the last param of the callbacks */
-/* Clip the mesh against the polygon contour */
+/* Clip the mesh against the polygon using the EvenOdd filling rule. */
SCPR_API res_T
scpr_mesh_clip
(struct scpr_mesh* mesh, /* Candidate mesh to clip */
@@ -179,6 +305,41 @@ scpr_mesh_get_position
const size_t ivert,
double position[2]);
+/*******************************************************************************
+ * An intersector provide a way to check for polygon intersections, either
+ * self intersections or intersections between polygons.
+ ******************************************************************************/
+SCPR_API res_T
+scpr_intersector_create
+ (struct scpr_device* dev,
+ struct scpr_intersector** intersector);
+
+SCPR_API res_T
+scpr_intersector_ref_get
+ (struct scpr_intersector* intersector);
+
+SCPR_API res_T
+scpr_intersector_ref_put
+ (struct scpr_intersector* intersector);
+
+/* Register a polygon's a polygon or a component for further analysis */
+SCPR_API res_T
+scpr_intersector_register_polygon
+ (struct scpr_intersector* intersector,
+ struct scpr_polygon* polygon);
+
+SCPR_API res_T
+scpr_intersector_register_component
+ (struct scpr_intersector* intersector,
+ struct scpr_polygon* polygon,
+ const size_t icomponent);
+
+SCPR_API res_T
+scpr_intersector_check
+ (struct scpr_intersector* intersector,
+ struct scpr_intersector_check_callbacks* callbacks,
+ void* data); /* Client data set as the last param of the callbacks */
+
END_DECLS
#endif /* SCPR_H */
diff --git a/src/scpr_c.h b/src/scpr_c.h
@@ -18,46 +18,269 @@
#include "scpr.h"
+#include <cstddef>
#include <rsys/rsys.h>
#include <rsys/ref_count.h>
#include <rsys/double2.h>
-#include <rsys/dynamic_array_double.h>
-#include <rsys/dynamic_array_size_t.h>
+#include <rsys/dynamic_array.h>
#include <rsys/hash_table.h>
+#include <math.h>
+
#undef PI
#include <clipper2/clipper.h>
+#define DARRAY_NAME uint32
+#define DARRAY_DATA uint32_t
+#include <rsys/dynamic_array.h>
+
+#define DARRAY_NAME int64
+#define DARRAY_DATA int64_t
+#include <rsys/dynamic_array.h>
+
#define ERR(Expr) { if((res = (Expr)) != RES_OK) goto error; } (void)0
+#define TRY(Expr) \
+ try { \
+ (Expr); \
+ } \
+ catch(std::bad_alloc &e) { \
+ res = RES_MEM_ERR; \
+ goto error; \
+ } \
+ catch(...) { \
+ res = RES_UNKNOWN_ERR; \
+ goto error; \
+ }
+
struct mem_allocator;
-struct vertex { double pos[2]; };
+struct vertex { int64_t pos[2]; };
static FINLINE int
vertex_eq(const struct vertex* a, const struct vertex* b)
-{ return d2_eq(a->pos, b->pos); }
+{ ASSERT(a && b); return (a->pos[0] == b->pos[0]) && (a->pos[1] == b->pos[1]); }
/* Define the vertex to index hash table */
#define HTABLE_NAME vertex
-#define HTABLE_DATA size_t
+#define HTABLE_DATA uint32_t
#define HTABLE_KEY struct vertex
#define HTABLE_KEY_FUNCTOR_EQ vertex_eq
#include <rsys/hash_table.h>
-struct scpr_polygon {
- Clipper2Lib::PathsD paths;
- double lower[2], upper[2]; /* Polygon AABB */
+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 scpr_device* device;
};
struct scpr_mesh {
- struct darray_double coords;
- struct darray_size_t indices;
+ struct darray_int64 coords;
+ struct darray_uint32 indices;
ref_T ref;
- struct mem_allocator* allocator;
+ struct scpr_device* device;
+};
+
+struct intersector_segment {
+ uint32_t component, first_vertex;
+};
+#define DARRAY_NAME segments
+#define DARRAY_DATA struct intersector_segment
+#include <rsys/dynamic_array.h>
+
+/* A segment htable links a count (1 or 2) to a segment index in the segments
+ * darray.
+ * The count is the number of ends of the segment at some range_point. */
+#define HTABLE_NAME segment_idx
+#define HTABLE_KEY uint32_t
+#define HTABLE_DATA char
+#include <rsys/hash_table.h>
+
+/* Each range_point stores the segments that have 1 or 2 ends there */
+struct intersector_range_point {
+ struct htable_segment_idx segments;
+ int64_t coord;
+};
+static FINLINE void
+range_point_init
+ (struct mem_allocator* alloc, struct intersector_range_point* data)
+{
+ data->coord = INT64_MIN;
+ htable_segment_idx_init(alloc, &data->segments);
+}
+static FINLINE void
+range_point_release(struct intersector_range_point* data)
+{
+ htable_segment_idx_release(&data->segments);
+}
+static FINLINE res_T
+range_point_copy
+ (struct intersector_range_point* dst, struct intersector_range_point const* src)
+{
+ dst->coord = src->coord;
+ return htable_segment_idx_copy(&dst->segments, &src->segments);
+}
+static FINLINE res_T
+range_point_copy_and_release
+ (struct intersector_range_point* dst, struct intersector_range_point* src)
+{
+ dst->coord = src->coord;
+ return htable_segment_idx_copy_and_release(&dst->segments, &src->segments);
+}
+/* A darray of range_point. */
+#define DARRAY_NAME range_points
+#define DARRAY_DATA struct intersector_range_point
+#define DARRAY_FUNCTOR_INIT range_point_init
+#define DARRAY_FUNCTOR_COPY range_point_copy
+#define DARRAY_FUNCTOR_RELEASE range_point_release
+#define DARRAY_FUNCTOR_COPY_AND_RELEASE range_point_copy_and_release
+#include <rsys/dynamic_array.h>
+
+/* An htable to link a pointer to a range_point to an 1D coordinate */
+#define HTABLE_NAME range_point_idx
+#define HTABLE_KEY int64_t
+#define HTABLE_DATA uint32_t
+#include <rsys/hash_table.h>
+
+enum segment_interaction {
+ NO_INTERACTION = 0,
+ OVERLAP_X = BIT(0),
+ CONTACT_X = BIT(1),
+ OVERLAP_Y = BIT(2),
+ CONTACT_Y = BIT(3),
+ SOMETHING_X = OVERLAP_X | CONTACT_X,
+ SOMETHING_Y = OVERLAP_Y | CONTACT_Y,
+ SOME_OVERLAP = OVERLAP_X | OVERLAP_Y
+};
+/* A dimension is a structure containing all the points where a segment starts
+ * or ends, each one storing a link to all the segments involved there. */
+struct intersector_dimension {
+ /* Unsorted 1D points registered against this dimension */
+ struct htable_range_point_idx unsorted;
+ /* Same points, but sorted by increasing coordinates along this dimension */
+ struct darray_range_points sorted;
+ /* Range on this dimension */
+ int64_t range[2];
+ /* Flags about segments interaction for this dimension */
+ enum segment_interaction overlap, contact;
+};
+
+/* An htable to store overlapping information */
+struct intersector_segment_pair {
+ uint32_t rank1, rank2;
+};
+static INLINE char
+pair_eq
+ (struct intersector_segment_pair const* a,
+ struct intersector_segment_pair const* b)
+{
+ ASSERT(a && b);
+ return a->rank1 == b->rank1 && a->rank2 == b->rank2;
+}
+#define HTABLE_NAME interacting_segments
+#define HTABLE_KEY struct intersector_segment_pair
+#define HTABLE_DATA unsigned char
+#define HTABLE_KEY_FUNCTOR_EQ pair_eq
+#include <rsys/hash_table.h>
+
+/* An htable to keep track of registered components */
+#pragma pack(push, 1) /* Avoid padding */
+struct intersector_component {
+ scpr_polygon* polygon;
+ uint32_t component;
+ /* If there is padding, hashing reports uninitialized data in htable code */
+};
+#pragma pack(pop)
+static FINLINE void
+component_init(struct mem_allocator* alloc, struct intersector_component* key)
+{
+ ASSERT(alloc && key);
+ (void)alloc;
+ key->polygon = NULL;
+ key->component = UINT32_MAX;
+}
+static INLINE char
+component_eq
+ (struct intersector_component const* a,
+ struct intersector_component const* b)
+{
+ ASSERT(a && b);
+ return a->polygon == b->polygon && a->component == b->component;
+}
+static FINLINE void
+component_release(struct intersector_component* key)
+{
+ ASSERT(key);
+ if(key->polygon) SCPR(polygon_ref_put(key->polygon));
+}
+static FINLINE res_T
+component_copy
+ (struct intersector_component* dst, struct intersector_component const* src)
+{
+ ASSERT(dst && src);
+ dst->polygon = src->polygon;
+ dst->component = src->component;
+ if(dst->polygon) SCPR(polygon_ref_get(dst->polygon));
+ return RES_OK;
+}
+static FINLINE res_T
+component_copy_and_release
+ (struct intersector_component* dst, struct intersector_component* src)
+{
+ ASSERT(dst && src);
+ dst->polygon = src->polygon;
+ dst->component = src->component;
+ return RES_OK;
+}
+#define HTABLE_NAME registered_components
+#define HTABLE_KEY struct intersector_component
+#define HTABLE_DATA char
+#define HTABLE_KEY_FUNCTOR_INIT component_init
+#define HTABLE_KEY_FUNCTOR_EQ component_eq
+#define HTABLE_KEY_FUNCTOR_COPY component_copy
+#define HTABLE_KEY_FUNCTOR_RELEASE component_release
+#define HTABLE_KEY_FUNCTOR_COPY_AND_RELEASE component_copy_and_release
+#include <rsys/hash_table.h>
+
+#define DARRAY_NAME components
+#define DARRAY_DATA struct intersector_component
+#define DARRAY_FUNCTOR_INIT component_init
+#define DARRAY_FUNCTOR_EQ component_eq
+#define DARRAY_FUNCTOR_COPY component_copy
+#define DARRAY_FUNCTOR_RELEASE component_release
+#define DARRAY_FUNCTOR_COPY_AND_RELEASE component_copy_and_release
+#include <rsys/dynamic_array.h>
+
+/* A set of segments from different polygons spatially sorted to help find
+ * (self-)intersections. */
+struct scpr_intersector {
+ struct scpr_device* device;
+ /* All the registered segments */
+ struct darray_segments segments;
+ /* Spatial partition of the segments along the 2 dimensions */
+ struct intersector_dimension dimensions[2];
+ /* All the pairs of segment that are in interaction in some way or another.
+ * They are all the candidates for actual intersection plus some others that
+ * are just close, but are without interaction. */
+ struct htable_interacting_segments interacting_segments;
+ /* All the registererd components (an htable to check for unicity and a darray
+ * to store the components with indexing capability) */
+ struct htable_registered_components registererd_components;
+ struct darray_components components;
+ ref_T ref;
};
#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_intersector.c b/src/scpr_intersector.c
@@ -0,0 +1,802 @@
+/* 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 <polygon.h>
+#include <rsys/logger.h>
+#include <rsys/ref_count.h>
+#include <rsys/mem_allocator.h>
+#include <rsys/rsys.h>
+#include <rsys/double2.h>
+#include <rsys/double3.h>
+
+#undef PI
+#include <clipper2/clipper.h>
+
+#include <new>
+#include <stdlib.h>
+#include <math.h>
+
+/*******************************************************************************
+ * Helper functions
+ ******************************************************************************/
+static void
+init_dimension
+ (struct mem_allocator* allocator,
+ struct intersector_dimension* dim,
+ enum segment_interaction overlap,
+ enum segment_interaction share)
+{
+ ASSERT(dim && allocator);
+ htable_range_point_idx_init(allocator, &dim->unsorted);
+ darray_range_points_init(allocator, &dim->sorted);
+ dim->range[0] = INT64_MAX;
+ dim->range[1] = INT64_MIN;
+ dim->overlap= overlap;
+ dim->contact = share;
+}
+
+static void
+release_dimension
+ (struct intersector_dimension* dim)
+{
+ ASSERT(dim);
+ htable_range_point_idx_release(&dim->unsorted);
+ darray_range_points_release(&dim->sorted);
+}
+
+static void
+intersector_release(ref_T* ref)
+{
+ struct scpr_intersector* intersector;
+ struct mem_allocator* allocator;
+ ASSERT(ref);
+ intersector = CONTAINER_OF(ref, struct scpr_intersector, ref);
+ allocator = intersector->device->allocator;
+ darray_segments_release(&intersector->segments);
+ release_dimension(intersector->dimensions);
+ release_dimension(intersector->dimensions + 1);
+ SCPR(device_ref_put(intersector->device));
+ htable_interacting_segments_release(&intersector->interacting_segments);
+ htable_registered_components_release(&intersector->registererd_components);
+ darray_components_release(&intersector->components);
+ MEM_RM(allocator, intersector);
+}
+
+/* Register a point (a segment end) against a dimension */
+static res_T
+register_point
+ (struct scpr_intersector* intersector,
+ const int dim_idx,
+ const int64_t coord,
+ const uint32_t seg_idx)
+{
+ uint32_t* pidx;
+ uint32_t rp_idx;
+ struct intersector_dimension* dim;
+ struct intersector_range_point* sorted;
+ res_T res = RES_OK;
+ ASSERT(intersector);
+
+ dim = intersector->dimensions + dim_idx;
+ pidx = htable_range_point_idx_find(&dim->unsorted, &coord);
+ if(!pidx) {
+ /* First time this point: create it in sorted and store its index in unsorted */
+ char one = 1;
+ size_t count = darray_range_points_size_get(&dim->sorted);
+ ERR(darray_range_points_resize(&dim->sorted, 1+count));
+ sorted = darray_range_points_data_get(&dim->sorted);
+ rp_idx = (uint32_t)count;
+ sorted[rp_idx].coord = coord;
+ /* Register this segment at this range point */
+ ERR(htable_segment_idx_set(&sorted[rp_idx].segments, &seg_idx, &one));
+ /* Update range */
+ if(dim->range[0] > coord) dim->range[0] = coord;
+ if(dim->range[1] < coord) dim->range[1] = coord;
+ } else {
+ /* This point is already known: look for seg_idx already there */
+ char *found, one_or_two;
+ rp_idx = *pidx;
+ sorted = darray_range_points_data_get(&dim->sorted);
+ found = htable_segment_idx_find(&sorted[rp_idx].segments, &seg_idx);
+ one_or_two = found ? 2 : 1;
+ ERR(htable_segment_idx_set(&sorted[rp_idx].segments, &seg_idx, &one_or_two));
+ }
+ ERR(htable_range_point_idx_set(&dim->unsorted, &coord, &rp_idx));
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+/* Compare 2 range_points */
+static int
+compare_range_points(const void* a, const void* b)
+{
+ struct intersector_range_point *pa = (struct intersector_range_point*)a;
+ struct intersector_range_point *pb = (struct intersector_range_point*)b;
+ ASSERT(a && b && pa->coord != pb->coord);
+ return (int)(pa->coord - pb->coord);
+}
+
+/* Sort the range points by increasing coordinates */
+static void
+sort_points
+ (struct intersector_dimension* dim)
+{
+ size_t count;
+ struct intersector_range_point* sorted;
+ ASSERT(dim);
+
+ /* All the range points are already in sorted; just need to sort them */
+ count = darray_range_points_size_get(&dim->sorted);
+ ASSERT(count == htable_range_point_idx_size_get(&dim->unsorted));
+ sorted = darray_range_points_data_get(&dim->sorted);
+ qsort(sorted, count, sizeof(*sorted), compare_range_points);
+}
+
+/* Create a segment pair suitable for use as an htable key
+ * To ensure unicity, pairs are ordered */
+static void
+init_segment_pair
+ (struct intersector_segment_pair* pair,
+ const uint32_t seg_idx1,
+ const uint32_t seg_idx2)
+{
+ ASSERT(pair);
+ pair->rank1 = seg_idx1;
+ pair->rank2 = seg_idx2;
+ if(pair->rank1 > pair->rank2) {
+ SWAP(uint32_t, pair->rank1, pair->rank2);
+ }
+}
+
+#ifndef NDEBUG
+static int
+segments_bbox_interacts_1_core
+ (int64_t lower1, int64_t upper1, int64_t lower2, int64_t upper2)
+{
+ if(lower1 > upper2) return 0;
+ if(lower2 > upper1) return 0;
+ return 1;
+}
+
+/* Check if 2 polygon Bbox interact */
+static int
+segments_bbox_interacts
+ (Clipper2Lib::Point64 *v11,
+ Clipper2Lib::Point64 *v12,
+ Clipper2Lib::Point64 *v21,
+ Clipper2Lib::Point64 *v22)
+{
+ int64_t lower1[2], upper1[2], lower2[2], upper2[2];
+ ASSERT(v11 && v12 && v21 && v22);
+ lower1[0] = MMIN(v11->x, v12->x);
+ lower1[1] = MMIN(v11->y, v12->y);
+ lower2[0] = MMIN(v21->x, v22->x);
+ lower2[1] = MMIN(v21->y, v22->y);
+ upper1[0] = MMAX(v11->x, v12->x);
+ upper1[1] = MMAX(v11->y, v12->y);
+ upper2[0] = MMAX(v21->x, v22->x);
+ upper2[1] = MMAX(v21->y, v22->y);
+ if(!segments_bbox_interacts_1_core(lower1[0], upper1[0], lower2[0], upper2[0]))
+ return 0;
+ return segments_bbox_interacts_1_core(lower1[1], upper1[1], lower2[1], upper2[1]);
+}
+
+static int
+segments_bbox_interacts_1
+ (uint32_t seg_idx1,
+ uint32_t seg_idx2,
+ enum segment_interaction inter,
+ struct scpr_intersector* intersector)
+{
+ const struct intersector_segment *segments, *seg1, *seg2;
+ Clipper2Lib::Point64 *v11, * v12, *v21, *v22;
+ int64_t lower1, upper1, lower2, upper2;
+ size_t count, r11, r12, r21, r22;
+ const struct intersector_component* components;
+ struct scpr_polygon *polygon1, *polygon2;
+ uint32_t comp_idx1, comp_idx2;
+
+ segments = darray_segments_cdata_get(&intersector->segments);
+ seg1 = segments + seg_idx1;
+ seg2 = segments + seg_idx2;
+ components = darray_components_cdata_get(&intersector->components);
+ comp_idx1 = components[seg1->component].component;
+ comp_idx2 = components[seg2->component].component;
+ polygon1 = components[seg1->component].polygon;
+ polygon2 = components[seg2->component].polygon;
+
+ /* Get vertices */
+ r11 = seg1->first_vertex;
+ SCPR(polygon_get_vertices_count(polygon1, comp_idx1, &count));
+ r12 = (r11 + 1) % count;
+ r21 = seg2->first_vertex;
+ SCPR(polygon_get_vertices_count(polygon2, comp_idx2, &count));
+ r22 = (r21 + 1) % count;
+ v11 = &polygon1->paths[comp_idx1][r11];
+ v12 = &polygon1->paths[comp_idx1][r12];
+ v21 = &polygon2->paths[comp_idx2][r21];
+ v22 = &polygon2->paths[comp_idx2][r22];
+ if(inter & SOMETHING_X) {
+ lower1 = MMIN(v11->x, v12->x);
+ upper1 = MMAX(v11->x, v12->x);
+ lower2 = MMIN(v21->x, v22->x);
+ upper2 = MMAX(v21->x, v22->x);
+ } else {
+ lower1 = MMIN(v11->y, v12->y);
+ upper1 = MMAX(v11->y, v12->y);
+ lower2 = MMIN(v21->y, v22->y);
+ upper2 = MMAX(v21->y, v22->y);
+ }
+ return segments_bbox_interacts_1_core(lower1, upper1, lower2, upper2);
+}
+#endif
+
+/* Register interaction of 2 segments along 1 dimension */
+static res_T
+register_segment_interaction
+ (const uint32_t seg_idx1,
+ const uint32_t seg_idx2,
+ const int allow_pair_creation,
+ const enum segment_interaction inter,
+ struct scpr_intersector* intersector)
+{
+ res_T res = RES_OK;
+ struct intersector_segment_pair pair;
+ unsigned char *pinteract;
+
+ ASSERT(intersector);
+ if(seg_idx1 != seg_idx2) {
+ ASSERT(segments_bbox_interacts_1(seg_idx1, seg_idx2, inter, intersector));
+
+ /* Fill a record for this pair of segments */
+ init_segment_pair(&pair, seg_idx1, seg_idx2);
+ pinteract =
+ htable_interacting_segments_find(&intersector->interacting_segments, &pair);
+ if(pinteract || allow_pair_creation) {
+ unsigned char interact;
+ if(!pinteract) { /* First occurence of this pair: create empty record */
+ interact = (unsigned char)inter;
+ } else {
+ ASSERT((*pinteract | inter) < UCHAR_MAX);
+ interact = (unsigned char)(*pinteract | inter);
+ }
+
+ /* Register this interaction */
+ ERR(htable_interacting_segments_set(&intersector->interacting_segments,
+ &pair, &interact));
+ }
+ }
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+/* Register interaction of segment of seg_idx with all the segments in
+ * open_segments along 1 dimension */
+static res_T
+register_segment_interactions
+ (const uint32_t seg_idx,
+ struct htable_segment_idx* open_segments,
+ const int allow_pair_creation,
+ const enum segment_interaction inter,
+ struct scpr_intersector* intersector)
+{
+ res_T res = RES_OK;
+ struct htable_segment_idx_iterator it, end;
+
+ ASSERT(open_segments && intersector);
+
+ htable_segment_idx_begin(open_segments, &it);
+ htable_segment_idx_end(open_segments, &end);
+ /* Loop on segments using this point */
+ while(!htable_segment_idx_iterator_eq(&it, &end)) {
+ uint32_t seg2_idx = *htable_segment_idx_iterator_key_get(&it);
+ ERR(register_segment_interaction(seg_idx, seg2_idx, allow_pair_creation,
+ inter, intersector));
+ htable_segment_idx_iterator_next(&it);
+ }
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+/* Register segments interaction along 1 dimension */
+static res_T
+register_interaction
+ (const int dim_idx,
+ struct scpr_intersector* intersector)
+{
+ res_T res = RES_OK;
+ size_t count, i;
+ struct intersector_range_point* points;
+ struct htable_segment_idx open_segments;
+ struct mem_allocator* allocator;
+ struct intersector_dimension* dimension;
+ char *begins = NULL, *ends = NULL;
+ uint32_t* seg_index = NULL;
+ ASSERT(intersector);
+
+ allocator = intersector->device->allocator;
+ dimension = intersector->dimensions + dim_idx;
+ htable_segment_idx_init(allocator, &open_segments);
+ count = darray_range_points_size_get(&dimension->sorted);
+ points = darray_range_points_data_get(&dimension->sorted);
+ for(i = 0; i < count; i++) {
+ struct intersector_range_point* point = points + i;
+ struct htable_segment_idx_iterator it, end;
+ size_t scount, s;
+
+ /* 0) Init begins, ends and seg_index
+ * Cannot recompute them on the fly as found will change */
+ scount = htable_segment_idx_size_get(&point->segments);
+ begins = (char*)MEM_REALLOC(allocator, begins, scount * sizeof(*begins));
+ ends = (char*)MEM_REALLOC(allocator, ends, scount * sizeof(*ends));
+ seg_index = (uint32_t*)MEM_REALLOC(allocator, seg_index,
+ scount * sizeof(*seg_index));
+ if(!begins || !ends || !seg_index) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ s = 0;
+ htable_segment_idx_begin(&point->segments, &it);
+ htable_segment_idx_end(&point->segments, &end);
+ while(!htable_segment_idx_iterator_eq(&it, &end)) {
+ char c = *htable_segment_idx_iterator_data_get(&it);
+ uint32_t seg_idx = *htable_segment_idx_iterator_key_get(&it);
+ if(c == 2) { /* Both ends of the segment at this point */
+ ends[s] = begins[s] = 1;
+ } else {
+ char* found = htable_segment_idx_find(&open_segments, &seg_idx);
+ begins[s] = (found == NULL);
+ ends[s] = (found != NULL);
+ }
+ seg_index[s] = seg_idx;
+ s++;
+ htable_segment_idx_iterator_next(&it);
+ }
+ ASSERT(s == scount);
+
+ /* 1) Segments beginning and ending at this point overlap other segments that
+ * begin and end at the same point and contact segments that only begin or
+ * only end at this point */
+ for(s = 0; s < scount; s++) {
+ if(begins[s] && ends[s]) {
+ size_t j;
+ for(j = 0; j < scount; j++) {
+ if(j > s && begins[j] && ends[j]) {
+ ERR(register_segment_interaction(seg_index[s], seg_index[j], !dim_idx,
+ dimension->overlap, intersector));
+ }
+ else if(begins[j] ^ ends[j]) {
+ ERR(register_segment_interaction(seg_index[s], seg_index[j], !dim_idx,
+ dimension->contact, intersector));
+ }
+ }
+ }
+ }
+
+ /* 2) Segments only ending at this point overlap open segments */
+ for(s = 0; s < scount; s++) {
+ if(!begins[s] && ends[s]) {
+ ERR(register_segment_interactions(seg_index[s], &open_segments, !dim_idx,
+ dimension->overlap, intersector));
+ }
+ }
+
+ /* 3) Segments ending at this point are now closed */
+ for(s = 0; s < scount; s++) {
+ if(ends[s]) {
+ size_t n = htable_segment_idx_erase(&open_segments, seg_index + s);
+ CHK(n == (begins[s] ? 0 : 1));
+ }
+ }
+
+ /* 4) Segments beginning and ending at this point overlap open segments */
+ for(s = 0; s < scount; s++) {
+ if(begins[s] && ends[s]) {
+ ERR(register_segment_interactions(seg_index[s], &open_segments, !dim_idx,
+ dimension->overlap, intersector));
+ }
+ }
+
+ /* 5) Segments only beginning at this point are now open */
+ for(s = 0; s < scount; s++) {
+ if(begins[s] && !ends[s]) {
+ char one = 1;
+ ERR(htable_segment_idx_set(&open_segments, seg_index + s, &one));
+ }
+ }
+
+ /* 6) Segments only beginning at this point overlap open segments */
+ for(s = 0; s < scount; s++) {
+ if(begins[s] && !ends[s]) {
+ ERR(register_segment_interactions(seg_index[s], &open_segments, !dim_idx,
+ dimension->overlap, intersector));
+ }
+ }
+ }
+
+exit:
+ MEM_RM(allocator, begins);
+ MEM_RM(allocator, ends);
+ MEM_RM(allocator, seg_index);
+ htable_segment_idx_release(&open_segments);
+ return res;
+error:
+ goto exit;
+}
+
+static int64_t
+safe_mul
+ (int64_t a,
+ int64_t b,
+ int* ovflw)
+{
+ if(*ovflw) return 0;
+ if(b != 0 && abs(a) > 1 + (INT64_MAX / abs(b))) goto error;
+ return a * b;
+error:
+ *ovflw = 1;
+ return 0;
+}
+
+/* Check if a triangle is CCW (>0), CW (<0), or flat (=0) */
+static int
+is_ccw
+ (Clipper2Lib::Point64* v1,
+ Clipper2Lib::Point64* v2,
+ Clipper2Lib::Point64* v3,
+ int* ovflw)
+{
+ int64_t tmp1, tmp2;
+ ASSERT(v1 && v2 && v3);
+ /* Limited coordinate range garanties that sub computations stay in int64_t
+ * range */
+ tmp1 = safe_mul(v2->x - v1->x, v3->y - v1->y, ovflw);
+ tmp2 = safe_mul(v3->x - v1->x, v2->y - v1->y, ovflw);
+ if(tmp1 < tmp2) return -1;
+ if(tmp1 > tmp2) return +1;
+ return 0;
+}
+
+/* Check if 2 segments intersect */
+static res_T
+check_two_segments_intersection
+ (const struct intersector_segment* seg1,
+ const struct intersector_segment* seg2,
+ struct scpr_intersector_check_callbacks* callbacks,
+ struct scpr_intersector* intersector,
+ void* data)
+{
+ res_T res = RES_OK;
+ size_t count, r11, r12, r21, r22;
+ Clipper2Lib::Point64 *v11, * v12, *v21, *v22;
+ int ccw1, ccw2, ccw3, ccw4, ovflw = 0;
+ const struct intersector_component* components;
+ struct scpr_polygon *polygon1, *polygon2;
+ uint32_t comp_idx1, comp_idx2;
+
+ ASSERT(seg1 && seg2 && callbacks);
+
+ components = darray_components_cdata_get(&intersector->components);
+ comp_idx1 = components[seg1->component].component;
+ comp_idx2 = components[seg2->component].component;
+ polygon1 = components[seg1->component].polygon;
+ polygon2 = components[seg2->component].polygon;
+
+ /* Get vertices */
+ r11 = seg1->first_vertex;
+ ERR(scpr_polygon_get_vertices_count(polygon1, comp_idx1, &count));
+ r12 = (r11 + 1) % count;
+ r21 = seg2->first_vertex;
+ ERR(scpr_polygon_get_vertices_count(polygon2, comp_idx2, &count));
+ r22 = (r21 + 1) % count;
+ v11 = &polygon1->paths[comp_idx1][r11];
+ v12 = &polygon1->paths[comp_idx1][r12];
+ v21 = &polygon2->paths[comp_idx2][r21];
+ v22 = &polygon2->paths[comp_idx2][r22];
+ ASSERT(segments_bbox_interacts(v11, v12, v21, v22));
+
+ /* Test triangles orientation */
+ ccw1 = is_ccw(v11, v12, v21, &ovflw);
+ ccw2 = is_ccw(v11, v12, v22, &ovflw);
+ ccw3 = is_ccw(v21, v22, v11, &ovflw);
+ ccw4 = is_ccw(v21, v22, v12, &ovflw);
+ if(ovflw) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ /* Analyse ccw results */
+ if(ccw1 * ccw2 > 0 && ccw3 * ccw4 > 0) {
+ /* No intersection at all */
+ }
+ else if(ccw1 == 0 && ccw2 == 0 && ccw3 == 0 && ccw4 == 0) {
+ /* collinear segments */
+ struct scpr_callback_segment arg1, arg2;
+ arg1.polygon = polygon1;
+ arg1.component = comp_idx1;
+ arg1.first_vertex = r11;
+ arg1.last_vertex = r12;
+ arg2.polygon = polygon2;
+ arg2.component = comp_idx2;
+ arg2.first_vertex = r21;
+ arg2.last_vertex = r22;
+ if(callbacks->collinear_segments
+ && callbacks->collinear_segments(&arg1, &arg2, data))
+ {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ }
+ else if(ccw1 * ccw2 < 0 && ccw3 * ccw4 < 0) {
+ /* Basic segment intersection */
+ struct scpr_callback_segment arg1, arg2;
+ arg1.polygon = polygon1;
+ arg1.component = comp_idx1;
+ arg1.first_vertex = r11;
+ arg1.last_vertex = r12;
+ arg2.polygon = polygon2;
+ arg2.component = comp_idx2;
+ arg2.first_vertex = r21;
+ arg2.last_vertex = r22;
+ if(callbacks->simple_intersection
+ && callbacks->simple_intersection(&arg1, &arg2, data))
+ {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+}
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+/* Test intersection of all the registered pairs of interacting segments */
+static res_T
+check_interacting_pairs
+ (struct scpr_intersector* intersector,
+ struct scpr_intersector_check_callbacks* callbacks,
+ void* data)
+{
+ res_T res = RES_OK;
+ struct htable_interacting_segments_iterator it, end;
+ const struct intersector_segment* segments;
+
+ ASSERT(intersector && callbacks);
+
+ segments = darray_segments_cdata_get(&intersector->segments);
+ htable_interacting_segments_begin(&intersector->interacting_segments, &it);
+ htable_interacting_segments_end(&intersector->interacting_segments, &end);
+ /* Loop on interacting segments */
+ while(!htable_interacting_segments_iterator_eq(&it, &end)) {
+ unsigned char overlapping
+ = *htable_interacting_segments_iterator_data_get(&it);
+ if(overlapping & SOME_OVERLAP
+ && overlapping & SOMETHING_X && overlapping & SOMETHING_Y)
+ {
+ struct intersector_segment_pair* pair
+ = htable_interacting_segments_iterator_key_get(&it);
+ const struct intersector_segment* seg1 = segments + pair->rank1;
+ const struct intersector_segment* seg2 = segments + pair->rank2;
+ ERR(check_two_segments_intersection(seg1, seg2, callbacks, intersector, data));
+ }
+ htable_interacting_segments_iterator_next(&it);
+ }
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+/*******************************************************************************
+ * Exported functions
+ ******************************************************************************/
+res_T
+scpr_intersector_create
+ (struct scpr_device* device,
+ struct scpr_intersector** out_intersector)
+{
+ res_T res = RES_OK;
+ struct scpr_intersector* intersector = NULL;
+ struct mem_allocator* allocator;
+
+ if(!device || ! out_intersector) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ allocator = device->allocator;
+ intersector = (struct scpr_intersector*)MEM_ALLOC(allocator, sizeof(*intersector));
+ if(!intersector) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ ref_init(&intersector->ref);
+ intersector->device = device;
+ ERR(scpr_device_ref_get(device));
+ darray_segments_init(allocator, &intersector->segments);
+ init_dimension(allocator, intersector->dimensions, OVERLAP_X, CONTACT_X);
+ init_dimension(allocator, intersector->dimensions + 1, OVERLAP_Y, CONTACT_Y);
+ htable_interacting_segments_init(allocator, &intersector->interacting_segments);
+ htable_registered_components_init(allocator, &intersector->registererd_components);
+ darray_components_init(allocator, &intersector->components);
+
+exit:
+ if(out_intersector) *out_intersector = intersector;
+ return res;
+error:
+ if(intersector) {
+ SCPR(intersector_ref_put(intersector));
+ intersector = NULL;
+ }
+ goto exit;
+}
+
+res_T
+scpr_intersector_ref_get
+ (struct scpr_intersector* intersector)
+{
+ if(!intersector) return RES_BAD_ARG;
+ ref_get(&intersector->ref);
+ return RES_OK;
+}
+
+res_T
+scpr_intersector_ref_put
+ (struct scpr_intersector* intersector)
+{
+ if(!intersector) return RES_BAD_ARG;
+ ref_put(&intersector->ref, intersector_release);
+ return RES_OK;
+}
+
+res_T
+scpr_intersector_register_polygon
+ (struct scpr_intersector* intersector,
+ struct scpr_polygon* polygon)
+{
+ res_T res = RES_OK;
+ size_t i;
+
+ if(!intersector || !polygon) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ for(i = 0; i < polygon->paths.size(); i++) {
+ ERR(scpr_intersector_register_component(intersector, polygon, i));
+ }
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+res_T
+scpr_intersector_register_component
+ (struct scpr_intersector* intersector,
+ struct scpr_polygon* polygon,
+ const size_t icomponent)
+{
+ res_T res = RES_OK;
+ uint32_t i;
+ size_t new_count, prev_count, c;
+ struct intersector_component comp;
+ char *found, one = 1;
+
+ if(!intersector || !polygon || icomponent >= polygon->paths.size()) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ /* Check single registration */
+ comp.polygon = polygon;
+ comp.component = (uint32_t)icomponent;
+ found = htable_registered_components_find(&intersector->registererd_components,
+ &comp);
+ if(found) {
+ logger_print(intersector->device->logger, LOG_ERROR,
+ "Component %zu already registered.\n", icomponent);
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ c = htable_registered_components_size_get(&intersector->registererd_components);
+ if(c >= UINT32_MAX) {
+ logger_print(intersector->device->logger, LOG_ERROR,
+ "Too many components registered.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ ASSERT(c == darray_components_size_get(&intersector->components));
+ ERR(htable_registered_components_set(&intersector->registererd_components,
+ &comp, &one));
+ ERR(darray_components_push_back(&intersector->components, &comp));
+
+ prev_count = darray_segments_size_get(&intersector->segments);
+ ASSERT(prev_count <= UINT32_MAX);
+ new_count = polygon->paths[icomponent].size();
+ if(prev_count + new_count > UINT32_MAX) {
+ logger_print(intersector->device->logger, LOG_ERROR,
+ "Too many segments registered.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ ERR(darray_segments_resize(&intersector->segments,
+ (uint32_t)(prev_count + new_count)));
+
+ for(i = 0; i < new_count; i++) {
+ Clipper2Lib::Point64 *p0, *p1;
+ uint32_t seg_idx = (uint32_t)(prev_count + i);
+ struct intersector_segment* segment
+ = darray_segments_data_get(&intersector->segments) + seg_idx;
+ segment->component = (uint32_t)c;
+ segment->first_vertex = i;
+ p0 = &polygon->paths[icomponent][i];
+ p1 = &polygon->paths[icomponent][(i+1)%new_count];
+ ERR(register_point(intersector, 0, p0->x, seg_idx));
+ ERR(register_point(intersector, 0, p1->x, seg_idx));
+ ERR(register_point(intersector, 1, p0->y, seg_idx));
+ ERR(register_point(intersector, 1, p1->y, seg_idx));
+ }
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+res_T
+scpr_intersector_check
+ (struct scpr_intersector* intersector,
+ struct scpr_intersector_check_callbacks* callbacks,
+ void* data)
+{
+ int i;
+ res_T res = RES_OK;
+
+ if(!intersector || !callbacks
+ || (!callbacks->simple_intersection && !callbacks->collinear_segments))
+ {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ /* Construct candidates for intersection */
+ for(i = 0; i < 2; i++) {
+ sort_points(&intersector->dimensions[i]);
+ ERR(register_interaction(i, intersector));
+ }
+ /* Check intersections */
+ ERR(check_interacting_pairs(intersector, callbacks, data));
+
+exit:
+ return res;
+error:
+ goto exit;
+}
diff --git a/src/scpr_mesh.c b/src/scpr_mesh.c
@@ -17,6 +17,7 @@
#include "scpr_c.h"
#include <rsys/mem_allocator.h>
+#include <rsys/logger.h>
#include <polygon.h>
#undef PI
@@ -39,10 +40,10 @@ scpr_operation_to_clip_type(const enum scpr_operation op)
static INLINE int
aabb_intersect
- (const double lower0[2],
- const double upper0[2],
- const double lower1[2],
- const double upper1[2])
+ (const int64_t lower0[2],
+ const int64_t upper0[2],
+ const int64_t lower1[2],
+ const int64_t upper1[2])
{
ASSERT(lower0 && upper0 && lower1 && upper1);
return lower0[0] < upper1[0]
@@ -52,7 +53,7 @@ aabb_intersect
}
static INLINE int
-aabb_is_degenerated(const double lower[2], const double upper[2])
+aabb_is_degenerated(const int64_t lower[2], const int64_t upper[2])
{
ASSERT(lower && upper);
return lower[0] >= upper[0] || lower[1] >= upper[1];
@@ -60,54 +61,58 @@ aabb_is_degenerated(const double lower[2], const double upper[2])
static void
triangle_compute_aabb
- (double tri[3][2],
- double lower[2],
- double upper[2])
+ (int64_t tri[3][2],
+ int64_t lower[2],
+ int64_t upper[2])
{
size_t ivert;
ASSERT(tri && lower && upper);
- d2_splat(lower, DBL_MAX);
- d2_splat(upper,-DBL_MAX);
+ lower[0] = lower[1] = INT64_MAX;
+ upper[0] = upper[1] = INT64_MIN;
FOR_EACH(ivert, 0, 3) {
- d2_min(lower, lower, tri[ivert]);
- d2_max(upper, upper, tri[ivert]);
+ int i;
+ for(i = 0; i < 2; i++) {
+ if(lower[i] > tri[ivert][i]) lower[i] = tri[ivert][i];
+ if(upper[i] < tri[ivert][i]) upper[i] = tri[ivert][i];
+ }
}
}
static res_T
register_vertex
- (const double pos[2],
- struct darray_double* coords,
- struct darray_size_t* indices,
+ (const int64_t pos[2],
+ struct darray_int64* coords,
+ struct darray_uint32* indices,
struct htable_vertex* vertices)
{
struct vertex v;
- size_t* pid, id;
+ uint32_t* pid, id;
+ unsigned i;
res_T res = RES_OK;
ASSERT(pos && coords && indices && vertices);
- d2_set(v.pos, pos);
+ for(i = 0; i < 2; i++) v.pos[i] = pos[i];
- /* FIXME dirty hack */
- v.pos[0] = (float)v.pos[0];
- v.pos[1] = (float)v.pos[1];
+ v.pos[0] = v.pos[0];
+ v.pos[1] = v.pos[1];
pid = htable_vertex_find(vertices, &v);
if(pid) {
id = *pid;
} else {
- const size_t ivert = darray_double_size_get(coords);
+ const size_t count = darray_int64_size_get(coords);
- ERR(darray_double_resize(coords, ivert+2/*#coords*/));
- d2_set(darray_double_data_get(coords)+ivert, pos);
+ ASSERT(count <= UINT32_MAX);
+ ERR(darray_int64_resize(coords, count+2/*#coords*/));
+ for(i = 0; i < 2; i++) darray_int64_data_get(coords)[count+i] = pos[i];
- id = ivert / 2;
+ id = (uint32_t)count / 2;
ERR(htable_vertex_set(vertices, &v, &id));
}
- ERR(darray_size_t_push_back(indices, &id));
+ ERR(darray_uint32_push_back(indices, &id));
exit:
return res;
@@ -117,9 +122,9 @@ error:
static FINLINE res_T
register_triangle
- (double tri[3][2],
- struct darray_double* coords, /* Vertex buffer */
- struct darray_size_t* indices, /* Index buffer */
+ (int64_t tri[3][2],
+ struct darray_int64* coords, /* Vertex buffer */
+ struct darray_uint32* indices, /* Index buffer */
struct htable_vertex* vertices) /* Map a vertex to its index */
{
size_t ivert;
@@ -137,11 +142,12 @@ error:
static res_T
register_paths
- (const Clipper2Lib::PathsD& paths,
- struct darray_double* coords, /* Vertex buffer */
- struct darray_size_t* indices, /* Index buffer */
+ (struct scpr_device* dev,
+ const Clipper2Lib::Paths64& paths,
+ struct darray_int64* coords, /* Vertex buffer */
+ struct darray_uint32* 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;
@@ -151,7 +157,7 @@ register_paths
FOR_EACH(ipath, 0, paths.size()) {
if(paths[ipath].size() == 3) {
FOR_EACH(ivert, 0, 3) {
- double pos[2];
+ int64_t pos[2];
pos[0] = paths[ipath][ivert].x;
pos[1] = paths[ipath][ivert].y;
ERR(register_vertex(pos, coords, indices, vertices));
@@ -165,23 +171,27 @@ register_paths
POLYGON(clear(polygon));
FOR_EACH(ivert, 0, paths[ipath].size()) {
float fpos[3] = {0.f, 0.f, 0.f};
- double pos[2];
+ double posd[2];
+ int64_t pos64[2];
- pos[0] = paths[ipath][ivert].x;
- pos[1] = paths[ipath][ivert].y;
+ pos64[0] = paths[ipath][ivert].x;
+ pos64[1] = paths[ipath][ivert].y;
+ SCPR(device_unscale(dev, pos64, 2, posd));
- fpos[0] = (float)pos[0], fpos[1] = (float)pos[1];
+ fpos[0] = (float)posd[0], fpos[1] = (float)posd[1];
ERR(polygon_vertex_add(polygon, fpos));
}
ERR(polygon_triangulate(polygon, &ids, &nids));
FOR_EACH(ivert, 0, nids) {
float fpos[3];
+ int64_t pos64[2];
double pos[2];
POLYGON(vertex_get(polygon, ids[ivert], fpos));
- pos[0] = (float)fpos[0];
- pos[1] = (float)fpos[1];
- ERR(register_vertex(pos, coords, indices, vertices));
+ pos[0] = (double)fpos[0];
+ pos[1] = (double)fpos[1];
+ SCPR(device_scale(dev, pos, 2, pos64));
+ ERR(register_vertex(pos64, coords, indices, vertices));
}
}
}
@@ -192,22 +202,29 @@ error:
}
static void
-mesh_compute_aabb(const struct scpr_mesh* mesh, double lower[2], double upper[2])
+mesh_compute_aabb
+ (const struct scpr_mesh* mesh,
+ int64_t lower[2],
+ int64_t upper[2])
{
- size_t itri, ntris;
+ size_t itri, ntris, i;
SCPR(mesh_get_triangles_count(mesh, &ntris));
- d2_splat(lower, DBL_MAX);
- d2_splat(upper,-DBL_MAX);
+ lower[0] = lower[1] = INT64_MAX;
+ upper[0] = upper[1] = INT64_MIN;
FOR_EACH(itri, 0, ntris) {
size_t ids[3], ivert;
SCPR(mesh_get_indices(mesh, itri, ids));
FOR_EACH(ivert, 0, 3) {
double pos[2];
+ int64_t pos64[2];
SCPR(mesh_get_position(mesh, ids[ivert], pos));
- d2_min(lower, lower, pos);
- d2_max(upper, upper, pos);
+ 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];
+ }
}
}
}
@@ -216,39 +233,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);
- darray_double_release(&mesh->coords);
- darray_size_t_release(&mesh->indices);
- MEM_RM(mesh->allocator, mesh);
+ allocator = mesh->device->allocator;
+ SCPR(device_ref_put(mesh->device));
+ darray_int64_release(&mesh->coords);
+ darray_uint32_release(&mesh->indices);
+ 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_double_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_uint32_init(dev->allocator, &mesh->indices);
exit:
if(out_mesh) *out_mesh = mesh;
@@ -287,9 +308,9 @@ scpr_mesh_setup_indexed_vertices
void (*get_position)(const size_t ivert, double pos[2], void* ctx),
void* data)
{
- double* pos = NULL;
- size_t* ids = NULL;
- size_t i;
+ int64_t* pos = NULL;
+ uint32_t* ids = NULL;
+ size_t i, j;
res_T res = RES_OK;
if(!mesh || !ntris || !get_indices || !nverts || !get_position || !data) {
@@ -297,33 +318,49 @@ scpr_mesh_setup_indexed_vertices
goto error;
}
- ERR(darray_double_resize(&mesh->coords, nverts*2/*#coords per vertex*/));
- ERR(darray_size_t_resize(&mesh->indices, ntris*3/*#vertices per triangle*/));
+ if(ntris > UINT32_MAX) {
+ logger_print(mesh->device->logger, LOG_ERROR,
+ "Too many triangles.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ if(nverts > UINT32_MAX) {
+ logger_print(mesh->device->logger, LOG_ERROR,
+ "Too many vertices.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ ERR(darray_int64_resize(&mesh->coords, nverts*2/*#coords per vertex*/));
+ ERR(darray_uint32_resize(&mesh->indices, ntris*3/*#vertices per triangle*/));
/* Fetch mesh positions */
- pos = darray_double_data_get(&mesh->coords);
+ pos = darray_int64_data_get(&mesh->coords);
FOR_EACH(i, 0, nverts) {
- get_position(i, pos+i*2, data);
+ double posd[2];
+ get_position(i, posd, data);
+ ERR(scpr_device_scale(mesh->device, posd, 2, pos + i*2));
}
/* Fetch mesh indices */
- ids = darray_size_t_data_get(&mesh->indices);
+ ids = darray_uint32_data_get(&mesh->indices);
FOR_EACH(i, 0, ntris) {
- get_indices(i, ids +i*3, data);
- if(ids[i*3+0] >= nverts
- || ids[i*3+1] >= nverts
- || ids[i*3+2] >= nverts) {
+ size_t tmp[3];
+ get_indices(i, tmp, data);
+ if(tmp[0] >= nverts || tmp[1] >= nverts || tmp[2] >= nverts) {
res = RES_BAD_ARG;
goto error;
}
+ for(j = 0; j < 3; j++) ids[i*3 + j] = (uint32_t)tmp[j];
}
exit:
return res;
error:
if(mesh) {
- darray_double_clear(&mesh->coords);
- darray_size_t_clear(&mesh->indices);
+ darray_int64_clear(&mesh->coords);
+ darray_uint32_clear(&mesh->indices);
}
goto exit;
}
@@ -332,8 +369,8 @@ res_T
scpr_mesh_get_vertices_count(const struct scpr_mesh* mesh, size_t* nverts)
{
if(!mesh || !nverts) return RES_BAD_ARG;
- ASSERT((darray_double_size_get(&mesh->coords) % 2/*#coords per vertex*/)==0);
- *nverts = darray_double_size_get(&mesh->coords) / 2/*#coords per vertex*/;
+ ASSERT((darray_int64_size_get(&mesh->coords) % 2/*#coords per vertex*/)==0);
+ *nverts = darray_int64_size_get(&mesh->coords) / 2/*#coords per vertex*/;
return RES_OK;
}
@@ -341,8 +378,8 @@ res_T
scpr_mesh_get_triangles_count(const struct scpr_mesh* mesh, size_t* ntris)
{
if(!mesh || !ntris) return RES_BAD_ARG;
- ASSERT((darray_size_t_size_get(&mesh->indices)%3/*#vertices per triangle*/)==0);
- *ntris = darray_size_t_size_get(&mesh->indices) / 3/*#vertices per triangle*/;
+ ASSERT((darray_uint32_size_get(&mesh->indices)%3/*#vertices per triangle*/)==0);
+ *ntris = darray_uint32_size_get(&mesh->indices) / 3/*#vertices per triangle*/;
return RES_OK;
}
@@ -355,9 +392,9 @@ scpr_mesh_get_indices
if(!mesh || !ids) return RES_BAD_ARG;
SCPR(mesh_get_triangles_count(mesh, &ntris));
if(itri >= ntris) return RES_BAD_ARG;
- ids[0] = darray_size_t_cdata_get(&mesh->indices)[i+0];
- ids[1] = darray_size_t_cdata_get(&mesh->indices)[i+1];
- ids[2] = darray_size_t_cdata_get(&mesh->indices)[i+2];
+ ids[0] = darray_uint32_cdata_get(&mesh->indices)[i+0];
+ ids[1] = darray_uint32_cdata_get(&mesh->indices)[i+1];
+ ids[2] = darray_uint32_cdata_get(&mesh->indices)[i+2];
return RES_OK;
}
@@ -366,12 +403,14 @@ scpr_mesh_get_position
(const struct scpr_mesh* mesh, const size_t ivert, double pos[2])
{
size_t nverts;
+ int64_t pos64[2];
const size_t i = ivert * 2/*#coords per vertex*/;
if(!mesh || !pos) return RES_BAD_ARG;
SCPR(mesh_get_vertices_count(mesh, &nverts));
if(ivert >= nverts) return RES_BAD_ARG;
- pos[0] = darray_double_cdata_get(&mesh->coords)[i+0];
- pos[1] = darray_double_cdata_get(&mesh->coords)[i+1];
+ pos64[0] = darray_int64_cdata_get(&mesh->coords)[i+0];
+ pos64[1] = darray_int64_cdata_get(&mesh->coords)[i+1];
+ SCPR(device_unscale(mesh->device, pos64, 2, pos));
return RES_OK;
}
@@ -381,28 +420,31 @@ scpr_mesh_clip
const enum scpr_operation op,
struct scpr_polygon* poly_desc)
{
- double lower[2], upper[2];
+ int64_t lower[2], upper[2];
struct polygon* polygon = NULL; /* Use to triangulate clipped polygons */
- struct darray_double coords; /* Coordinates of the clipped mesh */
- struct darray_size_t indices; /* Indices of the clipped mesh */
+ struct darray_int64 coords; /* Coordinates of the clipped mesh */
+ struct darray_uint32 indices; /* Indices of the clipped mesh */
struct htable_vertex vertices; /* Map a coordinate to its index */
- Clipper2Lib::ClipperD clipper;
- Clipper2Lib::PathsD output; /* Contour of the clipped polgyon */
- Clipper2Lib::PathsD cand_path; /* Contour of the candidate polygon */
- Clipper2Lib::PathD tmp;
+ Clipper2Lib::Clipper64 clipper;
+ Clipper2Lib::Paths64 output; /* Contour of the clipped polgyon */
+ Clipper2Lib::Paths64 cand_path; /* Contour of the candidate polygon */
+ 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;
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_double_init(mesh->allocator, &coords);
- darray_size_t_init(mesh->allocator, &indices);
- htable_vertex_init(mesh->allocator, &vertices);
+ darray_int64_init(allocator, &coords);
+ darray_uint32_init(allocator, &indices);
+ htable_vertex_init(allocator, &vertices);
if(aabb_is_degenerated(poly_desc->lower, poly_desc->upper)) goto exit;
@@ -410,29 +452,35 @@ scpr_mesh_clip
if(aabb_is_degenerated(lower, upper)) goto exit;
/* Compute the overall aabb of the candidate and the clip polygon */
- d2_min(lower, lower, poly_desc->lower);
- d2_max(upper, upper, poly_desc->upper);
+ for(i = 0; i < 2; i++) {
+ if(lower[i] > poly_desc->lower[i]) lower[i] = poly_desc->lower[i];
+ if(upper[i] < poly_desc->upper[i]) upper[i] = poly_desc->upper[i];
+ }
/* 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));
FOR_EACH(itri, 0, ntris) {
size_t ids[3];
double tri[3][2];
+ int64_t tri64[3][2];
/* Fetch the triangle vertices and compute its AABB */
SCPR(mesh_get_indices(mesh, itri, ids));
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]));
- triangle_compute_aabb(tri, lower, upper);
+ 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 */
if(!aabb_intersect(lower, upper, poly_desc->lower, poly_desc->upper)) {
if(op != SCPR_AND) {
- ERR(register_triangle(tri, &coords, &indices, &vertices));
+ ERR(register_triangle(tri64, &coords, &indices, &vertices));
}
continue;
}
@@ -441,9 +489,9 @@ scpr_mesh_clip
cand_path.clear();
tmp.clear();
FOR_EACH(ivert, 0, 3) {
- Clipper2Lib::PointD pt;
- pt.x = tri[ivert][0];
- pt.y = tri[ivert][1];
+ Clipper2Lib::Point64 pt;
+ pt.x = tri64[ivert][0];
+ pt.y = tri64[ivert][1];
tmp.push_back(pt);
}
cand_path.push_back(tmp);
@@ -455,16 +503,16 @@ 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_double_copy_and_clear(&mesh->coords, &coords));
- ERR(darray_size_t_copy_and_clear(&mesh->indices, &indices));
+ ERR(darray_int64_copy_and_clear(&mesh->coords, &coords));
+ ERR(darray_uint32_copy_and_clear(&mesh->indices, &indices));
exit:
if(polygon) POLYGON(ref_put(polygon));
- darray_double_release(&coords);
- darray_size_t_release(&indices);
+ darray_int64_release(&coords);
+ darray_uint32_release(&indices);
htable_vertex_release(&vertices);
return res;
error:
diff --git a/src/scpr_polygon.c b/src/scpr_polygon.c
@@ -13,32 +13,21 @@
* 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"
#include <new>
#include <polygon.h>
+#include <rsys/logger.h>
+#include <rsys/ref_count.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
******************************************************************************/
@@ -59,22 +48,26 @@ 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::PathsD::~PathsD();
- MEM_RM(polygon->allocator, polygon);
+ polygon->paths.Clipper2Lib::Paths64::~Paths64();
+ MEM_RM(allocator, polygon);
}
static int
path_is_eq
- (const Clipper2Lib::PathD* p1,
- const Clipper2Lib::PathD* p2)
+ (const Clipper2Lib::Path64* p1,
+ const Clipper2Lib::Path64* p2)
{
size_t i, first_vtx, sz;
int opposite_cw;
ASSERT(p1 && p2);
sz = p1->size();
+
if(sz != p2->size()) {
return 0;
}
@@ -99,8 +92,8 @@ path_is_eq
static int
one_path_is_eq
- (const Clipper2Lib::PathsD* pp,
- const Clipper2Lib::PathD* p,
+ (const Clipper2Lib::Paths64* pp,
+ const Clipper2Lib::Path64* p,
char* matched)
{
size_t i, sz;
@@ -108,7 +101,7 @@ one_path_is_eq
sz = pp->size();
FOR_EACH(i, 0, sz) {
if(matched[i]) continue;
- if(path_is_eq(&pp->at(i), p)) {
+ if(path_is_eq(&(*pp)[i], p)) {
matched[i] = 1;
return 1;
}
@@ -121,31 +114,30 @@ 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;
- d2_splat(polygon->lower, DBL_MAX);
- d2_splat(polygon->upper,-DBL_MAX);
+ polygon->lower[0] = polygon->lower[1] = INT64_MAX;
+ polygon->upper[0] = polygon->upper[1] = INT64_MIN;
exit:
if(out_polygon) *out_polygon = polygon;
@@ -177,30 +169,19 @@ 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 ncomponents, /* #connex components */
+ const size_t ncomponents,
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 c;
- double scale = pow(10, PRECISION);
+ struct scpr_device* dev;
+ Clipper2Lib::Paths64 paths;
+ int changedc, changedv = 0;
res_T res = RES_OK;
if(!polygon || !get_nverts || !get_position || !data) {
@@ -208,42 +189,74 @@ scpr_polygon_setup_indexed_vertices
goto error;
}
- TRY(polygon->paths.resize(ncomponents));
+ if(ncomponents > UINT32_MAX) {
+ logger_print(polygon->device->logger, LOG_ERROR,
+ "Too many components.\n");
+ res = RES_BAD_ARG;
+ goto error;
+ }
- d2_splat(polygon->lower, DBL_MAX);
- d2_splat(polygon->upper,-DBL_MAX);
+ dev = polygon->device;
+ TRY(paths.resize(ncomponents));
FOR_EACH(c, 0, ncomponents) {
size_t i, nverts;
/* Get count for connex component c */
get_nverts(c, &nverts, data);
- TRY(polygon->paths[c].resize(nverts));
+ if(nverts > UINT32_MAX) {
+ logger_print(polygon->device->logger, LOG_ERROR,
+ "Too many vertices for component %zu.\n", c);
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ TRY(paths[c].resize(nverts));
/* Fetch polygon positions for connex component c */
FOR_EACH(i, 0, nverts) {
double tmp[2];
- int64_t tmp2[2];
- Clipper2Lib::PointD pt;
+ int64_t tmp64[2];
+ Clipper2Lib::Point64 pt;
get_position(c, i, tmp, data);
- /* 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;
+ /* Check range and truncate precision to ensure further consistency */
+ ERR(scpr_device_scale(dev, tmp, 2, tmp64));
+ pt.x = tmp64[0];
+ pt.y = tmp64[1];
+ paths[c][i] = pt;
+ }
+ }
- d2_min(polygon->lower, polygon->lower, tmp);
- d2_max(polygon->upper, polygon->upper, tmp);
+ /* Merge vertices, ... */
+ TRY(paths = Clipper2Lib::SimplifyPaths(paths, dev->inv_scale));
+ polygon->paths = std::move(paths);
+ paths.Clipper2Lib::Paths64::~Paths64();
+ changedc = (ncomponents != polygon->paths.size());
+ for(c = 0; !changedv && c < polygon->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;
+ polygon->upper[0] = polygon->upper[1] = INT64_MIN;
+ FOR_EACH(c, 0, ncomponents) {
+ size_t i, nverts;
+ get_nverts(c, &nverts, data);
+ FOR_EACH(i, 0, nverts) {
+ int64_t tmp[2];
+ int j;
+ tmp[0] = polygon->paths[c][i].x;
+ tmp[1] = polygon->paths[c][i].y;
+ for(j = 0; j < 2; j++) {
+ if(polygon->lower[j] > tmp[j]) polygon->lower[j] = tmp[j];
+ if(polygon->upper[j] < tmp[j]) polygon->upper[j] = tmp[j];
+ }
}
}
@@ -252,42 +265,74 @@ exit:
error:
if(polygon) {
polygon->paths.clear();
- d2_splat(polygon->lower, DBL_MAX);
- d2_splat(polygon->upper,-DBL_MAX);
+ polygon->lower[0] = polygon->lower[1] = INT64_MAX;
+ polygon->upper[0] = polygon->upper[1] = INT64_MIN;
}
goto exit;
}
res_T
+scpr_polygon_in_bbox
+ (struct scpr_polygon* polygon,
+ const double lower[2],
+ const double upper[2],
+ int* inside)
+{
+ int i, in = 1;
+ int64_t low[2], up[2];
+ res_T res = RES_OK;
+
+ if(!polygon || !lower || !upper || !inside) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ SCPR(device_scale(polygon->device, lower, 2, low));
+ SCPR(device_scale(polygon->device, upper, 2, up));
+ for(i = 0; i < 2; i++) {
+ if(polygon->lower[i] < low[i] || polygon->upper[i] > up[i]) {
+ in = 0;
+ break;
+ }
+ }
+
+ *inside = in;
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+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)
{
- struct scpr_polygon* copy;
- int copy_created = 0;
+ struct scpr_polygon* copy = NULL;
+ 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_created = 1;
+ ERR(scpr_polygon_create(dev, ©));
copy->paths = src_polygon->paths;
- d2_set(copy->lower, src_polygon->lower);
- d2_set(copy->upper, src_polygon->upper);
+ for(i = 0; i < 2; i++) {
+ copy->lower[i] = src_polygon->lower[i];
+ copy->upper[i] = src_polygon->upper[i];
+ }
exit:
if(out_polygon) *out_polygon = copy;
return res;
error:
- if(copy_created) CHK(RES_OK == scpr_polygon_ref_put(copy));
+ if(copy) SCPR(polygon_ref_put(copy));
copy = NULL;
goto exit;
}
-#undef TRY
res_T
scpr_polygon_get_components_count
@@ -323,15 +368,17 @@ scpr_polygon_get_position
{
size_t nverts;
const size_t i = ivert;
- const Clipper2Lib::PointD* pt;
+ const Clipper2Lib::Point64* pt;
+ int64_t pos64[2];
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->paths[icomponent][i];
- pos[0] = pt->x;
- pos[1] = pt->y;
+ pos64[0] = pt->x;
+ pos64[1] = pt->y;
+ SCPR(device_unscale(polygon->device, pos64, 2, pos));
return RES_OK;
}
@@ -342,9 +389,11 @@ scpr_offset_polygon
const enum scpr_join_type join_type)
{
size_t c;
- Clipper2Lib::PathD tmp;
+ Clipper2Lib::Path64 tmp;
+ struct scpr_device* dev;
Clipper2Lib::JoinType cjt;
- Clipper2Lib::PathsD polygon;
+ Clipper2Lib::Paths64 polygon;
+ int64_t offset64;
res_T res = RES_OK;
if(!poly_desc) {
@@ -352,6 +401,23 @@ 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[0]
+ || (double)poly_desc->upper[i] + offset > range[1])
+ {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ }
+ }
+
/* Check join type */
switch(join_type) {
case SCPR_JOIN_SQUARE:
@@ -363,23 +429,27 @@ scpr_offset_polygon
goto error;
}
- /* Some known problems when offset=0, not leaving polygon unchanged */
+ ERR(scpr_device_scale(dev, &offset, 1, &offset64));
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);
+ TRY(poly_desc->paths = Clipper2Lib::InflatePaths(poly_desc->paths,
+ (double)offset64, cjt, Clipper2Lib::EndType::Polygon));
/* Rebuild AABB */
- d2_splat(poly_desc->lower, DBL_MAX);
- d2_splat(poly_desc->upper,-DBL_MAX);
+ poly_desc->lower[0] = poly_desc->lower[1] = INT64_MAX;
+ poly_desc->upper[0] = poly_desc->upper[1] = INT64_MIN;
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);
+ int64_t pos64[2];
+ int j;
+ pos64[0] = poly_desc->paths[c][i].x;
+ pos64[1] = poly_desc->paths[c][i].y;
+ for(j = 0; j < 2; j++) {
+ if(poly_desc->lower[j] > pos64[j]) poly_desc->lower[j] = pos64[j];
+ if(poly_desc->upper[j] < pos64[j]) poly_desc->upper[j] = pos64[j];
+ }
}
}
@@ -409,8 +479,10 @@ scpr_polygon_eq
*is_eq = 0;
goto exit;
}
- if(!d2_eq(polygon1->lower, polygon2->lower)
- || !d2_eq(polygon1->upper, polygon2->upper))
+ if(polygon1->lower[0] != polygon2->lower[0]
+ || polygon1->lower[1] != polygon2->lower[1]
+ || polygon1->upper[0] != polygon2->upper[0]
+ || polygon1->upper[1] != polygon2->upper[1])
{
*is_eq = 0;
goto exit;
@@ -419,7 +491,7 @@ scpr_polygon_eq
/* Check actual coordinates */
matched = (char*)calloc(sz, sizeof(*matched));
FOR_EACH(i, 0, sz) {
- if(!one_path_is_eq(&polygon1->paths, &polygon2->paths.at(i), matched)) {
+ if(!one_path_is_eq(&polygon1->paths, &polygon2->paths[i], matched)) {
*is_eq = 0;
goto exit;
}
@@ -433,4 +505,72 @@ error:
goto exit;
}
+res_T
+scpr_polygon_dump_to_obj
+ (struct scpr_polygon* polygon,
+ FILE* stream)
+{
+ res_T res = RES_OK;
+ size_t i, j, n = 1;
+
+ if(!polygon || !stream) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ /* Vertice */
+ for(i = 0; i < polygon->paths.size(); i++) {
+ for(j = 0; j < polygon->paths[i].size(); j++) {
+ Clipper2Lib::Point64& pt = polygon->paths[i][j];
+ fprintf(stream, "v %zu %zu 0\n", pt.x, pt.y);
+ }
+ }
+
+ /* Lines */
+ for(i = 0; i < polygon->paths.size(); i++) {
+ size_t fst = n;
+ fprintf(stream, "l ");
+ for(j = 0; j < polygon->paths[i].size(); j++) {
+ fprintf(stream, " %zu", n++);
+ }
+ fprintf(stream, " %zu\n", fst);
+ }
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+res_T
+scpr_polygon_dump_component_to_obj
+ (struct scpr_polygon* polygon,
+ const size_t icomponent,
+ FILE* stream)
+{
+ res_T res = RES_OK;
+ size_t i;
+ if(!polygon || !stream || icomponent >= polygon->paths.size()) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ /* Vertice */
+ for(i = 0; i < polygon->paths[icomponent].size(); i++) {
+ Clipper2Lib::Point64& pt = polygon->paths[icomponent][i];
+ fprintf(stream, "v %zu %zu 0\n", pt.x, pt.y);
+ }
+
+ /* Line */
+ fprintf(stream, "l ");
+ for(i = 0; i < polygon->paths[icomponent].size(); i++) {
+ fprintf(stream, " %zu", ++i);
+ }
+ fprintf(stream, " 1\n");
+
+exit:
+ return res;
+error:
+ goto exit;
+}
diff --git a/src/test_scpr_clip.c b/src/test_scpr_clip.c
@@ -13,6 +13,8 @@
* 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"
@@ -29,17 +31,17 @@ dump_obj(FILE* stream, const struct scpr_mesh* mesh)
CHK(stream != NULL);
CHK(mesh != NULL);
- CHK(scpr_mesh_get_vertices_count(mesh, &n) == RES_OK);
+ OK(scpr_mesh_get_vertices_count(mesh, &n));
FOR_EACH(i, 0, n) {
double pos[2];
- CHK(scpr_mesh_get_position(mesh, i, pos) == RES_OK);
+ OK(scpr_mesh_get_position(mesh, i, pos));
fprintf(stream, "v %g %g 0\n", SPLIT2(pos));
}
- CHK(scpr_mesh_get_triangles_count(mesh, &n) == RES_OK);
+ OK(scpr_mesh_get_triangles_count(mesh, &n));
FOR_EACH(i, 0, n) {
size_t ids[3];
- CHK(scpr_mesh_get_indices(mesh, i, ids) == RES_OK);
+ OK(scpr_mesh_get_indices(mesh, i, ids));
fprintf(stream, "f %lu %lu %lu\n",
(unsigned long)(ids[0] + 1),
(unsigned long)(ids[1] + 1),
@@ -49,12 +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_pos[] = { 0.0, 0.0, 0.0, 1.0, 1.0, 0.0 };
+ const double triangle_pos1[] = { 0, 0, 0, 1, 1, 0 };
+ 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 };
@@ -63,46 +68,55 @@ test_triangle
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));
+ /* 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));
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, ncomps, pget_nverts, pget_pos, &pctx)
- == RES_OK);
+ OK(scpr_polygon_create(dev, &poly));
+ OK(scpr_polygon_setup_indexed_vertices(poly, ncomps, pget_nverts, pget_pos, &pctx));
- mctx.coords = triangle_pos;
+ /* Check out-of-range */
+ mctx.coords = triangle_pos2;
mctx.nverts = 3;
mctx.indices = triangle_ids;
mctx.ntris = 1;
- CHK(scpr_mesh_setup_indexed_vertices
- (mesh, mctx.ntris, mget_ids, 3, mget_pos, &mctx) == RES_OK);
-
- CHK(scpr_mesh_clip(NULL, SCPR_OPERATIONS_COUNT__, NULL) == RES_BAD_ARG);
- CHK(scpr_mesh_clip(mesh, SCPR_OPERATIONS_COUNT__, NULL) == RES_BAD_ARG);
- CHK(scpr_mesh_clip(NULL, SCPR_OPERATIONS_COUNT__, poly) == RES_BAD_ARG);
- CHK(scpr_mesh_clip(mesh, SCPR_OPERATIONS_COUNT__, poly) == RES_BAD_ARG);
- CHK(scpr_mesh_clip(NULL, SCPR_SUB, NULL) == RES_BAD_ARG);
- CHK(scpr_mesh_clip(mesh, SCPR_SUB, NULL) == RES_BAD_ARG);
- CHK(scpr_mesh_clip(NULL, SCPR_SUB, poly) == RES_BAD_ARG);
- CHK(scpr_mesh_clip(mesh, SCPR_SUB, poly) == RES_OK);
+ BAD(scpr_mesh_setup_indexed_vertices
+ (mesh, mctx.ntris, mget_ids, 3, mget_pos, &mctx));
+
+ mctx.coords = triangle_pos1;
+ OK(scpr_mesh_setup_indexed_vertices
+ (mesh, mctx.ntris, mget_ids, 3, mget_pos, &mctx));
+
+ BAD(scpr_mesh_clip(NULL, SCPR_OPERATIONS_COUNT__, NULL));
+ BAD(scpr_mesh_clip(mesh, SCPR_OPERATIONS_COUNT__, NULL));
+ BAD(scpr_mesh_clip(NULL, SCPR_OPERATIONS_COUNT__, poly));
+ BAD(scpr_mesh_clip(mesh, SCPR_OPERATIONS_COUNT__, poly));
+ BAD(scpr_mesh_clip(NULL, SCPR_SUB, NULL));
+ BAD(scpr_mesh_clip(mesh, SCPR_SUB, NULL));
+ BAD(scpr_mesh_clip(NULL, SCPR_SUB, poly));
+ OK(scpr_mesh_clip(mesh, SCPR_SUB, poly));
/*dump_obj(stdout, mesh);*/
- CHK(scpr_mesh_get_triangles_count(mesh, &ntris) == RES_OK);
+ OK(scpr_mesh_get_triangles_count(mesh, &ntris));
CHK(ntris == 3);
MEM_RM(allocator, *clip_pos);
MEM_RM(allocator, clip_pos);
- CHK(scpr_polygon_ref_put(poly) == RES_OK);
+ OK(scpr_polygon_ref_put(poly));
}
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 };
@@ -115,36 +129,36 @@ test_quad
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));
+ 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));
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, ncomps, pget_nverts, pget_pos, &pctx)
- == RES_OK);
+ OK(scpr_polygon_create(dev, &poly));
+ OK(scpr_polygon_setup_indexed_vertices(poly, ncomps, pget_nverts, pget_pos, &pctx));
mctx.coords = quad_pos;
mctx.nverts = sizeof(quad_pos)/(2*sizeof(double));
mctx.indices = quad_ids;
mctx.ntris = sizeof(quad_ids)/(3*sizeof(size_t));
- CHK(scpr_mesh_setup_indexed_vertices
- (mesh, mctx.ntris, mget_ids, mctx.nverts, mget_pos, &mctx) == RES_OK);
+ OK(scpr_mesh_setup_indexed_vertices
+ (mesh, mctx.ntris, mget_ids, mctx.nverts, mget_pos, &mctx));
- CHK(scpr_mesh_clip(mesh, SCPR_AND, poly) == RES_OK);
+ OK(scpr_mesh_clip(mesh, SCPR_AND, poly));
/*dump_obj(stdout, mesh);*/
MEM_RM(allocator, *clip_pos);
MEM_RM(allocator, clip_pos);
- CHK(scpr_polygon_ref_put(poly) == RES_OK);
+ OK(scpr_polygon_ref_put(poly));
}
static void
test_disk
- (struct mem_allocator* allocator,
+ (struct scpr_device* dev,
+ struct mem_allocator* allocator,
struct scpr_mesh* mesh)
{
double** clip_pos;
@@ -163,8 +177,8 @@ 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));
+ 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));
FOR_EACH(i, 0, ninternal_disks) {
@@ -214,18 +228,17 @@ test_disk
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, ncomps, pget_nverts, pget_pos, &pctx)
- == RES_OK);
+ OK(scpr_polygon_create(dev, &poly));
+ OK(scpr_polygon_setup_indexed_vertices(poly, ncomps, pget_nverts, pget_pos, &pctx));
mctx.coords = pos;
mctx.nverts = sa_size(pos)/2;
mctx.indices = ids;
mctx.ntris = sa_size(ids)/3;
- CHK(scpr_mesh_setup_indexed_vertices
- (mesh, mctx.ntris, mget_ids, mctx.nverts, mget_pos, &mctx) == RES_OK);
+ OK(scpr_mesh_setup_indexed_vertices
+ (mesh, mctx.ntris, mget_ids, mctx.nverts, mget_pos, &mctx));
- CHK(scpr_mesh_clip(mesh, SCPR_SUB, poly) == RES_OK);
+ OK(scpr_mesh_clip(mesh, SCPR_SUB, poly));
dump_obj(stdout, mesh);
@@ -233,25 +246,30 @@ test_disk
MEM_RM(allocator, clip_pos);
sa_release(pos);
sa_release(ids);
- CHK(scpr_polygon_ref_put(poly) == RES_OK);
+ OK(scpr_polygon_ref_put(poly));
}
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);
- CHK(scpr_mesh_create(&allocator, &mesh) == RES_OK);
+ 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);
- CHK(scpr_mesh_ref_put(mesh) == RES_OK);
+ 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_intersector.c b/src/test_scpr_intersector.c
@@ -0,0 +1,206 @@
+/* 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>
+
+struct cpt {
+ size_t simple, collinear;
+};
+
+static int
+simple_intersection
+ (struct scpr_callback_segment* segment1,
+ struct scpr_callback_segment* segment2,
+ void* data)
+{
+ struct cpt* cpt = (struct cpt*)data;
+ (void)segment1; (void)segment2;
+ ASSERT(cpt);
+ cpt->simple++;
+ return 0;
+}
+
+static int
+collinear_segments
+ (struct scpr_callback_segment* segment1,
+ struct scpr_callback_segment* segment2,
+ void* data)
+{
+ struct cpt* cpt = (struct cpt*)data;
+ (void)segment1; (void)segment2;
+ ASSERT(cpt);
+ cpt->collinear++;
+ return 0;
+}
+
+int
+main(int argc, char** argv)
+{
+ struct mem_allocator allocator;
+ struct scpr_device* dev;
+ struct scpr_device_create_args args = SCPR_DEVICE_CREATE_ARGS_DEFAULT;
+ struct scpr_intersector* inter;
+ struct scpr_intersector_check_callbacks cb
+ = SCPR_INTERSECTOR_CHECK_CALLBACKS_NULL__ ;
+ struct polygon_context ctx;
+ struct scpr_polygon *p01, *p23, *p4;
+ size_t ncomps = 2;
+ double** coords;
+ /* 1,1
+ * +-----------+----+
+ * | / | /
+ * | / | /
+ * +-----------+-C2--+
+ * | | | |
+ * | | | |
+ * | +-C0--+-+---+----+----+
+ * | 0,0 |/ | |
+ * | +-C3-------+-C4-+
+ * +-C1--------+
+ * -0.5,-0.5
+ */
+ double coords0[] = {
+ 0.0, 0.0,
+ 1.0, 0.0,
+ 1.0, 1.0,
+ 0.0, 1.0
+ };
+ double coords1[] = {
+ -0.5, -0.5,
+ 0.5, -0.5,
+ 0.5, 0.5,
+ -0.5, 0.5
+ };
+ double coords2[] = {
+ 0.5, 0.5,
+ 1.0, 0.5,
+ 1.5, 1.0,
+ 1.0, 1.0
+ };
+ double coords3[] = {
+ 0.5, -0.3,
+ 1.5, -0.3,
+ 1.5, 0.0,
+ 0.7, 0.0
+ };
+ double coords4[] = {
+ 1.5, -0.3,
+ 2.0, -0.3,
+ 2.0, 0.0,
+ 1.5, 0
+ };
+ size_t nverts[] = { 4, 4 };
+ struct cpt cpt = { 0, 0 };
+ (void)argc, (void)argv;
+
+ mem_init_proxy_allocator(&allocator, &mem_default_allocator);
+ args.allocator = &allocator;
+ args.verbosity_level = 3;
+ args.precision = 6;
+ OK(scpr_device_create(&args, &dev));
+
+ BAD(scpr_intersector_create(NULL, NULL));
+ BAD(scpr_intersector_create(dev, NULL));
+ BAD(scpr_intersector_create(NULL, &inter));
+ OK(scpr_intersector_create(dev, &inter));
+
+ BAD(scpr_intersector_ref_get(NULL));
+ OK(scpr_intersector_ref_get(inter));
+
+ BAD(scpr_intersector_ref_put(NULL));
+ OK(scpr_intersector_ref_put(inter));
+
+ OK(scpr_polygon_create(dev, &p01));
+ OK(scpr_polygon_create(dev, &p23));
+ OK(scpr_polygon_create(dev, &p4));
+ coords = (double**)MEM_CALLOC(&allocator, ncomps, sizeof(*coords));
+ coords[0] = (double*)MEM_CALLOC(&allocator, nverts[0], 2*sizeof(**coords));
+ coords[1] = (double*)MEM_CALLOC(&allocator, nverts[1], 2*sizeof(**coords));
+
+ ctx.coords = coords;
+ ctx.nverts = nverts;
+ ctx.ncomps = ncomps;
+
+ #define SETUP scpr_polygon_setup_indexed_vertices
+ memcpy(coords[0], coords0, 2*nverts[0]*sizeof(**coords));
+ memcpy(coords[1], coords1, 2*nverts[1]*sizeof(**coords));
+ OK(SETUP(p01, ncomps, pget_nverts, pget_pos, &ctx));
+
+ memcpy(coords[0], coords2, 2*nverts[0]*sizeof(**coords));
+ memcpy(coords[1], coords3, 2*nverts[1]*sizeof(**coords));
+ OK(SETUP(p23, ncomps, pget_nverts, pget_pos, &ctx));
+
+ memcpy(coords[0], coords4, 2*nverts[0]*sizeof(**coords));
+ OK(SETUP(p4, 1, pget_nverts, pget_pos, &ctx));
+
+ BAD(scpr_intersector_register_component(NULL, NULL, ncomps));
+ BAD(scpr_intersector_register_component(NULL, NULL, 0));
+ BAD(scpr_intersector_register_component(NULL, p01, ncomps));
+ BAD(scpr_intersector_register_component(inter, NULL, ncomps));
+ BAD(scpr_intersector_register_component(NULL, p01, 0));
+ BAD(scpr_intersector_register_component(inter, NULL, 0));
+ BAD(scpr_intersector_register_component(inter, p01, ncomps));
+ OK(scpr_intersector_register_component(inter, p01, 0));
+ OK(scpr_intersector_register_component(inter, p01, 1));
+ OK(scpr_intersector_register_component(inter, p23, 0));
+ OK(scpr_intersector_register_component(inter, p23, 1));
+ BAD(scpr_intersector_register_component(inter, p23, 1)); /* Registered twice */
+
+ BAD(scpr_intersector_register_polygon(NULL, NULL));
+ BAD(scpr_intersector_register_polygon(NULL, p01));
+ BAD(scpr_intersector_register_polygon(inter, NULL));
+ OK(scpr_intersector_register_polygon(inter, p4));
+ BAD(scpr_intersector_register_polygon(inter, p4)); /* Registered twice */
+
+ BAD(scpr_intersector_check(NULL, NULL, NULL));
+ BAD(scpr_intersector_check(NULL, &cb, NULL));
+ BAD(scpr_intersector_check(inter, NULL, NULL));
+ BAD(scpr_intersector_check(inter, &cb, NULL));
+ BAD(scpr_intersector_check(inter, &cb, &cpt)); /* Callbacks are all NULL */
+ /* Report only intersections */
+ cb.simple_intersection = simple_intersection;
+ OK(scpr_intersector_check(inter, &cb, &cpt));
+ CHK(cpt.simple == 2);
+ CHK(cpt.collinear == 0);
+ /* Report intersections and collinearity */
+ cb.collinear_segments = collinear_segments;
+ cpt.simple = cpt.collinear = 0;
+ OK(scpr_intersector_check(inter, &cb, &cpt));
+ CHK(cpt.simple == 2);
+ CHK(cpt.collinear == 2);
+
+ OK(scpr_polygon_ref_put(p01));
+ OK(scpr_polygon_ref_put(p23));
+ OK(scpr_polygon_ref_put(p4));
+ OK(scpr_intersector_ref_put(inter));
+ OK(scpr_device_ref_put(dev));
+
+ MEM_RM(&allocator, coords[0]);
+ 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;
+}
+
diff --git a/src/test_scpr_mesh.c b/src/test_scpr_mesh.c
@@ -13,6 +13,8 @@
* 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"
@@ -46,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;
@@ -53,17 +57,20 @@ main(int argc, char** argv)
mem_init_proxy_allocator(&allocator, &mem_default_allocator);
- CHK(scpr_mesh_create(NULL, NULL) == RES_BAD_ARG);
- CHK(scpr_mesh_create(&allocator, NULL) == RES_BAD_ARG);
- CHK(scpr_mesh_create(NULL, &mesh) == RES_OK);
+ args.allocator = &allocator;
+ OK(scpr_device_create(&args, &dev));
+
+ BAD(scpr_mesh_create(NULL, NULL));
+ BAD(scpr_mesh_create(dev, NULL));
+ OK(scpr_mesh_create(dev, &mesh));
- CHK(scpr_mesh_ref_get(NULL) == RES_BAD_ARG);
- CHK(scpr_mesh_ref_get(mesh) == RES_OK);
- CHK(scpr_mesh_ref_put(NULL) == RES_BAD_ARG);
- CHK(scpr_mesh_ref_put(mesh) == RES_OK);
- CHK(scpr_mesh_ref_put(mesh) == RES_OK);
+ BAD(scpr_mesh_ref_get(NULL));
+ OK(scpr_mesh_ref_get(mesh));
+ BAD(scpr_mesh_ref_put(NULL));
+ OK(scpr_mesh_ref_put(mesh));
+ OK(scpr_mesh_ref_put(mesh));
- CHK(scpr_mesh_create(&allocator, &mesh) == RES_OK);
+ OK(scpr_mesh_create(dev, &mesh));
ctx.coords = coords;
ctx.nverts = nverts;
@@ -71,122 +78,122 @@ main(int argc, char** argv)
ctx.ntris = ntris;
#define SETUP scpr_mesh_setup_indexed_vertices
- CHK(SETUP(NULL, 0, NULL, 0, NULL, NULL) == RES_BAD_ARG);
- CHK(SETUP(mesh, 0, NULL, 0, NULL, NULL) == RES_BAD_ARG);
- CHK(SETUP(NULL, ntris, NULL, 0, NULL, NULL) == RES_BAD_ARG);
- CHK(SETUP(mesh, ntris, NULL, 0, NULL, NULL) == RES_BAD_ARG);
- CHK(SETUP(NULL, 0, mget_ids, 0, NULL, NULL) == RES_BAD_ARG);
- CHK(SETUP(mesh, 0, mget_ids, 0, NULL, NULL) == RES_BAD_ARG);
- CHK(SETUP(NULL, ntris, mget_ids, 0, NULL, NULL) == RES_BAD_ARG);
- CHK(SETUP(mesh, ntris, mget_ids, 0, NULL, NULL) == RES_BAD_ARG);
- CHK(SETUP(NULL, 0, NULL, nverts, NULL, NULL) == RES_BAD_ARG);
- CHK(SETUP(mesh, 0, NULL, nverts, NULL, NULL) == RES_BAD_ARG);
- CHK(SETUP(NULL, ntris, NULL, nverts, NULL, NULL) == RES_BAD_ARG);
- CHK(SETUP(mesh, ntris, NULL, nverts, NULL, NULL) == RES_BAD_ARG);
- CHK(SETUP(NULL, 0, mget_ids, nverts, NULL, NULL) == RES_BAD_ARG);
- CHK(SETUP(mesh, 0, mget_ids, nverts, NULL, NULL) == RES_BAD_ARG);
- CHK(SETUP(NULL, ntris, mget_ids, nverts, NULL, NULL) == RES_BAD_ARG);
- CHK(SETUP(mesh, ntris, mget_ids, nverts, NULL, NULL) == RES_BAD_ARG);
- CHK(SETUP(NULL, 0, NULL, 0, mget_pos, NULL) == RES_BAD_ARG);
- CHK(SETUP(mesh, 0, NULL, 0, mget_pos, NULL) == RES_BAD_ARG);
- CHK(SETUP(NULL, ntris, NULL, 0, mget_pos, NULL) == RES_BAD_ARG);
- CHK(SETUP(mesh, ntris, NULL, 0, mget_pos, NULL) == RES_BAD_ARG);
- CHK(SETUP(NULL, 0, mget_ids, 0, mget_pos, NULL) == RES_BAD_ARG);
- CHK(SETUP(mesh, 0, mget_ids, 0, mget_pos, NULL) == RES_BAD_ARG);
- CHK(SETUP(NULL, ntris, mget_ids, 0, mget_pos, NULL) == RES_BAD_ARG);
- CHK(SETUP(mesh, ntris, mget_ids, 0, mget_pos, NULL) == RES_BAD_ARG);
- CHK(SETUP(NULL, 0, NULL, nverts, mget_pos, NULL) == RES_BAD_ARG);
- CHK(SETUP(mesh, 0, NULL, nverts, mget_pos, NULL) == RES_BAD_ARG);
- CHK(SETUP(NULL, ntris, NULL, nverts, mget_pos, NULL) == RES_BAD_ARG);
- CHK(SETUP(mesh, ntris, NULL, nverts, mget_pos, NULL) == RES_BAD_ARG);
- CHK(SETUP(NULL, 0, mget_ids, nverts, mget_pos, NULL) == RES_BAD_ARG);
- CHK(SETUP(mesh, 0, mget_ids, nverts, mget_pos, NULL) == RES_BAD_ARG);
- CHK(SETUP(NULL, ntris, mget_ids, nverts, mget_pos, NULL) == RES_BAD_ARG);
- CHK(SETUP(mesh, ntris, mget_ids, nverts, mget_pos, NULL) == RES_BAD_ARG);
- CHK(SETUP(NULL, 0, NULL, 0, NULL, &ctx) == RES_BAD_ARG);
- CHK(SETUP(mesh, 0, NULL, 0, NULL, &ctx) == RES_BAD_ARG);
- CHK(SETUP(NULL, ntris, NULL, 0, NULL, &ctx) == RES_BAD_ARG);
- CHK(SETUP(mesh, ntris, NULL, 0, NULL, &ctx) == RES_BAD_ARG);
- CHK(SETUP(NULL, 0, mget_ids, 0, NULL, &ctx) == RES_BAD_ARG);
- CHK(SETUP(mesh, 0, mget_ids, 0, NULL, &ctx) == RES_BAD_ARG);
- CHK(SETUP(NULL, ntris, mget_ids, 0, NULL, &ctx) == RES_BAD_ARG);
- CHK(SETUP(mesh, ntris, mget_ids, 0, NULL, &ctx) == RES_BAD_ARG);
- CHK(SETUP(NULL, 0, NULL, nverts, NULL, &ctx) == RES_BAD_ARG);
- CHK(SETUP(mesh, 0, NULL, nverts, NULL, &ctx) == RES_BAD_ARG);
- CHK(SETUP(NULL, ntris, NULL, nverts, NULL, &ctx) == RES_BAD_ARG);
- CHK(SETUP(mesh, ntris, NULL, nverts, NULL, &ctx) == RES_BAD_ARG);
- CHK(SETUP(NULL, 0, mget_ids, nverts, NULL, &ctx) == RES_BAD_ARG);
- CHK(SETUP(mesh, 0, mget_ids, nverts, NULL, &ctx) == RES_BAD_ARG);
- CHK(SETUP(NULL, ntris, mget_ids, nverts, NULL, &ctx) == RES_BAD_ARG);
- CHK(SETUP(mesh, ntris, mget_ids, nverts, NULL, &ctx) == RES_BAD_ARG);
- CHK(SETUP(NULL, 0, NULL, 0, mget_pos, &ctx) == RES_BAD_ARG);
- CHK(SETUP(mesh, 0, NULL, 0, mget_pos, &ctx) == RES_BAD_ARG);
- CHK(SETUP(NULL, ntris, NULL, 0, mget_pos, &ctx) == RES_BAD_ARG);
- CHK(SETUP(mesh, ntris, NULL, 0, mget_pos, &ctx) == RES_BAD_ARG);
- CHK(SETUP(NULL, 0, mget_ids, 0, mget_pos, &ctx) == RES_BAD_ARG);
- CHK(SETUP(mesh, 0, mget_ids, 0, mget_pos, &ctx) == RES_BAD_ARG);
- CHK(SETUP(NULL, ntris, mget_ids, 0, mget_pos, &ctx) == RES_BAD_ARG);
- CHK(SETUP(mesh, ntris, mget_ids, 0, mget_pos, &ctx) == RES_BAD_ARG);
- CHK(SETUP(NULL, 0, NULL, nverts, mget_pos, &ctx) == RES_BAD_ARG);
- CHK(SETUP(mesh, 0, NULL, nverts, mget_pos, &ctx) == RES_BAD_ARG);
- CHK(SETUP(NULL, ntris, NULL, nverts, mget_pos, &ctx) == RES_BAD_ARG);
- CHK(SETUP(mesh, ntris, NULL, nverts, mget_pos, &ctx) == RES_BAD_ARG);
- CHK(SETUP(NULL, 0, mget_ids, nverts, mget_pos, &ctx) == RES_BAD_ARG);
- CHK(SETUP(mesh, 0, mget_ids, nverts, mget_pos, &ctx) == RES_BAD_ARG);
- CHK(SETUP(NULL, ntris, mget_ids, nverts, mget_pos, &ctx) == RES_BAD_ARG);
- CHK(SETUP(mesh, ntris, mget_ids, nverts, mget_pos, &ctx) == RES_OK);
+ BAD(SETUP(NULL, 0, NULL, 0, NULL, NULL));
+ BAD(SETUP(mesh, 0, NULL, 0, NULL, NULL));
+ BAD(SETUP(NULL, ntris, NULL, 0, NULL, NULL));
+ BAD(SETUP(mesh, ntris, NULL, 0, NULL, NULL));
+ BAD(SETUP(NULL, 0, mget_ids, 0, NULL, NULL));
+ BAD(SETUP(mesh, 0, mget_ids, 0, NULL, NULL));
+ BAD(SETUP(NULL, ntris, mget_ids, 0, NULL, NULL));
+ BAD(SETUP(mesh, ntris, mget_ids, 0, NULL, NULL));
+ BAD(SETUP(NULL, 0, NULL, nverts, NULL, NULL));
+ BAD(SETUP(mesh, 0, NULL, nverts, NULL, NULL));
+ BAD(SETUP(NULL, ntris, NULL, nverts, NULL, NULL));
+ BAD(SETUP(mesh, ntris, NULL, nverts, NULL, NULL));
+ BAD(SETUP(NULL, 0, mget_ids, nverts, NULL, NULL));
+ BAD(SETUP(mesh, 0, mget_ids, nverts, NULL, NULL));
+ BAD(SETUP(NULL, ntris, mget_ids, nverts, NULL, NULL));
+ BAD(SETUP(mesh, ntris, mget_ids, nverts, NULL, NULL));
+ BAD(SETUP(NULL, 0, NULL, 0, mget_pos, NULL));
+ BAD(SETUP(mesh, 0, NULL, 0, mget_pos, NULL));
+ BAD(SETUP(NULL, ntris, NULL, 0, mget_pos, NULL));
+ BAD(SETUP(mesh, ntris, NULL, 0, mget_pos, NULL));
+ BAD(SETUP(NULL, 0, mget_ids, 0, mget_pos, NULL));
+ BAD(SETUP(mesh, 0, mget_ids, 0, mget_pos, NULL));
+ BAD(SETUP(NULL, ntris, mget_ids, 0, mget_pos, NULL));
+ BAD(SETUP(mesh, ntris, mget_ids, 0, mget_pos, NULL));
+ BAD(SETUP(NULL, 0, NULL, nverts, mget_pos, NULL));
+ BAD(SETUP(mesh, 0, NULL, nverts, mget_pos, NULL));
+ BAD(SETUP(NULL, ntris, NULL, nverts, mget_pos, NULL));
+ BAD(SETUP(mesh, ntris, NULL, nverts, mget_pos, NULL));
+ BAD(SETUP(NULL, 0, mget_ids, nverts, mget_pos, NULL));
+ BAD(SETUP(mesh, 0, mget_ids, nverts, mget_pos, NULL));
+ BAD(SETUP(NULL, ntris, mget_ids, nverts, mget_pos, NULL));
+ BAD(SETUP(mesh, ntris, mget_ids, nverts, mget_pos, NULL));
+ BAD(SETUP(NULL, 0, NULL, 0, NULL, &ctx));
+ BAD(SETUP(mesh, 0, NULL, 0, NULL, &ctx));
+ BAD(SETUP(NULL, ntris, NULL, 0, NULL, &ctx));
+ BAD(SETUP(mesh, ntris, NULL, 0, NULL, &ctx));
+ BAD(SETUP(NULL, 0, mget_ids, 0, NULL, &ctx));
+ BAD(SETUP(mesh, 0, mget_ids, 0, NULL, &ctx));
+ BAD(SETUP(NULL, ntris, mget_ids, 0, NULL, &ctx));
+ BAD(SETUP(mesh, ntris, mget_ids, 0, NULL, &ctx));
+ BAD(SETUP(NULL, 0, NULL, nverts, NULL, &ctx));
+ BAD(SETUP(mesh, 0, NULL, nverts, NULL, &ctx));
+ BAD(SETUP(NULL, ntris, NULL, nverts, NULL, &ctx));
+ BAD(SETUP(mesh, ntris, NULL, nverts, NULL, &ctx));
+ BAD(SETUP(NULL, 0, mget_ids, nverts, NULL, &ctx));
+ BAD(SETUP(mesh, 0, mget_ids, nverts, NULL, &ctx));
+ BAD(SETUP(NULL, ntris, mget_ids, nverts, NULL, &ctx));
+ BAD(SETUP(mesh, ntris, mget_ids, nverts, NULL, &ctx));
+ BAD(SETUP(NULL, 0, NULL, 0, mget_pos, &ctx));
+ BAD(SETUP(mesh, 0, NULL, 0, mget_pos, &ctx));
+ BAD(SETUP(NULL, ntris, NULL, 0, mget_pos, &ctx));
+ BAD(SETUP(mesh, ntris, NULL, 0, mget_pos, &ctx));
+ BAD(SETUP(NULL, 0, mget_ids, 0, mget_pos, &ctx));
+ BAD(SETUP(mesh, 0, mget_ids, 0, mget_pos, &ctx));
+ BAD(SETUP(NULL, ntris, mget_ids, 0, mget_pos, &ctx));
+ BAD(SETUP(mesh, ntris, mget_ids, 0, mget_pos, &ctx));
+ BAD(SETUP(NULL, 0, NULL, nverts, mget_pos, &ctx));
+ BAD(SETUP(mesh, 0, NULL, nverts, mget_pos, &ctx));
+ BAD(SETUP(NULL, ntris, NULL, nverts, mget_pos, &ctx));
+ BAD(SETUP(mesh, ntris, NULL, nverts, mget_pos, &ctx));
+ BAD(SETUP(NULL, 0, mget_ids, nverts, mget_pos, &ctx));
+ BAD(SETUP(mesh, 0, mget_ids, nverts, mget_pos, &ctx));
+ BAD(SETUP(NULL, ntris, mget_ids, nverts, mget_pos, &ctx));
+ OK(SETUP(mesh, ntris, mget_ids, nverts, mget_pos, &ctx));
ctx.indices = indices_bad;
- CHK(SETUP(mesh, 1, mget_ids, nverts, mget_pos, &ctx) == RES_BAD_ARG);
+ BAD(SETUP(mesh, 1, mget_ids, nverts, mget_pos, &ctx));
#undef SETUP
- CHK(scpr_mesh_get_triangles_count(NULL, NULL) == RES_BAD_ARG);
- CHK(scpr_mesh_get_triangles_count(mesh, NULL) == RES_BAD_ARG);
- CHK(scpr_mesh_get_triangles_count(NULL, &n) == RES_BAD_ARG);
- CHK(scpr_mesh_get_triangles_count(mesh, &n) == RES_OK);
+ BAD(scpr_mesh_get_triangles_count(NULL, NULL));
+ BAD(scpr_mesh_get_triangles_count(mesh, NULL));
+ BAD(scpr_mesh_get_triangles_count(NULL, &n));
+ OK(scpr_mesh_get_triangles_count(mesh, &n));
CHK(n == 0);
- CHK(scpr_mesh_get_vertices_count(NULL, NULL) == RES_BAD_ARG);
- CHK(scpr_mesh_get_vertices_count(mesh, NULL) == RES_BAD_ARG);
- CHK(scpr_mesh_get_vertices_count(NULL, &n) == RES_BAD_ARG);
- CHK(scpr_mesh_get_vertices_count(mesh, &n) == RES_OK);
+ BAD(scpr_mesh_get_vertices_count(NULL, NULL));
+ BAD(scpr_mesh_get_vertices_count(mesh, NULL));
+ BAD(scpr_mesh_get_vertices_count(NULL, &n));
+ OK(scpr_mesh_get_vertices_count(mesh, &n));
CHK(n == 0);
ctx.indices = indices;
- CHK(scpr_mesh_setup_indexed_vertices
- (mesh, ntris, mget_ids, nverts, mget_pos, &ctx) == RES_OK);
- CHK(scpr_mesh_get_triangles_count(mesh, &n) == RES_OK);
+ OK(scpr_mesh_setup_indexed_vertices(mesh, ntris, mget_ids, nverts, mget_pos, &ctx));
+ OK(scpr_mesh_get_triangles_count(mesh, &n));
CHK(n == ntris);
- CHK(scpr_mesh_get_vertices_count(mesh, &n) == RES_OK);
+ OK(scpr_mesh_get_vertices_count(mesh, &n));
CHK(n == nverts);
- CHK(scpr_mesh_get_indices(NULL, ntris, NULL) == RES_BAD_ARG);
- CHK(scpr_mesh_get_indices(mesh, ntris, NULL) == RES_BAD_ARG);
- CHK(scpr_mesh_get_indices(NULL, 0, NULL) == RES_BAD_ARG);
- CHK(scpr_mesh_get_indices(mesh, 0, NULL) == RES_BAD_ARG);
- CHK(scpr_mesh_get_indices(NULL, ntris, ids) == RES_BAD_ARG);
- CHK(scpr_mesh_get_indices(mesh, ntris, ids) == RES_BAD_ARG);
- CHK(scpr_mesh_get_indices(NULL, 0, ids) == RES_BAD_ARG);
+ BAD(scpr_mesh_get_indices(NULL, ntris, NULL));
+ BAD(scpr_mesh_get_indices(mesh, ntris, NULL));
+ BAD(scpr_mesh_get_indices(NULL, 0, NULL));
+ BAD(scpr_mesh_get_indices(mesh, 0, NULL));
+ BAD(scpr_mesh_get_indices(NULL, ntris, ids));
+ BAD(scpr_mesh_get_indices(mesh, ntris, ids));
+ BAD(scpr_mesh_get_indices(NULL, 0, ids));
FOR_EACH(i, 0, ntris) {
- CHK(scpr_mesh_get_indices(mesh, i, ids) == RES_OK);
+ OK(scpr_mesh_get_indices(mesh, i, ids));
CHK(ids[0] == indices[i*3+0]);
CHK(ids[1] == indices[i*3+1]);
CHK(ids[2] == indices[i*3+2]);
}
- CHK(scpr_mesh_get_position(NULL, nverts, NULL) == RES_BAD_ARG);
- CHK(scpr_mesh_get_position(mesh, nverts, NULL) == RES_BAD_ARG);
- CHK(scpr_mesh_get_position(NULL, 0, NULL) == RES_BAD_ARG);
- CHK(scpr_mesh_get_position(mesh, 0, NULL) == RES_BAD_ARG);
- CHK(scpr_mesh_get_position(NULL, nverts, pos) == RES_BAD_ARG);
- CHK(scpr_mesh_get_position(mesh, nverts, pos) == RES_BAD_ARG);
- CHK(scpr_mesh_get_position(NULL, 0, pos) == RES_BAD_ARG);
+ BAD(scpr_mesh_get_position(NULL, nverts, NULL));
+ BAD(scpr_mesh_get_position(mesh, nverts, NULL));
+ BAD(scpr_mesh_get_position(NULL, 0, NULL));
+ BAD(scpr_mesh_get_position(mesh, 0, NULL));
+ BAD(scpr_mesh_get_position(NULL, nverts, pos));
+ BAD(scpr_mesh_get_position(mesh, nverts, pos));
+ CHK(scpr_mesh_get_position(NULL, 0, pos));
FOR_EACH(i, 0, nverts) {
- CHK(scpr_mesh_get_position(mesh, i, pos) == RES_OK);
+ OK(scpr_mesh_get_position(mesh, i, pos));
CHK(pos[0] == coords[i*2+0]);
CHK(pos[1] == coords[i*2+1]);
}
- CHK(scpr_mesh_ref_put(mesh) == RES_OK);
+ 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
@@ -13,6 +13,8 @@
* 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"
@@ -33,102 +35,123 @@ 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, 9223372036854,
+ 1.0, 1000000000000, /* To be replaced */
1.0, 0.0
};
- const double coords3[] = {
- 9223372036855, 0.0,
- 0.0, 1.0,
- 1.0, 9223372036854,
- 1.0, 0.0
+ double coords2_reverse[] = {
+ 0.12345678901234, 0.0,
+ 1.0, 0.0,
+ 1.0, 1000000000000, /* To be replaced */
+ 0.0, 1.0
};
double** coords;
+ double range[2];
size_t nverts[] = { 4 };
size_t 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));
- coords = MEM_CALLOC(&allocator, ncomps, sizeof(*coords));
- *coords = MEM_CALLOC(&allocator, nverts[0], 2*sizeof(**coords));
+ /* 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));
- CHK(scpr_polygon_create(&allocator, &polygon) == RES_OK);
- CHK(scpr_polygon_create(&allocator, &expected) == RES_OK);
+ OK(scpr_polygon_create(dev, &polygon));
+ OK(scpr_polygon_create(dev, &expected));
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);
+ OK(scpr_polygon_setup_indexed_vertices(polygon, ncomps, pget_nverts, pget_pos, &ctx));
+ OK(scpr_polygon_setup_indexed_vertices(expected, ncomps, pget_nverts, pget_pos, &ctx));
/* Offset 0 = unchanged */
- CHK(scpr_offset_polygon(polygon, 0, SCPR_JOIN_MITER) == RES_OK);
- CHK(scpr_polygon_eq(polygon, expected, &eq) == RES_OK);
+ OK(scpr_offset_polygon(polygon, 0, SCPR_JOIN_MITER));
+ OK(scpr_polygon_eq(polygon, expected, &eq));
CHK(eq);
+ CHK(check_stability(dev, polygon));
/* 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);
+ OK(scpr_polygon_setup_indexed_vertices(expected, ncomps, pget_nverts, pget_pos, &ctx));
- CHK(scpr_offset_polygon(polygon, 1, SCPR_JOIN_MITER) == RES_OK);
- CHK(scpr_polygon_eq(polygon, expected, &eq) == RES_OK);
+ OK(scpr_offset_polygon(polygon, 1, SCPR_JOIN_MITER));
+ OK(scpr_polygon_eq(polygon, expected, &eq));
CHK(eq);
+ CHK(check_stability(dev, polygon));
/* 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);
+ OK(scpr_polygon_setup_indexed_vertices(expected, ncomps, pget_nverts, pget_pos, &ctx));
+
+ OK(scpr_offset_polygon(polygon, -1, SCPR_JOIN_MITER));
+ OK(scpr_polygon_eq(polygon, expected, &eq));
+ CHK(eq);
+ CHK(check_stability(dev, polygon));
- CHK(scpr_offset_polygon(polygon, -1, SCPR_JOIN_MITER) == RES_OK);
- CHK(scpr_polygon_eq(polygon, expected, &eq) == RES_OK);
+ /* 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(dev, polygon));
/* Offset -5: empty polygon */
ncomps = 0;
ctx.ncomps = ncomps;
- CHK(scpr_polygon_setup_indexed_vertices(expected, ncomps, pget_nverts, pget_pos, &ctx)
- == RES_OK);
+ OK(scpr_polygon_setup_indexed_vertices(expected, ncomps, pget_nverts, pget_pos, &ctx));
- CHK(scpr_offset_polygon(polygon, -5, SCPR_JOIN_MITER) == RES_OK);
- CHK(scpr_polygon_eq(polygon, expected, &eq) == RES_OK);
+ OK(scpr_offset_polygon(polygon, -5, SCPR_JOIN_MITER));
+ OK(scpr_polygon_eq(polygon, expected, &eq));
CHK(eq);
+ CHK(check_stability(dev, polygon));
- /* Check consistency with a non representable coordinate */
+ /* Check coordinates barely in-range */
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);
+ OK(scpr_polygon_setup_indexed_vertices(polygon, ncomps, pget_nverts, pget_pos, &ctx));
+ OK(scpr_polygon_setup_indexed_vertices(expected, ncomps, pget_nverts, pget_pos, &ctx));
/* Offset 0 = unchanged */
- CHK(scpr_offset_polygon(polygon, 0, SCPR_JOIN_MITER) == RES_OK);
- CHK(scpr_polygon_eq(polygon, expected, &eq) == RES_OK);
+ OK(scpr_offset_polygon(polygon, 0, SCPR_JOIN_MITER));
+ OK(scpr_polygon_eq(polygon, expected, &eq));
CHK(eq);
+ CHK(check_stability(dev, polygon));
- /* 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);
+ /* Check non-effect of CW/CCW */
+ memcpy(*coords, coords2_reverse, 2*nverts[0]*sizeof(**coords));
+ OK(scpr_polygon_setup_indexed_vertices(polygon, ncomps, pget_nverts, pget_pos, &ctx));
+ OK(scpr_offset_polygon(polygon, 0, SCPR_JOIN_MITER));
+ OK(scpr_polygon_eq(polygon, expected, &eq));
+ CHK(eq);
+ CHK(check_stability(dev, polygon));
+
+ /* Check out of range after offset being detected */
+ BAD(scpr_offset_polygon(polygon, 1, SCPR_JOIN_MITER));
/* Cleanup */
- CHK(scpr_polygon_ref_put(polygon) == RES_OK);
- CHK(scpr_polygon_ref_put(expected) == RES_OK);
+ OK(scpr_polygon_ref_put(polygon));
+ OK(scpr_device_ref_put(dev));
+ OK(scpr_polygon_ref_put(expected));
MEM_RM(&allocator, *coords);
MEM_RM(&allocator, coords);
@@ -178,76 +201,75 @@ 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 = 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));
+ coords = (double**)MEM_CALLOC(&allocator, ncomps, sizeof(*coords));
+ *coords = (double*)MEM_CALLOC(&allocator, nverts[0], 2*sizeof(**coords));
+ *(coords+1) = (double*)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);
+ OK(scpr_polygon_create(dev, &polygon));
+ OK(scpr_polygon_create(dev, &expected));
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);
+ OK(scpr_polygon_setup_indexed_vertices(polygon, ncomps, pget_nverts, pget_pos, &ctx));
+ OK(scpr_polygon_setup_indexed_vertices(expected, ncomps, pget_nverts, pget_pos, &ctx));
/* Offset 0 = unchanged */
- CHK(scpr_offset_polygon(polygon, 0, SCPR_JOIN_MITER) == RES_OK);
- CHK(scpr_polygon_eq(polygon, expected, &eq) == RES_OK);
+ OK(scpr_offset_polygon(polygon, 0, SCPR_JOIN_MITER));
+ OK(scpr_polygon_eq(polygon, expected, &eq));
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);
+ OK(scpr_polygon_setup_indexed_vertices(expected, ncomps, pget_nverts, pget_pos, &ctx));
- CHK(scpr_offset_polygon(polygon, 1, SCPR_JOIN_MITER) == RES_OK);
- CHK(scpr_polygon_eq(polygon, expected, &eq) == RES_OK);
+ OK(scpr_offset_polygon(polygon, 1, SCPR_JOIN_MITER));
+ OK(scpr_polygon_eq(polygon, expected, &eq));
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);
+ OK(scpr_polygon_setup_indexed_vertices(expected, ncomps, pget_nverts, pget_pos, &ctx));
- CHK(scpr_offset_polygon(polygon, -1, SCPR_JOIN_MITER) == RES_OK);
- CHK(scpr_polygon_eq(polygon, expected, &eq) == RES_OK);
+ OK(scpr_offset_polygon(polygon, -1, SCPR_JOIN_MITER));
+ OK(scpr_polygon_eq(polygon, expected, &eq));
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);
+ OK(scpr_polygon_setup_indexed_vertices(expected, ncomps, pget_nverts, pget_pos, &ctx));
- CHK(scpr_offset_polygon(polygon, 4.5, SCPR_JOIN_MITER) == RES_OK);
- CHK(scpr_polygon_eq(polygon, expected, &eq) == RES_OK);
+ OK(scpr_offset_polygon(polygon, 4.5, SCPR_JOIN_MITER));
+ OK(scpr_polygon_eq(polygon, expected, &eq));
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);
+ OK(scpr_polygon_setup_indexed_vertices(expected, ncomps, pget_nverts, pget_pos, &ctx));
- CHK(scpr_offset_polygon(polygon, -5, SCPR_JOIN_MITER) == RES_OK);
- CHK(scpr_polygon_eq(polygon, expected, &eq) == RES_OK);
+ OK(scpr_offset_polygon(polygon, -5, SCPR_JOIN_MITER));
+ OK(scpr_polygon_eq(polygon, expected, &eq));
CHK(eq);
- CHK(scpr_polygon_ref_put(polygon) == RES_OK);
- CHK(scpr_polygon_ref_put(expected) == RES_OK);
+ OK(scpr_polygon_ref_put(polygon));
+ OK(scpr_device_ref_put(dev));
+ OK(scpr_polygon_ref_put(expected));
MEM_RM(&allocator, *coords);
MEM_RM(&allocator, *(coords+1));
@@ -298,62 +320,61 @@ 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 = 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));
+ coords = (double**)MEM_CALLOC(&allocator, ncomps, sizeof(*coords));
+ *coords = (double*)MEM_CALLOC(&allocator, nverts[0], 2*sizeof(**coords));
+ *(coords+1) = (double*)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);
+ OK(scpr_polygon_create(dev, &polygon));
+ OK(scpr_polygon_create(dev, &expected));
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);
+ OK(scpr_polygon_setup_indexed_vertices(polygon, ncomps, pget_nverts, pget_pos, &ctx));
+ OK(scpr_polygon_setup_indexed_vertices(expected, ncomps, pget_nverts, pget_pos, &ctx));
/* Offset 0 = unchanged */
- CHK(scpr_offset_polygon(polygon, 0, SCPR_JOIN_MITER) == RES_OK);
- CHK(scpr_polygon_eq(polygon, expected, &eq) == RES_OK);
+ OK(scpr_offset_polygon(polygon, 0, SCPR_JOIN_MITER));
+ OK(scpr_polygon_eq(polygon, expected, &eq));
CHK(eq);
/* Offset -1 */
memcpy(*coords, coords3, 2*nverts[0]*sizeof(**coords));
memcpy(*(coords+1), coords2, 2*nverts[1]*sizeof(**coords));
- CHK(scpr_polygon_setup_indexed_vertices(expected, ncomps, pget_nverts, pget_pos, &ctx)
- == RES_OK);
+ OK(scpr_polygon_setup_indexed_vertices(expected, ncomps, pget_nverts, pget_pos, &ctx));
- CHK(scpr_offset_polygon(polygon, -1, SCPR_JOIN_MITER) == RES_OK);
- CHK(scpr_polygon_eq(polygon, expected, &eq) == RES_OK);
+ OK(scpr_offset_polygon(polygon, -1, SCPR_JOIN_MITER));
+ OK(scpr_polygon_eq(polygon, expected, &eq));
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);
+ OK(scpr_polygon_setup_indexed_vertices(expected, ncomps, pget_nverts, pget_pos, &ctx));
- CHK(scpr_offset_polygon(polygon, 1, SCPR_JOIN_MITER) == RES_OK);
- CHK(scpr_polygon_eq(polygon, expected, &eq) == RES_OK);
+ OK(scpr_offset_polygon(polygon, 1, SCPR_JOIN_MITER));
+ OK(scpr_polygon_eq(polygon, expected, &eq));
CHK(eq);
/* Offset 5: internal path disappears */
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);
+ OK(scpr_polygon_setup_indexed_vertices(expected, ncomps, pget_nverts, pget_pos, &ctx));
- CHK(scpr_offset_polygon(polygon, 5, SCPR_JOIN_MITER) == RES_OK);
- CHK(scpr_polygon_eq(polygon, expected, &eq) == RES_OK);
+ OK(scpr_offset_polygon(polygon, 5, SCPR_JOIN_MITER));
+ OK(scpr_polygon_eq(polygon, expected, &eq));
CHK(eq);
/* From the original polygon, offset -2.5: empty polygon */
@@ -361,20 +382,19 @@ test_internal(void)
ctx.ncomps = ncomps;
memcpy(*coords, coords0, 2*nverts[0]*sizeof(**coords));
memcpy(*(coords+1), coords1, 2*nverts[1]*sizeof(**coords));
- CHK(scpr_polygon_setup_indexed_vertices(polygon, ncomps, pget_nverts, pget_pos, &ctx)
- == RES_OK);
+ OK(scpr_polygon_setup_indexed_vertices(polygon, ncomps, pget_nverts, pget_pos, &ctx));
ncomps = 0;
ctx.ncomps = ncomps;
- CHK(scpr_polygon_setup_indexed_vertices(expected, ncomps, pget_nverts, pget_pos, &ctx)
- == RES_OK);
+ OK(scpr_polygon_setup_indexed_vertices(expected, ncomps, pget_nverts, pget_pos, &ctx));
- CHK(scpr_offset_polygon(polygon, -2.5, SCPR_JOIN_MITER) == RES_OK);
- CHK(scpr_polygon_eq(polygon, expected, &eq) == RES_OK);
+ OK(scpr_offset_polygon(polygon, -2.5, SCPR_JOIN_MITER));
+ OK(scpr_polygon_eq(polygon, expected, &eq));
CHK(eq);
- CHK(scpr_polygon_ref_put(polygon) == RES_OK);
- CHK(scpr_polygon_ref_put(expected) == RES_OK);
+ 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
@@ -13,6 +13,9 @@
* 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 <stdio.h>
+#define _POSIX_C_SOURCE 200112L
+
#include "scpr.h"
#include "test_scpr_utils.h"
@@ -24,48 +27,71 @@ main(int argc, char** argv)
{
double coords0[] = {
0.0, 0.0,
- 0.0, 0.5,
- 0.0, 1.0,
0.5, 0.0,
- 0.5, 0.5,
- 0.5, 1.0,
1.0, 0.0,
- 1.0, 0.5,
- 1.0, 1.0
+ 1.0, 1.0,
+ 1.0, 1.0,
+ 0.0, 1.0
+ };
+ double coords0_reduced[] = {
+ 0.0, 0.0,
+ 1.0, 0.0,
+ 1.0, 1.0,
+ 0.0, 1.0
+ };
+ double coords1[] = {
+ 0.0, 0.0,
+ 0.5, 0.0,
+ 0.0, 1.0,
+ 1.0, 1.0,
+ 1.0, 1000000000001, /* To be replaced */
+ 1.0, 0.0
};
double** coords;
- size_t nverts[] = { 9 };
+ 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};
+ FILE* stream = NULL;
(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));
+ 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));
- 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);
- CHK(scpr_polygon_create(NULL, ©) == RES_OK);
- CHK(scpr_polygon_ref_put(copy) == RES_OK);
+ 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;
- CHK(scpr_polygon_ref_get(NULL) == RES_BAD_ARG);
- CHK(scpr_polygon_ref_get(polygon) == RES_OK);
- CHK(scpr_polygon_ref_put(NULL) == RES_BAD_ARG);
- CHK(scpr_polygon_ref_put(polygon) == RES_OK);
- CHK(scpr_polygon_ref_put(polygon) == RES_OK);
+ BAD(scpr_polygon_create(NULL, NULL));
+ BAD(scpr_polygon_create(dev, NULL));
+ OK(scpr_polygon_create(dev, &polygon));
+ OK(scpr_polygon_create(dev, ©));
+ OK(scpr_polygon_ref_put(copy));
- CHK(scpr_polygon_create(&allocator, &polygon) == RES_OK);
+ BAD(scpr_polygon_ref_get(NULL));
+ OK(scpr_polygon_ref_get(polygon));
+ BAD(scpr_polygon_ref_put(NULL));
+ OK(scpr_polygon_ref_put(polygon));
+ OK(scpr_polygon_ref_put(polygon));
- CHK(scpr_polygon_get_components_count(polygon, &n) == RES_OK);
+ OK(scpr_polygon_create(dev, &polygon));
+
+ OK(scpr_polygon_get_components_count(polygon, &n));
CHK(n == 0);
ctx.coords = coords;
@@ -73,79 +99,137 @@ main(int argc, char** argv)
ctx.ncomps = ncomps;
#define SETUP scpr_polygon_setup_indexed_vertices
- 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);
+ BAD(SETUP(NULL, ncomps, NULL, NULL, NULL));
+ BAD(SETUP(polygon, ncomps, NULL, NULL, NULL));
+ BAD(SETUP(NULL, ncomps, pget_nverts, NULL, NULL));
+ BAD(SETUP(polygon, ncomps, pget_nverts, NULL, NULL));
+ BAD(SETUP(NULL, ncomps, NULL, pget_pos, NULL));
+ BAD(SETUP(polygon, ncomps, NULL, pget_pos, NULL));
+ BAD(SETUP(NULL, ncomps, pget_nverts, pget_pos, NULL));
+ BAD(SETUP(polygon, ncomps, pget_nverts, pget_pos, NULL));
+ BAD(SETUP(NULL, ncomps, NULL, NULL, &ctx));
+ BAD(SETUP(polygon, ncomps, NULL, NULL, &ctx));
+ BAD(SETUP(NULL, ncomps, pget_nverts, NULL, &ctx));
+ BAD(SETUP(polygon, ncomps, pget_nverts, NULL, &ctx));
+ BAD(SETUP(NULL, ncomps, NULL, pget_pos, &ctx));
+ BAD(SETUP(polygon, ncomps, NULL, pget_pos, &ctx));
+ BAD(SETUP(NULL, ncomps, pget_nverts, pget_pos, &ctx));
+ /* Check out of range data being detected at setup */
+ memcpy(*coords, coords1, 2*nverts[0]*sizeof(**coords));
+ BAD(SETUP(polygon, ncomps, pget_nverts, pget_pos, &ctx));
+ /* Check polygon was simplified to 4 vertices */
+ memcpy(*coords, coords0, 2*nverts[0]*sizeof(**coords));
+ OK(SETUP(polygon, ncomps, pget_nverts, pget_pos, &ctx));
+ OK(scpr_polygon_get_vertices_count(polygon, 0, &n));
+ CHK(n == sizeof(coords0_reduced)/(2*sizeof(*coords0_reduced)));
#undef SETUP
- 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);
+ 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));
+ OK(scpr_polygon_get_components_count(polygon, &n));
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);
+ BAD(scpr_polygon_get_vertices_count(NULL, ncomps, NULL));
+ BAD(scpr_polygon_get_vertices_count(polygon, ncomps, NULL));
+ BAD(scpr_polygon_get_vertices_count(NULL, 0, NULL));
+ BAD(scpr_polygon_get_vertices_count(polygon, 0, NULL));
+ BAD(scpr_polygon_get_vertices_count(NULL, ncomps, &n));
+ BAD(scpr_polygon_get_vertices_count(polygon, ncomps, &n));
+ BAD(scpr_polygon_get_vertices_count(NULL, 0, &n));
+ OK(scpr_polygon_get_vertices_count(polygon, 0, &n));
+
+ BAD(scpr_polygon_eq(NULL, NULL, NULL));
+ BAD(scpr_polygon_eq(polygon, NULL, NULL));
+ BAD(scpr_polygon_eq(NULL, polygon, NULL));
+ BAD(scpr_polygon_eq(polygon, polygon, NULL));
+ BAD(scpr_polygon_eq(NULL, NULL, &eq));
+ BAD(scpr_polygon_eq(polygon, NULL, &eq));
+ BAD(scpr_polygon_eq(polygon, 0, &eq));
+ OK(scpr_polygon_eq(polygon, polygon, &eq));
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);
+ BAD(scpr_polygon_get_position(NULL, ncomps, nverts[0], NULL));
+ BAD(scpr_polygon_get_position(NULL, ncomps, nverts[0], pos));
+ BAD(scpr_polygon_get_position(NULL, ncomps, 0, NULL));
+ BAD(scpr_polygon_get_position(NULL, ncomps, 0, pos));
+ BAD(scpr_polygon_get_position(NULL, 0, nverts[0], NULL));
+ BAD(scpr_polygon_get_position(NULL, 0, nverts[0], pos));
+ BAD(scpr_polygon_get_position(NULL, 0, 0, NULL));
+ BAD(scpr_polygon_get_position(NULL, 0, 0, pos));
+ BAD(scpr_polygon_get_position(polygon, ncomps, nverts[0], NULL));
+ BAD(scpr_polygon_get_position(polygon, ncomps, nverts[0], pos));
+ BAD(scpr_polygon_get_position(polygon, ncomps, 0, NULL));
+ BAD(scpr_polygon_get_position(polygon, ncomps, 0, pos));
+ BAD(scpr_polygon_get_position(polygon, 0, nverts[0], NULL));
+ BAD(scpr_polygon_get_position(polygon, 0, nverts[0], pos));
+ BAD(scpr_polygon_get_position(polygon, 0, 0, NULL));
+ memcpy(*coords, coords0_reduced, sizeof(coords0_reduced));
FOR_EACH(c, 0, ncomps) {
- FOR_EACH(i, 0, nverts[c]) {
- CHK(scpr_polygon_get_position(polygon, c, i, pos) == RES_OK);
+ size_t count;
+ OK(scpr_polygon_get_vertices_count(polygon, c, &count));
+ FOR_EACH(i, 0, count) {
+ OK(scpr_polygon_get_position(polygon, c, i, pos));
CHK(pos[0] == coords[c][i*2+0]);
CHK(pos[1] == coords[c][i*2+1]);
}
}
- CHK(scpr_polygon_create_copy(NULL, NULL, NULL) == RES_BAD_ARG);
- CHK(scpr_polygon_create_copy(NULL, NULL, ©) == RES_BAD_ARG);
- CHK(scpr_polygon_create_copy(NULL, polygon, NULL) == RES_BAD_ARG);
- CHK(scpr_polygon_create_copy(NULL, polygon, ©) == RES_OK);
- CHK(scpr_polygon_eq(polygon, copy, &eq) == RES_OK);
+ BAD(scpr_polygon_create_copy(NULL, NULL, NULL));
+ BAD(scpr_polygon_create_copy(NULL, NULL, ©));
+ BAD(scpr_polygon_create_copy(NULL, polygon, NULL));
+ OK(scpr_polygon_create_copy(dev, polygon, ©));
+ OK(scpr_polygon_eq(polygon, copy, &eq));
CHK(eq);
- CHK(scpr_offset_polygon(NULL, 0, SCPR_JOIN_TYPES_COUNT__) == RES_BAD_ARG);
- CHK(scpr_offset_polygon(polygon, 0, SCPR_JOIN_TYPES_COUNT__) == RES_BAD_ARG);
- CHK(scpr_offset_polygon(NULL, 0, SCPR_JOIN_MITER) == RES_BAD_ARG);
- CHK(scpr_offset_polygon(polygon, 0, SCPR_JOIN_MITER) == RES_OK);
-
- CHK(scpr_polygon_ref_put(polygon) == RES_OK);
- CHK(scpr_polygon_ref_put(copy) == RES_OK);
+ BAD(scpr_offset_polygon(NULL, 0, SCPR_JOIN_TYPES_COUNT__));
+ BAD(scpr_offset_polygon(polygon, 0, SCPR_JOIN_TYPES_COUNT__));
+ BAD(scpr_offset_polygon(NULL, 0, SCPR_JOIN_MITER));
+ OK(scpr_offset_polygon(polygon, 0, SCPR_JOIN_MITER));
+
+ BAD(scpr_polygon_dump_to_obj(NULL, NULL));
+ BAD(scpr_polygon_dump_to_obj(polygon, NULL));
+ BAD(scpr_polygon_dump_to_obj(NULL, stream));
+ BAD(scpr_polygon_dump_to_obj(polygon, stream));
+ stream = tmpfile();
+ OK(scpr_polygon_dump_to_obj(polygon, stream));
+ fclose(stream);
+ stream = NULL;
+
+ BAD(scpr_polygon_dump_component_to_obj(NULL, ncomps, NULL));
+ BAD(scpr_polygon_dump_component_to_obj(polygon, ncomps, NULL));
+ BAD(scpr_polygon_dump_component_to_obj(NULL, 0, NULL));
+ BAD(scpr_polygon_dump_component_to_obj(NULL, ncomps, stream));
+ BAD(scpr_polygon_dump_component_to_obj(polygon, ncomps, stream));
+ BAD(scpr_polygon_dump_component_to_obj(polygon, 0, stream));
+ stream = tmpfile();
+ OK(scpr_polygon_dump_component_to_obj(polygon, 0, stream));
+ fclose(stream);
+
+ OK(scpr_device_ref_put(dev));
+ OK(scpr_polygon_ref_put(polygon));
+ OK(scpr_polygon_ref_put(copy));
MEM_RM(&allocator, *coords);
MEM_RM(&allocator, coords);
diff --git a/src/test_scpr_utils.h b/src/test_scpr_utils.h
@@ -16,8 +16,16 @@
#ifndef TEST_CPR_UTILS_H
#define TEST_CPR_UTILS_H
+#define _POSIX_C_SOURCE 200112L
+
+#include "scpr.h"
#include <rsys/mem_allocator.h>
#include <stdio.h>
+#include <math.h>
+
+#define ERR(Expr) { if((res = (Expr)) != RES_OK) goto error; } (void)0
+#define BAD(Expr) CHK((Expr) == RES_BAD_ARG);
+#define OK(Expr) CHK((Expr) == RES_OK);
struct polygon_context {
double** coords;
@@ -28,7 +36,7 @@ struct polygon_context {
static INLINE void
pget_nverts(const size_t icomp, size_t* nverts, void* context)
{
- const struct polygon_context* ctx = context;
+ const struct polygon_context* ctx = (const struct polygon_context*)context;
CHK(nverts != NULL);
CHK(context != NULL);
CHK(icomp < ctx->ncomps);
@@ -38,7 +46,7 @@ pget_nverts(const size_t icomp, size_t* nverts, void* context)
static INLINE void
pget_pos(const size_t icomp, const size_t ivert, double pos[2], void* context)
{
- const struct polygon_context* ctx = context;
+ const struct polygon_context* ctx = (const struct polygon_context*)context;
CHK(pos != NULL);
CHK(context != NULL);
CHK(ctx->coords != NULL);
@@ -58,7 +66,7 @@ struct mesh_context {
static INLINE void
mget_pos(const size_t ivert, double pos[2], void* context)
{
- const struct mesh_context* ctx = context;
+ const struct mesh_context* ctx = (const struct mesh_context*)context;
CHK(pos != NULL);
CHK(context != NULL);
CHK(ctx->coords != NULL);
@@ -70,7 +78,7 @@ mget_pos(const size_t ivert, double pos[2], void* context)
static INLINE void
mget_ids(const size_t itri, size_t ids[3], void* context)
{
- const struct mesh_context* ctx = context;
+ const struct mesh_context* ctx = (const struct mesh_context*)context;
CHK(ids != NULL);
CHK(context != NULL);
CHK(ctx->indices != NULL);
@@ -91,4 +99,35 @@ check_memory_allocator(struct mem_allocator* allocator)
}
}
+INLINE int
+check_stability
+ (struct scpr_device* dev,
+ const struct scpr_polygon* polygon)
+{
+ res_T res = RES_OK;
+ size_t i, j, ccount, vcount;
+ ASSERT(dev && polygon);
+
+ ERR(scpr_polygon_get_components_count(polygon, &ccount));
+
+ for(i = 0; i < ccount; i++) {
+ ERR(scpr_polygon_get_vertices_count(polygon, i, &vcount));
+ for(j = 0; j < vcount; j++) {
+ double pt[2], tmp[2];
+ int64_t tmp2[2];
+ ERR(scpr_polygon_get_position(polygon, i, j, pt));
+ 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;
+ }
+ }
+ }
+end:
+ return (res == RES_OK);
+error:
+ goto end;
+}
+
#endif /* TEST_CPR_UTILS_H */