star-cpr

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

commit ec191db746ff998fbf84ff111eb840db02f8b997
parent bf5ee0c1fce474a78500db63aca3cfd7ea08f83c
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Wed, 28 Sep 2022 17:45:09 +0200

Make scpr_polygon an opaque type

Diffstat:
Mcmake/CMakeLists.txt | 11+++++++----
Msrc/scpr.h | 46++++++++++++++++++++++++++++++++++++++--------
Asrc/scpr_c.h | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/scpr_mesh.c | 114+++++++------------------------------------------------------------------------
Asrc/scpr_polygon.c | 258+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_scpr_clip.c | 119++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Msrc/test_scpr_mesh.c | 100++++++++++++++++++++++++++++++++++++++++----------------------------------------
Asrc/test_scpr_polygon.c | 106+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_scpr_utils.h | 25+++++++++++++++++++++----
9 files changed, 616 insertions(+), 224 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -51,15 +51,17 @@ set(VERSION_MINOR 1) set(VERSION_PATCH 3) set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) -set(SCPR_FILES_SRC scpr_mesh.c) -set(SCPR_FILES_INC scpr.h) +set(SCPR_FILES_SRC scpr_mesh.c scpr_polygon.c) +set(SCPR_FILES_INC_API scpr.h) +set(SCPR_FILES_INC scpr_c.h) set(SCPR_FILES_DOC COPYING README.md) rcmake_prepend_path(SCPR_FILES_SRC ${SCPR_SOURCE_DIR}) rcmake_prepend_path(SCPR_FILES_INC ${SCPR_SOURCE_DIR}) +rcmake_prepend_path(SCPR_FILES_INC_API ${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}) +add_library(scpr SHARED ${SCPR_FILES_SRC} ${SCPR_FILES_INC} ${SCPR_FILES_INC_API}) set_target_properties(scpr PROPERTIES DEFINE_SYMBOL SCPR_SHARED_BUILD VERSION ${VERSION} @@ -88,6 +90,7 @@ if(NOT NO_TEST) endfunction() new_test(test_scpr_clip) new_test(test_scpr_mesh) + new_test(test_scpr_polygon) rcmake_copy_runtime_libraries(test_scpr_clip) endif() @@ -99,6 +102,6 @@ install(TARGETS scpr ARCHIVE DESTINATION lib LIBRARY DESTINATION lib RUNTIME DESTINATION bin) -install(FILES ${SCPR_FILES_INC} DESTINATION include/star/) +install(FILES ${SCPR_FILES_INC_API} DESTINATION include/star/) install(FILES ${SCPR_FILES_DOC} DESTINATION share/doc/star-cpr/) diff --git a/src/scpr.h b/src/scpr.h @@ -41,25 +41,55 @@ enum scpr_operation { 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 data provided as the last argument of the get_position functor */ - void* context; +enum scpr_join_type { + SCPR_JOIN_SQUARE, + SCPR_JOIN_ROUND, + SCPR_JOIN_MITER, + SCPR_JOIN_TYPES_COUNT__ }; -#define SCPR_POLYGON_NULL__ { NULL, 0, NULL } -static const struct scpr_polygon SCPR_POLYGON_NULL = SCPR_POLYGON_NULL__; /* Forward declaration */ struct mem_allocator; +/* Opaque 2D closed polygon data type */ +struct scpr_polygon; /* Opaque 2D mesh data type */ struct scpr_mesh; BEGIN_DECLS SCPR_API res_T +scpr_polygon_create + (struct mem_allocator* allocator, /* May be NULL <=> Use default allocator */ + struct scpr_polygon** polygon); + +SCPR_API res_T +scpr_polygon_ref_get + (struct scpr_polygon* polygon); + +SCPR_API res_T +scpr_polygon_ref_put + (struct scpr_polygon* polygon); + +SCPR_API res_T +scpr_polygon_setup_indexed_vertices + (struct scpr_polygon* polygon, + const size_t nverts, /* #vertices */ + void (*get_position)(const size_t ivert, double pos[2], void* ctx), + void* data); /* Client data set as the last param of the callbacks */ + +SCPR_API res_T +scpr_polygon_get_vertices_count + (const struct scpr_polygon* polygon, + size_t* nverts); + +SCPR_API res_T +scpr_polygon_get_position + (const struct scpr_polygon* polygon, + const size_t ivert, + double position[2]); + +SCPR_API res_T scpr_mesh_create (struct mem_allocator* allocator, /* May be NULL <=> Use default allocator */ struct scpr_mesh** mesh); diff --git a/src/scpr_c.h b/src/scpr_c.h @@ -0,0 +1,61 @@ +/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef SCPR_C_H__ +#define SCPR_C_H__ + +#include "scpr.h" + +#include <rsys/rsys.h> +#include <rsys/ref_count.h> +#include <rsys/double2.h> +#include <rsys/dynamic_array_double.h> +#include <rsys/dynamic_array_size_t.h> +#include <rsys/hash_table.h> + +#undef PI +#include <clipper.core.h> + +struct mem_allocator; + +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 scpr_polygon { + Clipper2Lib::PathsD path; + double lower[2], upper[2]; /* Polygon AABB */ + + ref_T ref; + struct mem_allocator* allocator; +}; + +struct scpr_mesh { + struct darray_double coords; + struct darray_size_t indices; + + ref_T ref; + struct mem_allocator* allocator; +}; + +#endif diff --git a/src/scpr_mesh.c b/src/scpr_mesh.c @@ -14,44 +14,14 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "scpr.h" +#include "scpr_c.h" -#include <clipper.core.h> -#include <clipper.engine.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> +#undef PI #include <clipper.h> -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 ******************************************************************************/ @@ -88,53 +58,6 @@ aabb_is_degenerated(const double lower[2], const double upper[2]) 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], @@ -466,7 +389,6 @@ scpr_mesh_clip struct scpr_polygon* poly_desc) { double lower[2], upper[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 */ @@ -475,10 +397,8 @@ scpr_mesh_clip Clipper2Lib::PathsD output; /* Contour of the clipped polgyon */ Clipper2Lib::PathsD cand_path; /* Contour of the candidate polygon */ Clipper2Lib::PathD tmp; - Clipper2Lib::PathsD clip_path; /* Contour of the clip polygon */ Clipper2Lib::ClipType clip_type; /* Type of clipping to perform */ - size_t ivert, nverts; - size_t itri, ntris; + size_t itri, ntris, ivert; res_T res = RES_OK; @@ -491,29 +411,14 @@ scpr_mesh_clip 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; + if(aabb_is_degenerated(poly_desc->lower, poly_desc->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); - - /* Setup the clip path */ - nverts = darray_double_size_get(&poly.coords) / 2/*#coords per vertex*/; - tmp.clear(); - FOR_EACH(ivert, 0, nverts) { - const double* v = darray_double_cdata_get(&poly.coords) + ivert*2; - Clipper2Lib::PointD pt; - pt.x = v[0]; - pt.y = v[1]; - tmp.push_back(pt); - } - clip_path.push_back(tmp); + d2_min(lower, lower, poly_desc->lower); + d2_max(upper, upper, poly_desc->upper); /* Create the polygon structure used to triangulate the clipped polygons */ res = polygon_create(mesh->allocator, &polygon); @@ -532,8 +437,8 @@ scpr_mesh_clip 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)) { + /* Do not clip triangles that do not intersect the clip AABB */ + if(!aabb_intersect(lower, upper, poly_desc->lower, poly_desc->upper)) { if(op != SCPR_AND) { res = register_triangle(tri, &coords, &indices, &vertices); if(res != RES_OK) goto error; @@ -555,7 +460,7 @@ scpr_mesh_clip /* Clip the polygon */ clipper.Clear(); clipper.AddSubject(cand_path); - clipper.AddClip(clip_path); + clipper.AddClip(poly_desc->path); clipper.Execute(clip_type, Clipper2Lib::FillRule::EvenOdd, output); /* Register the resulting clipped polygons */ @@ -573,7 +478,6 @@ exit: 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_polygon.c b/src/scpr_polygon.c @@ -0,0 +1,258 @@ +/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "scpr.h" +#include "scpr_c.h" + +#include <new> +#include <rsys/mem_allocator.h> +#include <rsys/rsys.h> + +#undef PI +#include <clipper.h> + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static FINLINE Clipper2Lib::JoinType +scpr_join_type_to_clipper_join_type(const enum scpr_join_type t) +{ + Clipper2Lib::JoinType jtype; + switch(t) { + case SCPR_JOIN_SQUARE: jtype = Clipper2Lib::JoinType::Square; break; + case SCPR_JOIN_ROUND: jtype = Clipper2Lib::JoinType::Round; break; + case SCPR_JOIN_MITER: jtype = Clipper2Lib::JoinType::Miter; break; + default: FATAL("Unreachable code\n"); break; + } + return jtype; +} + +static void +polygon_release(ref_T* ref) +{ + struct scpr_polygon* polygon; + ASSERT(ref); + polygon = CONTAINER_OF(ref, struct scpr_polygon, ref); + polygon->path.clear(); + MEM_RM(polygon->allocator, polygon); +} + +/******************************************************************************* + * Exported functions + ******************************************************************************/ +res_T +scpr_polygon_create + (struct mem_allocator* mem_allocator, + struct scpr_polygon** out_polygon) +{ + struct scpr_polygon* polygon = NULL; + struct mem_allocator* allocator = NULL; + res_T res = RES_OK; + + if(!out_polygon) { + res = RES_BAD_ARG; + goto error; + } + + allocator = mem_allocator ? mem_allocator : &mem_default_allocator; + polygon = (struct scpr_polygon*) + MEM_CALLOC(allocator, 1, sizeof(struct scpr_polygon)); + if(!polygon) { + res = RES_MEM_ERR; + goto error; + } + ref_init(&polygon->ref); + polygon->allocator = allocator; + +exit: + if(out_polygon) *out_polygon = polygon; + return res; + +error: + if(polygon) { + SCPR(polygon_ref_put(polygon)); + polygon = NULL; + } + goto exit; +} + +res_T +scpr_polygon_ref_get + (struct scpr_polygon* polygon) +{ + if(!polygon) return RES_BAD_ARG; + ref_get(&polygon->ref); + return RES_OK; +} + +res_T +scpr_polygon_ref_put + (struct scpr_polygon* polygon) +{ + if(!polygon) return RES_BAD_ARG; + ref_put(&polygon->ref, polygon_release); + return RES_OK; +} + +res_T +scpr_polygon_setup_indexed_vertices + (struct scpr_polygon* polygon, + const size_t nverts, + void (*get_position)(const size_t ivert, double pos[2], void* ctx), + void* data) +{ + size_t i; + Clipper2Lib::PathD path; + res_T res = RES_OK; + + if(!polygon || !nverts || !get_position || !data) { + res = RES_BAD_ARG; + goto error; + } + + try { + path.reserve(nverts); + } + catch(std::bad_alloc &e) { + res = RES_MEM_ERR; + goto error; + } + catch(...) { + res = RES_UNKNOWN_ERR; + goto error; + } + + d2_splat(polygon->lower, DBL_MAX); + d2_splat(polygon->upper,-DBL_MAX); + + /* Fetch polygon positions */ + FOR_EACH(i, 0, nverts) { + double tmp[2]; + Clipper2Lib::PointD pt; + get_position(i, tmp, data); + pt.x = tmp[0]; + pt.y = tmp[1]; + path.push_back(pt); + + d2_min(polygon->lower, polygon->lower, tmp); + d2_max(polygon->upper, polygon->upper, tmp); + } + + try { + polygon->path.push_back(path); + } + catch(std::bad_alloc &e) { + res = RES_MEM_ERR; + goto error; + } + catch(...) { + res = RES_UNKNOWN_ERR; + goto error; + } + +exit: + return res; +error: + if(polygon) { + polygon->path.clear(); + d2_splat(polygon->lower, DBL_MAX); + d2_splat(polygon->upper,-DBL_MAX); + } + goto exit; +} + +res_T +scpr_polygon_get_vertices_count + (const struct scpr_polygon* polygon, + size_t* nverts) +{ + if(!polygon || !nverts) return RES_BAD_ARG; + ASSERT(polygon->path.size() <= 1); + if(polygon->path.size() < 1) { + *nverts = 0; + } else { + *nverts = polygon->path[0].size(); + } + return RES_OK; +} + +res_T +scpr_polygon_get_position + (const struct scpr_polygon* polygon, + const size_t ivert, + double pos[2]) +{ + size_t nverts; + const size_t i = ivert; + const Clipper2Lib::PointD* pt; + if(!polygon || !pos) return RES_BAD_ARG; + SCPR(polygon_get_vertices_count(polygon, &nverts)); + if(ivert >= nverts) return RES_BAD_ARG; + pt = &polygon->path[0][i]; + pos[0] = pt->x; + pos[1] = pt->y; + return RES_OK; +} + +res_T +scpr_offset_polygon + (struct scpr_polygon* poly_desc, + const double offset, /* Can be either positive or negative */ + const enum scpr_join_type join_type) +{ + size_t i, nverts; + Clipper2Lib::PathD tmp; + Clipper2Lib::PathsD polygon; + Clipper2Lib::JoinType cjt; + res_T res = RES_OK; + + if(!poly_desc) { + res = RES_BAD_ARG; + goto error; + } + switch(join_type) { + case SCPR_JOIN_SQUARE: + case SCPR_JOIN_ROUND: + case SCPR_JOIN_MITER: + break; + default: + res = RES_BAD_ARG; + goto error; + } + cjt = scpr_join_type_to_clipper_join_type(join_type); + poly_desc->path = Clipper2Lib::InflatePaths(poly_desc->path, offset, cjt, + Clipper2Lib::EndType::Polygon); + + /* Rebuild AABB */ + res = scpr_polygon_get_vertices_count(poly_desc, &nverts); + if(res != RES_OK) goto error; + + if(nverts > 0) { + d2_splat(poly_desc->lower, DBL_MAX); + d2_splat(poly_desc->upper,-DBL_MAX); + FOR_EACH(i, 0, nverts) { + double pos[2]; + res = scpr_polygon_get_position(poly_desc, i, pos); + if(res != RES_OK) goto error; + d2_min(poly_desc->lower, poly_desc->lower, pos); + d2_max(poly_desc->upper, poly_desc->upper, pos); + } + } + +exit: + return res; +error: + goto exit; +} diff --git a/src/test_scpr_clip.c b/src/test_scpr_clip.c @@ -20,16 +20,6 @@ #include <rsys/stretchy_array.h> static void -get_clip_pos(const size_t ivert, double pos[2], void* ctx) -{ - const double* coords = ctx; - CHK(pos != NULL); - CHK(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; @@ -56,74 +46,93 @@ dump_obj(FILE* stream, const struct scpr_mesh* mesh) } static void -test_triangle(struct scpr_mesh* mesh) +test_triangle + (struct mem_allocator* allocator, + struct scpr_mesh* mesh) { const double triangle_pos[] = { 0.0, 0.0, 0.0, 1.0, 1.0, 0.0 }; const 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; + struct scpr_polygon* poly; + struct polygon_context pctx; + struct mesh_context mctx; size_t ntris; - ctx.coords = triangle_pos; - ctx.nverts = 3; - ctx.indices = triangle_ids; - ctx.ntris = 1; + pctx.coords = clip_pos; + pctx.nverts = sizeof(clip_pos)/(2*sizeof(double)); + CHK(scpr_polygon_create(allocator, &poly) == RES_OK); + CHK(scpr_polygon_setup_indexed_vertices(poly, pctx.nverts, pget_pos, &pctx) + == RES_OK); + + mctx.coords = triangle_pos; + mctx.nverts = 3; + mctx.indices = triangle_ids; + mctx.ntris = 1; CHK(scpr_mesh_setup_indexed_vertices - (mesh, ctx.ntris, get_ids, 3, get_pos, &ctx) == RES_OK); + (mesh, mctx.ntris, mget_ids, 3, mget_pos, &mctx) == RES_OK); - poly.get_position = get_clip_pos; - poly.nvertices = sizeof(clip_pos)/(2*sizeof(double)); - poly.context = (void*)clip_pos; CHK(scpr_mesh_clip(NULL, SCPR_OPERATIONS_COUNT__, NULL) == RES_BAD_ARG); CHK(scpr_mesh_clip(mesh, SCPR_OPERATIONS_COUNT__, NULL) == RES_BAD_ARG); - CHK(scpr_mesh_clip(NULL, SCPR_OPERATIONS_COUNT__, &poly) == RES_BAD_ARG); - CHK(scpr_mesh_clip(mesh, SCPR_OPERATIONS_COUNT__, &poly) == RES_BAD_ARG); + CHK(scpr_mesh_clip(NULL, SCPR_OPERATIONS_COUNT__, poly) == RES_BAD_ARG); + CHK(scpr_mesh_clip(mesh, SCPR_OPERATIONS_COUNT__, poly) == RES_BAD_ARG); CHK(scpr_mesh_clip(NULL, SCPR_SUB, NULL) == RES_BAD_ARG); CHK(scpr_mesh_clip(mesh, SCPR_SUB, NULL) == RES_BAD_ARG); - CHK(scpr_mesh_clip(NULL, SCPR_SUB, &poly) == RES_BAD_ARG); - CHK(scpr_mesh_clip(mesh, SCPR_SUB, &poly) == RES_OK); + CHK(scpr_mesh_clip(NULL, SCPR_SUB, poly) == RES_BAD_ARG); + CHK(scpr_mesh_clip(mesh, SCPR_SUB, poly) == RES_OK); /*dump_obj(stdout, mesh);*/ CHK(scpr_mesh_get_triangles_count(mesh, &ntris) == RES_OK); CHK(ntris == 3); + + CHK(scpr_polygon_ref_put(poly) == RES_OK); } static void -test_quad(struct scpr_mesh* mesh) +test_quad + (struct mem_allocator* allocator, + struct scpr_mesh* mesh) { const double quad_pos[] = { 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 0.0 }; 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)/(2*sizeof(double)); - ctx.indices = quad_ids; - ctx.ntris = sizeof(quad_ids)/(3*sizeof(size_t)); + struct scpr_polygon* poly; + struct polygon_context pctx; + struct mesh_context mctx; + + pctx.coords = clip_pos; + pctx.nverts = sizeof(clip_pos)/(2*sizeof(double)); + CHK(scpr_polygon_create(allocator, &poly) == RES_OK); + CHK(scpr_polygon_setup_indexed_vertices(poly, pctx.nverts, pget_pos, &pctx) + == RES_OK); + + mctx.coords = quad_pos; + mctx.nverts = sizeof(quad_pos)/(2*sizeof(double)); + mctx.indices = quad_ids; + mctx.ntris = sizeof(quad_ids)/(3*sizeof(size_t)); CHK(scpr_mesh_setup_indexed_vertices - (mesh, ctx.ntris, get_ids, ctx.nverts, get_pos, &ctx) == RES_OK); + (mesh, mctx.ntris, mget_ids, mctx.nverts, mget_pos, &mctx) == RES_OK); - poly.get_position = get_clip_pos; - poly.nvertices = sizeof(clip_pos)/(2*sizeof(double)); - poly.context = (void*)clip_pos; - CHK(scpr_mesh_clip(mesh, SCPR_AND, &poly) == RES_OK); + CHK(scpr_mesh_clip(mesh, SCPR_AND, poly) == RES_OK); /*dump_obj(stdout, mesh);*/ + + CHK(scpr_polygon_ref_put(poly) == RES_OK); } static void -test_disk(struct scpr_mesh* mesh) +test_disk + (struct mem_allocator* allocator, + 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; + struct scpr_polygon* poly; + struct polygon_context pctx; + struct mesh_context mctx; double* pos = NULL; size_t* ids = NULL; size_t i, j; @@ -172,22 +181,26 @@ test_disk(struct scpr_mesh* mesh) sa_push(ids, id1); } - ctx.coords = pos; - ctx.nverts = sa_size(pos)/2; - ctx.indices = ids; - ctx.ntris = sa_size(ids)/3; + pctx.coords = clip; + pctx.nverts = sizeof(clip)/(2*sizeof(double)); + CHK(scpr_polygon_create(allocator, &poly) == RES_OK); + CHK(scpr_polygon_setup_indexed_vertices(poly, pctx.nverts, pget_pos, &pctx) + == RES_OK); + + mctx.coords = pos; + mctx.nverts = sa_size(pos)/2; + mctx.indices = ids; + mctx.ntris = sa_size(ids)/3; CHK(scpr_mesh_setup_indexed_vertices - (mesh, ctx.ntris, get_ids, ctx.nverts, get_pos, &ctx) == RES_OK); + (mesh, mctx.ntris, mget_ids, mctx.nverts, mget_pos, &mctx) == RES_OK); - poly.get_position = get_clip_pos; - poly.nvertices = sizeof(clip)/(2*sizeof(double)); - poly.context = (void*)clip; - CHK(scpr_mesh_clip(mesh, SCPR_SUB, &poly) == RES_OK); + CHK(scpr_mesh_clip(mesh, SCPR_SUB, poly) == RES_OK); dump_obj(stdout, mesh); sa_release(pos); sa_release(ids); + CHK(scpr_polygon_ref_put(poly) == RES_OK); } int @@ -201,9 +214,9 @@ main(int argc, char** argv) CHK(scpr_mesh_create(&allocator, &mesh) == RES_OK); - test_triangle(mesh); - test_quad(mesh); - test_disk(mesh); + test_triangle(&allocator, mesh); + test_quad(&allocator, mesh); + test_disk(&allocator, mesh); CHK(scpr_mesh_ref_put(mesh) == RES_OK); diff --git a/src/test_scpr_mesh.c b/src/test_scpr_mesh.c @@ -75,68 +75,68 @@ main(int argc, char** argv) CHK(SETUP(mesh, 0, NULL, 0, NULL, NULL) == RES_BAD_ARG); CHK(SETUP(NULL, ntris, NULL, 0, NULL, NULL) == RES_BAD_ARG); CHK(SETUP(mesh, ntris, NULL, 0, NULL, NULL) == RES_BAD_ARG); - CHK(SETUP(NULL, 0, get_ids, 0, NULL, NULL) == RES_BAD_ARG); - CHK(SETUP(mesh, 0, get_ids, 0, NULL, NULL) == RES_BAD_ARG); - CHK(SETUP(NULL, ntris, get_ids, 0, NULL, NULL) == RES_BAD_ARG); - CHK(SETUP(mesh, ntris, get_ids, 0, NULL, NULL) == RES_BAD_ARG); + CHK(SETUP(NULL, 0, mget_ids, 0, NULL, NULL) == RES_BAD_ARG); + CHK(SETUP(mesh, 0, mget_ids, 0, NULL, NULL) == RES_BAD_ARG); + CHK(SETUP(NULL, ntris, mget_ids, 0, NULL, NULL) == RES_BAD_ARG); + CHK(SETUP(mesh, ntris, mget_ids, 0, NULL, NULL) == RES_BAD_ARG); CHK(SETUP(NULL, 0, NULL, nverts, NULL, NULL) == RES_BAD_ARG); CHK(SETUP(mesh, 0, NULL, nverts, NULL, NULL) == RES_BAD_ARG); CHK(SETUP(NULL, ntris, NULL, nverts, NULL, NULL) == RES_BAD_ARG); CHK(SETUP(mesh, ntris, NULL, nverts, NULL, NULL) == RES_BAD_ARG); - CHK(SETUP(NULL, 0, get_ids, nverts, NULL, NULL) == RES_BAD_ARG); - CHK(SETUP(mesh, 0, get_ids, nverts, NULL, NULL) == RES_BAD_ARG); - CHK(SETUP(NULL, ntris, get_ids, nverts, NULL, NULL) == RES_BAD_ARG); - CHK(SETUP(mesh, ntris, get_ids, nverts, NULL, NULL) == RES_BAD_ARG); - CHK(SETUP(NULL, 0, NULL, 0, get_pos, NULL) == RES_BAD_ARG); - CHK(SETUP(mesh, 0, NULL, 0, get_pos, NULL) == RES_BAD_ARG); - CHK(SETUP(NULL, ntris, NULL, 0, get_pos, NULL) == RES_BAD_ARG); - CHK(SETUP(mesh, ntris, NULL, 0, get_pos, NULL) == RES_BAD_ARG); - CHK(SETUP(NULL, 0, get_ids, 0, get_pos, NULL) == RES_BAD_ARG); - CHK(SETUP(mesh, 0, get_ids, 0, get_pos, NULL) == RES_BAD_ARG); - CHK(SETUP(NULL, ntris, get_ids, 0, get_pos, NULL) == RES_BAD_ARG); - CHK(SETUP(mesh, ntris, get_ids, 0, get_pos, NULL) == RES_BAD_ARG); - CHK(SETUP(NULL, 0, NULL, nverts, get_pos, NULL) == RES_BAD_ARG); - CHK(SETUP(mesh, 0, NULL, nverts, get_pos, NULL) == RES_BAD_ARG); - CHK(SETUP(NULL, ntris, NULL, nverts, get_pos, NULL) == RES_BAD_ARG); - CHK(SETUP(mesh, ntris, NULL, nverts, get_pos, NULL) == RES_BAD_ARG); - CHK(SETUP(NULL, 0, get_ids, nverts, get_pos, NULL) == RES_BAD_ARG); - CHK(SETUP(mesh, 0, get_ids, nverts, get_pos, NULL) == RES_BAD_ARG); - CHK(SETUP(NULL, ntris, get_ids, nverts, get_pos, NULL) == RES_BAD_ARG); - CHK(SETUP(mesh, ntris, get_ids, nverts, get_pos, NULL) == RES_BAD_ARG); + CHK(SETUP(NULL, 0, mget_ids, nverts, NULL, NULL) == RES_BAD_ARG); + CHK(SETUP(mesh, 0, mget_ids, nverts, NULL, NULL) == RES_BAD_ARG); + CHK(SETUP(NULL, ntris, mget_ids, nverts, NULL, NULL) == RES_BAD_ARG); + CHK(SETUP(mesh, ntris, mget_ids, nverts, NULL, NULL) == RES_BAD_ARG); + CHK(SETUP(NULL, 0, NULL, 0, mget_pos, NULL) == RES_BAD_ARG); + CHK(SETUP(mesh, 0, NULL, 0, mget_pos, NULL) == RES_BAD_ARG); + CHK(SETUP(NULL, ntris, NULL, 0, mget_pos, NULL) == RES_BAD_ARG); + CHK(SETUP(mesh, ntris, NULL, 0, mget_pos, NULL) == RES_BAD_ARG); + CHK(SETUP(NULL, 0, mget_ids, 0, mget_pos, NULL) == RES_BAD_ARG); + CHK(SETUP(mesh, 0, mget_ids, 0, mget_pos, NULL) == RES_BAD_ARG); + CHK(SETUP(NULL, ntris, mget_ids, 0, mget_pos, NULL) == RES_BAD_ARG); + CHK(SETUP(mesh, ntris, mget_ids, 0, mget_pos, NULL) == RES_BAD_ARG); + CHK(SETUP(NULL, 0, NULL, nverts, mget_pos, NULL) == RES_BAD_ARG); + CHK(SETUP(mesh, 0, NULL, nverts, mget_pos, NULL) == RES_BAD_ARG); + CHK(SETUP(NULL, ntris, NULL, nverts, mget_pos, NULL) == RES_BAD_ARG); + CHK(SETUP(mesh, ntris, NULL, nverts, mget_pos, NULL) == RES_BAD_ARG); + CHK(SETUP(NULL, 0, mget_ids, nverts, mget_pos, NULL) == RES_BAD_ARG); + CHK(SETUP(mesh, 0, mget_ids, nverts, mget_pos, NULL) == RES_BAD_ARG); + CHK(SETUP(NULL, ntris, mget_ids, nverts, mget_pos, NULL) == RES_BAD_ARG); + CHK(SETUP(mesh, ntris, mget_ids, nverts, mget_pos, NULL) == RES_BAD_ARG); CHK(SETUP(NULL, 0, NULL, 0, NULL, &ctx) == RES_BAD_ARG); CHK(SETUP(mesh, 0, NULL, 0, NULL, &ctx) == RES_BAD_ARG); CHK(SETUP(NULL, ntris, NULL, 0, NULL, &ctx) == RES_BAD_ARG); CHK(SETUP(mesh, ntris, NULL, 0, NULL, &ctx) == RES_BAD_ARG); - CHK(SETUP(NULL, 0, get_ids, 0, NULL, &ctx) == RES_BAD_ARG); - CHK(SETUP(mesh, 0, get_ids, 0, NULL, &ctx) == RES_BAD_ARG); - CHK(SETUP(NULL, ntris, get_ids, 0, NULL, &ctx) == RES_BAD_ARG); - CHK(SETUP(mesh, ntris, get_ids, 0, NULL, &ctx) == RES_BAD_ARG); + CHK(SETUP(NULL, 0, mget_ids, 0, NULL, &ctx) == RES_BAD_ARG); + CHK(SETUP(mesh, 0, mget_ids, 0, NULL, &ctx) == RES_BAD_ARG); + CHK(SETUP(NULL, ntris, mget_ids, 0, NULL, &ctx) == RES_BAD_ARG); + CHK(SETUP(mesh, ntris, mget_ids, 0, NULL, &ctx) == RES_BAD_ARG); CHK(SETUP(NULL, 0, NULL, nverts, NULL, &ctx) == RES_BAD_ARG); CHK(SETUP(mesh, 0, NULL, nverts, NULL, &ctx) == RES_BAD_ARG); CHK(SETUP(NULL, ntris, NULL, nverts, NULL, &ctx) == RES_BAD_ARG); CHK(SETUP(mesh, ntris, NULL, nverts, NULL, &ctx) == RES_BAD_ARG); - CHK(SETUP(NULL, 0, get_ids, nverts, NULL, &ctx) == RES_BAD_ARG); - CHK(SETUP(mesh, 0, get_ids, nverts, NULL, &ctx) == RES_BAD_ARG); - CHK(SETUP(NULL, ntris, get_ids, nverts, NULL, &ctx) == RES_BAD_ARG); - CHK(SETUP(mesh, ntris, get_ids, nverts, NULL, &ctx) == RES_BAD_ARG); - CHK(SETUP(NULL, 0, NULL, 0, get_pos, &ctx) == RES_BAD_ARG); - CHK(SETUP(mesh, 0, NULL, 0, get_pos, &ctx) == RES_BAD_ARG); - CHK(SETUP(NULL, ntris, NULL, 0, get_pos, &ctx) == RES_BAD_ARG); - CHK(SETUP(mesh, ntris, NULL, 0, get_pos, &ctx) == RES_BAD_ARG); - CHK(SETUP(NULL, 0, get_ids, 0, get_pos, &ctx) == RES_BAD_ARG); - CHK(SETUP(mesh, 0, get_ids, 0, get_pos, &ctx) == RES_BAD_ARG); - CHK(SETUP(NULL, ntris, get_ids, 0, get_pos, &ctx) == RES_BAD_ARG); - CHK(SETUP(mesh, ntris, get_ids, 0, get_pos, &ctx) == RES_BAD_ARG); - CHK(SETUP(NULL, 0, NULL, nverts, get_pos, &ctx) == RES_BAD_ARG); - CHK(SETUP(mesh, 0, NULL, nverts, get_pos, &ctx) == RES_BAD_ARG); - CHK(SETUP(NULL, ntris, NULL, nverts, get_pos, &ctx) == RES_BAD_ARG); - CHK(SETUP(mesh, ntris, NULL, nverts, get_pos, &ctx) == RES_BAD_ARG); - CHK(SETUP(NULL, 0, get_ids, nverts, get_pos, &ctx) == RES_BAD_ARG); - CHK(SETUP(mesh, 0, get_ids, nverts, get_pos, &ctx) == RES_BAD_ARG); - CHK(SETUP(NULL, ntris, get_ids, nverts, get_pos, &ctx) == RES_BAD_ARG); - CHK(SETUP(mesh, ntris, get_ids, nverts, get_pos, &ctx) == RES_OK); + CHK(SETUP(NULL, 0, mget_ids, nverts, NULL, &ctx) == RES_BAD_ARG); + CHK(SETUP(mesh, 0, mget_ids, nverts, NULL, &ctx) == RES_BAD_ARG); + CHK(SETUP(NULL, ntris, mget_ids, nverts, NULL, &ctx) == RES_BAD_ARG); + CHK(SETUP(mesh, ntris, mget_ids, nverts, NULL, &ctx) == RES_BAD_ARG); + CHK(SETUP(NULL, 0, NULL, 0, mget_pos, &ctx) == RES_BAD_ARG); + CHK(SETUP(mesh, 0, NULL, 0, mget_pos, &ctx) == RES_BAD_ARG); + CHK(SETUP(NULL, ntris, NULL, 0, mget_pos, &ctx) == RES_BAD_ARG); + CHK(SETUP(mesh, ntris, NULL, 0, mget_pos, &ctx) == RES_BAD_ARG); + CHK(SETUP(NULL, 0, mget_ids, 0, mget_pos, &ctx) == RES_BAD_ARG); + CHK(SETUP(mesh, 0, mget_ids, 0, mget_pos, &ctx) == RES_BAD_ARG); + CHK(SETUP(NULL, ntris, mget_ids, 0, mget_pos, &ctx) == RES_BAD_ARG); + CHK(SETUP(mesh, ntris, mget_ids, 0, mget_pos, &ctx) == RES_BAD_ARG); + CHK(SETUP(NULL, 0, NULL, nverts, mget_pos, &ctx) == RES_BAD_ARG); + CHK(SETUP(mesh, 0, NULL, nverts, mget_pos, &ctx) == RES_BAD_ARG); + CHK(SETUP(NULL, ntris, NULL, nverts, mget_pos, &ctx) == RES_BAD_ARG); + CHK(SETUP(mesh, ntris, NULL, nverts, mget_pos, &ctx) == RES_BAD_ARG); + CHK(SETUP(NULL, 0, mget_ids, nverts, mget_pos, &ctx) == RES_BAD_ARG); + CHK(SETUP(mesh, 0, mget_ids, nverts, mget_pos, &ctx) == RES_BAD_ARG); + CHK(SETUP(NULL, ntris, mget_ids, nverts, mget_pos, &ctx) == RES_BAD_ARG); + CHK(SETUP(mesh, ntris, mget_ids, nverts, mget_pos, &ctx) == RES_OK); ctx.indices = indices_bad; - CHK(SETUP(mesh, 1, get_ids, nverts, get_pos, &ctx) == RES_BAD_ARG); + CHK(SETUP(mesh, 1, mget_ids, nverts, mget_pos, &ctx) == RES_BAD_ARG); #undef SETUP CHK(scpr_mesh_get_triangles_count(NULL, NULL) == RES_BAD_ARG); @@ -153,7 +153,7 @@ main(int argc, char** argv) ctx.indices = indices; CHK(scpr_mesh_setup_indexed_vertices - (mesh, ntris, get_ids, nverts, get_pos, &ctx) == RES_OK); + (mesh, ntris, mget_ids, nverts, mget_pos, &ctx) == RES_OK); CHK(scpr_mesh_get_triangles_count(mesh, &n) == RES_OK); CHK(n == ntris); CHK(scpr_mesh_get_vertices_count(mesh, &n) == RES_OK); diff --git a/src/test_scpr_polygon.c b/src/test_scpr_polygon.c @@ -0,0 +1,106 @@ +/* Copyright (C) 2016-2018, 2021 |Meso|Star> (contact@meso-star.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "scpr.h" +#include "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)/(2*sizeof(double)); + double pos[2]; + size_t i, n; + struct mem_allocator allocator; + struct polygon_context ctx; + struct scpr_polygon* polygon; + (void)argc, (void)argv; + + mem_init_proxy_allocator(&allocator, &mem_default_allocator); + + CHK(scpr_polygon_create(NULL, NULL) == RES_BAD_ARG); + CHK(scpr_polygon_create(&allocator, NULL) == RES_BAD_ARG); + CHK(scpr_polygon_create(NULL, &polygon) == RES_OK); + + CHK(scpr_polygon_ref_get(NULL) == RES_BAD_ARG); + CHK(scpr_polygon_ref_get(polygon) == RES_OK); + CHK(scpr_polygon_ref_put(NULL) == RES_BAD_ARG); + CHK(scpr_polygon_ref_put(polygon) == RES_OK); + CHK(scpr_polygon_ref_put(polygon) == RES_OK); + + CHK(scpr_polygon_create(&allocator, &polygon) == RES_OK); + + CHK(scpr_polygon_get_vertices_count(polygon, &n) == RES_OK); + CHK(n == 0); + + ctx.coords = coords; + ctx.nverts = nverts; + + #define SETUP scpr_polygon_setup_indexed_vertices + CHK(SETUP(NULL, 0, NULL, NULL) == RES_BAD_ARG); + CHK(SETUP(polygon, 0, NULL, NULL) == RES_BAD_ARG); + CHK(SETUP(NULL, nverts, NULL, NULL) == RES_BAD_ARG); + CHK(SETUP(polygon, nverts, NULL, NULL) == RES_BAD_ARG); + CHK(SETUP(NULL, 0, pget_pos, NULL) == RES_BAD_ARG); + CHK(SETUP(polygon, 0, pget_pos, NULL) == RES_BAD_ARG); + CHK(SETUP(NULL, nverts, pget_pos, NULL) == RES_BAD_ARG); + CHK(SETUP(polygon, nverts, pget_pos, NULL) == RES_BAD_ARG); + CHK(SETUP(NULL, 0, NULL, &ctx) == RES_BAD_ARG); + CHK(SETUP(polygon, 0, NULL, &ctx) == RES_BAD_ARG); + CHK(SETUP(NULL, nverts, NULL, &ctx) == RES_BAD_ARG); + CHK(SETUP(polygon, nverts, NULL, &ctx) == RES_BAD_ARG); + CHK(SETUP(NULL, 0, pget_pos, &ctx) == RES_BAD_ARG); + CHK(SETUP(polygon, 0, pget_pos, &ctx) == RES_BAD_ARG); + CHK(SETUP(NULL, nverts, pget_pos, &ctx) == RES_BAD_ARG); + CHK(SETUP(polygon, nverts, pget_pos, &ctx) == RES_OK); + #undef SETUP + + CHK(scpr_polygon_get_vertices_count(NULL, NULL) == RES_BAD_ARG); + CHK(scpr_polygon_get_vertices_count(polygon, NULL) == RES_BAD_ARG); + CHK(scpr_polygon_get_vertices_count(NULL, &n) == RES_BAD_ARG); + CHK(scpr_polygon_get_vertices_count(polygon, &n) == RES_OK); + CHK(n == nverts); + + CHK(scpr_polygon_get_position(NULL, nverts, NULL) == RES_BAD_ARG); + CHK(scpr_polygon_get_position(polygon, nverts, NULL) == RES_BAD_ARG); + CHK(scpr_polygon_get_position(NULL, 0, NULL) == RES_BAD_ARG); + CHK(scpr_polygon_get_position(polygon, 0, NULL) == RES_BAD_ARG); + CHK(scpr_polygon_get_position(NULL, nverts, pos) == RES_BAD_ARG); + CHK(scpr_polygon_get_position(polygon, nverts, pos) == RES_BAD_ARG); + CHK(scpr_polygon_get_position(NULL, 0, pos) == RES_BAD_ARG); + FOR_EACH(i, 0, nverts) { + CHK(scpr_polygon_get_position(polygon, i, pos) == RES_OK); + CHK(pos[0] == coords[i*2+0]); + CHK(pos[1] == coords[i*2+1]); + } + + CHK(scpr_polygon_ref_put(polygon) == RES_OK); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHK(mem_allocated_size() == 0); + return 0; +} + diff --git a/src/test_scpr_utils.h b/src/test_scpr_utils.h @@ -19,6 +19,23 @@ #include <rsys/mem_allocator.h> #include <stdio.h> +struct polygon_context { + const double* coords; + size_t nverts; +}; + +static INLINE void +pget_pos(const size_t ivert, double pos[2], void* context) +{ + const struct polygon_context* ctx = context; + CHK(pos != NULL); + CHK(context != NULL); + CHK(ctx->coords != NULL); + CHK(ivert < ctx->nverts); + pos[0] = ctx->coords[ivert*2 + 0]; + pos[1] = ctx->coords[ivert*2 + 1]; +} + struct mesh_context { const double* coords; size_t nverts; @@ -26,8 +43,8 @@ struct mesh_context { size_t ntris; }; -static void -get_pos(const size_t ivert, double pos[2], void* context) +static INLINE void +mget_pos(const size_t ivert, double pos[2], void* context) { const struct mesh_context* ctx = context; CHK(pos != NULL); @@ -38,8 +55,8 @@ get_pos(const size_t ivert, double pos[2], void* context) pos[1] = ctx->coords[ivert*2 + 1]; } -static void -get_ids(const size_t itri, size_t ids[3], void* context) +static INLINE void +mget_ids(const size_t itri, size_t ids[3], void* context) { const struct mesh_context* ctx = context; CHK(ids != NULL);