commit e82eb0573824642a130b44c16cd6bf6388c88908
parent 962b49caff4fe4db3919c083242e13f662ead6bc
Author: Vincent Forest <vincent.forest@meso-star.com>
Date: Mon, 29 Aug 2016 16:59:38 +0200
Rename the library in Star-CliPpeR
Diffstat:
11 files changed, 1139 insertions(+), 1137 deletions(-)
diff --git a/README.md b/README.md
@@ -1,14 +1,16 @@
-# CliPpeR
+# Star-CliPpeR
-This library clips triangulated 2D meshes against 2D polygons. The clipping
-region is either subtracted or kept whether its contour is CCW or CW ordered,
-respectively.
+This library clips triangulated 2D meshes against 2D polygons. Two clipping
+operations between the mesh and the clipping region are provided: the
+subtraction and the intersection. The former remove the clipping region from
+the original mesh while the latter kept only the mesh part that intersects the
+clipping polygon.
## How to build
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
+depends on the
[Clipper](http://www.angusj.com/delphi/clipper.php),
[Polygon](https://gitlab.Com/vaplv/polygon/#tab-readme) and
[RSys](https://gitlab.com/vaplv/rsys/#tab-readme) libraries.
@@ -20,8 +22,8 @@ project from the `cmake/CMakeLists.txt` file by appending to the
## License
-Clipper is Copyright (C) |Meso|Star> 2016 (<contact@meso-star.com>). It is a
-free software released under the [OSI](http://opensource.org)-approved GPL v3+
-license. You are welcome to redistribute it under certain conditions;
-refer to the COPYING file for details.
+Star-Clipper is Copyright (C) |Meso|Star> 2016 (<contact@meso-star.com>). It is
+a free software released under the [OSI](http://opensource.org)-approved GPL
+v3+ license. You are welcome to redistribute it under certain conditions; refer
+to the COPYING file for details.
diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt
@@ -14,12 +14,12 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
cmake_minimum_required(VERSION 3.1)
-project(cpr C CXX)
+project(scpr C CXX)
enable_testing()
option(NO_TEST "Do not compile the test pograms" OFF)
-set(CPR_SOURCE_DIR ${PROJECT_SOURCE_DIR}/../src)
+set(SCPR_SOURCE_DIR ${PROJECT_SOURCE_DIR}/../src)
################################################################################
# Setup the PkgConfig cmake module
@@ -64,26 +64,26 @@ set(VERSION_MINOR 0)
set(VERSION_PATCH 0)
set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH})
-set(CPR_FILES_SRC cpr_mesh.c)
-set(CPR_FILES_INC cpr.h)
-set(CPR_FILES_DOC COPYING README.md)
-rcmake_prepend_path(CPR_FILES_SRC ${CPR_SOURCE_DIR})
-rcmake_prepend_path(CPR_FILES_INC ${CPR_SOURCE_DIR})
-rcmake_prepend_path(CPR_FILES_DOC ${PROJECT_SOURCE_DIR}/../)
-set_source_files_properties(${CPR_FILES_SRC} PROPERTIES LANGUAGE CXX)
-
-add_library(cpr SHARED ${CPR_FILES_SRC} ${CPR_FILES_INC})
-set_target_properties(cpr PROPERTIES
- DEFINE_SYMBOL CPR_SHARED_BUILD
+set(SCPR_FILES_SRC scpr_mesh.c)
+set(SCPR_FILES_INC scpr.h)
+set(SCPR_FILES_DOC COPYING README.md)
+rcmake_prepend_path(SCPR_FILES_SRC ${SCPR_SOURCE_DIR})
+rcmake_prepend_path(SCPR_FILES_INC ${SCPR_SOURCE_DIR})
+rcmake_prepend_path(SCPR_FILES_DOC ${PROJECT_SOURCE_DIR}/../)
+set_source_files_properties(${SCPR_FILES_SRC} PROPERTIES LANGUAGE CXX)
+
+add_library(scpr SHARED ${SCPR_FILES_SRC} ${SCPR_FILES_INC})
+set_target_properties(scpr PROPERTIES
+ DEFINE_SYMBOL SCPR_SHARED_BUILD
VERSION ${VERSION}
SOVERSION ${VERSION_MAJOR})
-target_link_libraries(cpr RSys Polygon ${Clipper_LIBRARIES})
+target_link_libraries(scpr RSys Polygon ${Clipper_LIBRARIES})
if(CMAKE_COMPILER_IS_GNUCXX)
- set_target_properties(cpr PROPERTIES COMPILE_FLAGS "-Wno-long-long")
+ set_target_properties(scpr PROPERTIES COMPILE_FLAGS "-Wno-long-long")
endif()
-rcmake_setup_devel(cpr CPR ${VERSION} cpr_version.h)
+rcmake_setup_devel(scpr SCPR ${VERSION} scpr_version.h)
################################################################################
# Define tests
@@ -95,22 +95,22 @@ if(NOT NO_TEST)
endif()
function(new_test _name)
- add_executable(${_name} ${CPR_SOURCE_DIR}/${_name}.c)
- target_link_libraries(${_name} cpr RSys ${MATH_LIB})
+ add_executable(${_name} ${SCPR_SOURCE_DIR}/${_name}.c)
+ target_link_libraries(${_name} scpr RSys ${MATH_LIB})
add_test(${_name} ${_name})
rcmake_set_test_runtime_dirs(${_name} _runtime_dirs)
endfunction()
- new_test(test_cpr_clip)
- new_test(test_cpr_mesh)
+ new_test(test_scpr_clip)
+ new_test(test_scpr_mesh)
endif()
################################################################################
# Install directories
################################################################################
-install(TARGETS cpr
+install(TARGETS scpr
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin)
-install(FILES ${CPR_FILES_INC} DESTINATION include/)
-install(FILES ${CPR_FILES_DOC} DESTINATION share/doc/cpr/)
+install(FILES ${SCPR_FILES_INC} DESTINATION include/)
+install(FILES ${SCPR_FILES_DOC} DESTINATION share/doc/star-cpr/)
diff --git a/src/cpr.h b/src/cpr.h
@@ -1,115 +0,0 @@
-/* Copyright (C) |Meso|Star> 2016 (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/>. */
-
-#ifndef CPR_H
-#define CPR_H
-
-#include <rsys/rsys.h>
-
-#ifdef CPR_SHARED_BUILD
- #define CPR_API extern EXPORT_SYM
-#elif defined(CPR_STATIC_BUILD)
- #define CPR_API extern LOCAL_SYM
-#else
- #define CPR_API extern IMPORT_SYM
-#endif
-
-#ifndef NDEBUG
- #define CPR(Func) ASSERT(cpr_##Func == RES_OK)
-#else
- #define CPR(Func) cpr_##Func
-#endif
-
-enum cpr_operation {
- CPR_AND,
- CPR_SUB,
- CPR_OPERATIONS_COUNT__
-};
-
-/* Public polygon data type. Define the list of the countour vertices. */
-struct cpr_polygon {
- void (*get_position)(const size_t ivert, double pos[2], void* ctx);
- size_t nvertices; /* #vertices */
- /* User defined data provided as the last argument of the get_position
- * functor */
- void* context;
-};
-
-#define CPR_POLYGON_NULL__ { NULL, 0, NULL }
-static const struct cpr_polygon CPR_POLYGON_NULL = CPR_POLYGON_NULL__;
-
-/* Opaque 2 dimensionnal mesh data type */
-struct cpr_mesh;
-
-/* Forward declarations */
-struct mem_allocator;
-
-BEGIN_DECLS
-
-CPR_API res_T
-cpr_mesh_create
- (struct mem_allocator* allocator, /* May be NULL <=> Use default allocator */
- struct cpr_mesh** mesh);
-
-CPR_API res_T
-cpr_mesh_ref_get
- (struct cpr_mesh* mesh);
-
-CPR_API res_T
-cpr_mesh_ref_put
- (struct cpr_mesh* mesh);
-
-CPR_API res_T
-cpr_mesh_setup_indexed_vertices
- (struct cpr_mesh* mesh,
- const size_t ntris,
- void (*get_indices)(const size_t itri, size_t ids[3], void* ctx),
- const size_t nverts,
- 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 */
-CPR_API res_T
-cpr_mesh_clip
- (struct cpr_mesh* mesh,
- const enum cpr_operation op,
- struct cpr_polygon* polygon);
-
-CPR_API res_T
-cpr_mesh_get_triangles_count
- (const struct cpr_mesh* mesh,
- size_t* ntris);
-
-CPR_API res_T
-cpr_mesh_get_vertices_count
- (const struct cpr_mesh* mesh,
- size_t* nverts);
-
-CPR_API res_T
-cpr_mesh_get_indices
- (const struct cpr_mesh* mesh,
- const size_t itri,
- size_t ids[3]);
-
-CPR_API res_T
-cpr_mesh_get_position
- (const struct cpr_mesh* mesh,
- const size_t ivert,
- double position[2]);
-
-END_DECLS
-
-#endif /* CPR_H */
-
diff --git a/src/cpr_mesh.c b/src/cpr_mesh.c
@@ -1,611 +0,0 @@
-/* Copyright (C) |Meso|Star> 2016 (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 "cpr.h"
-
-#include <rsys/double2.h>
-#include <rsys/dynamic_array_double.h>
-#include <rsys/dynamic_array_size_t.h>
-#include <rsys/hash_table.h>
-#include <rsys/mem_allocator.h>
-#include <rsys/ref_count.h>
-
-#include <polygon.h>
-#include <clipper.hpp>
-STATIC_ASSERT(sizeof(ClipperLib::cInt) >= sizeof(double), Unexpected_Type_Size);
-
-struct vertex { double pos[2]; };
-static FINLINE int
-vertex_eq(const struct vertex* a, const struct vertex* b)
-{ return d2_eq(a->pos, b->pos); }
-
-/* Define the vertex to index hash table */
-#define HTABLE_NAME vertex
-#define HTABLE_DATA size_t
-#define HTABLE_KEY struct vertex
-#define HTABLE_KEY_FUNCTOR_EQ vertex_eq
-#include <rsys/hash_table.h>
-
-struct poly {
- struct darray_double coords;
- double lower[2], upper[2]; /* Polygon AABB */
-};
-
-struct cpr_mesh {
- struct darray_double coords;
- struct darray_size_t indices;
-
- ref_T ref;
- struct mem_allocator* allocator;
-};
-
-/*******************************************************************************
- * Helper functions
- ******************************************************************************/
-static FINLINE ClipperLib::cInt
-double_to_cInt(const double d, const double scale)
-{
- double dbl = d;
- union { int64_t i; double d; } ucast;
- ClipperLib::cInt i;
- ASSERT(scale > 0);
-
- /* Map 'd' in [1, 2] => Fix the exponent */
- ucast.d = 1 + fabs(d / scale);
-
- /* Store the positive or null exponent in the 52^th bit. This ensure that if
- * 'd' is equal to 2 then it is not encoded as 0. */
- i = (((ucast.i >> 52) & 0x7FF) - 1023) << 52;
-
- /* Store the mantissa in the [0 .. 51] bits */
- i = (ucast.i & 0x000FFFFFFFFFFFFF) | i;
-
- /* Apply the sign to the resulting integer */
- return dbl < 0 ? -i : i;
-}
-
-static FINLINE double
-cInt_to_double(const ClipperLib::cInt i, const double scale)
-{
- double dbl;
- union { int64_t i; double d; } ucast;
- ASSERT(scale > 0);
-
- ucast.i = llabs(i);
- ucast.i = ((1023 + (ucast.i >> 52)) << 52) | (ucast.i & 0x000FFFFFFFFFFFFF);
- dbl = (ucast.d - 1) * scale;
- return i < 0 ? -dbl : dbl;
-}
-
-static FINLINE ClipperLib::ClipType
-cpr_operation_to_clip_type(const enum cpr_operation op)
-{
- ClipperLib::ClipType ctype = ClipperLib::ctIntersection;
- switch(op) {
- case CPR_AND: ctype = ClipperLib::ctIntersection; break;
- case CPR_SUB: ctype = ClipperLib::ctDifference; break;
- default: FATAL("Unreachable code\n"); break;
- }
- return ctype;
-}
-
-static INLINE int
-aabb_intersect
- (const double lower0[2],
- const double upper0[2],
- const double lower1[2],
- const double upper1[2])
-{
- ASSERT(lower0 && upper0 && lower1 && upper1);
- return lower0[0] < upper1[0]
- && lower0[1] < upper1[1]
- && lower1[0] < upper0[0]
- && lower1[1] < upper0[1];
-}
-
-static INLINE int
-aabb_is_degenerated(const double lower[2], const double upper[2])
-{
- ASSERT(lower && upper);
- return lower[0] >= upper[0] || lower[1] >= upper[1];
-}
-
-static INLINE void
-poly_init(struct mem_allocator* allocator, struct poly* poly)
-{
- ASSERT(allocator && poly);
- darray_double_init(allocator, &poly->coords);
-}
-
-static INLINE void
-poly_release(struct poly* poly)
-{
- ASSERT(poly);
- darray_double_release(&poly->coords);
-}
-
-static res_T
-poly_setup(struct poly* poly, const struct cpr_polygon* desc)
-{
- size_t ivert;
- res_T res = RES_OK;
- ASSERT(poly && desc);
-
- if(!desc->get_position && !desc->nvertices) {
- res = RES_BAD_ARG;
- goto error;
- }
-
- res = darray_double_resize(&poly->coords, desc->nvertices * 2/*#coords*/);
- if(res != RES_OK) goto error;
-
- d2_splat(poly->lower, DBL_MAX);
- d2_splat(poly->upper,-DBL_MAX);
- FOR_EACH(ivert, 0, desc->nvertices) {
- double* pos = darray_double_data_get(&poly->coords) + ivert*2;
- desc->get_position(ivert, pos, desc->context);
- d2_min(poly->lower, poly->lower, pos);
- d2_max(poly->upper, poly->upper, pos);
- }
-
-exit:
- return res;
-error:
- darray_double_clear(&poly->coords);
- d2_splat(poly->lower, DBL_MAX);
- d2_splat(poly->upper,-DBL_MAX);
- goto exit;
-}
-
-static void
-triangle_compute_aabb
- (double tri[3][2],
- double lower[2],
- double upper[2])
-{
- size_t ivert;
- ASSERT(tri && lower && upper);
-
- d2_splat(lower, DBL_MAX);
- d2_splat(upper,-DBL_MAX);
- FOR_EACH(ivert, 0, 3) {
- d2_min(lower, lower, tri[ivert]);
- d2_max(upper, upper, tri[ivert]);
- }
-}
-
-static res_T
-register_vertex
- (const double pos[2],
- struct darray_double* coords,
- struct darray_size_t* indices,
- struct htable_vertex* vertices)
-{
- struct vertex v;
- size_t* pid, id;
- res_T res = RES_OK;
- ASSERT(pos && coords && indices && vertices);
-
- d2_set(v.pos, pos);
-
- /* FIXME dirty hack */
- v.pos[0] = (float)v.pos[0];
- v.pos[1] = (float)v.pos[1];
-
- pid = htable_vertex_find(vertices, &v);
-
- if(pid) {
- id = *pid;
- } else {
- const size_t ivert = darray_double_size_get(coords);
-
- res = darray_double_resize(coords, ivert+2/*#coords*/);
- if(res != RES_OK) goto error;
- d2_set(darray_double_data_get(coords)+ivert, pos);
-
- id = ivert / 2;
- res = htable_vertex_set(vertices, &v, &id);
- if(res != RES_OK) goto error;
- }
-
- res = darray_size_t_push_back(indices, &id);
- if(res != RES_OK) goto error;
-
-exit:
- return res;
-error:
- goto exit;
-}
-
-static FINLINE res_T
-register_triangle
- (double tri[3][2],
- struct darray_double* coords, /* Vertex buffer */
- struct darray_size_t* indices, /* Index buffer */
- struct htable_vertex* vertices) /* Map a vertex to its index */
-{
- size_t ivert;
- res_T res = RES_OK;
- ASSERT(tri && coords && indices && vertices);
- FOR_EACH(ivert, 0, 3) {
- res = register_vertex(tri[ivert], coords, indices, vertices);
- if(res != RES_OK) return res;
- }
- return RES_OK;
-}
-
-
-static res_T
-register_paths
- (const ClipperLib::Paths& paths,
- struct darray_double* coords, /* Vertex buffer */
- struct darray_size_t* indices, /* Index buffer */
- struct htable_vertex* vertices, /* Map a vertex to its index */
- const double extend[2], /* Scale to apply to the cInt coordinates */
- struct polygon* polygon) /* Use to triangulate the clipped polygons */
-{
- size_t ivert;
- size_t ipath;
- res_T res = RES_OK;
- ASSERT(coords && indices && vertices);
-
- FOR_EACH(ipath, 0, paths.size()) {
- if(paths[ipath].size() == 3) {
- FOR_EACH(ivert, 0, 3) {
- double pos[2];
- pos[0] = cInt_to_double(paths[ipath][ivert].X, extend[0]);
- pos[1] = cInt_to_double(paths[ipath][ivert].Y, extend[1]);
- res = register_vertex(pos, coords, indices, vertices);
- if(res != RES_OK) goto error;
- }
- } else {
- /* Triangulate the clipped primitive */
- const uint32_t* ids;
- uint32_t nids;
-
- /* Define the contour of the polygon to triangulate */
- POLYGON(clear(polygon));
- FOR_EACH(ivert, 0, paths[ipath].size()) {
- float fpos[3] = {0.f, 0.f, 0.f};
- double pos[2];
-
- pos[0] = cInt_to_double(paths[ipath][ivert].X, extend[0]);
- pos[1] = cInt_to_double(paths[ipath][ivert].Y, extend[1]);
-
- fpos[0] = (float)pos[0], fpos[1] = (float)pos[1];
- res = polygon_vertex_add(polygon, fpos);
- if(res != RES_OK) goto error;
- }
- res = polygon_triangulate(polygon, &ids, &nids);
- if(res != RES_OK) goto error;
-
- FOR_EACH(ivert, 0, nids) {
- float fpos[3];
- double pos[2];
- POLYGON(vertex_get(polygon, ids[ivert], fpos));
- pos[0] = (float)fpos[0];
- pos[1] = (float)fpos[1];
- res = register_vertex(pos, coords, indices, vertices);
- if(res != RES_OK) goto error;
- }
- }
- }
-exit:
- return res;
-error:
- goto exit;
-}
-
-static void
-mesh_compute_aabb(const struct cpr_mesh* mesh, double lower[2], double upper[2])
-{
- size_t itri, ntris;
-
- CPR(mesh_get_triangles_count(mesh, &ntris));
- d2_splat(lower, DBL_MAX);
- d2_splat(upper,-DBL_MAX);
-
- FOR_EACH(itri, 0, ntris) {
- size_t ids[3], ivert;
- CPR(mesh_get_indices(mesh, itri, ids));
- FOR_EACH(ivert, 0, 3) {
- double pos[2];
- CPR(mesh_get_position(mesh, ids[ivert], pos));
- d2_min(lower, lower, pos);
- d2_max(upper, upper, pos);
- }
- }
-}
-
-static void
-mesh_release(ref_T* ref)
-{
- struct cpr_mesh* mesh;
- ASSERT(ref);
- mesh = CONTAINER_OF(ref, struct cpr_mesh, ref);
- darray_double_release(&mesh->coords);
- darray_size_t_release(&mesh->indices);
- MEM_RM(mesh->allocator, mesh);
-}
-
-/*******************************************************************************
- * Exported functions
- ******************************************************************************/
-res_T
-cpr_mesh_create(struct mem_allocator* mem_allocator, struct cpr_mesh** out_mesh)
-{
- struct cpr_mesh* mesh = NULL;
- struct mem_allocator* allocator = NULL;
- res_T res = RES_OK;
-
- if(!out_mesh) {
- res = RES_BAD_ARG;
- goto error;
- }
-
- allocator = mem_allocator ? mem_allocator : &mem_default_allocator;
- mesh = (struct cpr_mesh*)
- MEM_CALLOC(allocator, 1, sizeof(struct cpr_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;
-
-exit:
- if(out_mesh) *out_mesh = mesh;
- return res;
-
-error:
- if(mesh) {
- CPR(mesh_ref_put(mesh));
- mesh = NULL;
- }
- goto exit;
-}
-
-res_T
-cpr_mesh_ref_get(struct cpr_mesh* mesh)
-{
- if(!mesh) return RES_BAD_ARG;
- ref_get(&mesh->ref);
- return RES_OK;
-}
-
-res_T
-cpr_mesh_ref_put(struct cpr_mesh* mesh)
-{
- if(!mesh) return RES_BAD_ARG;
- ref_put(&mesh->ref, mesh_release);
- return RES_OK;
-}
-
-res_T
-cpr_mesh_setup_indexed_vertices
- (struct cpr_mesh* mesh,
- const size_t ntris,
- void (*get_indices)(const size_t itri, size_t ids[3], void* ctx),
- const size_t nverts,
- void (*get_position)(const size_t ivert, double pos[2], void* ctx),
- void* data)
-{
- double* pos = NULL;
- size_t* ids = NULL;
- size_t i;
- res_T res = RES_OK;
-
- if(!mesh || !ntris || !get_indices || !nverts || !get_position) {
- res = RES_BAD_ARG;
- goto error;
- }
-
- res = darray_double_resize(&mesh->coords, nverts*2/*#coords per vertex*/);
- if(res != RES_OK) goto error;
- res = darray_size_t_resize(&mesh->indices, ntris*3/*#vertices per triangle*/);
- if(res != RES_OK) goto error;
-
- /* Fetch mesh positions */
- pos = darray_double_data_get(&mesh->coords);
- FOR_EACH(i, 0, nverts) {
- get_position(i, pos+i*2, data);
- }
-
- /* Fetch mesh indices */
- ids = darray_size_t_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) {
- res = RES_BAD_ARG;
- goto error;
- }
- }
-
-exit:
- return res;
-error:
- if(mesh) {
- darray_double_clear(&mesh->coords);
- darray_size_t_clear(&mesh->indices);
- }
- goto exit;
-}
-
-res_T
-cpr_mesh_get_vertices_count(const struct cpr_mesh* mesh, size_t* nverts)
-{
- if(!mesh || !nverts) return RES_BAD_ARG;
- ASSERT((darray_double_size_get(&mesh->coords) % 2) == 0);
- *nverts = darray_double_size_get(&mesh->coords) / 2;
- return RES_OK;
-}
-
-res_T
-cpr_mesh_get_triangles_count(const struct cpr_mesh* mesh, size_t* ntris)
-{
- if(!mesh || !ntris) return RES_BAD_ARG;
- ASSERT((darray_size_t_size_get(&mesh->indices)%3) == 0);
- *ntris = darray_size_t_size_get(&mesh->indices) / 3;
- return RES_OK;
-}
-
-res_T
-cpr_mesh_get_indices
- (const struct cpr_mesh* mesh, const size_t itri, size_t ids[3])
-{
- size_t ntris;
- if(!mesh || !ids) return RES_BAD_ARG;
- CPR(mesh_get_triangles_count(mesh, &ntris));
- if(itri >= ntris) return RES_BAD_ARG;
- ids[0] = darray_size_t_cdata_get(&mesh->indices)[itri*3 + 0];
- ids[1] = darray_size_t_cdata_get(&mesh->indices)[itri*3 + 1];
- ids[2] = darray_size_t_cdata_get(&mesh->indices)[itri*3 + 2];
- return RES_OK;
-}
-
-res_T
-cpr_mesh_get_position
- (const struct cpr_mesh* mesh, const size_t ivert, double pos[2])
-{
- size_t nverts;
- if(!mesh || !pos) return RES_BAD_ARG;
- CPR(mesh_get_vertices_count(mesh, &nverts));
- if(ivert >= nverts) return RES_BAD_ARG;
- pos[0] = darray_double_cdata_get(&mesh->coords)[ivert*2+0];
- pos[1] = darray_double_cdata_get(&mesh->coords)[ivert*2+1];
- return RES_OK;
-}
-
-res_T
-cpr_mesh_clip
- (struct cpr_mesh* mesh,
- const enum cpr_operation op,
- struct cpr_polygon* poly_desc)
-{
- double lower[2], upper[2], extend[2];
- struct poly poly;
- 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 htable_vertex vertices; /* Map a coordinate to its index */
- ClipperLib::Clipper clipper(ClipperLib::ioStrictlySimple);
- ClipperLib::Paths output;
- ClipperLib::Path cand_path;
- ClipperLib::Path clip_path;
- ClipperLib::ClipType clip_type;
- size_t ivert, nverts;
- size_t itri, ntris;
-
- res_T res = RES_OK;
-
- if(!mesh || !poly_desc || (unsigned)op >= CPR_OPERATIONS_COUNT__)
- return RES_BAD_ARG;
-
- clip_type = cpr_operation_to_clip_type(op);
-
- darray_double_init(mesh->allocator, &coords);
- darray_size_t_init(mesh->allocator, &indices);
- htable_vertex_init(mesh->allocator, &vertices);
-
- poly_init(mesh->allocator, &poly);
- res = poly_setup(&poly, poly_desc);
- if(res != RES_OK) goto error;
- if(aabb_is_degenerated(poly.lower, poly.upper)) goto exit;
-
- mesh_compute_aabb(mesh, lower, upper);
- if(aabb_is_degenerated(lower, upper)) goto exit;
-
- /* Compute the overall aabb of the candidate and the clip polygon */
- d2_min(lower, lower, poly.lower);
- d2_max(upper, upper, poly.upper);
- d2_sub(extend, upper, lower);
-
- /* Setup the clip path */
- nverts = darray_double_size_get(&poly.coords) / 2/*#coords per vertex*/;
- FOR_EACH(ivert, 0, nverts) {
- const double* v = darray_double_cdata_get(&poly.coords) + ivert*2;
- ClipperLib::IntPoint pt;
- pt.X = double_to_cInt(v[0], extend[0]);
- pt.Y = double_to_cInt(v[1], extend[1]);
- clip_path.push_back(pt);
- }
-
- /* Create the polygon structure used to triangulate the clipped polygons */
- res = polygon_create(mesh->allocator, &polygon);
- if(res != RES_OK) goto error;
-
- /* Clip the triangles of the mesh */
- CPR(mesh_get_triangles_count(mesh, &ntris));
- FOR_EACH(itri, 0, ntris) {
- size_t ids[3];
- double tri[3][2];
-
- /* Fetch the triangle vertices and compute its AABB */
- CPR(mesh_get_indices(mesh, itri, ids));
- CPR(mesh_get_position(mesh, ids[0], tri[0]));
- CPR(mesh_get_position(mesh, ids[1], tri[1]));
- CPR(mesh_get_position(mesh, ids[2], tri[2]));
- triangle_compute_aabb(tri, lower, upper);
-
- /* Do not clip triangles that don not intersect the clip AABB */
- if(!aabb_intersect(lower, upper, poly.lower, poly.upper)) {
- if(op != CPR_AND) {
- res = register_triangle(tri, &coords, &indices, &vertices);
- if(res != RES_OK) goto error;
- }
- continue;
- }
-
- /* Setup the candidate path */
- cand_path.clear();
- FOR_EACH(ivert, 0, 3) {
- ClipperLib::IntPoint pt;
- pt.X = double_to_cInt(tri[ivert][0], extend[0]);
- pt.Y = double_to_cInt(tri[ivert][1], extend[1]);
- cand_path.push_back(pt);
- }
-
- /* Clip the polygon */
- clipper.Clear();
- clipper.AddPath(cand_path, ClipperLib::ptSubject, 1);
- clipper.AddPath(clip_path, ClipperLib::ptClip, 1);
- clipper.Execute(clip_type, output);
-
- /* Register the polygons */
- res = register_paths
- (output, &coords, &indices, &vertices, extend, polygon);
- if(res != RES_OK) goto error;
- }
-
- res = darray_double_copy_and_clear(&mesh->coords, &coords);
- if(res != RES_OK) FATAL("Unexpected error.\n");
- res = darray_size_t_copy_and_clear(&mesh->indices, &indices);
- if(res != RES_OK) FATAL("Unexpected error.\n");
-
-exit:
- if(polygon) POLYGON(ref_put(polygon));
- darray_double_release(&coords);
- darray_size_t_release(&indices);
- htable_vertex_release(&vertices);
- poly_release(&poly);
- return res;
-error:
- goto exit;
-}
-
diff --git a/src/scpr.h b/src/scpr.h
@@ -0,0 +1,115 @@
+/* Copyright (C) |Meso|Star> 2016 (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/>. */
+
+#ifndef SCPR_H
+#define SCPR_H
+
+#include <rsys/rsys.h>
+
+#ifdef SCPR_SHARED_BUILD
+ #define SCPR_API extern EXPORT_SYM
+#elif defined(SCPR_STATIC_BUILD)
+ #define SCPR_API extern LOCAL_SYM
+#else
+ #define SCPR_API extern IMPORT_SYM
+#endif
+
+#ifndef NDEBUG
+ #define SCPR(Func) ASSERT(scpr_##Func == RES_OK)
+#else
+ #define SCPR(Func) scpr_##Func
+#endif
+
+enum scpr_operation {
+ SCPR_AND,
+ SCPR_SUB,
+ SCPR_OPERATIONS_COUNT__
+};
+
+/* Public polygon data type. Define the list of the countour vertices. */
+struct scpr_polygon {
+ void (*get_position)(const size_t ivert, double pos[2], void* ctx);
+ size_t nvertices; /* #vertices */
+ /* User defined data provided as the last argument of the get_position
+ * functor */
+ void* context;
+};
+
+#define SCPR_POLYGON_NULL__ { NULL, 0, NULL }
+static const struct scpr_polygon SCPR_POLYGON_NULL = SCPR_POLYGON_NULL__;
+
+/* Opaque 2 dimensionnal mesh data type */
+struct scpr_mesh;
+
+/* Forward declarations */
+struct mem_allocator;
+
+BEGIN_DECLS
+
+SCPR_API res_T
+scpr_mesh_create
+ (struct mem_allocator* allocator, /* May be NULL <=> Use default allocator */
+ struct scpr_mesh** mesh);
+
+SCPR_API res_T
+scpr_mesh_ref_get
+ (struct scpr_mesh* mesh);
+
+SCPR_API res_T
+scpr_mesh_ref_put
+ (struct scpr_mesh* mesh);
+
+SCPR_API res_T
+scpr_mesh_setup_indexed_vertices
+ (struct scpr_mesh* mesh,
+ const size_t ntris,
+ void (*get_indices)(const size_t itri, size_t ids[3], void* ctx),
+ const size_t nverts,
+ 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 */
+SCPR_API res_T
+scpr_mesh_clip
+ (struct scpr_mesh* mesh,
+ const enum scpr_operation op,
+ struct scpr_polygon* polygon);
+
+SCPR_API res_T
+scpr_mesh_get_triangles_count
+ (const struct scpr_mesh* mesh,
+ size_t* ntris);
+
+SCPR_API res_T
+scpr_mesh_get_vertices_count
+ (const struct scpr_mesh* mesh,
+ size_t* nverts);
+
+SCPR_API res_T
+scpr_mesh_get_indices
+ (const struct scpr_mesh* mesh,
+ const size_t itri,
+ size_t ids[3]);
+
+SCPR_API res_T
+scpr_mesh_get_position
+ (const struct scpr_mesh* mesh,
+ const size_t ivert,
+ double position[2]);
+
+END_DECLS
+
+#endif /* SCPR_H */
+
diff --git a/src/scpr_mesh.c b/src/scpr_mesh.c
@@ -0,0 +1,611 @@
+/* Copyright (C) |Meso|Star> 2016 (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 <rsys/double2.h>
+#include <rsys/dynamic_array_double.h>
+#include <rsys/dynamic_array_size_t.h>
+#include <rsys/hash_table.h>
+#include <rsys/mem_allocator.h>
+#include <rsys/ref_count.h>
+
+#include <polygon.h>
+#include <clipper.hpp>
+STATIC_ASSERT(sizeof(ClipperLib::cInt) >= sizeof(double), Unexpected_Type_Size);
+
+struct vertex { double pos[2]; };
+static FINLINE int
+vertex_eq(const struct vertex* a, const struct vertex* b)
+{ return d2_eq(a->pos, b->pos); }
+
+/* Define the vertex to index hash table */
+#define HTABLE_NAME vertex
+#define HTABLE_DATA size_t
+#define HTABLE_KEY struct vertex
+#define HTABLE_KEY_FUNCTOR_EQ vertex_eq
+#include <rsys/hash_table.h>
+
+struct poly {
+ struct darray_double coords;
+ double lower[2], upper[2]; /* Polygon AABB */
+};
+
+struct scpr_mesh {
+ struct darray_double coords;
+ struct darray_size_t indices;
+
+ ref_T ref;
+ struct mem_allocator* allocator;
+};
+
+/*******************************************************************************
+ * Helper functions
+ ******************************************************************************/
+static FINLINE ClipperLib::cInt
+double_to_cInt(const double d, const double scale)
+{
+ double dbl = d;
+ union { int64_t i; double d; } ucast;
+ ClipperLib::cInt i;
+ ASSERT(scale > 0);
+
+ /* Map 'd' in [1, 2] => Fix the exponent */
+ ucast.d = 1 + fabs(d / scale);
+
+ /* Store the positive or null exponent in the 52^th bit. This ensure that if
+ * 'd' is equal to 2 then it is not encoded as 0. */
+ i = (((ucast.i >> 52) & 0x7FF) - 1023) << 52;
+
+ /* Store the mantissa in the [0 .. 51] bits */
+ i = (ucast.i & 0x000FFFFFFFFFFFFF) | i;
+
+ /* Apply the sign to the resulting integer */
+ return dbl < 0 ? -i : i;
+}
+
+static FINLINE double
+cInt_to_double(const ClipperLib::cInt i, const double scale)
+{
+ double dbl;
+ union { int64_t i; double d; } ucast;
+ ASSERT(scale > 0);
+
+ ucast.i = llabs(i);
+ ucast.i = ((1023 + (ucast.i >> 52)) << 52) | (ucast.i & 0x000FFFFFFFFFFFFF);
+ dbl = (ucast.d - 1) * scale;
+ return i < 0 ? -dbl : dbl;
+}
+
+static FINLINE ClipperLib::ClipType
+scpr_operation_to_clip_type(const enum scpr_operation op)
+{
+ ClipperLib::ClipType ctype = ClipperLib::ctIntersection;
+ switch(op) {
+ case SCPR_AND: ctype = ClipperLib::ctIntersection; break;
+ case SCPR_SUB: ctype = ClipperLib::ctDifference; break;
+ default: FATAL("Unreachable code\n"); break;
+ }
+ return ctype;
+}
+
+static INLINE int
+aabb_intersect
+ (const double lower0[2],
+ const double upper0[2],
+ const double lower1[2],
+ const double upper1[2])
+{
+ ASSERT(lower0 && upper0 && lower1 && upper1);
+ return lower0[0] < upper1[0]
+ && lower0[1] < upper1[1]
+ && lower1[0] < upper0[0]
+ && lower1[1] < upper0[1];
+}
+
+static INLINE int
+aabb_is_degenerated(const double lower[2], const double upper[2])
+{
+ ASSERT(lower && upper);
+ return lower[0] >= upper[0] || lower[1] >= upper[1];
+}
+
+static INLINE void
+poly_init(struct mem_allocator* allocator, struct poly* poly)
+{
+ ASSERT(allocator && poly);
+ darray_double_init(allocator, &poly->coords);
+}
+
+static INLINE void
+poly_release(struct poly* poly)
+{
+ ASSERT(poly);
+ darray_double_release(&poly->coords);
+}
+
+static res_T
+poly_setup(struct poly* poly, const struct scpr_polygon* desc)
+{
+ size_t ivert;
+ res_T res = RES_OK;
+ ASSERT(poly && desc);
+
+ if(!desc->get_position && !desc->nvertices) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ res = darray_double_resize(&poly->coords, desc->nvertices * 2/*#coords*/);
+ if(res != RES_OK) goto error;
+
+ d2_splat(poly->lower, DBL_MAX);
+ d2_splat(poly->upper,-DBL_MAX);
+ FOR_EACH(ivert, 0, desc->nvertices) {
+ double* pos = darray_double_data_get(&poly->coords) + ivert*2;
+ desc->get_position(ivert, pos, desc->context);
+ d2_min(poly->lower, poly->lower, pos);
+ d2_max(poly->upper, poly->upper, pos);
+ }
+
+exit:
+ return res;
+error:
+ darray_double_clear(&poly->coords);
+ d2_splat(poly->lower, DBL_MAX);
+ d2_splat(poly->upper,-DBL_MAX);
+ goto exit;
+}
+
+static void
+triangle_compute_aabb
+ (double tri[3][2],
+ double lower[2],
+ double upper[2])
+{
+ size_t ivert;
+ ASSERT(tri && lower && upper);
+
+ d2_splat(lower, DBL_MAX);
+ d2_splat(upper,-DBL_MAX);
+ FOR_EACH(ivert, 0, 3) {
+ d2_min(lower, lower, tri[ivert]);
+ d2_max(upper, upper, tri[ivert]);
+ }
+}
+
+static res_T
+register_vertex
+ (const double pos[2],
+ struct darray_double* coords,
+ struct darray_size_t* indices,
+ struct htable_vertex* vertices)
+{
+ struct vertex v;
+ size_t* pid, id;
+ res_T res = RES_OK;
+ ASSERT(pos && coords && indices && vertices);
+
+ d2_set(v.pos, pos);
+
+ /* FIXME dirty hack */
+ v.pos[0] = (float)v.pos[0];
+ v.pos[1] = (float)v.pos[1];
+
+ pid = htable_vertex_find(vertices, &v);
+
+ if(pid) {
+ id = *pid;
+ } else {
+ const size_t ivert = darray_double_size_get(coords);
+
+ res = darray_double_resize(coords, ivert+2/*#coords*/);
+ if(res != RES_OK) goto error;
+ d2_set(darray_double_data_get(coords)+ivert, pos);
+
+ id = ivert / 2;
+ res = htable_vertex_set(vertices, &v, &id);
+ if(res != RES_OK) goto error;
+ }
+
+ res = darray_size_t_push_back(indices, &id);
+ if(res != RES_OK) goto error;
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+static FINLINE res_T
+register_triangle
+ (double tri[3][2],
+ struct darray_double* coords, /* Vertex buffer */
+ struct darray_size_t* indices, /* Index buffer */
+ struct htable_vertex* vertices) /* Map a vertex to its index */
+{
+ size_t ivert;
+ res_T res = RES_OK;
+ ASSERT(tri && coords && indices && vertices);
+ FOR_EACH(ivert, 0, 3) {
+ res = register_vertex(tri[ivert], coords, indices, vertices);
+ if(res != RES_OK) return res;
+ }
+ return RES_OK;
+}
+
+
+static res_T
+register_paths
+ (const ClipperLib::Paths& paths,
+ struct darray_double* coords, /* Vertex buffer */
+ struct darray_size_t* indices, /* Index buffer */
+ struct htable_vertex* vertices, /* Map a vertex to its index */
+ const double extend[2], /* Scale to apply to the cInt coordinates */
+ struct polygon* polygon) /* Use to triangulate the clipped polygons */
+{
+ size_t ivert;
+ size_t ipath;
+ res_T res = RES_OK;
+ ASSERT(coords && indices && vertices);
+
+ FOR_EACH(ipath, 0, paths.size()) {
+ if(paths[ipath].size() == 3) {
+ FOR_EACH(ivert, 0, 3) {
+ double pos[2];
+ pos[0] = cInt_to_double(paths[ipath][ivert].X, extend[0]);
+ pos[1] = cInt_to_double(paths[ipath][ivert].Y, extend[1]);
+ res = register_vertex(pos, coords, indices, vertices);
+ if(res != RES_OK) goto error;
+ }
+ } else {
+ /* Triangulate the clipped primitive */
+ const uint32_t* ids;
+ uint32_t nids;
+
+ /* Define the contour of the polygon to triangulate */
+ POLYGON(clear(polygon));
+ FOR_EACH(ivert, 0, paths[ipath].size()) {
+ float fpos[3] = {0.f, 0.f, 0.f};
+ double pos[2];
+
+ pos[0] = cInt_to_double(paths[ipath][ivert].X, extend[0]);
+ pos[1] = cInt_to_double(paths[ipath][ivert].Y, extend[1]);
+
+ fpos[0] = (float)pos[0], fpos[1] = (float)pos[1];
+ res = polygon_vertex_add(polygon, fpos);
+ if(res != RES_OK) goto error;
+ }
+ res = polygon_triangulate(polygon, &ids, &nids);
+ if(res != RES_OK) goto error;
+
+ FOR_EACH(ivert, 0, nids) {
+ float fpos[3];
+ double pos[2];
+ POLYGON(vertex_get(polygon, ids[ivert], fpos));
+ pos[0] = (float)fpos[0];
+ pos[1] = (float)fpos[1];
+ res = register_vertex(pos, coords, indices, vertices);
+ if(res != RES_OK) goto error;
+ }
+ }
+ }
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+static void
+mesh_compute_aabb(const struct scpr_mesh* mesh, double lower[2], double upper[2])
+{
+ size_t itri, ntris;
+
+ SCPR(mesh_get_triangles_count(mesh, &ntris));
+ d2_splat(lower, DBL_MAX);
+ d2_splat(upper,-DBL_MAX);
+
+ 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];
+ SCPR(mesh_get_position(mesh, ids[ivert], pos));
+ d2_min(lower, lower, pos);
+ d2_max(upper, upper, pos);
+ }
+ }
+}
+
+static void
+mesh_release(ref_T* ref)
+{
+ struct scpr_mesh* mesh;
+ 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);
+}
+
+/*******************************************************************************
+ * Exported functions
+ ******************************************************************************/
+res_T
+scpr_mesh_create(struct mem_allocator* mem_allocator, struct scpr_mesh** out_mesh)
+{
+ struct scpr_mesh* mesh = NULL;
+ struct mem_allocator* allocator = NULL;
+ res_T res = RES_OK;
+
+ if(!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));
+ 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;
+
+exit:
+ if(out_mesh) *out_mesh = mesh;
+ return res;
+
+error:
+ if(mesh) {
+ SCPR(mesh_ref_put(mesh));
+ mesh = NULL;
+ }
+ goto exit;
+}
+
+res_T
+scpr_mesh_ref_get(struct scpr_mesh* mesh)
+{
+ if(!mesh) return RES_BAD_ARG;
+ ref_get(&mesh->ref);
+ return RES_OK;
+}
+
+res_T
+scpr_mesh_ref_put(struct scpr_mesh* mesh)
+{
+ if(!mesh) return RES_BAD_ARG;
+ ref_put(&mesh->ref, mesh_release);
+ return RES_OK;
+}
+
+res_T
+scpr_mesh_setup_indexed_vertices
+ (struct scpr_mesh* mesh,
+ const size_t ntris,
+ void (*get_indices)(const size_t itri, size_t ids[3], void* ctx),
+ const size_t nverts,
+ void (*get_position)(const size_t ivert, double pos[2], void* ctx),
+ void* data)
+{
+ double* pos = NULL;
+ size_t* ids = NULL;
+ size_t i;
+ res_T res = RES_OK;
+
+ if(!mesh || !ntris || !get_indices || !nverts || !get_position) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ res = darray_double_resize(&mesh->coords, nverts*2/*#coords per vertex*/);
+ if(res != RES_OK) goto error;
+ res = darray_size_t_resize(&mesh->indices, ntris*3/*#vertices per triangle*/);
+ if(res != RES_OK) goto error;
+
+ /* Fetch mesh positions */
+ pos = darray_double_data_get(&mesh->coords);
+ FOR_EACH(i, 0, nverts) {
+ get_position(i, pos+i*2, data);
+ }
+
+ /* Fetch mesh indices */
+ ids = darray_size_t_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) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ }
+
+exit:
+ return res;
+error:
+ if(mesh) {
+ darray_double_clear(&mesh->coords);
+ darray_size_t_clear(&mesh->indices);
+ }
+ goto exit;
+}
+
+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) == 0);
+ *nverts = darray_double_size_get(&mesh->coords) / 2;
+ return RES_OK;
+}
+
+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) == 0);
+ *ntris = darray_size_t_size_get(&mesh->indices) / 3;
+ return RES_OK;
+}
+
+res_T
+scpr_mesh_get_indices
+ (const struct scpr_mesh* mesh, const size_t itri, size_t ids[3])
+{
+ size_t ntris;
+ 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)[itri*3 + 0];
+ ids[1] = darray_size_t_cdata_get(&mesh->indices)[itri*3 + 1];
+ ids[2] = darray_size_t_cdata_get(&mesh->indices)[itri*3 + 2];
+ return RES_OK;
+}
+
+res_T
+scpr_mesh_get_position
+ (const struct scpr_mesh* mesh, const size_t ivert, double pos[2])
+{
+ size_t nverts;
+ 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)[ivert*2+0];
+ pos[1] = darray_double_cdata_get(&mesh->coords)[ivert*2+1];
+ return RES_OK;
+}
+
+res_T
+scpr_mesh_clip
+ (struct scpr_mesh* mesh,
+ const enum scpr_operation op,
+ struct scpr_polygon* poly_desc)
+{
+ double lower[2], upper[2], extend[2];
+ struct poly poly;
+ 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 htable_vertex vertices; /* Map a coordinate to its index */
+ ClipperLib::Clipper clipper(ClipperLib::ioStrictlySimple);
+ ClipperLib::Paths output;
+ ClipperLib::Path cand_path;
+ ClipperLib::Path clip_path;
+ ClipperLib::ClipType clip_type;
+ size_t ivert, nverts;
+ size_t itri, ntris;
+
+ res_T res = RES_OK;
+
+ if(!mesh || !poly_desc || (unsigned)op >= SCPR_OPERATIONS_COUNT__)
+ return RES_BAD_ARG;
+
+ 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);
+
+ poly_init(mesh->allocator, &poly);
+ res = poly_setup(&poly, poly_desc);
+ if(res != RES_OK) goto error;
+ if(aabb_is_degenerated(poly.lower, poly.upper)) goto exit;
+
+ mesh_compute_aabb(mesh, lower, upper);
+ if(aabb_is_degenerated(lower, upper)) goto exit;
+
+ /* Compute the overall aabb of the candidate and the clip polygon */
+ d2_min(lower, lower, poly.lower);
+ d2_max(upper, upper, poly.upper);
+ d2_sub(extend, upper, lower);
+
+ /* Setup the clip path */
+ nverts = darray_double_size_get(&poly.coords) / 2/*#coords per vertex*/;
+ FOR_EACH(ivert, 0, nverts) {
+ const double* v = darray_double_cdata_get(&poly.coords) + ivert*2;
+ ClipperLib::IntPoint pt;
+ pt.X = double_to_cInt(v[0], extend[0]);
+ pt.Y = double_to_cInt(v[1], extend[1]);
+ clip_path.push_back(pt);
+ }
+
+ /* Create the polygon structure used to triangulate the clipped polygons */
+ res = polygon_create(mesh->allocator, &polygon);
+ if(res != RES_OK) goto error;
+
+ /* 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];
+
+ /* 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);
+
+ /* Do not clip triangles that don not intersect the clip AABB */
+ if(!aabb_intersect(lower, upper, poly.lower, poly.upper)) {
+ if(op != SCPR_AND) {
+ res = register_triangle(tri, &coords, &indices, &vertices);
+ if(res != RES_OK) goto error;
+ }
+ continue;
+ }
+
+ /* Setup the candidate path */
+ cand_path.clear();
+ FOR_EACH(ivert, 0, 3) {
+ ClipperLib::IntPoint pt;
+ pt.X = double_to_cInt(tri[ivert][0], extend[0]);
+ pt.Y = double_to_cInt(tri[ivert][1], extend[1]);
+ cand_path.push_back(pt);
+ }
+
+ /* Clip the polygon */
+ clipper.Clear();
+ clipper.AddPath(cand_path, ClipperLib::ptSubject, 1);
+ clipper.AddPath(clip_path, ClipperLib::ptClip, 1);
+ clipper.Execute(clip_type, output);
+
+ /* Register the polygons */
+ res = register_paths
+ (output, &coords, &indices, &vertices, extend, polygon);
+ if(res != RES_OK) goto error;
+ }
+
+ res = darray_double_copy_and_clear(&mesh->coords, &coords);
+ if(res != RES_OK) FATAL("Unexpected error.\n");
+ res = darray_size_t_copy_and_clear(&mesh->indices, &indices);
+ if(res != RES_OK) FATAL("Unexpected error.\n");
+
+exit:
+ if(polygon) POLYGON(ref_put(polygon));
+ darray_double_release(&coords);
+ darray_size_t_release(&indices);
+ htable_vertex_release(&vertices);
+ poly_release(&poly);
+ return res;
+error:
+ goto exit;
+}
+
diff --git a/src/test_cpr_clip.c b/src/test_cpr_clip.c
@@ -1,215 +0,0 @@
-/* Copyright (C) |Meso|Star> 2016 (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 "cpr.h"
-#include "test_cpr_utils.h"
-
-#include <rsys/math.h>
-#include <rsys/stretchy_array.h>
-
-static void
-get_clip_pos(const size_t ivert, double pos[2], void* ctx)
-{
- const double* coords = ctx;
- NCHECK(pos, NULL);
- NCHECK(ctx, NULL);
- pos[0] = coords[ivert*2+0];
- pos[1] = coords[ivert*2+1];
-}
-
-static void
-dump_obj(FILE* stream, const struct cpr_mesh* mesh)
-{
- size_t i, n;
-
- NCHECK(stream, NULL);
- NCHECK(mesh, NULL);
-
- CHECK(cpr_mesh_get_vertices_count(mesh, &n), RES_OK);
- FOR_EACH(i, 0, n) {
- double pos[2];
- CHECK(cpr_mesh_get_position(mesh, i, pos), RES_OK);
- fprintf(stream, "v %g %g 0\n", SPLIT2(pos));
- }
-
- CHECK(cpr_mesh_get_triangles_count(mesh, &n), RES_OK);
- FOR_EACH(i, 0, n) {
- size_t ids[3];
- CHECK(cpr_mesh_get_indices(mesh, i, ids), RES_OK);
- fprintf(stream, "f %lu %lu %lu\n",
- (unsigned long)(ids[0] + 1),
- (unsigned long)(ids[1] + 1),
- (unsigned long)(ids[2] + 1));
- }
-}
-
-static void
-test_triangle(struct cpr_mesh* mesh)
-{
- const double triangle_pos[] = { 0.0, 0.0, 0.0, 1.0, 1.0, 0.0 };
- const size_t triangle_ids[] = { 0, 1, 2 };
- const double clip_pos[] = { -1.0, 0.25, 1.0, 0.75, 1, 0.25 };
- struct cpr_polygon poly;
- struct mesh_context ctx;
- size_t ntris;
-
- ctx.coords = triangle_pos;
- ctx.nverts = 3;
- ctx.indices = triangle_ids;
- ctx.ntris = 1;
- CHECK(cpr_mesh_setup_indexed_vertices
- (mesh, ctx.ntris, get_ids, 3, get_pos, &ctx), RES_OK);
-
- poly.get_position = get_clip_pos;
- poly.nvertices = sizeof(clip_pos)/sizeof(double[2]);
- poly.context = (void*)clip_pos;
- CHECK(cpr_mesh_clip(NULL, CPR_OPERATIONS_COUNT__, NULL), RES_BAD_ARG);
- CHECK(cpr_mesh_clip(mesh, CPR_OPERATIONS_COUNT__, NULL), RES_BAD_ARG);
- CHECK(cpr_mesh_clip(NULL, CPR_OPERATIONS_COUNT__, &poly), RES_BAD_ARG);
- CHECK(cpr_mesh_clip(mesh, CPR_OPERATIONS_COUNT__, &poly), RES_BAD_ARG);
- CHECK(cpr_mesh_clip(NULL, CPR_SUB, NULL), RES_BAD_ARG);
- CHECK(cpr_mesh_clip(mesh, CPR_SUB, NULL), RES_BAD_ARG);
- CHECK(cpr_mesh_clip(NULL, CPR_SUB, &poly), RES_BAD_ARG);
- CHECK(cpr_mesh_clip(mesh, CPR_SUB, &poly), RES_OK);
-
- /*dump_obj(stdout, mesh);*/
-
- CHECK(cpr_mesh_get_triangles_count(mesh, &ntris), RES_OK);
- CHECK(ntris, 3);
-}
-
-static void
-test_quad(struct cpr_mesh* mesh)
-{
- const double quad_pos[] = { 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0 };
- const size_t quad_ids[] = { 0, 1, 3, 3, 1, 2 };
- const double clip_pos[] = { -0.25, 0.25, -0.25, 0.75, 1.25, 0.75, 1.25, 0.25 };
- struct cpr_polygon poly;
- struct mesh_context ctx;
-
- ctx.coords = quad_pos;
- ctx.nverts = sizeof(quad_pos)/sizeof(double[2]);
- ctx.indices = quad_ids;
- ctx.ntris = sizeof(quad_ids)/sizeof(size_t[3]);
- CHECK(cpr_mesh_setup_indexed_vertices
- (mesh, ctx.ntris, get_ids, ctx.nverts, get_pos, &ctx), RES_OK);
-
- poly.get_position = get_clip_pos;
- poly.nvertices = sizeof(clip_pos)/sizeof(double[2]);
- poly.context = (void*)clip_pos;
- CHECK(cpr_mesh_clip(mesh, CPR_AND, &poly), RES_OK);
-
- /*dump_obj(stdout, mesh);*/
-}
-
-static void
-test_disk(struct cpr_mesh* mesh)
-{
- const double clip[] = { -1.75, -1.75, 1.75, -1.75, 1.75, 1.75, -1.75, 1.75 };
- const size_t ninternal_disks = 10;
- const double radius = 2.5;
- const double internal_disk_step = radius / (double)ninternal_disks;
- const size_t nslices = 64;
- struct mesh_context ctx;
- struct cpr_polygon poly;
- double* pos = NULL;
- size_t* ids = NULL;
- size_t i, j;
-
- FOR_EACH(i, 0, ninternal_disks) {
- const double r = (double)(i+1)*internal_disk_step;
- FOR_EACH(j, 0, nslices) {
- const double theta = (double)j / (double)nslices * 2 * PI;
- const double x = r * cos(theta);
- const double y = r * sin(theta);
- sa_push(pos, x);
- sa_push(pos, y);
- }
- }
-
- /* Center point */
- sa_push(pos, 0.0);
- sa_push(pos, 0.0);
-
- FOR_EACH(i, 0, ninternal_disks-1) {
- const size_t offset = (i+1) * nslices;
- FOR_EACH(j, 0, nslices) {
- const size_t id0 = j + offset;
- const size_t id1 = ((j + 1) % nslices) + offset;
- const size_t id2 = id0 - nslices;
- const size_t id3 = id1 - nslices;
-
- sa_push(ids, id0);
- sa_push(ids, id2);
- sa_push(ids, id1);
-
- sa_push(ids, id1);
- sa_push(ids, id2);
- sa_push(ids, id3);
- }
- }
-
- /* Center triangles */
- FOR_EACH(j, 0, nslices) {
- const size_t id0 = j;
- const size_t id1 = (j + 1) % nslices;
- const size_t id2 = sa_size(pos)/2 - 1;
-
- sa_push(ids, id0);
- sa_push(ids, id2);
- sa_push(ids, id1);
- }
-
- ctx.coords = pos;
- ctx.nverts = sa_size(pos)/2;
- ctx.indices = ids;
- ctx.ntris = sa_size(ids)/3;
- CHECK(cpr_mesh_setup_indexed_vertices
- (mesh, ctx.ntris, get_ids, ctx.nverts, get_pos, &ctx), RES_OK);
-
- poly.get_position = get_clip_pos;
- poly.nvertices = sizeof(clip)/sizeof(double[2]);
- poly.context = (void*)clip;
- CHECK(cpr_mesh_clip(mesh, CPR_SUB, &poly), RES_OK);
-
- dump_obj(stdout, mesh);
-
- sa_release(pos);
- sa_release(ids);
-}
-
-int
-main(int argc, char** argv)
-{
- struct mem_allocator allocator;
- struct cpr_mesh* mesh;
- (void)argc, (void)argv;
-
- mem_init_proxy_allocator(&allocator, &mem_default_allocator);
-
- CHECK(cpr_mesh_create(&allocator, &mesh), RES_OK);
-
- test_triangle(mesh);
- test_quad(mesh);
- test_disk(mesh);
-
- CHECK(cpr_mesh_ref_put(mesh), RES_OK);
-
- check_memory_allocator(&allocator);
- mem_shutdown_proxy_allocator(&allocator);
- CHECK(mem_allocated_size(), 0);
- return 0;
-}
-
diff --git a/src/test_cpr_mesh.c b/src/test_cpr_mesh.c
@@ -1,164 +0,0 @@
-/* Copyright (C) |Meso|Star> 2016 (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 "cpr.h"
-#include "test_cpr_utils.h"
-
-int
-main(int argc, char** argv)
-{
- const double coords[] = {
- 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
- };
- const size_t nverts = sizeof(coords)/sizeof(double[2]);
- const size_t indices[] = {
- 0, 1, 3,
- 3, 1, 4,
- 1, 2, 4,
- 4, 2, 5,
- 3, 4, 6,
- 6, 4, 7,
- 4, 5, 7,
- 7, 5, 8
- };
- const size_t ntris = sizeof(indices)/sizeof(size_t[3]);
- const size_t indices_bad[] = { 7, 5, 9 };
- size_t ids[3];
- double pos[2];
- size_t i, n;
- struct mem_allocator allocator;
- struct mesh_context ctx;
- struct cpr_mesh* mesh;
- (void)argc, (void)argv;
-
- mem_init_proxy_allocator(&allocator, &mem_default_allocator);
-
- CHECK(cpr_mesh_create(NULL, NULL), RES_BAD_ARG);
- CHECK(cpr_mesh_create(&allocator, NULL), RES_BAD_ARG);
- CHECK(cpr_mesh_create(NULL, &mesh), RES_OK);
-
- CHECK(cpr_mesh_ref_get(NULL), RES_BAD_ARG);
- CHECK(cpr_mesh_ref_get(mesh), RES_OK);
- CHECK(cpr_mesh_ref_put(NULL), RES_BAD_ARG);
- CHECK(cpr_mesh_ref_put(mesh), RES_OK);
- CHECK(cpr_mesh_ref_put(mesh), RES_OK);
-
- CHECK(cpr_mesh_create(&allocator, &mesh), RES_OK);
-
- ctx.coords = coords;
- ctx.nverts = nverts;
- ctx.indices = indices;
- ctx.ntris = ntris;
-
- #define SETUP cpr_mesh_setup_indexed_vertices
- CHECK(SETUP(NULL, 0, NULL, 0, NULL, &ctx), RES_BAD_ARG);
- CHECK(SETUP(mesh, 0, NULL, 0, NULL, &ctx), RES_BAD_ARG);
- CHECK(SETUP(NULL, ntris, NULL, 0, NULL, &ctx), RES_BAD_ARG);
- CHECK(SETUP(mesh, ntris, NULL, 0, NULL, &ctx), RES_BAD_ARG);
- CHECK(SETUP(NULL, 0, get_ids, 0, NULL, &ctx), RES_BAD_ARG);
- CHECK(SETUP(mesh, 0, get_ids, 0, NULL, &ctx), RES_BAD_ARG);
- CHECK(SETUP(NULL, ntris, get_ids, 0, NULL, &ctx), RES_BAD_ARG);
- CHECK(SETUP(mesh, ntris, get_ids, 0, NULL, &ctx), RES_BAD_ARG);
- CHECK(SETUP(NULL, 0, NULL, nverts, NULL, &ctx), RES_BAD_ARG);
- CHECK(SETUP(mesh, 0, NULL, nverts, NULL, &ctx), RES_BAD_ARG);
- CHECK(SETUP(NULL, ntris, NULL, nverts, NULL, &ctx), RES_BAD_ARG);
- CHECK(SETUP(mesh, ntris, NULL, nverts, NULL, &ctx), RES_BAD_ARG);
- CHECK(SETUP(NULL, 0, get_ids, nverts, NULL, &ctx), RES_BAD_ARG);
- CHECK(SETUP(mesh, 0, get_ids, nverts, NULL, &ctx), RES_BAD_ARG);
- CHECK(SETUP(NULL, ntris, get_ids, nverts, NULL, &ctx), RES_BAD_ARG);
- CHECK(SETUP(mesh, ntris, get_ids, nverts, NULL, &ctx), RES_BAD_ARG);
- CHECK(SETUP(NULL, 0, NULL, 0, get_pos, &ctx), RES_BAD_ARG);
- CHECK(SETUP(mesh, 0, NULL, 0, get_pos, &ctx), RES_BAD_ARG);
- CHECK(SETUP(NULL, ntris, NULL, 0, get_pos, &ctx), RES_BAD_ARG);
- CHECK(SETUP(mesh, ntris, NULL, 0, get_pos, &ctx), RES_BAD_ARG);
- CHECK(SETUP(NULL, 0, get_ids, 0, get_pos, &ctx), RES_BAD_ARG);
- CHECK(SETUP(mesh, 0, get_ids, 0, get_pos, &ctx), RES_BAD_ARG);
- CHECK(SETUP(NULL, ntris, get_ids, 0, get_pos, &ctx), RES_BAD_ARG);
- CHECK(SETUP(mesh, ntris, get_ids, 0, get_pos, &ctx), RES_BAD_ARG);
- CHECK(SETUP(NULL, 0, NULL, nverts, get_pos, &ctx), RES_BAD_ARG);
- CHECK(SETUP(mesh, 0, NULL, nverts, get_pos, &ctx), RES_BAD_ARG);
- CHECK(SETUP(NULL, ntris, NULL, nverts, get_pos, &ctx), RES_BAD_ARG);
- CHECK(SETUP(mesh, ntris, NULL, nverts, get_pos, &ctx), RES_BAD_ARG);
- CHECK(SETUP(NULL, 0, get_ids, nverts, get_pos, &ctx), RES_BAD_ARG);
- CHECK(SETUP(mesh, 0, get_ids, nverts, get_pos, &ctx), RES_BAD_ARG);
- CHECK(SETUP(NULL, ntris, get_ids, nverts, get_pos, &ctx), RES_BAD_ARG);
- CHECK(SETUP(mesh, ntris, get_ids, nverts, get_pos, &ctx), RES_OK);
- ctx.indices = indices_bad;
- CHECK(SETUP(mesh, 1, get_ids, nverts, get_pos, &ctx), RES_BAD_ARG);
- #undef SETUP
-
- CHECK(cpr_mesh_get_triangles_count(NULL, NULL), RES_BAD_ARG);
- CHECK(cpr_mesh_get_triangles_count(mesh, NULL), RES_BAD_ARG);
- CHECK(cpr_mesh_get_triangles_count(NULL, &n), RES_BAD_ARG);
- CHECK(cpr_mesh_get_triangles_count(mesh, &n), RES_OK);
- CHECK(n, 0);
-
- CHECK(cpr_mesh_get_vertices_count(NULL, NULL), RES_BAD_ARG);
- CHECK(cpr_mesh_get_vertices_count(mesh, NULL), RES_BAD_ARG);
- CHECK(cpr_mesh_get_vertices_count(NULL, &n), RES_BAD_ARG);
- CHECK(cpr_mesh_get_vertices_count(mesh, &n), RES_OK);
- CHECK(n, 0);
-
- ctx.indices = indices;
- CHECK(cpr_mesh_setup_indexed_vertices
- (mesh, ntris, get_ids, nverts, get_pos, &ctx), RES_OK);
- CHECK(cpr_mesh_get_triangles_count(mesh, &n), RES_OK);
- CHECK(n, ntris);
- CHECK(cpr_mesh_get_vertices_count(mesh, &n), RES_OK);
- CHECK(n, nverts);
-
- CHECK(cpr_mesh_get_indices(NULL, ntris, NULL), RES_BAD_ARG);
- CHECK(cpr_mesh_get_indices(mesh, ntris, NULL), RES_BAD_ARG);
- CHECK(cpr_mesh_get_indices(NULL, 0, NULL), RES_BAD_ARG);
- CHECK(cpr_mesh_get_indices(mesh, 0, NULL), RES_BAD_ARG);
- CHECK(cpr_mesh_get_indices(NULL, ntris, ids), RES_BAD_ARG);
- CHECK(cpr_mesh_get_indices(mesh, ntris, ids), RES_BAD_ARG);
- CHECK(cpr_mesh_get_indices(NULL, 0, ids), RES_BAD_ARG);
- FOR_EACH(i, 0, ntris) {
- CHECK(cpr_mesh_get_indices(mesh, i, ids), RES_OK);
- CHECK(ids[0], indices[i*3+0]);
- CHECK(ids[1], indices[i*3+1]);
- CHECK(ids[2], indices[i*3+2]);
- }
-
- CHECK(cpr_mesh_get_position(NULL, nverts, NULL), RES_BAD_ARG);
- CHECK(cpr_mesh_get_position(mesh, nverts, NULL), RES_BAD_ARG);
- CHECK(cpr_mesh_get_position(NULL, 0, NULL), RES_BAD_ARG);
- CHECK(cpr_mesh_get_position(mesh, 0, NULL), RES_BAD_ARG);
- CHECK(cpr_mesh_get_position(NULL, nverts, pos), RES_BAD_ARG);
- CHECK(cpr_mesh_get_position(mesh, nverts, pos), RES_BAD_ARG);
- CHECK(cpr_mesh_get_position(NULL, 0, pos), RES_BAD_ARG);
- FOR_EACH(i, 0, nverts) {
- CHECK(cpr_mesh_get_position(mesh, i, pos), RES_OK);
- CHECK(pos[0], coords[i*2+0]);
- CHECK(pos[1], coords[i*2+1]);
- }
-
- CHECK(cpr_mesh_ref_put(mesh), RES_OK);
-
- check_memory_allocator(&allocator);
- mem_shutdown_proxy_allocator(&allocator);
- CHECK(mem_allocated_size(), 0);
- return 0;
-}
-
diff --git a/src/test_scpr_clip.c b/src/test_scpr_clip.c
@@ -0,0 +1,215 @@
+/* Copyright (C) |Meso|Star> 2016 (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 "test_scpr_utils.h"
+
+#include <rsys/math.h>
+#include <rsys/stretchy_array.h>
+
+static void
+get_clip_pos(const size_t ivert, double pos[2], void* ctx)
+{
+ const double* coords = ctx;
+ NCHECK(pos, NULL);
+ NCHECK(ctx, NULL);
+ pos[0] = coords[ivert*2+0];
+ pos[1] = coords[ivert*2+1];
+}
+
+static void
+dump_obj(FILE* stream, const struct scpr_mesh* mesh)
+{
+ size_t i, n;
+
+ NCHECK(stream, NULL);
+ NCHECK(mesh, NULL);
+
+ CHECK(scpr_mesh_get_vertices_count(mesh, &n), RES_OK);
+ FOR_EACH(i, 0, n) {
+ double pos[2];
+ CHECK(scpr_mesh_get_position(mesh, i, pos), RES_OK);
+ fprintf(stream, "v %g %g 0\n", SPLIT2(pos));
+ }
+
+ CHECK(scpr_mesh_get_triangles_count(mesh, &n), RES_OK);
+ FOR_EACH(i, 0, n) {
+ size_t ids[3];
+ CHECK(scpr_mesh_get_indices(mesh, i, ids), RES_OK);
+ fprintf(stream, "f %lu %lu %lu\n",
+ (unsigned long)(ids[0] + 1),
+ (unsigned long)(ids[1] + 1),
+ (unsigned long)(ids[2] + 1));
+ }
+}
+
+static void
+test_triangle(struct scpr_mesh* mesh)
+{
+ const double triangle_pos[] = { 0.0, 0.0, 0.0, 1.0, 1.0, 0.0 };
+ const size_t triangle_ids[] = { 0, 1, 2 };
+ const double clip_pos[] = { -1.0, 0.25, 1.0, 0.75, 1, 0.25 };
+ struct scpr_polygon poly;
+ struct mesh_context ctx;
+ size_t ntris;
+
+ ctx.coords = triangle_pos;
+ ctx.nverts = 3;
+ ctx.indices = triangle_ids;
+ ctx.ntris = 1;
+ CHECK(scpr_mesh_setup_indexed_vertices
+ (mesh, ctx.ntris, get_ids, 3, get_pos, &ctx), RES_OK);
+
+ poly.get_position = get_clip_pos;
+ poly.nvertices = sizeof(clip_pos)/sizeof(double[2]);
+ poly.context = (void*)clip_pos;
+ CHECK(scpr_mesh_clip(NULL, SCPR_OPERATIONS_COUNT__, NULL), RES_BAD_ARG);
+ CHECK(scpr_mesh_clip(mesh, SCPR_OPERATIONS_COUNT__, NULL), RES_BAD_ARG);
+ CHECK(scpr_mesh_clip(NULL, SCPR_OPERATIONS_COUNT__, &poly), RES_BAD_ARG);
+ CHECK(scpr_mesh_clip(mesh, SCPR_OPERATIONS_COUNT__, &poly), RES_BAD_ARG);
+ CHECK(scpr_mesh_clip(NULL, SCPR_SUB, NULL), RES_BAD_ARG);
+ CHECK(scpr_mesh_clip(mesh, SCPR_SUB, NULL), RES_BAD_ARG);
+ CHECK(scpr_mesh_clip(NULL, SCPR_SUB, &poly), RES_BAD_ARG);
+ CHECK(scpr_mesh_clip(mesh, SCPR_SUB, &poly), RES_OK);
+
+ /*dump_obj(stdout, mesh);*/
+
+ CHECK(scpr_mesh_get_triangles_count(mesh, &ntris), RES_OK);
+ CHECK(ntris, 3);
+}
+
+static void
+test_quad(struct scpr_mesh* mesh)
+{
+ const double quad_pos[] = { 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0 };
+ const size_t quad_ids[] = { 0, 1, 3, 3, 1, 2 };
+ const double clip_pos[] = { -0.25, 0.25, -0.25, 0.75, 1.25, 0.75, 1.25, 0.25 };
+ struct scpr_polygon poly;
+ struct mesh_context ctx;
+
+ ctx.coords = quad_pos;
+ ctx.nverts = sizeof(quad_pos)/sizeof(double[2]);
+ ctx.indices = quad_ids;
+ ctx.ntris = sizeof(quad_ids)/sizeof(size_t[3]);
+ CHECK(scpr_mesh_setup_indexed_vertices
+ (mesh, ctx.ntris, get_ids, ctx.nverts, get_pos, &ctx), RES_OK);
+
+ poly.get_position = get_clip_pos;
+ poly.nvertices = sizeof(clip_pos)/sizeof(double[2]);
+ poly.context = (void*)clip_pos;
+ CHECK(scpr_mesh_clip(mesh, SCPR_AND, &poly), RES_OK);
+
+ /*dump_obj(stdout, mesh);*/
+}
+
+static void
+test_disk(struct scpr_mesh* mesh)
+{
+ const double clip[] = { -1.75, -1.75, 1.75, -1.75, 1.75, 1.75, -1.75, 1.75 };
+ const size_t ninternal_disks = 10;
+ const double radius = 2.5;
+ const double internal_disk_step = radius / (double)ninternal_disks;
+ const size_t nslices = 64;
+ struct mesh_context ctx;
+ struct scpr_polygon poly;
+ double* pos = NULL;
+ size_t* ids = NULL;
+ size_t i, j;
+
+ FOR_EACH(i, 0, ninternal_disks) {
+ const double r = (double)(i+1)*internal_disk_step;
+ FOR_EACH(j, 0, nslices) {
+ const double theta = (double)j / (double)nslices * 2 * PI;
+ const double x = r * cos(theta);
+ const double y = r * sin(theta);
+ sa_push(pos, x);
+ sa_push(pos, y);
+ }
+ }
+
+ /* Center point */
+ sa_push(pos, 0.0);
+ sa_push(pos, 0.0);
+
+ FOR_EACH(i, 0, ninternal_disks-1) {
+ const size_t offset = (i+1) * nslices;
+ FOR_EACH(j, 0, nslices) {
+ const size_t id0 = j + offset;
+ const size_t id1 = ((j + 1) % nslices) + offset;
+ const size_t id2 = id0 - nslices;
+ const size_t id3 = id1 - nslices;
+
+ sa_push(ids, id0);
+ sa_push(ids, id2);
+ sa_push(ids, id1);
+
+ sa_push(ids, id1);
+ sa_push(ids, id2);
+ sa_push(ids, id3);
+ }
+ }
+
+ /* Center triangles */
+ FOR_EACH(j, 0, nslices) {
+ const size_t id0 = j;
+ const size_t id1 = (j + 1) % nslices;
+ const size_t id2 = sa_size(pos)/2 - 1;
+
+ sa_push(ids, id0);
+ sa_push(ids, id2);
+ sa_push(ids, id1);
+ }
+
+ ctx.coords = pos;
+ ctx.nverts = sa_size(pos)/2;
+ ctx.indices = ids;
+ ctx.ntris = sa_size(ids)/3;
+ CHECK(scpr_mesh_setup_indexed_vertices
+ (mesh, ctx.ntris, get_ids, ctx.nverts, get_pos, &ctx), RES_OK);
+
+ poly.get_position = get_clip_pos;
+ poly.nvertices = sizeof(clip)/sizeof(double[2]);
+ poly.context = (void*)clip;
+ CHECK(scpr_mesh_clip(mesh, SCPR_SUB, &poly), RES_OK);
+
+ dump_obj(stdout, mesh);
+
+ sa_release(pos);
+ sa_release(ids);
+}
+
+int
+main(int argc, char** argv)
+{
+ struct mem_allocator allocator;
+ struct scpr_mesh* mesh;
+ (void)argc, (void)argv;
+
+ mem_init_proxy_allocator(&allocator, &mem_default_allocator);
+
+ CHECK(scpr_mesh_create(&allocator, &mesh), RES_OK);
+
+ test_triangle(mesh);
+ test_quad(mesh);
+ test_disk(mesh);
+
+ CHECK(scpr_mesh_ref_put(mesh), RES_OK);
+
+ check_memory_allocator(&allocator);
+ mem_shutdown_proxy_allocator(&allocator);
+ CHECK(mem_allocated_size(), 0);
+ return 0;
+}
+
diff --git a/src/test_scpr_mesh.c b/src/test_scpr_mesh.c
@@ -0,0 +1,164 @@
+/* Copyright (C) |Meso|Star> 2016 (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 "test_scpr_utils.h"
+
+int
+main(int argc, char** argv)
+{
+ const double coords[] = {
+ 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
+ };
+ const size_t nverts = sizeof(coords)/sizeof(double[2]);
+ const size_t indices[] = {
+ 0, 1, 3,
+ 3, 1, 4,
+ 1, 2, 4,
+ 4, 2, 5,
+ 3, 4, 6,
+ 6, 4, 7,
+ 4, 5, 7,
+ 7, 5, 8
+ };
+ const size_t ntris = sizeof(indices)/sizeof(size_t[3]);
+ const size_t indices_bad[] = { 7, 5, 9 };
+ size_t ids[3];
+ double pos[2];
+ size_t i, n;
+ struct mem_allocator allocator;
+ struct mesh_context ctx;
+ struct scpr_mesh* mesh;
+ (void)argc, (void)argv;
+
+ mem_init_proxy_allocator(&allocator, &mem_default_allocator);
+
+ CHECK(scpr_mesh_create(NULL, NULL), RES_BAD_ARG);
+ CHECK(scpr_mesh_create(&allocator, NULL), RES_BAD_ARG);
+ CHECK(scpr_mesh_create(NULL, &mesh), RES_OK);
+
+ CHECK(scpr_mesh_ref_get(NULL), RES_BAD_ARG);
+ CHECK(scpr_mesh_ref_get(mesh), RES_OK);
+ CHECK(scpr_mesh_ref_put(NULL), RES_BAD_ARG);
+ CHECK(scpr_mesh_ref_put(mesh), RES_OK);
+ CHECK(scpr_mesh_ref_put(mesh), RES_OK);
+
+ CHECK(scpr_mesh_create(&allocator, &mesh), RES_OK);
+
+ ctx.coords = coords;
+ ctx.nverts = nverts;
+ ctx.indices = indices;
+ ctx.ntris = ntris;
+
+ #define SETUP scpr_mesh_setup_indexed_vertices
+ CHECK(SETUP(NULL, 0, NULL, 0, NULL, &ctx), RES_BAD_ARG);
+ CHECK(SETUP(mesh, 0, NULL, 0, NULL, &ctx), RES_BAD_ARG);
+ CHECK(SETUP(NULL, ntris, NULL, 0, NULL, &ctx), RES_BAD_ARG);
+ CHECK(SETUP(mesh, ntris, NULL, 0, NULL, &ctx), RES_BAD_ARG);
+ CHECK(SETUP(NULL, 0, get_ids, 0, NULL, &ctx), RES_BAD_ARG);
+ CHECK(SETUP(mesh, 0, get_ids, 0, NULL, &ctx), RES_BAD_ARG);
+ CHECK(SETUP(NULL, ntris, get_ids, 0, NULL, &ctx), RES_BAD_ARG);
+ CHECK(SETUP(mesh, ntris, get_ids, 0, NULL, &ctx), RES_BAD_ARG);
+ CHECK(SETUP(NULL, 0, NULL, nverts, NULL, &ctx), RES_BAD_ARG);
+ CHECK(SETUP(mesh, 0, NULL, nverts, NULL, &ctx), RES_BAD_ARG);
+ CHECK(SETUP(NULL, ntris, NULL, nverts, NULL, &ctx), RES_BAD_ARG);
+ CHECK(SETUP(mesh, ntris, NULL, nverts, NULL, &ctx), RES_BAD_ARG);
+ CHECK(SETUP(NULL, 0, get_ids, nverts, NULL, &ctx), RES_BAD_ARG);
+ CHECK(SETUP(mesh, 0, get_ids, nverts, NULL, &ctx), RES_BAD_ARG);
+ CHECK(SETUP(NULL, ntris, get_ids, nverts, NULL, &ctx), RES_BAD_ARG);
+ CHECK(SETUP(mesh, ntris, get_ids, nverts, NULL, &ctx), RES_BAD_ARG);
+ CHECK(SETUP(NULL, 0, NULL, 0, get_pos, &ctx), RES_BAD_ARG);
+ CHECK(SETUP(mesh, 0, NULL, 0, get_pos, &ctx), RES_BAD_ARG);
+ CHECK(SETUP(NULL, ntris, NULL, 0, get_pos, &ctx), RES_BAD_ARG);
+ CHECK(SETUP(mesh, ntris, NULL, 0, get_pos, &ctx), RES_BAD_ARG);
+ CHECK(SETUP(NULL, 0, get_ids, 0, get_pos, &ctx), RES_BAD_ARG);
+ CHECK(SETUP(mesh, 0, get_ids, 0, get_pos, &ctx), RES_BAD_ARG);
+ CHECK(SETUP(NULL, ntris, get_ids, 0, get_pos, &ctx), RES_BAD_ARG);
+ CHECK(SETUP(mesh, ntris, get_ids, 0, get_pos, &ctx), RES_BAD_ARG);
+ CHECK(SETUP(NULL, 0, NULL, nverts, get_pos, &ctx), RES_BAD_ARG);
+ CHECK(SETUP(mesh, 0, NULL, nverts, get_pos, &ctx), RES_BAD_ARG);
+ CHECK(SETUP(NULL, ntris, NULL, nverts, get_pos, &ctx), RES_BAD_ARG);
+ CHECK(SETUP(mesh, ntris, NULL, nverts, get_pos, &ctx), RES_BAD_ARG);
+ CHECK(SETUP(NULL, 0, get_ids, nverts, get_pos, &ctx), RES_BAD_ARG);
+ CHECK(SETUP(mesh, 0, get_ids, nverts, get_pos, &ctx), RES_BAD_ARG);
+ CHECK(SETUP(NULL, ntris, get_ids, nverts, get_pos, &ctx), RES_BAD_ARG);
+ CHECK(SETUP(mesh, ntris, get_ids, nverts, get_pos, &ctx), RES_OK);
+ ctx.indices = indices_bad;
+ CHECK(SETUP(mesh, 1, get_ids, nverts, get_pos, &ctx), RES_BAD_ARG);
+ #undef SETUP
+
+ CHECK(scpr_mesh_get_triangles_count(NULL, NULL), RES_BAD_ARG);
+ CHECK(scpr_mesh_get_triangles_count(mesh, NULL), RES_BAD_ARG);
+ CHECK(scpr_mesh_get_triangles_count(NULL, &n), RES_BAD_ARG);
+ CHECK(scpr_mesh_get_triangles_count(mesh, &n), RES_OK);
+ CHECK(n, 0);
+
+ CHECK(scpr_mesh_get_vertices_count(NULL, NULL), RES_BAD_ARG);
+ CHECK(scpr_mesh_get_vertices_count(mesh, NULL), RES_BAD_ARG);
+ CHECK(scpr_mesh_get_vertices_count(NULL, &n), RES_BAD_ARG);
+ CHECK(scpr_mesh_get_vertices_count(mesh, &n), RES_OK);
+ CHECK(n, 0);
+
+ ctx.indices = indices;
+ CHECK(scpr_mesh_setup_indexed_vertices
+ (mesh, ntris, get_ids, nverts, get_pos, &ctx), RES_OK);
+ CHECK(scpr_mesh_get_triangles_count(mesh, &n), RES_OK);
+ CHECK(n, ntris);
+ CHECK(scpr_mesh_get_vertices_count(mesh, &n), RES_OK);
+ CHECK(n, nverts);
+
+ CHECK(scpr_mesh_get_indices(NULL, ntris, NULL), RES_BAD_ARG);
+ CHECK(scpr_mesh_get_indices(mesh, ntris, NULL), RES_BAD_ARG);
+ CHECK(scpr_mesh_get_indices(NULL, 0, NULL), RES_BAD_ARG);
+ CHECK(scpr_mesh_get_indices(mesh, 0, NULL), RES_BAD_ARG);
+ CHECK(scpr_mesh_get_indices(NULL, ntris, ids), RES_BAD_ARG);
+ CHECK(scpr_mesh_get_indices(mesh, ntris, ids), RES_BAD_ARG);
+ CHECK(scpr_mesh_get_indices(NULL, 0, ids), RES_BAD_ARG);
+ FOR_EACH(i, 0, ntris) {
+ CHECK(scpr_mesh_get_indices(mesh, i, ids), RES_OK);
+ CHECK(ids[0], indices[i*3+0]);
+ CHECK(ids[1], indices[i*3+1]);
+ CHECK(ids[2], indices[i*3+2]);
+ }
+
+ CHECK(scpr_mesh_get_position(NULL, nverts, NULL), RES_BAD_ARG);
+ CHECK(scpr_mesh_get_position(mesh, nverts, NULL), RES_BAD_ARG);
+ CHECK(scpr_mesh_get_position(NULL, 0, NULL), RES_BAD_ARG);
+ CHECK(scpr_mesh_get_position(mesh, 0, NULL), RES_BAD_ARG);
+ CHECK(scpr_mesh_get_position(NULL, nverts, pos), RES_BAD_ARG);
+ CHECK(scpr_mesh_get_position(mesh, nverts, pos), RES_BAD_ARG);
+ CHECK(scpr_mesh_get_position(NULL, 0, pos), RES_BAD_ARG);
+ FOR_EACH(i, 0, nverts) {
+ CHECK(scpr_mesh_get_position(mesh, i, pos), RES_OK);
+ CHECK(pos[0], coords[i*2+0]);
+ CHECK(pos[1], coords[i*2+1]);
+ }
+
+ CHECK(scpr_mesh_ref_put(mesh), RES_OK);
+
+ check_memory_allocator(&allocator);
+ mem_shutdown_proxy_allocator(&allocator);
+ CHECK(mem_allocated_size(), 0);
+ return 0;
+}
+
diff --git a/src/test_cpr_utils.h b/src/test_scpr_utils.h