commit a0fd5bfdb863f94ca2f86bb4e74b3c16493d4e48
parent eda2d20a855b782ea530bef5b3a3e1a75e237dfe
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date: Fri, 22 Nov 2019 16:12:14 +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:
7 files changed, 275 insertions(+), 64 deletions(-)
diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt
@@ -119,9 +119,7 @@ if(NOT NO_TEST)
register_test(${_name} ${_name})
endfunction()
- new_test(test_senc2d_square_behind_square)
- new_test(test_senc2d_square_in_square)
- new_test(test_senc2d_square_on_square)
+ new_test(test_senc2d_add_n_merge)
new_test(test_senc2d_descriptor)
new_test(test_senc2d_device)
new_test(test_senc2d_enclosure)
@@ -130,6 +128,9 @@ if(NOT NO_TEST)
new_test(test_senc2d_many_segments)
new_test(test_senc2d_sample_enclosure)
new_test(test_senc2d_scene)
+ new_test(test_senc2d_square_behind_square)
+ new_test(test_senc2d_square_in_square)
+ new_test(test_senc2d_square_on_square)
new_test(test_senc2d_undefined_medium)
new_test(test_senc2d_undefined_medium_attr)
diff --git a/src/senc2d.h b/src/senc2d.h
@@ -171,17 +171,23 @@ senc2d_scene_reserve
const unsigned media_count);
/* Add a new set of vertices and segments to the scene.
- * Vertices can be duplicates and are deduplicated on the fly.
- * Segments 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.
+ * Segments can be duplicates as long as media are compatible on both sides.
+ * Valid segment duplicates are silently deduplicated, invalid duplicates
+ * trigger an error (add_geometry returns RES_BAD_ARG).
* The special value SENC2D_UNDEFINED_MEDIUM denotes an undefined medium.
* It can be used to define the 2 sides of a segment at different times.
+ * Media on duplicate segments are consider compatible if:
+ * - the merge_segment callback is provided and returns RES_OK,
+ * - or media are identical or SENC2D_UNDEFINED_MEDIUM.
* When deduplicating segments, the first occurence remains (with its
- * original index in user world, regardless of deduplication).
- * The add_segment and merge_segment callbacks can be used for attributes
- * management including segment 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
+ * SENC2D_UNDEFINED_MEDIUM to any defined medium.
+ * The add_segment and merge_segment callbacks can be used for attribute
+ * management (including segment 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. */
SENC2D_API res_T
senc2d_scene_add_geometry
(struct senc2d_scene* scene,
@@ -198,16 +204,26 @@ senc2d_scene_add_geometry
void(*position)(const unsigned ivert, double pos[2], void* context),
/* Called for each new segment so that the client app can manage its own
* segment 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_segment returned. */
res_T(*add_segment) /* Can be NULL */
(const unsigned global_id, const unsigned iseg, void* context),
/* Called if the iseg_th segment of the current add_geometry is equal to
* the global_id_th global segment so that the client app can try to merge
- * its own segment data. The reversed_segment arg indicates if the segment
- * vertices' order is the same it was when the segment was first added.
- * If return is not RES_OK, add_geometry stops and fails. */
+ * its own segment data or record a possible media conflict.
+ * The reversed_segment arg indicates if the segment vertices' order is the
+ * same it was when the segment was first added.
+ * segment_media and merge_media contain the involved media.
+ * If those media are incompatible and merge_segment returns RES_OK the
+ * process will continue with the next segment and possibly end in success.
+ * If return is not RES_OK, add_geometry stops immediately and returns
+ * whatever value merge_segment returned.
+ * If merge_segment 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 segment with incompatible media. */
res_T(*merge_segment) /* Can be NULL */
(const unsigned global_id, const unsigned iseg, const int reversed_segment,
+ const unsigned segment_media[2], const unsigned merge_media[2],
void* context),
void* context);
diff --git a/src/senc2d_scene.c b/src/senc2d_scene.c
@@ -80,7 +80,6 @@ senc2d_scene_create
SENC2D(device_ref_get(dev));
scn->dev = dev;
scn->convention = conv;
- scn->ngeoms = 0;
scn->nsegs = 0;
scn->nusegs = 0;
scn->next_medium_idx = 0;
@@ -135,7 +134,8 @@ senc2d_scene_add_geometry
const unsigned nverts,
void(*position)(const unsigned, double* pos, void*),
res_T(*add_segment)(const unsigned, const unsigned, void*),
- res_T(*merge_segment)(const unsigned, const unsigned, const int, void*),
+ res_T(*merge_segment)(const unsigned, const unsigned, const int,
+ const unsigned*, const unsigned*, void*),
void* ctx)
{
struct darray_vrtx_id unique_vertice_ids;
@@ -173,10 +173,6 @@ senc2d_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).\n",
- FUNC_NAME, (unsigned long)(scn->nverts + i), SPLIT2(tmp.vec));
unique_v = *p_vrtx;
} else {
/* New vertex */
@@ -237,54 +233,39 @@ senc2d_scene_add_geometry
reversed = seg_make_key(&seg_key, tmp.vertice_id);
p_seg = htable_seg_find(&scn->unique_segments, &seg_key);
if(p_seg) {
+ /* Duplicate segment. Need to check duplicate validity */
union vrtx_id2 useg_key;
char ureversed = seg_make_key(&useg_key, seg[*p_seg].vertice_id);
int same = (reversed == ureversed);
const medium_id_t* umed;
- /* Duplicate segment. Need to check duplicate validity */
ASSERT(seg_key_eq(&seg_key, &useg_key));
if(!same) SWAP(unsigned, tmp.medium[0], tmp.medium[1]);
umed = seg[*p_seg].medium;
- if(!compatible_medium(umed[0], tmp.medium[0])
- || !compatible_medium(umed[1], tmp.medium[1]))
- {
- /* Same segments with different media: invalid! */
- const union double2* positions
- = darray_position_cdata_get(&scn->vertices);
- log_err(scn->dev, "%s: segment %lu is a duplicate"
- " of segment %lu with incoherent media.\n",
- FUNC_NAME, (unsigned long)tmp.global_id,
- (unsigned long)seg[*p_seg].global_id);
- log_err(scn->dev,
- "Segment %lu:\n (%g %g ) (%g %g)\n",
- (unsigned long)seg[*p_seg].global_id,
- SPLIT2(positions[seg[*p_seg].vertice_id[0]].vec),
- SPLIT2(positions[seg[*p_seg].vertice_id[1]].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_segment_in_data_get(&scn->segments_in) + *p_seg;
- /* Replace possible undefined media */
+ if(merge_segment) {
+ /* Let the client app rule. */
+ unsigned smed[2], mmed[2];
FOR_EACH(j, 0, 2) {
- if(range_adjust_ptr->medium[j] == SENC2D_UNDEFINED_MEDIUM
- && tmp.medium[j] != SENC2D_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_segment) {
- OK(merge_segment(seg[*p_seg].global_id, i, same, ctx));
- } else {
- log_warn(scn->dev,
- "%s: segment %lu is a duplicate of segment %lu.\n",
- FUNC_NAME, (unsigned long)tmp.global_id,
- (unsigned long)seg[*p_seg].global_id);
+ OK(merge_segment(seg[*p_seg].global_id, i, same, smed, mmed, ctx));
+ /* If merge_segment 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_segment): replace undef media. */
+ range_adjust_ptr = darray_segment_in_data_get(&scn->segments_in) + *p_seg;
+ /* Replace possible undefined media */
+ FOR_EACH(j, 0, 2) {
+ if(range_adjust_ptr->medium[j] == SENC2D_UNDEFINED_MEDIUM
+ && tmp.medium[j] != SENC2D_UNDEFINED_MEDIUM) {
+ range_adjust_ptr->medium[j] = tmp.medium[j];
+ scn->sides_with_defined_medium_count++;
}
}
} else {
@@ -331,7 +312,6 @@ exit:
scn->nsegs += actual_nsegs;
ASSERT(scn->nuverts == htable_vrtx_size_get(&scn->unique_vertices));
ASSERT(scn->nusegs == htable_seg_size_get(&scn->unique_segments));
- ++scn->ngeoms;
return res;
error:
goto exit;
diff --git a/src/senc2d_scene_analyze.c b/src/senc2d_scene_analyze.c
@@ -175,7 +175,7 @@ extract_connex_components
/* If there are sides with undefined medium, its like another medium */
undefs = (scn->sides_with_defined_medium_count < 2 * scn->nsegs) ? 1 : 0;
-#pragma omp single
+ #pragma omp single
{
if(undefs) {
/* Range is unknown, process each side */
diff --git a/src/senc2d_scene_c.h b/src/senc2d_scene_c.h
@@ -172,7 +172,6 @@ struct senc2d_scene {
struct htable_seg unique_segments;
/* Keep sizes */
- unsigned ngeoms; /* Not used yet (just counted). */
seg_id_t nsegs, nusegs; /* Segment count, unique segment count */
vrtx_id_t nverts, nuverts; /* Vrtx count, unique vrtx count */
medium_id_t next_medium_idx;
diff --git a/src/test_senc2d_add_n_merge.c b/src/test_senc2d_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 "senc2d.h"
+#include "test_senc2d_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_seg
+ (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_seg
+ (const unsigned global_id,
+ const unsigned iseg,
+ const int reversed_segment,
+ const unsigned segment_media[2],
+ const unsigned merge_media[2],
+ void* context)
+{
+ struct context* ctx = context;
+ struct add_geom_ctx* add_geom_ctx;
+ ASSERT(ctx && segment_media && merge_media);
+ (void)global_id; (void)iseg; (void)reversed_segment; (void)segment_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 senc2d_device* dev = NULL;
+ struct senc2d_scene* scn = NULL;
+ struct context ctx;
+ unsigned i;
+ struct add_geom_ctx add_geom_ctx;
+ unsigned media[4];
+ const int conv = SENC2D_CONVENTION_NORMAL_FRONT | SENC2D_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(senc2d_device_create(NULL, &allocator, SENC2D_NTHREADS_DEFAULT, 1, &dev));
+ OK(senc2d_scene_create(dev, conv, &scn));
+
+ /* A 2D square.
+ * 2 enclosures (inside, outside) sharing the same segments,
+ * but opposite sides */
+ ctx.positions = box_vertices;
+ ctx.indices = box_indices;
+ ctx.scale = 1;
+ ctx.reverse_vrtx = 0;
+ ctx.reverse_med = 0;
+ d2(ctx.offset, 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] = SENC2D_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(senc2d_scene_add_geometry(scn, nsegments, get_indices, NULL,
+ nvertices, get_position, add_seg, merge_seg, &ctx));
+ CHK(add_geom_ctx.add_cpt == 0);
+ add_geom_ctx.add_res = RES_MEM_ERR;
+ CHK(senc2d_scene_add_geometry(scn, nsegments, get_indices, NULL,
+ nvertices, get_position, add_seg, merge_seg, &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(senc2d_scene_add_geometry(scn, nsegments, get_indices, NULL,
+ nvertices, get_position, add_seg, merge_seg, &ctx));
+ CHK(add_geom_ctx.add_cpt == media_count);
+ CHK(add_geom_ctx.merge_cpt == 0);
+
+ /* Clear scene */
+ SENC2D(scene_ref_put(scn));
+ OK(senc2d_scene_create(dev, conv, &scn));
+
+ /* Successful add geometry without add callback */
+ add_geom_ctx.add_cpt = 0;
+ OK(senc2d_scene_add_geometry(scn, nsegments, get_indices, NULL,
+ nvertices, get_position, NULL, merge_seg, &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(senc2d_scene_add_geometry(scn, nsegments, get_indices, NULL,
+ nvertices, get_position, add_seg, merge_seg, &ctx));
+ CHK(add_geom_ctx.merge_cpt == 0);
+ add_geom_ctx.merge_res = RES_MEM_ERR;
+ CHK(senc2d_scene_add_geometry(scn, nsegments, get_indices, NULL,
+ nvertices, get_position, add_seg, merge_seg, &ctx) == RES_MEM_ERR);
+ CHK(add_geom_ctx.merge_cpt == 0);
+
+ /* Successful add geometry without merge callback */
+ OK(senc2d_scene_add_geometry(scn, nsegments, get_indices, NULL,
+ nvertices, get_position, add_seg, NULL, &ctx));
+ CHK(add_geom_ctx.merge_cpt == 0);
+
+ /* Successful add geometry with merge callback */
+ add_geom_ctx.merge_res = RES_OK;
+ OK(senc2d_scene_add_geometry(scn, nsegments, get_indices, NULL,
+ nvertices, get_position, add_seg, merge_seg, &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 */
+ SENC2D(scene_ref_put(scn));
+ OK(senc2d_scene_create(dev, conv, &scn));
+
+ /* Successful add geometry with add callback */
+ add_geom_ctx.add_res = RES_OK;
+ OK(senc2d_scene_add_geometry(scn, nsegments, get_indices, NULL,
+ nvertices, get_position, add_seg, merge_seg, &ctx));
+ CHK(add_geom_ctx.add_cpt == media_count);
+ CHK(add_geom_ctx.merge_cpt == 0);
+ add_geom_ctx.add_cpt = 0;
+
+ /* Clear scene */
+ SENC2D(scene_ref_put(scn));
+ OK(senc2d_scene_create(dev, conv, &scn));
+
+ /* Successful add geometry with add callback */
+ add_geom_ctx.add_res = RES_OK;
+ OK(senc2d_scene_add_geometry(scn, nsegments, get_indices, NULL,
+ nvertices, get_position, add_seg, merge_seg, &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(senc2d_scene_add_geometry(scn, nsegments, get_indices, NULL,
+ nvertices, get_position, add_seg, merge_seg, &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(senc2d_scene_add_geometry(scn, nsegments, get_indices, NULL,
+ nvertices, get_position, add_seg, NULL, &ctx));
+ CHK(add_geom_ctx.merge_cpt == 0);
+
+ /* Successful add geometry with merge callback */
+ add_geom_ctx.merge_res = RES_OK;
+ OK(senc2d_scene_add_geometry(scn, nsegments, get_indices, NULL,
+ nvertices, get_position, add_seg, merge_seg, &ctx));
+ CHK(add_geom_ctx.merge_cpt == media_count);
+ add_geom_ctx.merge_cpt = 0;
+
+ SENC2D(scene_ref_put(scn));
+ SENC2D(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_senc2d_undefined_medium_attr.c b/src/test_senc2d_undefined_medium_attr.c
@@ -68,6 +68,8 @@ merge_seg
(const unsigned global_id,
const unsigned iseg,
const int reversed_segment,
+ const unsigned segment_media[2],
+ const unsigned merge_media[2],
void* context)
{
res_T res = RES_OK;
@@ -76,7 +78,15 @@ merge_seg
int need_merge;
unsigned interf;
unsigned* interf_data;
- ASSERT(ctx); (void)reversed_segment;
+ int i, compat;
+ ASSERT(ctx); (void)reversed_segment; (void)segment_media; (void)merge_media;
+ /* Check media compatibility */
+ compat = 1;
+ FOR_EACH(i, 0, 2)
+ compat &= (segment_media[i] == SENC2D_UNDEFINED_MEDIUM
+ || merge_media[i] == SENC2D_UNDEFINED_MEDIUM
+ || segment_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;