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:
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], ¢er_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 */