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 f714362a43df38fc2418344e8153905dcbd9f44c
parent 3546aba57689b7d9fdc4f2083c8b52e50edcbe89
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Fri, 22 Nov 2019 17:26:54 +0100

Add involved medias as args to the merge callback.

Merge can now decide to continue add_geometry even if media are
incompatible. In this case the client app is supposed to record the
error and stop after the add_geometry. A common use of this could
be to record all the incompatibilities and stop afterwards.

Diffstat:
Mcmake/CMakeLists.txt | 1+
Msrc/senc.h | 40++++++++++++++++++++++++++++------------
Msrc/senc_scene.c | 73++++++++++++++++++++++++++-----------------------------------------------
Msrc/senc_scene_analyze.c | 2+-
Msrc/senc_scene_c.h | 1-
Asrc/test_senc_add_n_merge.c | 205+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_senc_undefined_medium_attr.c | 10++++++++++
7 files changed, 271 insertions(+), 61 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -120,6 +120,7 @@ if(NOT NO_TEST) register_test(${_name} ${_name}) endfunction() + new_test(test_senc_add_n_merge) new_test(test_senc_cube_behind_cube) new_test(test_senc_cube_in_cube) new_test(test_senc_cube_on_cube) diff --git a/src/senc.h b/src/senc.h @@ -172,17 +172,23 @@ senc_scene_reserve const unsigned media_count); /* Add a new set of vertices and triangles to the scene. - * Vertices can be duplicates and are deduplicated on the fly. - * Triangles can be duplicates as long as they constantly define the same - * medium on both sides (or an error will be reported, with the exception - * of SENC_UNDEFINED_MEDIUM that causes no error) and are deduplicated. + * Vertices can be duplicates and are silently deduplicated on the fly. + * Triangles can be duplicates as long as media are compatible on both sides. + * Valid triangle duplicates are silently deduplicated, invalid duplicates + * trigger an error (add_geometry returns RES_BAD_ARG). * The special value SENC_UNDEFINED_MEDIUM denotes an undefined medium. * It can be used to define the 2 sides of a triangle at different times. + * Media on duplicate triangles are consider compatible if: + * - the merge_triangle callback is provided and returns RES_OK, + * - or media are identical or SENC_UNDEFINED_MEDIUM. * When deduplicating triangles, the first occurence remains (with its - * original index in user world, regardless of deduplication). - * The add_triangle and merge_triangle callbacks can be used for attributes - * management including triangle IDs; they allow the client app to store - * its own data. They can also fail and stop the add_geometry call. */ + * original index in user world, regardless of deduplication); the only + * situation where deduplication changes a previously recorded media is from + * SENC_UNDEFINED_MEDIUM to any defined medium. + * The add_triangle and merge_triangle callbacks can be used for attribute + * management (including triangle IDs) and to record media incompatibilities + * for a subsequent report; they allow the client app to store its own data. + * By returning an error, they can also stop the add_geometry call. */ SENC_API res_T senc_scene_add_geometry (struct senc_scene* scene, @@ -199,16 +205,26 @@ senc_scene_add_geometry 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. */ + * If return is not RES_OK, add_geometry stops immediately and returns + * whatever value add_triangle returned. */ 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_id_th 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. */ + * its own triangle data or record a possible media conflict. + * The reversed_triangle arg indicates if the triangle vertices' order is + * the same it was when the triangle was first added. + * triangle_media and merge_media contain the involved media. + * If those media are incompatible and merge_triangle returns RES_OK the + * process will continue with the next triangle and possibly end in success. + * If return is not RES_OK, add_geometry stops immediately and returns + * whatever value merge_triangle returned. + * If merge_triangle is NULL, a strict media compatibility is required for + * add_geometry to success: add_geometry would stop and return RES_BAD_ARG + * on the first occurence of duplicate triangle with incompatible media. */ res_T(*merge_triangle) /* Can be NULL */ (const unsigned global_id, const unsigned itri, const int reversed_triangle, + const unsigned triangle_media[2], const unsigned merge_media[2], void* context), void* context); diff --git a/src/senc_scene.c b/src/senc_scene.c @@ -80,7 +80,6 @@ senc_scene_create SENC(device_ref_get(dev)); scn->dev = dev; scn->convention = conv; - scn->ngeoms = 0; scn->ntris = 0; scn->nutris = 0; scn->next_medium_idx = 0; @@ -135,7 +134,8 @@ senc_scene_add_geometry const unsigned nverts, 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*), + res_T(*merge_triangle)(const unsigned, const unsigned, const int, + const unsigned*, const unsigned*, void*), void* ctx) { struct darray_vrtx_id unique_vertice_ids; @@ -173,10 +173,6 @@ senc_scene_add_geometry p_vrtx = htable_vrtx_find(&scn->unique_vertices, &tmp); if(p_vrtx) { /* Duplicate vertex */ - log_warn(scn->dev, "%s: vertex %lu is a duplicate of unique vertex %lu.\n", - FUNC_NAME, (unsigned long)(scn->nverts + i), (unsigned long)*p_vrtx); - log_warn(scn->dev, "%s: vertex %lu: (%g %g %g).\n", - FUNC_NAME, (unsigned long)(scn->nverts + i), SPLIT3(tmp.vec)); unique_v = *p_vrtx; } else { /* New vertex */ @@ -240,55 +236,39 @@ senc_scene_add_geometry reversed = trg_make_key(&trg_key, tmp.vertice_id); p_trg = htable_trg_find(&scn->unique_triangles, &trg_key); if(p_trg) { + /* Duplicate triangle. Need to check duplicate validity */ union vrtx_id3 utrg_key; char ureversed = trg_make_key(&utrg_key, trg[*p_trg].vertice_id); int same = (reversed == ureversed); const medium_id_t* umed; - /* Duplicate triangle. Need to check duplicate validity */ ASSERT(trg_key_eq(&trg_key, &utrg_key)); if(!same) SWAP(unsigned, tmp.medium[0], tmp.medium[1]); umed = trg[*p_trg].medium; - if(!compatible_medium(umed[0], tmp.medium[0]) - || !compatible_medium(umed[1], tmp.medium[1])) - { - /* Same triangles with different media: invalid! */ - const union double3* positions - = darray_position_cdata_get(&scn->vertices); - log_err(scn->dev, "%s: triangle %lu is a duplicate" - " of triangle %lu with incoherent media.\n", - FUNC_NAME, (unsigned long)tmp.global_id, - (unsigned long)trg[*p_trg].global_id); - log_err(scn->dev, - "Triangle %lu:\n (%g %g %g)\n (%g %g %g)\n (%g %g %g)\n", - (unsigned long)trg[*p_trg].global_id, - SPLIT3(positions[trg[*p_trg].vertice_id[0]].vec), - SPLIT3(positions[trg[*p_trg].vertice_id[1]].vec), - SPLIT3(positions[trg[*p_trg].vertice_id[2]].vec)); - log_err(scn->dev, "Media: (%lu, %lu) VS (%lu, %lu)\n", - (unsigned long)umed[ureversed? 1 : 0], - (unsigned long)umed[ureversed ? 0 : 1], - (unsigned long)tmp.medium[reversed ? 1 : 0], - (unsigned long)tmp.medium[reversed ? 0 : 1]); - res = RES_BAD_ARG; - goto error; - } else { - /* Legit duplicate */ - range_adjust_ptr = darray_triangle_in_data_get(&scn->triangles_in) + *p_trg; - /* Replace possible undefined media */ + if(merge_triangle) { + /* Let the client app rule. */ + unsigned smed[2], mmed[2]; FOR_EACH(j, 0, 2) { - if(range_adjust_ptr->medium[j] == SENC_UNDEFINED_MEDIUM - && tmp.medium[j] != SENC_UNDEFINED_MEDIUM) { - range_adjust_ptr->medium[j] = tmp.medium[j]; - scn->sides_with_defined_medium_count++; - } + smed[j] = (unsigned)umed[j]; + mmed[j] = (unsigned)tmp.medium[j]; } - 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); + OK(merge_triangle(trg[*p_trg].global_id, i, same, smed, mmed, ctx)); + /* If merge_triangle returns OK its OK even if media are incompatible. */ + } else { + if(!compatible_medium(umed[0], tmp.medium[0]) + || !compatible_medium(umed[1], tmp.medium[1])) + { + res = RES_BAD_ARG; + goto error; + } + } + /* Legit duplicate (or accepted by merge_triangle): replace undef media. */ + range_adjust_ptr = darray_triangle_in_data_get(&scn->triangles_in) + *p_trg; + /* Replace possible undefined media */ + FOR_EACH(j, 0, 2) { + if(range_adjust_ptr->medium[j] == SENC_UNDEFINED_MEDIUM + && tmp.medium[j] != SENC_UNDEFINED_MEDIUM) { + range_adjust_ptr->medium[j] = tmp.medium[j]; + scn->sides_with_defined_medium_count++; } } } else { @@ -335,7 +315,6 @@ exit: scn->ntris += actual_ntris; ASSERT(scn->nuverts == htable_vrtx_size_get(&scn->unique_vertices)); ASSERT(scn->nutris == htable_trg_size_get(&scn->unique_triangles)); - ++scn->ngeoms; return res; error: goto exit; diff --git a/src/senc_scene_analyze.c b/src/senc_scene_analyze.c @@ -177,7 +177,7 @@ extract_connex_components /* If there are sides with undefined medium, its like another medium */ undefs = (scn->sides_with_defined_medium_count < 2 * scn->nutris) ? 1 : 0; -#pragma omp single + #pragma omp single { if(undefs) { /* Range is unknown, process each side */ diff --git a/src/senc_scene_c.h b/src/senc_scene_c.h @@ -214,7 +214,6 @@ struct senc_scene { struct htable_trg unique_triangles; /* Keep sizes */ - unsigned ngeoms; /* Not used yet (just counted). */ trg_id_t ntris, nutris; /* Trg count, unique trg count */ vrtx_id_t nverts, nuverts; /* Vrtx count, unique vrtx count */ medium_id_t next_medium_idx; diff --git a/src/test_senc_add_n_merge.c b/src/test_senc_add_n_merge.c @@ -0,0 +1,205 @@ +/* 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 "test_senc_utils.h" + +/* Manage add_geometry behaviour */ +struct add_geom_ctx { + unsigned add_cpt, merge_cpt; + res_T add_res, merge_res; +}; + +static res_T +add_trg + (const unsigned global_id, + const unsigned iseg, + void* context) +{ + struct context* ctx = context; + struct add_geom_ctx* add_geom_ctx; + ASSERT(ctx); (void)global_id; (void)iseg; + add_geom_ctx = ctx->custom; + if(add_geom_ctx->add_res == RES_OK) ++add_geom_ctx->add_cpt; + return add_geom_ctx->add_res; +} + +static res_T +merge_trg + (const unsigned global_id, + const unsigned iseg, + const int reversed_segment, + const unsigned triangle_media[2], + const unsigned merge_media[2], + void* context) +{ + struct context* ctx = context; + struct add_geom_ctx* add_geom_ctx; + ASSERT(ctx && triangle_media && merge_media); + (void)global_id; (void)iseg; (void)reversed_segment; (void)triangle_media; (void)merge_media; + add_geom_ctx = ctx->custom; + if(add_geom_ctx->merge_res == RES_OK) ++add_geom_ctx->merge_cpt; + return add_geom_ctx->merge_res; +} + +int +main(int argc, char** argv) +{ + struct mem_allocator allocator; + struct senc_device* dev = NULL; + struct senc_scene* scn = NULL; + struct context ctx; + unsigned i; + struct add_geom_ctx add_geom_ctx; + unsigned media[12]; + const int conv = SENC_CONVENTION_NORMAL_FRONT | SENC_CONVENTION_NORMAL_INSIDE; + const unsigned media_count = sizeof(media) / sizeof(*media); + (void)argc; (void)argv; + + 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, conv, &scn)); + + /* 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 = &add_geom_ctx; + + add_geom_ctx.add_cpt = add_geom_ctx.merge_cpt = 0; + add_geom_ctx.add_res = add_geom_ctx.merge_res = RES_OK; + + /* Geometry with no media information on both sides */ + for (i = 0; i < media_count; i++) media[i] = SENC_UNDEFINED_MEDIUM; + ctx.front_media = media; + ctx.back_media = media; + + /* If add fails, add geometry fails the same way */ + add_geom_ctx.add_res = RES_BAD_ARG; + BA(senc_scene_add_geometry(scn, ntriangles, get_indices, NULL, + nvertices, get_position, add_trg, merge_trg, &ctx)); + CHK(add_geom_ctx.add_cpt == 0); + add_geom_ctx.add_res = RES_MEM_ERR; + CHK(senc_scene_add_geometry(scn, ntriangles, get_indices, NULL, + nvertices, get_position, add_trg, merge_trg, &ctx) == RES_MEM_ERR); + CHK(add_geom_ctx.add_cpt == 0); + + /* Successful add geometry with add callback */ + add_geom_ctx.add_res = RES_OK; + OK(senc_scene_add_geometry(scn, ntriangles, get_indices, NULL, + nvertices, get_position, add_trg, merge_trg, &ctx)); + CHK(add_geom_ctx.add_cpt == media_count); + CHK(add_geom_ctx.merge_cpt == 0); + + /* Clear scene */ + SENC(scene_ref_put(scn)); + OK(senc_scene_create(dev, conv, &scn)); + + /* Successful add geometry without add callback */ + add_geom_ctx.add_cpt = 0; + OK(senc_scene_add_geometry(scn, ntriangles, get_indices, NULL, + nvertices, get_position, NULL, merge_trg, &ctx)); + CHK(add_geom_ctx.add_cpt == 0); + CHK(add_geom_ctx.merge_cpt == 0); + + /* If merge fails, add geometry fails the same way */ + add_geom_ctx.merge_res = RES_BAD_ARG; + BA(senc_scene_add_geometry(scn, ntriangles, get_indices, NULL, + nvertices, get_position, add_trg, merge_trg, &ctx)); + CHK(add_geom_ctx.merge_cpt == 0); + add_geom_ctx.merge_res = RES_MEM_ERR; + CHK(senc_scene_add_geometry(scn, ntriangles, get_indices, NULL, + nvertices, get_position, add_trg, merge_trg, &ctx) == RES_MEM_ERR); + CHK(add_geom_ctx.merge_cpt == 0); + + /* Successful add geometry without merge callback */ + OK(senc_scene_add_geometry(scn, ntriangles, get_indices, NULL, + nvertices, get_position, add_trg, NULL, &ctx)); + CHK(add_geom_ctx.merge_cpt == 0); + + /* Successful add geometry with merge callback */ + add_geom_ctx.merge_res = RES_OK; + OK(senc_scene_add_geometry(scn, ntriangles, get_indices, NULL, + nvertices, get_position, add_trg, merge_trg, &ctx)); + CHK(add_geom_ctx.merge_cpt == media_count); + add_geom_ctx.merge_cpt = 0; + + /* Geometry with media information on both sides */ + ctx.front_media = medium0; + ctx.back_media = medium1; + + /* Clear scene */ + SENC(scene_ref_put(scn)); + OK(senc_scene_create(dev, conv, &scn)); + + /* Successful add geometry with add callback */ + add_geom_ctx.add_res = RES_OK; + OK(senc_scene_add_geometry(scn, ntriangles, get_indices, NULL, + nvertices, get_position, add_trg, merge_trg, &ctx)); + CHK(add_geom_ctx.add_cpt == media_count); + CHK(add_geom_ctx.merge_cpt == 0); + add_geom_ctx.add_cpt = 0; + + /* Clear scene */ + SENC(scene_ref_put(scn)); + OK(senc_scene_create(dev, conv, &scn)); + + /* Successful add geometry with add callback */ + add_geom_ctx.add_res = RES_OK; + OK(senc_scene_add_geometry(scn, ntriangles, get_indices, NULL, + nvertices, get_position, add_trg, merge_trg, &ctx)); + CHK(add_geom_ctx.add_cpt == media_count); + CHK(add_geom_ctx.merge_cpt == 0); + add_geom_ctx.add_cpt = 0; + + /* Successful add geometry with merge callback */ + add_geom_ctx.merge_res = RES_OK; + OK(senc_scene_add_geometry(scn, ntriangles, get_indices, NULL, + nvertices, get_position, add_trg, merge_trg, &ctx)); + CHK(add_geom_ctx.merge_cpt == media_count); + add_geom_ctx.merge_cpt = 0; + + /* Geometry with incompatible media information on both sides */ + ctx.front_media = medium1; + ctx.back_media = medium0; + + /* Unsuccessful add geometry without merge callback */ + OK(senc_scene_add_geometry(scn, ntriangles, get_indices, NULL, + nvertices, get_position, add_trg, NULL, &ctx)); + CHK(add_geom_ctx.merge_cpt == 0); + + /* Successful add geometry with merge callback */ + add_geom_ctx.merge_res = RES_OK; + OK(senc_scene_add_geometry(scn, ntriangles, get_indices, NULL, + nvertices, get_position, add_trg, merge_trg, &ctx)); + CHK(add_geom_ctx.merge_cpt == media_count); + add_geom_ctx.merge_cpt = 0; + + SENC(scene_ref_put(scn)); + SENC(device_ref_put(dev)); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHK(mem_allocated_size() == 0); + + return 0; +} diff --git a/src/test_senc_undefined_medium_attr.c b/src/test_senc_undefined_medium_attr.c @@ -67,6 +67,8 @@ merge_trg (const unsigned global_id, const unsigned itri, const int reversed_triangle, + const unsigned triangle_media[2], + const unsigned merge_media[2], void* context) { res_T res = RES_OK; @@ -75,7 +77,15 @@ merge_trg int need_merge; unsigned interf; unsigned* interf_data; + int i, compat; ASSERT(ctx); (void)reversed_triangle; + /* Check media compatibility */ + compat = 1; + FOR_EACH(i, 0, 2) + compat &= (triangle_media[i] == SENC_UNDEFINED_MEDIUM + || merge_media[i] == SENC_UNDEFINED_MEDIUM + || triangle_media[i] == merge_media[i]); + if (!compat) return RES_BAD_ARG; merge_ctx = ctx->custom; res = darray_intface_id_resize(&merge_ctx->global_interf_data, global_id + 1); if(res != RES_OK) return res;