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:
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> 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, ¤t_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(¤t_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(¤t_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(¤t_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(¤t_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(¤t_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(¤t_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(¤t_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 */