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 3503c8f56901411c9f3485216f05162bddbaa0e7
parent fe939049b21927749f5021d73f1c7965e26467ff
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Mon,  3 Mar 2025 09:32:12 +0100

Merge branch 'features_mode2' into develop

Diffstat:
MMakefile | 4++++
Mconfig.mk | 4++++
Mdoc/city_generator2-input.5 | 36+++++++++++++++++++++++++++++++++---
Mdoc/city_generator2.1.in | 21+++++++++++++++------
Msrc/cg_args.c | 17++++++++++++++---
Msrc/cg_args.h | 1+
Msrc/cg_building.c | 2+-
Msrc/cg_building.h | 8+++++---
Msrc/cg_catalog.c | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/cg_catalog.h | 2++
Msrc/cg_catalog_parsing.c | 13+++++++++++--
Msrc/cg_city.c | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Msrc/cg_city.h | 2++
Msrc/cg_city_parsing_schemas.h | 8+++++++-
Msrc/cg_construction_mode.c | 7++++++-
Msrc/cg_construction_mode_0.c | 91+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Msrc/cg_construction_mode_1.c | 143++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
Asrc/cg_construction_mode_2.c | 2322+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/cg_construction_mode_2.h | 136+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/cg_construction_mode_2_parsing_schemas.h | 133+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/cg_construction_modes_parsing_schemas.h | 4+++-
Msrc/cg_default.h.in | 2++
Msrc/cg_ground.c | 25+++++++++++++++++++++++++
Msrc/cg_ground.h | 1+
Asrc/cg_stardis_model.h | 357+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
25 files changed, 3403 insertions(+), 49 deletions(-)

