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:
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