star-enclosures-3d

Extract enclosures from 3D geometry
git clone git://git.meso-star.fr/star-enclosures-3d.git
Log | Files | Refs | README | LICENSE

commit 0b57cc86614e82b4ae0bcfd01e99b792fe36e16c
parent 892da0223168ea7c6c9071cfda30616782d1b80b
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Mon, 14 Oct 2019 15:02:23 +0200

Add callbacks to manage app data for triangles.

Diffstat:
Mcmake/CMakeLists.txt | 1+
Msrc/senc.h | 49++++++++++++++++++++++++++++++++++++++++++++++---
Msrc/senc_scene.c | 26++++++++++++++++++--------
Msrc/test_senc_undefined_medium.c | 2+-
Asrc/test_senc_undefined_medium_attr.c | 341+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_senc_utils.h | 1+
6 files changed, 408 insertions(+), 12 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -132,6 +132,7 @@ if(NOT NO_TEST) new_test(test_senc_sample_enclosure) new_test(test_senc_scene) new_test(test_senc_undefined_medium) + new_test(test_senc_undefined_medium_attr) target_link_libraries(test_senc_sample_enclosure StarSP) target_link_libraries(test_senc_many_enclosures Star3DUT) diff --git a/src/senc.h b/src/senc.h @@ -172,19 +172,62 @@ senc_scene_reserve * It can be used to define the 2 sides of a triangle at different times. * When deduplicating triangles, the first occurence is kept (with its original * global_id). Users can provide their own global ids for triangles; these ids - * are not used by the library but are returned as-is by some API calls. */ + * are not used by the library but are returned as-is by some API calls. + * The callbacks devoted to attributes management allow the client app to store + * its own data. Add and merge events can fail and stop the add_geometry. */ SENC_API res_T +senc_scene_add_geometry_attr + (struct senc_scene* scene, + /* Number of added triangles */ + const unsigned triangles_count, + /* User function that provides vertices ids for added triangles */ + void(*indices)(const unsigned itri, unsigned ids[3], void* context), + /* User function that provides media ids for added triangles */ + void(*media) /* Can be NULL <=> SENC_UNDEFINED_MEDIUM medium used */ + (const unsigned itri, unsigned med[2], void* context), + void(*global_id) /* Can be NULL <=> use triangle rank */ + (const unsigned itri, unsigned* gid, void* context), + /* Number of added vertices */ + const unsigned vertices_count, + /* User function that provides coordinates for added vertices */ + void(*position)(const unsigned ivert, double pos[3], void* context), + /* Called for each new triangle so that the client app can manage its own + * triangle data/properties/attributes. + * If return is not RES_OK, add_geometry stops and fails. */ + res_T(*add_triangle) /* Can be NULL */ + (const unsigned global_id, const unsigned itri, void* context), + /* Called if the IVERTth triangle of the current add_geometry is equal to + * the GLOBAL_IDth global triangle so that the client app can try to merge + * its own triangle data. The reversed_triangle arg indicates if the triangle + * vertices' order is the same it was when the triangle was first added. + * If return is not RES_OK, add_geometry stops and fails. */ + res_T(*merge_triangle) /* Can be NULL */ + (const unsigned global_id, const unsigned itri, const int reversed_triangle, + void* context), + void* context); + +SENC_API INLINE res_T senc_scene_add_geometry (struct senc_scene* scene, + /* Number of added triangles */ const unsigned triangles_count, + /* User function that provides vertices ids for added triangles */ void(*indices)(const unsigned itri, unsigned ids[3], void* context), + /* User function that provides media ids for added triangles */ void(*media) /* Can be NULL <=> SENC_UNDEFINED_MEDIUM medium used */ (const unsigned itri, unsigned med[2], void* context), + /* User function that provides a global id for added triangles. */ void(*global_id) /* Can be NULL <=> use triangle rank */ (const unsigned itri, unsigned* gid, void* context), + /* Number of added vertices */ const unsigned vertices_count, + /* User function that provides coordinates for added vertices */ void(*position)(const unsigned ivert, double pos[3], void* context), - void* context); + void* context) +{ + return senc_scene_add_geometry_attr(scene, triangles_count, indices, + media, global_id, vertices_count, position, NULL, NULL, context); +} /* Returns a descriptor of the scene that holds the analysis' result. */ SENC_API res_T @@ -196,7 +239,7 @@ senc_scene_analyze SENC_API res_T senc_scene_get_convention (const struct senc_scene* scene, - int* convention); + int* convention); /* Returns the number of triangles in the scene. */ SENC_API res_T diff --git a/src/senc_scene.c b/src/senc_scene.c @@ -127,14 +127,16 @@ error: } res_T -senc_scene_add_geometry +senc_scene_add_geometry_attr (struct senc_scene* scn, const unsigned ntris, - void(*indices)(const unsigned itri, unsigned ids[3], void* ctx), - void(*media)(const unsigned itri, unsigned med[2], void* ctx), - void(*global_id)(const unsigned itri, unsigned* gid, void* context), + void(*indices)(const unsigned, unsigned*, void*), + void(*media)(const unsigned, unsigned*, void*), + void(*global_id)(const unsigned, unsigned*, void*), const unsigned nverts, - void(*position)(const unsigned ivert, double pos[3], void* ctx), + void(*position)(const unsigned, double*, void* ctx), + res_T(*add_triangle)(const unsigned, const unsigned, void*), + res_T(*merge_triangle)(const unsigned, const unsigned, const int, void*), void* ctx) { struct darray_vrtx_id unique_vertice_ids; @@ -275,9 +277,6 @@ senc_scene_add_geometry goto error; } else { /* Legit duplicate */ - log_warn(scn->dev, "%s: triangle %lu is a duplicate of triangle %lu.\n", - FUNC_NAME, (unsigned long)tmp.global_id, - (unsigned long)trg[*p_trg].global_id); range_adjust_ptr = darray_triangle_in_data_get(&scn->triangles_in) + *p_trg; /* Replace possible undefined media */ FOR_EACH(j, 0, 2) { @@ -287,6 +286,14 @@ senc_scene_add_geometry scn->sides_with_defined_medium_count++; } } + if(merge_triangle) { + OK(merge_triangle(trg[*p_trg].global_id, i, same, ctx)); + } else { + log_warn(scn->dev, + "%s: triangle %lu is a duplicate of triangle %lu.\n", + FUNC_NAME, (unsigned long)tmp.global_id, + (unsigned long)trg[*p_trg].global_id); + } } } else { /* New triangle */ @@ -299,6 +306,9 @@ senc_scene_add_geometry if(tmp.medium[j] != SENC_UNDEFINED_MEDIUM) scn->sides_with_defined_medium_count++; } + if(add_triangle) { + OK(add_triangle(tmp.global_id, i, ctx)); + } ++actual_nutris; } if(range_adjust_ptr) { diff --git a/src/test_senc_undefined_medium.c b/src/test_senc_undefined_medium.c @@ -42,7 +42,7 @@ test(int convention) conv_front = (convention & SENC_CONVENTION_NORMAL_FRONT) != 0; conv_in = (convention & SENC_CONVENTION_NORMAL_INSIDE) != 0; - /* Create the box with reversed triangles */ + /* Create a box with reversed triangles */ FOR_EACH(i, 0, sizeof(rev_box_indices) / sizeof(*rev_box_indices)) { switch (i % 3) { case 0: rev_box_indices[i] = box_indices[i]; break; diff --git a/src/test_senc_undefined_medium_attr.c b/src/test_senc_undefined_medium_attr.c @@ -0,0 +1,341 @@ +/* Copyright (C) |Meso|Star> 2016-2018 (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 "senc.h" +#include "senc_s3d_wrapper.h" +#include "test_senc_utils.h" + +#include <rsys/dynamic_array_uint.h> +#include <rsys/double3.h> + +#include <star/s3d.h> + +/* Manage interface properties */ +struct merge_ctx { + struct darray_uint global_interf_data; + const unsigned* current_add_interf_data; +}; + +res_T add_trg + (const unsigned global_id, + const unsigned itri, + void* context) +{ + res_T res = RES_OK; + struct context* ctx = context; + struct merge_ctx* merge_ctx; + unsigned interf; + ASSERT(ctx); + merge_ctx = ctx->custom; + /* Get interface information from ctx */ + interf = merge_ctx->current_add_interf_data[itri]; + /* Keep data */ + res = darray_uint_resize(&merge_ctx->global_interf_data, global_id + 1); + if(res != RES_OK) return res; + darray_uint_data_get(&merge_ctx->global_interf_data)[global_id] = interf; + return res; +} + +res_T merge_trg + (const unsigned global_id, + const unsigned itri, + const int reversed_triangle, + void* context) +{ + struct context* ctx = context; + struct merge_ctx* merge_ctx; + unsigned interf; + ASSERT(ctx); (void)reversed_triangle; + merge_ctx = ctx->custom; + /* Get interface information from ctx */ + interf = merge_ctx->current_add_interf_data[itri]; + if(darray_uint_data_get(&merge_ctx->global_interf_data)[global_id] != interf) + /* Previous interface id is different: no possible merge */ + return RES_BAD_ARG; + /* Same data: no merge needed */ + return RES_OK; +} + +static void +test(int convention) +{ + struct mem_allocator allocator; + struct senc_descriptor* desc = NULL; + struct senc_device* dev = NULL; + struct senc_scene* scn = NULL; + struct senc_enclosure* enclosure; + struct senc_enclosure_header header; + unsigned medium, expected_external_medium, expected_internal_medium; + unsigned gid; + struct context ctx; + unsigned i, t, ecount, vcount, tcount, scount; + int is_front, is_in; + unsigned media[12], interface_ids[12] = { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5 }; + unsigned rev_box_indices[sizeof(box_indices) / sizeof(*box_indices)]; + int conv_front, conv_in; + struct merge_ctx merge_ctx; + + darray_uint_init(&allocator, &merge_ctx.global_interf_data); + merge_ctx.current_add_interf_data = interface_ids; + + conv_front = (convention & SENC_CONVENTION_NORMAL_FRONT) != 0; + conv_in = (convention & SENC_CONVENTION_NORMAL_INSIDE) != 0; + + /* Create a box with reversed triangles */ + FOR_EACH(i, 0, sizeof(rev_box_indices) / sizeof(*rev_box_indices)) { + switch (i % 3) { + case 0: rev_box_indices[i] = box_indices[i]; break; + case 1: rev_box_indices[i] = box_indices[i + 1]; break; + case 2: rev_box_indices[i] = box_indices[i - 1]; break; + } + } + OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); + OK(senc_device_create(NULL, &allocator, SENC_NTHREADS_DEFAULT, 1, &dev)); + + OK(senc_scene_create(dev, convention, &scn)); + is_front = (convention & SENC_CONVENTION_NORMAL_FRONT) != 0; + is_in = (convention & SENC_CONVENTION_NORMAL_INSIDE) != 0; + + /* A 3D cube. + * 2 enclosures (inside, outside) sharing the same triangles, + * but opposite sides */ + ctx.positions = box_vertices; + ctx.indices = box_indices; + ctx.scale = 1; + ctx.reverse_vrtx = 0; + ctx.reverse_med = 0; + d3(ctx.offset, 0, 0, 0); + ctx.front_media = media; + ctx.back_media = medium1; + ctx.custom = &merge_ctx; + + /* Can add the same triangles again defined/undefined media in any order */ + + /* Add geometry with no media information on both sides */ + for (i = 0; i < sizeof(media) / sizeof(*media); i++) + media[i] = SENC_UNDEFINED_MEDIUM; + OK(senc_scene_add_geometry_attr(scn, ntriangles, get_indices, NULL, + NULL, nvertices, get_position, add_trg, merge_trg, &ctx)); + OK(senc_scene_get_unique_sides_without_medium_count(scn, &scount)); + CHK(scount == 2 * ntriangles); + + /* If merge fails, add geometry fails */ + interface_ids[0] = 6; + BA(senc_scene_add_geometry_attr(scn, ntriangles, get_indices, NULL, + NULL, nvertices, get_position, add_trg, merge_trg, &ctx)); + interface_ids[0] = 0; + + /* Add geometry with no media information on the front sides */ + OK(senc_scene_add_geometry_attr(scn, ntriangles, get_indices, get_media, + NULL, nvertices, get_position, add_trg, merge_trg, &ctx)); + OK(senc_scene_get_unique_sides_without_medium_count(scn, &scount)); + CHK(scount == ntriangles); + + /* Analyze with undefined media on the front sides */ + OK(senc_scene_analyze(scn, &desc)); + OK(senc_descriptor_get_enclosure_count(desc, &ecount)); + CHK(ecount == 2); + + FOR_EACH(i, 0, ecount) { + struct senc_enclosure* ee; + struct senc_enclosure_header hh; + unsigned cc; + OK(senc_descriptor_get_enclosure(desc, i, &enclosure)); + OK(senc_enclosure_get_header(enclosure, &header)); + + CHK(header.enclosure_id == i); + CHK(header.enclosed_media_count == 1); + + OK(senc_enclosure_get_medium(enclosure, 0, &medium)); + /* Geometrical normals point outside the cube in input triangles: + * if convention is front, front medium (undef) is outside, + * that is medium 0's enclosure is infinite */ + expected_external_medium = conv_front ? SENC_UNDEFINED_MEDIUM : 1; + expected_internal_medium = conv_front ? 1 :SENC_UNDEFINED_MEDIUM; + + CHK(medium == (header.is_infinite + ? expected_external_medium : expected_internal_medium)); + CHK(header.triangle_count == ntriangles); + CHK(header.unique_triangle_count == ntriangles); + CHK(header.vertices_count == nvertices); + CHK(header.is_infinite == (i == 0)); + + OK(senc_descriptor_get_enclosure_count_by_medium(desc, medium, &cc)); + CHK(cc == 1); + OK(senc_descriptor_get_enclosure_by_medium(desc, medium, 0, &ee)); + OK(senc_enclosure_get_header(ee, &hh)); + CHK(header.enclosure_id == hh.enclosure_id); + OK(senc_enclosure_ref_put(ee)); + + FOR_EACH(t, 0, header.triangle_count) { + unsigned ind[3]; + OK(senc_enclosure_get_triangle_global_id(enclosure, t, &gid)); + CHK(gid == t); + OK(senc_enclosure_get_triangle(enclosure, t, ind)); + } + OK(senc_enclosure_ref_put(enclosure)); + } + OK(senc_descriptor_ref_put(desc)); + + /* Same geometry, front media are defined for odd triangles */ + for (i = 0; i < sizeof(media) / sizeof(*media); i++) + media[i] = (i % 2) ? 0 : SENC_UNDEFINED_MEDIUM; + OK(senc_scene_add_geometry_attr(scn, ntriangles, get_indices, get_media, + NULL, nvertices, get_position, add_trg, merge_trg, &ctx)); + OK(senc_scene_get_unique_sides_without_medium_count(scn, &scount)); + CHK(scount == ntriangles / 2); + + /* Analyze with undefined media */ + OK(senc_scene_analyze(scn, &desc)); + OK(senc_descriptor_ref_put(desc)); + + /* Get the deduplicated geometry without (successful) analyze */ + OK(senc_scene_get_unique_vertices_count(scn, &vcount)); + CHK(vcount == nvertices); + OK(senc_scene_get_unique_triangles_count(scn, &tcount)); + CHK(tcount == ntriangles); + FOR_EACH(i, 0, tcount) { + int j; + unsigned med[2], ids[3]; + OK(senc_scene_get_unique_triangle(scn, i, ids)); + OK(senc_scene_get_unique_triangle_media(scn, i, med)); + CHK(med[0] == ((i % 2) ? 0 : SENC_UNDEFINED_MEDIUM) && med[1] == 1); + FOR_EACH(j, 0, 3) { + double pos[3]; + CHK(ids[j] < vcount); + OK(senc_scene_get_unique_vertex(scn, ids[j], pos)); + } + } + + /* Same information again, using a reversed box */ + ctx.indices = rev_box_indices; + SWAP(const unsigned*, ctx.front_media, ctx.back_media); + OK(senc_scene_add_geometry_attr(scn, ntriangles, get_indices, get_media, + NULL, nvertices, get_position, add_trg, merge_trg, &ctx)); + OK(senc_scene_get_unique_sides_without_medium_count(scn, &scount)); + CHK(scount == ntriangles / 2); + + /* Analyze with undefined media */ + OK(senc_scene_analyze(scn, &desc)); + OK(senc_descriptor_ref_put(desc)); + + /* Define media for remaining triangles, using reversed box */ + for (i = 0; i < sizeof(media) / sizeof(*media); i++) + media[i] = (i % 2) ? SENC_UNDEFINED_MEDIUM : 0; + OK(senc_scene_add_geometry_attr(scn, ntriangles, get_indices, get_media, + NULL, nvertices, get_position, add_trg, merge_trg, &ctx)); + OK(senc_scene_get_unique_sides_without_medium_count(scn, &scount)); + CHK(scount == 0); + + /* Get the deduplicated geometry without (successful) analyze */ + OK(senc_scene_get_unique_vertices_count(scn, &vcount)); + CHK(vcount == nvertices); + OK(senc_scene_get_unique_triangles_count(scn, &tcount)); + CHK(tcount == ntriangles); + FOR_EACH(i, 0, tcount) { + int j; + unsigned med[2], ids[3]; + OK(senc_scene_get_unique_triangle(scn, i, ids)); + OK(senc_scene_get_unique_triangle_media(scn, i, med)); + CHK(med[0] == 0 && med[1] == 1); + FOR_EACH(j, 0, 3) { + double pos[3]; + CHK(ids[j] < vcount); + OK(senc_scene_get_unique_vertex(scn, ids[j], pos)); + } + } + + /* Analyze with all media defined */ + OK(senc_scene_analyze(scn, &desc)); + OK(senc_descriptor_ref_put(desc)); + + /* Define media for all triangles, nothing new here */ + for (i = 0; i < sizeof(media) / sizeof(*media); i++) + media[i] = 0; + OK(senc_scene_add_geometry_attr(scn, ntriangles, get_indices, get_media, + NULL, nvertices, get_position, add_trg, merge_trg, &ctx)); + OK(senc_scene_get_unique_sides_without_medium_count(scn, &scount)); + CHK(scount == 0); + + /* Define incoherent media for some triangles */ + for (i = 0; i < sizeof(media) / sizeof(*media); i++) + media[i] = (i % 2); + BA(senc_scene_add_geometry_attr(scn, ntriangles, get_indices, get_media, + NULL, nvertices, get_position, add_trg, merge_trg, &ctx)); + OK(senc_scene_get_unique_sides_without_medium_count(scn, &scount)); + CHK(scount == 0); + + /* Scene is still OK and can be analyzed */ + OK(senc_scene_analyze(scn, &desc)); + + OK(senc_descriptor_get_global_triangles_count(desc, &tcount)); + CHK(tcount == sizeof(media) / sizeof(*media)); + + OK(senc_descriptor_get_enclosure_count(desc, &ecount)); + CHK(ecount == 2); + + FOR_EACH(i, 0, ecount) { + struct senc_enclosure* ee; + struct senc_enclosure_header hh; + unsigned cc; + OK(senc_descriptor_get_enclosure(desc, i, &enclosure)); + OK(senc_enclosure_get_header(enclosure, &header)); + + CHK(header.enclosure_id == i); + CHK(header.enclosed_media_count == 1); + OK(senc_enclosure_get_medium(enclosure, 0, &medium)); + /* Geometrical normals point outside the cube in input triangles: + * if convention is front, front medium (0) is outside, + * that is medium 0's enclosure is infinite */ + CHK(is_front == ((medium == 0) == header.is_infinite)); + CHK(header.triangle_count == ntriangles); + CHK(header.unique_triangle_count == ntriangles); + CHK(header.vertices_count == nvertices); + CHK(header.is_infinite == (i == 0)); + + OK(senc_descriptor_get_enclosure_count_by_medium(desc, medium, &cc)); + CHK(cc == 1); + OK(senc_descriptor_get_enclosure_by_medium(desc, medium, 0, &ee)); + OK(senc_enclosure_get_header(ee, &hh)); + CHK(header.enclosure_id == hh.enclosure_id); + OK(senc_enclosure_ref_put(ee)); + + FOR_EACH(t, 0, header.triangle_count) { + OK(senc_enclosure_get_triangle_global_id(enclosure, t, &gid)); + CHK(gid == t); + } + OK(senc_enclosure_ref_put(enclosure)); + } + + SENC(scene_ref_put(scn)); + SENC(device_ref_put(dev)); + SENC(descriptor_ref_put(desc)); + darray_uint_release(&merge_ctx.global_interf_data); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHK(mem_allocated_size() == 0); +} + +int +main(int argc, char** argv) +{ + (void) argc, (void) argv; + test(SENC_CONVENTION_NORMAL_FRONT | SENC_CONVENTION_NORMAL_INSIDE); + test(SENC_CONVENTION_NORMAL_BACK | SENC_CONVENTION_NORMAL_INSIDE); + test(SENC_CONVENTION_NORMAL_FRONT | SENC_CONVENTION_NORMAL_OUTSIDE); + test(SENC_CONVENTION_NORMAL_BACK | SENC_CONVENTION_NORMAL_OUTSIDE); + return 0; +} diff --git a/src/test_senc_utils.h b/src/test_senc_utils.h @@ -82,6 +82,7 @@ struct context { const unsigned* front_media; const unsigned* back_media; const unsigned* global_ids; + void* custom; double offset[3]; double scale; char reverse_vrtx, reverse_med;