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 8ffcfb95673ed4393fb3ab03a11c57c2be763d73
parent 10558d205b15530f9e521bde7d73e77c02e60929
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Fri,  5 Oct 2018 12:35:15 +0200

Merge branch 'release_0.2'

Diffstat:
MREADME.md | 63++++++++++++++++++++++++++++++++++++++++++---------------------
Mcmake/CMakeLists.txt | 13++++++++++---
Msrc/senc2d.h | 83+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Msrc/senc2d_descriptor.c | 56+++++++++++++++++++++++++++++++++++++++++++++++++++++---
Msrc/senc2d_descriptor_c.h | 19+++++++++++++++----
Msrc/senc2d_device.c | 5++---
Msrc/senc2d_device_c.h | 2+-
Msrc/senc2d_enclosure.c | 25+++++++++++++++++++++----
Msrc/senc2d_enclosure_c.h | 2+-
Msrc/senc2d_enclosure_data.h | 89++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Msrc/senc2d_internal_types.h | 20++++++++++++++++++--
Msrc/senc2d_scene.c | 35++++++++++++++++++++++-------------
Msrc/senc2d_scene_analyze.c | 870++++++++++++++++++++++++++++++++++++++-----------------------------------------
Msrc/senc2d_scene_analyze_c.h | 60++++++++++++++++++++----------------------------------------
Msrc/senc2d_scene_c.h | 3+++
Msrc/test_senc2d_descriptor.c | 99+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Msrc/test_senc2d_enclosure.c | 151+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Asrc/test_senc2d_inconsistant_square.c | 150+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/test_senc2d_many_enclosures.c | 21+++++++++++++--------
Msrc/test_senc2d_many_segments.c | 8+++++---
Msrc/test_senc2d_sample_enclosure.c | 24+++++++++++++-----------
Msrc/test_senc2d_scene.c | 162+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Msrc/test_senc2d_square_behind_square.c | 58+++++++++++++++++++++++++++++++++++++++++++++++-----------
Msrc/test_senc2d_square_in_square.c | 53++++++++++++++++++++++++++++++++++++++++-------------
Msrc/test_senc2d_square_on_square.c | 21++++++++++++---------
Msrc/test_senc2d_utils.h | 113++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
26 files changed, 1451 insertions(+), 754 deletions(-)