diff --git a/Makefile b/Makefile @@ -33,6 +33,7 @@ SRC =\ src/cg_city_parsing.c\ src/cg_construction_mode_0.c\ src/cg_construction_mode_1.c\ + src/cg_construction_mode_2.c\ src/cg_construction_mode.c\ src/cg_ground.c\ src/cg_main.c\ @@ -75,6 +76,8 @@ src/cg_default.h: config.mk src/cg_default.h.in -e 's/@CG2_CLOSE_NEIGHBOR_DISTANCE@/$(CG2_CLOSE_NEIGHBOR_DISTANCE)/' \ -e 's/@CG2_MIN_DISTANCE_TO_MAP_LIMITS@/$(CG2_MIN_DISTANCE_TO_MAP_LIMITS)/' \ -e 's/@CG2_MIN_WINDOWS_WIDTH@/$(CG2_MIN_WINDOWS_WIDTH)/' \ + -e 's/@CG2_GLAZING_THICKNESS@/$(CG2_GLAZING_THICKNESS)/' \ + -e 's/@CG2_ARGS_DEFAULT_STARDIS_FILES_BASENAME@/$(CG2_ARGS_DEFAULT_STARDIS_FILES_BASENAME)/' \ $@.in > $@ src/cg_version.h: config.mk src/cg_version.h.in @@ -101,6 +104,7 @@ doc/city_generator2.1: doc/city_generator2.1.in -e 's/@CG2_ARGS_CHANGE_BINARY_DEFAULT_OPTION@/$(CG2_ARGS_CHANGE_BINARY_DEFAULT_OPTION)/' \ -e 's/@CG2_ARGS_STL_NON_DEFAULT_STR@/$(CG2_ARGS_STL_NON_DEFAULT_STR)/' \ -e 's/@CG2_ARGS_STL_DEFAULT_STR@/$(CG2_ARGS_STL_DEFAULT_STR)/' \ + -e 's/@CG2_ARGS_DEFAULT_STARDIS_FILES_BASENAME@/$(CG2_ARGS_DEFAULT_STARDIS_FILES_BASENAME)/' \ $@.in > $@ ################################################################################ diff --git a/config.mk b/config.mk @@ -23,6 +23,10 @@ CG2_CLOSE_NEIGHBOR_DISTANCE = 2 CG2_MIN_DISTANCE_TO_MAP_LIMITS = 2 # Minimum width for windows or they are not created (in m) CG2_MIN_WINDOWS_WIDTH = 0.1 +# Glazing thickness +CG2_GLAZING_THICKNESS = 0.024 +# Default base name for the stardis files +CG2_ARGS_DEFAULT_STARDIS_FILES_BASENAME = stardis_model ################################################################################ # Tools diff --git a/doc/city_generator2-input.5 b/doc/city_generator2-input.5 @@ -79,20 +79,24 @@ The city map file format is as follows: .It Ao Va building Ac Ta ::= Ta Qo \ \ -name: \& Qc Ao Va name Ac .It Ta Ta Qo \ \ \ construction_mode: \& Qc Ao Va cmode-name Ac .It Ta Ta Qo \ \ \ dataset: \& Qc Ao Va dataset-name Ac -.It Ta Ta Qo \ \ \ height: \& Qc Ao Va height Ac .It Ta Ta Qo \ \ \ footprint: [ Qc Ao Va vertices Ac Qo ] Qc +.It Ta Ta Ao Va height-spec Ac .It Ao Va name Ac Ta ::= Ta Vt STRING .It Ao Va cmode-name Ac Ta ::= Ta Qo Construction_Mode_0 Qc | Qo Construction_Mode_1 Qc .It Ao Va dataset-name Ac Ta ::= Ta Vt STRING .It Ta Ta # The name must be found in a catalog file .It Ta Ta # with the same construction mode -.It Ao Va height Ac Ta ::= Ta Vt REAL No # > 0, in m +.It Ao Va height-spec Ac Ta ::= Ta Qo \ \ \ height: \& Qc Ao Va height Ac +.It Ta | Ta Qo \ \ \ levels_height: [ Qc Ao Va height-list Ac Qo ] Qc .It Ao Va vertices Ac Ta ::= Ta Ao Va vertex Ac Bo \& Qo , Qc Ao Va vertices Ac \& Bc .It Ta Ta # \&At least 3 vertices .It Ao Va vertex Ac Ta ::= Ta Qo \&[ Qc Ao Va X Ac Qo , Qc Ao Va Y Ac Qo ] Qc .It Ao Va X Ac Ta ::= Ta Vt REAL No # in m .It Ao Va Y Ac Ta ::= Ta Vt REAL No # in m +.It Ao Va height-list Ac Ta ::= Ta Ao Va height Ac Bo \& Qo , Qc Ao Va height-list Ac \& Bc +.It Ao Va height Ac Ta ::= Ta Vt REAL No # > 0, in m .El + .\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .Ss CATALOG FILE FORMAT There are currently two different catalog file formats, identified by name, @@ -108,13 +112,18 @@ construction mode. .Pp The catalog file format is as follows: .Bl -column (descri-line) (::) () -.It Ao Va catalog-file Ac Ta ::= Ta Ao Va catalog-file-0 Ac | Ao Va catalog-file-1 Ac +.It Ao Va catalog-file Ac Ta ::= Ta Ao Va catalog-file-0 Ac +.It Ta | Ta Ao Va catalog-file-1 Ac +.It Ta | Ta Ao Va catalog-file-2 Ac .It Ao Va catalog-file-0 Ac Ta ::= Ta Qo construction_mode: Construction_Mode_0 Qc .It Ta Ta Qo datasets: \& Qc .It Ta Ta Ao Va datasets-0 Ac .It Ao Va catalog-file-1 Ac Ta ::= Ta Qo construction_mode: Construction_Mode_1 Qc .It Ta Ta Qo datasets: \& Qc .It Ta Ta Ao Va datasets-1 Ac +.It Ao Va catalog-file-2 Ac Ta ::= Ta Qo construction_mode: Construction_Mode_2 Qc +.It Ta Ta Qo datasets: \& Qc +.It Ta Ta Ao Va datasets-2 Ac .It Ao Va datasets-0 Ac Ta ::= Ta Ao Va dataset-0 Ac .It Ta Ta [ \& Ao Va datasets-0 Ac \& ] .It Ao Va datasets-1 Ac Ta ::= Ta Ao Va dataset-1 Ac @@ -140,6 +149,27 @@ The catalog file format is as follows: .It Ta Ta Qo \ \ \ external_insulation_thickness: \& Qc Vt REAL .It Ta Ta No # >= 0, in m .It Ta Ta Qo \ \ \ foundation_depth: \& Qc Vt REAL No # >= 0, in m +.It Ao Va dataset-2 Ac Ta ::= Ta Qo \ \ -name: \& Qc Ao Va name Ac +.It Ta Ta Qo \ \ \ wall_thickness: \& Qc Vt REAL No # > 0, in m +.It Ta Ta Qo \ \ \ floor_thickness: \& Qc Vt REAL No # > 0, in m +.It Ta Ta Qo \ \ \ inter_floor_thickness: \& Qc Vt REAL No # > 0, in m +.It Ta Ta Qo \ \ \ roof_thickness: \& Qc Vt REAL No # > 0, in m +.It Ta Ta Qo \ \ \ crawl_height: \& Qc Vt REAL No # >= 0, in m +.It Ta Ta Qo \ \ \ foundation_depth: \& Qc Vt REAL No # >= 0, in m +.It Ta Ta Qo \ \ \ has_attic: \& Qc Vt 0 | 1 No +.It Ta Ta Qo \ \ \ glass_ratio: \& Qc Vt REAL No # >= 0 and <= 1 +.It Ta Ta Qo \ \ \ windows_min_width: \& Qc Vt REAL No # >= 0 +.It Ta Ta Qo \ \ \ windows_max_width: \& Qc Vt REAL No # >= 0 +.It Ta Ta Qo \ \ \ windows_min_spacing: \& Qc Vt REAL No # >= 0 +.It Ta Ta Qo \ \ \ windows_height_ratio: \& Qc Vt REAL No # >= 0 and <= 1 +.It Ta Ta Qo \ \ \ floor_insulation_thickness: \& Qc Vt REAL +.It Ta Ta No # >= 0, in m +.It Ta Ta Qo \ \ \ roof_insulation_thickness: \& Qc Vt REAL +.It Ta Ta No # >= 0, in m +.It Ta Ta Qo \ \ \ internal_insulation_thickness: \& Qc Vt REAL +.It Ta Ta No # >= 0, in m +.It Ta Ta Qo \ \ \ external_insulation_thickness: \& Qc Vt REAL +.It Ta Ta No # >= 0, in m .It Ao Va name Ac Ta ::= Ta Vt STRING .El .Sh SEE ALSO diff --git a/doc/city_generator2.1.in b/doc/city_generator2.1.in @@ -97,12 +97,15 @@ construction mode among other things. Please refer to .Xr city_generator2-input 5 for more information on formats. -.It Fl s -Force single threaded execution. -By default use as many threads as available. -Note however that only some parts of the processing is parallelized. -Therefore, poor speedup when run multithreaded is not a bug and can even be -expected. +.It Fl s Ar base_name +Specify a base name for the stardis files that are created along the STL files. +The two files created are the stardis model file, whose name is +.Ar base_name.txt, and the shell script file name dedicated to set the numerous +script shell variables used in the model file, and whose name is +.Ar base_name.sh. +Default +.Ar base_name +is "@CG2_ARGS_DEFAULT_STARDIS_FILES_BASENAME@". .It Fl v Output version information and exit. .It Fl V Ar level @@ -124,6 +127,12 @@ All the messages are written to standard error. Default verbosity .Ar level is @CG2_ARGS_DEFAULT_VERBOSITY_LEVEL@. +.It Fl 1 +Force single threaded execution. +By default use as many threads as available. +Note however that only a small part of the processing is parallelized. +Therefore, poor speedup when run multithreaded is not a bug and can even be +expected. .El .Sh EXIT STATUS .Ex -std diff --git a/src/cg_args.c b/src/cg_args.c @@ -112,9 +112,9 @@ parse_args { res_T res = RES_OK; int opt; - int info_provided = 0, c_provided = 0, m_provided = 0; + int info_provided = 0, c_provided = 0, m_provided = 0, s_provided = 0; struct args* args; - char option_list[] = "?c:m:hkEF:f:svV:"; + char option_list[] = "?c:m:hkEF:f:s:vV:1"; ASSERT(allocator && logger && argv && out_args); @@ -135,6 +135,7 @@ parse_args /* Set non-zero default values */ args->binary_export = CG2_ARGS_BINARY_STL_DEFAULT; args->verbosity_level = CG2_ARGS_DEFAULT_VERBOSITY_LEVEL; + args->stardis_basename = CG2_ARGS_DEFAULT_STARDIS_FILES_BASENAME; opterr = 0; /* No default error messages */ while((opt = getopt(argc, argv, option_list)) != -1) { @@ -212,7 +213,13 @@ parse_args break; case 's': - args->single_thread = 1; + if(s_provided) { + logger_print(logger, LOG_ERROR, "Option -%c provided twice.\n", opt); + res = RES_BAD_ARG; + goto error; + } + args->stardis_basename = optarg; + s_provided = 1; break; case 'v': @@ -233,6 +240,10 @@ parse_args goto error; } break; + + case '1': + args->single_thread = 1; + break; } } diff --git a/src/cg_args.h b/src/cg_args.h @@ -34,6 +34,7 @@ struct args { struct mem_allocator* allocator; struct logger* logger; char* city_filename; + char* stardis_basename; struct darray_names catalog_files; struct darray_names dump_footprint_names; int binary_export; diff --git a/src/cg_building.c b/src/cg_building.c @@ -84,7 +84,7 @@ build_envelop { res_T res = RES_OK; size_t nverts = 0; - double height = building->height; + double height = building->total_height; double d[3] = {0, 0, 0}; struct scpr_polygon* pg = building->pg; struct scad_geometry* footprint = NULL; diff --git a/src/cg_building.h b/src/cg_building.h @@ -23,7 +23,7 @@ #include <rsys/rsys.h> #include <rsys/str.h> #include <rsys/dynamic_array.h> -#include <rsys/dynamic_array.h> +#include <rsys/dynamic_array_double.h> #include <rsys/hash_table.h> struct scpr_polygon; @@ -33,7 +33,6 @@ struct building; struct parsed_city_building; struct city; struct darray_adjoining_data; -struct darray_double; struct darray_geometries; /* An htable to uniquely associate flags to buildings */ @@ -99,6 +98,7 @@ struct construction_mode_functors { enum construction_mode_type { mode_0, mode_1, + mode_2, Construction_MODES_COUNT__ }; @@ -113,9 +113,11 @@ struct building { /* generic construction mode data */ struct str name; + struct str dataset_name; struct str external_layer_name; struct city* city; - double height; + double total_height; + struct darray_double levels_height; struct scpr_polygon* pg; struct htable_building close_buildings; /* links to other buildings */ int structs_initialized; diff --git a/src/cg_catalog.c b/src/cg_catalog.c @@ -23,8 +23,10 @@ #include "cg_catalog_parsing.h" #include "cg_construction_mode_0.h" #include "cg_construction_mode_1.h" +#include "cg_construction_mode_2.h" #include "cg_construction_mode_0_parsing_schemas.h" #include "cg_construction_mode_1_parsing_schemas.h" +#include "cg_construction_mode_2_parsing_schemas.h" #include <rsys/rsys.h> #include <rsys/mem_allocator.h> @@ -65,6 +67,16 @@ find_first_filename } break; } + case mode_2: { + const struct parsed_catalog_cmode_2* prsd_2 = items[i].parsed_data; + for(j = 0; j < prsd_2->datasets_count; j++) { + const struct parsed_dataset_cmode_2* prsd_item = prsd_2->datasets + j; + if(0 == strcmp(prsd_item->name, dataset_name)) { + return items[i].filename; + } + } + break; + } default: FATAL("Invalid enum value."); } } @@ -100,6 +112,7 @@ create_catalog catalog->logger = logger; htable_dataset_cmode_0_init(allocator, &catalog->catalog_0); htable_dataset_cmode_1_init(allocator, &catalog->catalog_1); + htable_dataset_cmode_2_init(allocator, &catalog->catalog_2); count = darray_parsed_catalog_items_size_get(&parsed_catalog->catalog); items = darray_parsed_catalog_items_cdata_get(&parsed_catalog->catalog); @@ -166,6 +179,46 @@ create_catalog } break; } + case mode_2: { + const struct parsed_catalog_cmode_2* parsed_2 = items[i].parsed_data; + for(j = 0; j < parsed_2->datasets_count; j++) { + const struct parsed_dataset_cmode_2* parsed_item + = parsed_2->datasets + j; + struct dataset_cmode_2 item; + ERR(str_set(&name, parsed_item->name)); + /* test name unicity */ + if(htable_dataset_cmode_2_find(&catalog->catalog_2, &name)) { + const char* first_filename + = find_first_filename(parsed_item->name, PARSED_CMODE_2, items, count); + logger_print(logger, LOG_ERROR, + "Duplicate dataset name: '%s' (in files '%s' and '%s').\n", + parsed_item->name, first_filename, items[i].filename); + res = RES_BAD_ARG; + goto error; + } + /* Setup item */ + item.wall_thickness = parsed_item->wall_thickness; + item.floor_thickness = parsed_item->floor_thickness; + item.inter_floor_thickness = parsed_item->inter_floor_thickness; + item.roof_thickness = parsed_item->roof_thickness; + item.internal_insulation_thickness + = parsed_item->internal_insulation_thickness; + item.external_insulation_thickness + = parsed_item->external_insulation_thickness; + item.floor_insulation_thickness = parsed_item->floor_insulation_thickness; + item.roof_insulation_thickness = parsed_item->roof_insulation_thickness; + item.foundation_depth = parsed_item->foundation_depth; + item.crawl_height = parsed_item->crawl_height; + item.has_attic = parsed_item->has_attic; + item.glass_ratio = parsed_item->glass_ratio; + item.windows_min_width = parsed_item->windows_min_width; + item.windows_max_width = parsed_item->windows_max_width; + item.windows_min_spacing = parsed_item->windows_min_spacing; + item.windows_height_ratio = parsed_item->windows_height_ratio; + ERR(htable_dataset_cmode_2_set(&catalog->catalog_2, &name, &item)); + } + break; + } default: FATAL("Invalid enum value."); } } @@ -187,6 +240,7 @@ release_catalog htable_dataset_cmode_0_release(&catalog->catalog_0); htable_dataset_cmode_1_release(&catalog->catalog_1); + htable_dataset_cmode_2_release(&catalog->catalog_2); MEM_RM(catalog->allocator, catalog); } diff --git a/src/cg_catalog.h b/src/cg_catalog.h @@ -22,6 +22,7 @@ #include "cg_construction_mode_0.h" #include "cg_construction_mode_1.h" +#include "cg_construction_mode_2.h" struct mem_allocator; struct logger; @@ -33,6 +34,7 @@ struct catalog { struct logger* logger; struct htable_dataset_cmode_0 catalog_0; struct htable_dataset_cmode_1 catalog_1; + struct htable_dataset_cmode_2 catalog_2; }; res_T diff --git a/src/cg_catalog_parsing.c b/src/cg_catalog_parsing.c @@ -22,6 +22,7 @@ #include "cg_catalog_parsing.h" #include "cg_construction_mode_0_parsing_schemas.h" #include "cg_construction_mode_1_parsing_schemas.h" +#include "cg_construction_mode_2_parsing_schemas.h" #include "cg_city_parsing_schemas.h" #include "cg_cyaml.h" @@ -38,6 +39,8 @@ get_schema_from_parsed_cmode return &construction_mode_0_schema; case PARSED_CMODE_1: return &construction_mode_1_schema; + case PARSED_CMODE_2: + return &construction_mode_2_schema; default: FATAL("Invalid enum value.\n"); } } @@ -101,10 +104,16 @@ parse_catalog /* Log outcome */ switch(items[i].construction_mode) { case PARSED_CMODE_0: - set_count = ((struct parsed_catalog_cmode_0*)items[i].parsed_data)->datasets_count; + set_count = + ((struct parsed_catalog_cmode_0*)items[i].parsed_data)->datasets_count; break; case PARSED_CMODE_1: - set_count = ((struct parsed_catalog_cmode_1*)items[i].parsed_data)->datasets_count; + set_count = + ((struct parsed_catalog_cmode_1*)items[i].parsed_data)->datasets_count; + break; + case PARSED_CMODE_2: + set_count = + ((struct parsed_catalog_cmode_2*)items[i].parsed_data)->datasets_count; break; default: FATAL("Invalid enum value.\n"); } diff --git a/src/cg_city.c b/src/cg_city.c @@ -22,6 +22,7 @@ #include "cg_city.h" #include "cg_construction_mode_0.h" #include "cg_construction_mode_1.h" +#include "cg_construction_mode_2.h" #include "cg_ground.h" #include "cg_catalog_parsing.h" #include "cg_city_parsing.h" @@ -29,6 +30,7 @@ #include "cg_args.h" #include "cg_city_parsing_schemas.h" #include "cg_vertex_denoiser.h" +#include "cg_stardis_model.h" #include <rsys/rsys.h> #include <rsys/logger.h> @@ -383,7 +385,7 @@ create_city = SCPR_INTERSECTOR_CHECK_CALLBACKS_NULL__; struct callback_ctx ctx = CB_CTX_NULL__; struct scpr_polygon* tmp_polygon = NULL; - struct str name; + struct str name, filename; int error_occured = 0; struct building* building = NULL; struct darray_polygons offset_polygons; @@ -394,6 +396,7 @@ create_city time_current(&t0); str_init(allocator, &name); + str_init(allocator, &filename); darray_polygons_init(allocator, &offset_polygons); htable_names_init(allocator, &names); @@ -425,6 +428,46 @@ create_city city->dump_footprints_level = args->dump_footprints_level; htable_names_init(allocator, &city->dump_footprint_names); htable_common_init(allocator, &city->common); + ERR(str_printf(&filename, "%s.txt", args->stardis_basename)); + city->stardis_model = fopen(str_cget(&filename), "w"); + if(!city->stardis_model) { + logger_print(logger, LOG_ERROR, + "Cannot create stardis model file: '%s'.\n", + str_cget(&filename)); + res = RES_IO_ERR; + goto error; + } + ERR(str_printf(&filename, "%s.sh", args->stardis_basename)); + city->set_vars = fopen(str_cget(&filename), "w"); + if(!city->set_vars) { + logger_print(logger, LOG_ERROR, + "Cannot create stardis shell script file: '%s'.\n", + str_cget(&filename)); + res = RES_IO_ERR; + goto error; + } + fprintf(city->set_vars, "#!/bin/sh\n\n"); + fprintf(city->set_vars, GENERIC_INTERNAL_INSULATION_VARS); + fprintf(city->set_vars, GENERIC_GROUND_VARS); + fprintf(city->set_vars, GENERIC_B_GROUND_VARS); + fprintf(city->set_vars, GENERIC_WALL_VARS); + fprintf(city->set_vars, GENERIC_FLOOR_VARS); + fprintf(city->set_vars, GENERIC_INTERMEDIATE_FLOOR_VARS); + fprintf(city->set_vars, GENERIC_ROOF_VARS); + fprintf(city->set_vars, GENERIC_FOUNDATION_VARS); + fprintf(city->set_vars, GENERIC_INTERNAL_INSULATION_VARS); + fprintf(city->set_vars, GENERIC_EXTERNAL_INSULATION_VARS); + fprintf(city->set_vars, GENERIC_ROOF_INSULATION_VARS); + fprintf(city->set_vars, GENERIC_FLOOR_INSULATION_VARS); + fprintf(city->set_vars, GENERIC_GLAZING_VARS); + fprintf(city->set_vars, GENERIC_CAVITY_VARS); + fprintf(city->set_vars, GENERIC_ATTIC_CAVITY_VARS); + fprintf(city->set_vars, GENERIC_HABITABLE_CAVITY_VARS); + fprintf(city->set_vars, GENERIC_CRAWLSPACE_CAVITY_VARS); + fprintf(city->set_vars, GENERIC_SFC_VARS); + fprintf(city->set_vars, GENERIC_B_WALL_VARS); + fprintf(city->set_vars, GENERIC_B_ROOF_VARS); + ground_init(allocator, &city->ground); ERR(vertex_denoiser_create(allocator, pow(10, -(CLIPPER_PRECISON+2)), &city->denoiser)); @@ -462,6 +505,10 @@ create_city tmp_res = init_cmode_1(building, city, parsed_data, catalog, city->lower, city->upper); break; + case PARSED_CMODE_2: + tmp_res = init_cmode_2(building, city, parsed_data, catalog, + city->lower, city->upper); + break; default: FATAL("Unknown construction mode"); } /* Dump polygon if required */ @@ -585,6 +632,7 @@ create_city ERR(scpr_intersector_check(close_intersector, &callbacks, &ctx)); exit: + str_release(&filename); str_release(&name); darray_polygons_release(&offset_polygons); if(tmp_polygon) SCPR(polygon_ref_put(tmp_polygon)); @@ -625,7 +673,14 @@ release_city(struct city* city) res = RES_BAD_ARG; goto error; } - + if(city->stardis_model) { + fclose(city->stardis_model); + city->stardis_model = NULL; + } + if(city->set_vars) { + fclose(city->set_vars); + city->set_vars = NULL; + } if(city->scpr) SCPR(device_ref_put(city->scpr)); if(city->denoiser) vertex_denoiser_release(city->denoiser); if(city->array_and_tables_initialized) { diff --git a/src/cg_city.h b/src/cg_city.h @@ -142,6 +142,8 @@ struct city { int keep_running_on_errors; int dump_footprints_level; int array_and_tables_initialized; + FILE* stardis_model; + FILE* set_vars; }; res_T diff --git a/src/cg_city_parsing_schemas.h b/src/cg_city_parsing_schemas.h @@ -37,7 +37,9 @@ struct parsed_city_building { enum parsed_cmode_type cmode_type; char* dataset_name; double* vertice; /* flat array of vertice: { x0 y0 x1 y1 x2 y2... } */ + double* levels_height; double height; + unsigned levels_height_count; unsigned vertice_count; }; @@ -108,8 +110,12 @@ static const cyaml_schema_field_t city_building_fields_schema[] = { .count_size = sizeof(((struct parsed_city_building*)0)->vertice_count), .count_offset = offsetof(struct parsed_city_building, vertice_count), }, - CYAML_FIELD_FLOAT("height", CYAML_FLAG_DEFAULT, struct parsed_city_building, + CYAML_FIELD_FLOAT("height", CYAML_FLAG_OPTIONAL, struct parsed_city_building, height), + CYAML_FIELD_SEQUENCE("levels_height", + CYAML_FLAG_OPTIONAL | CYAML_FLAG_POINTER | CYAML_FLAG_FLOW, + struct parsed_city_building, levels_height, &entry_schema_double, + 1, CYAML_UNLIMITED), CYAML_FIELD_END }; diff --git a/src/cg_construction_mode.c b/src/cg_construction_mode.c @@ -63,12 +63,15 @@ init_building_base ASSERT(city && building && parsed_data); building->city = city; - building->height = parsed_data->height; str_init(city->allocator, &building->name); + str_init(city->allocator, &building->dataset_name); str_init(city->allocator, &building->external_layer_name); htable_building_init(city->allocator, &building->close_buildings); + darray_double_init(city->allocator, &building->levels_height); building->structs_initialized = 1; + ERR(str_set(&building->name, parsed_data->name)); + ERR(str_set(&building->dataset_name, parsed_data->dataset_name)); ERR(scpr_polygon_create(city->scpr, &building->pg)); ERR(scpr_polygon_setup_indexed_vertices(building->pg, 1, get_nverts, get_pos, parsed_data)); @@ -119,8 +122,10 @@ release_building_base if(building->structs_initialized) { str_release(&building->name); + str_release(&building->dataset_name); str_release(&building->external_layer_name); htable_building_release(&building->close_buildings); + darray_double_release(&building->levels_height); } if(building->pg) { ERR(scpr_polygon_ref_put(building->pg)); diff --git a/src/cg_construction_mode_0.c b/src/cg_construction_mode_0.c @@ -26,6 +26,7 @@ #include "cg_construction_mode_0.h" #include "cg_construction_mode.h" #include "cg_vertex_denoiser.h" +#include "cg_stardis_model.h" #include <rsys/rsys.h> #include <rsys/str.h> @@ -136,7 +137,7 @@ build_roof ERR(str_append(&name, "_S_roof")); roofname = str_get(&name); - height = b->height; + height = b->total_height; data = (struct dataset_cmode_0*)b->data; e = data->floor_thickness; d[2] = height - e ; @@ -198,7 +199,7 @@ build_wall ERR(build_wall_footprint(pg, pg_int, &footprint)); - d[2] = b->height; + d[2] = b->total_height; ERR(scad_geometry_extrude(footprint, wallname, d, wall)); ERR(darray_geometries_push_back(current_cad, wall)); @@ -228,7 +229,7 @@ build_cavity ASSERT(prefix && pg && b && cavity); - height = b->height; + height = b->total_height; data = (struct dataset_cmode_0*)b->data; e = data->floor_thickness; @@ -446,6 +447,25 @@ init_cmode_0 ERR(init_building_base(building, city, parsed_data)); + if(parsed_data->levels_height_count != 0) { + ERR(logger_print(city->logger, LOG_ERROR, + "Building '%s' defines 'levels_height' " + "(feature not available in construction mode 0).\n", + str_cget(&building->name))); + res = RES_BAD_ARG; + goto error; + } + + if(parsed_data->height <= 0) { + ERR(logger_print(city->logger, LOG_ERROR, + "Building '%s' height definition is invalid " + "(construction mode 0 should define a positive height through the 'height' field).\n", + str_cget(&building->name))); + res = RES_BAD_ARG; + goto error; + } + building->total_height = parsed_data->height; + str_init(allocator, &dataset_name); name_initialized = 1; ERR(str_set(&dataset_name, parsed_data->dataset_name)); @@ -513,10 +533,12 @@ build_cad_cmode_0 scpr = building->city->scpr; allocator = building->city->allocator; logger = building->city->logger; - height = building->height; + height = building->total_height; data = (struct dataset_cmode_0 *)building->data; - logger_print(logger, LOG_OUTPUT, "Building '%s' construction mode 0.\n", name); + logger_print(logger, LOG_OUTPUT, + "Building '%s' construction mode 0, dataset '%s', %g m tall.\n", + name, str_cget(&building->dataset_name), building->total_height); if(height <= 0 || data->wall_thickness <= 0 || data->floor_thickness <= 0) { res = RES_BAD_ARG; @@ -725,16 +747,39 @@ export_stl_cmode_0 int initialized = 0; struct mem_allocator* allocator = NULL; struct city* city; + struct building* building; struct vertex_denoiser* denoiser; + FILE* model = NULL; + FILE* vars = NULL; + fpos_t model_pos, vars_pos; + long model_count = 0; + const char* n; + static int fst = 1; if(!cad) { res = RES_BAD_ARG; goto error; } - city = data_cad->building->city; + building = data_cad->building; + city = building->city; allocator = city->allocator; denoiser = city->denoiser; + model = city->stardis_model; + vars = city->set_vars; + + if(!fst) fprintf(model, "\n"); + fst = 0; + fprintf(model, + "# Building '%s' construction mode 0, dataset '%s', %g m tall\n", + str_cget(&building->name), str_cget(&building->dataset_name), + building->total_height); + fgetpos(model, &model_pos); + fprintf(vars, + "\n# Building '%s' construction mode 0, dataset '%s', %g m tall\n", + str_cget(&building->name), str_cget(&building->dataset_name), + building->total_height); + fgetpos(vars, &vars_pos); /* wall export */ if(darray_geometries_size_get(&data_cad->adj_walls) == 0) { @@ -787,34 +832,53 @@ export_stl_cmode_0 binary)); } + STARDIS_SOLID(wall); + /* floor export */ ERR(stl_export_denoised_geometry(denoiser, data_cad->floor, Scad_force_normals_outward, binary)); + STARDIS_SOLID(floor); + /* roof export */ ERR(stl_export_denoised_geometry(denoiser, data_cad->roof, Scad_force_normals_outward, binary)); + STARDIS_SOLID(roof); + /* cavity export */ ERR(stl_export_denoised_geometry(denoiser, data_cad->cavity, Scad_force_normals_outward, binary)); + STARDIS_FLUID(cavity); + /* connection export */ for(i = 0; i < data_cad->n_connection; i++) { - ERR(stl_export_denoised_geometry(denoiser, data_cad->connection[i], + struct scad_geometry* c = data_cad->connection[i]; + ERR(stl_export_denoised_geometry(denoiser, c, Scad_keep_normals_unchanged, binary)); + + ERR(scad_geometry_get_name(c, &n)); + STARDIS_SFC_2(SFC, n); } /* boundary export */ ERR(stl_export_denoised_geometry(denoiser, data_cad->boundary_wall, Scad_keep_normals_unchanged, binary)); + STARDIS_H_BOUND(boundary_wall); + ERR(stl_export_denoised_geometry(denoiser, data_cad->boundary_roof, Scad_keep_normals_unchanged, binary)); + STARDIS_H_BOUND(boundary_roof); + /* ground/building connection export*/ ERR(stl_export_denoised_geometry(denoiser, data_cad->ground_connection, Scad_keep_normals_unchanged, binary)); + /* No need to describe solid-solid connections until there is a contact + * resistance */ + exit: if(initialized) { darray_double_release(&trg); @@ -827,6 +891,19 @@ error: "Internal error '"__FILE__"': creating STL for building '%s'.\n", str_cget(&data_cad->building->name)); } + /* Reset stardis model file */ + if(model) { + long l; + fsetpos(model, &model_pos); + for(l = 0; l < model_count; l += 40) + fprintf(model, " \n"); + fprintf(model, "# Building '%s' construction cancelled\n", + str_cget(&building->name)); + } + if(vars) { + fprintf(vars, "# Building '%s' construction cancelled\n", + str_cget(&building->name)); + } goto exit; } diff --git a/src/cg_construction_mode_1.c b/src/cg_construction_mode_1.c @@ -27,6 +27,7 @@ #include "cg_construction_mode.h" #include "cg_construction_mode_1.h" #include "cg_vertex_denoiser.h" +#include "cg_stardis_model.h" #include <rsys/rsys.h> #include <rsys/str.h> @@ -244,7 +245,7 @@ build_int_insulation double e_wall = data->wall_thickness; double e_roof = data->roof_thickness; double e_roof_insulation = data->roof_insulation_thickness; - double attic = data->attic_height; + double e_attic = data->attic_height; double e_ext_insulation = data->external_insulation_thickness; double e_int_insulation = data->internal_insulation_thickness; double offset = 0; @@ -280,7 +281,7 @@ build_int_insulation ERR(build_footprint(pg_ext, 0, &footprint_ext)); ERR(scad_cut_geometries(NULL, &footprint_ext, 1, &footprint_int, 1, &footprint)); - d[2] = height - e_roof - attic - e_roof_insulation; + d[2] = height - e_roof - e_attic - e_roof_insulation; ERR(scad_geometry_extrude(footprint, NULL, d, &geom)); if(inter_floor) { @@ -371,7 +372,7 @@ build_roof_insulation double e_wall = data->wall_thickness; double e_insulation = data->external_insulation_thickness; double e_roof = data->roof_thickness; - double attic = data->attic_height; + double e_attic = data->attic_height; double e_roof_insulation = data->roof_insulation_thickness; double offset = 0; double z_insulation = 0; @@ -395,7 +396,7 @@ build_roof_insulation offset, &pg_int)); /*footprint*/ - z_insulation = height - e_roof - attic - e_roof_insulation; + z_insulation = height - e_roof - e_attic - e_roof_insulation; ERR(build_footprint(pg_int, z_insulation, &footprint)); d[2] = e_roof_insulation; @@ -480,7 +481,7 @@ build_inter_floor size_t floor_n = data->inter_floor_count; double e_roof = data->roof_thickness; double e_roof_ins = data->roof_insulation_thickness; - double attic = data->attic_height; + double e_attic = data->attic_height; double e_floor = data->inter_floor_thickness; double e_wall = data->wall_thickness; double e_insulation = data->external_insulation_thickness; @@ -512,7 +513,7 @@ build_inter_floor offset, &pg_int)); ERR(scpr_polygon_get_vertices_count(pg_int, 0, &nverts)); - h_cavity = height - e_roof - attic - e_roof_ins - (double)floor_n*e_floor; + h_cavity = height - e_roof - e_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++) { @@ -809,11 +810,13 @@ build_windows struct scad_geometry* benv = NULL; struct scad_geometry* problem = NULL; size_t floor_n; - double e_roof, e_roof_ins, attic, h_wall, e_floor; + double e_roof, e_roof_ins, e_attic, h_wall, e_floor; struct str name; struct adjoining_data* adj; struct city* city; struct mem_allocator* allocator; + double effective_ratio, total_wall_surface = 0, total_windows_surface = 0; + int meet_effective_ratio; (void)removed_hor; ASSERT(data && data_cad && adjoining_data); @@ -830,9 +833,9 @@ build_windows floor_n = data->inter_floor_count; e_roof = data->roof_thickness; e_roof_ins = data->roof_insulation_thickness; - attic = data->attic_height; + e_attic = data->attic_height; e_floor = data->inter_floor_thickness; - h_wall = (data_cad->building->height- e_roof - attic - e_roof_ins + h_wall = (data_cad->building->total_height- e_roof - e_attic - e_roof_ins - (double)floor_n*e_floor) / (double)(floor_n + 1); d3_splat(scale, sqrt(data->glass_ratio)); @@ -855,7 +858,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++) { - double hsz, mass, center[3]; + double hsz, wall_surface, center[3]; size_t center_n; size_t count; int removed = 0; @@ -873,8 +876,9 @@ build_windows continue; /* keep only vertical face */ } - ERR(scad_geometry_get_mass(list[i], &mass)); - if(mass < CG2_MIN_WINDOWS_WIDTH * h_wall) { + ERR(scad_geometry_get_mass(list[i], &wall_surface)); + total_wall_surface += wall_surface; + if(wall_surface < CG2_MIN_WINDOWS_WIDTH * h_wall) { /* this window would be too small */ ERR(scad_geometry_ref_put(surface)); surface = NULL; @@ -955,13 +959,14 @@ build_windows ERR(darray_geometries_push_back(&hole_array, &hole)); ERR(scad_geometry_ref_put(hole)); hole = NULL; - d3_muld(dir, N, 0.024); + d3_muld(dir, N, CG2_GLAZING_THICKNESS); ERR(str_printf(&name, "glass_%lu", (long unsigned)i)); ERR(scad_geometry_extrude(win_surface, str_cget(&name), dir, &glass)); ERR(darray_geometries_push_back(&glass_array, &glass)); ERR(darray_geometries_push_back(current_cad, &glass)); ERR(scad_geometry_ref_put(glass)); glass = NULL; + total_windows_surface += wall_surface * data->glass_ratio; } ASSERT(hole == NULL); ASSERT(detect == NULL); @@ -1004,6 +1009,12 @@ build_windows logger_print(city->logger, LOG_OUTPUT, "Building '%s' has no window removed (out of %zu).\n", prefix, array_n); } + effective_ratio = total_windows_surface / total_wall_surface; + meet_effective_ratio = data->glass_ratio == 0 + || fabs(effective_ratio - data->glass_ratio) / data->glass_ratio < 0.01; + logger_print(city->logger, (meet_effective_ratio ? LOG_OUTPUT : LOG_WARNING), + "Building '%s' overall glass ratio is %.1f %% (expected is %.1f %%).\n", + prefix, 100 * effective_ratio, 100 * data->glass_ratio); if(array_n > 0) { hole_list = darray_geometries_data_get(&hole_array); @@ -1299,7 +1310,9 @@ build_connection CREATE_CONNECT(attic_cavity, roof,"_C_attic_roof"); /* with roof insulation */ - CREATE_CONNECT(attic_cavity, roof_insulation,"_C_attic_insulation"); + if(data_cad->roof_insulation) { + CREATE_CONNECT(attic_cavity, roof_insulation,"_C_attic_insulation"); + } /* with wall */ CREATE_CONNECT(attic_cavity, wall,"_C_attic_walls"); @@ -1330,7 +1343,7 @@ build_fake_ground struct scad_geometry* footprint = NULL; struct scad_geometry* geom = NULL; - ASSERT(cad && ground ); + ASSERT(cad && ground); darray_geometries_init(cad->building->city->allocator, &array); @@ -1477,6 +1490,25 @@ init_cmode_1 ERR(init_building_base(building, city, parsed_data)); + if(parsed_data->levels_height_count != 0) { + ERR(logger_print(city->logger, LOG_ERROR, + "Building '%s' defines 'levels_height' " + "(feature not available in construction mode 1).\n", + str_cget(&building->name))); + res = RES_BAD_ARG; + goto error; + } + + if(parsed_data->height <= 0) { + ERR(logger_print(city->logger, LOG_ERROR, + "Building '%s' height definition is invalid " + "(construction mode 1 should define a positive height through the 'height' field).\n", + str_cget(&building->name))); + res = RES_BAD_ARG; + goto error; + } + building->total_height = parsed_data->height; + str_init(allocator, &dataset_name); name_initialized = 1; ERR(str_set(&dataset_name, parsed_data->dataset_name)); @@ -1518,7 +1550,7 @@ build_cad_cmode_1 void** cad) { res_T res = RES_OK; - double height = building->height; + double height = building->total_height; double depth = 0; struct dataset_cmode_1* data = (struct dataset_cmode_1 *)building->data; struct data_cad_cmode_1* data_cad = NULL; @@ -1551,8 +1583,8 @@ build_cad_cmode_1 logger = building->city->logger; logger_print(logger, LOG_OUTPUT, - "Building '%s' construction mode 1, with %lu levels.\n", - name, 1 + data->inter_floor_count); + "Building '%s' construction mode 1, dataset '%s', %g m tall.\n", + name, str_cget(&building->dataset_name), building->total_height); data_cad = MEM_CALLOC(allocator, 1, sizeof(struct data_cad_cmode_1)); if(!data_cad) { @@ -1863,16 +1895,39 @@ export_stl_cmode_1 struct scad_geometry** list = NULL; struct mem_allocator* allocator = NULL; struct city* city; + struct building* building; struct vertex_denoiser* denoiser; + FILE* model = NULL; + FILE* vars = NULL; + fpos_t model_pos, vars_pos; + long model_count = 0; + const char* n; + static int fst = 1; if(!cad) { res = RES_BAD_ARG; goto error; } - city = data_cad->building->city; + building = data_cad->building; + city = building->city; allocator = city->allocator; denoiser = city->denoiser; + model = city->stardis_model; + vars = city->set_vars; + + if(!fst) fprintf(model, "\n"); + fst = 0; + fprintf(model, + "# Building '%s' construction mode 1, dataset '%s', %g m tall\n", + str_cget(&building->name), str_cget(&building->dataset_name), + building->total_height); + fgetpos(model, &model_pos); + fprintf(vars, + "\n# Building '%s' construction mode 1, dataset '%s', %g m tall\n", + str_cget(&building->name), str_cget(&building->dataset_name), + building->total_height); + fgetpos(vars, &vars_pos); if(darray_geometries_size_get(&data_cad->adj_walls) == 0) { /* wall export */ @@ -1940,64 +1995,92 @@ export_stl_cmode_1 binary)); } + STARDIS_SOLID(wall); + + if(data_cad->external_insulation) { + STARDIS_SOLID(external_insulation); + } + /* floor export */ ERR(stl_export_denoised_geometry(denoiser, data_cad->floor, Scad_force_normals_outward, binary)); + STARDIS_SOLID(floor); + /* roof export */ ERR(stl_export_denoised_geometry(denoiser, data_cad->roof, Scad_force_normals_outward, binary)); + STARDIS_SOLID(roof); + /* foundation export */ if(data_cad->foundation) { ERR(stl_export_denoised_geometry(denoiser, data_cad->foundation, Scad_force_normals_outward, binary)); + + STARDIS_SOLID(foundation); } /* glass export */ if(data_cad->glazing) { ERR(stl_export_denoised_geometry(denoiser, data_cad->glazing, Scad_force_normals_outward, binary)); + + STARDIS_SOLID(glazing); } /* intermediate floor export*/ if(data_cad->intermediate_floor) { ERR(stl_export_denoised_geometry(denoiser, data_cad->intermediate_floor, Scad_force_normals_outward, binary)); + + STARDIS_SOLID(intermediate_floor); } /* internal insulation export*/ if(data_cad->internal_insulation) { ERR(stl_export_denoised_geometry(denoiser, data_cad->internal_insulation, Scad_force_normals_outward, binary)); + + STARDIS_SOLID(internal_insulation); } /* roof insulation export*/ if(data_cad->roof_insulation) { ERR(stl_export_denoised_geometry(denoiser, data_cad->roof_insulation, Scad_force_normals_outward, binary)); + + STARDIS_SOLID(roof_insulation); } /* floor insulation export*/ if(data_cad->floor_insulation) { ERR(stl_export_denoised_geometry(denoiser, data_cad->floor_insulation, Scad_force_normals_outward, binary)); + + STARDIS_SOLID(floor_insulation); } /* attic cavity export*/ if(data_cad->attic_cavity) { ERR(stl_export_denoised_geometry(denoiser, data_cad->attic_cavity, Scad_force_normals_outward, binary)); + + STARDIS_FLUID(attic_cavity); } /* habitable cavity export*/ ERR(stl_export_denoised_geometry(denoiser, data_cad->habitable_cavity, Scad_force_normals_outward, binary)); + STARDIS_FLUID(habitable_cavity); + /* crawlspace cavity export*/ if(data_cad->crawlspace_cavity) { ERR(stl_export_denoised_geometry(denoiser, data_cad->crawlspace_cavity, Scad_force_normals_outward, binary)); + + STARDIS_FLUID(crawlspace_cavity); } /* boundary export*/ @@ -2005,6 +2088,9 @@ export_stl_cmode_1 struct scad_geometry* b = darray_geometries_data_get(&data_cad->boundary)[i]; ERR(stl_export_denoised_geometry(denoiser, b, Scad_keep_normals_unchanged, binary)); + + ERR(scad_geometry_get_name(b, &n)); + STARDIS_H_BOUND_2(SFC, n); } /* connections export*/ @@ -2012,12 +2098,18 @@ export_stl_cmode_1 struct scad_geometry* c = darray_geometries_data_get(&data_cad->connection)[i]; ERR(stl_export_denoised_geometry(denoiser, c, Scad_keep_normals_unchanged, binary)); + + ERR(scad_geometry_get_name(c, &n)); + STARDIS_SFC_2(SFC, n); } /* ground/building connection export*/ ERR(stl_export_denoised_geometry(denoiser, data_cad->ground_connection, Scad_keep_normals_unchanged, binary)); + /* No need to describe solid-solid connections until there is a contact + * resistance */ + exit: for(j = 0; j < common_n; j++) { if(list[j]) SCAD(geometry_ref_put(list[j])); @@ -2034,6 +2126,19 @@ error: "Internal error '"__FILE__"': creating STL for building '%s'.\n", str_cget(&data_cad->building->name)); } + /* Reset stardis model file */ + if(model) { + long l; + fsetpos(model, &model_pos); + for(l = 0; l < model_count; l += 40) + fprintf(model, " \n"); + fprintf(model, "# Building '%s' construction cancelled\n", + str_cget(&building->name)); + } + if(vars) { + fprintf(vars, "# Building '%s' construction cancelled\n", + str_cget(&building->name)); + } goto exit; } diff --git a/src/cg_construction_mode_2.c b/src/cg_construction_mode_2.c @@ -0,0 +1,2322 @@ +/* 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_types.h" +#include "cg_default.h" +#include "cg_building.h" +#include "cg_catalog.h" +#include "cg_city.h" +#include "cg_city_parsing_schemas.h" +#include "cg_construction_mode.h" +#include "cg_construction_mode_2.h" +#include "cg_vertex_denoiser.h" +#include "cg_stardis_model.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 res_T +build_footprint + (struct scpr_polygon* pg, + const double z, + struct scad_geometry** footprint) +{ + res_T res = RES_OK; + size_t nverts = 0; + + ASSERT(pg && footprint); + + ERR(scpr_polygon_get_vertices_count(pg, 0, &nverts)); + ERR(scad_add_polygon(NULL, get_position_pg, pg, z, nverts, footprint)); + +exit: + return res; +error: + goto exit; +} + +/* Return a polygon with the given offset. + * Get a reference: do put a reference on the result! */ +static res_T +do_offset + (struct building* building, + struct htable_polygons* polygons, + struct scpr_intersector* overlapping_intersector, + int *error_msg_printed, + const double offset, + struct scpr_polygon** p_out) +{ + res_T res = RES_OK; + struct scpr_polygon** ptr_p; + + ASSERT(building && polygons && overlapping_intersector + && error_msg_printed && p_out); + + ptr_p = htable_polygons_find(polygons, &offset); + if(ptr_p) { + *p_out = *ptr_p; + ERR(scpr_polygon_ref_get(*p_out)); + } else { + size_t c; + ERR(scpr_polygon_create_copy(building->city->scpr, building->pg, p_out)); + ERR(scpr_offset_polygon(*p_out, offset, SCPR_JOIN_MITER)); + ERR(scpr_polygon_get_components_count(*p_out, &c)); + if(c != 1) { + ASSERT(offset < 0); + logger_print(building->city->logger, + (building->city->keep_running_on_errors ? LOG_WARNING : LOG_ERROR), + "Building '%s' shape is not compatible with wall thickness.\n", + str_cget(&building->name)); + building->event_flags |= BUILDING_REMOVED; + *error_msg_printed = 1; + res = RES_BAD_ARG; + goto error; + } + ERR(scpr_intersector_register_polygon(overlapping_intersector, *p_out)); + ERR(htable_polygons_set(polygons, &offset, p_out)); + } + +exit: + return res; +error: + if(!ptr_p) SCPR(polygon_ref_put(*p_out)); /* Created here */ + *p_out = NULL; + goto exit; +} + +static res_T +build_floor + (struct building* building, + int *error_msg_printed, + const struct dataset_cmode_2* data, + struct scpr_intersector* overlapping_intersector, + struct htable_polygons* polygons, + struct darray_geometries* current_cad, + struct scad_geometry** floor) +{ + res_T res = RES_OK; + double e_wall = data->wall_thickness; + double e_insulation = data->external_insulation_thickness; + double e_floor = data->floor_thickness; + double offset = 0; + struct scpr_polygon* pg_int = NULL; + struct scad_geometry* footprint = NULL; + double d[3] = {0, 0, 0}; + char* floorname = NULL; + struct str name; + const char* prefix; + + ASSERT(building && error_msg_printed && data && floor); + + prefix = str_cget(&building->name); + str_init(building->city->allocator, &name); + ERR(str_set(&name, prefix)); + ERR(str_append(&name, "_S_floor")); + floorname = str_get(&name); + + offset = -(e_wall + e_insulation); + ERR(do_offset(building, polygons, overlapping_intersector, error_msg_printed, + offset, &pg_int)); + + /*footprint*/ + ERR(build_footprint(pg_int, 0, &footprint)); + + d[2] = -e_floor; + ERR(scad_geometry_extrude(footprint, floorname, d, floor)); + ERR(darray_geometries_push_back(current_cad, floor)); + +exit: + if(footprint) SCAD(geometry_ref_put(footprint)); + str_release(&name); + if(pg_int) SCPR(polygon_ref_put(pg_int)); + return res; +error: + goto exit; +} + +static res_T +build_wall + (struct building* building, + int *error_msg_printed, + const int is_foundation, + const char* suffix, + const double height, + const struct dataset_cmode_2* data, + struct scpr_intersector* overlapping_intersector, + struct htable_polygons* polygons, + struct darray_geometries* current_cad, + struct scad_geometry** wall) +{ + res_T res = RES_OK; + double e_wall = data->wall_thickness; + double e_insulation = data->external_insulation_thickness; + double offset = 0; + struct scpr_polygon* pg_int = NULL; + struct scpr_polygon* pg_ext = NULL; + struct scad_geometry* footprint = NULL; + struct scad_geometry* footprint_int = NULL; + struct scad_geometry* footprint_ext = NULL; + double d[3] = {0, 0, 0}; + char* wallname = NULL; + struct str name; + const char* prefix; + + ASSERT(building && error_msg_printed && data && wall); + + prefix = str_cget(&building->name); + str_init(building->city->allocator, &name); + ERR(str_set(&name, prefix)); + if(suffix) { + ERR(str_append(&name, "_")); + ERR(str_append(&name, suffix)); + } + wallname = str_get(&name); + + if(is_foundation) { + offset = -(e_insulation + 0.1*e_wall); + } else { + offset = -e_insulation; + } + ERR(do_offset(building, polygons, overlapping_intersector, error_msg_printed, + offset, &pg_ext)); + + offset = -(e_wall + e_insulation); + ERR(do_offset(building, polygons, overlapping_intersector, error_msg_printed, + offset, &pg_int)); + + /* wall footprint*/ + ERR(build_footprint(pg_int, 0, &footprint_int)); + ERR(build_footprint(pg_ext, 0, &footprint_ext)); + ERR(scad_cut_geometries(NULL, &footprint_ext, 1, &footprint_int, 1, &footprint)); + + d[2] = height; + ERR(scad_geometry_extrude(footprint, wallname, d, wall)); + ERR(darray_geometries_push_back(current_cad, wall)); + +exit: + if(footprint) SCAD(geometry_ref_put(footprint)); + if(footprint_int) SCAD(geometry_ref_put(footprint_int)); + if(footprint_ext) SCAD(geometry_ref_put(footprint_ext)); + str_release(&name); + if(pg_ext) SCPR(polygon_ref_put(pg_ext)); + if(pg_int) SCPR(polygon_ref_put(pg_int)); + return res; +error: + goto exit; +} + +static res_T +build_int_insulation + (struct building* building, + int *error_msg_printed, + const double height, + const struct dataset_cmode_2* data, + struct scad_geometry* inter_floor, + struct scpr_intersector* overlapping_intersector, + struct htable_polygons* polygons, + struct darray_geometries* current_cad, + struct scad_geometry** insulation) +{ + res_T res = RES_OK; + double e_wall = data->wall_thickness; + double e_roof = data->roof_thickness; + double e_roof_insulation = data->roof_insulation_thickness; + size_t levels_n = data->has_attic ? + darray_double_size_get(&building->levels_height) - 1 : + darray_double_size_get(&building->levels_height); + const double* levels = darray_double_cdata_get(&building->levels_height); + double e_attic = data->has_attic ? levels[levels_n] : 0; + double e_ext_insulation = data->external_insulation_thickness; + double e_int_insulation = data->internal_insulation_thickness; + double offset = 0; + struct scpr_polygon* pg_int = NULL; + struct scpr_polygon* pg_ext = NULL; + struct scad_geometry* footprint = NULL; + struct scad_geometry* footprint_int = NULL; + struct scad_geometry* footprint_ext = NULL; + struct scad_geometry* geom = NULL; + double d[3] = {0, 0, 0}; + char* insulationname = NULL; + struct str name; + const char* prefix; + + ASSERT(building && error_msg_printed && data && insulation); + + prefix = str_cget(&building->name); + str_init(building->city->allocator, &name); + ERR(str_set(&name, prefix)); + ERR(str_append(&name, "_S_internal_insulation")); + insulationname = str_get(&name); + + offset = -(e_ext_insulation + e_wall); + ERR(do_offset(building, polygons, overlapping_intersector, error_msg_printed, + offset, &pg_ext)); + + offset = -(e_ext_insulation + e_wall + e_int_insulation); + ERR(do_offset(building, polygons, overlapping_intersector, error_msg_printed, + offset, &pg_int)); + + /* insulation footprint */ + ERR(build_footprint(pg_int, 0, &footprint_int)); + ERR(build_footprint(pg_ext, 0, &footprint_ext)); + ERR(scad_cut_geometries(NULL, &footprint_ext, 1, &footprint_int, 1, &footprint)); + + d[2] = height - e_roof - e_attic - e_roof_insulation; + ERR(scad_geometry_extrude(footprint, NULL, d, &geom)); + + if(inter_floor) { + ERR(scad_cut_geometries(insulationname, &geom, 1, &inter_floor, 1, insulation)); + } else { + ERR(scad_geometry_copy(geom, insulationname, insulation)); + } + ERR(darray_geometries_push_back(current_cad, insulation)); + +exit: + if(footprint) SCAD(geometry_ref_put(footprint)); + if(footprint_int) SCAD(geometry_ref_put(footprint_int)); + if(footprint_ext) SCAD(geometry_ref_put(footprint_ext)); + if(geom) SCAD(geometry_ref_put(geom)); + str_release(&name); + if(pg_ext) SCPR(polygon_ref_put(pg_ext)); + if(pg_int) SCPR(polygon_ref_put(pg_int)); + return res; +error: + goto exit; +} + +static res_T +build_roof + (struct building* building, + int *error_msg_printed, + const double height, + const struct dataset_cmode_2* data, + struct scpr_intersector* overlapping_intersector, + struct htable_polygons* polygons, + struct darray_geometries* current_cad, + struct scad_geometry** roof) +{ + res_T res = RES_OK; + double e_wall = data->wall_thickness; + double e_insulation = data->external_insulation_thickness; + double e_roof = data->roof_thickness; + double offset = 0; + double z_roof = 0; + struct scpr_polygon* pg_int = NULL; + struct scad_geometry* footprint = NULL; + double d[3] = {0, 0, 0}; + char* roofname = NULL; + struct str name; + const char* prefix; + + ASSERT(building && error_msg_printed && data && roof); + + prefix = str_cget(&building->name); + str_init(building->city->allocator, &name); + ERR(str_set(&name, prefix)); + ERR(str_append(&name, "_S_roof")); + roofname = str_get(&name); + + offset = -(e_wall + e_insulation); + ERR(do_offset(building, polygons, overlapping_intersector, error_msg_printed, + offset, &pg_int)); + + /*footprint*/ + z_roof = height - e_roof; + ERR(build_footprint(pg_int, z_roof, &footprint)); + + d[2] = e_roof; + ERR(scad_geometry_extrude(footprint, roofname, d, roof)); + ERR(darray_geometries_push_back(current_cad, roof)); + +exit: + if(footprint) SCAD(geometry_ref_put(footprint)); + str_release(&name); + if(pg_int) SCPR(polygon_ref_put(pg_int)); + return res; +error: + goto exit; +} + +static res_T +build_roof_insulation + (struct building* building, + int *error_msg_printed, + const double height, + const struct dataset_cmode_2* data, + struct scpr_intersector* overlapping_intersector, + struct htable_polygons* polygons, + struct darray_geometries* current_cad, + struct scad_geometry** insulation) +{ + res_T res = RES_OK; + double e_wall = data->wall_thickness; + double e_insulation = data->external_insulation_thickness; + double e_roof = data->roof_thickness; + size_t levels_n = data->has_attic ? + darray_double_size_get(&building->levels_height) - 1 : + darray_double_size_get(&building->levels_height); + const double* levels = darray_double_cdata_get(&building->levels_height); + double e_attic = data->has_attic ? levels[levels_n] : 0; + double e_roof_insulation = data->roof_insulation_thickness; + double offset = 0; + double z_insulation = 0; + struct scpr_polygon* pg_int = NULL; + struct scad_geometry* footprint = NULL; + double d[3] = {0, 0, 0}; + char* insulationname = NULL; + struct str name; + const char* prefix; + + ASSERT(building && error_msg_printed && data && insulation); + + prefix = str_cget(&building->name); + str_init(building->city->allocator, &name); + ERR(str_set(&name, prefix)); + ERR(str_append(&name, "_S_roof_insulation")); + insulationname = str_get(&name); + + offset = -(e_wall + e_insulation); + ERR(do_offset(building, polygons, overlapping_intersector, error_msg_printed, + offset, &pg_int)); + + /*footprint*/ + z_insulation = height - e_roof - e_attic - e_roof_insulation; + ERR(build_footprint(pg_int, z_insulation, &footprint)); + + d[2] = e_roof_insulation; + ERR(scad_geometry_extrude(footprint, insulationname, d, insulation)); + ERR(darray_geometries_push_back(current_cad, insulation)); + +exit: + if(footprint) SCAD(geometry_ref_put(footprint)); + str_release(&name); + if(pg_int) SCPR(polygon_ref_put(pg_int)); + return res; +error: + goto exit; +} + +static res_T +build_floor_insulation + (struct building* building, + int *error_msg_printed, + const struct dataset_cmode_2* data, + struct scpr_intersector* overlapping_intersector, + struct htable_polygons* polygons, + struct darray_geometries* current_cad, + struct scad_geometry** insulation) +{ + res_T res = RES_OK; + double e_wall = data->wall_thickness; + double e_insulation = data->external_insulation_thickness; + double e_floor = data->floor_thickness; + double e_floor_insulation = data->floor_insulation_thickness; + double offset = 0; + double z_insulation = 0; + struct scpr_polygon* pg_int = NULL; + struct scad_geometry* footprint = NULL; + double d[3] = {0, 0, 0}; + char* insulationname = NULL; + struct str name; + const char* prefix; + + ASSERT(building && error_msg_printed && data && insulation); + + prefix = str_cget(&building->name); + str_init(building->city->allocator, &name); + ERR(str_set(&name, prefix)); + ERR(str_append(&name, "_S_floor_insulation")); + insulationname = str_get(&name); + + offset = -(e_wall + e_insulation); + ERR(do_offset(building, polygons, overlapping_intersector, error_msg_printed, + offset, &pg_int)); + + /*footprint*/ + z_insulation = - e_floor - e_floor_insulation; + ERR(build_footprint(pg_int, z_insulation, &footprint)); + + d[2] = e_floor_insulation; + ERR(scad_geometry_extrude(footprint, insulationname, d, insulation)); + ERR(darray_geometries_push_back(current_cad, insulation)); + +exit: + if(footprint) SCAD(geometry_ref_put(footprint)); + str_release(&name); + if(pg_int) SCPR(polygon_ref_put(pg_int)); + return res; +error: + goto exit; +} + +static res_T +build_inter_floor + (struct building* building, + int *error_msg_printed, + const struct dataset_cmode_2* data, + struct scpr_intersector* overlapping_intersector, + struct htable_polygons* polygons, + struct darray_geometries* current_cad, + struct scad_geometry** inter_floor) +{ + res_T res = RES_OK; + size_t i = 0; + size_t levels_n = data->has_attic ? + darray_double_size_get(&building->levels_height) - 1 : + darray_double_size_get(&building->levels_height); + const double* levels = darray_double_cdata_get(&building->levels_height); + double e_floor = data->inter_floor_thickness; + double e_wall = data->wall_thickness; + double e_insulation = data->external_insulation_thickness; + double offset = 0; + double z_floor = 0; + struct scpr_polygon* pg_int = NULL; + size_t nverts = 0; + struct scad_geometry** floor_list = NULL; + struct darray_geometries floor_array; + struct scad_geometry *footprint = NULL, *floor = NULL; + double d[3] = {0, 0, 0}; + char* floorname = NULL; + struct str name; + const char* prefix; + + ASSERT(building && error_msg_printed && data && inter_floor); + + darray_geometries_init(building->city->allocator, &floor_array); + + prefix = str_cget(&building->name); + str_init(building->city->allocator, &name); + ERR(str_set(&name, prefix)); + ERR(str_append(&name, "_S_intermediate_floors")); + floorname = str_get(&name); + + offset = -(e_wall + e_insulation); + ERR(do_offset(building, polygons, overlapping_intersector, error_msg_printed, + offset, &pg_int)); + ERR(scpr_polygon_get_vertices_count(pg_int, 0, &nverts)); + + z_floor = levels[0] - e_floor; + d[2] = e_floor; + for(i = 1; i < levels_n; i++) { + ERR(scad_add_polygon(NULL, get_position_pg, pg_int, z_floor, nverts, + &footprint)); + ERR(scad_geometry_extrude(footprint, NULL, d, &floor)); + ERR(scad_geometry_ref_put(footprint)); + footprint = NULL; /* Avoid double free */ + ERR(darray_geometries_push_back(&floor_array, &floor)); + ERR(scad_geometry_ref_put(floor)); + floor = NULL; /* Avoid double free */ + z_floor += levels[i]; + } + ASSERT(darray_geometries_size_get(&floor_array) == levels_n - 1); + + floor_list = darray_geometries_data_get(&floor_array); + ERR(scad_fuse_geometries(floorname, floor_list, levels_n - 1, + floor_list, levels_n - 1, inter_floor)); + ERR(darray_geometries_push_back(current_cad, inter_floor)); + +exit: + str_release(&name); + if(footprint) SCAD(geometry_ref_put(footprint)); + if(floor) SCAD(geometry_ref_put(floor)); + if(floor_list) darray_geometries_release(&floor_array); + if(pg_int) SCPR(polygon_ref_put(pg_int)); + return res; +error: + goto exit; +} + +static res_T +build_ext_insulation + (struct building* building, + int *error_msg_printed, + const double height, + const struct dataset_cmode_2* data, + struct scpr_intersector* overlapping_intersector, + struct htable_polygons* polygons, + struct darray_geometries* current_cad, + struct scad_geometry** insulation) +{ + res_T res = RES_OK; + double e_insulation = data->external_insulation_thickness; + double offset = 0; + struct scpr_polygon* pg_int = NULL; + struct scpr_polygon* pg_ext = NULL; + struct scpr_polygon** ptr_p; + struct scad_geometry* footprint = NULL; + struct scad_geometry* footprint_int = NULL; + struct scad_geometry* footprint_ext = NULL; + double d[3] = {0, 0, 0}; + char* insulationname = NULL; + struct str name; + const char* prefix; + + ASSERT(building && data && polygons && insulation); + + prefix = str_cget(&building->name); + str_init(building->city->allocator, &name); + ERR(str_set(&name, prefix)); + ERR(str_append(&name, "_S_external_insulation")); + insulationname = str_get(&name); + + offset = -e_insulation; + ERR(do_offset(building, polygons, overlapping_intersector, error_msg_printed, + offset, &pg_int)); + + offset = 0; + ptr_p = htable_polygons_find(polygons, &offset); + ASSERT(ptr_p); /* Offset 0 is the first to be inserted in polygons */ + pg_ext = *ptr_p; + + /*insulation footprint*/ + ERR(build_footprint(pg_int, 0, &footprint_int)); + ERR(build_footprint(pg_ext, 0, &footprint_ext)); + ERR(scad_cut_geometries(NULL, &footprint_ext, 1, &footprint_int, 1, &footprint)); + + d[2] = height; + ERR(scad_geometry_extrude(footprint, insulationname, d, insulation)); + ERR(darray_geometries_push_back(current_cad, insulation)); + +exit: + if(footprint) SCAD(geometry_ref_put(footprint)); + if(footprint_int) SCAD(geometry_ref_put(footprint_int)); + if(footprint_ext) SCAD(geometry_ref_put(footprint_ext)); + str_release(&name); + if(pg_int) SCPR(polygon_ref_put(pg_int)); + return res; +error: + goto exit; +} + +static res_T +build_crawlspace + (struct building* building, + int *error_msg_printed, + const struct dataset_cmode_2* data, + struct scpr_intersector* overlapping_intersector, + struct htable_polygons* polygons, + struct darray_geometries* current_cad, + struct scad_geometry** crawlspace) +{ + res_T res = RES_OK; + double e_wall = data->wall_thickness; + double e_insulation = data->external_insulation_thickness; + double e_crawl = data->crawl_height; + double e_floor = data->floor_thickness; + double e_floor_insulation = data->floor_insulation_thickness; + double offset = 0; + double z_crawl= 0; + struct scpr_polygon* pg_int = NULL; + struct scad_geometry* footprint = NULL; + double d[3] = {0, 0, 0}; + char* crawlname = NULL; + struct str name; + const char* prefix; + + ASSERT(building && error_msg_printed && data && crawlspace); + + prefix = str_cget(&building->name); + str_init(building->city->allocator, &name); + ERR(str_set(&name, prefix)); + ERR(str_append(&name, "_F_crawlspace")); + crawlname = str_get(&name); + + offset = -(e_wall + e_insulation); + ERR(do_offset(building, polygons, overlapping_intersector, error_msg_printed, + offset, &pg_int)); + + /*footprint*/ + z_crawl = - e_floor - e_floor_insulation - e_crawl; + ERR(build_footprint(pg_int, z_crawl, &footprint)); + + d[2] = e_crawl; + ERR(scad_geometry_extrude(footprint, crawlname, d, crawlspace)); + ERR(darray_geometries_push_back(current_cad, crawlspace)); + +exit: + if(footprint) SCAD(geometry_ref_put(footprint)); + str_release(&name); + if(pg_int) SCPR(polygon_ref_put(pg_int)); + return res; +error: + goto exit; +} + +static res_T +build_habitable + (struct building* building, + int *error_msg_printed, + const double height, + const struct dataset_cmode_2* data, + struct scad_geometry* floor, + struct scpr_intersector* overlapping_intersector, + struct htable_polygons* polygons, + struct darray_geometries* current_cad, + struct scad_geometry** cavity) +{ + res_T res = RES_OK; + double e_wall = data->wall_thickness; + double e_ext_insulation = data->external_insulation_thickness; + double e_int_insulation = data->internal_insulation_thickness; + double e_roof = data->roof_thickness; + double e_roof_insulation = data->roof_insulation_thickness; + size_t levels_n = data->has_attic ? + darray_double_size_get(&building->levels_height) - 1 : + darray_double_size_get(&building->levels_height); + const double* levels = darray_double_cdata_get(&building->levels_height); + double e_attic = data->has_attic ? levels[levels_n] : 0; + double offset = 0; + struct scpr_polygon* pg_int = NULL; + struct scad_geometry* footprint = NULL; + struct scad_geometry* geom = NULL; + double d[3] = {0, 0, 0}; + char* cavityname = NULL; + struct str name; + const char* prefix; + + ASSERT(building && error_msg_printed && data && cavity); + + prefix = str_cget(&building->name); + str_init(building->city->allocator, &name); + ERR(str_set(&name, prefix)); + ERR(str_append(&name, "_F_levels")); + cavityname = str_get(&name); + + offset = -(e_wall + e_ext_insulation + e_int_insulation); + ERR(do_offset(building, polygons, overlapping_intersector, error_msg_printed, + offset, &pg_int)); + ERR(build_footprint(pg_int, 0, &footprint)); + + d[2] = height - e_roof - e_attic - e_roof_insulation; + ERR(scad_geometry_extrude(footprint, NULL, d, &geom)); + if(floor) { + ERR(scad_cut_geometries(cavityname, &geom, 1, &floor, 1, cavity)); + } else { + ERR(scad_geometry_copy(geom, cavityname, cavity)); + } + ERR(darray_geometries_push_back(current_cad, cavity)); + +exit: + if(footprint) SCAD(geometry_ref_put(footprint)); + if(geom) SCAD(geometry_ref_put(geom)); + str_release(&name); + if(pg_int) SCPR(polygon_ref_put(pg_int)); + return res; +error: + goto exit; +} + +static res_T +build_attic + (struct building* building, + int *error_msg_printed, + const double height, + const struct dataset_cmode_2* data, + struct scpr_intersector* overlapping_intersector, + struct htable_polygons* polygons, + struct darray_geometries* current_cad, + struct scad_geometry** attic) +{ + res_T res = RES_OK; + double e_wall = data->wall_thickness; + double e_insulation = data->external_insulation_thickness; + double e_roof = data->roof_thickness; + size_t levels_n = data->has_attic ? + darray_double_size_get(&building->levels_height) - 1 : + darray_double_size_get(&building->levels_height); + const double* levels = darray_double_cdata_get(&building->levels_height); + double e_attic = data->has_attic ? levels[levels_n] : 0; + double offset = 0; + double z_attic = 0; + struct scpr_polygon* pg_int = NULL; + struct scad_geometry* footprint = NULL; + double d[3] = {0, 0, 0}; + char* atticname = NULL; + struct str name; + const char* prefix; + + ASSERT(building && error_msg_printed && data && attic); + + prefix = str_cget(&building->name); + str_init(building->city->allocator, &name); + ERR(str_set(&name, prefix)); + ERR(str_append(&name, "_F_attic")); + atticname = str_get(&name); + + offset = -(e_wall + e_insulation); + ERR(do_offset(building, polygons, overlapping_intersector, error_msg_printed, + offset, &pg_int)); + + /*footprint*/ + z_attic = height - e_roof - e_attic; + ERR(build_footprint(pg_int, z_attic, &footprint)); + + d[2] = e_attic; + ERR(scad_geometry_extrude(footprint, atticname, d, attic)); + ERR(darray_geometries_push_back(current_cad, attic)); + +exit: + if(footprint) SCAD(geometry_ref_put(footprint)); + str_release(&name); + if(pg_int) SCPR(polygon_ref_put(pg_int)); + return res; +error: + goto exit; +} + +static res_T +build_windows + (const struct dataset_cmode_2* data, + struct data_cad_cmode_2* data_cad, + struct darray_geometries* current_cad, + struct darray_adjoining_data* adjoining_data) +{ + res_T res = RES_OK; + size_t i, j, adjoining_n, removed_count; + size_t removed_windows_adj = 0, removed_windows_self = 0; + size_t removed_wall_sz = 0, missed_windows_ratio = 0; + size_t list_n = 0, array_n; + const char* prefix; + double N[3], dir[3], scale[3]; + double effective_ratio, total_wall_surface = 0, total_windows_surface = 0; + int meet_effective_ratio; + struct scad_geometry* surface = NULL; + struct scad_geometry* win_surface = NULL; + struct scad_geometry* surface_ = NULL; + struct scad_geometry* scaled = NULL; + struct scad_geometry* scaled_ = NULL; + struct scad_geometry* hole = NULL; + struct scad_geometry* detect = NULL; + struct scad_geometry** hole_list = NULL; + struct darray_geometries hole_array; + struct scad_geometry* geom = NULL; + struct scad_geometry* hole_adjoining_intersect = NULL; + struct scad_geometry* bcavity = NULL; + struct scad_geometry** list = NULL; + struct scad_geometry** adj_list = NULL; + struct scad_geometry* glass = NULL; + struct scad_geometry** glass_list = NULL; + struct darray_geometries glass_array; + struct scad_geometry* env = NULL; + struct scad_geometry* benv = NULL; + struct scad_geometry* problem = NULL; + double wall_height; + struct str name; + struct adjoining_data* adj; + struct city* city; + struct mem_allocator* allocator; + + ASSERT(data && data_cad && adjoining_data); + + city = data_cad->building->city; + allocator = city->allocator; + adjoining_n = darray_adjoining_data_size_get(adjoining_data); + adj = darray_adjoining_data_data_get(adjoining_data); + darray_geometries_init(allocator, &hole_array); + darray_geometries_init(allocator, &glass_array); + str_init(allocator, &name); + + /* windows are build from the vertical faces of habitable cavities */ + ERR(scad_geometry_boundary("cavity_boundary", &data_cad->habitable_cavity, 1, + &bcavity)); + ERR(scad_geometry_explode(bcavity, "cavity_elt", &list, &list_n)); + ERR(scad_geometry_ref_put(bcavity)); + bcavity = NULL; + + /* The boundary of the envelop of the building. + * To be used for windows validation */ + ERR(build_envelop(data_cad->building, &env)); + ERR(scad_geometry_boundary("envelop_boundary", &env, 1, &benv)); + ERR(scad_geometry_ref_put(env)); + env = NULL; + + ERR(darray_geometries_reserve(&hole_array, list_n)); + ERR(darray_geometries_reserve(&glass_array, list_n)); + for(i = 0; i < list_n; i++) { + double hsz, wall_surface, wall_width, center[3]; + size_t center_n, count, n; + int removed_wall = 0; + double mi[3], ma[3]; + + ERR(scad_geometry_get_count(list[i], &center_n)); + ASSERT(center_n == 1); + ERR(scad_geometry_get_centerofmass(list[i], center)); + ERR(str_printf(&name, "surface_%lu", (long unsigned)i)); + ERR(scad_geometry_normal(list[i], center, N, str_cget(&name), &surface)); + + if(N[2] != 0) { + /* keep only vertical face */ + removed_wall = 1; + } + + if(!removed_wall) { + double window_height, expected_total_width_from_ratio; + double wc, best_wc, best_wc_score, best_wc_spacing, best_wc_width; + d3_normalize(N, N); + ERR(scad_geometry_get_mass(list[i], &wall_surface)); + total_wall_surface += wall_surface; + ERR(scad_geometry_get_bounding_box(list[i], mi, ma)); + wall_height = ma[2] - mi[2]; + wall_width = wall_surface / wall_height; + /* The basic result is: no window */ + best_wc = 0; + best_wc_score = 0; + best_wc_spacing = wall_width; + best_wc_width = 0; + /* search for the count, width, spacing that best meet expectations */ + window_height = data->windows_height_ratio * wall_height; + expected_total_width_from_ratio = wall_width * data->glass_ratio; + wc = 1; + while(wall_width >= + wc * data->windows_min_width + (wc + 1) * data->windows_min_spacing) + { + double score; + double max_achievable_width = MMIN(data->windows_max_width, + (wall_width - (wc + 1) * data->windows_min_spacing) / wc); + if(max_achievable_width * wc >= expected_total_width_from_ratio + && data->windows_min_width * wc <= expected_total_width_from_ratio) + { + /* Expected ratio is achievable! */ + score = 1; + if(score > best_wc_score) { + best_wc = wc; + best_wc_score = score; + best_wc_width = expected_total_width_from_ratio / wc; + best_wc_spacing = (wall_width - wc * best_wc_width) / (wc + 1); + } + } else { + /* Compute the width that is closest to expectations */ + double best_width_for_wc = + (max_achievable_width * wc < expected_total_width_from_ratio) + ? max_achievable_width : data->windows_min_width; + score = 1 / (1 + fabs(wc * best_width_for_wc - expected_total_width_from_ratio)); + ASSERT(score < 1); + if(score > best_wc_score) { + best_wc = wc; + best_wc_score = score; + best_wc_width = best_width_for_wc; + best_wc_spacing = (wall_width - wc * best_wc_width) / (wc + 1); + } + } + /* Try with 1 more window */ + wc++; + } + if(best_wc_score == 0) { + /* No room for a single minimal window */ + removed_wall = 1; + removed_wall_sz++; + } + else if(best_wc_score < 1) { + missed_windows_ratio++; + } + + if(!removed_wall) { + double sw, sh; + sw = MMIN(1, 1.01 * best_wc_width / wall_width); + sh = MMIN(1, 1.01 * window_height / wall_height); + d3(scale, sw, sw, sh); + ERR(scad_geometry_dilate(surface, center, scale, "scaled_", &scaled_)); + sw = best_wc_width / wall_width; + sh = window_height / wall_height; + d3(scale, sw, sw, sh); + ERR(scad_geometry_dilate(surface, center, scale, "scaled", &scaled)); + + /* Try to create the planed windows */ + for(n = 0; n < (size_t)best_wc; n++) { + int removed_windows = 0; + double dxdydz[3], offset; + /* Compute windows positioning */ + offset = - 0.5 * (wall_width - best_wc_width) /* to the left border */ + + best_wc_spacing + (double)n * (best_wc_spacing + best_wc_width); + dxdydz[0] = -N[1] * offset; + dxdydz[1] = N[0] * offset; + /* Z positioning (centered) */ + dxdydz[2] = 0; + ERR(str_printf(&name, "surface+_%lu_w%lu", + (long unsigned)i, (long unsigned)n)); + /* surface_ is used to check for neighbor compatibility with a slitghly + * bigger size than the actual window */ + ERR(scad_geometry_translate(scaled_, dxdydz, str_cget(&name), &surface_)); + + if(adjoining_n) { + /* Use the same distance used in early stages for close neighbor detection */ + hsz = CG2_CLOSE_NEIGHBOR_DISTANCE + data->wall_thickness + + data->internal_insulation_thickness + data->external_insulation_thickness; + d3_muld(dir, N, hsz); + ERR(str_printf(&name, "detect_adj_%lu_w%lu", + (long unsigned)i, (long unsigned)n)); + ERR(scad_geometry_extrude(surface_, str_cget(&name), dir, &detect)); + + /* Check if detect intersects adjoining envelops */ + /* Push only if don't intersect */ + adj_list = MEM_REALLOC(allocator, adj_list, + adjoining_n * sizeof(struct scad_geometry*)); + for(j = 0; j < adjoining_n; j++) adj_list[j] = adj[j].envelop; + + ERR(str_printf(&name, "adj_intersect_%lu", (long unsigned)i)); + ERR(scad_intersect_geometries(str_cget(&name), &detect, 1, adj_list, + adjoining_n, &hole_adjoining_intersect)); + ERR(scad_geometry_get_count(hole_adjoining_intersect, &count)); + if(count) { + removed_windows = 1; + removed_windows_adj++; + } + ERR(scad_geometry_ref_put(hole_adjoining_intersect)); + hole_adjoining_intersect = NULL; + ERR(scad_geometry_ref_put(detect)); + detect = NULL; + } + + /* Check if the window intersects an unexpected wall of the building: + * - the window is too large wrt of external size of the wall (the + * window's size is a % of the internal size of the wall that can be + * larger than the external size due to corners). + * - another wall facing the current one at a too close distance (can be + * the prev/next with sharp angle, or not), + * - a tiny unwanted wall created by noise at the polygon level (mainly + * due to the offseting algorithm). */ + if(!removed_windows) { + /* Use smaller distance than the one used for neighbor detection */ + hsz = 0.1 + data->wall_thickness + data->internal_insulation_thickness + + data->external_insulation_thickness; + d3_muld(dir, N, hsz); + ERR(str_printf(&name, "detect_self_%lu", (long unsigned)i)); + ERR(scad_geometry_extrude(surface_, str_cget(&name), dir, &detect)); + /* Compute intersection between detect and envelop: the number of + * components is expected to be 1, or the window is better removed */ + ERR(str_printf(&name, "self_intersect_%lu", (long unsigned)i)); + ERR(scad_intersect_geometries(str_cget(&name), &benv, 1, &detect, 1, + &problem)); + ERR(scad_geometry_get_count(problem, &count)); + if(count != 1) { + removed_windows = 1; + removed_windows_self++; + } + ERR(scad_geometry_ref_put(detect)); + detect = NULL; + ERR(scad_geometry_ref_put(problem)); + problem = NULL; + ERR(scad_geometry_ref_put(surface_)); + surface_ = NULL; + } + + if(!removed_windows) { + ERR(scad_geometry_translate(scaled, dxdydz, "win_surface", &win_surface)); + ERR(str_printf(&name, "hole_%lu_w%lu", (long unsigned)i, (long unsigned)n)); + ERR(scad_geometry_extrude(win_surface, str_cget(&name), dir, &hole)); + ERR(darray_geometries_push_back(&hole_array, &hole)); + d3_muld(dir, N, CG2_GLAZING_THICKNESS); + ERR(str_printf(&name, "glass_%lu_w%lu", (long unsigned)i, (long unsigned)n)); + ERR(scad_geometry_extrude(win_surface, str_cget(&name), dir, &glass)); + ERR(darray_geometries_push_back(&glass_array, &glass)); + ERR(darray_geometries_push_back(current_cad, &glass)); + ERR(scad_geometry_ref_put(hole)); + hole = NULL; + ERR(scad_geometry_ref_put(glass)); + glass = NULL; + ERR(scad_geometry_ref_put(win_surface)); + win_surface = NULL; + total_windows_surface += best_wc_width * window_height; + } + } + } + if(scaled) { + ERR(scad_geometry_ref_put(scaled)); + scaled = NULL; + } + if(scaled_) { + ERR(scad_geometry_ref_put(scaled_)); + scaled_ = NULL; + } + } + if(hole) { + ERR(scad_geometry_ref_put(hole)); + hole = NULL; + } + if(detect) { + ERR(scad_geometry_ref_put(detect)); + detect = NULL; + } + if(win_surface) { + ERR(scad_geometry_ref_put(win_surface)); + win_surface = NULL; + } + if(surface) { + ERR(scad_geometry_ref_put(surface)); + surface = NULL; + } + if(scaled) { + ERR(scad_geometry_ref_put(scaled)); + scaled = NULL; + } + if(scaled_) { + ERR(scad_geometry_ref_put(scaled_)); + scaled_ = NULL; + } + if(surface_) { + ERR(scad_geometry_ref_put(surface_)); + surface_ = NULL; + } + } + array_n = darray_geometries_size_get(&hole_array); + ASSERT(array_n == darray_geometries_size_get(&glass_array)); + + prefix = str_cget(&data_cad->building->name); + if(removed_wall_sz != 0) { + logger_print(city->logger, LOG_WARNING, + "Building '%s' has %zu walls with no windows due to wall size.\n", + prefix, removed_wall_sz); + } + if(missed_windows_ratio != 0) { + logger_print(city->logger, LOG_WARNING, + "Building '%s' has %zu walls with windows, but missing glass ratio target.\n", + prefix, missed_windows_ratio); + } + removed_count = removed_windows_adj + removed_windows_self; + if(removed_count != 0) { + logger_print(city->logger, LOG_WARNING, + "Building '%s' has %zu/%zu windows removed on walls with windows:\n", + prefix, removed_count, removed_count + array_n); + if(removed_windows_adj != 0) { + logger_print(city->logger, LOG_WARNING, + "- %zu windows removed due to close neighbors.\n", removed_windows_adj); + } + if(removed_windows_self != 0) { + logger_print(city->logger, LOG_WARNING, + "- %zu windows removed due to self conflicts.\n", removed_windows_self); + } + } else { + logger_print(city->logger, LOG_OUTPUT, + "Building '%s' has no window removed (out of %zu).\n", prefix, array_n); + } + effective_ratio = total_windows_surface / total_wall_surface; + meet_effective_ratio = data->glass_ratio == 0 + || fabs(effective_ratio - data->glass_ratio) / data->glass_ratio < 0.01; + logger_print(city->logger, (meet_effective_ratio ? LOG_OUTPUT : LOG_WARNING), + "Building '%s' overall glass ratio is %.1f %% (expected is %.1f %%).\n", + prefix, 100 * effective_ratio, 100 * data->glass_ratio); + + if(array_n > 0) { + hole_list = darray_geometries_data_get(&hole_array); + glass_list = darray_geometries_data_get(&glass_array); + + /* wall perforation */ + ERR(scad_cut_geometries(NULL, &data_cad->wall, 1, hole_list, array_n, &geom)); + ERR(scad_geometries_swap(&data_cad->wall, &geom, 1, Scad_swap_geometry)); + ERR(scad_geometry_ref_put(geom)); + geom = NULL; + + /* internal insulation perforation */ + 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, + Scad_swap_geometry)); + ERR(scad_geometry_ref_put(geom)); + geom = NULL; + } + + /* external insulation perforation */ + 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, + Scad_swap_geometry)); + ERR(scad_geometry_ref_put(geom)); + geom = NULL; + } + + /* build glass */ + ERR(str_set(&name, prefix)); + ERR(str_append(&name, "_S_glazing")); + ERR(scad_fuse_geometries(str_cget(&name), glass_list, array_n, + glass_list, array_n, &data_cad->glazing)); + } + +exit: + for(i = 0 ; i < list_n; i++) SCAD(geometry_ref_put(list[i])); + darray_geometries_release(&hole_array); + darray_geometries_release(&glass_array); + if(env) SCAD(geometry_ref_put(env)); + if(benv) SCAD(geometry_ref_put(benv)); + if(surface) SCAD(geometry_ref_put(surface)); + if(win_surface) SCAD(geometry_ref_put(win_surface)); + if(problem) SCAD(geometry_ref_put(problem)); + if(hole) SCAD(geometry_ref_put(hole)); + if(detect) SCAD(geometry_ref_put(detect)); + if(surface_) SCAD(geometry_ref_put(surface_)); + if(glass) SCAD(geometry_ref_put(glass)); + if(geom) SCAD(geometry_ref_put(geom)); + if(bcavity) SCAD(geometry_ref_put(bcavity)); + if(hole_adjoining_intersect) SCAD(geometry_ref_put(hole_adjoining_intersect)); + MEM_RM(allocator, list); + MEM_RM(allocator, adj_list); + str_release(&name); + return res; +error: + goto exit; +} + +static res_T +build_boundary + (struct data_cad_cmode_2* data_cad, + struct darray_adjoining_data* adjoining_data, + struct darray_geometries* boundary) +{ + res_T res = RES_OK; + struct darray_geometries array; + struct scad_geometry** list = NULL; + struct scad_geometry* bound = NULL; + char* boundaryname = NULL; + struct str name; + struct adjoining_data* adj; + size_t adjoining_n, i = 0, count = 0; + const char* prefix; + + ASSERT(data_cad && adjoining_data && boundary); + + prefix = str_cget(&data_cad->building->name); + adjoining_n = darray_adjoining_data_size_get(adjoining_data); + adj = darray_adjoining_data_data_get(adjoining_data); + darray_geometries_init(data_cad->building->city->allocator, &array); + str_init(data_cad->building->city->allocator, &name); + + /* Ensure enough room for all geometries without error nor mem move */ + ERR(darray_geometries_reserve(&array, 14 + adjoining_n)); + /* 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 + * part that could end to be not conformal is not part of the boundary. As a + * consequence it cannot be part of the result. */ + ERR(darray_geometries_push_back(&array, &data_cad->wall)); + ERR(darray_geometries_push_back(&array, &data_cad->roof)); + 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) { + ERR(darray_geometries_push_back(&array, &data_cad->foundation)); + } + if(data_cad->intermediate_floor) { + ERR(darray_geometries_push_back(&array, &data_cad->intermediate_floor)); + } + if(data_cad->external_insulation) { + ERR(darray_geometries_push_back(&array, &data_cad->external_insulation)); + } + if(data_cad->internal_insulation) { + ERR(darray_geometries_push_back(&array, &data_cad->internal_insulation)); + } + if(data_cad->roof_insulation) { + ERR(darray_geometries_push_back(&array, &data_cad->roof_insulation)); + } + if(data_cad->floor_insulation) { + ERR(darray_geometries_push_back(&array, &data_cad->floor_insulation)); + } + if(data_cad->attic_cavity) { + ERR(darray_geometries_push_back(&array, &data_cad->attic_cavity)); + } + if(data_cad->crawlspace_cavity) { + ERR(darray_geometries_push_back(&array, &data_cad->crawlspace_cavity)); + } + if(data_cad->glazing) { + ERR(darray_geometries_push_back(&array, &data_cad->glazing)); + } + 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)); + } + } + + count = darray_geometries_size_get(&array); + list = darray_geometries_data_get(&array); + + /* Ensure enough room for all geometries without error nor mem move */ + ERR(darray_geometries_reserve(boundary, 5+darray_geometries_size_get(boundary))); + + ERR(str_set(&name, prefix)); + ERR(str_append(&name, "_B_walls")); + boundaryname = str_get(&name); + ERR(scad_geometries_common_boundaries(boundaryname, list, count, + &data_cad->wall, 1, &bound)); + ERR(darray_geometries_push_back(boundary, &bound)); + ERR(scad_geometry_ref_put(bound)); + bound = NULL; + + ERR(str_set(&name, prefix)); + ERR(str_append(&name, "_B_roof")); + boundaryname = str_get(&name); + ERR(scad_geometries_common_boundaries(boundaryname, list, count, + &data_cad->roof, 1, &bound)); + ERR(darray_geometries_push_back(boundary, &bound)); + ERR(scad_geometry_ref_put(bound)); + bound = NULL; + + if(data_cad->glazing) { + ERR(str_set(&name, prefix)); + ERR(str_append(&name, "_B_glazing")); + boundaryname = str_get(&name); + ERR(scad_geometries_common_boundaries(boundaryname, list, count, + &data_cad->glazing, 1, &bound)); + ERR(darray_geometries_push_back(boundary, &bound)); + ERR(scad_geometry_ref_put(bound)); + bound = NULL; + } + + if(data_cad->external_insulation) { + ERR(str_set(&name, prefix)); + ERR(str_append(&name, "_B_external_insulation")); + boundaryname = str_get(&name); + ERR(scad_geometries_common_boundaries(boundaryname, list, count, + &data_cad->external_insulation, 1, &bound)); + ERR(darray_geometries_push_back(boundary, &bound)); + ERR(scad_geometry_ref_put(bound)); + bound = NULL; + } + + if(data_cad->internal_insulation) { + size_t bcount = 0; + ERR(str_set(&name, prefix)); + ERR(str_append(&name, "_B_internal_insulation")); + boundaryname = str_get(&name); + ERR(scad_geometries_common_boundaries(boundaryname, list, count, + &data_cad->internal_insulation, 1, &bound)); + ERR(scad_geometry_get_count(bound, &bcount)); + if(bcount > 0) { + ERR(darray_geometries_push_back(boundary, &bound)); + } + ERR(scad_geometry_ref_put(bound)); + bound = NULL; + } + +exit: + if(bound) SCAD(geometry_ref_put(bound)); + str_release(&name); + darray_geometries_release(&array); + return res; +error: + goto exit; +} + +static res_T +build_connection + (struct data_cad_cmode_2* data_cad, + struct darray_geometries* connection) +{ + res_T res = RES_OK; + struct scad_geometry* connect = NULL; + size_t count = 0; + char* cname = NULL; + struct str name; + const char* prefix; + + ASSERT(data_cad && connection); + + prefix = str_cget(&data_cad->building->name); + str_init(data_cad->building->city->allocator, &name); + +#define CREATE_CONNECT(G1,G2,SUFFIX) \ + ERR(str_set(&name, prefix));\ + ERR(str_append(&name, SUFFIX));\ + cname = str_get(&name);\ + 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) { \ + ERR(darray_geometries_push_back(connection, &connect)); \ + } \ + ERR(scad_geometry_ref_put(connect)); \ + connect = NULL; + + /* -------------------------------------------------------------------------*/ + /* habitable cavity connections */ + /* -------------------------------------------------------------------------*/ + + /* with floor */ + CREATE_CONNECT(habitable_cavity,floor,"_C_levels_floor"); + + /* with wall */ + CREATE_CONNECT(habitable_cavity,wall,"_C_levels_walls"); + + /* with glass */ + if(data_cad->glazing) { + CREATE_CONNECT(habitable_cavity,glazing,"_C_levels_glazing"); + } + + /* with 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) { + CREATE_CONNECT(habitable_cavity,roof_insulation,"_C_levels_roof_insulation"); + } else { + /* with roof */ + CREATE_CONNECT(habitable_cavity,roof,"_C_levels_roof"); + } + + /* with intermediate floor */ + if(data_cad->intermediate_floor) { + CREATE_CONNECT(habitable_cavity,intermediate_floor,"_C_levels_intermediate_floors"); + } + + /* -------------------------------------------------------------------------*/ + /* crawlspace cavity connections */ + /* -------------------------------------------------------------------------*/ + + if(data_cad->crawlspace_cavity) { + /* with floor insulation */ + if(data_cad->floor_insulation) { + CREATE_CONNECT(crawlspace_cavity, floor_insulation,"_C_crawlspace_insulation"); + } else { + /* with floor */ + CREATE_CONNECT(crawlspace_cavity, floor,"_C_crawlspace_floor"); + } + + /* with wall */ + CREATE_CONNECT(crawlspace_cavity, foundation,"_C_crawlspace_foundation"); + + /* with ground */ + CREATE_CONNECT(crawlspace_cavity, fake_ground,"_C_crawlspace_ground"); + } + + /* -------------------------------------------------------------------------*/ + /* attic cavity connections */ + /* -------------------------------------------------------------------------*/ + + if(data_cad->attic_cavity) { + /* with roof */ + CREATE_CONNECT(attic_cavity, roof,"_C_attic_roof"); + + /* with roof insulation */ + if(data_cad->roof_insulation) { + CREATE_CONNECT(attic_cavity, roof_insulation,"_C_attic_insulation"); + } + + /* with wall */ + CREATE_CONNECT(attic_cavity, wall,"_C_attic_walls"); + } + +#undef CREATE_CONNECT + +exit: + str_release(&name); + return res; +error: + goto exit; +} + +static res_T +build_fake_ground + (struct data_cad_cmode_2* cad, + const double depth, + struct darray_geometries* current_cad, + struct scad_geometry** ground) +{ + res_T res = RES_OK; + double dir[3] = {0, 0, 0}; + struct scpr_polygon* pg_offset = NULL; + size_t count; + struct darray_geometries array; + struct scad_geometry** list = NULL; + struct scad_geometry* footprint = NULL; + struct scad_geometry* geom = NULL; + + ASSERT(cad && ground); + + darray_geometries_init(cad->building->city->allocator, &array); + + /* Ensure enough room for all geometries without error nor mem move */ + ERR(darray_geometries_reserve(&array, 4)); + if(cad->foundation) { + ERR(darray_geometries_push_back(&array, &cad->foundation)); + } + if(cad->crawlspace_cavity) { + ERR(darray_geometries_push_back(&array, &cad->crawlspace_cavity)); + } + if(cad->floor) { + ERR(darray_geometries_push_back(&array, &cad->floor)); + } + if(cad->floor_insulation) { + ERR(darray_geometries_push_back(&array, &cad->floor_insulation)); + } + + count = darray_geometries_size_get(&array); + list = darray_geometries_data_get(&array); + + ERR(scpr_polygon_create_copy(cad->building->city->scpr, cad->building->pg, &pg_offset)); + ERR(scpr_offset_polygon(pg_offset, 0.1, SCPR_JOIN_MITER)); + + ERR(build_footprint(pg_offset, 0, &footprint)); + + dir[2] = -depth*1.1; + ERR(scad_geometry_extrude(footprint, NULL, dir, &geom)); + + ERR(scad_cut_geometries("fake_ground", &geom, 1, list, count, ground)); + ERR(darray_geometries_push_back(current_cad, ground)); + +exit: + 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)); + return res; +error: + goto exit; +} + +static res_T +building_ground_connection + (struct data_cad_cmode_2* cad, + struct scad_geometry** connection) +{ + res_T res = RES_OK; + char* cname = NULL; + struct str name; + size_t count; + struct darray_geometries array; + struct scad_geometry** list = NULL; + struct scad_geometry* list_boundary = NULL; + struct scad_geometry* footprint = NULL; + const char* prefix; + + ASSERT(cad && connection); + + prefix = str_cget(&cad->building->name); + darray_geometries_init(cad->building->city->allocator, &array); + str_init(cad->building->city->allocator, &name); + ERR(str_set(&name, prefix)); + ERR(str_append(&name, "_C_ground")); + cname = str_get(&name); + + /* Ensure enough room for all geometries without error nor mem move */ + ERR(darray_geometries_reserve(&array, 6)); + if(cad->foundation) { + ERR(darray_geometries_push_back(&array, &cad->foundation)); + } + if(cad->crawlspace_cavity) { + ERR(darray_geometries_push_back(&array, &cad->crawlspace_cavity)); + } + if(cad->floor) { + ERR(darray_geometries_push_back(&array, &cad->floor)); + } + if(cad->floor_insulation) { + ERR(darray_geometries_push_back(&array, &cad->floor_insulation)); + } + if(cad->external_insulation) { + ERR(darray_geometries_push_back(&array, &cad->external_insulation)); + } + if(cad->wall) { + ERR(darray_geometries_push_back(&array, &cad->wall)); + } + + count = darray_geometries_size_get(&array); + list = darray_geometries_data_get(&array); + + ERR(scad_geometries_common_boundaries(cname, list, count, &cad->fake_ground, 1, + 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)); + return res; +error: + goto exit; +} + +/*----------------------------------------------------------------------------*/ +/*----------------------------------------------------------------------------*/ +/*----------------------------------------------------------------------------*/ + +res_T +init_cmode_2 + (struct building* building, + struct city* city, + struct parsed_city_building* parsed_data, + struct catalog* catalog, + const double lower[2], + const double upper[2]) +{ + res_T res = RES_OK; + struct dataset_cmode_2* data; + static struct construction_mode_functors functors_2 = { + &init_cmode_2, + &release_cmode_2, + &build_cad_cmode_2, + &build_footprint_cmode_2, + &save_ground_connection_triangles_2, + &export_stl_cmode_2, + &release_cad_cmode_2 + }; + struct logger* logger; + int has_external_insulation; + double *lh; + unsigned count; + + if(!building || !city || !parsed_data || !catalog || !lower || !upper) { + res = RES_BAD_ARG; + goto error; + } + + logger = city->logger; + building->construction_mode = mode_2; + building->functors = &functors_2; + + ERR(init_building_base(building, city, parsed_data)); + + if(parsed_data->height != 0) { + ERR(logger_print(city->logger, LOG_ERROR, + "Building '%s' defines 'height' " + "(feature not available in construction mode 2).\n", + str_cget(&building->name))); + res = RES_BAD_ARG; + goto error; + } + + if(parsed_data->levels_height_count <= 0) { + ERR(logger_print(city->logger, LOG_ERROR, + "Building '%s' height definition is invalid " + "(construction mode 2 should define the 'levels_height' field).\n", + str_cget(&building->name))); + res = RES_BAD_ARG; + goto error; + } + + building->data = htable_dataset_cmode_2_find(&catalog->catalog_2, + &building->dataset_name); + if(building->data == NULL) { + ERR(logger_print(logger, LOG_ERROR, + "Unknown dataset name: '%s' used by building '%s'.\n", + str_cget(&building->dataset_name), str_cget(&building->name))); + res = RES_BAD_ARG; + goto error; + } + data = (struct dataset_cmode_2 *)building->data; + has_external_insulation = (data->external_insulation_thickness != 0); + ERR(str_set(&building->external_layer_name, + (has_external_insulation ? "external_insulation" : "walls"))); + + if(data->has_attic && parsed_data->levels_height_count <= 1) { + ERR(logger_print(city->logger, LOG_ERROR, + "Building '%s' height definition is invalid " + "(At least 2 level heights needed when there is an attic).\n", + str_cget(&building->name))); + res = RES_BAD_ARG; + goto error; + } + + ERR(darray_double_resize(&building->levels_height, + parsed_data->levels_height_count)); + lh = darray_double_data_get(&building->levels_height); + + building->total_height = 0; + for(count = 0; count < parsed_data->levels_height_count; count++) { + lh[count] = parsed_data->levels_height[count]; + if(lh[count] <= 0) { + ERR(logger_print(city->logger, LOG_ERROR, + "Building '%s' height definition is invalid " + "(level heights should be positive).\n", + str_cget(&building->name))); + res = RES_BAD_ARG; + goto error; + } + building->total_height += lh[count]; + } + +exit: + return res; +error: + goto exit; +} + +res_T +release_cmode_2 + (struct building* building) +{ + if(!building) return RES_BAD_ARG; + return release_building_base(building); +} + +res_T +build_cad_cmode_2 + (struct building* building, + int dump_footprints_level, + int keep_running_on_errors, + struct darray_adjoining_data* adjoining_data, + struct darray_geometries* current_cad, + void** cad) +{ + res_T res = RES_OK; + double height = building->total_height; + double depth = 0; + struct dataset_cmode_2* data = (struct dataset_cmode_2 *)building->data; + struct data_cad_cmode_2* data_cad = NULL; + double e_attic; + const double* levels; + const char* name; + struct scpr_intersector* overlapping_intersector = NULL; + struct scpr_intersector_check_callbacks callbacks + = SCPR_INTERSECTOR_CHECK_CALLBACKS_NULL__; + size_t adjoining_n = 0, levels_n; + struct callback_ctx ctx = CB_CTX_NULL__; + int error_occured = 0, error_msg_printed = 0;; + struct htable_polygons polygons; + int polygons_initialized = 0; + double zero = 0; + struct mem_allocator* allocator; + struct logger* logger = NULL; + 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) { + res = RES_BAD_ARG; + goto error; + } + + time_current(&t0); + name = str_cget(&building->name); + allocator = building->city->allocator; + logger = building->city->logger; + levels = darray_double_cdata_get(&building->levels_height); + levels_n = data->has_attic ? + darray_double_size_get(&building->levels_height) - 1 : + darray_double_size_get(&building->levels_height); + e_attic = data->has_attic ? levels[levels_n] : 0; + + logger_print(logger, LOG_OUTPUT, + "Building '%s' construction mode 2, dataset '%s', %g m tall, %lu levels.\n", + name, str_cget(&building->dataset_name), building->total_height, levels_n); + + data_cad = MEM_CALLOC(allocator, 1, sizeof(struct data_cad_cmode_2)); + if(!data_cad) { + res = RES_MEM_ERR; + goto error; + } + building->data_cad = data_cad; + data_cad->building = building; + darray_common_trg_init(allocator, &data_cad->common_trg); + darray_geometries_init(allocator, &data_cad->adj_walls); + darray_geometries_init(allocator, &data_cad->boundary); + darray_geometries_init(allocator, &data_cad->connection); + + htable_polygons_init(allocator, &polygons); + polygons_initialized = 1; + + ERR(scpr_intersector_create(building->city->scpr, &overlapping_intersector)); + ERR(scpr_intersector_register_polygon(overlapping_intersector, building->pg)); + /* An htable to associate offset polygons to offsets. Not really for + * performance, but to avoid considering the same polygon more than once when + * checking for polygon intersections (that would be an error). */ + ERR(htable_polygons_set(&polygons, &zero, &building->pg)); + + /* build mandatories elements : + - floor + - wall + - roof + */ + + ERR(build_floor(building, &error_msg_printed, data, overlapping_intersector, + &polygons, current_cad, &data_cad->floor)); + + ERR(build_wall(building, &error_msg_printed, 0, "S_walls", height, data, + overlapping_intersector, &polygons, current_cad, &data_cad->wall)); + + ERR(build_roof(building, &error_msg_printed, + height, data, + overlapping_intersector, &polygons, current_cad, &data_cad->roof)); + + /* build optionnal elements : + - foundation + - intermediate floor + - external insulation + - internal insulation + - roof insulation + - floor insulation + */ + + 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(levels_n > 1) { + ERR(build_inter_floor(building, &error_msg_printed, data, + overlapping_intersector, &polygons, current_cad, &data_cad->intermediate_floor)); + } + + 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) { + 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) { + 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) { + ERR(build_floor_insulation(building, &error_msg_printed, data, + overlapping_intersector, &polygons, current_cad, &data_cad->floor_insulation)); + } + + /* build cavities : + - attic + - habitable + - crawlspace + */ + + if(e_attic > 0) { + ERR(build_attic(building, &error_msg_printed, height, data, + overlapping_intersector, &polygons, current_cad, &data_cad->attic_cavity)); + } + + ERR(build_habitable(building, &error_msg_printed, height, data, + data_cad->intermediate_floor, overlapping_intersector, &polygons, + current_cad, &data_cad->habitable_cavity)); + + if(data->crawl_height > 0) { + ERR(build_crawlspace(building, &error_msg_printed, data, + overlapping_intersector, &polygons, current_cad, &data_cad->crawlspace_cavity)); + } + + /* Check for registered polygons overlapping */ + ctx.city = building->city; + ctx.buildings = building; + ctx.buildings_count = 1; + ctx.intersection_found = &error_occured; + ctx.search_type = TESTING_1_BUILDING_INTERNALS; + ctx.dump_footprints_level = dump_footprints_level; + ctx.keep_running_on_errors = keep_running_on_errors; + callbacks.simple_intersection = simple_intersection; + ERR(scpr_intersector_check(overlapping_intersector, &callbacks, &ctx)); + if(error_occured) { + logger_print(logger, LOG_ERROR, + "Internal error generating CAD for building '%s'.\n", + name); + building->event_flags |= BUILDING_REMOVED; + error_msg_printed = 1; + res = RES_BAD_ARG; + goto error; + } + + /* build adjoining envelop */ + adjoining_n = htable_building_size_get(&building->close_buildings); + if(adjoining_n > 0) { + ERR(build_adjoining(building, 0, current_cad, adjoining_data)); + } + + /* windows */ + 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 */ + depth = MMAX(data->foundation_depth, + 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)); + if(!partitioned) { + res = RES_MEM_ERR; + goto error; + } + cur_cad = darray_geometries_data_get(current_cad); + ERR(scad_geometries_partition(cur_cad, cad_count, Scad_dump_on_overlapping_error, + partitioned)); + /* Swap original geometry and partitioned geometry in data_cad (was + * accumulated into current_cad) */ + ERR(scad_geometries_swap(cur_cad, partitioned, cad_count, Scad_swap_geometry)); + for(i = 0; i < cad_count; i++) { + if(partitioned[i]) { + ERR(scad_geometry_ref_put(partitioned[i])); + } + } + 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) { + size_t a, c; + struct b_pair pair; + struct darray_double* common; + struct adjoining_data* adjoining + = darray_adjoining_data_data_get(adjoining_data); + struct scad_geometry* extern_most = data_cad->external_insulation + ? data_cad->external_insulation : data_cad->wall; + for(a = 0; a < adjoining_n; a++) { + struct adjoining_data* adj = adjoining + a; + ERR(scad_geometries_common_boundaries(NULL, &extern_most, 1, + &adj->envelop, 1, &adj->common_geometry)); + ERR(scad_geometry_get_count(adj->common_geometry, &c)); + adj->really_adjoining = (c != 0); + if(!adj->really_adjoining) { + logger_print(logger, LOG_OUTPUT, + "Building '%s': neighbor '%s' not really adjoining.\n", + name, + str_cget(&adj->adjoining_building->name)); + continue; + } + make_b_pair(&pair, building, adj->adjoining_building); + /* Keep track of the geometry to replace and the mesh to output instead */ + ERR(darray_geometries_push_back(&data_cad->adj_walls, &adj->common_geometry)); + ERR(darray_common_trg_push_back(&data_cad->common_trg, &pair)); + common = htable_common_find(&building->city->common, &pair); + if(!common) { + /* The mesh doesn't exist yet and won't be created until a further step. + * We need to store the geometry id so that the mesh can be stored when + * created. */ + adj->save = 1; + } + } + } + + /* build ground/building connection */ + ERR(building_ground_connection(data_cad, &data_cad->ground_connection)); + + /* build boundaries */ + ERR(build_boundary(data_cad, adjoining_data, &data_cad->boundary)); + + /* 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++) { + if(partitioned[i]) SCAD(geometry_ref_put(partitioned[i])); + } + MEM_RM(allocator, partitioned); + } + if(polygons_initialized) htable_polygons_release(&polygons); + if(overlapping_intersector) SCPR(intersector_ref_put(overlapping_intersector)); + if(cad) *(struct data_cad_cmode_2**)cad = data_cad; + return res; +error: + if(logger && building && !error_msg_printed) { + logger_print(logger, LOG_ERROR, + "Unknown error generating CAD for building '%s'.\n", + str_cget(&building->name)); + } + if(data_cad) CHK(RES_OK == release_cad_cmode_2(data_cad)); + data_cad = NULL; + goto exit; +} + +res_T +build_footprint_cmode_2 + (struct building* building, + struct scad_geometry** footprint) +{ + res_T res = RES_OK; + + if(!building || !footprint) { + res = RES_BAD_ARG; + goto error; + } + + ERR(build_footprint(building->pg, 0, footprint)); + +exit: + return res; +error: + goto exit; +} + +res_T save_ground_connection_triangles_2 + (void* cad, + struct darray_double* triangles) +{ + res_T res = RES_OK; + struct data_cad_cmode_2* data_cad = cad; + + if(!cad || !triangles) { + res = RES_BAD_ARG; + goto error; + } + + ERR(scad_stl_get_data(data_cad->ground_connection, triangles)); + +exit: + return res; +error: + goto exit; +} + +res_T +export_stl_cmode_2 + (void* cad, + const int binary) +{ + res_T res = RES_OK; + struct data_cad_cmode_2* data_cad = (struct data_cad_cmode_2 *)cad; + size_t i = 0, j, common_n = 0, coord_count = 0; + struct darray_double trg; + struct str name; + int initialized = 0; + struct scad_geometry** list = NULL; + struct mem_allocator* allocator = NULL; + struct city* city; + struct building* building; + struct vertex_denoiser* denoiser; + FILE* model = NULL; + FILE* vars = NULL; + fpos_t model_pos, vars_pos; + long model_count = 0; + const char* n; + static int fst = 1; + + if(!cad) { + res = RES_BAD_ARG; + goto error; + } + + building = data_cad->building; + city = building->city; + allocator = city->allocator; + denoiser = city->denoiser; + model = city->stardis_model; + vars = city->set_vars; + + if(!fst) fprintf(model, "\n"); + fst = 0; + fprintf(model, + "# Building '%s' construction mode 2, dataset '%s', %g m tall\n", + str_cget(&building->name), str_cget(&building->dataset_name), + building->total_height); + fgetpos(model, &model_pos); + fprintf(vars, + "\n# Building '%s' construction mode 2, dataset '%s', %g m tall\n", + str_cget(&building->name), str_cget(&building->dataset_name), + building->total_height); + fgetpos(vars, &vars_pos); + + if(darray_geometries_size_get(&data_cad->adj_walls) == 0) { + /* wall export */ + ERR(stl_export_denoised_geometry(denoiser, data_cad->wall, + Scad_force_normals_outward, binary)); + + /* external insulation export */ + if(data_cad->external_insulation) { + ERR(stl_export_denoised_geometry(denoiser, data_cad->external_insulation, + Scad_force_normals_outward, binary)); + } + } else { + /* There is some adjoining building(s) to manage */ + struct scad_geometry* extern_most = data_cad->external_insulation + ? data_cad->external_insulation : data_cad->wall; + size_t common_count = darray_common_trg_size_get(&data_cad->common_trg); + const char* tmp; + + if(data_cad->external_insulation) { + /* wall export is not impacted as walls are not the external layer */ + ERR(stl_export_denoised_geometry(denoiser, data_cad->wall, + Scad_force_normals_outward, binary)); + } + + /* The external layer must use the common triangles */ + darray_double_init(allocator, &trg); + str_init(allocator, &name); + initialized = 1; + /* Get the triangles that are not common with adjoining buildings */ + ERR(scad_stl_get_data_partial(extern_most, + darray_geometries_data_get(&data_cad->adj_walls), + darray_geometries_size_get(&data_cad->adj_walls), &trg)); + coord_count = darray_double_size_get(&trg); + /* Add the triangles from adjoining buildings */ + for(i = 0; i < common_count; i++) { + size_t sz; + struct b_pair* pair = darray_common_trg_data_get(&data_cad->common_trg)+ i; + const double *t9; + double* tgt; + struct darray_double* common = NULL; + /* Get triangles */ + common = htable_common_find(&city->common, pair); + if(!common) { + res = RES_BAD_ARG; + goto error; + } + t9 = darray_double_cdata_get(common); + /* Add common triangles */ + sz = darray_double_size_get(common); + ASSERT(sz % 9 == 0); + ASSERT(coord_count == darray_double_size_get(&trg)); + ERR(darray_double_resize(&trg, coord_count + sz)); + tgt = darray_double_data_get(&trg); + for(j = 0; j < sz; j++) { + tgt[coord_count + j] = t9[j]; + } + coord_count += sz; + ASSERT(coord_count % 9 == 0); + } + 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)); + } + + STARDIS_SOLID(wall); + + if(data_cad->external_insulation) { + STARDIS_SOLID(external_insulation); + } + + /* floor export */ + ERR(stl_export_denoised_geometry(denoiser, data_cad->floor, + Scad_force_normals_outward, binary)); + + STARDIS_SOLID(floor); + + /* roof export */ + ERR(stl_export_denoised_geometry(denoiser, data_cad->roof, + Scad_force_normals_outward, binary)); + + STARDIS_SOLID(roof); + + /* foundation export */ + if(data_cad->foundation) { + ERR(stl_export_denoised_geometry(denoiser, data_cad->foundation, + Scad_force_normals_outward, binary)); + + STARDIS_SOLID(foundation); + } + + /* glass export */ + if(data_cad->glazing) { + ERR(stl_export_denoised_geometry(denoiser, data_cad->glazing, + Scad_force_normals_outward, binary)); + + STARDIS_SOLID(glazing); + } + + /* intermediate floor export*/ + if(data_cad->intermediate_floor) { + ERR(stl_export_denoised_geometry(denoiser, data_cad->intermediate_floor, + Scad_force_normals_outward, binary)); + + STARDIS_SOLID(intermediate_floor); + } + + /* internal insulation export*/ + if(data_cad->internal_insulation) { + ERR(stl_export_denoised_geometry(denoiser, data_cad->internal_insulation, + Scad_force_normals_outward, binary)); + + STARDIS_SOLID(internal_insulation); + } + + /* roof insulation export*/ + if(data_cad->roof_insulation) { + ERR(stl_export_denoised_geometry(denoiser, data_cad->roof_insulation, + Scad_force_normals_outward, binary)); + + STARDIS_SOLID(roof_insulation); + } + + /* floor insulation export*/ + if(data_cad->floor_insulation) { + ERR(stl_export_denoised_geometry(denoiser, data_cad->floor_insulation, + Scad_force_normals_outward, binary)); + + STARDIS_SOLID(floor_insulation); + } + + /* attic cavity export*/ + if(data_cad->attic_cavity) { + ERR(stl_export_denoised_geometry(denoiser, data_cad->attic_cavity, + Scad_force_normals_outward, binary)); + + STARDIS_FLUID(attic_cavity); + } + + /* habitable cavity export*/ + ERR(stl_export_denoised_geometry(denoiser, data_cad->habitable_cavity, + Scad_force_normals_outward, binary)); + + STARDIS_FLUID(habitable_cavity); + + /* crawlspace cavity export*/ + if(data_cad->crawlspace_cavity) { + ERR(stl_export_denoised_geometry(denoiser, data_cad->crawlspace_cavity, + Scad_force_normals_outward, binary)); + + STARDIS_FLUID(crawlspace_cavity); + } + + /* 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(stl_export_denoised_geometry(denoiser, b, Scad_keep_normals_unchanged, + binary)); + + ERR(scad_geometry_get_name(b, &n)); + STARDIS_H_BOUND_2(SFC, n); + } + + /* 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(stl_export_denoised_geometry(denoiser, c, Scad_keep_normals_unchanged, + binary)); + + ERR(scad_geometry_get_name(c, &n)); + STARDIS_SFC_2(SFC, n); + } + + /* ground/building connection export*/ + ERR(stl_export_denoised_geometry(denoiser, data_cad->ground_connection, + Scad_keep_normals_unchanged, binary)); + + /* No need to describe solid-solid connections until there is a contact + * resistance */ + +exit: + for(j = 0; j < common_n; j++) { + if(list[j]) SCAD(geometry_ref_put(list[j])); + } + MEM_RM(allocator, list); + if(initialized) { + darray_double_release(&trg); + str_release(&name); + } + return res; +error: + if(data_cad) { + logger_print(data_cad->building->city->logger, LOG_ERROR, + "Internal error '"__FILE__"': creating STL for building '%s'.\n", + str_cget(&data_cad->building->name)); + } + /* Reset stardis model file */ + if(model) { + long l; + fsetpos(model, &model_pos); + for(l = 0; l < model_count; l += 40) + fprintf(model, " \n"); + fprintf(model, "# Building '%s' construction cancelled\n", + str_cget(&building->name)); + } + if(vars) { + fprintf(vars, "# Building '%s' construction cancelled\n", + str_cget(&building->name)); + } + goto exit; +} + +res_T +release_cad_cmode_2 + (void* cad) +{ + res_T res = RES_OK; + struct data_cad_cmode_2* data_cad = (struct data_cad_cmode_2 *)cad; + struct mem_allocator* allocator; + + if(!cad) { + res = RES_BAD_ARG; + goto error; + } + + allocator = data_cad->building->city->allocator; + darray_geometries_release(&data_cad->boundary); + darray_geometries_release(&data_cad->connection); + darray_common_trg_release(&data_cad->common_trg); + darray_geometries_release(&data_cad->adj_walls); + +#define GDEL(Field) \ + if(data_cad->Field) SCAD(geometry_ref_put(data_cad->Field)); \ + /* To ease debugging, write NULL after deletion */ \ + data_cad->Field = NULL + GDEL(attic_cavity); + GDEL(crawlspace_cavity); + GDEL(external_insulation); + GDEL(fake_ground); + GDEL(floor); + GDEL(floor_insulation); + GDEL(foundation); + GDEL(glazing); + GDEL(ground_connection); + GDEL(habitable_cavity); + GDEL(intermediate_floor); + GDEL(internal_insulation); + GDEL(roof); + GDEL(roof_insulation); + GDEL(wall); +#undef GDEL + MEM_RM(allocator, data_cad); + +exit: + return res; +error: + goto exit; +} diff --git a/src/cg_construction_mode_2.h b/src/cg_construction_mode_2.h @@ -0,0 +1,136 @@ +/* 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 Construction_MODE_2_H +#define Construction_MODE_2_H + +#include "cg_construction_mode.h" + +#include <rsys/rsys.h> +#include <rsys/str.h> +#include <rsys/hash_table.h> +#include <rsys/dynamic_array.h> + +struct scad_geometry; +struct building; +struct city; +struct parsed_city_building; +struct catalog; + +/* specific data for construction mode 1 */ +struct dataset_cmode_2 { + double wall_thickness; /* must be > 0 */ + double floor_thickness; /* must be > 0 */ + double inter_floor_thickness; /* must be > 0 */ + double roof_thickness; /* must be > 0 */ + double internal_insulation_thickness; /* can be 0 */ + double external_insulation_thickness; /* can be 0 */ + double floor_insulation_thickness; /* can be 0 */ + double roof_insulation_thickness; /* can be 0 */ + double foundation_depth; /* can be 0 */ + double crawl_height; /* can be 0 */ + double glass_ratio; /* in [0, 1] */ + double windows_min_width; /* must be > 0 */ + double windows_max_width; /* must be >= windows_min_width */ + double windows_min_spacing; /* must be > 0 */ + double windows_height_ratio; /* in [0, 1] */ + int has_attic; +}; + +#define HTABLE_NAME dataset_cmode_2 +#define HTABLE_DATA struct dataset_cmode_2 +#define HTABLE_KEY struct str +#define HTABLE_KEY_FUNCTOR_INIT str_init +#define HTABLE_KEY_FUNCTOR_RELEASE str_release +#define HTABLE_KEY_FUNCTOR_COPY str_copy +#define HTABLE_KEY_FUNCTOR_COPY_AND_RELEASE str_copy_and_release +#define HTABLE_KEY_FUNCTOR_COPY_AND_CLEAR str_copy_and_clear +#define HTABLE_KEY_FUNCTOR_EQ str_eq +#define HTABLE_KEY_FUNCTOR_HASH str_hash +#include <rsys/hash_table.h> + +struct data_cad_cmode_2 { + struct darray_common_trg common_trg; + struct darray_geometries adj_walls; + struct darray_geometries boundary; + struct darray_geometries connection; + struct building* building; + struct scad_geometry* wall; + struct scad_geometry* roof; + struct scad_geometry* floor; + struct scad_geometry* foundation; /* can be NULL */ + struct scad_geometry* intermediate_floor; /* can be NULL */ + struct scad_geometry* habitable_cavity; + struct scad_geometry* crawlspace_cavity; /* can be NULL */ + struct scad_geometry* attic_cavity; /* can be NULL */ + struct scad_geometry* internal_insulation; /* can be NULL */ + struct scad_geometry* external_insulation; /* can be NULL */ + struct scad_geometry* floor_insulation; /* can be NULL */ + struct scad_geometry* roof_insulation; /* can be NULL */ + struct scad_geometry* glazing; + struct scad_geometry* fake_ground;/*not exported, used for ground connection*/ + struct scad_geometry* ground_connection; + size_t n_connection; +}; + +res_T +init_cmode_2 + (struct building* building, + struct city* city, + struct parsed_city_building* parsed_data, + struct catalog* catalog, + const double lower[2], + const double upper[2]); + +res_T +release_cmode_2 + (struct building* building); + +res_T +build_cad_cmode_2 + (struct building* building, + int dump_footprints_on_error, + int keep_running_on_errors, + struct darray_adjoining_data* adjoining_data, + struct darray_geometries* current_cad, + void** cad); + +res_T +build_footprint_cmode_2 + (struct building* building, + struct scad_geometry** footprint); + +res_T save_ground_connection_triangles_2 + (void* cad, + struct darray_double* triangles); + +res_T get_external_layer_name_2 + (struct building* building, + const char* name); + +res_T +export_stl_cmode_2 + (void* cad, + const int binary); + +res_T +release_cad_cmode_2 + (void* cad); + +#endif /* Construction_MODE_2_H */ diff --git a/src/cg_construction_mode_2_parsing_schemas.h b/src/cg_construction_mode_2_parsing_schemas.h @@ -0,0 +1,133 @@ +/* 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 FG_MODE2_PARSING_SCHEMAS__ +#define FG_MODE2_PARSING_SCHEMAS__ + +#include "cg_cyaml.h" + +#include <rsys/str.h> + +struct parsed_dataset_cmode_2 { + char* name; + double wall_thickness; /* must be > 0 */ + double floor_thickness; /* must be > 0 */ + double inter_floor_thickness; /* must be > 0 */ + double roof_thickness; /* must be > 0 */ + double internal_insulation_thickness; /* can be 0 */ + double external_insulation_thickness; /* can be 0 */ + double floor_insulation_thickness; /* can be 0 */ + double roof_insulation_thickness; /* can be 0 */ + double foundation_depth; /* can be 0 */ + double crawl_height; /* can be 0 */ + double glass_ratio; /* in [0, 1] */ + double windows_min_width; /* must be > 0 */ + double windows_max_width; /* must be >= windows_min_width */ + double windows_min_spacing; /* must be > 0 */ + double windows_height_ratio; /* in [0, 1] */ + int has_attic; +}; + +/********************************************************/ +/* Types used for parsing and to define parsing schemas */ +/********************************************************/ + +struct parsed_catalog_cmode_2 { + struct parsed_dataset_cmode_2* datasets; + size_t datasets_count; +}; + +static const cyaml_schema_field_t dataset_cmode_2_fields_schema[] = { + CYAML_FIELD_STRING_PTR("name", CYAML_FLAG_POINTER, + struct parsed_dataset_cmode_2, name, 0, CYAML_UNLIMITED), + CYAML_FIELD_FLOAT("wall_thickness", CYAML_FLAG_DEFAULT, + struct parsed_dataset_cmode_2, wall_thickness), + CYAML_FIELD_FLOAT("floor_thickness", CYAML_FLAG_DEFAULT, + struct parsed_dataset_cmode_2, floor_thickness), + CYAML_FIELD_FLOAT("inter_floor_thickness", CYAML_FLAG_DEFAULT, + struct parsed_dataset_cmode_2, inter_floor_thickness), + CYAML_FIELD_FLOAT("roof_thickness", CYAML_FLAG_DEFAULT, + struct parsed_dataset_cmode_2, roof_thickness), + CYAML_FIELD_FLOAT("internal_insulation_thickness", CYAML_FLAG_DEFAULT, + struct parsed_dataset_cmode_2, internal_insulation_thickness), + CYAML_FIELD_FLOAT("external_insulation_thickness", CYAML_FLAG_DEFAULT, + struct parsed_dataset_cmode_2, external_insulation_thickness), + CYAML_FIELD_FLOAT("floor_insulation_thickness", CYAML_FLAG_DEFAULT, + struct parsed_dataset_cmode_2, floor_insulation_thickness), + CYAML_FIELD_FLOAT("roof_insulation_thickness", CYAML_FLAG_DEFAULT, + struct parsed_dataset_cmode_2, roof_insulation_thickness), + CYAML_FIELD_FLOAT("foundation_depth", CYAML_FLAG_DEFAULT, + struct parsed_dataset_cmode_2, foundation_depth), + CYAML_FIELD_FLOAT("crawl_height", CYAML_FLAG_DEFAULT, + struct parsed_dataset_cmode_2, crawl_height), + CYAML_FIELD_BOOL("has_attic", CYAML_FLAG_DEFAULT, + struct parsed_dataset_cmode_2, has_attic), + CYAML_FIELD_FLOAT("glass_ratio", CYAML_FLAG_DEFAULT, + struct parsed_dataset_cmode_2, glass_ratio), + CYAML_FIELD_FLOAT("windows_min_width", CYAML_FLAG_DEFAULT, + struct parsed_dataset_cmode_2, windows_min_width), + CYAML_FIELD_FLOAT("windows_max_width", CYAML_FLAG_DEFAULT, + struct parsed_dataset_cmode_2, windows_max_width), + CYAML_FIELD_FLOAT("windows_min_spacing", CYAML_FLAG_DEFAULT, + struct parsed_dataset_cmode_2, windows_min_spacing), + CYAML_FIELD_FLOAT("windows_height_ratio", CYAML_FLAG_DEFAULT, + struct parsed_dataset_cmode_2, windows_height_ratio), + CYAML_FIELD_END +}; + +static const struct cyaml_schema_value p_dataset_cmode_2_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct parsed_dataset_cmode_2, + dataset_cmode_2_fields_schema), +}; + +static const struct cyaml_schema_value dataset_cmode_2_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT, struct parsed_dataset_cmode_2, + dataset_cmode_2_fields_schema), +}; + +static const cyaml_schema_field_t cmode_2_fields_schemas[] = { + CYAML_FIELD_IGNORE("construction_mode", CYAML_FLAG_DEFAULT), + { + .key = "datasets", + .value = { + .type = CYAML_SEQUENCE, + .flags = CYAML_FLAG_POINTER, + .data_size = sizeof(struct parsed_dataset_cmode_2), + .sequence = { + .entry = &dataset_cmode_2_schema, + .min = 1, + .max = CYAML_UNLIMITED, + } + }, + .data_offset = offsetof(struct parsed_catalog_cmode_2, datasets), + .count_size = sizeof(((struct parsed_catalog_cmode_2*)0)->datasets_count), + .count_offset = offsetof(struct parsed_catalog_cmode_2, datasets_count), + }, + CYAML_FIELD_END +}; + +/* Top-level schema. The top level value for the construction mode is a mapping. + * Its fields are defined in cmode_2_fields_schemas. + */ +static const cyaml_schema_value_t construction_mode_2_schema = { + CYAML_VALUE_MAPPING(CYAML_FLAG_POINTER, struct parsed_catalog_cmode_2, + cmode_2_fields_schemas), +}; + +#endif diff --git a/src/cg_construction_modes_parsing_schemas.h b/src/cg_construction_modes_parsing_schemas.h @@ -30,6 +30,7 @@ enum parsed_cmode_type { PARSED_CMODE_0, PARSED_CMODE_1, + PARSED_CMODE_2, PARSED_CMODE_TYPE_UNDEFINED }; @@ -37,7 +38,8 @@ enum parsed_cmode_type { * schema. */ static const cyaml_strval_t city_building_types_strings[] = { { "Construction_Mode_0", PARSED_CMODE_0 }, - { "Construction_Mode_1", PARSED_CMODE_1 } + { "Construction_Mode_1", PARSED_CMODE_1 }, + { "Construction_Mode_2", PARSED_CMODE_2 } }; struct parsed_cmode { diff --git a/src/cg_default.h.in b/src/cg_default.h.in @@ -24,5 +24,7 @@ #define CG2_CLOSE_NEIGHBOR_DISTANCE @CG2_CLOSE_NEIGHBOR_DISTANCE@ #define CG2_MIN_DISTANCE_TO_MAP_LIMITS @CG2_MIN_DISTANCE_TO_MAP_LIMITS@ #define CG2_MIN_WINDOWS_WIDTH @CG2_MIN_WINDOWS_WIDTH@ +#define CG2_GLAZING_THICKNESS @CG2_GLAZING_THICKNESS@ +#define CG2_ARGS_DEFAULT_STARDIS_FILES_BASENAME "@CG2_ARGS_DEFAULT_STARDIS_FILES_BASENAME@" #endif diff --git a/src/cg_ground.c b/src/cg_ground.c @@ -22,6 +22,7 @@ #include "cg_building.h" #include "cg_city.h" #include "cg_vertex_denoiser.h" +#include "cg_stardis_model.h" #include <rsys/rsys.h> #include <rsys/dynamic_array_double.h> @@ -90,6 +91,7 @@ ground_build_cad if (N[0] == 0 && N[1] == 0 && N[2] == -1) { ERR(scad_geometry_rename(surface, "ground_B_bottom")); + ground->bottom = surface; GPUSH(surface); } else if (N[0] == 0 && N[1] == 0 && N[2] == 1) { @@ -104,6 +106,7 @@ ground_build_cad GPUSH(tmp); } else { ERR(scad_geometry_rename(surface, "ground_B_top")); + ground->top = surface; GPUSH(surface); } } @@ -152,18 +155,29 @@ ground_export_stl const char* name; struct str filename; int binary; + FILE* model = NULL; + FILE* vars = NULL; + const char* n; size_t i = 0; + long model_count = 0; ASSERT(city); + (void)model_count; /* To allow STARDIS_SOLID_ use */ ground = &city->ground; binary = city->binary_export; denoiser = city->denoiser; allocator = city->allocator; darray_double_init(allocator, &trg); str_init(allocator, &filename); + model = city->stardis_model; + vars = city->set_vars; + + fprintf(model, "\n# GROUND\n"); + fprintf(vars, "\n# GROUND\n"); for(i = 0; i < darray_geometries_size_get(&ground->boundaries); i++) { struct scad_geometry* b = darray_geometries_data_get(&ground->boundaries)[i]; + ERR(scad_geometry_get_name(b, &n)); /* Boundary files */ darray_double_clear(&trg); ERR(scad_stl_get_data(b, &trg)); @@ -177,6 +191,15 @@ ground_export_stl ERR(denoise_array(denoiser, &trg)); ERR(scad_stl_data_write(&trg, str_cget(&filename), Scad_keep_normals_unchanged, binary)); + if(b == ground->bottom) { + STARDIS_T_BOUND_2_model(ground_B_bottom, n); + } else { + if(b == ground->top) { + STARDIS_H_BOUND_2_model(ground_B_top, n); + }else { + STARDIS_F_BOUND_2(ground_B_lateral, n); + } + } /* Also collect boundaries triangles into ground body */ ERR(darray_double_merge(&ground->ground_building_connections, &trg)); } @@ -184,6 +207,8 @@ ground_export_stl ERR(scad_stl_data_write(&ground->ground_building_connections, "ground_body.stl", Scad_force_normals_outward, binary)); + STARDIS_SOLID_2_model(ground_body, "ground_body"); + exit: darray_double_release(&trg); str_release(&filename); diff --git a/src/cg_ground.h b/src/cg_ground.h @@ -38,6 +38,7 @@ struct ground { 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 */ + struct scad_geometry* bottom; /* Uncounted ptr to bottom boundary */ }; res_T diff --git a/src/cg_stardis_model.h b/src/cg_stardis_model.h @@ -0,0 +1,357 @@ +/* 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 STARDIS_MODEL_H +#define STARDIS_MODEL_H + +#include <rsys/rsys.h> + +/* Utility macros for stardis model output */ +#define STARDIS_FLUID_2(Group, Name) { \ + model_count += fprintf(model, \ + "FLUID %s " \ + "$%s_RHO $%s_CP $%s_TINIT $%s_T_IMPOSED " \ + "FRONT %s.stl\n", \ + Name, Name, Name, Name, Name, Name); \ + fprintf(vars, \ + "export %s_RHO=$" #Group "_RHO\n" \ + "export %s_CP=$" #Group "_CP\n" \ + "export %s_TINIT=$" #Group "_TINIT\n" \ + "export %s_T_IMPOSED=$" #Group "_T_IMPOSED\n", \ + Name, Name, Name, Name); \ +} (void)0 + +#define STARDIS_FLUID(Field) { \ + const char* n___; \ + ERR(scad_geometry_get_name(data_cad->Field, &n___)); \ + STARDIS_FLUID_2(Field, n___); \ +} (void)0 + +#define STARDIS_SOLID_2_model(Group, Name) { \ + model_count += fprintf(model, \ + "SOLID %s " \ + "$%s_LAMBDA $%s_RHO $%s_CP $%s_DELTA " \ + "$%s_TINIT $%s_T_IMPOSED $%s_POWER " \ + "FRONT %s.stl\n", \ + Name, Name, Name, Name, Name, Name, Name, Name, Name); \ +} (void)0 + +#define STARDIS_SOLID_2_vars(Group, Name) { \ + fprintf(vars, \ + "export %s_LAMBDA=$" #Group "_LAMBDA\n" \ + "export %s_RHO=$" #Group "_RHO\n" \ + "export %s_CP=$" #Group "_CP\n" \ + "export %s_DELTA=$" #Group "_DELTA\n" \ + "export %s_TINIT=$" #Group "_TINIT\n" \ + "export %s_T_IMPOSED=$" #Group "_T_IMPOSED\n" \ + "export %s_POWER=$" #Group "_POWER\n", \ + Name, Name, Name, Name, Name, Name, Name); \ +} (void)0 + +#define STARDIS_SOLID_2(Group, Name) { \ + STARDIS_SOLID_2_model(Group, Name); \ + STARDIS_SOLID_2_vars(Group, Name); \ +} (void)0 + +#define STARDIS_SOLID(Field) { \ + const char* n___; \ + ERR(scad_geometry_get_name(data_cad->Field, &n___)); \ + STARDIS_SOLID_2(Field, n___); \ +} (void)0 + +#define STARDIS_F_BOUND_2_model(Group, Name) { \ + model_count += fprintf(model, \ + "F_BOUNDARY_FOR_SOLID %s " \ + "$%s_F " \ + "%s.stl\n", \ + Name, Name, Name); \ +} (void)0 + +#define STARDIS_F_BOUND_2_vars(Group, Name) { \ + fprintf(vars, \ + "export %s_F=$" #Group "_F\n", \ + Name); \ +} (void)0 + +#define STARDIS_F_BOUND_2(Group, Name) { \ + STARDIS_F_BOUND_2_model(Group, Name); \ + STARDIS_F_BOUND_2_vars(Group, Name); \ +} (void)0 + +#define STARDIS_F_BOUND(Field) { \ + const char* n___; \ + ERR(scad_geometry_get_name(data_cad->Field, &n___)); \ + STARDIS_F_BOUND_2(Field, n___); \ +} (void)0 + +#define STARDIS_T_BOUND_2_model(Group, Name) { \ + model_count += fprintf(model, \ + "T_BOUNDARY_FOR_SOLID %s " \ + "$%s_T " \ + "%s.stl\n", \ + Name, Name, Name); \ +} (void)0 + +#define STARDIS_T_BOUND_2_vars(Group, Name) { \ + fprintf(vars, \ + "export %s_T=$" #Group "_T\n", \ + Name); \ +} (void)0 + +#define STARDIS_T_BOUND_2(Group, Name) { \ + STARDIS_T_BOUND_2_model(Group, Name); \ + STARDIS_T_BOUND_2_vars(Group, Name); \ +} (void)0 + +#define STARDIS_T_BOUND(Field) { \ + const char* n___; \ + ERR(scad_geometry_get_name(data_cad->Field, &n___)); \ + STARDIS_T_BOUND_2(Field, n___); \ +} (void)0 + +#define STARDIS_H_BOUND_2_model(Group, Name) { \ + model_count += fprintf(model, \ + "H_BOUNDARY_FOR_SOLID %s " \ + "$%s_TREF $%s_EMISSIVITY $%s_SPEC_FRACTION $%s_H $%s_TEXT " \ + "%s.stl\n", \ + Name, Name, Name, Name, Name, Name, Name); \ +} (void)0 + +#define STARDIS_H_BOUND_2_vars(Group, Name) { \ + fprintf(vars, \ + "export %s_TREF=$" #Group "_TREF\n" \ + "export %s_EMISSIVITY=$" #Group "_EMISSIVITY\n" \ + "export %s_SPEC_FRACTION=$" #Group "_SPEC_FRACTION\n" \ + "export %s_H=$" #Group "_H\n" \ + "export %s_TEXT=$" #Group "_TEXT\n", \ + Name, Name, Name, Name, Name); \ +} (void)0 + +#define STARDIS_H_BOUND_2(Group, Name) { \ + STARDIS_H_BOUND_2_model(Group, Name); \ + STARDIS_H_BOUND_2_vars(Group, Name); \ +} (void)0 + +#define STARDIS_H_BOUND(Field) { \ + const char* n___; \ + ERR(scad_geometry_get_name(data_cad->Field, &n___)); \ + STARDIS_H_BOUND_2(Field, n___); \ +} (void)0 + +#define STARDIS_SFC_2_model(Group, Name) { \ + model_count += fprintf(model, \ + "SOLID_FLUID_CONNECTION %s " \ + "$%s_TREF $%s_EMISSIVITY $%s_SPEC_FRACTION $%s_H " \ + "%s.stl\n", \ + Name, Name, Name, Name, Name, Name); \ +} (void)0 + +#define STARDIS_SFC_2_vars(Group, Name) { \ + fprintf(vars, \ + "export %s_TREF=$" #Group "_TREF\n" \ + "export %s_EMISSIVITY=$" #Group "_EMISSIVITY\n" \ + "export %s_SPEC_FRACTION=$" #Group "_SPEC_FRACTION\n" \ + "export %s_H=$" #Group "_H\n", \ + Name, Name, Name, Name); \ +} (void)0 + +#define STARDIS_SFC_2(Group, Name) { \ + STARDIS_SFC_2_model(Group, Name); \ + STARDIS_SFC_2_vars(Group, Name); \ +} (void)0 + +#define STARDIS_SFC(Field) { \ + const char* n___; \ + ERR(scad_geometry_get_name(data_cad->Field, &n___)); \ + STARDIS_SFC_2(Field, n___); \ +} (void)0 + +#define GENERIC_GROUND_VARS \ + "# GROUND properties.\n" \ + "export ground_body_LAMBDA=2\n" \ + "export ground_body_RHO=2000\n" \ + "export ground_body_CP=1000\n" \ + "export ground_body_DELTA=AUTO\n" \ + "export ground_body_TINIT=273\n" \ + "export ground_body_T_IMPOSED=UNKNOWN\n" \ + "export ground_body_POWER=0\n" + +#define GENERIC_B_GROUND_VARS \ + "# GROUND BOUNDARIES properties.\n" \ + "export ground_B_top_TREF=273\n" \ + "export ground_B_top_EMISSIVITY=0.9\n" \ + "export ground_B_top_SPEC_FRACTION=0\n" \ + "export ground_B_top_H=10\n" \ + "export ground_B_top_TEXT=273\n" \ + "export ground_B_bottom_T=286\n" \ + "export ground_B_lateral_F=0\n" + +#define GENERIC_WALL_VARS \ + "# Generic WALL properties.\n" \ + "export wall_LAMBDA=0.4\n" \ + "export wall_RHO=650\n" \ + "export wall_CP=1000\n" \ + "export wall_DELTA=AUTO\n" \ + "export wall_TINIT=273\n" \ + "export wall_T_IMPOSED=UNKNOWN\n" \ + "export wall_POWER=0\n" + +#define GENERIC_FLOOR_VARS \ + "# Generic FLOOR properties.\n" \ + "export floor_LAMBDA=1.5\n" \ + "export floor_RHO=2300\n" \ + "export floor_CP=2500\n" \ + "export floor_DELTA=AUTO\n" \ + "export floor_TINIT=273\n" \ + "export floor_T_IMPOSED=UNKNOWN\n" \ + "export floor_POWER=0\n" + +#define GENERIC_INTERMEDIATE_FLOOR_VARS \ + "# Generic INTERMEDIATE FLOOR properties.\n" \ + "export intermediate_floor_LAMBDA=1.5\n" \ + "export intermediate_floor_RHO=2300\n" \ + "export intermediate_floor_CP=2500\n" \ + "export intermediate_floor_DELTA=AUTO\n" \ + "export intermediate_floor_TINIT=273\n" \ + "export intermediate_floor_T_IMPOSED=UNKNOWN\n" \ + "export intermediate_floor_POWER=0\n" + +#define GENERIC_ROOF_VARS \ + "# Generic ROOF properties.\n" \ + "export roof_LAMBDA=1.5\n" \ + "export roof_RHO=2300\n" \ + "export roof_CP=2500\n" \ + "export roof_DELTA=AUTO\n" \ + "export roof_TINIT=273\n" \ + "export roof_T_IMPOSED=UNKNOWN\n" \ + "export roof_POWER=0\n" + +#define GENERIC_FOUNDATION_VARS \ + "# Generic FOUNDATION properties.\n" \ + "export foundation_LAMBDA=1.5\n" \ + "export foundation_RHO=2300\n" \ + "export foundation_CP=2500\n" \ + "export foundation_DELTA=AUTO\n" \ + "export foundation_TINIT=273\n" \ + "export foundation_T_IMPOSED=UNKNOWN\n" \ + "export foundation_POWER=0\n" + +#define GENERIC_GLAZING_VARS \ + "# Generic GLAZING properties.\n" \ + "export glazing_LAMBDA=1\n" \ + "export glazing_RHO=2500\n" \ + "export glazing_CP=720\n" \ + "export glazing_DELTA=AUTO\n" \ + "export glazing_TINIT=273\n" \ + "export glazing_T_IMPOSED=UNKNOWN\n" \ + "export glazing_POWER=0\n" + +#define GENERIC_INTERNAL_INSULATION_VARS \ + "# Generic INTERNAL INSULATION properties.\n" \ + "export internal_insulation_LAMBDA=0.05\n" \ + "export internal_insulation_RHO=18\n" \ + "export internal_insulation_CP=1000\n" \ + "export internal_insulation_DELTA=AUTO\n" \ + "export internal_insulation_TINIT=273\n" \ + "export internal_insulation_T_IMPOSED=UNKNOWN\n" \ + "export internal_insulation_POWER=0\n" + +#define GENERIC_EXTERNAL_INSULATION_VARS \ + "# Generic EXTERNAL INSULATION properties.\n" \ + "export external_insulation_LAMBDA=0.05\n" \ + "export external_insulation_RHO=18\n" \ + "export external_insulation_CP=1000\n" \ + "export external_insulation_DELTA=AUTO\n" \ + "export external_insulation_TINIT=273\n" \ + "export external_insulation_T_IMPOSED=UNKNOWN\n" \ + "export external_insulation_POWER=0\n" + +#define GENERIC_ROOF_INSULATION_VARS \ + "# Generic ROOF INSULATION properties.\n" \ + "export roof_insulation_LAMBDA=0.05\n" \ + "export roof_insulation_RHO=18\n" \ + "export roof_insulation_CP=1000\n" \ + "export roof_insulation_DELTA=AUTO\n" \ + "export roof_insulation_TINIT=273\n" \ + "export roof_insulation_T_IMPOSED=UNKNOWN\n" \ + "export roof_insulation_POWER=0\n" + +#define GENERIC_FLOOR_INSULATION_VARS \ + "# Generic FLOOR INSULATION properties.\n" \ + "export floor_insulation_LAMBDA=0.05\n" \ + "export floor_insulation_RHO=18\n" \ + "export floor_insulation_CP=1000\n" \ + "export floor_insulation_DELTA=AUTO\n" \ + "export floor_insulation_TINIT=273\n" \ + "export floor_insulation_T_IMPOSED=UNKNOWN\n" \ + "export floor_insulation_POWER=0\n" + +#define GENERIC_CAVITY_VARS \ + "# Generic FLUID CAVITY properties.\n" \ + "export cavity_RHO=1.25\n" \ + "export cavity_CP=1000\n" \ + "export cavity_TINIT=273\n" \ + "export cavity_T_IMPOSED=UNKNOWN\n" + +#define GENERIC_ATTIC_CAVITY_VARS \ + "# Generic FLUID ATTIC CAVITY properties.\n" \ + "export attic_cavity_RHO=1.25\n" \ + "export attic_cavity_CP=1000\n" \ + "export attic_cavity_TINIT=273\n" \ + "export attic_cavity_T_IMPOSED=UNKNOWN\n" + +#define GENERIC_HABITABLE_CAVITY_VARS \ + "# Generic FLUID HABITABLE CAVITY properties.\n" \ + "export habitable_cavity_RHO=1.25\n" \ + "export habitable_cavity_CP=1000\n" \ + "export habitable_cavity_TINIT=273\n" \ + "export habitable_cavity_T_IMPOSED=UNKNOWN\n" + +#define GENERIC_CRAWLSPACE_CAVITY_VARS \ + "# Generic FLUID CRAWLSPACE CAVITY properties.\n" \ + "export crawlspace_cavity_RHO=1.25\n" \ + "export crawlspace_cavity_CP=1000\n" \ + "export crawlspace_cavity_TINIT=273\n" \ + "export crawlspace_cavity_T_IMPOSED=UNKNOWN\n" + +#define GENERIC_SFC_VARS \ + "# Generic SOLID-FLUID CONNECTION properties.\n" \ + "export SFC_TREF=293\n" \ + "export SFC_EMISSIVITY=0.9\n" \ + "export SFC_SPEC_FRACTION=0\n" \ + "export SFC_H=5\n" \ + "export SFC_TEXT=273\n" + +#define GENERIC_B_WALL_VARS \ + "# Generic WALL BOUNDARY properties.\n" \ + "export boundary_wall_TREF=273\n" \ + "export boundary_wall_EMISSIVITY=0.9\n" \ + "export boundary_wall_SPEC_FRACTION=0\n" \ + "export boundary_wall_H=10\n" \ + "export boundary_wall_TEXT=273\n" \ + +#define GENERIC_B_ROOF_VARS \ + "# Generic ROOF BOUNDARY properties.\n" \ + "export boundary_roof_TREF=273\n" \ + "export boundary_roof_EMISSIVITY=0.9\n" \ + "export boundary_roof_SPEC_FRACTION=0\n" \ + "export boundary_roof_H=10\n" \ + "export boundary_roof_TEXT=273\n" + +#endif /* STARDIS_MODEL_H */