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 01dd4cd24f717d73293deb57a7369f1f73a412a7
parent ce25c6cc1cadb5cd3600c7e278d635f609cf325c
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Mon, 15 Jun 2020 13:50:49 +0200

Stop returning enclosures on ill-formed scenes.

When a scene includes overlapping triangles, scene API calls related to enclosures now return RES_BAD_OP

Diffstat:
Mcmake/CMakeLists.txt | 1+
Msrc/senc3d.h | 6++++--
Msrc/senc3d_descriptor.c | 24+++++++++++++++++++-----
Msrc/senc3d_scene_analyze.c | 37+++++++++++++++++++++----------------
Asrc/test_senc3d_invalid_scenes.c | 176+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_senc3d_scene.c | 6++++++
Msrc/test_senc3d_utils.h | 1+
7 files changed, 228 insertions(+), 23 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -159,6 +159,7 @@ if(NOT NO_TEST) new_test(test_senc3d_device) new_test(test_senc3d_enclosure) new_test(test_senc3d_inconsistant_cube) + new_test(test_senc3d_invalid_scenes) new_test(test_senc3d_sample_enclosure) new_test(test_senc3d_scene) new_test(test_senc3d_some_enclosures) diff --git a/src/senc3d.h b/src/senc3d.h @@ -171,6 +171,8 @@ senc3d_device_ref_put /****************************************************************************** * star_enclosures-3d scene. A scene is a collection of triangles. Each * triangle is defined with a medium on each side. + * Scenes with overlapping triangles are considered ill-formed and any + * enclosure-related API call on such a scene will return RES_BAD_OP. *****************************************************************************/ /* Creates a scene from some vertices and triangles. * Neither vertices nor triangles can include duplicates. @@ -305,8 +307,8 @@ senc3d_scene_get_frontier_segment /* Returns the number of overlapping triangles. * A model including overlapping triangles cannot be split into enclosures * unequivocally and will probably be ruled invalid by most softwares. - * Additionally there is no garantee that 2 different scenes created from such - * a model give the same enclosures. + * As a consequence, one cannot query enclosure-related information on a model + * with overlapping triangles. * The library currently only detects overlapping triangles that share an * edge. */ SENC3D_API res_T diff --git a/src/senc3d_descriptor.c b/src/senc3d_descriptor.c @@ -38,6 +38,8 @@ senc3d_scene_get_enclosure_count (const struct senc3d_scene* scn, enclosure_id_t* count) { if(!scn || !count) return RES_BAD_ARG; + if(darray_trg_id_size_get(&scn->analyze.overlapping_ids)) + return RES_BAD_OP; ASSERT(scn->analyze.enclosures_count == darray_enclosure_size_get(&scn->analyze.enclosures)); *count = scn->analyze.enclosures_count; @@ -56,6 +58,8 @@ senc3d_scene_get_enclosure_count_by_medium if(!scn || !count || (imed != SENC3D_UNSPECIFIED_MEDIUM && imed >= scn->next_medium_idx)) return RES_BAD_ARG; + if(darray_trg_id_size_get(&scn->analyze.overlapping_ids)) + return RES_BAD_OP; ASSERT(darray_enc_ids_array_size_get(&scn->analyze.enc_ids_array_by_medium) == 1 + scn->next_medium_idx); m_idx = medium_id_2_medium_idx(imed); @@ -74,8 +78,10 @@ senc3d_scene_get_enclosure struct senc3d_enclosure** out_enc) { struct senc3d_enclosure* enc; - if(!scn || idx >= darray_enclosure_size_get(&scn->analyze.enclosures) - || !out_enc) + if(!scn || !out_enc) return RES_BAD_ARG; + if(darray_trg_id_size_get(&scn->analyze.overlapping_ids)) + return RES_BAD_OP; + if(idx >= darray_enclosure_size_get(&scn->analyze.enclosures)) return RES_BAD_ARG; enc = enclosure_create(scn, idx); if(!enc) return RES_MEM_ERR; @@ -96,6 +102,8 @@ senc3d_scene_get_enclosure_by_medium if(!scn || !out_enc || (imed != SENC3D_UNSPECIFIED_MEDIUM && imed >= scn->next_medium_idx)) return RES_BAD_ARG; + if(darray_trg_id_size_get(&scn->analyze.overlapping_ids)) + return RES_BAD_OP; ASSERT(darray_enc_ids_array_size_get(&scn->analyze.enc_ids_array_by_medium) == 1 + scn->next_medium_idx); m_idx = medium_id_2_medium_idx(imed); @@ -114,8 +122,10 @@ senc3d_scene_get_triangle_enclosures { const struct triangle_enc* trg; int i; - if(!enclosures || !scn - || itri >= darray_triangle_enc_size_get(&scn->analyze.triangles_enc)) + if(!enclosures || !scn) return RES_BAD_ARG; + if(darray_trg_id_size_get(&scn->analyze.overlapping_ids)) + return RES_BAD_OP; + if(itri >= darray_triangle_enc_size_get(&scn->analyze.triangles_enc)) return RES_BAD_ARG; trg = darray_triangle_enc_cdata_get(&scn->analyze.triangles_enc) + itri; FOR_EACH(i, 0, 2) enclosures[i] = trg->enclosure[i]; @@ -130,6 +140,8 @@ senc3d_scene_get_frontier_segments_count size_t tmp; if(!scn || !count) return RES_BAD_ARG; + if(darray_trg_id_size_get(&scn->analyze.overlapping_ids)) + return RES_BAD_OP; tmp = darray_frontier_edge_size_get(&scn->analyze.frontiers); ASSERT(tmp <= VRTX_MAX__); *count = (vrtx_id_t)tmp; /* Back to API type */ @@ -147,6 +159,8 @@ senc3d_scene_get_frontier_segment if(!vrtx_id || !scn || !trg_id || iseg >= darray_frontier_edge_size_get(&scn->analyze.frontiers)) return RES_BAD_ARG; + if(darray_trg_id_size_get(&scn->analyze.overlapping_ids)) + return RES_BAD_OP; edge = darray_frontier_edge_cdata_get(&scn->analyze.frontiers) + iseg; ASSERT(edge->vrtx0 <= VRTX_MAX__); ASSERT(edge->vrtx1 <= VRTX_MAX__); @@ -176,7 +190,7 @@ senc3d_scene_get_overlapping_triangle const unsigned idx, unsigned* trg_id) { - if (!scn || !trg_id + if(!scn || !trg_id || idx >= darray_trg_id_size_get(&scn->analyze.overlapping_ids)) return RES_BAD_ARG; *trg_id = darray_trg_id_cdata_get(&scn->analyze.overlapping_ids)[idx]; diff --git a/src/senc3d_scene_analyze.c b/src/senc3d_scene_analyze.c @@ -795,6 +795,7 @@ collect_and_link_neighbours const union double3* vertices; const int thread_count = omp_get_num_threads(); const int rank = omp_get_thread_num(); + const int front = ((scn->convention & SENC3D_CONVENTION_NORMAL_FRONT) != 0); /* Htable used to give an id to edges */ struct htable_edge_id edge_ids; /* Htable used to store overlapping triangles */ @@ -925,36 +926,35 @@ collect_and_link_neighbours d3_sub(edge, vertices[v2].vec, vertices[v0].vec); d33_muld3(edge, basis, edge); ASSERT(d3_len(edge) && (edge[0] || edge[1])); - neighbour_info->angle = atan2(edge[1], edge[0]); + neighbour_info->angle = atan2(edge[1], edge[0]); /* in ]-pi + pi]*/ if(is_reversed) d3(n.vec, +edge[1], -edge[0], 0); else d3(n.vec, -edge[1], +edge[0], 0); - if(neighbour_info->angle < 0) neighbour_info->angle += 2 * PI; /* Normal orientation calculation. */ - if(neighbour_info->angle <= PI / 4) { + if(neighbour_info->angle > 3 * PI / 4) { ASSERT(n.pos.y); - neighbour_info->normal_toward_next_neighbour = (n.pos.y > 0); - } else if(neighbour_info->angle <= 3 * PI / 4) { + neighbour_info->normal_toward_next_neighbour = (n.pos.y < 0); + } else if(neighbour_info->angle > PI / 4) { ASSERT(n.pos.x); neighbour_info->normal_toward_next_neighbour = (n.pos.x < 0); - } else if(neighbour_info->angle <= 5 * PI / 4) { + } else if(neighbour_info->angle > -PI / 4) { ASSERT(n.pos.y); - neighbour_info->normal_toward_next_neighbour = (n.pos.y < 0); - } else if(neighbour_info->angle <= 7 * PI / 4) { + neighbour_info->normal_toward_next_neighbour = (n.pos.y > 0); + } else if(neighbour_info->angle > -3 * PI / 4) { ASSERT(n.pos.x); neighbour_info->normal_toward_next_neighbour = (n.pos.x > 0); } else { ASSERT(n.pos.y); - neighbour_info->normal_toward_next_neighbour = (n.pos.y > 0); + neighbour_info->normal_toward_next_neighbour = (n.pos.y < 0); } } /* Sort triangles by rotation angle */ qsort(darray_neighbour_data_get(neighbour_list), neighbour_count, sizeof(struct neighbour_info), neighbour_cmp); - /* Link sides. - * Create cycles of sides by neighbourhood around common edge. */ + /* Link sides. + * Create cycles of sides by neighbourhood around common edge. */ a = -DBL_MAX; FOR_EACH(i, 0, neighbour_count) { /* Neighbourhood info for current pair of triangles */ @@ -972,12 +972,12 @@ collect_and_link_neighbours const trg_id_t crt_id = current->trg_id; const trg_id_t ccw_id = ccw_neighbour->trg_id; /* Facing sides of triangles */ - const int front = ((scn->convention & SENC3D_CONVENTION_NORMAL_FRONT) != 0); const enum senc3d_side crt_side - = current->normal_toward_next_neighbour == front ? SENC3D_FRONT : SENC3D_BACK; + = (current->normal_toward_next_neighbour == front) + ? SENC3D_FRONT : SENC3D_BACK; const enum senc3d_side ccw_side - = ccw_neighbour->normal_toward_next_neighbour == front ? - SENC3D_BACK : SENC3D_FRONT; + = (ccw_neighbour->normal_toward_next_neighbour == front) + ? SENC3D_BACK : SENC3D_FRONT; /* Index of sides in trgsides */ const side_id_t crt_side_idx = TRGIDxSIDE_2_TRGSIDE(crt_id, crt_side); const side_id_t ccw_side_idx = TRGIDxSIDE_2_TRGSIDE(ccw_id, ccw_side); @@ -990,7 +990,6 @@ collect_and_link_neighbours /* Two consecutive triangles with same angle! Store them */ const struct neighbour_info* previous; trg_id_t prev_id; - ASSERT(i > 0); previous = darray_neighbour_cdata_get(neighbour_list) + i - 1; prev_id = previous->trg_id; #pragma omp critical @@ -1388,6 +1387,11 @@ scene_analyze } /* Implicit barrier here: constraints on step 1 data are now met */ + if(darray_trg_id_size_get(&scn->analyze.overlapping_ids)) { + /* Stop analysis here as the model is ill-formed */ + goto end_; + } + if(res != RES_OK || res2 != RES_OK) { #pragma omp single nowait { @@ -1493,6 +1497,7 @@ scene_analyze } } /* No barrier here */ +end_: error_: ; } /* Implicit barrier here */ diff --git a/src/test_senc3d_invalid_scenes.c b/src/test_senc3d_invalid_scenes.c @@ -0,0 +1,176 @@ +/* Copyright (C) |Meso|Star> 2016-2020 (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/>. */ + +/* This test has been created using the sg3_geometry_dump_as_C_code feature + * of star-geometry. It uses output from test_sg3_cube_on_cube. */ + +#define _POSIX_C_SOURCE 200112L /* snprintf */ + +#include "senc3d.h" +#include "test_senc3d_utils.h" + +#include <rsys/double3.h> + +#include <stdio.h> + +/* + 2 overlapping cubes with faces splitted on opposite diagonals + */ + +/* Dump of star-geometry-3d 'invalid_1'. */ +static const unsigned invalid_vertices_count = 8; +static const double invalid_vertices[24] = +{ + 0, 0, 0, + 1, 0, 0, + 0, 1, 0, + 1, 1, 0, + 0, 0, 1, + 1, 0, 1, + 0, 1, 1, + 1, 1, 1 +}; +static const unsigned invalid_triangles_count = 24; +static const unsigned invalid_triangles[72] = +{ + 0, 2, 1, + 1, 2, 3, + 0, 4, 2, + 2, 4, 6, + 4, 5, 6, + 6, 5, 7, + 3, 7, 1, + 1, 7, 5, + 2, 6, 3, + 3, 6, 7, + 0, 1, 4, + 4, 1, 5, + 0, 3, 1, + 0, 2, 3, + 0, 6, 2, + 0, 4, 6, + 4, 5, 7, + 6, 4, 7, + 3, 7, 5, + 1, 3, 5, + 2, 6, 7, + 3, 2, 7, + 0, 1, 5, + 4, 0, 5 +}; +static const unsigned invalid_properties[72] = +{ + 0, 1, 0, + 0, 1, 0, + 0, 1, 0, + 0, 1, 0, + 0, 1, 0, + 0, 1, 0, + 0, 1, 0, + 0, 1, 0, + 0, 1, 0, + 0, 1, 0, + 0, 1, 0, + 0, 1, 0, + 0, 1, 0, + 0, 1, 0, + 0, 1, 0, + 0, 1, 0, + 0, 1, 0, + 0, 1, 0, + 0, 1, 0, + 0, 1, 0, + 0, 1, 0, + 0, 1, 0, + 0, 1, 0, + 0, 1, 0 +}; + +static const unsigned degenerated1_triangles_count = 1; +static const unsigned degenerated1_vertices_count = 3; +static const unsigned degenerated1[3] = { 0, 0, 0 }; + +static const unsigned degenerated2_triangles_count = 1; +static const unsigned degenerated2_vertices_count = 3; +static const unsigned degenerated2[3] = { 0, 1, 2 }; + +static const double degenerated_vertices[9] += { 0, 0, 0, 1, 0, 0, 2, 0, 0 }; +static const unsigned degenerated_properties[9] += { 0, 0, 0, 1, 0, 0, 2, 0, 0 }; + +int +main(int argc, char** argv) +{ + struct mem_allocator allocator; + struct senc3d_device* dev = NULL; + struct senc3d_scene* scn = NULL; + struct context ctx = CONTEXT_NULL__; + unsigned count, tcount, t, e; + struct senc3d_enclosure* enc; + (void)argc, (void)argv; + + OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); + OK(senc3d_device_create(NULL, &allocator, 1, 1, &dev)); + + /* Degenerated triangle: duplicated vertex */ + ctx.positions = degenerated_vertices; + ctx.indices = degenerated1; + ctx.properties = degenerated_properties; + BA(senc3d_scene_create(dev, + SENC3D_CONVENTION_NORMAL_FRONT | SENC3D_CONVENTION_NORMAL_INSIDE, + degenerated1_triangles_count, get_indices, get_media_from_properties, + degenerated1_vertices_count, get_position, &ctx, &scn)); + + /* Degenerated triangles: flat triangle */ + ctx.positions = degenerated_vertices; + ctx.indices = degenerated2; + ctx.properties = degenerated_properties; + BA(senc3d_scene_create(dev, + SENC3D_CONVENTION_NORMAL_FRONT | SENC3D_CONVENTION_NORMAL_INSIDE, + degenerated2_triangles_count, get_indices, get_media_from_properties, + degenerated2_vertices_count, get_position, &ctx, &scn)); + + ctx.positions = invalid_vertices; + ctx.indices = invalid_triangles; + ctx.properties = invalid_properties; + OK(senc3d_scene_create(dev, + SENC3D_CONVENTION_NORMAL_FRONT | SENC3D_CONVENTION_NORMAL_INSIDE, + invalid_triangles_count, get_indices, get_media_from_properties, + invalid_vertices_count, get_position, &ctx, &scn)); + + OK(senc3d_scene_get_triangles_count(scn, &tcount)); + FOR_EACH(t, 0, tcount) { + unsigned ids[2]; + BO(senc3d_scene_get_triangle_enclosures(scn, t, ids)); + } + BO(senc3d_scene_get_enclosure_count(scn, &count)); + BO(senc3d_scene_get_enclosure(scn, 0, &enc)); + + OK(senc3d_scene_get_overlapping_triangles_count(scn, &count)); + FOR_EACH(e, 0, count) { + OK(senc3d_scene_get_overlapping_triangle(scn, e, &t)); + ASSERT(t < tcount); + } + CHK(count == invalid_triangles_count); + + OK(senc3d_scene_ref_put(scn)); + OK(senc3d_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_senc3d_scene.c b/src/test_senc3d_scene.c @@ -210,6 +210,12 @@ main(int argc, char** argv) BA(senc3d_scene_get_frontier_segment(scn, UINT_MAX, NULL, NULL)); BA(senc3d_scene_get_frontier_segment(NULL, UINT_MAX, NULL, NULL)); + BA(senc3d_scene_get_overlapping_triangles_count(NULL, NULL)); + BA(senc3d_scene_get_overlapping_triangles_count(scn, NULL)); + BA(senc3d_scene_get_overlapping_triangles_count(NULL, &count)); + OK(senc3d_scene_get_overlapping_triangles_count(scn, &count)); + CHK(count == 0); + BA(senc3d_scene_ref_get(NULL)); OK(senc3d_scene_ref_get(scn)); BA(senc3d_scene_ref_put(NULL)); diff --git a/src/test_senc3d_utils.h b/src/test_senc3d_utils.h @@ -24,6 +24,7 @@ #define OK(Expr) CHK((Expr) == RES_OK) #define BA(Expr) CHK((Expr) == RES_BAD_ARG) +#define BO(Expr) CHK((Expr) == RES_BAD_OP) /****************************************************************************** * Geometry