star-enclosures-2d

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

commit d3dc3115564e6e67950a2f94ed75bec59ab55755
parent d63d9621865eed644204a84a3b56d5a8397d1241
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Fri, 20 Jul 2018 12:05:15 +0200

An enclosure can now be made of more than 1 medium.

Diffstat:
Mcmake/CMakeLists.txt | 4++--
Msrc/senc2d.h | 14++++++++++----
Msrc/senc2d_descriptor.c | 9++++-----
Msrc/senc2d_descriptor_c.h | 18+++++++++---------
Msrc/senc2d_enclosure.c | 21+++++++++++++++++++--
Msrc/senc2d_enclosure_c.h | 2+-
Msrc/senc2d_enclosure_data.h | 75++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/senc2d_internal_types.h | 16++++++++++++++--
Msrc/senc2d_scene.c | 2+-
Msrc/senc2d_scene_analyze.c | 866++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Msrc/senc2d_scene_analyze_c.h | 109+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Msrc/test_senc2d_descriptor.c | 10+++++-----
Msrc/test_senc2d_enclosure.c | 15++++++++++++---
Msrc/test_senc2d_many_enclosures.c | 7+++++--
Msrc/test_senc2d_scene.c | 21+++++++++++++++++----
Msrc/test_senc2d_square_behind_square.c | 60+++++++++++++++++++++++++++++++++---------------------------
Msrc/test_senc2d_square_in_square.c | 31++++++++++++++++++++++++++++---
Msrc/test_senc2d_utils.h | 38++++++++++++++++++++++++++++++++++++++
18 files changed, 881 insertions(+), 437 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -46,8 +46,8 @@ endif() # Configure and define targets ################################################################################ set(VERSION_MAJOR 0) -set(VERSION_MINOR 1) -set(VERSION_PATCH 1) +set(VERSION_MINOR 2) +set(VERSION_PATCH 0) set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) set(SENC2D_FILES_SRC diff --git a/src/senc2d.h b/src/senc2d.h @@ -66,8 +66,8 @@ struct senc2d_enclosure_header { unsigned unique_segment_count; /* Number of vertices */ unsigned vertices_count; - /* The medium inside the enclosure */ - unsigned enclosed_medium; + /* The number of media inside the enclosure */ + unsigned enclosed_media_count; /* Is the enclosure infinite? * Only the outermost enclosure is infinite. */ char is_infinite; @@ -163,7 +163,7 @@ senc2d_scene_ref_put SENC2D_API res_T senc2d_descriptor_get_max_medium (const struct senc2d_descriptor* descriptor, - unsigned* rank); + unsigned* max_medium_id); SENC2D_API res_T senc2d_descriptor_get_enclosure_count @@ -173,7 +173,7 @@ senc2d_descriptor_get_enclosure_count SENC2D_API res_T senc2d_descriptor_get_enclosure_count_by_medium (const struct senc2d_descriptor* descriptor, - const unsigned imed, + const unsigned imed, /* Must be in [0 max_medium_id] */ unsigned* count); SENC2D_API res_T @@ -278,6 +278,12 @@ senc2d_enclosure_get_segment_global_id unsigned* gid); SENC2D_API res_T +senc2d_enclosure_get_medium + (const struct senc2d_enclosure* enclosure, + const unsigned imed, + unsigned* medium); + +SENC2D_API res_T senc2d_enclosure_ref_get (struct senc2d_enclosure* enclosure); diff --git a/src/senc2d_descriptor.c b/src/senc2d_descriptor.c @@ -86,11 +86,11 @@ struct mem_allocator* ******************************************************************************/ res_T senc2d_descriptor_get_max_medium - (const struct senc2d_descriptor* desc, unsigned* rank) + (const struct senc2d_descriptor* desc, unsigned* max_medium_id) { - if(!desc || !rank) return RES_BAD_ARG; + if(!desc || !max_medium_id) return RES_BAD_ARG; ASSERT(desc->scene->nmeds < UINT_MAX); /* API type */ - *rank = (unsigned)desc->scene->nmeds; + *max_medium_id = (unsigned)desc->scene->nmeds - 1; return RES_OK; } @@ -136,8 +136,7 @@ senc2d_descriptor_get_enclosure struct senc2d_enclosure* enc; if(!desc || idx >= darray_enclosure_size_get(&desc->enclosures) || !out_enc) return RES_BAD_ARG; - enc = - enclosure_create(desc, darray_enclosure_data_get(&desc->enclosures) + idx); + enc = enclosure_create(desc, idx); if(!enc) return RES_MEM_ERR; *out_enc = enc; return RES_OK; diff --git a/src/senc2d_descriptor_c.h b/src/senc2d_descriptor_c.h @@ -26,26 +26,26 @@ struct senc2d_scene; struct mem_allocator; -struct segment_comp { - /* The connex component in which each side is. */ - component_id_t component[2]; +struct segment_part { + /* The connex component part in which each side is. */ + part_id_t part[2]; }; static void -segment_comp_init(struct mem_allocator* alloc, struct segment_comp* seg) { +segment_part_init(struct mem_allocator* alloc, struct segment_part* seg) { int i; (void)alloc; ASSERT(seg); - FOR_EACH(i, 0, 2) seg->component[i] = COMPONENT_NULL__; + FOR_EACH(i, 0, 2) seg->part[i] = PART_NULL__; } -#define DARRAY_NAME segment_comp -#define DARRAY_DATA struct segment_comp -#define DARRAY_FUNCTOR_INIT segment_comp_init +#define DARRAY_NAME segment_part +#define DARRAY_DATA struct segment_part +#define DARRAY_FUNCTOR_INIT segment_part_init #include <rsys/dynamic_array.h> struct segment_enc { - /* The connex component in which each side is. */ + /* The enclosure in which each side is. */ enclosure_id_t enclosure[2]; }; diff --git a/src/senc2d_enclosure.c b/src/senc2d_enclosure.c @@ -45,13 +45,15 @@ enclosure_release(ref_T * ref) struct senc2d_enclosure* enclosure_create (struct senc2d_descriptor* desc, - const struct enclosure_data* data) + const unsigned idx) { struct senc2d_enclosure* enc; - ASSERT(desc); + ASSERT(desc && idx < darray_enclosure_size_get(&desc->enclosures)); enc = MEM_CALLOC(descriptor_get_allocator(desc), 1, sizeof(struct senc2d_enclosure)); if(enc) { + const struct enclosure_data* data + = darray_enclosure_data_get(&desc->enclosures) + idx; SENC2D(descriptor_ref_get(desc)); enc->desc = desc; enc->data = data; @@ -156,6 +158,21 @@ senc2d_enclosure_get_segment_global_id } res_T +senc2d_enclosure_get_medium + (const struct senc2d_enclosure* enclosure, + const unsigned imed, + unsigned* medium) +{ + if(!enclosure || !medium + || imed >= enclosure->data->header.enclosed_media_count) + return RES_BAD_ARG; + ASSERT(enclosure->data->header.enclosed_media_count + == darray_media_size_get(&enclosure->data->enclosed_media)); + *medium = darray_media_cdata_get(&enclosure->data->enclosed_media)[imed]; + return RES_OK; +} + +res_T senc2d_enclosure_ref_get(struct senc2d_enclosure* enc) { if(!enc) return RES_BAD_ARG; diff --git a/src/senc2d_enclosure_c.h b/src/senc2d_enclosure_c.h @@ -32,6 +32,6 @@ struct senc2d_enclosure { struct senc2d_enclosure* enclosure_create (struct senc2d_descriptor* desc, - const struct enclosure_data* data); + const unsigned idx); #endif /* SENC2D_ENCLOSURE_C_H */ diff --git a/src/senc2d_enclosure_data.h b/src/senc2d_enclosure_data.h @@ -18,6 +18,8 @@ #include <rsys/rsys.h> #include <rsys/ref_count.h> +#include <rsys/hash_table.h> +#include <rsys/dynamic_array.h> #include "senc2d.h" #include "senc2d_scene_c.h" @@ -33,16 +35,78 @@ init_header(struct senc2d_enclosure_header* header) header->segment_count = 0; header->unique_segment_count = 0; header->vertices_count = 0; - header->enclosed_medium = MEDIUM_NULL__; + header->enclosed_media_count = 0; header->is_infinite = CHAR_MAX; } +#define HTABLE_NAME media +#define HTABLE_KEY medium_id_t +#define HTABLE_DATA char +#include <rsys/hash_table.h> + +static FINLINE res_T +htable_media_merge +(struct htable_media* dst, + struct htable_media* src) +{ + res_T res = RES_OK; + ASSERT(src && dst); + struct htable_media_iterator it, end; + char one = 1; + + htable_media_begin(src, &it); + htable_media_end(src, &end); + while(!htable_media_iterator_eq(&it, &end)) { + medium_id_t* k = htable_media_iterator_key_get(&it); + res = htable_media_set(dst, k, &one); + if(res != RES_OK) goto error; + htable_media_iterator_next(&it); + } +end: + return res; +error: + goto end; +} + +#define DARRAY_NAME media +#define DARRAY_DATA medium_id_t +#include <rsys/dynamic_array.h> + +static FINLINE res_T +htable_media_to_darray_media + (struct darray_media* dst, + struct htable_media* src) +{ + res_T res = RES_OK; + ASSERT(src && dst); + struct htable_media_iterator it, end; + + darray_media_clear(dst); + res = darray_media_reserve(dst, htable_media_size_get(src)); + if(res != RES_OK) goto error; + htable_media_begin(src, &it); + htable_media_end(src, &end); + while(!htable_media_iterator_eq(&it, &end)) { + medium_id_t* k = htable_media_iterator_key_get(&it); + res = darray_media_push_back(dst, k); + if(res != RES_OK) goto error; + htable_media_iterator_next(&it); + } +end: + return res; +error: + goto end; +} + struct enclosure_data { struct senc2d_enclosure_header header; /* Same segment can appear twice if both sides */ struct darray_segment_in sides; /* Index of vertices in scene's unique vertices */ struct darray_vrtx_id vertices; + /* List of the enclosed media */ + struct htable_media tmp_enclosed_media; + struct darray_media enclosed_media; /* Number of components involved in this enclosure */ component_id_t cc_count; /* Linked list of the components */ @@ -64,6 +128,8 @@ enclosure_data_init(struct mem_allocator* alloc, struct enclosure_data* enc) { enc->side_count = 0; darray_segment_in_init(alloc, &enc->sides); darray_vrtx_id_init(alloc, &enc->vertices); + htable_media_init(alloc, &enc->tmp_enclosed_media); + darray_media_init(alloc, &enc->enclosed_media); } static FINLINE res_T @@ -80,6 +146,8 @@ enclosure_data_copy dst->side_count = src->side_count; OK(darray_segment_in_copy(&dst->sides, &src->sides)); OK(darray_vrtx_id_copy(&dst->vertices, &src->vertices)); + OK(htable_media_copy(&dst->tmp_enclosed_media, &src->tmp_enclosed_media)); + OK(darray_media_copy(&dst->enclosed_media, &src->enclosed_media)); error: return res; } @@ -89,6 +157,8 @@ enclosure_data_release(struct enclosure_data* n) { ASSERT(n); darray_segment_in_release(&n->sides); darray_vrtx_id_release(&n->vertices); + htable_media_release(&n->tmp_enclosed_media); + darray_media_release(&n->enclosed_media); } static FINLINE res_T @@ -105,6 +175,9 @@ enclosure_data_copy_and_release dst->side_count = src->side_count; OK(darray_segment_in_copy_and_release(&dst->sides, &src->sides)); OK(darray_vrtx_id_copy_and_release(&dst->vertices, &src->vertices)); + OK(htable_media_copy_and_release(&dst->tmp_enclosed_media, + &src->tmp_enclosed_media)); + OK(darray_media_copy_and_release(&dst->enclosed_media, &src->enclosed_media)); error: return res; } diff --git a/src/senc2d_internal_types.h b/src/senc2d_internal_types.h @@ -81,8 +81,20 @@ typedef uint32_t enclosure_id_t; /* Component IDs use the same type than enclosure IDs */ typedef enclosure_id_t component_id_t; -#define COMPONENT_MAX__ ENCLOSURE_MAX__ -#define COMPONENT_NULL__ ENCLOSURE_NULL__ +#define COMPONENT_MAX__ (UINT32_MAX - 2) /* To allow special values */ +#define COMPONENT_NULL__ UINT32_MAX +/* Special part values */ +#define CC_GROUP_ROOT_NONE UINT32_MAX +#define CC_GROUP_ROOT_INFINITE (UINT32_MAX - 1) +#define CC_GROUP_ID_NONE UINT32_MAX +#define CC_ID_NONE UINT32_MAX + +/* Part IDs use the same type than Component IDs */ +typedef component_id_t part_id_t; +#define PART_MAX__ (UINT32_MAX - 2) /* To allow special values */ +#define PART_NULL__ UINT32_MAX +/* Special part values */ +#define PART_IN_STACK (UINT32_MAX - 1) /* This one is used as an index to arrays */ enum side_id { diff --git a/src/senc2d_scene.c b/src/senc2d_scene.c @@ -178,7 +178,7 @@ senc2d_scene_add_geometry if(tmp.vertice_id[0] == tmp.vertice_id[1]) { const union double2* positions = darray_position_cdata_get(&scn->vertices); - log_err(scn->dev, "%s: segment %lu is degenerate.\n", + log_warn(scn->dev, "%s: segment %lu is degenerate.\n", FUNC_NAME, (unsigned long)tmp.global_id); log_err(scn->dev, " (%g %g) (%g %g)\n", SPLIT2(positions[seg[i].vertice_id[0]].vec), diff --git a/src/senc2d_scene_analyze.c b/src/senc2d_scene_analyze.c @@ -32,8 +32,14 @@ #include <limits.h> #include <stdlib.h> +#define PART_DESCRIPTOR_NULL__ {\ + 0, PART_NULL__, VRTX_NULL__, 0, MEDIUM_NULL__,\ + { SEG_NULL__, 0}\ +} +const struct part_descriptor PART_DESCRIPTOR_NULL = PART_DESCRIPTOR_NULL__; + #define CC_DESCRIPTOR_NULL__ {\ - CHAR_MAX, VRTX_NULL__, 0, MEDIUM_NULL__,\ + CHAR_MAX, VRTX_NULL__, 0,\ CC_ID_NONE, CC_GROUP_ROOT_NONE, ENCLOSURE_NULL__,\ { SEG_NULL__, 0}\ } @@ -43,36 +49,10 @@ const struct cc_descriptor CC_DESCRIPTOR_NULL = CC_DESCRIPTOR_NULL__; #define DARRAY_DATA component_id_t #include <rsys/dynamic_array.h> -#define DARRAY_NAME side_id -#define DARRAY_DATA side_id_t -#include <rsys/dynamic_array.h> - /******************************************************************************* * Helper function ******************************************************************************/ static INLINE int -find_side_in_list - (const struct segside* segsides, - const struct darray_side_id* side_ids, - const side_id_t side_id, - const enum list_id list_id) -{ - side_id_t i; - size_t tmp; - (void)list_id; - (void)segsides; - ASSERT(segsides && side_ids); - tmp = darray_side_id_size_get(side_ids); - ASSERT(tmp <= SIDE_MAX__); - FOR_EACH(i, 0, (side_id_t)tmp) { - const side_id_t id = darray_side_id_cdata_get(side_ids)[i]; - ASSERT(segsides[id].list_id & list_id); - if(id == side_id) return 1; - } - return 0; -} - -static INLINE int neighbour_cmp(const void* w1, const void* w2) { const double a1 = ((struct neighbour_info*)w1)->angle; @@ -84,54 +64,56 @@ static FINLINE res_T add_side_to_stack (const struct senc2d_scene* scn, struct darray_side_id* stack, - struct segside* segsides, + struct segment_part* segments_part, const side_id_t side_id) { (void)scn; - ASSERT(scn && segsides && stack + seg_id_t seg = SEGSIDE_2_SEG(side_id); + enum side_flag sf = SEGSIDE_2_SIDE(side_id); + ASSERT(scn && segments_part && stack && side_id < SIDE_MAX__ && side_id < 2 * scn->nusegs); - ASSERT((darray_side_id_size_get(stack) > 128) - || !find_side_in_list(segsides, stack, side_id, FLAG_WAITING_STACK)); - segsides[side_id].list_id = FLAG_WAITING_STACK; + ASSERT(segments_part[seg].part[sf] == PART_NULL__); + segments_part[seg].part[sf] = PART_IN_STACK; return darray_side_id_push_back(stack, &side_id); } static side_id_t -get_side_not_in_connex_component - (const struct senc2d_scene* scn, +get_side_not_in_connex_part + (const seg_id_t last_side, const struct segside* segsides, - side_id_t* first_side_not_in_component, + const struct segment_part* segments_part, + side_id_t* first_side_not_in_part, const medium_id_t medium) { - side_id_t i; - const seg_id_t last_side - = darray_side_range_cdata_get(&scn->media_use)[medium].last; - ASSERT(scn && segsides && first_side_not_in_component); - i = *first_side_not_in_component; - while(i <= last_side - && (segsides[i].medium != medium - || segsides[i].list_id != FLAG_LIST_SIDE_LIST)) { - ++i; + ASSERT(segsides && segments_part && first_side_not_in_part); + { + side_id_t i = *first_side_not_in_part; + while(i <= last_side + && (segsides[i].medium != medium + || (segments_part[SEGSIDE_2_SEG(i)].part[SEGSIDE_2_SIDE(i)] + <= PART_MAX__))) { + ++i; + } + *first_side_not_in_part = i + 1; + if(i > last_side) return SIDE_NULL__; + return i; } - - *first_side_not_in_component = i+1; - if(i > last_side) return SIDE_NULL__; - return i; } static FINLINE side_id_t get_side_from_stack - (const struct segside* segsides, + (const struct segment_part* segments_part, struct darray_side_id* stack) { side_id_t id; size_t sz; - (void)segsides; - ASSERT(segsides && stack); + (void) segments_part; + ASSERT(segments_part && stack); sz = darray_side_id_size_get(stack); ASSERT(sz); id = darray_side_id_cdata_get(stack)[sz - 1]; - ASSERT(segsides[id].list_id == FLAG_WAITING_STACK); + ASSERT(segments_part[SEGSIDE_2_SEG(id)].part[SEGSIDE_2_SIDE(id)] + == PART_IN_STACK); darray_side_id_pop_back(stack); return id; } @@ -156,6 +138,22 @@ get_scn_position(const unsigned ivert, float pos[2], void* ctx) { f2_set_d2(pos, pt->vec); } +static component_id_t FINLINE +part2comp + (const struct darray_component_id* part2comp_table, + const part_id_t part) +{ + if(!part2comp_table) return part; + ASSERT(part < darray_component_id_size_get(part2comp_table)); + return darray_component_id_cdata_get(part2comp_table)[part]; +} + +struct filter_context { + const struct darray_segment_part* segments_part; + const struct darray_component_id* part2comp_table; + component_id_t* self_hit_component; +}; + static int self_hit_filter (const struct s2d_hit* hit, @@ -164,19 +162,26 @@ self_hit_filter void* ray_data, void* filter_data) { - const struct darray_segment_comp* segments_comp = filter_data; - const component_id_t* origin_component = ray_data; - const struct segment_comp* hit_seg_comp; + struct filter_context* context = ray_data; + const struct darray_segment_part* segments_part; + const struct darray_component_id* part2comp_table; + const component_id_t* origin_component; + const struct segment_part* hit_seg_part; enum side_id hit_side; + part_id_t hit_part; component_id_t hit_component; - (void)ray_org; (void)ray_dir; - ASSERT(hit && segments_comp && origin_component); - ASSERT(hit->prim.prim_id < darray_segment_comp_size_get(segments_comp)); - hit_seg_comp = darray_segment_comp_cdata_get(segments_comp) + (void)ray_org; (void)ray_dir; (void)filter_data; + ASSERT(hit && context && context); + segments_part = context->segments_part; + part2comp_table = context->part2comp_table; + origin_component = context->self_hit_component; + ASSERT(hit->prim.prim_id < darray_segment_part_size_get(segments_part)); + hit_seg_part = darray_segment_part_cdata_get(segments_part) + hit->prim.prim_id; hit_side = (hit->normal[1] > 0) ? SIDE_FRONT : SIDE_BACK; - hit_component = hit_seg_comp->component[hit_side]; + hit_part = hit_seg_part->part[hit_side]; + hit_component = part2comp(part2comp_table, hit_part); /* If self hit, distance should be small */ ASSERT(hit_component != *origin_component || hit->distance < 1e-6); @@ -184,14 +189,14 @@ self_hit_filter } static void -extract_connex_components +extract_connex_parts (struct senc2d_descriptor* desc, struct segside* segsides, - struct darray_ptr_component_descriptor* connex_components, + struct darray_ptr_part_descriptor* connex_parts, const struct darray_segment_tmp* segments_tmp_array, - struct darray_segment_comp* segments_comp, + struct darray_segment_part* segments_part_array, struct s2d_scene_view** s2d_view, - ATOMIC* component_count, + ATOMIC* part_count, /* Shared error status. * We accept to overwritte an error with a different error */ res_T* res) @@ -204,24 +209,25 @@ extract_connex_components struct darray_side_id stack; struct darray_side_id ids_of_sides_around_max_y_vertex; const union double2* positions; - size_t sz, ii; -#ifndef NDEBUG - seg_id_t s_; - component_id_t c; -#endif + const struct segment_tmp* segments_tmp; + struct segment_part* segments_part; + size_t sz; - ASSERT(segsides && desc && connex_components && segments_tmp_array - && s2d_view && component_count && res); + ASSERT(segsides && desc && connex_parts && segments_tmp_array + && segments_part_array && s2d_view && part_count && res); alloc = descriptor_get_allocator(desc); scn = desc->scene; positions = darray_position_cdata_get(&scn->vertices); + segments_tmp = darray_segment_tmp_cdata_get(segments_tmp_array); + segments_part = darray_segment_part_data_get(segments_part_array); darray_side_id_init(alloc, &stack); darray_side_id_init(alloc, &ids_of_sides_around_max_y_vertex); #ifndef NDEBUG #pragma omp single { - ASSERT(darray_ptr_component_descriptor_size_get(connex_components) == 0); + seg_id_t s_; + ASSERT(darray_ptr_part_descriptor_size_get(connex_parts) == 0); FOR_EACH(s_, 0, scn->nusegs) { const struct segment_in* seg_in = darray_segment_in_cdata_get(&scn->segments_in) + s_; @@ -237,24 +243,26 @@ extract_connex_components } /* Implicit barrier here */ #endif + /* We loop on sides to build parts of connex components. */ #pragma omp for schedule(dynamic) nowait for(mm = 0; mm < (int64_t)scn->nmeds; mm++) { /* Process all media */ const medium_id_t m = (medium_id_t)mm; - struct cc_descriptor* cc; + struct part_descriptor* part; /* Any not-already-used side is used as a starting point */ - side_id_t first_side_not_in_component; - double max_y_ny; + const struct side_range* media_use = + darray_side_range_cdata_get(&scn->media_use) + m; + side_id_t first_side_not_in_part = media_use->first; + const seg_id_t last_side = media_use->last; + ATOMIC id; if(*res != RES_OK) continue; - first_side_not_in_component - = darray_side_range_cdata_get(&scn->media_use)[m].first; - if(first_side_not_in_component == SIDE_NULL__) + if(first_side_not_in_part == SIDE_NULL__) continue; /* Unused medium */ - ASSERT(first_side_not_in_component < 2 * scn->nusegs); + ASSERT(first_side_not_in_part < 2 * scn->nusegs); ASSERT(darray_side_id_size_get(&stack) == 0); - for(;;) { /* Process all components for this medium */ - const side_id_t start_side_id = get_side_not_in_connex_component(scn, - segsides, &first_side_not_in_component, m); + for(;;) { /* Process all parts for this medium */ + const side_id_t start_side_id = get_side_not_in_connex_part + (last_side, segsides, segments_part, &first_side_not_in_part, m); side_id_t crt_side_id = start_side_id; side_id_t last_side_id = start_side_id; double max_y = -DBL_MAX; @@ -264,7 +272,6 @@ extract_connex_components if(*res != RES_OK) break; if(start_side_id == SIDE_NULL__) break; /* start_side_id=SIDE_NULL__ => done! */ - ASSERT(segsides[start_side_id].list_id == FLAG_LIST_SIDE_LIST); #ifndef NDEBUG { @@ -276,36 +283,36 @@ extract_connex_components } #endif - /* Create and init a new component */ - cc = MEM_ALLOC(alloc, sizeof(struct cc_descriptor)); - if(!cc) { + /* Create and init a new part */ + part = MEM_ALLOC(alloc, sizeof(struct part_descriptor)); + if(!part) { *res = RES_MEM_ERR; continue; } - cc_descriptor_init(alloc, cc); + part_descriptor_init(alloc, part); ASSERT(m == segsides[start_side_id].medium); - cc->cc_id = (component_id_t)(ATOMIC_INCR(component_count) - 1); - cc->medium = m; - cc->side_range.first = start_side_id; + id = ATOMIC_INCR(part_count) - 1; + ASSERT(id <= PART_MAX__); + part->part_id = (part_id_t)id; + part->side_range.first = start_side_id; + part->medium = m; - for(;;) { /* Process all sides of this component */ + for(;;) { /* Process all sides of this part */ int i; enum side_flag crt_side_flag = SEGSIDE_2_SIDE(crt_side_id); struct segside* crt_side = segsides + crt_side_id; const seg_id_t crt_seg_id = SEGSIDE_2_SEG(crt_side_id); const struct segment_in* seg_in = darray_segment_in_cdata_get(&scn->segments_in) + crt_seg_id; - struct segment_comp* seg_comp = - darray_segment_comp_data_get(segments_comp) + crt_seg_id; - const struct segment_tmp* const seg_tmp = - darray_segment_tmp_cdata_get(segments_tmp_array) + crt_seg_id; + struct segment_part* seg_part = segments_part + crt_seg_id; + const struct segment_tmp* const seg_tmp = segments_tmp + crt_seg_id; ASSERT(crt_seg_id < scn->nusegs); - ASSERT(segsides[crt_side_id].medium == m); + ASSERT(seg_in->medium[crt_side_flag] == part->medium); if(*res != RES_OK) break; /* Record Ymax information - * Keep track of the appropriate vertex of the connex component in order + * Keep track of the appropriate vertex of the part in order * to cast a ray at the component grouping step of the algorithm. * The most appropriate vertex is (the) one with the greater Y * coordinate. */ @@ -317,10 +324,10 @@ extract_connex_components /* Select one vertex with y == max_y */ if(max_y == positions[seg_in->vertice_id[0]].pos.y) { - cc->max_y_vrtx_id = seg_in->vertice_id[0]; + part->max_y_vrtx_id = seg_in->vertice_id[0]; } else { ASSERT(max_y == positions[seg_in->vertice_id[1]].pos.y); - cc->max_y_vrtx_id = seg_in->vertice_id[1]; + part->max_y_vrtx_id = seg_in->vertice_id[1]; } /* List of sides using the vertex */ *res = darray_side_id_push_back(&ids_of_sides_around_max_y_vertex, @@ -328,7 +335,7 @@ extract_connex_components } else if(max_y == seg_tmp->max_y) { /* Does this segment use the currently selected max_y vertex? */ FOR_EACH(i, 0, 2) { - if(cc->max_y_vrtx_id == seg_in->vertice_id[i]) { + if(part->max_y_vrtx_id == seg_in->vertice_id[i]) { /* List of sides using the vertex */ *res = darray_side_id_push_back(&ids_of_sides_around_max_y_vertex, &crt_side_id); @@ -338,130 +345,74 @@ extract_connex_components } if(*res != RES_OK) break; - /* Record crt_side both as component and segment level */ - cc->side_count++; - segsides[crt_side_id].list_id = FLAG_LIST_COMPONENT; - ASSERT(seg_comp->component[crt_side_flag] == COMPONENT_NULL__); - seg_comp->component[crt_side_flag] = cc->cc_id; -#ifndef NDEBUG - crt_side->member_of_cc = cc->cc_id; -#endif + /* Record crt_side both as part and segment level */ + part->side_count++; + ASSERT(seg_part->part[crt_side_flag] > PART_MAX__); + seg_part->part[crt_side_flag] = part->part_id; - /* Store neighbour sides in a waiting stack */ + /* Store neighbours' sides in a stack */ FOR_EACH(i, 0, 2) { side_id_t neighbour_id = crt_side->facing_side_id[i]; seg_id_t nbour_seg_id = SEGSIDE_2_SEG(neighbour_id); + seg_id_t nbour_side_id = SEGSIDE_2_SIDE(neighbour_id); + struct segment_part* nbour_part = segments_part + nbour_seg_id; const struct segside* neighbour = segsides + neighbour_id; - ASSERT(m == crt_side->medium); if(neighbour->medium != crt_side->medium) { - /* Found medium discontinuity! Model topology is broken. */ - const struct segment_in* segments_in - = darray_segment_in_cdata_get(&scn->segments_in); - log_err(scn->dev, - "Medium mismatch found between neighbour segments %lu %s" - " side and %u %s side.\n", - (unsigned long)segments_in[crt_seg_id].global_id, - SEGSIDE_IS_FRONT(crt_side_id) ? "front" : "back", - segments_in[nbour_seg_id].global_id, - SEGSIDE_IS_FRONT(neighbour_id) ? "front" : "back"); - log_err(scn->dev, - "Segment %lu:\n (%g %g) (%g %g))\n", - (unsigned long)segments_in[crt_seg_id].global_id, - SPLIT2(positions[segments_in[crt_seg_id].vertice_id[0]].vec), - SPLIT2(positions[segments_in[crt_seg_id].vertice_id[1]].vec)); - log_err(scn->dev, - "Segment %lu:\n (%g %g) (%g %g)\n", - (unsigned long)segments_in[nbour_seg_id].global_id, - SPLIT2(positions[segments_in[nbour_seg_id].vertice_id[0]].vec), - SPLIT2(positions[segments_in[nbour_seg_id].vertice_id[1]].vec)); - log_err(desc->scene->dev, "Media: %lu VS %lu\n", - (unsigned long)neighbour->medium, (unsigned long)crt_side->medium); - *res = RES_BAD_ARG; - break; + /* Not the same medium: will be included in another computation. + * If the id of the part including it is already known + * record it for a further merge. In the opposite, the other + * computation will reach the very same point at a point in time + * when it will have the information: it will then do the job. */ + if(nbour_part->part[nbour_side_id] <= PART_MAX__) { + res_T tmp_res; + char one = 1; + tmp_res = htable_part_id_set(&part->parts_to_merge_with, + &nbour_part->part[nbour_side_id], &one); + if(tmp_res != RES_OK) *res = tmp_res; + if(*res != RES_OK) break; + } + continue; } - if(neighbour->list_id == FLAG_LIST_COMPONENT) { - /* Already processed */ + if(nbour_part->part[nbour_side_id] != PART_NULL__) { #ifndef NDEBUG - ASSERT(neighbour->member_of_cc == cc->cc_id); + /* If already affected must be to the same part */ + ASSERT(nbour_part->part[nbour_side_id] == PART_IN_STACK + || nbour_part->part[nbour_side_id] == part->part_id); #endif - continue; - } - if(neighbour->list_id == FLAG_WAITING_STACK) { continue; /* Already processed */ } - *res = add_side_to_stack(scn, &stack, segsides, neighbour_id); + *res = add_side_to_stack(scn, &stack, segments_part, neighbour_id); if(*res != RES_OK) break; } if(*res != RES_OK) break; if(darray_side_id_size_get(&stack) == 0) - break; /* Empty stack => connex component is done! */ - crt_side_id = get_side_from_stack(segsides, &stack); + break; /* Empty stack => part is done! */ + crt_side_id = get_side_from_stack(segments_part, &stack); last_side_id = MMAX(last_side_id, crt_side_id); } if(*res != RES_OK) { - MEM_RM(alloc, cc); - cc = NULL; + MEM_RM(alloc, part); + part = NULL; break; } - /* Keep track of this new connex component */ - cc->side_range.last = last_side_id; - - /* Compute the normal at the max_y vertex. */ - max_y_ny = 0; - sz = darray_side_id_size_get(&ids_of_sides_around_max_y_vertex); - FOR_EACH(ii, 0, sz) { - const side_id_t side_id = - darray_side_id_cdata_get(&ids_of_sides_around_max_y_vertex)[ii]; - const seg_id_t seg_id = SEGSIDE_2_SEG(side_id); - const struct segment_in* seg_in = - darray_segment_in_cdata_get(&scn->segments_in) + seg_id; - struct segment_comp* seg_comp = - darray_segment_comp_data_get(segments_comp) + seg_id; - const union double2* vertices = - darray_position_cdata_get(&scn->vertices); - double edge[2], normal[2], norm; - - /* To garanty that segments with 2 sides in the component total to 0 - * regardless of numeric accuracy, we need to prevent them to - * contribute (remember than x + y - x - y can be non-zero). */ - ASSERT(seg_comp->component[SIDE_FRONT] == cc->cc_id - || seg_comp->component[SIDE_BACK] == cc->cc_id); - if(seg_comp->component[SIDE_FRONT] == seg_comp->component[SIDE_BACK]) - continue; - - d2_sub(edge, vertices[seg_in->vertice_id[1]].vec, - vertices[seg_in->vertice_id[0]].vec); - d2(normal, edge[1], -edge[0]); - norm = d2_normalize(normal, normal); - ASSERT(norm); (void)norm; - - /* Geometrical normal points toward the back side */ - if(SEGSIDE_IS_FRONT(side_id)) { - max_y_ny -= normal[1]; - } else { - max_y_ny += normal[1]; - } - } - if(*res != RES_OK) break; - cc->is_outer_border = (max_y_ny < 0); + part->side_range.last = last_side_id; - /* Need to synchronize connex_components growth as this global structure + /* Need to synchronize connex_parts growth as this global structure * is accessed by multipe threads */ #pragma omp critical { - struct cc_descriptor** components; - sz = darray_ptr_component_descriptor_size_get(connex_components); - if(sz <= cc->cc_id) { - res_T tmp_res = darray_ptr_component_descriptor_resize(connex_components, - 1 + cc->cc_id); + struct part_descriptor** parts; + sz = darray_ptr_part_descriptor_size_get(connex_parts); + if(sz <= part->part_id) { + res_T tmp_res = darray_ptr_part_descriptor_resize(connex_parts, + 1 + part->part_id); if(tmp_res != RES_OK) *res = tmp_res; } if(*res == RES_OK) { /* Don't set the pointer before resize as this can lead to move data */ - components = - darray_ptr_component_descriptor_data_get(connex_components); - ASSERT(components[cc->cc_id] == NULL); - components[cc->cc_id] = cc; + parts = darray_ptr_part_descriptor_data_get(connex_parts); + ASSERT(parts[part->part_id] == NULL); + parts[part->part_id] = part; } } } @@ -494,8 +445,7 @@ extract_connex_components OK2(s2d_line_segments_setup_indexed_vertices(s2d_shp, (unsigned)desc->scene->nusegs, get_scn_indices, (unsigned)desc->scene->nuverts, &attribs, 1, desc->scene)); - s2d_line_segments_set_hit_filter_function(s2d_shp, self_hit_filter, - segments_comp); + s2d_line_segments_set_hit_filter_function(s2d_shp, self_hit_filter, NULL); OK2(s2d_scene_attach_shape(s2d_scn, s2d_shp)); OK2(s2d_scene_view_create(s2d_scn, S2D_TRACE, s2d_view)); tmp_error: @@ -511,32 +461,248 @@ extract_connex_components if(*res != RES_OK) return; #pragma omp single { - ASSERT(ATOMIC_GET(component_count) == - (int)darray_ptr_component_descriptor_size_get(connex_components)); + seg_id_t s_; + part_id_t p; + ASSERT(ATOMIC_GET(part_count) == + (int)darray_ptr_part_descriptor_size_get(connex_parts)); FOR_EACH(s_, 0, scn->nusegs) { - struct segment_comp* seg_comp = - darray_segment_comp_data_get(segments_comp) + s_; - ASSERT(seg_comp->component[SIDE_FRONT] != COMPONENT_NULL__); - ASSERT(seg_comp->component[SIDE_BACK] != COMPONENT_NULL__); + struct segment_part* seg_part = segments_part + s_; + ASSERT(seg_part->part[SIDE_FRONT] != PART_NULL__); + ASSERT(seg_part->part[SIDE_BACK] != PART_NULL__); } - FOR_EACH(c, 0, ATOMIC_GET(component_count)) { - struct cc_descriptor** components = - darray_ptr_component_descriptor_data_get(connex_components); - ASSERT(components[c] != NULL && - components[c]->cc_id == c); + FOR_EACH(p, 0, ATOMIC_GET(part_count)) { + struct part_descriptor** parts = + darray_ptr_part_descriptor_data_get(connex_parts); + ASSERT(parts[p] != NULL && parts[p]->part_id == p); } - ASSERT(desc->segment_count - == darray_segment_comp_size_get(segments_comp)); + ASSERT(desc->segment_count == scn->nusegs); } /* Implicit barrier here */ #endif } static void +merge_connex_parts + (struct senc2d_descriptor* desc, + const struct darray_segment_part* segments_part, + const struct darray_ptr_part_descriptor* connex_parts, + struct darray_ptr_component_descriptor* connex_components, + struct darray_component_id* part2comp_table, + ATOMIC* component_count, + /* Shared error status. */ + res_T* res) +{ + /* This function must be called from an omp single block. + * Not suitable for use by multiple threads. */ + struct part_descriptor* const* parts; + const union double2* vertices; + struct mem_allocator* alloc; + component_id_t* p2c; + char one = 1; + size_t tmp; + part_id_t part_count; + int64_t ppp; + ASSERT(desc && segments_part && connex_parts && connex_components + && part2comp_table && component_count && res); + + alloc = descriptor_get_allocator(desc); + parts = darray_ptr_part_descriptor_cdata_get(connex_parts); + tmp = darray_ptr_part_descriptor_size_get(connex_parts); + vertices = darray_position_cdata_get(&desc->scene->vertices); + p2c = darray_component_id_data_get(part2comp_table); + ASSERT(tmp <= PART_MAX__); + part_count = (component_id_t)tmp; + + /* Propagate merge information accross parts + * The goal is to have all part aware of any part with a higher id */ + #pragma omp single + for (ppp = 0; ppp < (int64_t) part_count; ppp++) { + part_id_t pid = (part_id_t)ppp; + struct part_descriptor* part = parts[pid]; + struct htable_part_id_iterator it, end; + res_T tmp_res = RES_OK; + if(*res != RES_OK) continue; + htable_part_id_begin(&part->parts_to_merge_with, &it); + htable_part_id_end(&part->parts_to_merge_with, &end); + while(!htable_part_id_iterator_eq(&it, &end)) { + part_id_t* did = htable_part_id_iterator_key_get(&it); + struct part_descriptor* d; + ASSERT(did && *did < part_count && *did != part->part_id); + htable_part_id_iterator_next(&it); + if(*did > part->part_id) { + parts[*did]->not_head_of_group = 1; + continue; + } + part->not_head_of_group = 1; + + d = parts[*did]; + tmp_res = + htable_part_id_set(&d->parts_to_merge_with, &part->part_id, &one); + if(tmp_res != RES_OK) { + *res = tmp_res; + break; + } + } + /* Add self */ + tmp_res = + htable_part_id_set(&part->parts_to_merge_with, &part->part_id, &one); + if(tmp_res != RES_OK) { + *res = tmp_res; + continue; + } + } /* Implicit barrier here */ + + /* Merge the parts to create components */ + #pragma omp for + for(ppp = 0; ppp < (int64_t)part_count; ppp++) { + part_id_t pid = (part_id_t)ppp; + struct cc_descriptor* cc = NULL; + struct part_descriptor* part = parts[pid]; + struct htable_part_id_iterator beg, it, end; + double max_y = -DBL_MAX; + vrtx_id_t max_y_vrtx_id = VRTX_NULL__; + double edge[2], normal[2], norm, max_y_ny = 0; + double comp_max_y; + ATOMIC id; + res_T tmp_res = RES_OK; + + if(*res != RES_OK) continue; + if(part->not_head_of_group) continue; + + /* Create the component to hold merged data */ + cc = MEM_ALLOC(alloc, sizeof(struct cc_descriptor)); + if(!cc) { + *res = RES_MEM_ERR; + continue; + } + + cc_descriptor_init(alloc, cc); + id = ATOMIC_INCR(component_count) - 1; + ASSERT(id <= COMPONENT_MAX__); + cc->cc_id = (component_id_t)id; + + /* Need to synchronize connex_components growth as this global structure + * is accessed by multipe threads */ + #pragma omp critical + { + struct cc_descriptor** components; + size_t sz = darray_ptr_component_descriptor_size_get(connex_components); + if(sz <= cc->cc_id) { + res_T r = darray_ptr_component_descriptor_resize(connex_components, + 1 + cc->cc_id); + if(r != RES_OK) *res = r; + } + if(*res == RES_OK) { + /* Don't set the pointer before resize as this can lead to move data */ + components = darray_ptr_component_descriptor_data_get(connex_components); + ASSERT(components[cc->cc_id] == NULL); + components[cc->cc_id] = cc; + } + } + if(*res != RES_OK) goto tmp_error; + + /* Find max y accross parts and process counts */ + htable_part_id_begin(&part->parts_to_merge_with, &beg); + htable_part_id_end(&part->parts_to_merge_with, &end); + it = beg; + while(!htable_part_id_iterator_eq(&it, &end)) { + part_id_t* did = htable_part_id_iterator_key_get(&it); + struct part_descriptor* d; + htable_part_id_iterator_next(&it); + + ASSERT(did); + d = parts[*did]; + + p2c[*did] = cc->cc_id; + + cc->side_count += d->side_count; + cc->side_range.first = MMIN(cc->side_range.first, d->side_range.first); + cc->side_range.last = MMAX(cc->side_range.last, d->side_range.last); + tmp_res = htable_media_set(&cc->media, &d->medium, &one); + if(tmp_res != RES_OK) { + *res = tmp_res; + goto tmp_error; + } + + comp_max_y = vertices[d->max_y_vrtx_id].pos.y; + if(max_y < comp_max_y) { + max_y = comp_max_y; + max_y_vrtx_id = d->max_y_vrtx_id; + } + } + ASSERT(max_y_vrtx_id != VRTX_NULL__); + cc->max_y_vrtx_id = max_y_vrtx_id; + + /* Process the parts with max_y as max y value to compute local normal + * at max_y_vrtx_id vertex from sides sharing it */ + it = beg; + while(!htable_part_id_iterator_eq(&it, &end)) { + part_id_t* did = htable_part_id_iterator_key_get(&it); + struct part_descriptor* d = parts[*did]; + side_id_t side_id; + htable_part_id_iterator_next(&it); + + comp_max_y = vertices[d->max_y_vrtx_id].pos.y; + if(max_y != comp_max_y) continue; + FOR_EACH(side_id, d->side_range.first, d->side_range.last) { + const seg_id_t seg_id = SEGSIDE_2_SEG(side_id); + enum side_flag side_flag = SEGSIDE_2_SIDE(side_id); + const struct segment_in* seg_in = + darray_segment_in_cdata_get(&desc->scene->segments_in) + seg_id; + const struct segment_part* seg_part = + darray_segment_part_cdata_get(segments_part) + seg_id; + + /* To garanty that segments with 2 sides in the component total to 0 + * regardless of numeric accuracy, we need to prevent them to + * contribute (remember than x + y - x - y can be non-zero). */ + if(part2comp(part2comp_table, seg_part->part[SIDE_FRONT]) == + part2comp(part2comp_table, seg_part->part[SIDE_BACK])) + continue; + /* Is this side a member of the component? */ + if(seg_part->part[side_flag] != d->part_id) continue; + /* Does this segment use the currently selected max_y vertex? */ + if(max_y_vrtx_id != seg_in->vertice_id[0] + && max_y_vrtx_id != seg_in->vertice_id[1]) + continue; + + /* This segments' normal contributes to the local normal */ + d2_sub(edge, vertices[seg_in->vertice_id[1]].vec, + vertices[seg_in->vertice_id[0]].vec); + d2(normal, edge[1], -edge[0]); + norm = d2_normalize(normal, normal); + ASSERT(norm); (void)norm; + + /* Geometrical normal points toward the back side */ + if(SEGSIDE_IS_FRONT(side_id)) { + max_y_ny -= normal[1]; + } else { + max_y_ny += normal[1]; + } + } + } + + /* All this computation was to set this flag! + * Note that it cannot be computed from the flags of the parts of the + * component as we need to filter segments whom 2 sides belongs to + * the component (to ensure n + -n = 0) but each side was member of 2 + * different parts. */ + cc->is_outer_border = (max_y_ny < 0); + continue; + tmp_error: + MEM_RM(alloc, cc); + *res = tmp_res; + continue; + } + + return; +} + +static void group_connex_components (struct senc2d_descriptor* desc, struct segside* segsides, - struct darray_segment_comp* segments_comp, + struct darray_segment_part* segments_part, struct darray_ptr_component_descriptor* connex_components, + struct darray_component_id* part2comp_table, struct s2d_scene_view* s2d_view, ATOMIC* next_enclosure_id, ATOMIC* infinity_first_cc, @@ -551,9 +717,11 @@ group_connex_components size_t tmp; component_id_t cc_count; int64_t ccc; + struct filter_context context; + (void)segsides; - ASSERT(desc && segsides && segments_comp && connex_components + ASSERT(desc && segsides && segments_part && connex_components && part2comp_table && s2d_view && next_enclosure_id && infinity_first_cc && res); ASSERT(desc->enclosures_count == 1); @@ -562,6 +730,8 @@ group_connex_components ASSERT(tmp <= COMPONENT_MAX__); cc_count = (component_id_t)tmp; positions = darray_position_cdata_get(&desc->scene->vertices); + context.segments_part = segments_part; + context.part2comp_table = part2comp_table; /* Cast rays to find links between connex components */ #pragma omp for @@ -574,26 +744,29 @@ group_connex_components const float range[2] = { 0, FLT_MAX }; struct cc_descriptor* const cc = descriptors[c]; component_id_t self_hit_component = cc->cc_id; - const double* max_vrtx = positions[cc->max_y_vrtx_id].vec; + const double* max_vrtx; if(*res != RES_OK) continue; ASSERT(cc->cc_id == c); ASSERT(cc->cc_group_root == CC_GROUP_ID_NONE); + ASSERT(cc->max_y_vrtx_id < desc->scene->nverts); + max_vrtx = positions[cc->max_y_vrtx_id].vec; if(cc->is_outer_border) { - int64_t id; + ATOMIC id; /* Don't need to cast a ray */ cc->cc_group_root = cc->cc_id; /* New group with self as root */ id = ATOMIC_INCR(next_enclosure_id) - 1; - ASSERT(id < ENCLOSURE_MAX__); + ASSERT(id <= ENCLOSURE_MAX__); cc->enclosure_id = (enclosure_id_t)id; continue; } f2_set_d2(origin, max_vrtx); /* Self-hit data: self hit if hit this component "on the other side" */ + context.self_hit_component = &self_hit_component; tmp_res = s2d_scene_view_trace_ray(s2d_view, origin, dir, range, - &self_hit_component, &hit); + &context, &hit); if(tmp_res != RES_OK) { *res = tmp_res; continue; @@ -606,77 +779,23 @@ group_connex_components /* Keep track of the first component facing infinity */ ATOMIC_CAS(infinity_first_cc, (ATOMIC)cc, (ATOMIC)NULL); inf_first_cc = (struct cc_descriptor*)ATOMIC_GET(infinity_first_cc); - if(inf_first_cc->medium != cc->medium) { - /* Medium mismatch! Model topology is broken. */ - const double* infinity_max_vrtx = - positions[inf_first_cc->max_y_vrtx_id].vec; - log_err(desc->scene->dev, - "Medium mismatch found between vertex %lu and vertex %lu," - " both facing infinity.\n", - (unsigned long) inf_first_cc->max_y_vrtx_id, - (unsigned long)cc->max_y_vrtx_id); - log_err(desc->scene->dev, - "Vertex %lu: (%g %g)\n", - (unsigned long) inf_first_cc->max_y_vrtx_id, - SPLIT2(infinity_max_vrtx)); - log_err(desc->scene->dev, - "Vertex %lu: (%g %g)\n", - (unsigned long)cc->max_y_vrtx_id, - SPLIT2(max_vrtx)); - log_err(desc->scene->dev, "Media: %lu VS %lu\n", - (unsigned long) inf_first_cc->medium, (unsigned long)cc->medium); - *res = RES_BAD_ARG; - } - /* Same medium as previous members of the group: OK */ } else { /* If hit, group this component */ const seg_id_t hit_seg_id = (seg_id_t)hit.prim.prim_id; - const struct segment_in* hit_seg_in = - darray_segment_in_cdata_get(&desc->scene->segments_in) + hit_seg_id; - const struct segment_comp* hit_seg_comp = - darray_segment_comp_cdata_get(segments_comp) + hit_seg_id; + /* const struct segment_in* hit_seg_in = + darray_segment_in_cdata_get(&desc->scene->segments_in) + hit_seg_id; */ + const struct segment_part* hit_seg_part = + darray_segment_part_cdata_get(segments_part) + hit_seg_id; enum side_id hit_side = (hit.normal[1] > 0) ? SIDE_FRONT : SIDE_BACK; - const side_id_t hit_side_id = SEGIDxSIDE_2_SEGSIDE(hit_seg_id, hit_side); + /* const side_id_t hit_side_id + = SEGIDxSIDE_2_SEGSIDE(hit_seg_id, hit_side); */ ASSERT(hit_seg_id < desc->scene->nusegs); /* Not really the root until following links */ - cc->cc_group_root = hit_seg_comp->component[hit_side]; -#ifndef NDEBUG - { - const struct cc_descriptor* hit_desc; - ASSERT(cc->cc_group_root - < darray_ptr_component_descriptor_size_get(connex_components)); - hit_desc = *(darray_ptr_component_descriptor_cdata_get(connex_components) - + cc->cc_group_root); - ASSERT(hit_desc->medium == hit_seg_in->medium[hit_side]); - } -#endif - if(hit_seg_in->medium[hit_side] != cc->medium) { - /* Medium mismatch! Model topology is broken. */ - const seg_id_t seg = SEGSIDE_2_SEG(hit_side_id); - const struct segment_in* segments_in - = darray_segment_in_cdata_get(&desc->scene->segments_in); - log_err(desc->scene->dev, - "Medium mismatch found between vertex %lu and segment" - " %lu %s side facing each other.\n", - (unsigned long)cc->max_y_vrtx_id, - (unsigned long)segments_in[seg].global_id, - SEGSIDE_IS_FRONT(hit_side_id) ? "front" : "back"); - log_err(desc->scene->dev, - "Vertex %lu: (%g %g)\n", - (unsigned long)cc->max_y_vrtx_id, - SPLIT2(max_vrtx)); - log_err(desc->scene->dev, - "Segment %lu:\n (%g %g) (%g %g)\n", - (unsigned long)segments_in[seg].global_id, - SPLIT2(positions[segments_in[seg].vertice_id[0]].vec), - SPLIT2(positions[segments_in[seg].vertice_id[1]].vec)); - log_err(desc->scene->dev, "Media: %lu VS %lu\n", - (unsigned long)cc->medium, - (unsigned long)hit_seg_in->medium[hit_side]); - *res = RES_BAD_ARG; - } + cc->cc_group_root = + part2comp(part2comp_table, hit_seg_part->part[hit_side]); + ASSERT(cc->cc_group_root < cc_count); } } /* Implicit barrier here */ @@ -705,7 +824,6 @@ group_connex_components } ASSERT(other_desc->cc_group_root != CC_GROUP_ROOT_NONE); ASSERT(other_desc->enclosure_id != CC_GROUP_ID_NONE); - ASSERT(cc->medium == other_desc->medium); cc->cc_group_root = other_desc->cc_group_root; cc->enclosure_id = other_desc->enclosure_id; ++enclosures[cc->enclosure_id].cc_count; @@ -716,6 +834,12 @@ group_connex_components enclosures[cc->enclosure_id].side_range.last = MMAX(enclosures[cc->enclosure_id].side_range.last, cc->side_range.last); enclosures[cc->enclosure_id].side_count += cc->side_count; + tmp_res = htable_media_merge( + &enclosures[cc->enclosure_id].tmp_enclosed_media, &cc->media); + if(tmp_res != RES_OK) { + *res = tmp_res; + break; + } } } } @@ -874,8 +998,6 @@ collect_and_link_neighbours p_ccw_side->medium = segments_in[ccw_id].medium[ccw_side]; ASSERT(p_crt_side->medium < scn->nmeds); ASSERT(p_ccw_side->medium < scn->nmeds); - p_crt_side->list_id = FLAG_LIST_SIDE_LIST; - p_ccw_side->list_id = FLAG_LIST_SIDE_LIST; } } /* Threads are allowed to return whitout sync. */ @@ -885,7 +1007,8 @@ static void build_result (struct senc2d_descriptor* desc, const struct darray_ptr_component_descriptor* connex_components, - const struct darray_segment_comp* segments_comp_array, + struct darray_component_id* part2comp_table, + const struct darray_segment_part* segments_part_array, /* Shared error status. * We accept to overwritte an error with a different error */ res_T* res) @@ -897,20 +1020,20 @@ build_result struct enclosure_data* enclosures; const struct segment_in* segments_in; struct segment_enc* segments_enc; - const struct segment_comp* segments_comp; + const struct segment_part* segments_part; struct htable_vrtx_id vtable; int64_t sg; int64_t ee; - ASSERT(desc && connex_components && segments_comp_array && res); + ASSERT(desc && connex_components && segments_part_array && res); alloc = descriptor_get_allocator(desc); ASSERT(darray_ptr_component_descriptor_size_get(connex_components) - < COMPONENT_MAX__); + <= COMPONENT_MAX__); cc_descriptors = darray_ptr_component_descriptor_cdata_get(connex_components); enclosures = darray_enclosure_data_get(&desc->enclosures); segments_in = darray_segment_in_cdata_get(&desc->scene->segments_in); - segments_comp = darray_segment_comp_cdata_get(segments_comp_array); + segments_part = darray_segment_part_cdata_get(segments_part_array); #pragma omp single { res_T tmp_res = @@ -924,8 +1047,10 @@ build_result #pragma omp for for(sg = 0; sg < (int64_t)desc->scene->nusegs; sg++) { seg_id_t s = (seg_id_t)sg; - const component_id_t cf_id = segments_comp[s].component[SIDE_FRONT]; - const component_id_t cb_id = segments_comp[s].component[SIDE_BACK]; + const part_id_t pf_id = segments_part[s].part[SIDE_FRONT]; + const part_id_t pb_id = segments_part[s].part[SIDE_BACK]; + const component_id_t cf_id = part2comp(part2comp_table, pf_id); + const component_id_t cb_id = part2comp(part2comp_table, pb_id); const struct cc_descriptor* cf = cc_descriptors[cf_id]; const struct cc_descriptor* cb = cc_descriptors[cb_id]; const enclosure_id_t ef_id = cf->enclosure_id; @@ -946,33 +1071,45 @@ build_result for(ee = 0; ee < (int64_t)desc->enclosures_count; ee++) { const enclosure_id_t e = (enclosure_id_t)ee; struct enclosure_data* enc = enclosures + e; - const struct cc_descriptor* current = cc_descriptors[enc->first_component]; - struct darray_enc_id* enc_ids_by_medium; seg_id_t fst_idx = 0; seg_id_t sgd_idx = enc->side_count; seg_id_t s; + medium_id_t m; res_T tmp_res = RES_OK; ASSERT(enc->first_component < darray_ptr_component_descriptor_size_get(connex_components)); - ASSERT(current->cc_id == enc->first_component); + ASSERT(cc_descriptors[enc->first_component]->cc_id + == enc->first_component); if(*res != RES_OK) continue; ASSERT(e <= UINT_MAX); enc->header.enclosure_id = (unsigned)e; /* Back to API type */ - ASSERT(current->enclosure_id == enc->header.enclosure_id); + ASSERT(cc_descriptors[enc->first_component]->enclosure_id + == enc->header.enclosure_id); enc->header.is_infinite = (e == 0); - enc->header.enclosed_medium - = (unsigned)current->medium; /* Back to API type */ - ASSERT(enc->header.enclosed_medium < desc->scene->nmeds); - enc_ids_by_medium = - darray_enc_ids_array_data_get(&desc->enc_ids_array_by_medium) - + current->medium; - #pragma omp critical - { - tmp_res = darray_enc_id_push_back(enc_ids_by_medium, &e); + ASSERT(htable_media_size_get(&enc->tmp_enclosed_media) <= UINT_MAX); + enc->header.enclosed_media_count + = (unsigned)htable_media_size_get(&enc->tmp_enclosed_media); + ASSERT(enc->header.enclosed_media_count <= desc->scene->nmeds); + OK2(htable_media_to_darray_media + (&enc->enclosed_media, &enc->tmp_enclosed_media)); + htable_media_purge(&enc->tmp_enclosed_media); + + /* Add this enclosure in relevant by-medium lists */ + FOR_EACH(m, 0, enc->header.enclosed_media_count) { + medium_id_t medium = darray_media_data_get(&enc->enclosed_media)[m]; + struct darray_enc_id* enc_ids_by_medium = + darray_enc_ids_array_data_get(&desc->enc_ids_array_by_medium) + medium; + #pragma omp critical + { + tmp_res = darray_enc_id_push_back(enc_ids_by_medium, &e); + } + if(tmp_res != RES_OK) { + *res = tmp_res; + break; + } } - if(tmp_res != RES_OK) *res = tmp_res; if(*res != RES_OK) continue; /* Build side and vertex lists. */ @@ -1057,11 +1194,15 @@ senc2d_scene_analyze char segments_tmp_initialized = 0; /* Array of connex components. * They are refered to by arrays of ids. */ + struct darray_ptr_part_descriptor connex_parts; + char connex_parts_initialized = 0; + struct darray_component_id part2comp_table; + char part2comp_table_initialized = 0; struct darray_ptr_component_descriptor connex_components; char connex_components_initialized = 0; - /* Store by-segment components */ - struct darray_segment_comp segments_comp; - char segments_comp_initialized = 0; + /* Store by-segment parts */ + struct darray_segment_part segments_part; + char segments_part_initialized = 0; /* Array of vertices (segment ends). */ struct segside* segsides = NULL; struct s2d_scene_view* s2d_view = NULL; @@ -1069,12 +1210,13 @@ senc2d_scene_analyze struct darray_neighbourhood neighbourhood_by_vertex; char neighbourhood_by_vertex_initialized = 0; /* Atomic counters to share beetwen threads */ + ATOMIC part_count = 0; ATOMIC component_count = 0; ATOMIC next_enclosure_id = 1; /* Used as args to have shared vars between threads in functions */ static_assert(sizeof(intptr_t) == sizeof(ATOMIC), "Cannot use ATOMIC to store pointers"); - ATOMIC infinity_first_cc = (ATOMIC) NULL; + ATOMIC infinity_first_cc = (ATOMIC)NULL; res_T res = RES_OK; res_T res2 = RES_OK; @@ -1102,7 +1244,7 @@ senc2d_scene_analyze /* We create a neighbourhood for every single unique vertex, * regardless the fact it is used by a segment. - * This allows threads to use these neighbourhoods whithout syn. */ + * This allows threads to use these neighbourhoods whithout sync. */ darray_neighbourhood_init(scn->dev->allocator, &neighbourhood_by_vertex); neighbourhood_by_vertex_initialized = 1; OK(darray_neighbourhood_resize(&neighbourhood_by_vertex, scn->nuverts)); @@ -1123,15 +1265,15 @@ senc2d_scene_analyze #pragma omp single { res_T tmp_res = RES_OK; - darray_ptr_component_descriptor_init(scn->dev->allocator, - &connex_components); - connex_components_initialized = 1; + + darray_segment_part_init(scn->dev->allocator, &segments_part); + segments_part_initialized = 1; + OK2(darray_segment_part_resize(&segments_part, scn->nusegs)); + darray_ptr_part_descriptor_init(scn->dev->allocator, + &connex_parts); + connex_parts_initialized = 1; /* Just a hint; to limit contention */ - OK2(darray_ptr_component_descriptor_reserve(&connex_components, - 2 * scn->nmeds)); - darray_segment_comp_init(scn->dev->allocator, &segments_comp); - segments_comp_initialized = 1; - OK2(darray_segment_comp_resize(&segments_comp, scn->nusegs)); + OK2(darray_ptr_part_descriptor_reserve(&connex_parts, 2 * scn->nmeds)); tmp_error: if(tmp_res != RES_OK) res2 = tmp_res; } @@ -1159,8 +1301,8 @@ senc2d_scene_analyze } /* No barrier here */ /* Step 2: extract segment connex components */ - extract_connex_components(desc, segsides, &connex_components, - &segments_tmp, &segments_comp, &s2d_view, &component_count, &res); + extract_connex_parts(desc, segsides, &connex_parts, &segments_tmp, + &segments_part, &s2d_view, &part_count, &res); /* No barrier at the end of step 2: data used in step 2 cannot be * released / data produced by step 2 cannot be used * until next sync point */ @@ -1177,19 +1319,51 @@ senc2d_scene_analyze goto error_; } - /* One thread releases some data before going to step 3, - * the others go to step 3 without sync */ - #pragma omp single nowait + #pragma omp sections { - darray_segment_tmp_release(&segments_tmp); - segments_tmp_initialized = 0; - } /* No barrier here */ + /* One thread releases some data */ + #pragma omp section + { + darray_segment_tmp_release(&segments_tmp); + segments_tmp_initialized = 0; + } + /* Another allocates */ + #pragma omp section + { + res_T tmp_res = RES_OK; + component_id_t* data; + size_t parts_count = darray_ptr_part_descriptor_size_get(&connex_parts); + size_t i; + + darray_component_id_init(scn->dev->allocator, &part2comp_table); + part2comp_table_initialized = 1; + tmp_res = darray_component_id_resize(&part2comp_table, parts_count); + if(tmp_res != RES_OK) goto tmp2_error; + data = darray_component_id_data_get(&part2comp_table); + FOR_EACH(i, 0, parts_count) data[i] = PART_NULL__; + darray_ptr_component_descriptor_init(scn->dev->allocator, + &connex_components); + connex_components_initialized = 1; + /* Just a starting point */ + tmp_res = darray_ptr_component_descriptor_reserve(&connex_components, + 2 * scn->nmeds); + tmp2_error: + if(tmp_res != RES_OK) res2 = tmp_res; + } + } + /* Implicit barrier here */ + + /* Step 3: merge parts */ + merge_connex_parts(desc, &segments_part, &connex_parts, + &connex_components, &part2comp_table, &component_count, &res); + /* Barrier at the end of step 3: data used in step 4 can be released / + * data produced by step 4 can be used */ - /* Step 3: group components */ - group_connex_components(desc, segsides, &segments_comp, &connex_components, - s2d_view, &next_enclosure_id, &infinity_first_cc, &res); - /* Barrier at the end of step 3: data used in step 3 can be released / - * data produced by step 3 can be used */ + /* Step 4: group components */ + group_connex_components(desc, segsides, &segments_part, &connex_components, + &part2comp_table, s2d_view, &next_enclosure_id, &infinity_first_cc, &res); + /* Barrier at the end of step 4: data used in step 4 can be released / + * data produced by step 4 can be used */ if(res != RES_OK) { #pragma omp single nowait @@ -1200,22 +1374,24 @@ senc2d_scene_analyze goto error_; } - /* One thread releases some data before going to step 4, - * the others go to step 4 without sync */ + /* One thread releases some data before going to step 5, + * the others go to step 5 without sync */ #pragma omp single nowait { + custom_darray_ptr_part_descriptor_release(&connex_parts); + connex_parts_initialized = 0; if(s2d_view) S2D(scene_view_ref_put(s2d_view)); s2d_view = NULL; } /* No barrier here */ - /* Step 4: Build result */ - build_result(desc, &connex_components, &segments_comp, &res); - /* No barrier at the end of step 4: data used in step 4 cannot be - * released / data produced by step 4 cannot be used + /* Step 5: Build result */ + build_result(desc, &connex_components, &part2comp_table, &segments_part, &res); + /* No barrier at the end of step 5: data used in step 5 cannot be + * released / data produced by step 5 cannot be used * until next sync point */ #pragma omp barrier - /* Constraints on step 4 data are now met */ + /* Constraints on step 5 data are now met */ if(res != RES_OK) { #pragma omp single nowait @@ -1230,14 +1406,18 @@ senc2d_scene_analyze { #pragma omp section { - ASSERT(connex_components_initialized); custom_darray_ptr_component_descriptor_release(&connex_components); connex_components_initialized = 0; } #pragma omp section { - darray_segment_comp_release(&segments_comp); - segments_comp_initialized = 0; + darray_segment_part_release(&segments_part); + segments_part_initialized = 0; + } +#pragma omp section + { + darray_component_id_release(&part2comp_table); + part2comp_table_initialized = 0; } } /* No barrier here */ error_: @@ -1246,13 +1426,17 @@ error_: if(res != RES_OK) goto error; exit: + if(connex_parts_initialized) + custom_darray_ptr_part_descriptor_release(&connex_parts); if(connex_components_initialized) custom_darray_ptr_component_descriptor_release(&connex_components); + if(connex_parts_initialized) + custom_darray_ptr_part_descriptor_release(&connex_parts); if(s2d_view) S2D(scene_view_ref_put(s2d_view)); if(neighbourhood_by_vertex_initialized) darray_neighbourhood_release(&neighbourhood_by_vertex); if(segments_tmp_initialized) darray_segment_tmp_release(&segments_tmp); - if(segments_comp_initialized) darray_segment_comp_release(&segments_comp); + if(segments_part_initialized) darray_segment_part_release(&segments_part); if(segsides) MEM_RM(scn->dev->allocator, segsides); if(desc) *out_desc = desc; diff --git a/src/senc2d_scene_analyze_c.h b/src/senc2d_scene_analyze_c.h @@ -20,65 +20,119 @@ #include "senc2d_internal_types.h" #include <rsys/mem_allocator.h> -#include <rsys/dynamic_array_uchar.h> #include <rsys/hash_table.h> #include <rsys/double2.h> - /* This one is used as flag */ enum side_flag { FLAG_FRONT = BIT(0), FLAG_BACK = BIT(1) }; -enum list_id { - FLAG_NO_LIST = 0, - FLAG_LIST_SIDE_LIST = BIT(1), - FLAG_LIST_COMPONENT = BIT(2), - FLAG_WAITING_STACK = BIT(3), - FLAG_ANY_LIST = 0xFF -}; - /* Information kept during the building side groups. */ struct segside { /* Rank of the segside facing this segside through its vertices */ side_id_t facing_side_id[2]; /* Id of this segside's medium */ medium_id_t medium; - /* The list containing the segside; made of enum list_id flags */ - unsigned char list_id; /* Implicit information that we don't need to store: * - segment_id * - side * This is due to the memory layout of the elt darray: * front(seg_0), back(seg_0), front(seg_1), back(seg_1), ... */ +}; -#ifndef NDEBUG - component_id_t member_of_cc; -#endif +#define HTABLE_NAME part_id +#define HTABLE_KEY part_id_t +#define HTABLE_DATA char +#include <rsys/hash_table.h> + +#define DARRAY_NAME side_id +#define DARRAY_DATA side_id_t +#include <rsys/dynamic_array.h> + +/* Descriptors for parts of connex components. + * Define lists of seg sides starting from a given head. + * Also keeps the maximum y info of the component + * along with the associated segment and vertex ids + * and the list of media found */ +struct part_descriptor { + char not_head_of_group; + component_id_t part_id; + vrtx_id_t max_y_vrtx_id; /* id of the vrtx with max y value */ + side_id_t side_count; + medium_id_t medium; + /* Range of sides member of this part */ + struct side_range side_range; + /* Other parts that need to be merged with this one */ + struct htable_part_id parts_to_merge_with; }; +extern const struct part_descriptor PART_DESCRIPTOR_NULL; + +static FINLINE void +part_descriptor_init + (struct mem_allocator* alloc, + struct part_descriptor* data) +{ + ASSERT(data); + (void) alloc; + *data = PART_DESCRIPTOR_NULL; + htable_part_id_init(alloc, &data->parts_to_merge_with); +} + +static FINLINE void +ptr_part_descriptor_init + (struct mem_allocator* alloc, + struct part_descriptor** data) +{ + (void) alloc; + ASSERT(data); + *data = NULL; +} -/* Descriptors for connex component. +#define DARRAY_NAME ptr_part_descriptor +#define DARRAY_DATA struct part_descriptor* +#define DARRAY_FUNCTOR_INIT ptr_part_descriptor_init +#include <rsys/dynamic_array.h> + +/* Need allocator to free array elts: cannot rely on standard +* darray release stuff */ +static FINLINE void +custom_darray_ptr_part_descriptor_release + (struct darray_ptr_part_descriptor* array) +{ + size_t c, cc_count; + struct part_descriptor** components; + if(!array) return; + cc_count = darray_ptr_part_descriptor_size_get(array); + components = darray_ptr_part_descriptor_data_get(array); + FOR_EACH(c, 0, cc_count) { + if(!components[c]) continue; + htable_part_id_release(&components[c]->parts_to_merge_with); + MEM_RM(array->allocator, components[c]); + } + darray_ptr_part_descriptor_release(array); +} + +/* Descriptors for connex components. * Define lists of seg sides starting from a given head. * Also keeps the maximum y info of the component - * along with the associated segment and vertex ids */ -#define CC_ID_NONE COMPONENT_MAX__ -#define CC_GROUP_ROOT_NONE COMPONENT_MAX__ -#define CC_GROUP_ROOT_INFINITE (COMPONENT_MAX__-1) -#define CC_GROUP_ID_NONE COMPONENT_MAX__ + * along with the associated segment and vertex ids + * and the list of media found */ struct cc_descriptor { /* Does this component is an outer border of an enclosure or an inner one? */ char is_outer_border; - vrtx_id_t max_y_vrtx_id; + vrtx_id_t max_y_vrtx_id; /* id of the vrtx with max y value */ side_id_t side_count; - medium_id_t medium; /* Used when grouping components to form enclosures */ component_id_t cc_id; component_id_t cc_group_root; enclosure_id_t enclosure_id; - /* Range of sides member of this component */ + /* Range of sides member of this part */ struct side_range side_range; + /* Media used by this component */ + struct htable_media media; }; extern const struct cc_descriptor CC_DESCRIPTOR_NULL; @@ -90,6 +144,7 @@ cc_descriptor_init ASSERT(data); (void)alloc; *data = CC_DESCRIPTOR_NULL; + htable_media_init(alloc, &data->media); } static FINLINE void @@ -118,7 +173,11 @@ custom_darray_ptr_component_descriptor_release if(!array) return; cc_count = darray_ptr_component_descriptor_size_get(array); components = darray_ptr_component_descriptor_data_get(array); - FOR_EACH(c, 0, cc_count) MEM_RM(array->allocator, components[c]); + FOR_EACH(c, 0, cc_count) { + if(!components[c]) continue; + htable_media_release(&components[c]->media); + MEM_RM(array->allocator, components[c]); + } darray_ptr_component_descriptor_release(array); } diff --git a/src/test_senc2d_descriptor.c b/src/test_senc2d_descriptor.c @@ -28,7 +28,7 @@ main(int argc, char** argv) struct senc2d_descriptor* desc = NULL; struct senc2d_enclosure* enc = NULL; struct context ctx; - unsigned count; + unsigned count, maxm; unsigned indices[2]; double coord[2]; unsigned media[2]; @@ -61,12 +61,12 @@ main(int argc, char** argv) CHK(senc2d_descriptor_ref_put(NULL) == RES_BAD_ARG); CHK(senc2d_descriptor_ref_put(desc) == RES_OK); - CHK(senc2d_descriptor_get_max_medium(NULL, &count) == RES_BAD_ARG); + CHK(senc2d_descriptor_get_max_medium(NULL, &maxm) == RES_BAD_ARG); CHK(senc2d_descriptor_get_max_medium(desc, NULL) == RES_BAD_ARG); - CHK(senc2d_descriptor_get_enclosure_count(NULL, NULL) == RES_BAD_ARG); - CHK(senc2d_descriptor_get_max_medium(desc, &count) == RES_OK); + CHK(senc2d_descriptor_get_max_medium(NULL, NULL) == RES_BAD_ARG); + CHK(senc2d_descriptor_get_max_medium(desc, &maxm) == RES_OK); - CHK(count == 2); + CHK(maxm == 1); CHK(senc2d_descriptor_get_enclosure_count(NULL, &count) == RES_BAD_ARG); CHK(senc2d_descriptor_get_enclosure_count(desc, NULL) == RES_BAD_ARG); diff --git a/src/test_senc2d_enclosure.c b/src/test_senc2d_enclosure.c @@ -132,7 +132,16 @@ main(int argc, char** argv) CHK(senc2d_enclosure_get_segment_global_id(NULL, nsegments, NULL) == RES_BAD_ARG); CHK(senc2d_enclosure_get_segment_global_id(enclosure, 0, &gid) == RES_OK); - + + CHK(senc2d_enclosure_get_medium(NULL, 0, medium) == RES_BAD_ARG); + CHK(senc2d_enclosure_get_medium(enclosure, 2, medium) == RES_BAD_ARG); + CHK(senc2d_enclosure_get_medium(enclosure, 0, NULL) == RES_BAD_ARG); + CHK(senc2d_enclosure_get_medium(NULL, 2, medium) == RES_BAD_ARG); + CHK(senc2d_enclosure_get_medium(NULL, 0, NULL) == RES_BAD_ARG); + CHK(senc2d_enclosure_get_medium(enclosure, 2, NULL) == RES_BAD_ARG); + CHK(senc2d_enclosure_get_medium(NULL, 2, NULL) == RES_BAD_ARG); + CHK(senc2d_enclosure_get_medium(enclosure, 0, medium) == RES_OK); + CHK(senc2d_enclosure_ref_put(enclosure) == RES_OK); FOR_EACH(i, 0, ecount) { @@ -144,7 +153,7 @@ main(int argc, char** argv) CHK(senc2d_enclosure_get_header(enclosure, &header) == RES_OK); CHK(header.enclosure_id == i); - CHK((header.enclosed_medium == 0) == header.is_infinite); + CHK(header.enclosed_media_count == 1); CHK(header.segment_count == nsegments); CHK(header.unique_segment_count == nsegments); CHK(header.vertices_count == nvertices); @@ -203,7 +212,7 @@ main(int argc, char** argv) CHK(senc2d_enclosure_get_header(enclosure, &header) == RES_OK); CHK(header.enclosure_id == 0); - CHK(header.enclosed_medium == 0); + CHK(header.enclosed_media_count == 1); CHK(header.segment_count == 2 * header.unique_segment_count); CHK(header.unique_segment_count == nsegments - 1); CHK(header.vertices_count == nvertices); diff --git a/src/test_senc2d_many_enclosures.c b/src/test_senc2d_many_enclosures.c @@ -128,12 +128,15 @@ main(int argc, char** argv) FOR_EACH(e, 0, count) { struct senc2d_enclosure* enclosure; struct senc2d_enclosure_header header; + unsigned m; CHK(senc2d_descriptor_get_enclosure(desc, e, &enclosure) == RES_OK); CHK(senc2d_enclosure_get_header(enclosure, &header) == RES_OK); + CHK(header.enclosed_media_count == 1); + CHK(senc2d_enclosure_get_medium(enclosure, 0, &m) == RES_OK); CHK(header.segment_count == - (e == 0 /* Outermost enclosure: NB_CIRC_1*NB_CIRC_1 circles */ + (header.is_infinite /* Outermost enclosure: NB_CIRC_1*NB_CIRC_1 circles */ ? NB_CIRC_1 * NB_CIRC_1 * circ_seg_count - : (header.enclosed_medium == 0 + : (m == 0 ? circ_seg_count /* Innermost enclosures: 1 circle */ : 2 * circ_seg_count))); /* Other enclosures: 2 circles */ CHK(senc2d_enclosure_ref_put(enclosure) == RES_OK); diff --git a/src/test_senc2d_scene.c b/src/test_senc2d_scene.c @@ -27,7 +27,7 @@ main(int argc, char** argv) struct senc2d_scene* scn = NULL; struct senc2d_descriptor* desc = NULL; struct context ctx; - unsigned count, i; + unsigned count, i, maxm; (void)argc, (void)argv; CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK); @@ -129,11 +129,24 @@ main(int argc, char** argv) CHK(senc2d_scene_create(dev, &scn) == RES_OK); CHK(senc2d_scene_add_geometry(scn, nsegments, get_indices, get_media, get_global_id, nvertices, get_position, &ctx) == RES_OK); - /* Medium mismatch between neighbour segments */ - CHK(senc2d_scene_analyze(scn, &desc) == RES_BAD_ARG); - ctx.front_media = medium0; + /* Medium mismatch between neighbour segments, but OK */ + CHK(senc2d_scene_analyze(scn, &desc) == RES_OK); + + CHK(senc2d_descriptor_get_max_medium(desc, &maxm) == RES_OK); + CHK(maxm == 3); + CHK(senc2d_descriptor_get_enclosure_count_by_medium(desc, 0, &count) == RES_OK); + CHK(count == 0); /* Medium 0 unused */ + CHK(senc2d_descriptor_get_enclosure_count_by_medium(desc, 1, &count) == RES_OK); + CHK(count == 2); /* Medium 1 used twice */ + CHK(senc2d_descriptor_get_enclosure_count_by_medium(desc, 2, &count) == RES_OK); + CHK(count == 0); /* Medium 2 unused */ + CHK(senc2d_descriptor_get_enclosure_count_by_medium(desc, 3, &count) == RES_OK); + CHK(count == 1); /* Medium 3 used */ + check_desc(desc); + ctx.front_media = medium0; CHK(senc2d_scene_ref_put(scn) == RES_OK); + CHK(senc2d_descriptor_ref_put(desc) == RES_OK); CHK(senc2d_scene_create(dev, &scn) == RES_OK); CHK(senc2d_scene_add_geometry(scn, nsegments, get_indices, get_media, NULL, nvertices, get_position, &ctx) == RES_OK); diff --git a/src/test_senc2d_square_behind_square.c b/src/test_senc2d_square_behind_square.c @@ -18,30 +18,6 @@ #include <rsys/double2.h> -void check_desc(struct senc2d_descriptor* desc) { - unsigned mcount, ecount, i; - CHK(senc2d_descriptor_get_max_medium(desc, &mcount) == RES_OK); - CHK(mcount == 2); - CHK(senc2d_descriptor_get_enclosure_count(desc, &ecount) == RES_OK); - FOR_EACH(i, 0, mcount) { - unsigned j, ecount_bym; - unsigned found = 0; - senc2d_descriptor_get_enclosure_count_by_medium(desc, i, &ecount_bym); - FOR_EACH(j, 0, ecount_bym) { - struct senc2d_enclosure* enc; - struct senc2d_enclosure_header h; - CHK(senc2d_descriptor_get_enclosure_by_medium(desc, i, j, &enc) == RES_OK); - CHK(senc2d_enclosure_get_header(enc, &h) == RES_OK); - found += (h.enclosed_medium == i); - CHK(senc2d_enclosure_ref_put(enc) == RES_OK); - } - ASSERT(found == ecount_bym); /* All the enclosures enclose medim i */ - ASSERT(ecount_bym); - ecount -= ecount_bym; - } - ASSERT(ecount == 0); -} - int main(int argc, char** argv) { @@ -50,6 +26,7 @@ main(int argc, char** argv) struct senc2d_device* dev = NULL; struct senc2d_scene* scn = NULL; struct context ctx; + unsigned i, ecount, maxm; (void)argc, (void)argv; CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK); @@ -83,13 +60,26 @@ main(int argc, char** argv) CHK(senc2d_scene_analyze(scn, &desc) == RES_OK); + CHK(senc2d_descriptor_get_enclosure_count(desc, &ecount) == RES_OK); + CHK(ecount == 3); + + FOR_EACH(i, 0, ecount) { + struct senc2d_enclosure* enclosure; + struct senc2d_enclosure_header header; + CHK(senc2d_descriptor_get_enclosure(desc, i, &enclosure) == RES_OK); + CHK(senc2d_enclosure_get_header(enclosure, &header) == RES_OK); + ASSERT(header.enclosed_media_count == 1); + CHK(senc2d_enclosure_ref_put(enclosure) == RES_OK); + } + + CHK(senc2d_descriptor_get_max_medium(desc, &maxm) == RES_OK); + CHK(maxm == 1); check_desc(desc); /* Even further in +Y, even bigger */ d2(ctx.offset, -3, 30); ctx.scale = 7; - /* Front/back media have been exchanged: external enclosure shows 2 media - * analyze will fail */ + /* Front/back media have been exchanged: external enclosure shows 2 media */ ctx.front_media = medium1; ctx.back_media = medium0; @@ -99,7 +89,23 @@ main(int argc, char** argv) if(desc) CHK(senc2d_descriptor_ref_put(desc) == RES_OK); desc = NULL; - CHK(senc2d_scene_analyze(scn, &desc) == RES_BAD_ARG); + CHK(senc2d_scene_analyze(scn, &desc) == RES_OK); + + CHK(senc2d_descriptor_get_enclosure_count(desc, &ecount) == RES_OK); + CHK(ecount == 4); + + FOR_EACH(i, 0, ecount) { + struct senc2d_enclosure* enclosure; + struct senc2d_enclosure_header header; + CHK(senc2d_descriptor_get_enclosure(desc, i, &enclosure) == RES_OK); + CHK(senc2d_enclosure_get_header(enclosure, &header) == RES_OK); + ASSERT(header.enclosed_media_count == (header.is_infinite ? 2u : 1u)); + CHK(senc2d_enclosure_ref_put(enclosure) == RES_OK); + } + + CHK(senc2d_descriptor_get_max_medium(desc, &maxm) == RES_OK); + CHK(maxm == 1); + check_desc(desc); CHK(senc2d_scene_ref_put(scn) == RES_OK); CHK(senc2d_device_ref_put(dev) == RES_OK); diff --git a/src/test_senc2d_square_in_square.c b/src/test_senc2d_square_in_square.c @@ -26,6 +26,7 @@ main(int argc, char** argv) struct senc2d_device* dev = NULL; struct senc2d_scene* scn = NULL; struct context ctx; + unsigned i, ecount, maxm; (void)argc, (void)argv; CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK); @@ -62,14 +63,31 @@ main(int argc, char** argv) CHK(senc2d_scene_analyze(scn, &desc) == RES_OK); + CHK(senc2d_descriptor_get_enclosure_count(desc, &ecount) == RES_OK); + CHK(ecount == 3); + + FOR_EACH(i, 0, ecount) { + struct senc2d_enclosure* enclosure; + struct senc2d_enclosure_header header; + CHK(senc2d_descriptor_get_enclosure(desc, i, &enclosure) == RES_OK); + CHK(senc2d_enclosure_get_header(enclosure, &header) == RES_OK); + ASSERT(header.enclosed_media_count == 1); + CHK(senc2d_enclosure_ref_put(enclosure) == RES_OK); + } + + CHK(senc2d_descriptor_get_max_medium(desc, &maxm) == RES_OK); + CHK(maxm == 1); + check_desc(desc); + d2(ctx.offset, -4, -4); ctx.scale = 10; ctx.reverse_vrtx = 1; ctx.reverse_med = 1; /* Biggest square exterior is medium 1 */ ctx.front_media = medium1; - /* Biggest square interior is medium 0 */ - ctx.back_media = medium0; /* mismatch with square 2 */ + /* Biggest square interior is medium 0 + * interior/exterior media have been exchanged: external enclosure shows 2 media */ + ctx.back_media = medium0; /* Third square */ CHK(senc2d_scene_add_geometry(scn, nsegments, get_indices, get_media, NULL, @@ -77,7 +95,14 @@ main(int argc, char** argv) if(desc) CHK(senc2d_descriptor_ref_put(desc) == RES_OK); desc = NULL; - CHK(senc2d_scene_analyze(scn, &desc) == RES_BAD_ARG); + CHK(senc2d_scene_analyze(scn, &desc) == RES_OK); + + CHK(senc2d_descriptor_get_enclosure_count(desc, &ecount) == RES_OK); + CHK(ecount == 4); + + CHK(senc2d_descriptor_get_max_medium(desc, &maxm) == RES_OK); + CHK(maxm == 1); + check_desc(desc); CHK(senc2d_scene_ref_put(scn) == RES_OK); CHK(senc2d_device_ref_put(dev) == RES_OK); diff --git a/src/test_senc2d_utils.h b/src/test_senc2d_utils.h @@ -236,5 +236,43 @@ circle_release(struct context* ctx) ctx->indices = NULL; } + +/******************************************************************************* + * Check functions + ******************************************************************************/ +static INLINE void check_desc(struct senc2d_descriptor* desc) +{ + unsigned maxm, ecount, i; + size_t e_cpt = 0; + CHK(senc2d_descriptor_get_max_medium(desc, &maxm) == RES_OK); + CHK(senc2d_descriptor_get_enclosure_count(desc, &ecount) == RES_OK); + for (i = 0; i <= maxm; i++) { + unsigned j, ecount_bym; + unsigned found = 0; + CHK(senc2d_descriptor_get_enclosure_count_by_medium(desc, i, &ecount_bym) == RES_OK); + /* Can be 0 if media numbering is not compact */ + FOR_EACH(j, 0, ecount_bym) { + struct senc2d_enclosure* enc; + struct senc2d_enclosure_header h; + unsigned k; + int f = 0; + CHK(senc2d_descriptor_get_enclosure_by_medium(desc, i, j, &enc) == RES_OK); + CHK(senc2d_enclosure_get_header(enc, &h) == RES_OK); + ASSERT(h.enclosed_media_count); + FOR_EACH(k, 0, h.enclosed_media_count) { + unsigned m; + CHK(senc2d_enclosure_get_medium(enc, k, &m) == RES_OK); + found += (m == i); + f += (m == i); + } + ASSERT(f == 1); /* Single reference expected */ + CHK(senc2d_enclosure_ref_put(enc) == RES_OK); + } + ASSERT(found == ecount_bym); /* All the enclosures enclose medim i */ + e_cpt += ecount_bym; + } + ASSERT(e_cpt >= ecount); /* Every enc has been visited at least once */ +} + #endif /* TEST_UTILS2_H */