commit 1a24b187e3a9f0032d591e3f5926cc4f4fc29295
parent fe939049b21927749f5021d73f1c7965e26467ff
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date: Wed, 25 Sep 2024 16:59:06 +0200
Add a new construction mode
Labeled Construction_Mode_2.
its a simple renaming of Construction_Mode_1.
Later commits will eventually add changes that make it a trully new mode.
Diffstat:
10 files changed, 2411 insertions(+), 1 deletion(-)
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\
diff --git a/src/cg_building.h b/src/cg_building.h
@@ -99,6 +99,7 @@ struct construction_mode_functors {
enum construction_mode_type {
mode_0,
mode_1,
+ mode_2,
Construction_MODES_COUNT__
};
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,43 @@ 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.inter_floor_count = parsed_item->inter_floor_count;
+ 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.attic_height = parsed_item->attic_height;
+ item.glass_ratio = parsed_item->glass_ratio;
+ ERR(htable_dataset_cmode_2_set(&catalog->catalog_2, &name, &item));
+ }
+ break;
+ }
default: FATAL("Invalid enum value.");
}
}
@@ -187,6 +237,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");
}
}
@@ -106,6 +109,9 @@ parse_catalog
case PARSED_CMODE_1:
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");
}
time_sub(&dt, time_current(&dt), &t0);
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"
@@ -462,6 +463,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 */
diff --git a/src/cg_construction_mode_2.c b/src/cg_construction_mode_2.c
@@ -0,0 +1,2085 @@
+/* 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 <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;
+ double attic = data->attic_height;
+ 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 - 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;
+ double attic = data->attic_height;
+ 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 - 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 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** inter_floor)
+{
+ res_T res = RES_OK;
+ size_t i = 0;
+ 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_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;
+ double h_cavity = 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));
+
+ h_cavity = height - e_roof - attic - e_roof_ins - (double)floor_n*e_floor;
+ z_floor = h_cavity/(double)(1 + floor_n);
+ d[2] = e_floor;
+ for(i = 0; i < floor_n; i++) {
+ 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 += h_cavity/(double)(1 + floor_n) + e_floor;
+ }
+ ASSERT(darray_geometries_size_get(&floor_array) == floor_n);
+
+ floor_list = darray_geometries_data_get(&floor_array);
+ ERR(scad_fuse_geometries(floorname, floor_list, floor_n, floor_list, floor_n,
+ 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;
+ double e_attic = data->attic_height;
+ 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;
+ double e_attic = data->attic_height;
+ 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, removed_hor = 0;
+ size_t removed_windows_sz = 0, removed_windows_adj = 0, removed_windows_self = 0;
+ size_t list_n = 0, array_n;
+ const char* prefix;
+ double N[3];
+ double dir[3];
+ double scale[3], scale_[3];
+ struct scad_geometry* surface = NULL;
+ struct scad_geometry* win_surface = NULL;
+ struct scad_geometry* surface_ = 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;
+ size_t floor_n;
+ double e_roof, e_roof_ins, attic, h_wall, e_floor;
+ struct str name;
+ struct adjoining_data* adj;
+ struct city* city;
+ struct mem_allocator* allocator;
+ (void)removed_hor;
+
+ 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);
+
+ /* Compute wall height to help compute the minimum for windows area */
+ floor_n = data->inter_floor_count;
+ e_roof = data->roof_thickness;
+ e_roof_ins = data->roof_insulation_thickness;
+ attic = data->attic_height;
+ e_floor = data->inter_floor_thickness;
+ h_wall = (data_cad->building->height- e_roof - attic - e_roof_ins
+ - (double)floor_n*e_floor) / (double)(floor_n + 1);
+
+ d3_splat(scale, sqrt(data->glass_ratio));
+ d3_splat(scale_, MMIN(1, 1.05 * sqrt(data->glass_ratio)));
+
+ /* 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, mass, center[3];
+ size_t center_n;
+ size_t count;
+ int removed = 0;
+
+ 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) {
+ ERR(scad_geometry_ref_put(surface));
+ surface = NULL;
+ removed_hor++;
+ continue; /* keep only vertical face */
+ }
+
+ ERR(scad_geometry_get_mass(list[i], &mass));
+ if(mass < CG2_MIN_WINDOWS_WIDTH * h_wall) {
+ /* this window would be too small */
+ ERR(scad_geometry_ref_put(surface));
+ surface = NULL;
+ removed = 1;
+ removed_windows_sz++;
+ }
+
+ if(!removed) {
+ /* Used to check for validity with a slitghly bigger size */
+ ERR(str_printf(&name, "surface+_%lu", (long unsigned)i));
+ ERR(scad_geometry_dilate(surface, center, scale_, str_cget(&name),
+ &surface_));
+ }
+
+ if(!removed && 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", (long unsigned)i));
+ 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 = 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) {
+ /* 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 = 1;
+ removed_windows_self++;
+ }
+ ERR(scad_geometry_ref_put(detect));
+ detect = NULL;
+ ERR(scad_geometry_ref_put(problem));
+ problem = NULL;
+ }
+
+ if(!removed) {
+ ERR(scad_geometry_dilate(surface, center, scale, NULL, &win_surface));
+ ERR(str_printf(&name, "hole_%lu", (long unsigned)i));
+ ERR(scad_geometry_extrude(win_surface, str_cget(&name), dir, &hole));
+ ERR(darray_geometries_push_back(&hole_array, &hole));
+ ERR(scad_geometry_ref_put(hole));
+ hole = NULL;
+ d3_muld(dir, N, 0.024);
+ 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;
+ }
+ ASSERT(hole == NULL);
+ ASSERT(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(surface_) {
+ ERR(scad_geometry_ref_put(surface_));
+ surface_ = NULL;
+ }
+ }
+ removed_count = removed_windows_sz + removed_windows_adj + removed_windows_self;
+ array_n = darray_geometries_size_get(&hole_array);
+ ASSERT(array_n == darray_geometries_size_get(&glass_array));
+ ASSERT(array_n + removed_hor + removed_count == list_n);
+
+ prefix = str_cget(&data_cad->building->name);
+ if(removed_count != 0) {
+ logger_print(city->logger, LOG_WARNING,
+ "Building '%s' has %zu/%zu windows removed:\n",
+ prefix, removed_count, removed_count + array_n);
+ if(removed_windows_sz != 0) {
+ logger_print(city->logger, LOG_WARNING,
+ "- %zu windows removed due to too small size.\n", removed_windows_sz);
+ }
+ 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);
+ }
+
+ 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, 1,
+ glass_list+1, array_n - 1, &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(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 */
+ 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 str dataset_name;
+ int name_initialized = 0;
+ struct dataset_cmode_2* data;
+ static struct construction_mode_functors functors_1 = {
+ &init_cmode_2,
+ &release_cmode_2,
+ &build_cad_cmode_2,
+ &build_footprint_cmode_2,
+ &save_ground_connection_triangles_1,
+ &export_stl_cmode_2,
+ &release_cad_cmode_2
+ };
+ struct mem_allocator* allocator;
+ struct logger* logger;
+ int has_external_insulation;
+ (void) parsed_data;
+
+ if(!building || !city || !parsed_data || !catalog || !lower || !upper) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ allocator = city->allocator;
+ logger = city->logger;
+ building->construction_mode = mode_2;
+ building->functors = &functors_1;
+
+ ERR(init_building_base(building, city, parsed_data));
+
+ str_init(allocator, &dataset_name);
+ name_initialized = 1;
+ ERR(str_set(&dataset_name, parsed_data->dataset_name));
+ building->data = htable_dataset_cmode_2_find(&catalog->catalog_2, &dataset_name);
+ if(building->data == NULL) {
+ ERR(logger_print(logger, LOG_ERROR,
+ "Unknown dataset name: '%s' used by building '%s'.\n",
+ str_cget(&dataset_name), str_cget(&building->name)));
+ res = RES_BAD_ARG;
+ goto error;
+ }
+ data = (struct dataset_cmode_2 *)building->data;
+ has_external_insulation = (data->external_insulation_thickness != 0);
+ ERR(str_set(&building->external_layer_name,
+ (has_external_insulation ? "external_insulation" : "walls")));
+
+exit:
+ if(name_initialized) str_release(&dataset_name);
+ 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->height;
+ double depth = 0;
+ struct dataset_cmode_2* data = (struct dataset_cmode_2 *)building->data;
+ struct data_cad_cmode_2* data_cad = NULL;
+ 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;
+ 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;
+
+ logger_print(logger, LOG_OUTPUT,
+ "Building '%s' construction mode 1, with %lu levels.\n",
+ name, 1 + data->inter_floor_count);
+
+ data_cad = MEM_CALLOC(allocator, 1, sizeof(struct data_cad_cmode_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(data->inter_floor_count > 0) {
+ ERR(build_inter_floor(building, &error_msg_printed, height, data,
+ overlapping_intersector, &polygons, current_cad, &data_cad->intermediate_floor));
+ }
+
+ if(data->external_insulation_thickness> 0) {
+ 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(data->attic_height > 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 vertex_denoiser* denoiser;
+
+ if(!cad) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ city = data_cad->building->city;
+ allocator = city->allocator;
+ denoiser = city->denoiser;
+
+ 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));
+ }
+
+ /* floor export */
+ ERR(stl_export_denoised_geometry(denoiser, data_cad->floor,
+ Scad_force_normals_outward, binary));
+
+ /* roof export */
+ ERR(stl_export_denoised_geometry(denoiser, data_cad->roof,
+ Scad_force_normals_outward, binary));
+
+ /* foundation export */
+ if(data_cad->foundation) {
+ ERR(stl_export_denoised_geometry(denoiser, data_cad->foundation,
+ Scad_force_normals_outward, binary));
+ }
+
+ /* glass export */
+ if(data_cad->glazing) {
+ ERR(stl_export_denoised_geometry(denoiser, data_cad->glazing,
+ Scad_force_normals_outward, binary));
+ }
+
+ /* intermediate floor export*/
+ if(data_cad->intermediate_floor) {
+ ERR(stl_export_denoised_geometry(denoiser, data_cad->intermediate_floor,
+ Scad_force_normals_outward, binary));
+ }
+
+ /* internal insulation export*/
+ if(data_cad->internal_insulation) {
+ ERR(stl_export_denoised_geometry(denoiser, data_cad->internal_insulation,
+ Scad_force_normals_outward, binary));
+ }
+
+ /* roof insulation export*/
+ if(data_cad->roof_insulation) {
+ ERR(stl_export_denoised_geometry(denoiser, data_cad->roof_insulation,
+ Scad_force_normals_outward, binary));
+ }
+
+ /* floor insulation export*/
+ if(data_cad->floor_insulation) {
+ ERR(stl_export_denoised_geometry(denoiser, data_cad->floor_insulation,
+ Scad_force_normals_outward, binary));
+ }
+
+ /* attic cavity export*/
+ if(data_cad->attic_cavity) {
+ ERR(stl_export_denoised_geometry(denoiser, data_cad->attic_cavity,
+ Scad_force_normals_outward, binary));
+ }
+
+ /* habitable cavity export*/
+ ERR(stl_export_denoised_geometry(denoiser, data_cad->habitable_cavity,
+ Scad_force_normals_outward, binary));
+
+ /* crawlspace cavity export*/
+ if(data_cad->crawlspace_cavity) {
+ ERR(stl_export_denoised_geometry(denoiser, data_cad->crawlspace_cavity,
+ Scad_force_normals_outward, binary));
+ }
+
+ /* boundary export*/
+ for(i = 0; i < darray_geometries_size_get(&data_cad->boundary); i++) {
+ struct scad_geometry* b = darray_geometries_data_get(&data_cad->boundary)[i];
+ ERR(stl_export_denoised_geometry(denoiser, b, Scad_keep_normals_unchanged,
+ binary));
+ }
+
+ /* connections export*/
+ for(i = 0; i < darray_geometries_size_get(&data_cad->connection); i++) {
+ struct scad_geometry* c = darray_geometries_data_get(&data_cad->connection)[i];
+ ERR(stl_export_denoised_geometry(denoiser, c, Scad_keep_normals_unchanged,
+ binary));
+ }
+
+ /* ground/building connection export*/
+ ERR(stl_export_denoised_geometry(denoiser, data_cad->ground_connection,
+ Scad_keep_normals_unchanged, binary));
+
+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));
+ }
+ 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,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 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 {
+ size_t inter_floor_count; /* can be 0 */
+ 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 attic_height; /* can be 0 */
+ double glass_ratio; /* in [0, 1] */
+};
+
+#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,124 @@
+/* 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;
+ size_t inter_floor_count; /* can be 0 */
+ 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 attic_height; /* can be 0 */
+ double glass_ratio; /* in [0, 1] */
+};
+
+/********************************************************/
+/* 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_INT("inter_floor_count", CYAML_FLAG_DEFAULT,
+ struct parsed_dataset_cmode_2, inter_floor_count),
+ 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_FLOAT("attic_height", CYAML_FLAG_DEFAULT,
+ struct parsed_dataset_cmode_2, attic_height),
+ CYAML_FIELD_FLOAT("glass_ratio", CYAML_FLAG_DEFAULT,
+ struct parsed_dataset_cmode_2, glass_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 {