diff --git a/README.md b/README.md @@ -1,30 +1,51 @@ -# StarEnclosures 2D +StarEnclosures 2D +================= -The purpose of this library is to extract enclosures from raw 2D geometry. An -enclosure is a set of segments enclosing a given area. The library manages -vertices and segments duplicates, easing the scene definition process. It also -checks some coherency properties, most noticeably the enclosed medium unicity. +The purpose of Star-enclosures-2D is to extract enclosures from raw +geometry. An enclosure is a set of segments enclosing a given area. The +enclosure notion extends to open enclosures for which there is no inside +or outside. For every detected enclosure, the library provides the set +of involved segment sides as well as the set of involved media and a few +metrics (number of segments, ...). -## How to build +The library allows geometry to be added by subsets of segments and +manages vertices and segments duplicates as long as the duplicate +information is coherent, easing the scene definition process. + +Also the convention regarding FRONT/BACK sides for input segments as +well as the convention regarding normals' orientation for output +segments can be set. + +How to build +------------ StarEnclosures 2D relies on the [CMake](http://www.cmake.org) and the -[RCMake](https://gitlab.com/vaplv/rcmake/) package to build. It also depends -on the [RSys](https://gitlab.com/vaplv/rsys/) and -[Star-2D](https://gitlab.com/meso-star/star-2d/) libraries. Additionaly, one -more library is needed to build tests +[RCMake](https://gitlab.com/vaplv/rcmake/) package to build. It also +depends on the [RSys](https://gitlab.com/vaplv/rsys/) and +[Star-2D](https://gitlab.com/meso-star/star-2d/) libraries. Additionaly, +one more library is needed to build tests ([Star-SP](https://gitlab.com/meso-star/star-sp/)). -First ensure that CMake and a C compiler that implements the OpenMP 2.0 are -installed on your system. Then install the RCMake package as well as all the -aforementioned prerequisites. -Finally generate the project from the `cmake/CMakeLists.txt` file by appending -to the `CMAKE_PREFIX_PATH` variable the install directories of its -dependencies. +First ensure that CMake and a C compiler that implements the OpenMP 2.0 +are installed on your system. Then install the RCMake package as well as +all the aforementioned prerequisites. Finally generate the project from +the `cmake/CMakeLists.txt` file by appending to the `CMAKE_PREFIX_PATH` +variable the install directories of its dependencies. + +Release notes +------------- + +### Version 0.2 -## License +- Add the support of enclosures with multiple media. +- Allow to set the FRONT/BACK convention for input segments. +- Allow to set the normal convention for output segments. -StarEnclosures 2D is Copyright (C) |Meso|Star> 2018 (<contact@meso-star.com>). -It is free software released under the GPLv3+ license. You are welcome to -redistribute it under certain conditions; refer to the COPYING files for -details. +License +------- +StarEnclosures 2D is Copyright (C) \\\|Meso\\\|Star&gt; 2018 +(<a href="mailto:contact@meso-star.com" class="email">contact@meso-star.com</a>). +It is free software released under the GPLv3+ license. You are welcome +to redistribute it under certain conditions; refer to the COPYING files +for details. diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -36,11 +36,17 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${RCMAKE_SOURCE_DIR}) include(rcmake) include(rcmake_runtime) +if(NO_TEST) +rcmake_append_runtime_dirs(_runtime_dirs RSys Star2D) +else() +rcmake_append_runtime_dirs(_runtime_dirs RSys StarSP Star2D) +endif() + ################################################################################ # Configure and define targets ################################################################################ set(VERSION_MAJOR 0) -set(VERSION_MINOR 1) +set(VERSION_MINOR 2) set(VERSION_PATCH 0) set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) @@ -90,7 +96,7 @@ if(CMAKE_COMPILER_IS_GNUCC) target_link_libraries(senc2d m) endif() -rcmake_setup_devel(senc2d StarEnc2D ${VERSION} senc2d_version.h) +rcmake_setup_devel(senc2d StarEnc2D ${VERSION} star/senc2d_version.h) ################################################################################ # Add tests @@ -119,6 +125,7 @@ if(NOT NO_TEST) new_test(test_senc2d_descriptor) new_test(test_senc2d_device) new_test(test_senc2d_enclosure) + new_test(test_senc2d_inconsistant_square) new_test(test_senc2d_many_enclosures) new_test(test_senc2d_many_segments) new_test(test_senc2d_sample_enclosure) @@ -135,5 +142,5 @@ install(TARGETS senc2d ARCHIVE DESTINATION bin LIBRARY DESTINATION lib RUNTIME DESTINATION bin) -install(FILES ${SENC2D_FILES_INC_API} DESTINATION include/) +install(FILES ${SENC2D_FILES_INC_API} DESTINATION include/star) install(FILES ${SENC2D_FILES_DOC} DESTINATION share/doc/star-enc2d) diff --git a/src/senc2d.h b/src/senc2d.h @@ -57,7 +57,7 @@ struct senc2d_scene; struct senc2d_enclosure; /* Enclosure2D header type */ -struct enclosure2d_header { +struct senc2d_enclosure_header { /* The ID of the enclosure; 0, 1, ... */ unsigned enclosure_id; /* Number of segments; a segment can be accounted for twice, once by side */ @@ -66,13 +66,55 @@ struct enclosure2d_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; }; +/* We consider the geometrical normal Ng to a segment V0 V1 + * that verifies "(V0, V0V1, Ng) is a direct system". + * + * The user can set the convention used to determine which side of + * a segment is to be considered front/back by using the flags : + * SENC2D_CONVENTION_NORMAL_FRONT => Ng points toward the front side, + * SENC2D_CONVENTION_NORMAL_BACK => Ng points toward the back side. + * + * Additionaly the user can set the convention used to output enclosures + * so that Ng points toward the enclosure or on the opposite direction + * (for a closed enclosure Ng points toward the inside or toward the outside) + * by using the flags : + * SENC2D_CONVENTION_NORMAL_INSIDE => Ng points toward the enclosure, + * SENC2D_CONVENTION_NORMAL_OUTSIDE => Ng points to the opposite of the enclosure. + * + * Note that normals in output data can be opposite to normals in input data + * (vertices are then given in reverse order). + * + * Also note that both sides of a segment can be part of the same enclosure; + * in this case the 2 sides will be given with opposite Ng (meaning they + * will be given with opposite vertices order). + */ +enum senc2d_convention { + /* + * Convention regarding FRONT/BACK sides in input data + */ + + /* Geometrical normals point toward the front side */ + SENC2D_CONVENTION_NORMAL_FRONT = BIT(0), + /* Geometrical normals point toward the back side */ + SENC2D_CONVENTION_NORMAL_BACK = BIT(1), + + /* + * Convention regarding geometrical normals in output data + */ + + /* Geometrical normals point toward the enclosure */ + SENC2D_CONVENTION_NORMAL_INSIDE = BIT(2), + /* Geometrical normals point to the opposite of the enclosure */ + SENC2D_CONVENTION_NORMAL_OUTSIDE = BIT(3) +}; + BEGIN_DECLS /******************************************************************************* @@ -104,7 +146,7 @@ senc2d_device_ref_put SENC2D_API res_T senc2d_scene_create (struct senc2d_device* device, - const unsigned media_count, + const enum senc2d_convention convention, struct senc2d_scene** scene); /* Add a new set of vertices and segments to the scene. @@ -131,6 +173,11 @@ senc2d_scene_analyze struct senc2d_descriptor** descriptor); SENC2D_API res_T +senc2d_scene_get_convention + (const struct senc2d_scene* scene, + enum senc2d_convention* convention); + +SENC2D_API res_T senc2d_scene_get_segments_count (const struct senc2d_scene* scene, unsigned* count); @@ -162,17 +209,35 @@ senc2d_scene_ref_put * StarEnclosures2D descriptor. It is an handle toward an analyze result. ******************************************************************************/ SENC2D_API res_T +senc2d_descriptor_get_max_medium + (const struct senc2d_descriptor* descriptor, + unsigned* max_medium_id); + +SENC2D_API res_T senc2d_descriptor_get_enclosure_count (const struct senc2d_descriptor* descriptor, unsigned* count); SENC2D_API res_T +senc2d_descriptor_get_enclosure_count_by_medium + (const struct senc2d_descriptor* descriptor, + const unsigned imed, /* Must be in [0 max_medium_id] */ + unsigned* count); + +SENC2D_API res_T senc2d_descriptor_get_enclosure (struct senc2d_descriptor* descriptor, const unsigned idx, struct senc2d_enclosure** enclosure); SENC2D_API res_T +senc2d_descriptor_get_enclosure_by_medium + (struct senc2d_descriptor* descriptor, + const unsigned imed, + const unsigned idx, + struct senc2d_enclosure** enclosure); + +SENC2D_API res_T senc2d_descriptor_get_global_segments_count (const struct senc2d_descriptor* descriptor, unsigned* count); /* Number of unique segments. */ @@ -228,13 +293,13 @@ senc2d_descriptor_ref_put * case the 2 occurences of the segment have reversed vertices order and * unique_segment_count and segment_count differ. * By-index API accesses of segments (or properties) visit unique segments - * for indexes in the [0 unique_segment_count[ and back-faces of the + * for indexes in the [0 unique_segment_count[ range and back-faces of the * doubly-listed segments in the [unique_segment_count segment_count[ range. ******************************************************************************/ SENC2D_API res_T senc2d_enclosure_get_header (const struct senc2d_enclosure* enclosure, - const struct enclosure2d_header** header); + struct senc2d_enclosure_header* header); SENC2D_API res_T senc2d_enclosure_get_segment @@ -261,6 +326,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 @@ -36,6 +36,7 @@ descriptor_release(ref_T * ref) scn = desc->scene; darray_segment_enc_release(&desc->segments_enc); darray_enclosure_release(&desc->enclosures); + darray_enc_ids_array_release(&desc->enc_ids_array_by_medium); MEM_RM(scn->dev->allocator, desc); SENC2D(scene_ref_put(scn)); @@ -56,8 +57,11 @@ descriptor_create(struct senc2d_scene* scn) SENC2D(scene_ref_get(desc->scene)); ref_init(&desc->ref); darray_segment_enc_init(scn->dev->allocator, &desc->segments_enc); - /* Enclosure 0 is always defined for infinite */ darray_enclosure_init(scn->dev->allocator, &desc->enclosures); + darray_enc_ids_array_init(scn->dev->allocator, + &desc->enc_ids_array_by_medium); + OK(darray_enc_ids_array_resize(&desc->enc_ids_array_by_medium, scn->nmeds)); + /* Enclosure 0 is always defined for infinite */ OK(darray_enclosure_resize(&desc->enclosures, 1)); desc->enclosures_count = 1; desc->segment_count = scn->nusegs; @@ -81,6 +85,16 @@ struct mem_allocator* * Exported functions ******************************************************************************/ res_T +senc2d_descriptor_get_max_medium + (const struct senc2d_descriptor* desc, unsigned* max_medium_id) +{ + if(!desc || !max_medium_id) return RES_BAD_ARG; + ASSERT(desc->scene->nmeds < UINT_MAX); /* API type */ + *max_medium_id = (unsigned)desc->scene->nmeds - 1; + return RES_OK; +} + +res_T senc2d_descriptor_get_enclosure_count (const struct senc2d_descriptor* desc, unsigned* count) { @@ -94,6 +108,26 @@ senc2d_descriptor_get_enclosure_count } res_T +senc2d_descriptor_get_enclosure_count_by_medium + (const struct senc2d_descriptor* desc, + const unsigned imed, + unsigned* count) +{ + size_t tmp; + const struct darray_enc_id* enc_ids; + if(!desc || !count || imed >= desc->scene->nmeds) + return RES_BAD_ARG; + ASSERT(darray_enc_ids_array_size_get(&desc->enc_ids_array_by_medium) + == desc->scene->nmeds); + enc_ids = + darray_enc_ids_array_cdata_get(&desc->enc_ids_array_by_medium) + imed; + tmp = darray_enc_id_size_get(enc_ids); + ASSERT(tmp < UINT_MAX); /* API type */ + *count = (unsigned)tmp; + return RES_OK; +} + +FINLINE res_T senc2d_descriptor_get_enclosure (struct senc2d_descriptor* desc, const unsigned idx, @@ -102,14 +136,30 @@ 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; } res_T +senc2d_descriptor_get_enclosure_by_medium + (struct senc2d_descriptor* desc, + const unsigned imed, + const unsigned idx, + struct senc2d_enclosure** out_enc) +{ + const struct darray_enc_id* enc_ids; + unsigned index; + if(!desc || imed >= desc->scene->nmeds || !out_enc) return RES_BAD_ARG; + enc_ids = + darray_enc_ids_array_cdata_get(&desc->enc_ids_array_by_medium) + imed; + if(idx >= darray_enc_id_size_get(enc_ids)) return RES_BAD_ARG; + index = darray_enc_id_cdata_get(enc_ids)[idx]; + return senc2d_descriptor_get_enclosure(desc, index, out_enc); +} + +res_T senc2d_descriptor_get_global_segments_count (const struct senc2d_descriptor* desc, unsigned* count) diff --git a/src/senc2d_descriptor_c.h b/src/senc2d_descriptor_c.h @@ -31,7 +31,6 @@ struct segment_comp { component_id_t component[2]; }; -#ifndef NDEBUG static void segment_comp_init(struct mem_allocator* alloc, struct segment_comp* seg) { int i; @@ -39,15 +38,14 @@ segment_comp_init(struct mem_allocator* alloc, struct segment_comp* seg) { ASSERT(seg); FOR_EACH(i, 0, 2) seg->component[i] = COMPONENT_NULL__; } -#define DARRAY_FUNCTOR_INIT segment_comp_init -#endif #define DARRAY_NAME segment_comp #define DARRAY_DATA struct segment_comp +#define DARRAY_FUNCTOR_INIT segment_comp_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]; }; @@ -74,6 +72,18 @@ segment_enc_init(struct mem_allocator* alloc, struct segment_enc* seg) { #define DARRAY_FUNCTOR_COPY_AND_RELEASE enclosure_data_copy_and_release #include <rsys/dynamic_array.h> +#define DARRAY_NAME enc_id +#define DARRAY_DATA enclosure_id_t +#include <rsys/dynamic_array.h> + +#define DARRAY_NAME enc_ids_array +#define DARRAY_DATA struct darray_enc_id +#define DARRAY_FUNCTOR_INIT darray_enc_id_init +#define DARRAY_FUNCTOR_COPY darray_enc_id_copy +#define DARRAY_FUNCTOR_RELEASE darray_enc_id_release +#define DARRAY_FUNCTOR_COPY_AND_RELEASE darray_enc_id_copy_and_release +#include <rsys/dynamic_array.h> + struct senc2d_descriptor { struct senc2d_scene* scene; enclosure_id_t enclosures_count; @@ -81,6 +91,7 @@ struct senc2d_descriptor { struct darray_segment_enc segments_enc; /* Store enclosures */ struct darray_enclosure enclosures; + struct darray_enc_ids_array enc_ids_array_by_medium; seg_id_t segment_count; vrtx_id_t vertices_count; diff --git a/src/senc2d_device.c b/src/senc2d_device.c @@ -88,7 +88,6 @@ senc2d_device_create struct senc2d_device* dev = NULL; struct mem_allocator* allocator = NULL; res_T res = RES_OK; - (void)nthreads_hint; /* Unused */ if(nthreads_hint == 0 || !out_dev) return RES_BAD_ARG; log = logger ? logger : LOGGER_DEFAULT; @@ -106,8 +105,8 @@ senc2d_device_create dev->logger = log; dev->allocator = allocator; dev->verbose = verbose; - dev->nthreads = MMIN(nthreads_hint, (unsigned)omp_get_num_procs()); - omp_set_num_threads((int)dev->nthreads); + /* Cannot use int args for MMIN here as default is -1 */ + dev->nthreads = (int)MMIN(nthreads_hint, (unsigned)omp_get_num_procs()); ref_init(&dev->ref); exit: diff --git a/src/senc2d_device_c.h b/src/senc2d_device_c.h @@ -27,7 +27,7 @@ struct senc2d_device { struct logger* logger; struct mem_allocator* allocator; int verbose; - unsigned nthreads; + int nthreads; ref_T ref; }; 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; @@ -66,10 +68,10 @@ enclosure_create res_T senc2d_enclosure_get_header (const struct senc2d_enclosure* enclosure, - const struct enclosure2d_header** header) + struct senc2d_enclosure_header* header) { if(!enclosure || !header) return RES_BAD_ARG; - *header = &enclosure->data->header; + *header = enclosure->data->header; return RES_OK; } @@ -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,19 @@ #include <rsys/rsys.h> #include <rsys/ref_count.h> +#include <rsys/hash_table.h> +#include <rsys/dynamic_array.h> + +/* unsigned char array with init to zero */ +static FINLINE void +zero_init_uchar + (struct mem_allocator* alloc, unsigned char* data) +{ + ASSERT(data); (void)alloc; + *data = 0; +} +#define DARRAY_FUNCTOR_INIT zero_init_uchar +#include <rsys/dynamic_array_uchar.h> #include "senc2d.h" #include "senc2d_scene_c.h" @@ -26,23 +39,84 @@ #include <limits.h> static void -init_header(struct enclosure2d_header* header) +init_header(struct senc2d_enclosure_header* header) { ASSERT(header); header->enclosure_id = ENCLOSURE_NULL__; 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 DARRAY_NAME media +#define DARRAY_DATA medium_id_t +#include <rsys/dynamic_array.h> + +static FINLINE res_T +bool_array_of_media_merge + (struct darray_uchar* dst, + const unsigned char* src, + const medium_id_t sz) +{ + res_T res = RES_OK; + medium_id_t i; + unsigned char* data_dst; + + ASSERT(src && dst); + + OK(darray_uchar_resize(dst, sz)); + data_dst = darray_uchar_data_get(dst); + ASSERT(sz <= MEDIUM_MAX__); + if(res != RES_OK) goto error; + FOR_EACH(i, 0, sz) { + if(!src[i]) continue; + data_dst[i] = 1; + } +end: + return res; +error: + goto end; +} + +static FINLINE res_T +bool_array_of_media_to_darray_media + (struct darray_media* dst, + struct darray_uchar* src) +{ + res_T res = RES_OK; + medium_id_t i; + size_t sz; + const unsigned char* data; + + ASSERT(src && dst); + + data = darray_uchar_cdata_get(src); + sz = darray_uchar_size_get(src); + ASSERT(sz <= MEDIUM_MAX__); + darray_media_clear(dst); + if(res != RES_OK) goto error; + FOR_EACH(i, 0, (medium_id_t)sz) { + if(!data[i]) continue; + res = darray_media_push_back(dst, &i); + if(res != RES_OK) goto error; + } +end: + return res; +error: + goto end; +} + struct enclosure_data { - struct enclosure2d_header header; + 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 darray_uchar 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 +138,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); + darray_uchar_init(alloc, &enc->tmp_enclosed_media); + darray_media_init(alloc, &enc->enclosed_media); } static FINLINE res_T @@ -80,6 +156,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(darray_uchar_copy(&dst->tmp_enclosed_media, &src->tmp_enclosed_media)); + OK(darray_media_copy(&dst->enclosed_media, &src->enclosed_media)); error: return res; } @@ -89,6 +167,8 @@ enclosure_data_release(struct enclosure_data* n) { ASSERT(n); darray_segment_in_release(&n->sides); darray_vrtx_id_release(&n->vertices); + darray_uchar_release(&n->tmp_enclosed_media); + darray_media_release(&n->enclosed_media); } static FINLINE res_T @@ -105,6 +185,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(darray_uchar_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,19 @@ 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 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 + +/* This one is used as flag */ +enum side_flag { + FLAG_FRONT = BIT(0), + FLAG_BACK = BIT(1) +}; /* This one is used as an index to arrays */ enum side_id { @@ -107,6 +118,11 @@ SEGSIDE_2_SIDE(side_id_t s) { return (s & 1) ? SIDE_BACK : SIDE_FRONT; } +static FINLINE enum side_flag +SEGSIDE_2_SIDEFLAG(side_id_t s) { + return (s & 1) ? FLAG_BACK : FLAG_FRONT; +} + static FINLINE side_id_t SEGIDxSIDE_2_SEGSIDE(seg_id_t s, enum side_id i) { ASSERT((((size_t)s << 1) | (i == SIDE_BACK)) < SIDE_MAX__); diff --git a/src/senc2d_scene.c b/src/senc2d_scene.c @@ -48,13 +48,16 @@ scene_release(ref_T * ref) res_T senc2d_scene_create (struct senc2d_device* dev, - const unsigned nmeds, + const enum senc2d_convention conv, struct senc2d_scene** out_scn) { struct senc2d_scene* scn = NULL; res_T res = RES_OK; - if(!dev || !out_scn || !nmeds || nmeds > MEDIUM_MAX__) + if(!dev || !out_scn + /* Convention must be set both regarding FRONT/BACK and INSIDE/OUTSIDE */ + || !(conv & (SENC2D_CONVENTION_NORMAL_FRONT | SENC2D_CONVENTION_NORMAL_BACK)) + || !(conv & (SENC2D_CONVENTION_NORMAL_INSIDE | SENC2D_CONVENTION_NORMAL_OUTSIDE))) return RES_BAD_ARG; scn = MEM_CALLOC(dev->allocator, 1, sizeof(struct senc2d_scene)); @@ -66,10 +69,11 @@ senc2d_scene_create ref_init(&scn->ref); SENC2D(device_ref_get(dev)); scn->dev = dev; + scn->convention = conv; scn->ngeoms = 0; scn->nsegs = 0; scn->nusegs = 0; - scn->nmeds = (medium_id_t)nmeds; + scn->nmeds = 0; scn->nverts = 0; scn->nuverts = 0; darray_segment_in_init(dev->allocator, &scn->segments_in); @@ -77,7 +81,6 @@ senc2d_scene_create htable_vrtx_init(dev->allocator, &scn->unique_vertices); htable_seg_init(dev->allocator, &scn->unique_segments); darray_side_range_init(dev->allocator, &scn->media_use); - darray_side_range_resize(&scn->media_use, nmeds); exit: if(scn) *out_scn = scn; @@ -191,17 +194,12 @@ senc2d_scene_add_geometry } /* Get media */ media(i, med, ctx); /* API: media needs an unsigned */ - ASSERT(scn->nmeds <= MEDIUM_MAX__); FOR_EACH(j, 0, 2) { if(med[j] >= scn->nmeds) { - log_err(scn->dev, - "%s: segment %lu %s side references invalid medium: %lu.\n", - FUNC_NAME, - (unsigned long)tmp.global_id, - (j ? "back" : "front"), - (unsigned long)med[j]); - res = RES_BAD_ARG; - goto error; + /* New medium */ + ASSERT(med[j] <= MEDIUM_MAX__); + scn->nmeds = 1 + med[j]; + darray_side_range_resize(&scn->media_use, scn->nmeds); } tmp.medium[j] = (medium_id_t)med[j]; } @@ -285,6 +283,17 @@ error: } res_T +senc2d_scene_get_convention + (const struct senc2d_scene* scn, + enum senc2d_convention* convention) +{ + if(!scn || !convention) return RES_BAD_ARG; + *convention = scn->convention; + return RES_OK; + +} + +res_T senc2d_scene_get_segments_count (const struct senc2d_scene* scn, unsigned* count) diff --git a/src/senc2d_scene_analyze.c b/src/senc2d_scene_analyze.c @@ -25,16 +25,7 @@ #include <rsys/mem_allocator.h> #include <rsys/hash_table.h> #include <rsys/dynamic_array.h> - -#if defined(COMPILER_GCC) -#define ATOMIC_CAS_PTR(Atom, NewVal, Comparand) /* Return the initial value */ \ - ATOMIC_CAS((Atom), (NewVal), (Comparand)) -#elif defined(COMPILER_CL) -#define ATOMIC_CAS_PTR(Atom, NewVal, Comparand) /* Return the initial value */ \ - (InterlockedCompareExchangePointer(Atom, NewVal, Comparand)) -#else -#error "Undefined atomic operations" -#endif +#include <rsys/dynamic_array_uchar.h> #include <star/s2d.h> @@ -43,46 +34,28 @@ #include <stdlib.h> #define CC_DESCRIPTOR_NULL__ {\ - {0,-DBL_MAX}, -1, SIDE_NULL__, VRTX_NULL__, 0, MEDIUM_NULL__,\ - CC_ID_NONE, CC_GROUP_ROOT_NONE, CC_GROUP_ID_NONE, CC_ID_NONE,\ - { SEG_NULL__, 0}\ + CHAR_MAX, VRTX_NULL__, 0,\ + CC_ID_NONE, CC_GROUP_ROOT_NONE, ENCLOSURE_NULL__,\ + { SEG_NULL__, 0},\ + NULL\ } +#ifdef COMPILER_GCC + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif const struct cc_descriptor CC_DESCRIPTOR_NULL = CC_DESCRIPTOR_NULL__; +#ifdef COMPILER_GCC + #pragma GCC diagnostic pop +#endif #define DARRAY_NAME component_id #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; @@ -90,60 +63,25 @@ neighbour_cmp(const void* w1, const void* w2) return (a1 > a2) - (a1 < a2); } -static FINLINE void -add_side_to_stack - (const struct senc2d_scene* scn, - struct darray_side_id* stack, - struct segside* segsides, - const side_id_t side_id) -{ - (void)scn; - ASSERT(scn && segsides && 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)); - darray_side_id_push_back(stack, &side_id); - segsides[side_id].list_id = FLAG_WAITING_STACK; -} - static side_id_t get_side_not_in_connex_component - (const struct senc2d_scene* scn, + (const side_id_t last_side, const struct segside* segsides, + const unsigned char* processed, side_id_t* first_side_not_in_component, 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 && processed && first_side_not_in_component); + { + side_id_t i = *first_side_not_in_component; + while(i <= last_side + && (segsides[i].medium != medium + || (processed[SEGSIDE_2_SEG(i)] & SEGSIDE_2_SIDEFLAG(i)))) + ++i; + *first_side_not_in_component = 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, - struct darray_side_id* stack) -{ - side_id_t id; - size_t sz; - (void)segsides; - ASSERT(segsides && 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); - darray_side_id_pop_back(stack); - return id; } static void @@ -180,7 +118,7 @@ self_hit_filter enum side_id hit_side; component_id_t hit_component; - (void) ray_org; (void) ray_dir; + (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) @@ -199,12 +137,12 @@ extract_connex_components struct segside* segsides, struct darray_ptr_component_descriptor* connex_components, const struct darray_segment_tmp* segments_tmp_array, - struct darray_segment_comp* segments_comp, + struct darray_segment_comp* segments_comp_array, struct s2d_scene_view** s2d_view, ATOMIC* component_count, /* Shared error status. * We accept to overwritte an error with a different error */ - res_T* res) + res_T* p_res) { /* This function is called from an omp parallel block and executed * concurrently. */ @@ -212,19 +150,38 @@ extract_connex_components struct mem_allocator* alloc; int64_t mm; struct darray_side_id stack; -#ifndef NDEBUG - seg_id_t s_; - component_id_t c; -#endif + struct darray_side_id ids_of_sides_around_max_y_vertex; + const union double2* positions; + const struct segment_tmp* segments_tmp; + struct segment_comp* segments_comp; + /* An array to flag sides when processed */ + unsigned char* processed; + /* An array to store the component being processed */ + struct darray_side_id current_component; + /* A bool array to store media of the component being processed */ + unsigned char* current_media = NULL; + size_t ii, sz; ASSERT(segsides && desc && connex_components && segments_tmp_array - && s2d_view && component_count && res); + && segments_comp_array && s2d_view && component_count && p_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_comp = darray_segment_comp_data_get(segments_comp_array); + darray_side_id_init(alloc, &stack); + darray_side_id_init(alloc, &ids_of_sides_around_max_y_vertex); + darray_side_id_init(alloc, &current_component); + processed = MEM_CALLOC(alloc, scn->nusegs, sizeof(unsigned char)); + if(!processed) { + *p_res = RES_MEM_ERR; + return; + } #ifndef NDEBUG #pragma omp single { + seg_id_t s_; ASSERT(darray_ptr_component_descriptor_size_get(connex_components) == 0); FOR_EACH(s_, 0, scn->nusegs) { const struct segment_in* seg_in = @@ -234,213 +191,233 @@ extract_connex_components FOR_EACH(mm, 0, 2) { const side_id_t side = SEGIDxSIDE_2_SEGSIDE(s_, mm); const medium_id_t medium = seg_in->medium[mm]; - ASSERT(media_use[medium].first <= side && side <= media_use[medium].last); + ASSERT(media_use[medium].first <= side && side + <= media_use[medium].last); } } } /* Implicit barrier here */ #endif - darray_side_id_init(alloc, &stack); - + /* We loop on sides to build 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; /* Any not-already-used side is used as a starting point */ - side_id_t first_side_not_in_component; + const struct side_range* media_use = + darray_side_range_cdata_get(&scn->media_use) + m; + side_id_t first_side_not_in_component = media_use->first; + double max_y_ny; + const side_id_t last_side = media_use->last; + int component_canceled = 0; + res_T tmp_res = RES_OK; + ATOMIC id; - if(*res != RES_OK) continue; - first_side_not_in_component - = darray_side_range_cdata_get(&scn->media_use)[m].first; + if(*p_res != RES_OK) continue; if(first_side_not_in_component == SIDE_NULL__) continue; /* Unused medium */ ASSERT(first_side_not_in_component < 2 * scn->nusegs); ASSERT(darray_side_id_size_get(&stack) == 0); + ASSERT(darray_side_id_size_get(&current_component) == 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); + const side_id_t start_side_id = get_side_not_in_connex_component + (last_side, segsides, processed, &first_side_not_in_component, m); side_id_t crt_side_id = start_side_id; side_id_t last_side_id = start_side_id; + vrtx_id_t max_y_vrtx_id = VRTX_NULL__; + struct cc_descriptor *cc; + double max_y = -DBL_MAX; + ASSERT(start_side_id == SIDE_NULL__ || start_side_id < 2 * scn->nusegs); + darray_side_id_clear(&current_component); + + if(*p_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); + break; /* start_side_id=SIDE_NULL__ => component done! */ #ifndef NDEBUG { - seg_id_t tid = SEGSIDE_2_SEG(start_side_id); - enum side_flag s = SEGSIDE_2_SIDE(start_side_id); + seg_id_t sid = SEGSIDE_2_SEG(start_side_id); + enum side_id s = SEGSIDE_2_SIDE(start_side_id); medium_id_t side_med - = darray_segment_in_data_get(&desc->scene->segments_in)[tid].medium[s]; + = darray_segment_in_data_get(&desc->scene->segments_in)[sid].medium[s]; ASSERT(side_med == m); } #endif - /* Create and init a new component */ - cc = MEM_ALLOC(alloc, sizeof(struct cc_descriptor)); - if(!cc) { - *res = RES_MEM_ERR; - continue; + /* Reuse array if possible, or create a new one */ + if(current_media) { + memset(current_media, 0, scn->nmeds); + } else { + current_media = MEM_CALLOC(alloc, scn->nmeds, sizeof(unsigned char)); + if(!current_media) { + *p_res = RES_MEM_ERR; + continue; + } } - cc_descriptor_init(alloc, cc); - 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; - + current_media[m] = 1; for(;;) { /* Process all sides of this component */ int i; - enum side_flag crt_side_flag = SEGSIDE_2_SIDE(crt_side_id); + enum side_flag crt_side_flag = SEGSIDE_2_SIDEFLAG(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; - const union double2* vertices = - darray_position_cdata_get(&scn->vertices); + unsigned char* seg_used = processed + 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); + if(*p_res != RES_OK) break; /* Record Ymax information - * Keep track of the appropriate vertex/side of the connex component - * in order to cast a ray at the component grouping step of the - * algorithm. + * Keep track of the appropriate vertex of the component 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. - * If more than one vertex/side has the same Y, we want the side that - * most faces Y (that is the one with the greater ny). - * This is mandatory to select the correct side when both sides of a - * segment are candidate. */ - if(cc->max_vrtx[1] <= seg_tmp->max_y) { - /* Can either improve y or ny */ - - if(cc->max_y_side_id == SEGSIDE_OPPOSITE(crt_side_id)) { - /* Both sides are in cc and the opposite side is currently selected - * Just keep the side with ny>0 */ - if(cc->max_y_ny < 0) { - /* Change side! */ - cc->max_y_ny = -cc->max_y_ny; - cc->max_y_side_id = crt_side_id; - } + * coordinate. */ + if(max_y < seg_tmp->max_y) { + /* New best vertex */ + max_y = seg_tmp->max_y; + /* New vertex: reset list of sides */ + darray_side_id_clear(&ids_of_sides_around_max_y_vertex); + + /* Select a vertex with y == max_y */ + if(max_y == positions[seg_in->vertice_id[0]].pos.y) { + max_y_vrtx_id = seg_in->vertice_id[0]; } else { - double edge[2], normal[2], norm, side_ny; - int process = 0; - - 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 right */ - if(SEGSIDE_IS_FRONT(crt_side_id)) { - side_ny = -normal[1]; - process = 1; - } else { - side_ny = normal[1]; - process = 1; - } - - if(process) { - int change = 0; - if(cc->max_vrtx[1] < seg_tmp->max_y) { - change = 1; /* Try first to improve y */ - } else if(cc->max_y_ny <= 0 && fabs(cc->max_y_ny) < fabs(side_ny)) { - change = 1; /* If ny <= 0, the more negative the better */ - } else if(cc->max_y_ny > 0 && cc->max_y_ny < side_ny) { - change = 1; /* If ny > 0, the more positive the better */ - } - - if(change) { - cc->max_y_ny = side_ny; - cc->max_y_side_id = crt_side_id; - ASSERT(seg_tmp->max_y_vrtx_rank < 2); - ASSERT(seg_in->vertice_id[seg_tmp->max_y_vrtx_rank] < scn->nverts); - cc->max_y_vrtx_id = seg_in->vertice_id[seg_tmp->max_y_vrtx_rank]; - ASSERT(seg_tmp->max_y == vertices[cc->max_y_vrtx_id].pos.y); - d2_set(cc->max_vrtx, vertices[cc->max_y_vrtx_id].vec); - } + ASSERT(max_y == positions[seg_in->vertice_id[1]].pos.y); + max_y_vrtx_id = seg_in->vertice_id[1]; + } + /* List of sides using the vertex */ + OK2(darray_side_id_push_back(&ids_of_sides_around_max_y_vertex, + &crt_side_id)); + } else if(max_y == seg_tmp->max_y) { + /* Does this segment use the currently selected max_y vertex? */ + FOR_EACH(i, 0, 2) { + if(max_y_vrtx_id == seg_in->vertice_id[i]) { + /* List of sides using the vertex */ + OK2(darray_side_id_push_back(&ids_of_sides_around_max_y_vertex, + &crt_side_id)); + 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 + if((*seg_used & crt_side_flag) == 0) { + OK2(darray_side_id_push_back(&current_component, &crt_side_id)); + *seg_used |= (unsigned char)crt_side_flag; + } - /* 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); + enum side_flag nbour_side_id = SEGSIDE_2_SIDEFLAG(neighbour_id); + unsigned char* nbour_used = processed + 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); - const union double2* positions - = darray_position_cdata_get(&scn->vertices); - 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; - continue; - } - if(neighbour->list_id == FLAG_LIST_COMPONENT) { - /* Already processed */ -#ifndef NDEBUG - ASSERT(neighbour->member_of_cc == cc->cc_id); -#endif - continue; + if(*nbour_used & nbour_side_id) continue; /* Already processed */ + if(neighbour->medium < m) { + /* Not the same medium. + * Neighbour's medium id is less than current medium: the whole + * component is to be processed by another thread (possibly the one + * associated with neighbour's medium). */ + component_canceled = 1; + goto canceled; } - if(neighbour->list_id == FLAG_WAITING_STACK) { - continue; /* Already processed */ - } - add_side_to_stack(scn, &stack, segsides, neighbour_id); + /* Mark neighbour as processed and stack it */ + *nbour_used |= (unsigned char)nbour_side_id; + OK2(darray_side_id_push_back(&stack, &neighbour_id)); + OK2(darray_side_id_push_back(&current_component, &neighbour_id)); + current_media[neighbour->medium] = 1; } - if(darray_side_id_size_get(&stack) == 0) - break; /* Empty stack => connex component is done! */ - crt_side_id = get_side_from_stack(segsides, &stack); + sz = darray_side_id_size_get(&stack); + if(sz == 0) break; /* Empty stack => component is done! */ + crt_side_id = darray_side_id_cdata_get(&stack)[sz - 1]; + darray_side_id_pop_back(&stack); last_side_id = MMAX(last_side_id, crt_side_id); } - /* Keep track of this new connex component */ +canceled: + if(component_canceled) continue; + + /* Register the new component and get it initialized */ + cc = MEM_ALLOC(alloc, sizeof(struct cc_descriptor)); + if(!cc) *p_res = RES_MEM_ERR; + if(*p_res != RES_OK) break; + + ASSERT(m == segsides[start_side_id].medium); + ASSERT(max_y_vrtx_id != VRTX_NULL__); + cc_descriptor_init(alloc, cc); + id = ATOMIC_INCR(component_count) - 1; + ASSERT(id <= COMPONENT_MAX__); + cc->cc_id = (component_id_t)id; + sz = darray_side_id_size_get(&current_component); + ASSERT(sz <= SIDE_MAX__); + cc->side_count = (side_id_t)sz; + cc->side_range.first = start_side_id; cc->side_range.last = last_side_id; + cc->max_y_vrtx_id = max_y_vrtx_id; + /* Tranfer ownership of the array to component */ + ASSERT(!cc->media && current_media); + cc->media = current_media; + current_media = NULL; + + /* Write component membership in the global structure + * No need for sync here as an unique thread write a given side */ + STATIC_ASSERT(sizeof(cc->cc_id) >= 4, Cannot_write_IDs_sync_free); + ASSERT(IS_ALIGNED(segments_comp->component, sizeof(cc->cc_id))); + FOR_EACH(ii, 0, sz) { + const side_id_t s = darray_side_id_cdata_get(&current_component)[ii]; + segments_comp[SEGSIDE_2_SEG(s)].component[SEGSIDE_2_SIDE(s)] = cc->cc_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 = 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 - y == 0 can be false). */ + 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; + + /* Orient the geometrical normal according to the convention */ + if(SEGSIDE_IS_FRONT(side_id) + == ((scn->convention & SENC2D_CONVENTION_NORMAL_FRONT) != 0)) { + max_y_ny += normal[1]; + } else { + max_y_ny -= normal[1]; + } + } + cc->is_outer_border = (max_y_ny < 0); + /* 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); + 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, + tmp_res = darray_ptr_component_descriptor_resize(connex_components, 1 + cc->cc_id); - if(tmp_res != RES_OK) *res = tmp_res; + if(tmp_res != RES_OK) *p_res = tmp_res; } - if(*res == RES_OK) { + if(*p_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); @@ -449,14 +426,20 @@ extract_connex_components } } } + tmp_error: + if(tmp_res != RES_OK) *p_res = tmp_res; } /* No barrier here */ + MEM_RM(alloc, processed); + MEM_RM(alloc, current_media); + darray_side_id_release(&current_component); darray_side_id_release(&stack); + darray_side_id_release(&ids_of_sides_around_max_y_vertex); /* The first thread here creates the s2d view */ #pragma omp single nowait - if(*res == RES_OK) { - res_T tmp_res = RES_OK; + if(*p_res == RES_OK) { + res_T res = RES_OK; struct s2d_device* s2d = NULL; struct s2d_scene* s2d_scn = NULL; struct s2d_shape* s2d_shp = NULL; @@ -467,49 +450,49 @@ extract_connex_components attribs.get = get_scn_position; /* Put geometry in a 2D view */ - OK2(s2d_device_create(desc->scene->dev->logger, alloc, 0, &s2d)); - OK2(s2d_scene_create(s2d, &s2d_scn)); - OK2(s2d_shape_create_line_segments(s2d, &s2d_shp)); + OK(s2d_device_create(desc->scene->dev->logger, alloc, 0, &s2d)); + OK(s2d_scene_create(s2d, &s2d_scn)); + OK(s2d_shape_create_line_segments(s2d, &s2d_shp)); /* Back to API type for ntris and nverts */ ASSERT(desc->scene->nusegs < UINT_MAX); ASSERT(desc->scene->nuverts < UINT_MAX); - OK2(s2d_line_segments_setup_indexed_vertices(s2d_shp, + OK(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); - OK2(s2d_scene_attach_shape(s2d_scn, s2d_shp)); - OK2(s2d_scene_view_create(s2d_scn, S2D_TRACE, s2d_view)); - tmp_error: - if(tmp_res != RES_OK) *res = tmp_res; + segments_comp_array); + OK(s2d_scene_attach_shape(s2d_scn, s2d_shp)); + OK(s2d_scene_view_create(s2d_scn, S2D_TRACE, s2d_view)); + error: + if(res != RES_OK) *p_res = res; if(s2d) S2D(device_ref_put(s2d)); if(s2d_scn) S2D(scene_ref_put(s2d_scn)); if(s2d_shp) S2D(shape_ref_put(s2d_shp)); } - if(*res != RES_OK) return; #ifndef NDEBUG /* Need to wait for all threads done to be able to check stuff */ #pragma omp barrier + if(*p_res != RES_OK) return; #pragma omp single { - ASSERT(*component_count == + seg_id_t s_; + component_id_t c; + ASSERT(ATOMIC_GET(component_count) == (int)darray_ptr_component_descriptor_size_get(connex_components)); FOR_EACH(s_, 0, scn->nusegs) { - struct segment_comp* seg_comp = - darray_segment_comp_data_get(segments_comp) + s_; + struct segment_comp* seg_comp = + darray_segment_comp_data_get(segments_comp_array) + s_; ASSERT(seg_comp->component[SIDE_FRONT] != COMPONENT_NULL__); ASSERT(seg_comp->component[SIDE_BACK] != COMPONENT_NULL__); } - FOR_EACH(c, 0, *component_count) { + 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); + ASSERT(components[c] != NULL && components[c]->cc_id == c); } - ASSERT(desc->segment_count - == darray_segment_comp_size_get(segments_comp)); + ASSERT(desc->segment_count == scn->nusegs); } /* Implicit barrier here */ #endif } @@ -522,7 +505,6 @@ group_connex_components struct darray_ptr_component_descriptor* connex_components, struct s2d_scene_view* s2d_view, ATOMIC* next_enclosure_id, - struct cc_descriptor** infinity_first_cc, /* Shared error status. * We accept to overwritte an error with a different error */ res_T* res) @@ -530,19 +512,21 @@ group_connex_components /* This function is called from an omp parallel block and executed * concurrently. */ struct cc_descriptor** descriptors; + const union double2* positions; size_t tmp; component_id_t cc_count; int64_t ccc; (void)segsides; ASSERT(desc && segsides && segments_comp && connex_components - && next_enclosure_id && infinity_first_cc && res); + && s2d_view && next_enclosure_id && res); ASSERT(desc->enclosures_count == 1); descriptors = darray_ptr_component_descriptor_data_get(connex_components); tmp = darray_ptr_component_descriptor_size_get(connex_components); ASSERT(tmp <= COMPONENT_MAX__); cc_count = (component_id_t)tmp; + positions = darray_position_cdata_get(&desc->scene->vertices); /* Cast rays to find links between connex components */ #pragma omp for @@ -554,34 +538,26 @@ group_connex_components const float dir[2] = { 0, 1 }; const float range[2] = { 0, FLT_MAX }; struct cc_descriptor* const cc = descriptors[c]; - const struct segment_comp* origin_seg = - darray_segment_comp_cdata_get(segments_comp) + cc->max_y_vrtx_id; - component_id_t self_hit_component - = origin_seg->component[1 - SEGSIDE_2_SIDE(cc->max_y_side_id)]; + component_id_t self_hit_component = cc->cc_id; + 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); - if(cc->max_y_ny < 0) { - int64_t id; + max_vrtx = positions[cc->max_y_vrtx_id].vec; + if(cc->is_outer_border) { + 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; } - ASSERT(cc->max_y_ny != 0 - /* The only situation with ny==0 we can think of is this one: */ - || (segsides[cc->max_y_side_id].medium - == segsides[SEGSIDE_OPPOSITE(cc->max_y_side_id)].medium - && (segsides[cc->max_y_side_id].facing_side_id[0] - == SEGSIDE_OPPOSITE(cc->max_y_side_id) - || segsides[cc->max_y_side_id].facing_side_id[1] - == SEGSIDE_OPPOSITE(cc->max_y_side_id)))); - f2_set_d2(origin, cc->max_vrtx); + f2_set_d2(origin, max_vrtx); /* Self-hit data: self hit if hit this component "on the other side" */ tmp_res = s2d_scene_view_trace_ray(s2d_view, origin, dir, range, &self_hit_component, &hit); @@ -593,117 +569,40 @@ group_connex_components if(S2D_HIT_NONE(&hit)) { cc->cc_group_root = CC_GROUP_ROOT_INFINITE; cc->enclosure_id = 0; - /* Keep track of the first component facing infinity */ - ATOMIC_CAS_PTR(infinity_first_cc, cc, NULL); - if((*infinity_first_cc)->medium != cc->medium) { - const side_id_t infinity_first_side = (*infinity_first_cc)->max_y_side_id; - const medium_id_t infinity_medium = (*infinity_first_cc)->medium; - /* Medium mismatch! Model topology is broken. */ - const seg_id_t t1 = SEGSIDE_2_SEG(infinity_first_side); - const seg_id_t t2 = SEGSIDE_2_SEG(cc->max_y_side_id); - const struct segment_in* segments_in - = darray_segment_in_cdata_get(&desc->scene->segments_in); - const union double2* positions - = darray_position_cdata_get(&desc->scene->vertices); - log_err(desc->scene->dev, - "Medium mismatch found between segment %lu %s side and segment" - " %lu %s side, both facing infinity.\n", - (unsigned long)segments_in[t1].global_id, - SEGSIDE_IS_FRONT(infinity_first_side) ? "front" : "back", - (unsigned long)segments_in[t2].global_id, - SEGSIDE_IS_FRONT(cc->max_y_side_id) ? "front" : "back"); - log_err(desc->scene->dev, - "Segment %lu:\n (%g %g) (%g %g)\n", - (unsigned long)segments_in[t1].global_id, - SPLIT2(positions[segments_in[t1].vertice_id[0]].vec), - SPLIT2(positions[segments_in[t1].vertice_id[1]].vec)); - log_err(desc->scene->dev, - "Segment %lu:\n (%g %g) (%g %g)\n", - (unsigned long)segments_in[t2].global_id, - SPLIT2(positions[segments_in[t2].vertice_id[0]].vec), - SPLIT2(positions[segments_in[t2].vertice_id[1]].vec)); - log_err(desc->scene->dev, "Media: %lu VS %lu\n", - (unsigned long)infinity_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; 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); - + 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 t1 = SEGSIDE_2_SEG(hit_side_id); - const seg_id_t t2 = SEGSIDE_2_SEG(cc->max_y_side_id); - const struct segment_in* segments_in - = darray_segment_in_cdata_get(&desc->scene->segments_in); - const union double2* positions - = darray_position_cdata_get(&desc->scene->vertices); - log_err(desc->scene->dev, - "Medium mismatch found between segment %lu %s side and segment" - " %lu %s side facing each other.\n", - (unsigned long)segments_in[t1].global_id, - SEGSIDE_IS_FRONT(hit_side) ? "front" : "back", - (unsigned long)segments_in[t2].global_id, - SEGSIDE_IS_FRONT(cc->max_y_side_id) ? "front" : "back"); - log_err(desc->scene->dev, - "Segment %lu:\n (%g %g) (%g %g)\n", - (unsigned long)segments_in[t1].global_id, - SPLIT2(positions[segments_in[t1].vertice_id[0]].vec), - SPLIT2(positions[segments_in[t1].vertice_id[1]].vec)); - log_err(desc->scene->dev, - "Segment %lu:\n (%g %g) (%g %g)\n", - (unsigned long)segments_in[t2].global_id, - SPLIT2(positions[segments_in[t2].vertice_id[0]].vec), - SPLIT2(positions[segments_in[t2].vertice_id[1]].vec)); - log_err(desc->scene->dev, "Media: %lu VS %lu\n", - (unsigned long)hit_seg_in->medium[hit_side], - (unsigned long)cc->medium); - - *res = RES_BAD_ARG; - } + ASSERT(cc->cc_group_root < cc_count); } } /* Implicit barrier here */ - ASSERT(*next_enclosure_id < ENCLOSURE_MAX__); + ASSERT(ATOMIC_GET(next_enclosure_id) < ENCLOSURE_MAX__); if(*res != RES_OK) return; /* One thread post-processes links to group connex components */ #pragma omp single { res_T tmp_res = RES_OK; - desc->enclosures_count = (enclosure_id_t)*next_enclosure_id; + desc->enclosures_count = (enclosure_id_t)ATOMIC_GET(next_enclosure_id); tmp_res = darray_enclosure_resize(&desc->enclosures, desc->enclosures_count); if(tmp_res != RES_OK) { *res = tmp_res; } else { + struct enclosure_data* enclosures + = darray_enclosure_data_get(&desc->enclosures); FOR_EACH(ccc, 0, cc_count) { component_id_t c = (component_id_t)ccc; struct cc_descriptor* const cc = descriptors[c]; const struct cc_descriptor* other_desc = cc; - struct enclosure_data* enclosures - = darray_enclosure_data_get(&desc->enclosures); - component_id_t fst; + struct enclosure_data* enc; while(other_desc->enclosure_id == CC_GROUP_ID_NONE) { other_desc = *(darray_ptr_component_descriptor_cdata_get(connex_components) @@ -711,19 +610,21 @@ 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; + enc = enclosures + cc->enclosure_id; + ++enc->cc_count; /* Linked list of componnents */ - fst = enclosures[cc->enclosure_id].first_component; - cc->enclosure_next_component = fst; - enclosures[cc->enclosure_id].first_component = cc->cc_id; - enclosures[cc->enclosure_id].side_range.first - = MMIN(enclosures[cc->enclosure_id].side_range.first, cc->side_range.first); - 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; + enc->first_component = cc->cc_id; + enc->side_range.first = MMIN(enc->side_range.first, cc->side_range.first); + enc->side_range.last = MMAX(enc->side_range.last, cc->side_range.last); + enc->side_count += cc->side_count; + tmp_res = bool_array_of_media_merge(&enc->tmp_enclosed_media, cc->media, + desc->scene->nmeds); + if(tmp_res != RES_OK) { + *res = tmp_res; + break; + } } } } @@ -828,30 +729,46 @@ collect_and_link_neighbours neighbour_count = (side_id_t)sz; FOR_EACH(i, 0, neighbour_count) { double max_y, disp[2]; - unsigned char max_y_vrank; struct neighbour_info* neighbour_info = darray_neighbour_data_get(neighbourhood) + i; const struct segment_in* seg_in = segments_in + neighbour_info->seg_id; struct segment_tmp* neighbour = segments_tmp + neighbour_info->seg_id; + + union double2 n; /* Geometrical normal to neighbour segment */ + const int is_reversed = neighbour_info->common_vertex_rank; + other_vrtx = seg_in->vertice_id[(neighbour_info->common_vertex_rank + 1) % 2]; - if(vertices[other_vrtx].pos.y > vertices[common_vrtx].pos.y) { - max_y = vertices[other_vrtx].pos.y; - max_y_vrank = (unsigned char)(1 - neighbour_info->common_vertex_rank); - } else { - max_y = vertices[common_vrtx].pos.y; - max_y_vrank = neighbour_info->common_vertex_rank; - } + max_y = MMAX(vertices[other_vrtx].pos.y, vertices[common_vrtx].pos.y); ASSERT(neighbour->max_y <= max_y); neighbour->max_y = max_y; - neighbour->max_y_vrtx_rank = max_y_vrank; /* Compute rotation angle around common vertex (in world system) */ d2_sub(disp, vertices[other_vrtx].vec, vertices[common_vrtx].vec); ASSERT(disp[0] || disp[1]); neighbour_info->angle = atan2(disp[1], disp[0]); + if(is_reversed) + d2(n.vec, +disp[1], -disp[0]); + else + d2(n.vec, -disp[1], +disp[0]); if(neighbour_info->angle < 0) neighbour_info->angle += 2 * PI; - /* Due to catastrophic cancelation, -eps+2pi translates to 2pi */ - ASSERT(0 <= neighbour_info->angle && neighbour_info->angle <= 2 * PI); + + /* Normal orientation calculation. */ + if(neighbour_info->angle <= PI / 4) { + ASSERT(n.pos.y); + neighbour_info->normal_toward_next_neighbour = (n.pos.y > 0); + } else if(neighbour_info->angle <= 3 * PI / 4) { + ASSERT(n.pos.x); + neighbour_info->normal_toward_next_neighbour = (n.pos.x < 0); + } else if(neighbour_info->angle <= 5 * PI / 4) { + ASSERT(n.pos.y); + neighbour_info->normal_toward_next_neighbour = (n.pos.y < 0); + } else if(neighbour_info->angle <= 7 * PI / 4) { + ASSERT(n.pos.x); + neighbour_info->normal_toward_next_neighbour = (n.pos.x > 0); + } else { + ASSERT(n.pos.y); + neighbour_info->normal_toward_next_neighbour = (n.pos.y > 0); + } } /* Sort segments by rotation angle */ qsort(darray_neighbour_data_get(neighbourhood), neighbour_count, @@ -866,13 +783,20 @@ collect_and_link_neighbours = darray_neighbour_cdata_get(neighbourhood) + (i + 1) % neighbour_count; /* Rank of the end of interest in segments */ const unsigned char crt_end = current->common_vertex_rank; + /* Here ccw refers to the rotation around the common vertex + * and has nothing to do with vertices order in segment definition + * nor Front/Back side convention */ const unsigned char ccw_end = ccw_neighbour->common_vertex_rank; /* User id of current segments */ const seg_id_t crt_id = current->seg_id; const seg_id_t ccw_id = ccw_neighbour->seg_id; /* Facing sides of segments */ - const enum side_id crt_side = crt_end ? SIDE_BACK : SIDE_FRONT; - const enum side_id ccw_side = ccw_end ? SIDE_FRONT : SIDE_BACK; + const int front = ((scn->convention & SENC2D_CONVENTION_NORMAL_FRONT) != 0); + const enum side_id crt_side + = current->normal_toward_next_neighbour == front ? SIDE_FRONT : SIDE_BACK; + const enum side_id ccw_side + = ccw_neighbour->normal_toward_next_neighbour == front ? + SIDE_BACK : SIDE_FRONT; /* Index of sides in segsides */ const side_id_t crt_side_idx = SEGIDxSIDE_2_SEGSIDE(crt_id, crt_side); const side_id_t ccw_side_idx = SEGIDxSIDE_2_SEGSIDE(ccw_id, ccw_side); @@ -887,8 +811,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. */ @@ -912,22 +834,30 @@ build_result struct segment_enc* segments_enc; const struct segment_comp* segments_comp; struct htable_vrtx_id vtable; + struct senc2d_scene* scn; + int output_normal_in, normals_front, normals_back; int64_t sg; int64_t ee; ASSERT(desc && connex_components && segments_comp_array && res); alloc = descriptor_get_allocator(desc); + scn = desc->scene; + output_normal_in = (scn->convention & SENC2D_CONVENTION_NORMAL_INSIDE) != 0; + normals_front = (scn->convention & SENC2D_CONVENTION_NORMAL_FRONT) != 0; + normals_back = (scn->convention & SENC2D_CONVENTION_NORMAL_BACK) != 0; + ASSERT(normals_back != normals_front); + ASSERT(output_normal_in != ((scn->convention & SENC2D_CONVENTION_NORMAL_OUTSIDE) != 0)); 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_in = darray_segment_in_cdata_get(&scn->segments_in); segments_comp = darray_segment_comp_cdata_get(segments_comp_array); #pragma omp single { res_T tmp_res = - darray_segment_enc_resize(&desc->segments_enc, desc->scene->nusegs); + darray_segment_enc_resize(&desc->segments_enc, scn->nusegs); if(tmp_res != RES_OK) *res = tmp_res; }/* Implicit barrier here. */ if(*res != RES_OK) return; @@ -935,7 +865,7 @@ build_result /* Build global enclosure information */ #pragma omp for - for(sg = 0; sg < (int64_t)desc->scene->nusegs; sg++) { + for(sg = 0; sg < (int64_t)scn->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]; @@ -959,34 +889,54 @@ 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]; 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); - /* Build side and vertex lists. */ - tmp_res = darray_segment_in_resize(&enc->sides, enc->side_count); + ASSERT(darray_uchar_size_get(&enc->tmp_enclosed_media) <= UINT_MAX); + ASSERT(enc->header.enclosed_media_count <= scn->nmeds); + OK2(bool_array_of_media_to_darray_media + (&enc->enclosed_media, &enc->tmp_enclosed_media)); + enc->header.enclosed_media_count + = (unsigned)darray_media_size_get(&enc->enclosed_media); + darray_uchar_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(*res != RES_OK) continue; + + /* Build side and vertex lists. */ + OK2(darray_segment_in_resize(&enc->sides, enc->side_count)); /* Size is just a int */ - tmp_res = darray_vrtx_id_reserve(&enc->vertices, enc->side_count + 1); - if(*res != RES_OK) continue; + OK2(darray_vrtx_id_reserve(&enc->vertices, enc->side_count + 1)); /* New vertex numbering scheme local to the enclosure */ htable_vrtx_id_clear(&vtable); - ASSERT(desc->scene->nusegs - == darray_segment_in_size_get(&desc->scene->segments_in)); + ASSERT(scn->nusegs == darray_segment_in_size_get(&scn->segments_in)); /* Put at the end the back-faces of segments that also have their * front-face in the list. */ for(s = SEGSIDE_2_SEG(enc->side_range.first); @@ -1003,16 +953,17 @@ build_result ++enc->header.unique_segment_count; FOR_EACH(i, 0, 2) { - vrtx_id_t* id = htable_vrtx_id_find(&vtable, seg_in->vertice_id + i); - if(id) { - vertice_id[i] = *id; /* Known vertex */ + vrtx_id_t* p_id = htable_vrtx_id_find(&vtable, seg_in->vertice_id + i); + if(p_id) { + vertice_id[i] = *p_id; /* Known vertex */ } else { /* Create new association */ size_t tmp = htable_vrtx_id_size_get(&vtable); ASSERT(tmp == darray_vrtx_id_size_get(&enc->vertices)); ASSERT(tmp < VRTX_MAX__); vertice_id[i] = (vrtx_id_t)tmp; - OK2(htable_vrtx_id_set(&vtable, seg_in->vertice_id + i, vertice_id + i)); + OK2(htable_vrtx_id_set(&vtable, seg_in->vertice_id + i, + vertice_id + i)); OK2(darray_vrtx_id_push_back(&enc->vertices, seg_in->vertice_id + i)); ++enc->header.vertices_count; } @@ -1020,21 +971,38 @@ build_result ASSERT(segments_enc[s].enclosure[SIDE_FRONT] == e || segments_enc[s].enclosure[SIDE_BACK] == e); if(segments_enc[s].enclosure[SIDE_FRONT] == e) { + /* Front side of the original triangle is member of the enclosure */ + int input_normal_in = normals_front; + int revert_segment = (input_normal_in != output_normal_in); ++enc->header.segment_count; seg = darray_segment_in_data_get(&enc->sides) + fst_idx++; - - FOR_EACH(i, 0, 2) seg->medium[i] = seg_in->medium[i]; + FOR_EACH(i, 0, 2) { + int ii = revert_segment ? 1 - i : i; + seg->medium[i] = seg_in->medium[ii]; + } seg->global_id = seg_in->global_id; - FOR_EACH(i, 0, 2) seg->vertice_id[i] = vertice_id[i]; + FOR_EACH(i, 0, 2) { + int ii = revert_segment ? 1 - i : i; + seg->vertice_id[i] = vertice_id[ii]; + } } if(segments_enc[s].enclosure[SIDE_BACK] == e) { + /* Back side of the original triangle is member of the enclosure */ + int input_normal_in = normals_back; + int revert_segment = (input_normal_in != output_normal_in); ++enc->header.segment_count; + /* If both sides are in the enclosure, put the second side at the end */ seg = darray_segment_in_data_get(&enc->sides) + ((segments_enc[s].enclosure[SIDE_FRONT] == e) ? --sgd_idx : fst_idx++); - - FOR_EACH(i, 0, 2) seg->medium[i] = seg_in->medium[1 - i]; + FOR_EACH(i, 0, 2) { + int ii = revert_segment ? 1 - i : i; + seg->medium[i] = seg_in->medium[ii]; + } seg->global_id = seg_in->global_id; - FOR_EACH(i, 0, 2) seg->vertice_id[i] = vertice_id[1 - i]; + FOR_EACH(i, 0, 2) { + int ii = revert_segment ? 1 - i : i; + seg->vertice_id[i] = vertice_id[ii]; + } } if(fst_idx == sgd_idx) break; } @@ -1074,9 +1042,8 @@ senc2d_scene_analyze /* Atomic counters to share beetwen threads */ ATOMIC component_count = 0; ATOMIC next_enclosure_id = 1; - /* Used as args to have shared vars between threads in functions */ - struct cc_descriptor* infinity_first_cc = NULL; res_T res = RES_OK; + res_T res2 = RES_OK; if(!scn || !out_desc) return RES_BAD_ARG; @@ -1102,13 +1069,14 @@ 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)); /* The end of the analyze is multithreaded */ - #pragma omp parallel + ASSERT(scn->dev->nthreads > 0); + #pragma omp parallel num_threads(scn->dev->nthreads) { /* Step 1: build neighbourhoods */ collect_and_link_neighbours(scn, segsides, &segments_tmp, @@ -1117,19 +1085,12 @@ senc2d_scene_analyze * released / data produced by step 1 cannot be used * until next sync point */ - if(res != RES_OK) { - #pragma omp single nowait - { - log_err(scn->dev, - "%s: could not build neighbourhoods from scene.\n", FUNC_NAME); - } /* No barrier here */ - } - if(res != RES_OK) goto error_; - - /* The first thread here allocates some data */ + /* The first thread here allocates some data. + * Barrier needed at the end to ensure data created before any use. */ #pragma omp single { res_T tmp_res = RES_OK; + darray_ptr_component_descriptor_init(scn->dev->allocator, &connex_components); connex_components_initialized = 1; @@ -1140,10 +1101,22 @@ senc2d_scene_analyze segments_comp_initialized = 1; OK2(darray_segment_comp_resize(&segments_comp, scn->nusegs)); tmp_error: - if(tmp_res != RES_OK) res = tmp_res; + if(tmp_res != RES_OK) res2 = tmp_res; } /* Implicit barrier here: constraints on step 1 data are now met */ - if(res != RES_OK) goto error_; + + if(res != RES_OK || res2 != RES_OK) { + #pragma omp single nowait + { + if(res != RES_OK) { + log_err(scn->dev, + "%s: could not build neighbourhoods from scene.\n", FUNC_NAME); + } else { + res = res2; + } + } + goto error_; + } /* One thread releases some data before going to step 2, * the others go to step 2 without sync */ @@ -1160,17 +1133,17 @@ senc2d_scene_analyze * released / data produced by step 2 cannot be used * until next sync point */ + #pragma omp barrier + /* Constraints on step 2 data are now met */ + if(res != RES_OK) { #pragma omp single nowait { log_err(scn->dev, "%s: could not extract connex components from scene.\n", FUNC_NAME); } /* No barrier here */ + goto error_; } - if(res != RES_OK) goto error_; - - #pragma omp barrier - /* Constraints on step 2 data are now met */ /* One thread releases some data before going to step 3, * the others go to step 3 without sync */ @@ -1182,9 +1155,18 @@ senc2d_scene_analyze /* Step 3: group components */ group_connex_components(desc, segsides, &segments_comp, &connex_components, - s2d_view, &next_enclosure_id, &infinity_first_cc, &res); + s2d_view, &next_enclosure_id, &res); /* Barrier at the end of step 3: data used in step 3 can be released / - * data produced by step 2 can be used */ + * data produced by step 3 can be used */ + + if(res != RES_OK) { + #pragma omp single nowait + { + log_err(scn->dev, + "%s: could not group connex components from scene.\n", FUNC_NAME); + } + goto error_; + } /* One thread releases some data before going to step 4, * the others go to step 4 without sync */ @@ -1194,38 +1176,28 @@ senc2d_scene_analyze s2d_view = NULL; } /* No barrier here */ - if(res != RES_OK) { - #pragma omp single nowait - { - log_err(scn->dev, - "%s: could not group connex components from scene.\n", FUNC_NAME); - } /* No barrier here */ - } - if(res != RES_OK) goto error_; - /* 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 * until next sync point */ + #pragma omp barrier + /* Constraints on step 4 data are now met */ + if(res != RES_OK) { #pragma omp single nowait { log_err(scn->dev, "%s: could not build result.\n", FUNC_NAME); - } /* No barrier here */ + } + goto error_; } - if(res != RES_OK) goto error_; - - #pragma omp barrier - /* Constraints on step 4 data are now met */ /* Some threads release data */ #pragma omp sections nowait { #pragma omp section { - ASSERT(connex_components_initialized); custom_darray_ptr_component_descriptor_release(&connex_components); connex_components_initialized = 0; } diff --git a/src/senc2d_scene_analyze_c.h b/src/senc2d_scene_analyze_c.h @@ -20,69 +20,45 @@ #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 }; -/* Descriptors for connex component. +#define DARRAY_NAME side_id +#define DARRAY_DATA side_id_t +#include <rsys/dynamic_array.h> + +/* 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 { - double max_vrtx[2]; - double max_y_ny; - side_id_t max_y_side_id; - vrtx_id_t max_y_vrtx_id; + /* 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; /* 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; - /* To create by-medium linked lists of componnents */ - component_id_t enclosure_next_component; /* Range of sides member of this component */ struct side_range side_range; - + /* Media used by this component */ + unsigned char* media; }; extern const struct cc_descriptor CC_DESCRIPTOR_NULL; @@ -122,7 +98,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; + MEM_RM(array->allocator, components[c]->media); + MEM_RM(array->allocator, components[c]); + } darray_ptr_component_descriptor_release(array); } @@ -135,8 +115,6 @@ custom_darray_ptr_component_descriptor_release * - segment_enc for information describing enclosures (kept in * senc2d_descriptor). */ struct segment_tmp { - /* tmp data used to find the +Y-most vertex of components */ - unsigned char max_y_vrtx_rank; double max_y; }; @@ -145,7 +123,6 @@ static FINLINE void segment_tmp_init(struct mem_allocator* alloc, struct segment_tmp* seg) { (void)alloc; ASSERT(seg); - seg->max_y_vrtx_rank = UCHAR_MAX; seg->max_y = -DBL_MAX; } #define DARRAY_FUNCTOR_INIT segment_tmp_init @@ -160,6 +137,9 @@ struct neighbour_info { seg_id_t seg_id; /* Rank of the vertex in the segment (in [0 1]) */ unsigned char common_vertex_rank; + /* Does the geometrical normal point towards the next neighbour + * (if not, it points towards the previous one)? */ + unsigned char normal_toward_next_neighbour; }; #define DARRAY_NAME neighbour #define DARRAY_DATA struct neighbour_info diff --git a/src/senc2d_scene_c.h b/src/senc2d_scene_c.h @@ -154,6 +154,9 @@ side_range_init(struct mem_allocator* alloc, struct side_range* data) #include <rsys/dynamic_array.h> struct senc2d_scene { + /* Front / Back sides convention */ + enum senc2d_convention convention; + /* Segment information as given by user; no duplicates here */ struct darray_segment_in segments_in; diff --git a/src/test_senc2d_descriptor.c b/src/test_senc2d_descriptor.c @@ -19,8 +19,6 @@ #include <rsys/float2.h> #include <rsys/double2.h> -#include <star/s2d.h> - int main(int argc, char** argv) { @@ -30,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]; @@ -41,11 +39,13 @@ main(int argc, char** argv) CHK(senc2d_device_create(NULL, &allocator, SENC2D_NTHREADS_DEFAULT, 1, &dev) == RES_OK); - CHK(senc2d_scene_create(dev, 2, &scn) == RES_OK); + CHK(senc2d_scene_create(dev, + SENC2D_CONVENTION_NORMAL_FRONT | SENC2D_CONVENTION_NORMAL_INSIDE, &scn) + == RES_OK); /* A 2D square */ - ctx.positions = square_vertices; - ctx.indices = square_indices; + ctx.positions = box_vertices; + ctx.indices = box_indices; ctx.scale = 1; ctx.reverse_vrtx = 0; ctx.reverse_med = 0; @@ -53,8 +53,8 @@ main(int argc, char** argv) ctx.front_media = medium0; ctx.back_media = medium1; - CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, NULL, - square_nvertices, get_position, &ctx) == RES_OK); + CHK(senc2d_scene_add_geometry(scn, nsegments, get_indices, get_media, NULL, + nvertices, get_position, &ctx) == RES_OK); CHK(senc2d_scene_analyze(scn, &desc) == RES_OK); @@ -63,6 +63,13 @@ 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, &maxm) == RES_BAD_ARG); + CHK(senc2d_descriptor_get_max_medium(desc, NULL) == RES_BAD_ARG); + CHK(senc2d_descriptor_get_max_medium(NULL, NULL) == RES_BAD_ARG); + CHK(senc2d_descriptor_get_max_medium(desc, &maxm) == RES_OK); + + 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); CHK(senc2d_descriptor_get_enclosure_count(NULL, NULL) == RES_BAD_ARG); @@ -70,42 +77,80 @@ main(int argc, char** argv) CHK(count == 2); - CHK(senc2d_descriptor_get_enclosure(NULL, 0, &enc) == RES_BAD_ARG); - CHK(senc2d_descriptor_get_enclosure(desc, count, &enc) == RES_BAD_ARG); + CHK(senc2d_descriptor_get_enclosure_count_by_medium(NULL, 0, &count) + == RES_BAD_ARG); + CHK(senc2d_descriptor_get_enclosure_count_by_medium(desc, 9, &count) + == RES_BAD_ARG); + CHK(senc2d_descriptor_get_enclosure_count_by_medium(desc, 0, NULL) + == RES_BAD_ARG); + CHK(senc2d_descriptor_get_enclosure_count_by_medium(NULL, 9, &count) + == RES_BAD_ARG); + CHK(senc2d_descriptor_get_enclosure_count_by_medium(NULL, 0, NULL) + == RES_BAD_ARG); + CHK(senc2d_descriptor_get_enclosure_count_by_medium(desc, 9, NULL) + == RES_BAD_ARG); + CHK(senc2d_descriptor_get_enclosure_count_by_medium(NULL, 9, NULL) + == RES_BAD_ARG); + CHK(senc2d_descriptor_get_enclosure_count_by_medium(desc, 0, &count) + == RES_OK); + + CHK(count == 1); + CHK(senc2d_descriptor_get_enclosure(desc, 0, NULL) == RES_BAD_ARG); - CHK(senc2d_descriptor_get_enclosure(NULL, count, &enc) == RES_BAD_ARG); + CHK(senc2d_descriptor_get_enclosure(desc, 9, &enc) == RES_BAD_ARG); + CHK(senc2d_descriptor_get_enclosure(desc, 9, NULL) == RES_BAD_ARG); + CHK(senc2d_descriptor_get_enclosure(NULL, 0, &enc) == RES_BAD_ARG); CHK(senc2d_descriptor_get_enclosure(NULL, 0, NULL) == RES_BAD_ARG); - CHK(senc2d_descriptor_get_enclosure(desc, count, NULL) == RES_BAD_ARG); - CHK(senc2d_descriptor_get_enclosure(NULL, count, NULL) == RES_BAD_ARG); + CHK(senc2d_descriptor_get_enclosure(NULL, 9, &enc) == RES_BAD_ARG); + CHK(senc2d_descriptor_get_enclosure(NULL, 9, NULL) == RES_BAD_ARG); CHK(senc2d_descriptor_get_enclosure(desc, 0, &enc) == RES_OK); + CHK(senc2d_enclosure_ref_put(enc) == RES_OK); + + CHK(senc2d_descriptor_get_enclosure_by_medium(desc, 0, 0, NULL) == RES_BAD_ARG); + CHK(senc2d_descriptor_get_enclosure_by_medium(desc, 0, 9, &enc) == RES_BAD_ARG); + CHK(senc2d_descriptor_get_enclosure_by_medium(desc, 0, 9, NULL) == RES_BAD_ARG); + CHK(senc2d_descriptor_get_enclosure_by_medium(desc, 9, 0, &enc) == RES_BAD_ARG); + CHK(senc2d_descriptor_get_enclosure_by_medium(desc, 9, 0, NULL) == RES_BAD_ARG); + CHK(senc2d_descriptor_get_enclosure_by_medium(desc, 9, 9, &enc) == RES_BAD_ARG); + CHK(senc2d_descriptor_get_enclosure_by_medium(desc, 9, 9, NULL) == RES_BAD_ARG); + CHK(senc2d_descriptor_get_enclosure_by_medium(NULL, 0, 0, &enc) == RES_BAD_ARG); + CHK(senc2d_descriptor_get_enclosure_by_medium(NULL, 0, 0, NULL) == RES_BAD_ARG); + CHK(senc2d_descriptor_get_enclosure_by_medium(NULL, 0, 9, &enc) == RES_BAD_ARG); + CHK(senc2d_descriptor_get_enclosure_by_medium(NULL, 0, 9, NULL) == RES_BAD_ARG); + CHK(senc2d_descriptor_get_enclosure_by_medium(NULL, 9, 0, &enc) == RES_BAD_ARG); + CHK(senc2d_descriptor_get_enclosure_by_medium(NULL, 9, 0, NULL) == RES_BAD_ARG); + CHK(senc2d_descriptor_get_enclosure_by_medium(NULL, 9, 9, &enc) == RES_BAD_ARG); + CHK(senc2d_descriptor_get_enclosure_by_medium(NULL, 9, 9, NULL) == RES_BAD_ARG); + CHK(senc2d_descriptor_get_enclosure_by_medium(desc, 0, 0, &enc) == RES_OK); + CHK(senc2d_descriptor_get_global_vertices_count(NULL, &count) == RES_BAD_ARG); CHK(senc2d_descriptor_get_global_vertices_count(desc, NULL) == RES_BAD_ARG); CHK(senc2d_descriptor_get_global_vertices_count(desc, &count) == RES_OK); - CHK(count == square_nvertices); + CHK(count == nvertices); CHK(senc2d_descriptor_get_global_segments_count(NULL, &count) == RES_BAD_ARG); CHK(senc2d_descriptor_get_global_segments_count(desc, NULL) == RES_BAD_ARG); CHK(senc2d_descriptor_get_global_segments_count(desc, &count) == RES_OK); - CHK(count == square_nsegments); + CHK(count == nsegments); CHK(senc2d_descriptor_get_global_segment(NULL, 0, indices) == RES_BAD_ARG); - CHK(senc2d_descriptor_get_global_segment(NULL, square_nsegments, indices) + CHK(senc2d_descriptor_get_global_segment(NULL, nsegments, indices) == RES_BAD_ARG); CHK(senc2d_descriptor_get_global_segment(desc, 0, NULL) == RES_BAD_ARG); CHK(senc2d_descriptor_get_global_segment(desc, 0, indices) == RES_OK); - CHK(indices[0] == square_indices[0] && indices[1] == square_indices[1]); + CHK(indices[0] == box_indices[0] && indices[1] == box_indices[1]); CHK(senc2d_descriptor_get_global_vertex(NULL, 0, coord) == RES_BAD_ARG); - CHK(senc2d_descriptor_get_global_vertex(NULL, square_nvertices, coord) + CHK(senc2d_descriptor_get_global_vertex(NULL, nvertices, coord) == RES_BAD_ARG); CHK(senc2d_descriptor_get_global_vertex(desc, 0, NULL) == RES_BAD_ARG); CHK(senc2d_descriptor_get_global_vertex(desc, 0, coord) == RES_OK); - CHK(coord[0] == square_vertices[0] && coord[1] == square_vertices[1]); + CHK(coord[0] == box_vertices[0] && coord[1] == box_vertices[1]); CHK(senc2d_descriptor_get_global_segment_media(NULL, 0, media) == RES_BAD_ARG); - CHK(senc2d_descriptor_get_global_segment_media(NULL, square_nvertices, media) + CHK(senc2d_descriptor_get_global_segment_media(NULL, nvertices, media) == RES_BAD_ARG); CHK(senc2d_descriptor_get_global_segment_media(desc, 0, NULL) == RES_BAD_ARG); @@ -115,7 +160,7 @@ main(int argc, char** argv) CHK(senc2d_descriptor_get_global_segment_enclosures( NULL, 0, enclosures) == RES_BAD_ARG); CHK(senc2d_descriptor_get_global_segment_enclosures( - NULL, square_nvertices, enclosures) == RES_BAD_ARG); + NULL, nvertices, enclosures) == RES_BAD_ARG); CHK(senc2d_descriptor_get_global_segment_enclosures(desc, 0, NULL) == RES_BAD_ARG); CHK(senc2d_descriptor_get_global_segment_enclosures(desc, 0, enclosures) @@ -125,25 +170,25 @@ main(int argc, char** argv) /* Add valid duplicate geometry */ CHK(senc2d_descriptor_ref_put(desc) == RES_OK); desc = NULL; - CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, - NULL, square_nvertices, get_position, &ctx) == RES_OK); + CHK(senc2d_scene_add_geometry(scn, nsegments, get_indices, get_media, + NULL, nvertices, get_position, &ctx) == RES_OK); CHK(senc2d_scene_analyze(scn, &desc) == RES_OK); CHK(senc2d_descriptor_get_global_vertices_count(desc, &count) == RES_OK); /* Duplicate vertices have been replaced */ - CHK(count == square_nvertices); + CHK(count == nvertices); CHK(senc2d_descriptor_get_global_segments_count(desc, &count) == RES_OK); /* Duplicate segments have been replaced */ - CHK(count == square_nsegments); + CHK(count == nsegments); /* Add invalid duplicate geometry */ CHK(senc2d_descriptor_ref_put(desc) == RES_OK); desc = NULL; ctx.front_media = medium1; ctx.back_media = medium0; - CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, NULL, - square_nvertices, get_position, &ctx) == RES_BAD_ARG); + CHK(senc2d_scene_add_geometry(scn, nsegments, get_indices, get_media, NULL, + nvertices, get_position, &ctx) == RES_BAD_ARG); CHK(senc2d_scene_ref_put(scn) == RES_OK); CHK(senc2d_device_ref_put(dev) == RES_OK); diff --git a/src/test_senc2d_enclosure.c b/src/test_senc2d_enclosure.c @@ -17,13 +17,12 @@ #include "senc2d_s2d_wrapper.h" #include "test_senc2d_utils.h" -#include <rsys/float2.h> #include <rsys/double2.h> #include <star/s2d.h> -int -main(int argc, char** argv) +static void +test(enum senc2d_convention convention) { struct mem_allocator allocator; struct senc2d_descriptor* desc = NULL; @@ -31,24 +30,30 @@ main(int argc, char** argv) struct senc2d_scene* scn = NULL; struct senc2d_enclosure* enclosures[2] = { NULL, NULL }; struct senc2d_enclosure* enclosure; - const struct enclosure2d_header* header; + struct senc2d_enclosure_header header; struct s2d_device* s2d = NULL; struct s2d_scene* s2d_scn = NULL; struct s2d_shape* s2d_shp = NULL; struct s2d_vertex_data s2d_attribs; unsigned indices[2][2]; - unsigned medium[2]; + unsigned medium, media[2]; unsigned gid; double vrtx[2]; struct context ctx; unsigned i, n, t, ecount; - (void)argc, (void)argv; + enum senc2d_convention conv; + int is_front, is_in; CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK); CHK(senc2d_device_create(NULL, &allocator, SENC2D_NTHREADS_DEFAULT, 1, &dev) == RES_OK); - CHK(senc2d_scene_create(dev, 2, &scn) == RES_OK); + CHK(senc2d_scene_create(dev, convention, &scn) == RES_OK); + + CHK(senc2d_scene_get_convention(scn, &conv) == RES_OK); + CHK(conv == convention); + is_front = (conv & SENC2D_CONVENTION_NORMAL_FRONT) != 0; + is_in = (conv & SENC2D_CONVENTION_NORMAL_INSIDE) != 0; s2d_attribs.type = S2D_FLOAT2; s2d_attribs.usage = S2D_POSITION; @@ -61,8 +66,8 @@ main(int argc, char** argv) /* A 2D square. * 2 enclosures (inside, outside) sharing the same segments, * but opposite sides */ - ctx.positions = square_vertices; - ctx.indices = square_indices; + ctx.positions = box_vertices; + ctx.indices = box_indices; ctx.scale = 1; ctx.reverse_vrtx = 0; ctx.reverse_med = 0; @@ -70,8 +75,8 @@ main(int argc, char** argv) ctx.front_media = medium0; ctx.back_media = medium1; - CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, - NULL, square_nvertices, get_position, &ctx) == RES_OK); + CHK(senc2d_scene_add_geometry(scn, nsegments, get_indices, get_media, + NULL, nvertices, get_position, &ctx) == RES_OK); CHK(senc2d_scene_analyze(scn, &desc) == RES_OK); @@ -85,55 +90,64 @@ main(int argc, char** argv) CHK(senc2d_enclosure_ref_put(enclosure) == RES_OK); CHK(senc2d_enclosure_get_segment(NULL, 0, indices[0]) == RES_BAD_ARG); - CHK(senc2d_enclosure_get_segment(enclosure, square_nsegments, indices[0]) + CHK(senc2d_enclosure_get_segment(enclosure, nsegments, indices[0]) == RES_BAD_ARG); CHK(senc2d_enclosure_get_segment(enclosure, 0, NULL) == RES_BAD_ARG); - CHK(senc2d_enclosure_get_segment(NULL, square_nsegments, indices[0]) + CHK(senc2d_enclosure_get_segment(NULL, nsegments, indices[0]) == RES_BAD_ARG); CHK(senc2d_enclosure_get_segment(NULL, 0, NULL) == RES_BAD_ARG); - CHK(senc2d_enclosure_get_segment(enclosure, square_nsegments, NULL) + CHK(senc2d_enclosure_get_segment(enclosure, nsegments, NULL) == RES_BAD_ARG); - CHK(senc2d_enclosure_get_segment(NULL, square_nsegments, NULL) == RES_BAD_ARG); + CHK(senc2d_enclosure_get_segment(NULL, nsegments, NULL) == RES_BAD_ARG); CHK(senc2d_enclosure_get_segment(enclosure, 0, indices[0]) == RES_OK); CHK(senc2d_enclosure_get_vertex(NULL, 0, vrtx) == RES_BAD_ARG); - CHK(senc2d_enclosure_get_vertex(enclosure, square_nvertices, vrtx) + CHK(senc2d_enclosure_get_vertex(enclosure, nvertices, vrtx) == RES_BAD_ARG); CHK(senc2d_enclosure_get_vertex(enclosure, 0, NULL) == RES_BAD_ARG); - CHK(senc2d_enclosure_get_vertex(NULL, square_nvertices, vrtx) == RES_BAD_ARG); + CHK(senc2d_enclosure_get_vertex(NULL, nvertices, vrtx) == RES_BAD_ARG); CHK(senc2d_enclosure_get_vertex(NULL, 0, NULL) == RES_BAD_ARG); - CHK(senc2d_enclosure_get_vertex(enclosure, square_nvertices, NULL) + CHK(senc2d_enclosure_get_vertex(enclosure, nvertices, NULL) == RES_BAD_ARG); - CHK(senc2d_enclosure_get_vertex(NULL, square_nvertices, NULL) == RES_BAD_ARG); + CHK(senc2d_enclosure_get_vertex(NULL, nvertices, NULL) == RES_BAD_ARG); CHK(senc2d_enclosure_get_vertex(enclosure, 0, vrtx) == RES_OK); - CHK(senc2d_enclosure_get_segment_media(NULL, 0, medium) == RES_BAD_ARG); - CHK(senc2d_enclosure_get_segment_media(enclosure, square_nsegments, medium) + CHK(senc2d_enclosure_get_segment_media(NULL, 0, media) == RES_BAD_ARG); + CHK(senc2d_enclosure_get_segment_media(enclosure, nsegments, media) == RES_BAD_ARG); CHK(senc2d_enclosure_get_segment_media(enclosure, 0, NULL) == RES_BAD_ARG); - CHK(senc2d_enclosure_get_segment_media(NULL, square_nsegments, medium) + CHK(senc2d_enclosure_get_segment_media(NULL, nsegments, media) == RES_BAD_ARG); CHK(senc2d_enclosure_get_segment_media(NULL, 0, NULL) == RES_BAD_ARG); - CHK(senc2d_enclosure_get_segment_media(enclosure, square_nsegments, NULL) + CHK(senc2d_enclosure_get_segment_media(enclosure, nsegments, NULL) == RES_BAD_ARG); - CHK(senc2d_enclosure_get_segment_media(NULL, square_nsegments, NULL) + CHK(senc2d_enclosure_get_segment_media(NULL, nsegments, NULL) == RES_BAD_ARG); - CHK(senc2d_enclosure_get_segment_media(enclosure, 0, medium) == RES_OK); + CHK(senc2d_enclosure_get_segment_media(enclosure, 0, media) == RES_OK); CHK(senc2d_enclosure_get_segment_global_id(NULL, 0, &gid) == RES_BAD_ARG); - CHK(senc2d_enclosure_get_segment_global_id(enclosure, square_nsegments, &gid) + CHK(senc2d_enclosure_get_segment_global_id(enclosure, nsegments, &gid) == RES_BAD_ARG); CHK(senc2d_enclosure_get_segment_global_id(enclosure, 0, NULL) == RES_BAD_ARG); - CHK(senc2d_enclosure_get_segment_global_id(NULL, square_nsegments, &gid) + CHK(senc2d_enclosure_get_segment_global_id(NULL, nsegments, &gid) == RES_BAD_ARG); CHK(senc2d_enclosure_get_segment_global_id(NULL, 0, NULL) == RES_BAD_ARG); - CHK(senc2d_enclosure_get_segment_global_id(enclosure, square_nsegments, NULL) + CHK(senc2d_enclosure_get_segment_global_id(enclosure, nsegments, NULL) == RES_BAD_ARG); - CHK(senc2d_enclosure_get_segment_global_id(NULL, square_nsegments, NULL) + 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,14 +158,18 @@ main(int argc, char** argv) CHK(senc2d_enclosure_get_header(NULL, NULL) == RES_BAD_ARG); CHK(senc2d_enclosure_get_header(enclosure, &header) == RES_OK); - CHK(header->enclosure_id == i); - CHK(header->enclosed_medium == (i == 0 ? 0U : 1U)); - CHK(header->segment_count == square_nsegments); - CHK(header->unique_segment_count == square_nsegments); - CHK(header->vertices_count == square_nvertices); - CHK(header->is_infinite == (i == 0)); - - FOR_EACH(t, 0, header->segment_count) { + CHK(header.enclosure_id == i); + CHK(header.enclosed_media_count == 1); + CHK(senc2d_enclosure_get_medium(enclosure, 0, &medium) == RES_OK); + /* Geometrical normals point outside the square in input segments: + * if convention is front, front medium (0) is outside, + * that is medium 0's enclosure is infinite */ + CHK(is_front == ((medium == 0) == header.is_infinite)); + CHK(header.segment_count == nsegments); + CHK(header.unique_segment_count == nsegments); + CHK(header.vertices_count == nvertices); + + FOR_EACH(t, 0, header.segment_count) { CHK(senc2d_enclosure_get_segment_global_id(enclosure, t, &gid) == RES_OK); CHK(gid == t); } @@ -161,13 +179,21 @@ main(int argc, char** argv) FOR_EACH(i, 0, 2) CHK(senc2d_descriptor_get_enclosure(desc, i, enclosures + i) == RES_OK); - FOR_EACH(n, 0, square_nsegments) { + FOR_EACH(n, 0, nsegments) { + int same, reversed; /* Read same segments in both enclosures */ FOR_EACH(i, 0, 2) CHK(senc2d_enclosure_get_segment(enclosures[i], n, indices[i]) == RES_OK); - /* Same segments, opposite sides */ - CHK(indices[0][0] == indices[1][1]); - CHK(indices[0][1] == indices[1][0]); + /* Same segments and opposite sides for the 2 enclosures */ + FOR_EACH(i, 0, 2) CHK(indices[0][i] == indices[1][1 - i]); + /* Enclosure 0 is outside (and contains medium 0 if convention is front). + * Geometrical normals in output data point in the same direction that those + * of input segments for enclosure 0 iff convention is inside. + * The opposite holds for enclosure 1. */ + cmp_seg(n, enclosures[0], box_indices + 2 * n, box_vertices, &same, &reversed); + CHK((same && !reversed) == is_in); + cmp_seg(n, enclosures[1], box_indices + 2 * n, box_vertices, &same, &reversed); + CHK(same && reversed == is_in); } FOR_EACH(i, 0, 2) CHK(senc2d_enclosure_ref_put(enclosures[i]) == RES_OK); @@ -178,17 +204,19 @@ main(int argc, char** argv) /* Same 2D square, but with a hole (incomplete). * 1 single enclosure including both sides of segments */ - CHK(senc2d_scene_create(dev, 2, &scn) == RES_OK); + CHK(senc2d_scene_create(dev, + SENC2D_CONVENTION_NORMAL_BACK | SENC2D_CONVENTION_NORMAL_INSIDE, &scn) + == RES_OK); - ctx.positions = square_vertices; - ctx.indices = square_indices; + ctx.positions = box_vertices; + ctx.indices = box_indices; ctx.scale = 1; d2(ctx.offset, 0, 0); ctx.front_media = medium0; ctx.back_media = medium0; - CHK(senc2d_scene_add_geometry(scn, square_nsegments - 1, get_indices, get_media, - NULL, square_nvertices, get_position, &ctx) == RES_OK); + CHK(senc2d_scene_add_geometry(scn, nsegments - 1, get_indices, get_media, + NULL, nvertices, get_position, &ctx) == RES_OK); CHK(senc2d_scene_analyze(scn, &desc) == RES_OK); @@ -204,26 +232,26 @@ main(int argc, char** argv) CHK(senc2d_enclosure_get_header(NULL, NULL) == RES_BAD_ARG); CHK(senc2d_enclosure_get_header(enclosure, &header) == RES_OK); - CHK(header->enclosure_id == 0); - CHK(header->enclosed_medium == 0); - CHK(header->segment_count == 2 * header->unique_segment_count); - CHK(header->unique_segment_count == square_nsegments - 1); - CHK(header->vertices_count == square_nvertices); - CHK(header->is_infinite == 1); + CHK(header.enclosure_id == 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); + CHK(header.is_infinite == 1); - FOR_EACH(t, 0, header->unique_segment_count) { + FOR_EACH(t, 0, header.unique_segment_count) { /* The first unique_segment_count segments of an enclosure * are unique segments */ CHK(senc2d_enclosure_get_segment_global_id(enclosure, t, &gid) == RES_OK); CHK(gid == t); } - FOR_EACH(n, 0, header->unique_segment_count) { + FOR_EACH(n, 0, header.unique_segment_count) { /* Put geometry in a 2D view */ CHK(s2d_shape_create_line_segments(s2d, &s2d_shp) == RES_OK); - CHK(s2d_line_segments_setup_indexed_vertices(s2d_shp, header->segment_count, - senc2d_enclosure_get_segment__, header->vertices_count, &s2d_attribs, + CHK(s2d_line_segments_setup_indexed_vertices(s2d_shp, header.segment_count, + senc2d_enclosure_get_segment__, header.vertices_count, &s2d_attribs, 1, enclosure) == RES_OK); @@ -242,6 +270,15 @@ main(int argc, char** argv) check_memory_allocator(&allocator); mem_shutdown_proxy_allocator(&allocator); CHK(mem_allocated_size() == 0); +} +int +main(int argc, char** argv) +{ + (void) argc, (void) argv; + test(SENC2D_CONVENTION_NORMAL_FRONT | SENC2D_CONVENTION_NORMAL_INSIDE); + test(SENC2D_CONVENTION_NORMAL_BACK | SENC2D_CONVENTION_NORMAL_INSIDE); + test(SENC2D_CONVENTION_NORMAL_FRONT | SENC2D_CONVENTION_NORMAL_OUTSIDE); + test(SENC2D_CONVENTION_NORMAL_BACK | SENC2D_CONVENTION_NORMAL_OUTSIDE); return 0; } diff --git a/src/test_senc2d_inconsistant_square.c b/src/test_senc2d_inconsistant_square.c @@ -0,0 +1,150 @@ +/* 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 "senc2d_s2d_wrapper.h" +#include "test_senc2d_utils.h" + +#include <rsys/double2.h> + +/* The following array lists the indices toward the 2D vertices of each +* segment. + * Y + * 2----3 | + * | | 0----X + * | | + * 0----1 */ +/* Segment #1 order is not consistant with other segments */ +static unsigned +inconsistant_box_indices[4/*#segments*/ * 2/*#indices per segment*/] = { + 2, 0, + 2, 3, + 3, 1, + 1, 0 +}; +static const unsigned inconsistant_box_nsegments += sizeof(inconsistant_box_indices) / (2 * sizeof(*inconsistant_box_indices)); + +/* Media definitions reflect segment #1 inconsistancy */ +static const unsigned +inconsistant_medium_front[4] = { 1, 0, 0, 0 }; +static const unsigned +inconsistant_medium_back[4] = { 0, 1, 1, 1 }; + +static void +test(enum senc2d_convention convention) +{ + struct mem_allocator allocator; + struct senc2d_descriptor* desc = NULL; + struct senc2d_device* dev = NULL; + struct senc2d_scene* scn = NULL; + struct senc2d_enclosure* enclosure; + struct senc2d_enclosure_header header; + enum senc2d_convention conv; + int conv_front, conv_in; + struct context ctx; + unsigned i, e, ecount; + + CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK); + CHK(senc2d_device_create(NULL, &allocator, SENC2D_NTHREADS_DEFAULT, 1, &dev) + == RES_OK); + + CHK(senc2d_scene_create(dev, convention, &scn) == RES_OK); + + /* A 2D square. + * 2 enclosures (inside, outside) sharing the same segments, + * but opposite sides. + * What differs in this test is that segment #0 vertices are given + * in the opposite order. */ + ctx.positions = box_vertices; + ctx.indices = inconsistant_box_indices; + ctx.scale = 1; + ctx.reverse_vrtx = 0; + ctx.reverse_med = 0; + d2(ctx.offset, 0, 0); + ctx.front_media = inconsistant_medium_front; + ctx.back_media = inconsistant_medium_back; + + CHK(senc2d_scene_add_geometry(scn, inconsistant_box_nsegments, get_indices, + get_media, NULL, nvertices, get_position, &ctx) == RES_OK); + + CHK(senc2d_scene_analyze(scn, &desc) == RES_OK); + + CHK(senc2d_descriptor_get_enclosure_count(desc, &ecount) == RES_OK); + CHK(ecount == 2); + + CHK(senc2d_scene_get_convention(scn, &conv) == RES_OK); + CHK(conv == convention); + conv_front = (conv & SENC2D_CONVENTION_NORMAL_FRONT) != 0; + conv_in = (conv & SENC2D_CONVENTION_NORMAL_INSIDE) != 0; + + FOR_EACH(e, 0, ecount) { + unsigned medium, expected_external_medium, expected_medium; + char name[128]; + int front_inside; + int expected_side; + 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, &medium) == RES_OK); + /* If NORMAL_FRONT the external enclosure's medium should be 0, + * 1 if NORMAL_BACK */ + expected_external_medium = conv_front ? 0 : 1; + expected_medium = (header.is_infinite ? + expected_external_medium : !expected_external_medium); + CHK(medium == expected_medium); + /* Common media, for non input triangles */ + front_inside = (conv_front == conv_in); + expected_side = front_inside ? 0 : 1; + + sprintf(name, "test_inconsistant_square_%s_%s_%u.obj", + conv_front ? "front" : "back", conv_in ? "in" : "out", e); + dump_enclosure(desc, e, name); + + FOR_EACH(i, 0, header.segment_count) { + int same, reversed, fst_reversed; + unsigned med[2]; + fst_reversed = (e == 0) == conv_in; + CHK(senc2d_enclosure_get_segment_media(enclosure, i, med) == RES_OK); + CHK(med[expected_side] == medium); + cmp_seg(i, enclosure, + inconsistant_box_indices + (2 * i), box_vertices, + &same, &reversed); + /* Should be made of the same segments */ + CHK(same); + CHK(i ? reversed != fst_reversed : reversed == fst_reversed); + } + SENC2D(enclosure_ref_put(enclosure)); + } + + SENC2D(scene_ref_put(scn)); + SENC2D(device_ref_put(dev)); + SENC2D(descriptor_ref_put(desc)); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHK(mem_allocated_size() == 0); +} + +int main(int argc, char** argv) +{ + (void) argc, (void) argv; + + test(SENC2D_CONVENTION_NORMAL_FRONT | SENC2D_CONVENTION_NORMAL_INSIDE); + test(SENC2D_CONVENTION_NORMAL_BACK | SENC2D_CONVENTION_NORMAL_INSIDE); + test(SENC2D_CONVENTION_NORMAL_FRONT | SENC2D_CONVENTION_NORMAL_OUTSIDE); + test(SENC2D_CONVENTION_NORMAL_BACK | SENC2D_CONVENTION_NORMAL_OUTSIDE); + return 0; +} diff --git a/src/test_senc2d_many_enclosures.c b/src/test_senc2d_many_enclosures.c @@ -26,7 +26,7 @@ static void get_ctx_indices(const unsigned iseg, unsigned ids[2], void* context) { struct context* ctx = context; - (void) ctx; + (void)ctx; ASSERT(ids && ctx); ASSERT(2 * iseg + 1 < sa_size(ctx->indices)); get_indices(iseg, ids, context); @@ -36,7 +36,7 @@ static void get_ctx_position(const unsigned ivert, double pos[2], void* context) { struct context* ctx = context; - (void) ctx; + (void)ctx; ASSERT(pos && ctx); ASSERT(2 * ivert + 1 < sa_size(ctx->positions)); get_position(ivert, pos, context); @@ -46,7 +46,7 @@ static void get_ctx_media(const unsigned iseg, unsigned medium[2], void* context) { struct context* ctx = context; - (void) iseg; + (void)iseg; ASSERT(medium && ctx); medium[ctx->reverse_med ? 1 : 0] = *ctx->front_media; medium[ctx->reverse_med ? 0 : 1] = *ctx->back_media; @@ -76,7 +76,9 @@ main(int argc, char** argv) /* 64^3 = 262144 circles */ #define NB_CIRC (NB_CIRC_1 * NB_CIRC_1 * NB_CIRC_1) /* Create the scene */ - CHK(senc2d_scene_create(dev, NB_CIRC_1 + 1, &scn) == RES_OK); + CHK(senc2d_scene_create(dev, + SENC2D_CONVENTION_NORMAL_FRONT | SENC2D_CONVENTION_NORMAL_INSIDE, &scn) + == RES_OK); ctx.positions = NULL; ctx.indices = NULL; @@ -127,13 +129,16 @@ main(int argc, char** argv) FOR_EACH(e, 0, count) { struct senc2d_enclosure* enclosure; - const struct enclosure2d_header* header; + 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->segment_count == - (e == 0 /* Outermost enclosure: NB_CIRC_1*NB_CIRC_1 circles */ + CHK(header.enclosed_media_count == 1); + CHK(senc2d_enclosure_get_medium(enclosure, 0, &m) == RES_OK); + CHK(header.segment_count == + (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_many_segments.c b/src/test_senc2d_many_segments.c @@ -73,7 +73,9 @@ main(int argc, char** argv) #define NB_CIRC 4 /* Create the scene */ - CHK(senc2d_scene_create(dev, NB_CIRC+1, &scn) == RES_OK); + CHK(senc2d_scene_create(dev, + SENC2D_CONVENTION_NORMAL_FRONT | SENC2D_CONVENTION_NORMAL_INSIDE, &scn) + == RES_OK); ctx.positions = NULL; ctx.indices = NULL; @@ -116,10 +118,10 @@ main(int argc, char** argv) CHK(count == 1 + NB_CIRC); FOR_EACH(i, 0, count) { struct senc2d_enclosure* enclosure; - const struct enclosure2d_header* header; + struct senc2d_enclosure_header header; CHK(senc2d_descriptor_get_enclosure(desc, i, &enclosure) == RES_OK); CHK(senc2d_enclosure_get_header(enclosure, &header) == RES_OK); - CHK(header->segment_count == + CHK(header.segment_count == i ? circ_seg_count : NB_CIRC * circ_seg_count); CHK(senc2d_enclosure_ref_put(enclosure) == RES_OK); } diff --git a/src/test_senc2d_sample_enclosure.c b/src/test_senc2d_sample_enclosure.c @@ -31,7 +31,7 @@ main(int argc, char** argv) struct senc2d_device* dev = NULL; struct senc2d_scene* scn = NULL; struct senc2d_enclosure* enclosure = NULL; - const struct enclosure2d_header* header = NULL; + struct senc2d_enclosure_header header; struct s2d_device* s2d = NULL; struct s2d_scene* s2d_scn = NULL; struct s2d_scene_view* s2d_view = NULL; @@ -48,7 +48,9 @@ main(int argc, char** argv) CHK(senc2d_device_create(NULL, &allocator, SENC2D_NTHREADS_DEFAULT, 1, &dev) == RES_OK); - CHK(senc2d_scene_create(dev, 2, &scn) == RES_OK); + CHK(senc2d_scene_create(dev, + SENC2D_CONVENTION_NORMAL_FRONT | SENC2D_CONVENTION_NORMAL_INSIDE, &scn) + == RES_OK); vrtx_get.type = S2D_FLOAT2; vrtx_get.usage = S2D_POSITION; @@ -61,8 +63,8 @@ main(int argc, char** argv) /* A 2D square, but with a hole (incomplete). * 1 single enclosure including both sides of segments */ - ctx.positions = square_vertices; - ctx.indices = square_indices; + ctx.positions = square_vertices; /* Need a true square */ + ctx.indices = box_indices; ctx.scale = 1; ctx.reverse_vrtx = 0; ctx.reverse_med = 0; @@ -70,8 +72,8 @@ main(int argc, char** argv) ctx.front_media = medium0; ctx.back_media = medium0; - CHK(senc2d_scene_add_geometry(scn, square_nsegments - 1, get_indices, - get_media, NULL, square_nvertices, get_position, &ctx) == RES_OK); + CHK(senc2d_scene_add_geometry(scn, nsegments - 1, get_indices, + get_media, NULL, nvertices, get_position, &ctx) == RES_OK); CHK(senc2d_scene_analyze(scn, &desc) == RES_OK); @@ -80,8 +82,8 @@ main(int argc, char** argv) /* Put enclosure in a 2D view... */ S2D(shape_create_line_segments(s2d, &s2d_shp)); - S2D(line_segments_setup_indexed_vertices(s2d_shp, header->segment_count, - senc2d_enclosure_get_segment__, header->vertices_count, &vrtx_get, 1, + S2D(line_segments_setup_indexed_vertices(s2d_shp, header.segment_count, + senc2d_enclosure_get_segment__, header.vertices_count, &vrtx_get, 1, enclosure)); S2D(scene_attach_shape(s2d_scn, s2d_shp)); @@ -103,19 +105,19 @@ main(int argc, char** argv) if(eq_eps(attrib.value[n], 0, FLT_EPSILON) || eq_eps(attrib.value[n], 1, FLT_EPSILON)) c++; - ASSERT(c == 1); + CHK(c == 1); S2D(primitive_get_attrib(&prim, S2D_GEOMETRY_NORMAL, s, &attrib)); c = 0; FOR_EACH(n, 0, 2) if(eq_eps(attrib.value[n], -1, FLT_EPSILON) || eq_eps(attrib.value[n], 1, FLT_EPSILON)) c++; - ASSERT(c == 1); + CHK(c == 1); c = 0; FOR_EACH(n, 0, 2) if(eq_eps(attrib.value[n], 0, FLT_EPSILON)) c++; - ASSERT(c == 1); + CHK(c == 1); } SENC2D(enclosure_ref_put(enclosure)); diff --git a/src/test_senc2d_scene.c b/src/test_senc2d_scene.c @@ -19,8 +19,6 @@ #include <rsys/float2.h> #include <rsys/double2.h> -#include <star/s2d.h> - int main(int argc, char** argv) { @@ -28,23 +26,41 @@ main(int argc, char** argv) struct senc2d_device* dev = NULL; struct senc2d_scene* scn = NULL; struct senc2d_descriptor* desc = NULL; + struct senc2d_enclosure* enc = NULL; + struct senc2d_enclosure_header header; struct context ctx; - unsigned count, i; + unsigned medfront[2], medback[2]; + unsigned count, i, maxm; + enum senc2d_convention convention; (void)argc, (void)argv; CHK(mem_init_proxy_allocator(&allocator, &mem_default_allocator) == RES_OK); CHK(senc2d_device_create(NULL, &allocator, SENC2D_NTHREADS_DEFAULT, 1, &dev) == RES_OK); - CHK(senc2d_scene_create(NULL, 2, &scn) == RES_BAD_ARG); + CHK(senc2d_scene_create(NULL, + SENC2D_CONVENTION_NORMAL_FRONT | SENC2D_CONVENTION_NORMAL_INSIDE, &scn) + == RES_BAD_ARG); CHK(senc2d_scene_create(dev, 0, &scn) == RES_BAD_ARG); - CHK(senc2d_scene_create(dev, 2, NULL) == RES_BAD_ARG); + CHK(senc2d_scene_create(dev, + SENC2D_CONVENTION_NORMAL_FRONT | SENC2D_CONVENTION_NORMAL_INSIDE, NULL) + == RES_BAD_ARG); CHK(senc2d_scene_create(NULL, 0, &scn) == RES_BAD_ARG); - CHK(senc2d_scene_create(NULL, 2, NULL) == RES_BAD_ARG); + CHK(senc2d_scene_create(NULL, + SENC2D_CONVENTION_NORMAL_FRONT | SENC2D_CONVENTION_NORMAL_INSIDE, NULL) + == RES_BAD_ARG); CHK(senc2d_scene_create(dev, 0, NULL) == RES_BAD_ARG); CHK(senc2d_scene_create(NULL, 0, NULL) == RES_BAD_ARG); - /* It is valid to have unused media */ - CHK(senc2d_scene_create(dev, 4, &scn) == RES_OK); + CHK(senc2d_scene_create(dev, + SENC2D_CONVENTION_NORMAL_FRONT | SENC2D_CONVENTION_NORMAL_INSIDE, &scn) + == RES_OK); + + CHK(senc2d_scene_get_convention(NULL, &convention) == RES_BAD_ARG); + CHK(senc2d_scene_get_convention(scn, NULL) == RES_BAD_ARG); + CHK(senc2d_scene_get_convention(NULL, NULL) == RES_BAD_ARG); + CHK(senc2d_scene_get_convention(scn, &convention) == RES_OK); + CHK(convention == + (SENC2D_CONVENTION_NORMAL_FRONT | SENC2D_CONVENTION_NORMAL_INSIDE)); CHK(senc2d_scene_get_segments_count(NULL, &count) == RES_BAD_ARG); CHK(senc2d_scene_get_segments_count(scn, NULL) == RES_BAD_ARG); @@ -70,9 +86,11 @@ main(int argc, char** argv) CHK(senc2d_scene_get_unique_vertices_count(scn, &count) == RES_OK); CHK(count == 0); - /* A 2D square */ - ctx.positions = square_vertices; - ctx.indices = square_indices; + /* A 2D square + * With this geometry front is inside with NORMAL_BACK convention, + * outside with NORMAL_FRONT convention */ + ctx.positions = box_vertices; + ctx.indices = box_indices; ctx.scale = 1; ctx.reverse_vrtx = 0; ctx.reverse_med = 0; @@ -81,29 +99,29 @@ main(int argc, char** argv) ctx.back_media = medium1; ctx.global_ids = gid_face; - CHK(senc2d_scene_add_geometry(NULL, square_nsegments, get_indices, get_media, - get_global_id, square_nvertices, get_position, &ctx) == RES_BAD_ARG); + CHK(senc2d_scene_add_geometry(NULL, nsegments, get_indices, get_media, + get_global_id, nvertices, get_position, &ctx) == RES_BAD_ARG); CHK(senc2d_scene_add_geometry(scn, 0, get_indices, get_media, get_global_id, - square_nvertices, get_position, &ctx) == RES_BAD_ARG); - CHK(senc2d_scene_add_geometry(scn, square_nsegments, NULL, get_media, - get_global_id, square_nvertices, get_position, &ctx) == RES_BAD_ARG); - CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, NULL, - get_global_id, square_nvertices, get_position, &ctx) == RES_BAD_ARG); - CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, + nvertices, get_position, &ctx) == RES_BAD_ARG); + CHK(senc2d_scene_add_geometry(scn, nsegments, NULL, get_media, + get_global_id, nvertices, get_position, &ctx) == RES_BAD_ARG); + CHK(senc2d_scene_add_geometry(scn, nsegments, get_indices, NULL, + get_global_id, nvertices, get_position, &ctx) == RES_BAD_ARG); + CHK(senc2d_scene_add_geometry(scn, nsegments, get_indices, get_media, get_global_id, 0, get_position, &ctx) == RES_BAD_ARG); - CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, - get_global_id, square_nvertices, NULL, &ctx) == RES_BAD_ARG); - CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, - get_global_id, square_nvertices, get_position, &ctx) == RES_OK); + CHK(senc2d_scene_add_geometry(scn, nsegments, get_indices, get_media, + get_global_id, nvertices, NULL, &ctx) == RES_BAD_ARG); + CHK(senc2d_scene_add_geometry(scn, nsegments, get_indices, get_media, + get_global_id, nvertices, get_position, &ctx) == RES_OK); CHK(senc2d_scene_get_segments_count(scn, &count) == RES_OK); - CHK(count == square_nsegments); + CHK(count == nsegments); CHK(senc2d_scene_get_unique_segments_count(scn, &count) == RES_OK); - CHK(count == square_nsegments); + CHK(count == nsegments); CHK(senc2d_scene_get_vertices_count(scn, &count) == RES_OK); - CHK(count == square_nvertices); + CHK(count == nvertices); CHK(senc2d_scene_get_unique_vertices_count(scn, &count) == RES_OK); - CHK(count == square_nvertices); + CHK(count == nvertices); CHK(senc2d_scene_analyze(NULL, NULL) == RES_BAD_ARG); CHK(senc2d_scene_analyze(scn, NULL) == RES_BAD_ARG); @@ -118,69 +136,107 @@ main(int argc, char** argv) CHK(senc2d_scene_ref_put(scn) == RES_OK); CHK(senc2d_descriptor_ref_put(desc) == RES_OK); - CHK(senc2d_scene_create(dev, 4, &scn) == RES_OK); - CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, - get_global_id, square_nvertices, get_position, &ctx) == RES_OK); + CHK(senc2d_scene_create(dev, + SENC2D_CONVENTION_NORMAL_BACK | SENC2D_CONVENTION_NORMAL_INSIDE, &scn) + == RES_OK); + CHK(senc2d_scene_get_convention(scn, &convention) == RES_OK); + CHK(convention == + (SENC2D_CONVENTION_NORMAL_BACK | SENC2D_CONVENTION_NORMAL_INSIDE)); + CHK(senc2d_scene_add_geometry(scn, nsegments, get_indices, get_media, + get_global_id, nvertices, get_position, &ctx) == RES_OK); CHK(senc2d_scene_analyze(scn, &desc) == RES_OK); + /* Check that medium 0 is inside */ + CHK(senc2d_descriptor_get_enclosure_by_medium(desc, 0, 0, &enc) == RES_OK); + CHK(senc2d_enclosure_get_header(enc, &header) == RES_OK); + CHK(!header.is_infinite); + CHK(senc2d_enclosure_ref_put(enc) == RES_OK); - FOR_EACH(i, 0, square_nsegments) { + FOR_EACH(i, 0, nsegments) { unsigned gid; CHK(senc2d_descriptor_get_global_segment_global_id(desc, i, &gid) == RES_OK); /* gid has been set to gid_face. */ CHK(gid == gid_face[i]); } + CHK(senc2d_descriptor_get_global_segment_media(desc, 0, medback) == RES_OK); + + ctx.front_media = medium1_3; + CHK(senc2d_scene_ref_put(scn) == RES_OK); + CHK(senc2d_descriptor_ref_put(desc) == RES_OK); + CHK(senc2d_scene_create(dev, + SENC2D_CONVENTION_NORMAL_FRONT | SENC2D_CONVENTION_NORMAL_INSIDE, &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, 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, 4, &scn) == RES_OK); - CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, NULL, - square_nvertices, get_position, &ctx) == RES_OK); + CHK(senc2d_scene_create(dev, + SENC2D_CONVENTION_NORMAL_FRONT | SENC2D_CONVENTION_NORMAL_INSIDE, &scn) + == RES_OK); + CHK(senc2d_scene_add_geometry(scn, nsegments, get_indices, get_media, NULL, + nvertices, get_position, &ctx) == RES_OK); CHK(senc2d_scene_analyze(scn, &desc) == RES_OK); + /* Check that medium 0 is outside */ + CHK(senc2d_descriptor_get_enclosure_by_medium(desc, 0, 0, &enc) == RES_OK); + CHK(senc2d_enclosure_get_header(enc, &header) == RES_OK); + CHK(header.is_infinite); + CHK(senc2d_enclosure_ref_put(enc) == RES_OK); - FOR_EACH(i, 0, square_nsegments) { + FOR_EACH(i, 0, nsegments) { unsigned gid; CHK(senc2d_descriptor_get_global_segment_global_id(desc, i, &gid) == RES_OK); /* Default gid: segments rank. */ CHK(gid == i); } - - /* Invalid medium ID */ - ctx.back_media = medium1_12; - CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, NULL, - square_nvertices, get_position, &ctx) == RES_BAD_ARG); - ctx.back_media = medium1; + CHK(senc2d_descriptor_get_global_segment_media(desc, 0, medfront) == RES_OK); + FOR_EACH(i, 0, 2) CHK(medback[i] == medfront[i]); /* Invalid vertex ID */ - CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, NULL, - square_nvertices - 1, get_position, &ctx) == RES_BAD_ARG); + CHK(senc2d_scene_add_geometry(scn, nsegments, get_indices, get_media, NULL, + nvertices - 1, get_position, &ctx) == RES_BAD_ARG); /* Incoherent medium on a duplicate segment */ ctx.back_media = medium1_3; - CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, NULL, - square_nvertices, get_position, &ctx) == RES_BAD_ARG); + CHK(senc2d_scene_add_geometry(scn, nsegments, get_indices, get_media, NULL, + nvertices, get_position, &ctx) == RES_BAD_ARG); /* It is OK dd geometry after a failed add */ ctx.back_media = medium1; - CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, NULL, - square_nvertices, get_position, &ctx) == RES_OK); + CHK(senc2d_scene_add_geometry(scn, nsegments, get_indices, get_media, NULL, + nvertices, get_position, &ctx) == RES_OK); /* Coherent medium on duplicate segment */ ctx.back_media = medium1; - CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, NULL, - square_nvertices, get_position, &ctx) == RES_OK); + CHK(senc2d_scene_add_geometry(scn, nsegments, get_indices, get_media, NULL, + nvertices, get_position, &ctx) == RES_OK); /* Coherent medium on duplicate segment V2 */ ctx.reverse_med = 1; ctx.front_media = medium1; ctx.back_media = medium0; - CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, NULL, - square_nvertices, get_position, &ctx) == RES_OK); + CHK(senc2d_scene_add_geometry(scn, nsegments, get_indices, get_media, NULL, + nvertices, get_position, &ctx) == RES_OK); /* Coherent medium on duplicate segment V3 */ ctx.reverse_med = 0; ctx.reverse_vrtx = 1; - CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, NULL, - square_nvertices, get_position, &ctx) == RES_OK); + CHK(senc2d_scene_add_geometry(scn, nsegments, get_indices, get_media, NULL, + nvertices, get_position, &ctx) == RES_OK); CHK(senc2d_scene_ref_put(scn) == RES_OK); CHK(senc2d_device_ref_put(dev) == RES_OK); diff --git a/src/test_senc2d_square_behind_square.c b/src/test_senc2d_square_behind_square.c @@ -26,17 +26,20 @@ 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); CHK(senc2d_device_create - (NULL, &allocator, 1/*SENC2D_NTHREADS_DEFAULT*/, 1, &dev) == RES_OK); + (NULL, &allocator, SENC2D_NTHREADS_DEFAULT, 1, &dev) == RES_OK); /* Create the scene */ - CHK(senc2d_scene_create(dev, 2, &scn) == RES_OK); + CHK(senc2d_scene_create(dev, + SENC2D_CONVENTION_NORMAL_FRONT | SENC2D_CONVENTION_NORMAL_INSIDE, &scn) + == RES_OK); - ctx.positions = square_vertices; - ctx.indices = square_indices; + ctx.positions = box_vertices; + ctx.indices = box_indices; ctx.scale = 1; ctx.reverse_vrtx = 0; ctx.reverse_med = 0; @@ -45,8 +48,8 @@ main(int argc, char** argv) ctx.back_media = medium1; /* First square */ - CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, NULL, - square_nvertices, get_position, &ctx) == RES_OK); + CHK(senc2d_scene_add_geometry(scn, nsegments, get_indices, get_media, NULL, + nvertices, get_position, &ctx) == RES_OK); /* +Y from the first square, * big enough to prevent rays from the first square to miss this one */ @@ -54,24 +57,57 @@ main(int argc, char** argv) ctx.scale = 5; /* Second square */ - CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, NULL, - square_nvertices, get_position, &ctx) == RES_OK); + CHK(senc2d_scene_add_geometry(scn, nsegments, get_indices, get_media, NULL, + nvertices, get_position, &ctx) == RES_OK); 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 */ ctx.front_media = medium1; ctx.back_media = medium0; /* Third square */ - CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, NULL, - square_nvertices, get_position, &ctx) == RES_OK); + CHK(senc2d_scene_add_geometry(scn, nsegments, get_indices, get_media, NULL, + nvertices, get_position, &ctx) == RES_OK); 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,17 +26,20 @@ 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); CHK(senc2d_device_create - (NULL, &allocator, 2/*SENC2D_NTHREADS_DEFAULT*/, 1, &dev) == RES_OK); + (NULL, &allocator, SENC2D_NTHREADS_DEFAULT, 1, &dev) == RES_OK); /* Create the scene */ - CHK(senc2d_scene_create(dev, 2, &scn) == RES_OK); + CHK(senc2d_scene_create(dev, + SENC2D_CONVENTION_NORMAL_FRONT | SENC2D_CONVENTION_NORMAL_INSIDE, &scn) + == RES_OK); - ctx.positions = square_vertices; - ctx.indices = square_indices; + ctx.positions = box_vertices; + ctx.indices = box_indices; ctx.scale = 1; ctx.reverse_vrtx = 0; ctx.reverse_med = 0; @@ -47,8 +50,8 @@ main(int argc, char** argv) ctx.back_media = medium1; /* First square */ - CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, NULL, - square_nvertices, get_position, &ctx) == RES_OK); + CHK(senc2d_scene_add_geometry(scn, nsegments, get_indices, get_media, NULL, + nvertices, get_position, &ctx) == RES_OK); d2(ctx.offset, -1, -1); ctx.scale = 3; @@ -57,27 +60,51 @@ main(int argc, char** argv) ctx.reverse_vrtx = 1; /* Second square */ - CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, NULL, - square_nvertices, get_position, &ctx) == RES_OK); + CHK(senc2d_scene_add_geometry(scn, nsegments, get_indices, get_media, NULL, + nvertices, get_position, &ctx) == RES_OK); 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, square_nsegments, get_indices, get_media, NULL, - square_nvertices, get_position, &ctx) == RES_OK); + CHK(senc2d_scene_add_geometry(scn, nsegments, get_indices, get_media, NULL, + nvertices, get_position, &ctx) == RES_OK); 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_square_on_square.c b/src/test_senc2d_square_on_square.c @@ -57,10 +57,12 @@ main(int argc, char** argv) (NULL, &allocator, SENC2D_NTHREADS_DEFAULT, 1, &dev) == RES_OK); /* Create the scene */ - CHK(senc2d_scene_create(dev, 3, &scn) == RES_OK); + CHK(senc2d_scene_create(dev, + SENC2D_CONVENTION_NORMAL_FRONT | SENC2D_CONVENTION_NORMAL_INSIDE, &scn) + == RES_OK); - ctx.positions = square_vertices; - ctx.indices = square_indices; + ctx.positions = square_vertices; /* Need true squares for squares #1 and #2 */ + ctx.indices = box_indices; ctx.scale = 1; ctx.reverse_vrtx = 0; ctx.reverse_med = 0; @@ -72,8 +74,8 @@ main(int argc, char** argv) ctx.back_media = medium0; /* Add square #1 */ - CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, - NULL, square_nvertices, get_position, &ctx) == RES_OK); + CHK(senc2d_scene_add_geometry(scn, nsegments, get_indices, get_media, + NULL, nvertices, get_position, &ctx) == RES_OK); d2(ctx.offset, 1, 2); ctx.scale = 1; @@ -84,9 +86,10 @@ main(int argc, char** argv) ctx.back_media = medium0; /* Add square #2 (has a duplicate face with square #1) */ - CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, - NULL, square_nvertices, get_position, &ctx) == RES_OK); + CHK(senc2d_scene_add_geometry(scn, nsegments, get_indices, get_media, + NULL, nvertices, get_position, &ctx) == RES_OK); + ctx.positions = box_vertices; /* Can use distorded square for square #3 */ d2(ctx.offset, 0, 0); ctx.scale = 4; ctx.reverse_vrtx = 1; @@ -97,8 +100,8 @@ main(int argc, char** argv) ctx.back_media = medium1; /* Add square #3 */ - CHK(senc2d_scene_add_geometry(scn, square_nsegments, get_indices, get_media, - NULL, square_nvertices, get_position, &ctx) == RES_OK); + CHK(senc2d_scene_add_geometry(scn, nsegments, get_indices, get_media, + NULL, nvertices, get_position, &ctx) == RES_OK); CHK(senc2d_scene_analyze(scn, &desc) == RES_OK); diff --git a/src/test_senc2d_utils.h b/src/test_senc2d_utils.h @@ -19,20 +19,31 @@ #include <rsys/rsys.h> #include <rsys/mem_allocator.h> #include <rsys/stretchy_array.h> +#include <rsys/double2.h> #include <stdio.h> /******************************************************************************* * Geometry ******************************************************************************/ -static double square_vertices[4/*#vertices*/*2/*#coords per vertex*/] = { + /* Distorded square */ +static double box_vertices[4/*#vertices*/*2/*#coords per vertex*/] = { + 0.1, 0.0, + 1.0, 0.0, + 0.0, 1.1, + 1.0, 1.0 +}; +/* Need a true square for some tests */ +static double square_vertices[4/*#vertices*/ * 2/*#coords per vertex*/] = { 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0 }; static const unsigned -square_nvertices = sizeof(square_vertices) / (2 * sizeof(*square_vertices)); +nvertices = sizeof(box_vertices) / (2 * sizeof(*box_vertices)); +STATIC_ASSERT(sizeof(box_vertices) == sizeof(square_vertices), + The_2_geometries_must_have_the_same_number_of_vertices); /* The following array lists the indices toward the 2D vertices of each * segment. @@ -43,14 +54,14 @@ square_nvertices = sizeof(square_vertices) / (2 * sizeof(*square_vertices)); * 0----1 */ static unsigned -square_indices[4/*#segments*/*2/*#indices per segment*/] = { +box_indices[4/*#segments*/*2/*#indices per segment*/] = { 0, 2, 2, 3, 3, 1, 1, 0 }; static const unsigned -square_nsegments = sizeof(square_indices) / (2 * sizeof(*square_indices)); +nsegments = sizeof(box_indices) / (2 * sizeof(*box_indices)); struct context { double* positions; @@ -67,7 +78,6 @@ static const unsigned medium0[4] = { 0, 0, 0, 0 }; static const unsigned medium1[4] = { 1, 1, 1, 1 }; static const unsigned medium2[4] = { 2, 2, 2, 2 }; static const unsigned medium1_3[4] = { 1, 1, 3, 1 }; -static const unsigned medium1_12[4] = { 1, 12, 1, 1 }; static const unsigned medium1_back0[4] = { 1, 1, 1, 0 }; static const unsigned medium1_front0[4] = { 1, 0, 1, 1 }; @@ -147,7 +157,7 @@ dump_enclosure const char* name) { struct senc2d_enclosure* enclosure; - const struct enclosure2d_header* header; + struct senc2d_enclosure_header header; FILE* stream; unsigned count, i; @@ -160,12 +170,12 @@ dump_enclosure stream = fopen(name, "w"); CHK(stream); - FOR_EACH(i, 0, header->vertices_count) { + FOR_EACH(i, 0, header.vertices_count) { double tmp[2]; CHK(senc2d_enclosure_get_vertex(enclosure, i, tmp) == RES_OK); fprintf(stream, "v %g %g 0\n", SPLIT2(tmp)); } - FOR_EACH(i, 0, header->segment_count) { + FOR_EACH(i, 0, header.segment_count) { unsigned indices[2]; CHK(senc2d_enclosure_get_segment(enclosure, i, indices) == RES_OK); fprintf(stream, "l %lu %lu\n", @@ -227,5 +237,90 @@ circle_release(struct context* ctx) ctx->indices = NULL; } -#endif /* TEST_UTILS2_H */ +/******************************************************************************* + * 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 */ +} + +/* Compare the itri-th segment of enclosure with a segment described by seg2 & vertices2 */ +static INLINE void +cmp_seg + (const unsigned iseg, + const struct senc2d_enclosure* enclosure, + const unsigned seg2[2], + const double* vertices2, + int* seg_eq, + int* seg_reversed) +{ + unsigned seg1[2]; + double s1[2][2]; + double s2[2][2]; + unsigned seg1_eq[2] = { 2, 2 }; + unsigned i, j; + + ASSERT(enclosure && seg2 && vertices2 && seg_eq && seg_reversed); + + CHK(senc2d_enclosure_get_segment(enclosure, iseg, seg1) == RES_OK); + FOR_EACH(i, 0, 2) { + CHK(senc2d_enclosure_get_vertex(enclosure, seg1[i], s1[i]) == RES_OK); + d2_set(s2[i], vertices2 + (2 * seg2[i])); + } + FOR_EACH(i, 0, 2) { + FOR_EACH(j, 0, 2) { + if (d2_eq(s1[i], s2[j])) { + seg1_eq[i] = j; + break; + } + } + } + FOR_EACH(i, 0, 2) { + if(seg1_eq[i] == 2) { + *seg_eq = 0; + return; + } + if(seg1_eq[i] == seg1_eq[(i + 1) % 2]) { + *seg_eq = 0; + return; + } + } + /* Same 2 vertices */ + *seg_eq = 1; + + *seg_reversed = (0 != seg1_eq[0]); + ASSERT(*seg_reversed == (1 != seg1_eq[1])); +} + +#endif /* TEST_UTILS2_H */