city_generator2

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

commit 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:
MMakefile | 1+
Msrc/cg_building.h | 1+
Msrc/cg_catalog.c | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/cg_catalog.h | 2++
Msrc/cg_catalog_parsing.c | 6++++++
Msrc/cg_city.c | 5+++++
Asrc/cg_construction_mode_2.c | 2085+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/cg_construction_mode_2.h | 133+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/cg_construction_mode_2_parsing_schemas.h | 124+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/cg_construction_modes_parsing_schemas.h | 4+++-
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], &center_n)); + ASSERT(center_n == 1); + ERR(scad_geometry_get_centerofmass(list[i], center)); + ERR(str_printf(&name, "surface_%lu", (long unsigned)i)); + ERR(scad_geometry_normal(list[i], center, N, str_cget(&name), &surface)); + + if(N[2] != 0) { + 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 {