city_generator2

Generated conformal 3D meshes representing a city
git clone git://git.meso-star.fr/city_generator2.git
Log | Files | Refs | README | LICENSE

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:
Mcmake/CMakeLists.txt | 6++++--
Msrc/cg_args.c | 8++++++--
Msrc/cg_building.c | 32++++++++++++++++++--------------
Msrc/cg_building.h | 30+++++++++++++++++++-----------
Msrc/cg_city.c | 246++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Msrc/cg_city.h | 15+++++++++++----
Msrc/cg_construction_mode.c | 4+++-
Msrc/cg_construction_mode_0.c | 74++++++++++++++++++++++++++++++++++++++++++++------------------------------
Msrc/cg_construction_mode_1.c | 253++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Msrc/cg_construction_mode_1.h | 4++++
Msrc/cg_ground.c | 50++++++++++++++++++++++++++++++++++++++++----------
Msrc/cg_ground.h | 7++++---
Msrc/cg_main.c | 30++++++++++++++++++++++++------
Asrc/cg_vertex_denoiser.c | 361+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/cg_vertex_denoiser.h | 81+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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, &current_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, &current_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(&current_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(&current_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(&current_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 +