commit 247a44fd97d300b83803ec48261518444dbf35a9
parent 0b7f78c48125a8dc05d8c09a07f3cb415196f189
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date: Wed, 10 Jan 2024 17:30:58 +0100
Another debugging step
Fix management of removed buildings to keep conformity with (removed) neighbours. Add a denoising step on vertex coordinates
Diffstat:
15 files changed, 961 insertions(+), 240 deletions(-)
diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt
@@ -117,7 +117,8 @@ set(CG2_FILES_SRC
cg_construction_mode_1.c
cg_ground.c
cg_main.c
- cg_types.c)
+ cg_types.c
+ cg_vertex_denoiser.c)
set(CG2_FILES_INC
cg.h
@@ -136,7 +137,8 @@ set(CG2_FILES_INC
cg_default.h.in
cg_ground.h
cg_types.h
- cg_version.h.in)
+ cg_version.h.in
+ cg_vertex_denoiser.h)
set(CG2_FILES_DOC COPYING README.md)
diff --git a/src/cg_args.c b/src/cg_args.c
@@ -42,9 +42,13 @@ print_version(void)
void
short_help(void)
{
+#ifndef NDEBUG
+ printf("Running debug binary.\n");
+#endif
+
print_version();
printf("\nUsage:\n"
- "city_generator2 -m <FILENAME> { -c <FILENAME> }+ -[%c] [-s] [-V verbosity] [-k] [-f <NAME>] [-F <level>][-E]\n"
+ "city_generator2 -m <FILENAME> {-c <FILENAME>}+ [-%c] [-s] [-V verbosity] [-k] [-f <NAME>] [-F <level>] [-E]\n"
"city_generator2 [-h]\n"
"city_generator2 [-v]\n",
CG2_ARGS_CHANGE_BINARY_DEFAULT_OPTION
@@ -69,7 +73,7 @@ short_help(void)
"-E\n"
" Don't use escape characters in logs.\n"
" Use as first option to avoid escape characters in initial log messages.\n"
- "-f <NAME>.\n"
+ "-f <NAME>\n"
" Dump the footprint of the building with the given name.\n"
" Can be used more than once.\n"
"-F <level>\n"
diff --git a/src/cg_building.c b/src/cg_building.c
@@ -111,6 +111,7 @@ error:
res_T
build_adjoining
(struct building* building,
+ const int silent,
struct darray_geometries* current_cad,
struct darray_adjoining_data* adjoining)
{
@@ -122,17 +123,19 @@ build_adjoining
struct mem_allocator* allocator;
struct logger* logger;
- ASSERT(building && adjoining);
+ ASSERT(building && current_cad && adjoining);
allocator = building->city->allocator;
logger = building->city->logger;
- str_init(allocator, &msg);
close_buildings = &building->close_buildings;
count = htable_building_size_get(close_buildings);
- ERR(str_printf(&msg,
- "building '%s' considering close neighbor(s) when creating CAD:",
- str_cget(&building->name)));
+ if(!silent) {
+ str_init(allocator, &msg);
+ ERR(str_printf(&msg,
+ "Building '%s' considering close neighbor(s) when creating CAD:",
+ str_cget(&building->name)));
+ }
/* iterate over adjoining building */
htable_building_begin(close_buildings, &it);
@@ -140,13 +143,10 @@ build_adjoining
prev = darray_adjoining_data_size_get(adjoining);
ERR(darray_adjoining_data_reserve(adjoining, prev + count));
while(!htable_building_iterator_eq(&it, &end)) {
- unsigned char flags = *htable_building_iterator_data_get(&it);
- struct building* close_building;
+ struct building* close_building = *htable_building_iterator_key_get(&it);
struct adjoining_data adj;
- ASSERT(flags & CLOSE_PROXIMITY); (void)flags;
- close_building = *htable_building_iterator_key_get(&it);
htable_building_iterator_next(&it);
- if(close_building->event_flags & BUILDING_REMOVED)
+ if(close_building->event_flags & BUILDING_REMOVED_EARLY)
continue;
adjoining_data_init(allocator, &adj);
ERR(build_envelop(close_building, &adj.envelop));
@@ -154,16 +154,20 @@ build_adjoining
ERR(scad_geometry_ref_put(adj.envelop)); /* Ownership transfer */
adj.main_building = building;
adj.adjoining_building = close_building;
- ERR(str_append_printf(&msg, (i ? ", '%s'" : " '%s'"),
- str_cget(&close_building->name)));
+ if(!silent) {
+ ERR(str_append_printf(&msg, (i ? ", '%s'" : " '%s'"),
+ str_cget(&close_building->name)));
+ }
ERR(darray_adjoining_data_push_back(adjoining, &adj));
i++;
}
- logger_print(logger, LOG_OUTPUT, "%s.\n", str_cget(&msg));
+ if(!silent) {
+ logger_print(logger, LOG_OUTPUT, "%s.\n", str_cget(&msg));
+ }
exit:
- str_release(&msg);
+ if(!silent) str_release(&msg);
return res;
error:
darray_adjoining_data_clear(adjoining);
diff --git a/src/cg_building.h b/src/cg_building.h
@@ -44,17 +44,23 @@ struct darray_geometries;
/* An enum to encode building events types. */
enum building_event {
- BUILDING_NO_EVENT = 0,
- BUILDING_WITH_OVERLAPPING = BIT(0),
- BUILDING_OUT_OF_GROUND_EXTENT = BIT(1),
- BUILDING_TOO_CLOSE_TO_BORDER = BIT(2),
- BUILDING_INSIDE_BUILDING = BIT(3),
- BUILDING_REMOVED = BIT(5),
- BUILDING_WITH_CLOSE_NEIGHBOR = BIT(6),
- BUILDING_DUMPED_TO_OBJ = BIT(7),
- BUILDING_CREATED = BIT(11),
- BUILDING_CAD_EXPORTED_TO_STL = BIT(12),
- BUILDING_ALREADY_UNREGISTRED = BIT(13)
+ /* 0x0000# */
+ BUILDING_REMOVED_EARLY = BIT(0), /* Removed before CAO stage started */
+ BUILDING_REMOVED = BIT(1), /* Removed after CAO stage started */
+
+ /* 0x000#0 */
+ BUILDING_INSIDE_BUILDING = BIT(4) | BUILDING_REMOVED_EARLY,
+ BUILDING_WITH_OVERLAPPING = BIT(5) | BUILDING_REMOVED_EARLY,
+ BUILDING_OUT_OF_GROUND_EXTENT = BIT(6) | BUILDING_REMOVED_EARLY,
+ BUILDING_TOO_CLOSE_TO_BORDER = BIT(7) | BUILDING_REMOVED_EARLY,
+
+ /* 0x0#000 */
+ BUILDING_CREATED = BIT(12),
+ BUILDING_WITH_CLOSE_NEIGHBOR = BIT(13),
+ BUILDING_CAD_EXPORTED_TO_STL = BIT(14),
+ BUILDING_GROUND_PATCH_CREATED = BIT(15),
+ /* 0x#0000 */
+ BUILDING_DUMPED_TO_OBJ = BIT(16)
};
/* A type to store the functors of a construction mode */
@@ -107,6 +113,7 @@ struct building {
/* generic construction mode data */
struct str name;
+ struct str external_layer_name;
struct city* city;
double height;
struct scpr_polygon* pg;
@@ -165,6 +172,7 @@ build_envelop
res_T
build_adjoining
(struct building* building,
+ const int silent,
struct darray_geometries* current_cad,
struct darray_adjoining_data* adjoining);
diff --git a/src/cg_city.c b/src/cg_city.c
@@ -28,6 +28,7 @@
#include "cg_building.h"
#include "cg_args.h"
#include "cg_city_parsing_schemas.h"
+#include "cg_vertex_denoiser.h"
#include <rsys/rsys.h>
#include <rsys/logger.h>
@@ -43,24 +44,31 @@
#include <string.h>
-/* Unregister building from adjoining information of other buildings */
-void
-unregister_close_building
- (struct city* city,
- struct building* building)
+res_T
+darray_double_merge
+ (struct darray_double* dest,
+ const struct darray_double* src)
{
- size_t i, n;
- ASSERT(city && building);
- for(i = 0; i < city->allocated_buildings_count; i++) {
- struct building *b = city->buildings+i;
- unsigned char *ptr;
- if(b->event_flags & BUILDING_ALREADY_UNREGISTRED) return;
- b->event_flags |= BUILDING_ALREADY_UNREGISTRED;
- ptr = htable_building_find(&b->close_buildings, &building);
- if(!ptr) continue; /* No registered information */
- n = htable_building_erase(&b->close_buildings, &building);
- ASSERT(n == 1); (void)n;
+ res_T res = RES_OK;
+ size_t count, i;
+ const double* data;
+
+ if(!dest || !src) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ count = darray_double_size_get(src);
+ data = darray_double_cdata_get(src);
+ ERR(darray_double_reserve(dest, darray_double_size_get(dest) + count));
+ for(i = 0; i < count; i++) {
+ ERR(darray_double_push_back(dest, data + i));
}
+
+exit:
+ return res;
+error:
+ goto exit;
}
void
@@ -122,6 +130,7 @@ int overlapping_segments
struct callback_ctx* ctx = (struct callback_ctx*)ctx__;
const char *name1 = NULL, *name2 = NULL;
struct building *building1 = NULL, *building2 = NULL;
+ int with_removed = 0;
ASSERT(segment1 && segment2 && ctx);
@@ -130,34 +139,41 @@ int overlapping_segments
struct building* building = ctx->buildings + i;
const struct scpr_polygon* pg =
ctx->alternate_polygons ? ctx->alternate_polygons[i] : building->pg;
- if(building->event_flags & BUILDING_REMOVED)
- continue;
+ int removed = (building->event_flags & BUILDING_REMOVED_EARLY);
if(pg == segment1->polygon) {
building1 = building;
name1 = str_cget(&building->name);
+ if(removed) {
+ with_removed = 1;
+ break;
+ }
building1->event_flags |= BUILDING_WITH_CLOSE_NEIGHBOR;
}
if(pg == segment2->polygon) {
building2 = building;
name2 = str_cget(&building->name);
+ if(removed) {
+ with_removed = 1;
+ break;
+ }
building2->event_flags |= BUILDING_WITH_CLOSE_NEIGHBOR;
}
+ if(name1 && name2) break;
}
- CHK(name1 && name2);
- switch(ctx->search_type) {
- case OVERLAPPING_PROXIMITY:
- /* store other polygon on building information */
- STORE_CLOSE_INFO(building1, building2, CLOSE_PROXIMITY);
- STORE_CLOSE_INFO(building2, building1, CLOSE_PROXIMITY);
- logger_print(ctx->city->logger, LOG_OUTPUT,
- "Overlapping segment detected between buildings '%s' and '%s'.\n",
- name1, name2);
- break;
- case CLOSE_PROXIMITY:
- /* No action required */
- break;
- default: FATAL("Invalid type.");
+ if(!with_removed) {
+ CHK(name1 && name2);
+ switch(ctx->search_type) {
+ case OVERLAPPING_PROXIMITY:
+ /* store other polygon on building information */
+ STORE_CLOSE_INFO(building1, building2, CLOSE_PROXIMITY);
+ STORE_CLOSE_INFO(building2, building1, CLOSE_PROXIMITY);
+ break;
+ case CLOSE_PROXIMITY:
+ /* No action required */
+ break;
+ default: FATAL("Invalid type.");
+ }
}
return 0;
@@ -177,7 +193,7 @@ int simple_intersection
const char *name1 = NULL, *name2 = NULL;
struct building *building1 = NULL, *building2 = NULL;
const enum building_event flag = (ctx->search_type == OVERLAPPING_PROXIMITY)
- ? BUILDING_WITH_OVERLAPPING | BUILDING_REMOVED : BUILDING_WITH_CLOSE_NEIGHBOR;
+ ? BUILDING_WITH_OVERLAPPING : BUILDING_WITH_CLOSE_NEIGHBOR;
struct logger* logger;
struct mem_allocator* allocator;
@@ -221,8 +237,7 @@ int simple_intersection
if(ctx->dump_footprints_level == 1) {
ERR(dump_footprint_to_obj(allocator, building1, NULL));
}
- building1->event_flags |= BUILDING_REMOVED;
- unregister_close_building(ctx->city, building1);
+ building1->event_flags |= BUILDING_REMOVED_EARLY;
break;
case CLOSE_PROXIMITY:
break;
@@ -247,10 +262,8 @@ int simple_intersection
ERR(dump_footprint_to_obj(allocator, building1, NULL));
ERR(dump_footprint_to_obj(allocator, building2, NULL));
}
- building1->event_flags |= BUILDING_REMOVED;
- building2->event_flags |= BUILDING_REMOVED;
- unregister_close_building(ctx->city, building1);
- unregister_close_building(ctx->city, building2);
+ building1->event_flags |= BUILDING_REMOVED_EARLY;
+ building2->event_flags |= BUILDING_REMOVED_EARLY;
break;
case CLOSE_PROXIMITY:
logger_print(logger, LOG_OUTPUT,
@@ -277,6 +290,73 @@ error:
#undef STORE_CLOSE_INFO
res_T
+save_ground_hole_patch_triangles
+ (struct building* building,
+ struct darray_double* ground_trg)
+{
+ res_T res = RES_OK;
+ struct scad_geometry* footprint = NULL;
+ struct scad_geometry** env = NULL;
+ struct scad_geometry** partitioned = NULL;
+ struct htable_building_iterator it, end;
+ struct mem_allocator* allocator;
+ size_t count, i, n = 0, nverts = 0;
+
+ ASSERT(building && ground_trg);
+
+ allocator = building->city->allocator;
+ count = htable_building_size_get(&building->close_buildings);
+ env = MEM_CALLOC(allocator, 1 + count, sizeof(*env));
+ partitioned = MEM_CALLOC(allocator, 1 + count, sizeof(*partitioned));
+ if(!env || !partitioned) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+
+ /* Get building's footprint */
+ ERR(scpr_polygon_get_vertices_count(building->pg, 0, &nverts));
+ ERR(scad_add_polygon(NULL, get_position_pg, building->pg, 0, nverts, env));
+ n = 1;
+
+ /* Get the footprints of all the close buildings in order to partition */
+ htable_building_begin(&building->close_buildings, &it);
+ htable_building_end(&building->close_buildings, &end);
+ while(!htable_building_iterator_eq(&it, &end)) {
+ struct building* close_building = *htable_building_iterator_key_get(&it);
+ htable_building_iterator_next(&it);
+ if(close_building->event_flags & BUILDING_REMOVED_EARLY)
+ continue;
+ ERR(scpr_polygon_get_vertices_count(close_building->pg, 0, &nverts));
+ ERR(scad_add_polygon(NULL, get_position_pg, close_building->pg, 0, nverts,
+ env + n));
+ n++;
+ }
+
+ /* Clean and mesh footprint */
+ ERR(scad_geometries_partition(env, n, 0, partitioned));
+ footprint = partitioned[0];
+ ERR(scad_geometry_ref_get(footprint));
+ for(i = 0; i < n; i++) {
+ SCAD(geometry_ref_put(env[i])); env[i] = NULL;
+ SCAD(geometry_ref_put(partitioned[i])); partitioned[i] = NULL;
+ }
+ ERR(scad_scene_mesh());
+ ERR(scad_stl_get_data(footprint, ground_trg));
+
+exit:
+ if(footprint) SCAD(geometry_ref_put(footprint));
+ for(i = 0; i < n; i++) {
+ if(env[i]) SCAD(geometry_ref_put(env[i]));
+ if(partitioned[i]) SCAD(geometry_ref_put(partitioned[i]));
+ }
+ MEM_RM(allocator, env);
+ MEM_RM(allocator, partitioned);
+ return res;
+error:
+ goto exit;
+}
+
+res_T
create_city
(struct mem_allocator* allocator,
struct logger* logger,
@@ -338,6 +418,8 @@ create_city
htable_names_init(allocator, &city->dump_footprint_names);
htable_common_init(allocator, &city->common);
ground_init(allocator, &city->ground);
+ ERR(vertex_denoiser_create(allocator, pow(10, -(CLIPPER_PRECISON+2)),
+ &city->denoiser));
city->array_and_tables_initialized = 1;
/* Some specific building footprints will be dumped (command line request) */
for(i = 0; i < darray_names_size_get(&args->dump_footprint_names); i++) {
@@ -386,8 +468,7 @@ create_city
logger_print(city->logger, LOG_WARNING,
"Building '%s' will not be part of the output.\n",
str_cget(&building->name));
- unregister_close_building(city, building);
- building->event_flags |= BUILDING_REMOVED;
+ building->event_flags |= BUILDING_REMOVED_EARLY;
/* Insert a NULL polygon to preserve indexes */
tmp_polygon = NULL;
ERR(darray_polygons_push_back(&offset_polygons, &tmp_polygon));
@@ -399,9 +480,11 @@ create_city
/* Check name unicity. The error case is not supposed to happen: can be
* tested after building creation as this simplifies the process */
if(htable_names_find(&names, &building->name)) {
+ building->event_flags |= BUILDING_REMOVED_EARLY;
logger_print(logger, LOG_ERROR,
"Duplicate building name: '%s' in file '%s'.\n",
str_cget(&building->name), str_cget(&parsed_city->filename));
+ /* Cannot keep going in this specific case */
res = RES_BAD_ARG;
goto error;
}
@@ -438,23 +521,21 @@ create_city
goto error;
}
/* Check for polygons in polygons.
- * As remaining polygons do not overlap, its enough to check a single vertex
- * to detect a polygon is inside another */
+ * As remaining polygons do not overlap, we can use component_in_component */
for(i = 0; i < city->allocated_buildings_count ; i++) {
struct building* b1 = city->buildings + i;
size_t j;
- if(b1->event_flags & BUILDING_REMOVED) continue;
+ if(b1->event_flags & BUILDING_REMOVED_EARLY) continue;
for(j = 0; j < i; j++) {
struct building* b2 = city->buildings + j;
int in1, in2;
- if((b2->event_flags & BUILDING_REMOVED)) continue;
+ if((b2->event_flags & BUILDING_REMOVED_EARLY)) continue;
/* b1 and b2 are 2 different valid buildings */
ERR(scpr_is_component_in_component(b1->pg, 0, b2->pg, 0, &in1));
ERR(scpr_is_component_in_component(b2->pg, 0, b1->pg, 0, &in2));
ASSERT(!in1 || !in2);
if(in2) {
- unregister_close_building(city, b2);
- b2->event_flags |= BUILDING_INSIDE_BUILDING | BUILDING_REMOVED;
+ b2->event_flags |= BUILDING_INSIDE_BUILDING;
if(city->keep_running_on_errors) {
logger_print(logger, LOG_WARNING,
"Building '%s' is inside building '%s'.\n",
@@ -469,8 +550,7 @@ create_city
}
}
else if(in1) {
- unregister_close_building(city, b1);
- b1->event_flags |= BUILDING_INSIDE_BUILDING | BUILDING_REMOVED;
+ b1->event_flags |= BUILDING_INSIDE_BUILDING;
if(city->keep_running_on_errors) {
logger_print(logger, LOG_WARNING,
"Building '%s' is inside building '%s'.\n",
@@ -540,6 +620,7 @@ release_city(struct city* city)
}
if(city->scpr) SCPR(device_ref_put(city->scpr));
+ if(city->denoiser) vertex_denoiser_release(city->denoiser);
if(city->array_and_tables_initialized) {
htable_common_release(&city->common);
htable_names_release(&city->dump_footprint_names);
@@ -573,8 +654,11 @@ city_cad_build(struct city* city)
struct darray_adjoining_data adjoining_data;
struct darray_double common;
void* cad = NULL;
+ struct scad_geometry* my_envelop = NULL;
+ struct str filename;
struct darray_geometries current_cad;
struct mem_allocator* allocator;
+ struct scad_geometry** partitioned = NULL;
struct time t0, dt;
char buf[128];
@@ -584,11 +668,13 @@ city_cad_build(struct city* city)
darray_geometries_init(allocator, ¤t_cad);
darray_adjoining_data_init(allocator, &adjoining_data);
darray_double_init(allocator, &common);
+ str_init(allocator, &filename);
/* Initialize star-cad */
ERR(scad_initialize(city->logger, allocator, city->verbosisty_level));
scad_initialized = 1;
options.Mesh.MeshSizeFromPoints = 0;
+ options.Mesh.MeshSizeMin = pow(10, -(CLIPPER_PRECISON+1));
#ifdef NDEBUG
options.Misc.LogRefCounting = Scad_log_dimTags_only_undeleted;
#else
@@ -602,12 +688,12 @@ city_cad_build(struct city* city)
for(i = 0; i < city->allocated_buildings_count; i++) {
struct adjoining_data* adj;
res_T tmp_res;
- building = city->buildings + i;
+ building = city->buildings + i;
if(city->dump_footprints_level == 2) {
ERR1(dump_footprint_to_obj(allocator, building, NULL));
}
- if(building->event_flags & BUILDING_REMOVED) {
+ if(building->event_flags & BUILDING_REMOVED_EARLY) {
continue;
}
@@ -619,7 +705,41 @@ city_cad_build(struct city* city)
tmp_res = building->functors->build_cad(building, city->dump_footprints_level,
city->keep_running_on_errors, &adjoining_data, ¤t_cad,
(void**)&cad);
+
if(tmp_res != RES_OK) {
+ /* Call to build_cad failed.
+ * As a consequence no current_cad is empty and cad is NULL.
+ * Any needed geometry must be created from here */
+ struct htable_building_iterator it, end;
+ ASSERT(darray_geometries_size_get(¤t_cad) == 0);
+ /* We will need to close the hole in the ground: save it */
+ ERR1(save_ground_hole_patch_triangles(building, &city->ground.ground_patches));
+ building->event_flags |= BUILDING_GROUND_PATCH_CREATED;
+ /* For every adjoining building that has considered this building, there
+ * is a hole in its boundary just where this building should have been
+ * adjoining. This hole must be closed. This situation can be detected
+ * simply by searching some common triangles between the 2 buildings. */
+ htable_building_begin(&building->close_buildings, &it);
+ htable_building_end(&building->close_buildings, &end);
+ while(!htable_building_iterator_eq(&it, &end)) {
+ struct building* close_building = *htable_building_iterator_key_get(&it);
+ struct b_pair pair;
+ struct darray_double* p_common;
+ htable_building_iterator_next(&it);
+ make_b_pair(&pair, building, close_building);
+ p_common = htable_common_find(&city->common, &pair);
+ if(p_common) {
+ /* The adjoining building has allready been processed.
+ * Need to create the boundary patch */
+ ERR1(str_copy(&filename, &close_building->name));
+ ERR1(str_append_printf(&filename, "_B_%s_complement_%s.stl",
+ str_cget(&building->external_layer_name),
+ str_cget(&building->name)));
+ ERR1(scad_stl_data_write(p_common, str_cget(&filename),
+ Scad_keep_normals_unchanged, city->binary_export));
+ }
+ }
+
if(city->dump_footprints_level == 1) {
ERR1(dump_footprint_to_obj(allocator, building, NULL));
}
@@ -629,8 +749,6 @@ city_cad_build(struct city* city)
logger_print(city->logger, LOG_WARNING,
"Building '%s' will not be part of the output (aborted after %s).\n",
str_cget(&building->name), buf);
- /* Unregister building from adjoining information of other buildings */
- unregister_close_building(city, building);
darray_adjoining_data_clear(&adjoining_data);
/* FIXME: adjoining buildings may have been built already, taking this
* building into account. There is no simple way to undo this other
@@ -664,9 +782,10 @@ city_cad_build(struct city* city)
}
darray_double_clear(&common);
ERR1(scad_stl_get_data(adj[a].common_geometry, &common));
- make_b_pair(&pair, building, adj[a].adjoining_building);
ASSERT(darray_double_size_get(&common) > 0
&& darray_double_size_get(&common) % 9 == 0);
+ make_b_pair(&pair, building, adj[a].adjoining_building);
+ ERR1(denoise_array(city->denoiser, &common));
ERR1(htable_common_set(&city->common, &pair, &common));
}
tmp_res = building->functors->export_stl(cad, city->binary_export);
@@ -674,7 +793,7 @@ city_cad_build(struct city* city)
/* Keep ground/building's boundary triangles now as the geometry will be
* released before ground is built */
ERR1(building->functors->save_ground_connection_triangles(cad,
- &city->ground.ground_trg));
+ &city->ground.ground_building_connections));
building->event_flags |= BUILDING_CAD_EXPORTED_TO_STL;
generated_buildings_count++;
logger_print(city->logger, LOG_OUTPUT,
@@ -697,6 +816,7 @@ error1:
}
darray_adjoining_data_clear(&adjoining_data);
darray_geometries_clear(¤t_cad);
+ if(my_envelop) SCAD(geometry_ref_put(my_envelop));
ERR(scad_scene_clear());
time_sub(&dt, time_current(&dt), &t0);
time_dump(&dt, TIME_SEC | TIME_MSEC, NULL, buf, sizeof(buf));
@@ -716,6 +836,14 @@ error1:
city->cad_generated_buildings_count = generated_buildings_count;
exit:
+ if(partitioned) {
+ for(i = 0; i < 2; i++) {
+ if(partitioned[i]) SCAD(geometry_ref_put(partitioned[i]));
+ }
+ MEM_RM(allocator, partitioned);
+ }
+ str_release(&filename);
+ if(my_envelop) SCAD(geometry_ref_put(my_envelop));
darray_geometries_release(¤t_cad);
darray_adjoining_data_release(&adjoining_data);
if(cad) CHK(RES_OK == building->functors->release_cad(cad));
@@ -751,6 +879,7 @@ city_ground_build(struct city* city)
ERR(scad_initialize(city->logger, city->allocator, city->verbosisty_level));
scad_initialized = 1;
options.Mesh.MeshSizeFromPoints = 0;
+ options.Mesh.MeshSizeMin = pow(10, -(CLIPPER_PRECISON+1));
#ifdef NDEBUG
options.Misc.LogRefCounting = Scad_log_dimTags_only_undeleted;
#else
@@ -762,10 +891,11 @@ city_ground_build(struct city* city)
ERR(darray_geometries_reserve(&city->ground.footprints,
city->allocated_buildings_count));
for(i = 0; i < city->allocated_buildings_count; i++) {
- building = city->buildings + i;
struct scad_geometry* footprint;
- if(!(building->event_flags & BUILDING_CAD_EXPORTED_TO_STL))
+ building = city->buildings + i;
+ if(!(building->event_flags
+ & (BUILDING_CAD_EXPORTED_TO_STL | BUILDING_GROUND_PATCH_CREATED)))
continue;
/* create building footprint */
@@ -777,7 +907,7 @@ city_ground_build(struct city* city)
ERR(ground_build_cad(city->allocator, city));
ERR(scad_scene_mesh());
- ERR(ground_export_stl(&city->ground, city->binary_export));
+ ERR(ground_export_stl(city));
exit:
ground_clear(&city->ground);
diff --git a/src/cg_city.h b/src/cg_city.h
@@ -39,6 +39,7 @@ struct parsed_city;
struct catalog;
struct scpr_device;
struct scpr_callback_segment;
+struct vertex_denoiser;
static FINLINE void
ppoly_init
@@ -127,6 +128,7 @@ struct city {
double ground_depth;
struct building* buildings; /* list of buildings */
struct ground ground;
+ struct vertex_denoiser* denoiser;
size_t cad_generated_buildings_count, allocated_buildings_count,
initialized_buildings_count;
struct htable_names dump_footprint_names;
@@ -141,10 +143,10 @@ struct city {
int array_and_tables_initialized;
};
-void
-unregister_close_building
- (struct city* city,
- struct building* building);
+res_T
+darray_double_merge
+ (struct darray_double* dest,
+ const struct darray_double* src);
res_T
create_city
@@ -201,4 +203,9 @@ int simple_intersection
struct scpr_callback_segment* segment2,
void* ctx__);
+res_T
+save_ground_hole_patch_triangles
+ (struct building* building,
+ struct darray_double* ground_trg);
+
#endif /*CITY_H*/
diff --git a/src/cg_construction_mode.c b/src/cg_construction_mode.c
@@ -65,6 +65,7 @@ init_building_base
building->city = city;
building->height = parsed_data->height;
str_init(city->allocator, &building->name);
+ str_init(city->allocator, &building->external_layer_name);
htable_building_init(city->allocator, &building->close_buildings);
building->structs_initialized = 1;
ERR(str_set(&building->name, parsed_data->name));
@@ -74,7 +75,7 @@ init_building_base
ERR(scpr_polygon_get_vertices_count(building->pg, 0, &count));
if(parsed_data->vertice_count != count) {
logger_print(city->logger, LOG_WARNING,
- "Building '%s' had quasi-identical vertices that have been merged.\n",
+ "Building '%s' had quasi-identical or aligned vertices that have been removed.\n",
str_cget(&building->name));
}
/* First try with a slightly decreased bbox as buildings are not allowed to
@@ -118,6 +119,7 @@ release_building_base
if(building->structs_initialized) {
str_release(&building->name);
+ str_release(&building->external_layer_name);
htable_building_release(&building->close_buildings);
}
if(building->pg) {
diff --git a/src/cg_construction_mode_0.c b/src/cg_construction_mode_0.c
@@ -25,6 +25,7 @@
#include "cg_city_parsing_schemas.h"
#include "cg_construction_mode_0.h"
#include "cg_construction_mode.h"
+#include "cg_vertex_denoiser.h"
#include <rsys/rsys.h>
#include <rsys/str.h>
@@ -329,22 +330,27 @@ build_boundary
goto error;
}
/* Using wall here to compute boundary_wall is OK even if it doesn't take care
- * of conformity wrt the adjoining building. The reason is that the common
+ * of conformity wrt the adjoining buildings. The reason is that the common
* part that could end to be not conformal is not part of the boundary. As a
- * consequence it cannot be part of the result. */
+ * consequence it cannot be part of the result.
+ * However, if the adjoining building is later removed, this part must then be
+ * created as an additional file. */
count = 5;
list[0] = data_cad->floor;
list[1] = data_cad->roof;
list[2] = data_cad->wall;
list[3] = data_cad->cavity;
list[4] = data_cad->fake_ground;
- for (i=0; i<adjoining_n; i++) {
- if(adj[i].really_adjoining) {
- list[count] = adj[i].envelop;
- count++;
+ for(i = 0; i < adjoining_n; i++) {
+ /* Here we consider truly adjoining buildings, except removed ones (early
+ * removed ones are not part of adjoining) */
+ if(adj[i].really_adjoining
+ && !(adj[i].adjoining_building->event_flags & BUILDING_REMOVED))
+ {
+ ASSERT(maxc > count);
+ list[count++] = adj[i].envelop;
}
}
- ASSERT(maxc >= count);
ERR(str_set(&name, prefix));
ERR(str_append(&name, "_B_walls"));
@@ -427,7 +433,6 @@ init_cmode_0
};
struct mem_allocator* allocator;
struct logger* logger;
- (void)parsed_data;
if(!city || !building || !parsed_data || !catalog || !lower || !upper) {
res = RES_BAD_ARG;
@@ -446,13 +451,14 @@ init_cmode_0
ERR(str_set(&dataset_name, parsed_data->dataset_name));
building->data = htable_dataset_cmode_0_find(&catalog->catalog_0, &dataset_name);
- if (building->data == NULL) {
+ if(building->data == NULL) {
ERR(logger_print(logger, LOG_ERROR,
"Unknown dataset name: '%s' used by building '%s'.\n",
str_cget(&dataset_name), str_cget(&building->name)));
res = RES_BAD_ARG;
goto error;
}
+ ERR(str_set(&building->external_layer_name, "walls"));
exit:
if(name_initialized) str_release(&dataset_name);
@@ -498,7 +504,7 @@ build_cad_cmode_0
struct scad_geometry** cur_cad = NULL;
struct scad_geometry** partitioned = NULL;
- if (!building || !cad || !adjoining_data) {
+ if(!building || !cad || !adjoining_data) {
res = RES_BAD_ARG;
goto error;
}
@@ -510,7 +516,9 @@ build_cad_cmode_0
height = building->height;
data = (struct dataset_cmode_0 *)building->data;
- if (height <= 0 || data->wall_thickness <= 0 || data->floor_thickness <= 0) {
+ logger_print(logger, LOG_OUTPUT, "Building '%s' construction mode 0.\n", name);
+
+ if(height <= 0 || data->wall_thickness <= 0 || data->floor_thickness <= 0) {
res = RES_BAD_ARG;
goto error;
}
@@ -536,7 +544,6 @@ build_cad_cmode_0
logger_print(logger, (keep_running_on_errors ? LOG_WARNING : LOG_ERROR),
"Building '%s' shape is not compatible with wall thickness.\n", name);
building->event_flags |= BUILDING_REMOVED;
- unregister_close_building(building->city, building);
error_msg_printed = 1;
res = RES_BAD_ARG;
goto error;
@@ -557,7 +564,6 @@ build_cad_cmode_0
logger_print(logger, LOG_ERROR,
"Internal error generating CAD for building '%s'.\n", name);
building->event_flags |= BUILDING_REMOVED;
- unregister_close_building(ctx.city, building);
error_msg_printed = 1;
res = RES_BAD_ARG;
goto error;
@@ -577,8 +583,8 @@ build_cad_cmode_0
/* build adjoining envelop */
adjoining_n = htable_building_size_get(&building->close_buildings);
- if (adjoining_n > 0) {
- ERR(build_adjoining(building, current_cad, adjoining_data));
+ if(adjoining_n > 0) {
+ ERR(build_adjoining(building, 0, current_cad, adjoining_data));
}
/* build fake ground */
@@ -614,7 +620,7 @@ build_cad_cmode_0
adj->really_adjoining = (c != 0);
if(!adj->really_adjoining) {
logger_print(logger, LOG_OUTPUT,
- "building '%s': neighbor '%s' not really adjoining.\n",
+ "Building '%s': neighbor '%s' not really adjoining.\n",
str_cget(&building->name),
str_cget(&adj->adjoining_building->name));
continue;
@@ -717,17 +723,22 @@ export_stl_cmode_0
struct str name;
int initialized = 0;
struct mem_allocator* allocator = NULL;
+ struct city* city;
+ struct vertex_denoiser* denoiser;
if(!cad) {
res = RES_BAD_ARG;
goto error;
}
- allocator = data_cad->building->city->allocator;
+ city = data_cad->building->city;
+ allocator = city->allocator;
+ denoiser = city->denoiser;
/* wall export */
if(darray_geometries_size_get(&data_cad->adj_walls) == 0) {
- ERR(scad_stl_export(data_cad->wall, NULL, Scad_force_normals_outward, binary));
+ ERR(stl_export_denoised_geometry(denoiser, data_cad->wall,
+ Scad_force_normals_outward, binary));
} else {
/* There is some adjoining building(s) to manage */
size_t common_count = darray_common_trg_size_get(&data_cad->common_trg);
@@ -747,7 +758,6 @@ export_stl_cmode_0
struct b_pair* pair = darray_common_trg_data_get(&data_cad->common_trg)+ i;
const double *t9;
double* tgt;
- struct city* city = data_cad->building->city;
struct darray_double* common = NULL;
/* Get triangles */
common = htable_common_find(&city->common, pair);
@@ -771,33 +781,37 @@ export_stl_cmode_0
ERR(scad_geometry_get_name(data_cad->wall, &tmp));
ERR(str_set(&name, tmp));
ERR(str_append(&name, ".stl"));
+ ERR(denoise_array(denoiser, &trg));
ERR(scad_stl_data_write(&trg, str_cget(&name), Scad_force_normals_outward,
binary));
}
/* floor export */
- ERR(scad_stl_export(data_cad->floor, NULL, Scad_force_normals_outward, binary));
+ ERR(stl_export_denoised_geometry(denoiser, data_cad->floor,
+ Scad_force_normals_outward, binary));
/* roof export */
- ERR(scad_stl_export(data_cad->roof, NULL, Scad_force_normals_outward, binary));
+ ERR(stl_export_denoised_geometry(denoiser, data_cad->roof,
+ Scad_force_normals_outward, binary));
/* cavity export */
- ERR(scad_stl_export(data_cad->cavity, NULL, Scad_force_normals_outward, binary));
+ ERR(stl_export_denoised_geometry(denoiser, data_cad->cavity,
+ Scad_force_normals_outward, binary));
/* connection export */
- for (i = 0; i < data_cad->n_connection; i++) {
- ERR(scad_stl_export(data_cad->connection[i], NULL,
+ for(i = 0; i < data_cad->n_connection; i++) {
+ ERR(stl_export_denoised_geometry(denoiser, data_cad->connection[i],
Scad_keep_normals_unchanged, binary));
}
/* boundary export */
- ERR(scad_stl_export(data_cad->boundary_wall, NULL, Scad_keep_normals_unchanged,
- binary));
- ERR(scad_stl_export(data_cad->boundary_roof, NULL, Scad_keep_normals_unchanged,
- binary));
+ ERR(stl_export_denoised_geometry(denoiser, data_cad->boundary_wall,
+ Scad_keep_normals_unchanged, binary));
+ ERR(stl_export_denoised_geometry(denoiser, data_cad->boundary_roof,
+ Scad_keep_normals_unchanged, binary));
- /* footprint export */
- ERR(scad_stl_export(data_cad->ground_connection, NULL,
+ /* ground/building connection export*/
+ ERR(stl_export_denoised_geometry(denoiser, data_cad->ground_connection,
Scad_keep_normals_unchanged, binary));
exit:
diff --git a/src/cg_construction_mode_1.c b/src/cg_construction_mode_1.c
@@ -26,18 +26,18 @@
#include "cg_city_parsing_schemas.h"
#include "cg_construction_mode.h"
#include "cg_construction_mode_1.h"
+#include "cg_vertex_denoiser.h"
#include <rsys/rsys.h>
#include <rsys/str.h>
#include <rsys/logger.h>
#include <rsys/hash_table.h>
#include <rsys/double3.h>
+#include <rsys/clock_time.h>
#include <star/scad.h>
#include <star/scpr.h>
-STATIC_ASSERT((CG2_CLOSE_NEIGHBOR_DISTANCE > 0.1), Close_neighbor_distance_cannot_be_less_than_10cm);
-
static res_T
build_footprint
(struct scpr_polygon* pg,
@@ -91,7 +91,6 @@ do_offset
"Building '%s' shape is not compatible with wall thickness.\n",
str_cget(&building->name));
building->event_flags |= BUILDING_REMOVED;
- unregister_close_building(building->city, building);
*error_msg_printed = 1;
res = RES_BAD_ARG;
goto error;
@@ -190,7 +189,7 @@ build_wall
prefix = str_cget(&building->name);
str_init(building->city->allocator, &name);
ERR(str_set(&name, prefix));
- if (suffix) {
+ if(suffix) {
ERR(str_append(&name, "_"));
ERR(str_append(&name, suffix));
}
@@ -284,7 +283,7 @@ build_int_insulation
d[2] = height - e_roof - attic - e_roof_insulation;
ERR(scad_geometry_extrude(footprint, NULL, d, &geom));
- if (inter_floor) {
+ if(inter_floor) {
ERR(scad_cut_geometries(insulationname, &geom, 1, &inter_floor, 1, insulation));
} else {
ERR(scad_geometry_copy(geom, insulationname, insulation));
@@ -516,7 +515,7 @@ build_inter_floor
h_cavity = height - e_roof - attic - e_roof_ins - (double)floor_n*e_floor;
z_floor = h_cavity/(double)(1 + floor_n);
d[2] = e_floor;
- for (i = 0; i < floor_n; i++) {
+ for(i = 0; i < floor_n; i++) {
ERR(scad_add_polygon(NULL, get_position_pg, pg_int, z_floor, nverts,
&footprint));
ERR(scad_geometry_extrude(footprint, NULL, d, &floor));
@@ -704,7 +703,7 @@ build_habitable
d[2] = height - e_roof - e_attic - e_roof_insulation;
ERR(scad_geometry_extrude(footprint, NULL, d, &geom));
- if (floor) {
+ if(floor) {
ERR(scad_cut_geometries(
cavityname, &geom, 1, &floor, 1, cavity));
} else {
@@ -841,7 +840,7 @@ build_windows
ERR(darray_geometries_reserve(&hole_array, list_n));
ERR(darray_geometries_reserve(&glass_array, list_n));
- for (i = 0; i < list_n; i++) {
+ for(i = 0; i < list_n; i++) {
double hsz, mass, center[3];
size_t center_n;
size_t count;
@@ -949,7 +948,6 @@ build_windows
ERR(darray_geometries_push_back(current_cad, &glass));
ERR(scad_geometry_ref_put(glass));
glass = NULL;
-
}
ASSERT(hole == NULL);
ASSERT(detect == NULL);
@@ -993,7 +991,7 @@ build_windows
"Building '%s' has no window removed (out of %zu).\n", prefix, array_n);
}
- if (array_n > 0) {
+ if(array_n > 0) {
hole_list = darray_geometries_data_get(&hole_array);
glass_list = darray_geometries_data_get(&glass_array);
@@ -1004,7 +1002,7 @@ build_windows
geom = NULL;
/* internal insulation perforation */
- if (data_cad->internal_insulation) {
+ if(data_cad->internal_insulation) {
ERR(scad_cut_geometries(NULL, &data_cad->internal_insulation, 1,
hole_list, array_n, &geom));
ERR(scad_geometries_swap(&data_cad->internal_insulation, &geom, 1,
@@ -1014,7 +1012,7 @@ build_windows
}
/* external insulation perforation */
- if (data_cad->external_insulation) {
+ if(data_cad->external_insulation) {
ERR(scad_cut_geometries(NULL, &data_cad->external_insulation, 1,
hole_list, array_n, &geom));
ERR(scad_geometries_swap(&data_cad->external_insulation, &geom, 1,
@@ -1088,35 +1086,39 @@ build_boundary
ERR(darray_geometries_push_back(&array, &data_cad->floor));
ERR(darray_geometries_push_back(&array, &data_cad->habitable_cavity));
ERR(darray_geometries_push_back(&array, &data_cad->fake_ground));
- if (data_cad->foundation) {
+ if(data_cad->foundation) {
ERR(darray_geometries_push_back(&array, &data_cad->foundation));
}
- if (data_cad->intermediate_floor) {
+ if(data_cad->intermediate_floor) {
ERR(darray_geometries_push_back(&array, &data_cad->intermediate_floor));
}
- if (data_cad->external_insulation) {
+ if(data_cad->external_insulation) {
ERR(darray_geometries_push_back(&array, &data_cad->external_insulation));
}
- if (data_cad->internal_insulation) {
+ if(data_cad->internal_insulation) {
ERR(darray_geometries_push_back(&array, &data_cad->internal_insulation));
}
- if (data_cad->roof_insulation) {
+ if(data_cad->roof_insulation) {
ERR(darray_geometries_push_back(&array, &data_cad->roof_insulation));
}
- if (data_cad->floor_insulation) {
+ if(data_cad->floor_insulation) {
ERR(darray_geometries_push_back(&array, &data_cad->floor_insulation));
}
- if (data_cad->attic_cavity) {
+ if(data_cad->attic_cavity) {
ERR(darray_geometries_push_back(&array, &data_cad->attic_cavity));
}
- if (data_cad->crawlspace_cavity) {
+ if(data_cad->crawlspace_cavity) {
ERR(darray_geometries_push_back(&array, &data_cad->crawlspace_cavity));
}
- if (data_cad->glazing) {
+ if(data_cad->glazing) {
ERR(darray_geometries_push_back(&array, &data_cad->glazing));
}
- for (i=0; i<adjoining_n; i++) {
- if(adj[i].really_adjoining) {
+ for(i = 0; i < adjoining_n; i++) {
+ /* Here we consider truly adjoining buildings, except removed ones (early
+ * removed ones are not part of adjoining) */
+ if(adj[i].really_adjoining
+ && !(adj[i].adjoining_building->event_flags & BUILDING_REMOVED))
+ {
ERR(darray_geometries_push_back(&array, &adj[i].envelop));
}
}
@@ -1145,7 +1147,7 @@ build_boundary
ERR(scad_geometry_ref_put(bound));
bound = NULL;
- if (data_cad->glazing) {
+ if(data_cad->glazing) {
ERR(str_set(&name, prefix));
ERR(str_append(&name, "_B_glazing"));
boundaryname = str_get(&name);
@@ -1156,7 +1158,7 @@ build_boundary
bound = NULL;
}
- if (data_cad->external_insulation) {
+ if(data_cad->external_insulation) {
ERR(str_set(&name, prefix));
ERR(str_append(&name, "_B_external_insulation"));
boundaryname = str_get(&name);
@@ -1167,7 +1169,7 @@ build_boundary
bound = NULL;
}
- if (data_cad->internal_insulation) {
+ if(data_cad->internal_insulation) {
size_t bcount = 0;
ERR(str_set(&name, prefix));
ERR(str_append(&name, "_B_internal_insulation"));
@@ -1175,7 +1177,7 @@ build_boundary
ERR(scad_geometries_common_boundaries(boundaryname, list, count,
&data_cad->internal_insulation, 1, &bound));
ERR(scad_geometry_get_count(bound, &bcount));
- if (bcount > 0) {
+ if(bcount > 0) {
ERR(darray_geometries_push_back(boundary, &bound));
}
ERR(scad_geometry_ref_put(bound));
@@ -1215,7 +1217,7 @@ build_connection
ERR(scad_geometries_common_boundaries(cname, &data_cad->G1, 1,\
&data_cad->G2, 1, &connect));\
ERR(scad_geometry_get_count(connect, &count)); \
- if (count > 0) { \
+ if(count > 0) { \
ERR(darray_geometries_push_back(connection, &connect)); \
} \
ERR(scad_geometry_ref_put(connect)); \
@@ -1232,17 +1234,17 @@ build_connection
CREATE_CONNECT(habitable_cavity,wall,"_C_levels_walls");
/* with glass */
- if (data_cad->glazing) {
+ if(data_cad->glazing) {
CREATE_CONNECT(habitable_cavity,glazing,"_C_levels_glazing");
}
/* with internal insulation */
- if (data_cad->internal_insulation) {
+ if(data_cad->internal_insulation) {
CREATE_CONNECT(habitable_cavity,internal_insulation,"_C_levels_internal_insulation");
}
/* with roof insulation */
- if (data_cad->roof_insulation) {
+ if(data_cad->roof_insulation) {
CREATE_CONNECT(habitable_cavity,roof_insulation,"_C_levels_roof_insulation");
} else {
/* with roof */
@@ -1250,7 +1252,7 @@ build_connection
}
/* with intermediate floor */
- if (data_cad->intermediate_floor) {
+ if(data_cad->intermediate_floor) {
CREATE_CONNECT(habitable_cavity,intermediate_floor,"_C_levels_intermediate_floors");
}
@@ -1258,9 +1260,9 @@ build_connection
/* crawlspace cavity connections */
/* -------------------------------------------------------------------------*/
- if (data_cad->crawlspace_cavity) {
+ if(data_cad->crawlspace_cavity) {
/* with floor insulation */
- if (data_cad->floor_insulation) {
+ if(data_cad->floor_insulation) {
CREATE_CONNECT(crawlspace_cavity, floor_insulation,"_C_crawlspace_insulation");
} else {
/* with floor */
@@ -1278,7 +1280,7 @@ build_connection
/* attic cavity connections */
/* -------------------------------------------------------------------------*/
- if (data_cad->attic_cavity) {
+ if(data_cad->attic_cavity) {
/* with roof */
CREATE_CONNECT(attic_cavity, roof,"_C_attic_roof");
@@ -1320,16 +1322,16 @@ build_fake_ground
/* Ensure enough room for all geometries without error nor mem move */
ERR(darray_geometries_reserve(&array, 4));
- if (cad->foundation) {
+ if(cad->foundation) {
ERR(darray_geometries_push_back(&array, &cad->foundation));
}
- if (cad->crawlspace_cavity) {
+ if(cad->crawlspace_cavity) {
ERR(darray_geometries_push_back(&array, &cad->crawlspace_cavity));
}
- if (cad->floor) {
+ if(cad->floor) {
ERR(darray_geometries_push_back(&array, &cad->floor));
}
- if (cad->floor_insulation) {
+ if(cad->floor_insulation) {
ERR(darray_geometries_push_back(&array, &cad->floor_insulation));
}
@@ -1348,10 +1350,10 @@ build_fake_ground
ERR(darray_geometries_push_back(current_cad, ground));
exit:
- if (pg_offset) SCPR(polygon_ref_put(pg_offset));
+ if(pg_offset) SCPR(polygon_ref_put(pg_offset));
darray_geometries_release(&array);
- if (footprint) SCAD(geometry_ref_put(footprint));
- if (geom) SCAD(geometry_ref_put(geom));
+ if(footprint) SCAD(geometry_ref_put(footprint));
+ if(geom) SCAD(geometry_ref_put(geom));
return res;
error:
goto exit;
@@ -1383,22 +1385,22 @@ building_ground_connection
/* Ensure enough room for all geometries without error nor mem move */
ERR(darray_geometries_reserve(&array, 6));
- if (cad->foundation) {
+ if(cad->foundation) {
ERR(darray_geometries_push_back(&array, &cad->foundation));
}
- if (cad->crawlspace_cavity) {
+ if(cad->crawlspace_cavity) {
ERR(darray_geometries_push_back(&array, &cad->crawlspace_cavity));
}
- if (cad->floor) {
+ if(cad->floor) {
ERR(darray_geometries_push_back(&array, &cad->floor));
}
- if (cad->floor_insulation) {
+ if(cad->floor_insulation) {
ERR(darray_geometries_push_back(&array, &cad->floor_insulation));
}
- if (cad->external_insulation) {
+ if(cad->external_insulation) {
ERR(darray_geometries_push_back(&array, &cad->external_insulation));
}
- if (cad->wall) {
+ if(cad->wall) {
ERR(darray_geometries_push_back(&array, &cad->wall));
}
@@ -1411,8 +1413,8 @@ building_ground_connection
exit:
darray_geometries_release(&array);
str_release(&name);
- if (list_boundary) SCAD(geometry_ref_put(list_boundary));
- if (footprint) SCAD(geometry_ref_put(footprint));
+ if(list_boundary) SCAD(geometry_ref_put(list_boundary));
+ if(footprint) SCAD(geometry_ref_put(footprint));
return res;
error:
goto exit;
@@ -1434,6 +1436,7 @@ init_cmode_1
res_T res = RES_OK;
struct str dataset_name;
int name_initialized = 0;
+ struct dataset_cmode_1* data;
static struct construction_mode_functors functors_1 = {
&init_cmode_1,
&release_cmode_1,
@@ -1445,6 +1448,7 @@ init_cmode_1
};
struct mem_allocator* allocator;
struct logger* logger;
+ int has_external_insulation;
(void) parsed_data;
if(!building || !city || !parsed_data || !catalog || !lower || !upper) {
@@ -1462,15 +1466,18 @@ init_cmode_1
str_init(allocator, &dataset_name);
name_initialized = 1;
ERR(str_set(&dataset_name, parsed_data->dataset_name));
-
building->data = htable_dataset_cmode_1_find(&catalog->catalog_1, &dataset_name);
- if (building->data == NULL) {
+ if(building->data == NULL) {
ERR(logger_print(logger, LOG_ERROR,
"Unknown dataset name: '%s' used by building '%s'.\n",
str_cget(&dataset_name), str_cget(&building->name)));
res = RES_BAD_ARG;
goto error;
}
+ data = (struct dataset_cmode_1 *)building->data;
+ has_external_insulation = (data->external_insulation_thickness != 0);
+ ERR(str_set(&building->external_layer_name,
+ (has_external_insulation ? "external_insulation" : "walls")));
exit:
if(name_initialized) str_release(&dataset_name);
@@ -1516,15 +1523,23 @@ build_cad_cmode_1
size_t i, cad_count = 0;
struct scad_geometry** cur_cad = NULL;
struct scad_geometry** partitioned = NULL;
+ struct time t0, dt, tw, dtw;
+ char buf[128];
- if (!building || !cad || !adjoining_data) {
+ if(!building || !cad || !adjoining_data) {
res = RES_BAD_ARG;
goto error;
}
+ time_current(&t0);
name = str_cget(&building->name);
allocator = building->city->allocator;
logger = building->city->logger;
+
+ logger_print(logger, LOG_OUTPUT,
+ "Building '%s' construction mode 1, with %lu levels.\n",
+ name, 1 + data->inter_floor_count);
+
data_cad = MEM_CALLOC(allocator, 1, sizeof(struct data_cad_cmode_1));
if(!data_cad) {
res = RES_MEM_ERR;
@@ -1572,34 +1587,34 @@ build_cad_cmode_1
- floor insulation
*/
- if (data->foundation_depth > 0) {
+ if(data->foundation_depth > 0) {
depth = -data->foundation_depth;
ERR(build_wall(building, &error_msg_printed, 1, "S_foundation", depth,
data, overlapping_intersector, &polygons, current_cad, &data_cad->foundation));
}
- if (data->inter_floor_count > 0) {
+ if(data->inter_floor_count > 0) {
ERR(build_inter_floor(building, &error_msg_printed, height, data,
overlapping_intersector, &polygons, current_cad, &data_cad->intermediate_floor));
}
- if (data->external_insulation_thickness> 0) {
+ if(data->external_insulation_thickness> 0) {
ERR(build_ext_insulation(building, &error_msg_printed, height, data,
overlapping_intersector, &polygons, current_cad, &data_cad->external_insulation));
}
- if (data->internal_insulation_thickness> 0) {
+ if(data->internal_insulation_thickness> 0) {
ERR(build_int_insulation(building, &error_msg_printed, height, data,
data_cad->intermediate_floor, overlapping_intersector, &polygons,
current_cad, &data_cad->internal_insulation));
}
- if (data->roof_insulation_thickness > 0) {
+ if(data->roof_insulation_thickness > 0) {
ERR(build_roof_insulation(building, &error_msg_printed, height, data,
overlapping_intersector, &polygons, current_cad, &data_cad->roof_insulation));
}
- if (data->floor_insulation_thickness > 0) {
+ if(data->floor_insulation_thickness > 0) {
ERR(build_floor_insulation(building, &error_msg_printed, data,
overlapping_intersector, &polygons, current_cad, &data_cad->floor_insulation));
}
@@ -1610,7 +1625,7 @@ build_cad_cmode_1
- crawlspace
*/
- if (data->attic_height > 0) {
+ if(data->attic_height > 0) {
ERR(build_attic(building, &error_msg_printed, height, data,
overlapping_intersector, &polygons, current_cad, &data_cad->attic_cavity));
}
@@ -1619,7 +1634,7 @@ build_cad_cmode_1
data_cad->intermediate_floor, overlapping_intersector, &polygons,
current_cad, &data_cad->habitable_cavity));
- if (data->crawl_height > 0) {
+ if(data->crawl_height > 0) {
ERR(build_crawlspace(building, &error_msg_printed, data,
overlapping_intersector, &polygons, current_cad, &data_cad->crawlspace_cavity));
}
@@ -1639,7 +1654,6 @@ build_cad_cmode_1
"Internal error generating CAD for building '%s'.\n",
name);
building->event_flags |= BUILDING_REMOVED;
- unregister_close_building(ctx.city, building);
error_msg_printed = 1;
res = RES_BAD_ARG;
goto error;
@@ -1647,13 +1661,15 @@ build_cad_cmode_1
/* build adjoining envelop */
adjoining_n = htable_building_size_get(&building->close_buildings);
- if (adjoining_n > 0) {
- ERR(build_adjoining(building, current_cad, adjoining_data));
+ if(adjoining_n > 0) {
+ ERR(build_adjoining(building, 0, current_cad, adjoining_data));
}
/* windows */
- if (data->glass_ratio > 0) {
+ if(data->glass_ratio > 0) {
+ time_current(&tw);
ERR(build_windows(data, data_cad, current_cad, adjoining_data));
+ time_sub(&dtw, time_current(&dtw), &tw);
}
/* fake ground */
@@ -1661,6 +1677,21 @@ build_cad_cmode_1
data->floor_thickness + data->floor_insulation_thickness + data->crawl_height);
ERR(build_fake_ground(data_cad, depth, current_cad, &data_cad->fake_ground));
+ /* print partial computation time */
+ time_sub(&dt, time_current(&dt), &t0);
+ time_dump(&dt, TIME_SEC | TIME_MSEC, NULL, buf, sizeof(buf));
+ if(data->glass_ratio > 0) {
+ char bufw[128];
+ time_dump(&dtw, TIME_SEC | TIME_MSEC, NULL, bufw, sizeof(bufw));
+ logger_print(logger, LOG_OUTPUT,
+ "Building '%s' CAO stage done in %s (windows creation in %s).\n",
+ name, buf, bufw);
+ } else {
+ logger_print(logger, LOG_OUTPUT,
+ "Building '%s' CAO stage done in %s.\n", name, buf);
+ }
+ time_current(&t0);
+
/* Partition CAD */
cad_count = darray_geometries_size_get(current_cad);
partitioned = MEM_CALLOC(allocator, cad_count, sizeof(*partitioned));
@@ -1681,6 +1712,13 @@ build_cad_cmode_1
MEM_RM(allocator, partitioned);
partitioned = NULL;
+ /* print partial computation time */
+ time_sub(&dt, time_current(&dt), &t0);
+ time_dump(&dt, TIME_SEC | TIME_MSEC, NULL, buf, sizeof(buf));
+ logger_print(logger, LOG_OUTPUT,
+ "Building '%s' partitioning stage done in %s.\n", name, buf);
+ time_current(&t0);
+
/* After partitioning, manage common parts with other buildings */
adjoining_n = darray_adjoining_data_size_get(adjoining_data);
if(adjoining_n > 0) {
@@ -1699,7 +1737,7 @@ build_cad_cmode_1
adj->really_adjoining = (c != 0);
if(!adj->really_adjoining) {
logger_print(logger, LOG_OUTPUT,
- "building '%s': neighbor '%s' not really adjoining.\n",
+ "Building '%s': neighbor '%s' not really adjoining.\n",
name,
str_cget(&adj->adjoining_building->name));
continue;
@@ -1727,6 +1765,13 @@ build_cad_cmode_1
/* build connections */
ERR(build_connection(data_cad, &data_cad->connection));
+ /* print partial computation time */
+ time_sub(&dt, time_current(&dt), &t0);
+ time_dump(&dt, TIME_SEC | TIME_MSEC, NULL, buf, sizeof(buf));
+ logger_print(logger, LOG_OUTPUT,
+ "Building '%s' connections stage done in %s.\n", name, buf);
+ time_current(&t0);
+
exit:
if(partitioned) {
for(i = 0; i < cad_count; i++) {
@@ -1756,7 +1801,7 @@ build_footprint_cmode_1
{
res_T res = RES_OK;
- if (!building || !footprint) {
+ if(!building || !footprint) {
res = RES_BAD_ARG;
goto error;
}
@@ -1802,21 +1847,26 @@ export_stl_cmode_1
int initialized = 0;
struct scad_geometry** list = NULL;
struct mem_allocator* allocator = NULL;
+ struct city* city;
+ struct vertex_denoiser* denoiser;
- if (!cad) {
+ if(!cad) {
res = RES_BAD_ARG;
goto error;
}
- allocator = data_cad->building->city->allocator;
+ city = data_cad->building->city;
+ allocator = city->allocator;
+ denoiser = city->denoiser;
if(darray_geometries_size_get(&data_cad->adj_walls) == 0) {
/* wall export */
- ERR(scad_stl_export(data_cad->wall, NULL, Scad_force_normals_outward, binary));
+ ERR(stl_export_denoised_geometry(denoiser, data_cad->wall,
+ Scad_force_normals_outward, binary));
/* external insulation export */
if(data_cad->external_insulation) {
- ERR(scad_stl_export(data_cad->external_insulation, NULL,
+ ERR(stl_export_denoised_geometry(denoiser, data_cad->external_insulation,
Scad_force_normals_outward, binary));
}
} else {
@@ -1828,7 +1878,8 @@ export_stl_cmode_1
if(data_cad->external_insulation) {
/* wall export is not impacted as walls are not the external layer */
- ERR(scad_stl_export(data_cad->wall, NULL, Scad_force_normals_outward, binary));
+ ERR(stl_export_denoised_geometry(denoiser, data_cad->wall,
+ Scad_force_normals_outward, binary));
}
/* The external layer must use the common triangles */
@@ -1846,7 +1897,6 @@ export_stl_cmode_1
struct b_pair* pair = darray_common_trg_data_get(&data_cad->common_trg)+ i;
const double *t9;
double* tgt;
- struct city* city = data_cad->building->city;
struct darray_double* common = NULL;
/* Get triangles */
common = htable_common_find(&city->common, pair);
@@ -1870,82 +1920,87 @@ export_stl_cmode_1
ERR(scad_geometry_get_name(extern_most, &tmp));
ERR(str_set(&name, tmp));
ERR(str_append(&name, ".stl"));
+ ERR(denoise_array(denoiser, &trg));
ERR(scad_stl_data_write(&trg, str_cget(&name), Scad_force_normals_outward,
binary));
}
/* floor export */
- ERR(scad_stl_export(data_cad->floor, NULL, Scad_force_normals_outward, binary));
+ ERR(stl_export_denoised_geometry(denoiser, data_cad->floor,
+ Scad_force_normals_outward, binary));
/* roof export */
- ERR(scad_stl_export(data_cad->roof, NULL, Scad_force_normals_outward, binary));
+ ERR(stl_export_denoised_geometry(denoiser, data_cad->roof,
+ Scad_force_normals_outward, binary));
/* foundation export */
- if (data_cad->foundation) {
- ERR(scad_stl_export(data_cad->foundation, NULL, Scad_force_normals_outward,
- binary));
+ if(data_cad->foundation) {
+ ERR(stl_export_denoised_geometry(denoiser, data_cad->foundation,
+ Scad_force_normals_outward, binary));
}
/* glass export */
- if (data_cad->glazing) {
- ERR(scad_stl_export(data_cad->glazing, NULL, Scad_force_normals_outward,
- binary));
+ if(data_cad->glazing) {
+ ERR(stl_export_denoised_geometry(denoiser, data_cad->glazing,
+ Scad_force_normals_outward, binary));
}
/* intermediate floor export*/
- if (data_cad->intermediate_floor) {
- ERR(scad_stl_export(data_cad->intermediate_floor, NULL,
+ if(data_cad->intermediate_floor) {
+ ERR(stl_export_denoised_geometry(denoiser, data_cad->intermediate_floor,
Scad_force_normals_outward, binary));
}
/* internal insulation export*/
- if (data_cad->internal_insulation) {
- ERR(scad_stl_export(data_cad->internal_insulation, NULL,
+ if(data_cad->internal_insulation) {
+ ERR(stl_export_denoised_geometry(denoiser, data_cad->internal_insulation,
Scad_force_normals_outward, binary));
}
/* roof insulation export*/
- if (data_cad->roof_insulation) {
- ERR(scad_stl_export(data_cad->roof_insulation, NULL,
+ if(data_cad->roof_insulation) {
+ ERR(stl_export_denoised_geometry(denoiser, data_cad->roof_insulation,
Scad_force_normals_outward, binary));
}
/* floor insulation export*/
- if (data_cad->floor_insulation) {
- ERR(scad_stl_export(data_cad->floor_insulation, NULL,
+ if(data_cad->floor_insulation) {
+ ERR(stl_export_denoised_geometry(denoiser, data_cad->floor_insulation,
Scad_force_normals_outward, binary));
}
/* attic cavity export*/
- if (data_cad->attic_cavity) {
- ERR(scad_stl_export(data_cad->attic_cavity, NULL,
+ if(data_cad->attic_cavity) {
+ ERR(stl_export_denoised_geometry(denoiser, data_cad->attic_cavity,
Scad_force_normals_outward, binary));
}
/* habitable cavity export*/
- ERR(scad_stl_export(data_cad->habitable_cavity, NULL,
+ ERR(stl_export_denoised_geometry(denoiser, data_cad->habitable_cavity,
Scad_force_normals_outward, binary));
/* crawlspace cavity export*/
- if (data_cad->crawlspace_cavity) {
- ERR(scad_stl_export(data_cad->crawlspace_cavity, NULL,
+ if(data_cad->crawlspace_cavity) {
+ ERR(stl_export_denoised_geometry(denoiser, data_cad->crawlspace_cavity,
Scad_force_normals_outward, binary));
}
/* boundary export*/
for(i = 0; i < darray_geometries_size_get(&data_cad->boundary); i++) {
struct scad_geometry* b = darray_geometries_data_get(&data_cad->boundary)[i];
- ERR(scad_stl_export(b, NULL, Scad_keep_normals_unchanged, binary));
+ ERR(stl_export_denoised_geometry(denoiser, b, Scad_keep_normals_unchanged,
+ binary));
}
/* connections export*/
for(i = 0; i < darray_geometries_size_get(&data_cad->connection); i++) {
struct scad_geometry* c = darray_geometries_data_get(&data_cad->connection)[i];
- ERR(scad_stl_export(c, NULL, Scad_keep_normals_unchanged, binary));
+ ERR(stl_export_denoised_geometry(denoiser, c, Scad_keep_normals_unchanged,
+ binary));
}
/* ground/building connection export*/
- ERR(scad_stl_export(data_cad->ground_connection, NULL,
+ ERR(stl_export_denoised_geometry(denoiser, data_cad->ground_connection,
Scad_keep_normals_unchanged, binary));
exit:
@@ -1961,7 +2016,7 @@ exit:
error:
if(data_cad) {
logger_print(data_cad->building->city->logger, LOG_ERROR,
- "Internal error creating STL for building '%s'.\n",
+ "Internal error '"__FILE__"': creating STL for building '%s'.\n",
str_cget(&data_cad->building->name));
}
goto exit;
@@ -1975,7 +2030,7 @@ release_cad_cmode_1
struct data_cad_cmode_1* data_cad = (struct data_cad_cmode_1 *)cad;
struct mem_allocator* allocator;
- if (!cad) {
+ if(!cad) {
res = RES_BAD_ARG;
goto error;
}
diff --git a/src/cg_construction_mode_1.h b/src/cg_construction_mode_1.h
@@ -117,6 +117,10 @@ res_T save_ground_connection_triangles_1
(void* cad,
struct darray_double* triangles);
+res_T get_external_layer_name_1
+ (struct building* building,
+ const char* name);
+
res_T
export_stl_cmode_1
(void* cad,
diff --git a/src/cg_ground.c b/src/cg_ground.c
@@ -17,10 +17,11 @@
* 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 "cg.h"
#include "cg_ground.h"
#include "cg_building.h"
#include "cg_city.h"
-#include "cg.h"
+#include "cg_vertex_denoiser.h"
#include <rsys/rsys.h>
#include <rsys/dynamic_array_double.h>
@@ -99,6 +100,7 @@ ground_build_cad
ERR(scad_cut_geometries("ground_B_top", &surface, 1, footprints, fpcount,
&tmp));
GDEL(surface);
+ ground->top = tmp;
GPUSH(tmp);
} else {
ERR(scad_geometry_rename(surface, "ground_B_top"));
@@ -140,25 +142,51 @@ error:
res_T
ground_export_stl
- (struct ground* ground,
- const int binary)
+ (struct city* city)
{
res_T res = RES_OK;
+ struct ground* ground;
+ struct vertex_denoiser* denoiser;
+ struct mem_allocator* allocator;
+ struct darray_double trg;
+ const char* name;
+ struct str filename;
+ int binary;
size_t i = 0;
- ASSERT(ground);
+ ASSERT(city);
+ ground = &city->ground;
+ binary = city->binary_export;
+ denoiser = city->denoiser;
+ allocator = city->allocator;
+ darray_double_init(allocator, &trg);
+ str_init(allocator, &filename);
for(i = 0; i < darray_geometries_size_get(&ground->boundaries); i++) {
struct scad_geometry* b = darray_geometries_data_get(&ground->boundaries)[i];
/* Boundary files */
- ERR(scad_stl_export(b, NULL, Scad_keep_normals_unchanged, binary));
- /* Collect triangles */
- ERR(scad_stl_get_data(b, &ground->ground_trg));
+ darray_double_clear(&trg);
+ ERR(scad_stl_get_data(b, &trg));
+ if(b == ground->top) {
+ /* Add patches */
+ ERR(darray_double_merge(&trg, &ground->ground_patches));
+ }
+ ERR(scad_geometry_get_name(b, &name));
+ ERR(str_set(&filename, name));
+ ERR(str_append(&filename, ".stl"));
+ ERR(denoise_array(denoiser, &trg));
+ ERR(scad_stl_data_write(&trg, str_cget(&filename),
+ Scad_keep_normals_unchanged, binary));
+ /* Also collect boundaries triangles into ground body */
+ ERR(darray_double_merge(&ground->ground_building_connections, &trg));
}
- ERR(scad_stl_data_write(&ground->ground_trg, "ground_body.stl",
+ ERR(denoise_array(denoiser, &ground->ground_building_connections));
+ ERR(scad_stl_data_write(&ground->ground_building_connections, "ground_body.stl",
Scad_force_normals_outward, binary));
exit:
+ darray_double_release(&trg);
+ str_release(&filename);
return res;
error:
goto exit;
@@ -170,7 +198,8 @@ ground_init
struct ground* ground)
{
ground->box = NULL;
- darray_double_init(allocator, &ground->ground_trg);
+ darray_double_init(allocator, &ground->ground_building_connections);
+ darray_double_init(allocator, &ground->ground_patches);
darray_geometries_init(allocator, &ground->footprints);
darray_geometries_init(allocator, &ground->boundaries);
}
@@ -182,7 +211,8 @@ ground_clear
GDEL(ground->box);
darray_geometries_release(&ground->footprints);
darray_geometries_release(&ground->boundaries);
- darray_double_release(&ground->ground_trg);
+ darray_double_release(&ground->ground_building_connections);
+ darray_double_release(&ground->ground_patches);
}
#undef GDEL
diff --git a/src/cg_ground.h b/src/cg_ground.h
@@ -35,7 +35,9 @@ struct ground {
struct scad_geometry* box;
struct darray_geometries boundaries;
struct darray_geometries footprints;
- struct darray_double ground_trg;
+ struct darray_double ground_building_connections; /* Connection of unremoved buildings */
+ struct darray_double ground_patches; /* patches for removed buildings */
+ struct scad_geometry* top; /* Uncounted ptr to top boundary */
};
res_T
@@ -45,8 +47,7 @@ ground_build_cad
res_T
ground_export_stl
- (struct ground* ground,
- const int binary);
+ (struct city* city);
void
ground_init
diff --git a/src/cg_main.c b/src/cg_main.c
@@ -24,6 +24,7 @@
#include "cg_city_parsing.h"
#include "cg_catalog.h"
#include "cg_catalog_parsing.h"
+#include "cg_vertex_denoiser.h"
#include <cyaml/cyaml.h>
@@ -135,6 +136,16 @@ int main
if(args->verbosity_level < 3)
logger_set_stream(&logger, LOG_OUTPUT, NULL, NULL);
+#ifndef NDEBUG
+ if(args->verbosity_level == 3) {
+ logger_print(&logger, LOG_WARNING,
+ "Running debug binary with increased verbosity.\n");
+ } else {
+ logger_print(&logger, LOG_WARNING,
+ "Running debug binary.\n");
+ }
+#endif
+
/* Parse catalog.
* No semantic validation is done at this stage */
ERR(parse_catalog(&args->catalog_files, &allocator, &logger, &config,
@@ -166,15 +177,22 @@ int main
ERR(city_ground_build(city));
exit:
- release_city(city);
- release_catalog(catalog);
- if(args) release_args(args);
- time_sub(&dt, time_current(&dt), &t0);
- time_dump(&dt, TIME_MIN | TIME_SEC, NULL, buf, sizeof(buf));
- if(logger_initialized) {
+ if(logger_initialized && city) {
+ size_t count, denoised_count;
+ count = vertex_denoiser_get_count(city->denoiser);
+ denoised_count = vertex_denoiser_get_denoised_count(city->denoiser);
+ if(denoised_count > 0) {
+ logger_print(&logger, LOG_OUTPUT, "Denoised %lu / %lu vertices.\n",
+ denoised_count, count);
+ }
+ time_sub(&dt, time_current(&dt), &t0);
+ time_dump(&dt, TIME_MIN | TIME_SEC, NULL, buf, sizeof(buf));
logger_print(&logger, LOG_OUTPUT, "Total run time: %s.\n", buf);
logger_release(&logger);
}
+ release_city(city);
+ release_catalog(catalog);
+ if(args) release_args(args);
if(allocator_initialized) {
if(check_memory_allocator(&allocator)) err = EXIT_FAILURE;
mem_shutdown_proxy_allocator(&allocator);
diff --git a/src/cg_vertex_denoiser.c b/src/cg_vertex_denoiser.c
@@ -0,0 +1,361 @@
+/* Copyright (C) 2022 Université de Pau et des Pays de l'Adour UPPA
+ * Copyright (C) 2022 CNRS
+ * Copyright (C) 2022 Sorbonne Université
+ * Copyright (C) 2022 Université Paul Sabatier
+ * Copyright (C) 2022 |Meso|Star> (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 "cg.h"
+#include "cg_vertex_denoiser.h"
+
+#include <rsys/rsys.h>
+#include <rsys/dynamic_array_double.h>
+#include <rsys/double3.h>
+#include <rsys/mem_allocator.h>
+#include <rsys/str.h>
+
+#include <float.h>
+
+#define NODE_COUNT_MAX 64
+
+enum node_type {
+ LEAF,
+ NODE
+};
+
+struct leaf {
+ double vertices[3*NODE_COUNT_MAX];
+ unsigned count;
+};
+
+struct node {
+ enum node_type type;
+ unsigned split_dim;
+ double split_pos;
+ union {
+ struct leaf* leaf;
+ struct node* children[2];
+ } c;
+};
+
+static res_T
+alloc_leaf_node
+ (struct mem_allocator* allocator,
+ struct node** leaf)
+{
+ res_T res = RES_OK;
+ struct node* node = NULL;
+
+ ASSERT(allocator && leaf);
+
+ node = MEM_CALLOC(allocator, 1, sizeof(*node));
+ if(!node) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ node->c.leaf = MEM_CALLOC(allocator, 1, sizeof(*node->c.leaf));
+ if(!node->c.leaf) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ node->type = LEAF;
+
+exit:
+ *leaf = node;
+ return res;
+error:
+ if(node && node->c.leaf) MEM_RM(allocator, node->c.leaf);
+ if(node) MEM_RM(allocator, node);
+ node = NULL;
+ goto exit;
+}
+
+static void
+release_node
+ (struct mem_allocator* allocator,
+ struct node* node)
+{
+ ASSERT(allocator && node);
+ if(node->type == LEAF) {
+ MEM_RM(allocator, node->c.leaf);
+ MEM_RM(allocator, node);
+ } else {
+ ASSERT(node->type == NODE);
+ release_node(allocator, node->c.children[0]);
+ release_node(allocator, node->c.children[1]);
+ MEM_RM(allocator, node);
+ }
+}
+
+static void
+add_to_leaf
+ (struct node* node,
+ const double v[3])
+{
+ ASSERT(node && v && node->c.leaf->count < NODE_COUNT_MAX);
+ d3_set(node->c.leaf->vertices + (node->c.leaf->count++ * 3), v);
+}
+
+static res_T
+search_and_add
+ (struct node* node,
+ struct vertex_denoiser* denoiser,
+ const double in[3],
+ const int can_add,
+ double out[3],
+ int* done,
+ size_t* added_count,
+ size_t* denoised_count)
+{
+ res_T res = RES_OK;
+ struct mem_allocator* allocator;
+
+ ASSERT(node && denoiser && in && out && added_count && denoised_count);
+ allocator = denoiser->allocator;
+
+ if(node->type == LEAF) {
+ int same = 0;
+ unsigned i;
+ double* replacement = NULL;
+ struct leaf* leaf = node->c.leaf;
+ for(i = 0; i < leaf->count; i++) {
+ double dist2, tmp[3];
+ if(d3_eq(leaf->vertices + i*3, in)) {
+ same = 1;
+ break;
+ }
+ d3_sub(tmp, leaf->vertices + i*3, in);
+ dist2 = d3_dot(tmp, tmp);
+ if(dist2 <= denoiser->eps2) {
+ replacement = leaf->vertices + i*3;
+ break;
+ }
+ }
+ if(same) {
+ if(in != out) d3_set(out, in);
+ if(done) *done = 1;
+ }
+ else if(replacement) {
+ d3_set(out, replacement);
+ (*denoised_count)++;
+ if(done) *done = 1;
+ }
+ else if(can_add) {
+ /* Vertex not registered yet: register and keep */
+ if(leaf->count < NODE_COUNT_MAX) {
+ add_to_leaf(node, in);
+ (*added_count)++;
+ } else {
+ /* Need mode room: change leaf to node with 2 leaf children */
+ double var[3], tmp1[3], tmp2[3], tmp3[3], split_pos;
+ unsigned split_dim;
+ double range[2] = { DBL_MAX, -DBL_MAX };
+ double sum[3] = { 0, 0, 0 }, sum2[3] = { 0, 0, 0 };
+ /* Chose the dim to split wrt variance */
+ for(i = 0; i < NODE_COUNT_MAX; i++) {
+ double tmp[3];
+ d3_mul(tmp, leaf->vertices + 3*i, leaf->vertices + 3*i);
+ d3_add(sum, sum, leaf->vertices + 3*i);
+ d3_add(sum2, sum2, tmp);
+ }
+ d3_divd(tmp1, sum2, NODE_COUNT_MAX);
+ d3_divd(tmp2, sum, NODE_COUNT_MAX);
+ d3_sub(var, tmp1, d3_mul(tmp3, tmp2, tmp2));
+ split_dim = (var[0] > var[1])
+ ? (var[0] > var[2] ? 0 : 2) : (var[1] > var[2] ? 1 : 2);
+ for(i = 0; i < NODE_COUNT_MAX; i++) {
+ range[0] = MMIN(range[0], leaf->vertices[3*i + split_dim]);
+ range[1] = MMAX(range[1], leaf->vertices[3*i + split_dim]);
+ }
+ split_pos = (range[0] + range[1]) * 0.5;
+ /* Slit dimension dim */
+ ERR(alloc_leaf_node(allocator, &node->c.children[0]));
+ ERR(alloc_leaf_node(allocator, &node->c.children[1]));
+ node->split_dim = split_dim;
+ node->split_pos = split_pos;
+ node->type = NODE;
+ for(i = 0; i < NODE_COUNT_MAX; i++) {
+ double* v = leaf->vertices + 3*i;
+ double pos = v[split_dim];
+ struct node* side
+ = (pos <= split_pos) ? node->c.children[0] : node->c.children[1];
+ /* Copy content to children wrt split */
+ add_to_leaf(side, v);
+ }
+ /* Free old leaf */
+ MEM_RM(allocator, leaf);
+ /* Retry same search_and_add */
+ ERR(search_and_add(node, denoiser, in, can_add, out, done, added_count,
+ denoised_count));
+ }
+ if(in != out) d3_set(out, in);
+ if(done) *done = 1;
+ }
+ } else {
+ int ldone = 0;
+ ASSERT(node->type == NODE);
+ /* Due to epsilon, one can have to search in both children.
+ * If the vertex doesn't exist yet, it should be added only once though! */
+ if(node->split_pos + denoiser->eps >= in[node->split_dim]) {
+ int in_range = (node->split_pos >= in[node->split_dim]);
+ ERR(search_and_add(node->c.children[0], denoiser, in, in_range, out,
+ &ldone, added_count, denoised_count));
+ if(done && ldone) *done = 1;
+ }
+ if(!ldone && node->split_pos - denoiser->eps <= in[node->split_dim]) {
+ int in_range = (node->split_pos < in[node->split_dim]);
+ ERR(search_and_add(node->c.children[1], denoiser, in, in_range, out, done,
+ added_count, denoised_count));
+ }
+ }
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+res_T
+vertex_denoiser_create
+ (struct mem_allocator* allocator,
+ const double eps,
+ struct vertex_denoiser** denoiser)
+{
+ res_T res = RES_OK;
+ struct vertex_denoiser* den = NULL;
+
+ ASSERT(allocator && eps >= 0 && denoiser);
+
+ den = MEM_CALLOC(allocator, 1, sizeof(*den));
+ if(!den) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ den->allocator = allocator;
+ den->eps = eps;
+ den->eps2 = eps*eps;
+ ERR(alloc_leaf_node(allocator, &den->root));
+
+exit:
+ *denoiser = den;
+ return res;
+error:
+ if(den) vertex_denoiser_release(den);
+ den = NULL;
+ goto exit;
+}
+
+void
+vertex_denoiser_release
+ (struct vertex_denoiser* denoiser)
+{
+ ASSERT(denoiser);
+ release_node(denoiser->allocator, denoiser->root);
+ MEM_RM(denoiser->allocator, denoiser);
+}
+
+size_t
+vertex_denoiser_get_count
+ (struct vertex_denoiser* denoiser)
+{
+ ASSERT(denoiser);
+ return denoiser->count;
+}
+
+size_t
+vertex_denoiser_get_denoised_count
+ (struct vertex_denoiser* denoiser)
+{
+ ASSERT(denoiser);
+ return denoiser->denoised_count;
+}
+
+res_T
+vertex_denoiser_denoise
+ (struct vertex_denoiser* denoiser,
+ const double *in,
+ const size_t count,
+ double *out)
+{
+ res_T res = RES_OK;
+ size_t n;
+
+ ASSERT(denoiser && in && out);
+
+ for(n = 0; n < count; n++) {
+ size_t added = 0, denoised = 0;
+ ERR(search_and_add(denoiser->root, denoiser, in + 3*n, 1, out + 3*n, NULL,
+ &added, &denoised));
+ denoiser->count += added;
+ denoiser->denoised_count += denoised;
+ }
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+res_T
+denoise_array
+ (struct vertex_denoiser* denoiser,
+ struct darray_double* array)
+{
+ res_T res = RES_OK;
+
+ ASSERT(denoiser && array);
+
+ ASSERT(darray_double_size_get(array) % 3 == 0);
+ ERR(vertex_denoiser_denoise(denoiser,
+ darray_double_cdata_get(array), darray_double_size_get(array) / 3,
+ darray_double_data_get(array)));
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
+res_T
+stl_export_denoised_geometry
+ (struct vertex_denoiser* denoiser,
+ struct scad_geometry* geom,
+ const enum scad_normals_orientation orientation,
+ const int binary)
+{
+ res_T res = RES_OK;
+ struct darray_double trg;
+ const char* name;
+ struct str filename;
+
+ ASSERT(denoiser && geom);
+
+ darray_double_init(denoiser->allocator, &trg);
+ str_init(denoiser->allocator, &filename);
+ ERR(scad_geometry_get_name(geom, &name));
+ ERR(str_set(&filename, name));
+ ERR(str_append(&filename, ".stl"));
+ ERR(scad_stl_get_data(geom, &trg));
+ ERR(denoise_array(denoiser, &trg));
+ ERR(scad_stl_data_write(&trg, str_cget(&filename), orientation, binary));
+
+exit:
+ darray_double_release(&trg);
+ str_release(&filename);
+ return res;
+error:
+ goto exit;
+}
+
diff --git a/src/cg_vertex_denoiser.h b/src/cg_vertex_denoiser.h
@@ -0,0 +1,81 @@
+/* Copyright (C) 2022 Université de Pau et des Pays de l'Adour UPPA
+ * Copyright (C) 2022 CNRS
+ * Copyright (C) 2022 Sorbonne Université
+ * Copyright (C) 2022 Université Paul Sabatier
+ * Copyright (C) 2022 |Meso|Star> (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/>. */
+
+#ifndef VERTEX_DENOISER_H__
+#define VERTEX_DENOISER_H__
+
+#include <star/scad.h>
+
+#include <rsys/rsys.h>
+
+struct mem_allocator;
+struct node;
+struct scpr_polygon;
+
+struct vertex_denoiser {
+ struct mem_allocator* allocator;
+ double eps, eps2;
+ struct node* root;
+ size_t count, denoised_count;
+};
+
+res_T
+vertex_denoiser_create
+ (struct mem_allocator* allocator,
+ const double eps,
+ struct vertex_denoiser** denoiser);
+
+void
+vertex_denoiser_release
+ (struct vertex_denoiser* denoiser);
+
+size_t
+vertex_denoiser_get_count
+ (struct vertex_denoiser* denoiser);
+
+size_t
+vertex_denoiser_get_denoised_count
+ (struct vertex_denoiser* denoiser);
+
+res_T
+vertex_denoiser_denoise
+ (struct vertex_denoiser* denoiser,
+ const double *in,
+ const size_t count,
+ double *out); /* Can be out==in */
+
+res_T
+denoise_polygon
+ (struct vertex_denoiser* denoiser,
+ struct scpr_polygon* polygon);
+
+res_T
+denoise_array
+ (struct vertex_denoiser* denoiser,
+ struct darray_double* array);
+
+res_T
+stl_export_denoised_geometry
+ (struct vertex_denoiser* denoiser,
+ struct scad_geometry* geom,
+ const enum scad_normals_orientation orientation,
+ const int binary);
+
+#endif
+