star-cpr

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

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:
MREADME.md | 20+++++++++++---------
Mcmake/CMakeLists.txt | 46+++++++++++++++++++++++-----------------------
Dsrc/cpr.h | 115-------------------------------------------------------------------------------
Dsrc/cpr_mesh.c | 611-------------------------------------------------------------------------------
Asrc/scpr.h | 115+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/scpr_mesh.c | 611+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/test_cpr_clip.c | 215-------------------------------------------------------------------------------
Dsrc/test_cpr_mesh.c | 164-------------------------------------------------------------------------------
Asrc/test_scpr_clip.c | 215+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/test_scpr_mesh.c | 164+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rsrc/test_cpr_utils.h -> src/test_scpr_utils.h | 0
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