cg_construction_mode_2.c (76737B)
1 /* Copyright (C) 2022 Université de Pau et des Pays de l'Adour UPPA 2 * Copyright (C) 2022 CNRS 3 * Copyright (C) 2022 Sorbonne Université 4 * Copyright (C) 2022 Université Paul Sabatier 5 * Copyright (C) 2022 |Meso|Star> (contact@meso-star.com) 6 * 7 * This program is free software: you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation, either version 3 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program. If not, see <http://www.gnu.org/licenses/>. */ 19 20 #include "cg.h" 21 #include "cg_types.h" 22 #include "cg_default.h" 23 #include "cg_building.h" 24 #include "cg_catalog.h" 25 #include "cg_city.h" 26 #include "cg_city_parsing_schemas.h" 27 #include "cg_construction_mode.h" 28 #include "cg_construction_mode_2.h" 29 #include "cg_vertex_denoiser.h" 30 #include "cg_stardis_model.h" 31 32 #include <rsys/rsys.h> 33 #include <rsys/str.h> 34 #include <rsys/logger.h> 35 #include <rsys/hash_table.h> 36 #include <rsys/double3.h> 37 #include <rsys/clock_time.h> 38 39 #include <star/scad.h> 40 #include <star/scpr.h> 41 42 static res_T 43 build_footprint 44 (struct scpr_polygon* pg, 45 const double z, 46 struct scad_geometry** footprint) 47 { 48 res_T res = RES_OK; 49 size_t nverts = 0; 50 51 ASSERT(pg && footprint); 52 53 ERR(scpr_polygon_get_vertices_count(pg, 0, &nverts)); 54 ERR(scad_add_polygon(NULL, get_position_pg, pg, z, nverts, footprint)); 55 56 exit: 57 return res; 58 error: 59 goto exit; 60 } 61 62 /* Return a polygon with the given offset. 63 * Get a reference: do put a reference on the result! */ 64 static res_T 65 do_offset 66 (struct building* building, 67 struct htable_polygons* polygons, 68 struct scpr_intersector* overlapping_intersector, 69 int *error_msg_printed, 70 const double offset, 71 struct scpr_polygon** p_out) 72 { 73 res_T res = RES_OK; 74 struct scpr_polygon** ptr_p; 75 76 ASSERT(building && polygons && overlapping_intersector 77 && error_msg_printed && p_out); 78 79 ptr_p = htable_polygons_find(polygons, &offset); 80 if(ptr_p) { 81 *p_out = *ptr_p; 82 ERR(scpr_polygon_ref_get(*p_out)); 83 } else { 84 size_t c; 85 ERR(scpr_polygon_create_copy(building->city->scpr, building->pg, p_out)); 86 ERR(scpr_offset_polygon(*p_out, offset, SCPR_JOIN_MITER)); 87 ERR(scpr_polygon_get_components_count(*p_out, &c)); 88 if(c != 1) { 89 ASSERT(offset < 0); 90 logger_print(building->city->logger, 91 (building->city->keep_running_on_errors ? LOG_WARNING : LOG_ERROR), 92 "Building '%s' shape is not compatible with wall thickness.\n", 93 str_cget(&building->name)); 94 building->event_flags |= BUILDING_REMOVED; 95 *error_msg_printed = 1; 96 res = RES_BAD_ARG; 97 goto error; 98 } 99 ERR(scpr_intersector_register_polygon(overlapping_intersector, *p_out)); 100 ERR(htable_polygons_set(polygons, &offset, p_out)); 101 } 102 103 exit: 104 return res; 105 error: 106 if(!ptr_p) SCPR(polygon_ref_put(*p_out)); /* Created here */ 107 *p_out = NULL; 108 goto exit; 109 } 110 111 static res_T 112 build_floor 113 (struct building* building, 114 int *error_msg_printed, 115 const struct dataset_cmode_2* data, 116 struct scpr_intersector* overlapping_intersector, 117 struct htable_polygons* polygons, 118 struct darray_geometries* current_cad, 119 struct scad_geometry** floor) 120 { 121 res_T res = RES_OK; 122 double e_wall = data->wall_thickness; 123 double e_insulation = data->external_insulation_thickness; 124 double e_floor = data->floor_thickness; 125 double offset = 0; 126 struct scpr_polygon* pg_int = NULL; 127 struct scad_geometry* footprint = NULL; 128 double d[3] = {0, 0, 0}; 129 char* floorname = NULL; 130 struct str name; 131 const char* prefix; 132 133 ASSERT(building && error_msg_printed && data && floor); 134 135 prefix = str_cget(&building->name); 136 str_init(building->city->allocator, &name); 137 ERR(str_set(&name, prefix)); 138 ERR(str_append(&name, "_S_floor")); 139 floorname = str_get(&name); 140 141 offset = -(e_wall + e_insulation); 142 ERR(do_offset(building, polygons, overlapping_intersector, error_msg_printed, 143 offset, &pg_int)); 144 145 /*footprint*/ 146 ERR(build_footprint(pg_int, 0, &footprint)); 147 148 d[2] = -e_floor; 149 ERR(scad_geometry_extrude(footprint, floorname, d, floor)); 150 ERR(darray_geometries_push_back(current_cad, floor)); 151 152 exit: 153 if(footprint) SCAD(geometry_ref_put(footprint)); 154 str_release(&name); 155 if(pg_int) SCPR(polygon_ref_put(pg_int)); 156 return res; 157 error: 158 goto exit; 159 } 160 161 static res_T 162 build_wall 163 (struct building* building, 164 int *error_msg_printed, 165 const int is_foundation, 166 const char* suffix, 167 const double height, 168 const struct dataset_cmode_2* data, 169 struct scpr_intersector* overlapping_intersector, 170 struct htable_polygons* polygons, 171 struct darray_geometries* current_cad, 172 struct scad_geometry** wall) 173 { 174 res_T res = RES_OK; 175 double e_wall = data->wall_thickness; 176 double e_insulation = data->external_insulation_thickness; 177 double offset = 0; 178 struct scpr_polygon* pg_int = NULL; 179 struct scpr_polygon* pg_ext = NULL; 180 struct scad_geometry* footprint = NULL; 181 struct scad_geometry* footprint_int = NULL; 182 struct scad_geometry* footprint_ext = NULL; 183 double d[3] = {0, 0, 0}; 184 char* wallname = NULL; 185 struct str name; 186 const char* prefix; 187 188 ASSERT(building && error_msg_printed && data && wall); 189 190 prefix = str_cget(&building->name); 191 str_init(building->city->allocator, &name); 192 ERR(str_set(&name, prefix)); 193 if(suffix) { 194 ERR(str_append(&name, "_")); 195 ERR(str_append(&name, suffix)); 196 } 197 wallname = str_get(&name); 198 199 if(is_foundation) { 200 offset = -(e_insulation + 0.1*e_wall); 201 } else { 202 offset = -e_insulation; 203 } 204 ERR(do_offset(building, polygons, overlapping_intersector, error_msg_printed, 205 offset, &pg_ext)); 206 207 offset = -(e_wall + e_insulation); 208 ERR(do_offset(building, polygons, overlapping_intersector, error_msg_printed, 209 offset, &pg_int)); 210 211 /* wall footprint*/ 212 ERR(build_footprint(pg_int, 0, &footprint_int)); 213 ERR(build_footprint(pg_ext, 0, &footprint_ext)); 214 ERR(scad_cut_geometries(NULL, &footprint_ext, 1, &footprint_int, 1, &footprint)); 215 216 d[2] = height; 217 ERR(scad_geometry_extrude(footprint, wallname, d, wall)); 218 ERR(darray_geometries_push_back(current_cad, wall)); 219 220 exit: 221 if(footprint) SCAD(geometry_ref_put(footprint)); 222 if(footprint_int) SCAD(geometry_ref_put(footprint_int)); 223 if(footprint_ext) SCAD(geometry_ref_put(footprint_ext)); 224 str_release(&name); 225 if(pg_ext) SCPR(polygon_ref_put(pg_ext)); 226 if(pg_int) SCPR(polygon_ref_put(pg_int)); 227 return res; 228 error: 229 goto exit; 230 } 231 232 static res_T 233 build_int_insulation 234 (struct building* building, 235 int *error_msg_printed, 236 const double height, 237 const struct dataset_cmode_2* data, 238 struct scad_geometry* inter_floor, 239 struct scpr_intersector* overlapping_intersector, 240 struct htable_polygons* polygons, 241 struct darray_geometries* current_cad, 242 struct scad_geometry** insulation) 243 { 244 res_T res = RES_OK; 245 double e_wall = data->wall_thickness; 246 double e_roof = data->roof_thickness; 247 double e_roof_insulation = data->roof_insulation_thickness; 248 size_t levels_n = data->has_attic ? 249 darray_double_size_get(&building->levels_height) - 1 : 250 darray_double_size_get(&building->levels_height); 251 const double* levels = darray_double_cdata_get(&building->levels_height); 252 double e_attic = data->has_attic ? levels[levels_n] : 0; 253 double e_ext_insulation = data->external_insulation_thickness; 254 double e_int_insulation = data->internal_insulation_thickness; 255 double offset = 0; 256 struct scpr_polygon* pg_int = NULL; 257 struct scpr_polygon* pg_ext = NULL; 258 struct scad_geometry* footprint = NULL; 259 struct scad_geometry* footprint_int = NULL; 260 struct scad_geometry* footprint_ext = NULL; 261 struct scad_geometry* geom = NULL; 262 double d[3] = {0, 0, 0}; 263 char* insulationname = NULL; 264 struct str name; 265 const char* prefix; 266 267 ASSERT(building && error_msg_printed && data && insulation); 268 269 prefix = str_cget(&building->name); 270 str_init(building->city->allocator, &name); 271 ERR(str_set(&name, prefix)); 272 ERR(str_append(&name, "_S_internal_insulation")); 273 insulationname = str_get(&name); 274 275 offset = -(e_ext_insulation + e_wall); 276 ERR(do_offset(building, polygons, overlapping_intersector, error_msg_printed, 277 offset, &pg_ext)); 278 279 offset = -(e_ext_insulation + e_wall + e_int_insulation); 280 ERR(do_offset(building, polygons, overlapping_intersector, error_msg_printed, 281 offset, &pg_int)); 282 283 /* insulation footprint */ 284 ERR(build_footprint(pg_int, 0, &footprint_int)); 285 ERR(build_footprint(pg_ext, 0, &footprint_ext)); 286 ERR(scad_cut_geometries(NULL, &footprint_ext, 1, &footprint_int, 1, &footprint)); 287 288 d[2] = height - e_roof - e_attic - e_roof_insulation; 289 ERR(scad_geometry_extrude(footprint, NULL, d, &geom)); 290 291 if(inter_floor) { 292 ERR(scad_cut_geometries(insulationname, &geom, 1, &inter_floor, 1, insulation)); 293 } else { 294 ERR(scad_geometry_copy(geom, insulationname, insulation)); 295 } 296 ERR(darray_geometries_push_back(current_cad, insulation)); 297 298 exit: 299 if(footprint) SCAD(geometry_ref_put(footprint)); 300 if(footprint_int) SCAD(geometry_ref_put(footprint_int)); 301 if(footprint_ext) SCAD(geometry_ref_put(footprint_ext)); 302 if(geom) SCAD(geometry_ref_put(geom)); 303 str_release(&name); 304 if(pg_ext) SCPR(polygon_ref_put(pg_ext)); 305 if(pg_int) SCPR(polygon_ref_put(pg_int)); 306 return res; 307 error: 308 goto exit; 309 } 310 311 static res_T 312 build_roof 313 (struct building* building, 314 int *error_msg_printed, 315 const double height, 316 const struct dataset_cmode_2* data, 317 struct scpr_intersector* overlapping_intersector, 318 struct htable_polygons* polygons, 319 struct darray_geometries* current_cad, 320 struct scad_geometry** roof) 321 { 322 res_T res = RES_OK; 323 double e_wall = data->wall_thickness; 324 double e_insulation = data->external_insulation_thickness; 325 double e_roof = data->roof_thickness; 326 double offset = 0; 327 double z_roof = 0; 328 struct scpr_polygon* pg_int = NULL; 329 struct scad_geometry* footprint = NULL; 330 double d[3] = {0, 0, 0}; 331 char* roofname = NULL; 332 struct str name; 333 const char* prefix; 334 335 ASSERT(building && error_msg_printed && data && roof); 336 337 prefix = str_cget(&building->name); 338 str_init(building->city->allocator, &name); 339 ERR(str_set(&name, prefix)); 340 ERR(str_append(&name, "_S_roof")); 341 roofname = str_get(&name); 342 343 offset = -(e_wall + e_insulation); 344 ERR(do_offset(building, polygons, overlapping_intersector, error_msg_printed, 345 offset, &pg_int)); 346 347 /*footprint*/ 348 z_roof = height - e_roof; 349 ERR(build_footprint(pg_int, z_roof, &footprint)); 350 351 d[2] = e_roof; 352 ERR(scad_geometry_extrude(footprint, roofname, d, roof)); 353 ERR(darray_geometries_push_back(current_cad, roof)); 354 355 exit: 356 if(footprint) SCAD(geometry_ref_put(footprint)); 357 str_release(&name); 358 if(pg_int) SCPR(polygon_ref_put(pg_int)); 359 return res; 360 error: 361 goto exit; 362 } 363 364 static res_T 365 build_roof_insulation 366 (struct building* building, 367 int *error_msg_printed, 368 const double height, 369 const struct dataset_cmode_2* data, 370 struct scpr_intersector* overlapping_intersector, 371 struct htable_polygons* polygons, 372 struct darray_geometries* current_cad, 373 struct scad_geometry** insulation) 374 { 375 res_T res = RES_OK; 376 double e_wall = data->wall_thickness; 377 double e_insulation = data->external_insulation_thickness; 378 double e_roof = data->roof_thickness; 379 size_t levels_n = data->has_attic ? 380 darray_double_size_get(&building->levels_height) - 1 : 381 darray_double_size_get(&building->levels_height); 382 const double* levels = darray_double_cdata_get(&building->levels_height); 383 double e_attic = data->has_attic ? levels[levels_n] : 0; 384 double e_roof_insulation = data->roof_insulation_thickness; 385 double offset = 0; 386 double z_insulation = 0; 387 struct scpr_polygon* pg_int = NULL; 388 struct scad_geometry* footprint = NULL; 389 double d[3] = {0, 0, 0}; 390 char* insulationname = NULL; 391 struct str name; 392 const char* prefix; 393 394 ASSERT(building && error_msg_printed && data && insulation); 395 396 prefix = str_cget(&building->name); 397 str_init(building->city->allocator, &name); 398 ERR(str_set(&name, prefix)); 399 ERR(str_append(&name, "_S_roof_insulation")); 400 insulationname = str_get(&name); 401 402 offset = -(e_wall + e_insulation); 403 ERR(do_offset(building, polygons, overlapping_intersector, error_msg_printed, 404 offset, &pg_int)); 405 406 /*footprint*/ 407 z_insulation = height - e_roof - e_attic - e_roof_insulation; 408 ERR(build_footprint(pg_int, z_insulation, &footprint)); 409 410 d[2] = e_roof_insulation; 411 ERR(scad_geometry_extrude(footprint, insulationname, d, insulation)); 412 ERR(darray_geometries_push_back(current_cad, insulation)); 413 414 exit: 415 if(footprint) SCAD(geometry_ref_put(footprint)); 416 str_release(&name); 417 if(pg_int) SCPR(polygon_ref_put(pg_int)); 418 return res; 419 error: 420 goto exit; 421 } 422 423 static res_T 424 build_floor_insulation 425 (struct building* building, 426 int *error_msg_printed, 427 const struct dataset_cmode_2* data, 428 struct scpr_intersector* overlapping_intersector, 429 struct htable_polygons* polygons, 430 struct darray_geometries* current_cad, 431 struct scad_geometry** insulation) 432 { 433 res_T res = RES_OK; 434 double e_wall = data->wall_thickness; 435 double e_insulation = data->external_insulation_thickness; 436 double e_floor = data->floor_thickness; 437 double e_floor_insulation = data->floor_insulation_thickness; 438 double offset = 0; 439 double z_insulation = 0; 440 struct scpr_polygon* pg_int = NULL; 441 struct scad_geometry* footprint = NULL; 442 double d[3] = {0, 0, 0}; 443 char* insulationname = NULL; 444 struct str name; 445 const char* prefix; 446 447 ASSERT(building && error_msg_printed && data && insulation); 448 449 prefix = str_cget(&building->name); 450 str_init(building->city->allocator, &name); 451 ERR(str_set(&name, prefix)); 452 ERR(str_append(&name, "_S_floor_insulation")); 453 insulationname = str_get(&name); 454 455 offset = -(e_wall + e_insulation); 456 ERR(do_offset(building, polygons, overlapping_intersector, error_msg_printed, 457 offset, &pg_int)); 458 459 /*footprint*/ 460 z_insulation = - e_floor - e_floor_insulation; 461 ERR(build_footprint(pg_int, z_insulation, &footprint)); 462 463 d[2] = e_floor_insulation; 464 ERR(scad_geometry_extrude(footprint, insulationname, d, insulation)); 465 ERR(darray_geometries_push_back(current_cad, insulation)); 466 467 exit: 468 if(footprint) SCAD(geometry_ref_put(footprint)); 469 str_release(&name); 470 if(pg_int) SCPR(polygon_ref_put(pg_int)); 471 return res; 472 error: 473 goto exit; 474 } 475 476 static res_T 477 build_inter_floor 478 (struct building* building, 479 int *error_msg_printed, 480 const struct dataset_cmode_2* data, 481 struct scpr_intersector* overlapping_intersector, 482 struct htable_polygons* polygons, 483 struct darray_geometries* current_cad, 484 struct scad_geometry** inter_floor) 485 { 486 res_T res = RES_OK; 487 size_t i = 0; 488 size_t levels_n = data->has_attic ? 489 darray_double_size_get(&building->levels_height) - 1 : 490 darray_double_size_get(&building->levels_height); 491 const double* levels = darray_double_cdata_get(&building->levels_height); 492 double e_floor = data->inter_floor_thickness; 493 double e_wall = data->wall_thickness; 494 double e_insulation = data->external_insulation_thickness; 495 double offset = 0; 496 double z_floor = 0; 497 struct scpr_polygon* pg_int = NULL; 498 size_t nverts = 0; 499 struct scad_geometry** floor_list = NULL; 500 struct darray_geometries floor_array; 501 struct scad_geometry *footprint = NULL, *floor = NULL; 502 double d[3] = {0, 0, 0}; 503 char* floorname = NULL; 504 struct str name; 505 const char* prefix; 506 507 ASSERT(building && error_msg_printed && data && inter_floor); 508 509 darray_geometries_init(building->city->allocator, &floor_array); 510 511 prefix = str_cget(&building->name); 512 str_init(building->city->allocator, &name); 513 ERR(str_set(&name, prefix)); 514 ERR(str_append(&name, "_S_intermediate_floors")); 515 floorname = str_get(&name); 516 517 offset = -(e_wall + e_insulation); 518 ERR(do_offset(building, polygons, overlapping_intersector, error_msg_printed, 519 offset, &pg_int)); 520 ERR(scpr_polygon_get_vertices_count(pg_int, 0, &nverts)); 521 522 z_floor = levels[0] - e_floor; 523 d[2] = e_floor; 524 for(i = 1; i < levels_n; i++) { 525 ERR(scad_add_polygon(NULL, get_position_pg, pg_int, z_floor, nverts, 526 &footprint)); 527 ERR(scad_geometry_extrude(footprint, NULL, d, &floor)); 528 ERR(scad_geometry_ref_put(footprint)); 529 footprint = NULL; /* Avoid double free */ 530 ERR(darray_geometries_push_back(&floor_array, &floor)); 531 ERR(scad_geometry_ref_put(floor)); 532 floor = NULL; /* Avoid double free */ 533 z_floor += levels[i]; 534 } 535 ASSERT(darray_geometries_size_get(&floor_array) == levels_n - 1); 536 537 floor_list = darray_geometries_data_get(&floor_array); 538 ERR(scad_fuse_geometries(floorname, floor_list, levels_n - 1, 539 floor_list, levels_n - 1, inter_floor)); 540 ERR(darray_geometries_push_back(current_cad, inter_floor)); 541 542 exit: 543 str_release(&name); 544 if(footprint) SCAD(geometry_ref_put(footprint)); 545 if(floor) SCAD(geometry_ref_put(floor)); 546 if(floor_list) darray_geometries_release(&floor_array); 547 if(pg_int) SCPR(polygon_ref_put(pg_int)); 548 return res; 549 error: 550 goto exit; 551 } 552 553 static res_T 554 build_ext_insulation 555 (struct building* building, 556 int *error_msg_printed, 557 const double height, 558 const struct dataset_cmode_2* data, 559 struct scpr_intersector* overlapping_intersector, 560 struct htable_polygons* polygons, 561 struct darray_geometries* current_cad, 562 struct scad_geometry** insulation) 563 { 564 res_T res = RES_OK; 565 double e_insulation = data->external_insulation_thickness; 566 double offset = 0; 567 struct scpr_polygon* pg_int = NULL; 568 struct scpr_polygon* pg_ext = NULL; 569 struct scpr_polygon** ptr_p; 570 struct scad_geometry* footprint = NULL; 571 struct scad_geometry* footprint_int = NULL; 572 struct scad_geometry* footprint_ext = NULL; 573 double d[3] = {0, 0, 0}; 574 char* insulationname = NULL; 575 struct str name; 576 const char* prefix; 577 578 ASSERT(building && data && polygons && insulation); 579 580 prefix = str_cget(&building->name); 581 str_init(building->city->allocator, &name); 582 ERR(str_set(&name, prefix)); 583 ERR(str_append(&name, "_S_external_insulation")); 584 insulationname = str_get(&name); 585 586 offset = -e_insulation; 587 ERR(do_offset(building, polygons, overlapping_intersector, error_msg_printed, 588 offset, &pg_int)); 589 590 offset = 0; 591 ptr_p = htable_polygons_find(polygons, &offset); 592 ASSERT(ptr_p); /* Offset 0 is the first to be inserted in polygons */ 593 pg_ext = *ptr_p; 594 595 /*insulation footprint*/ 596 ERR(build_footprint(pg_int, 0, &footprint_int)); 597 ERR(build_footprint(pg_ext, 0, &footprint_ext)); 598 ERR(scad_cut_geometries(NULL, &footprint_ext, 1, &footprint_int, 1, &footprint)); 599 600 d[2] = height; 601 ERR(scad_geometry_extrude(footprint, insulationname, d, insulation)); 602 ERR(darray_geometries_push_back(current_cad, insulation)); 603 604 exit: 605 if(footprint) SCAD(geometry_ref_put(footprint)); 606 if(footprint_int) SCAD(geometry_ref_put(footprint_int)); 607 if(footprint_ext) SCAD(geometry_ref_put(footprint_ext)); 608 str_release(&name); 609 if(pg_int) SCPR(polygon_ref_put(pg_int)); 610 return res; 611 error: 612 goto exit; 613 } 614 615 static res_T 616 build_crawlspace 617 (struct building* building, 618 int *error_msg_printed, 619 const struct dataset_cmode_2* data, 620 struct scpr_intersector* overlapping_intersector, 621 struct htable_polygons* polygons, 622 struct darray_geometries* current_cad, 623 struct scad_geometry** crawlspace) 624 { 625 res_T res = RES_OK; 626 double e_wall = data->wall_thickness; 627 double e_insulation = data->external_insulation_thickness; 628 double e_crawl = data->crawl_height; 629 double e_floor = data->floor_thickness; 630 double e_floor_insulation = data->floor_insulation_thickness; 631 double offset = 0; 632 double z_crawl= 0; 633 struct scpr_polygon* pg_int = NULL; 634 struct scad_geometry* footprint = NULL; 635 double d[3] = {0, 0, 0}; 636 char* crawlname = NULL; 637 struct str name; 638 const char* prefix; 639 640 ASSERT(building && error_msg_printed && data && crawlspace); 641 642 prefix = str_cget(&building->name); 643 str_init(building->city->allocator, &name); 644 ERR(str_set(&name, prefix)); 645 ERR(str_append(&name, "_F_crawlspace")); 646 crawlname = str_get(&name); 647 648 offset = -(e_wall + e_insulation); 649 ERR(do_offset(building, polygons, overlapping_intersector, error_msg_printed, 650 offset, &pg_int)); 651 652 /*footprint*/ 653 z_crawl = - e_floor - e_floor_insulation - e_crawl; 654 ERR(build_footprint(pg_int, z_crawl, &footprint)); 655 656 d[2] = e_crawl; 657 ERR(scad_geometry_extrude(footprint, crawlname, d, crawlspace)); 658 ERR(darray_geometries_push_back(current_cad, crawlspace)); 659 660 exit: 661 if(footprint) SCAD(geometry_ref_put(footprint)); 662 str_release(&name); 663 if(pg_int) SCPR(polygon_ref_put(pg_int)); 664 return res; 665 error: 666 goto exit; 667 } 668 669 static res_T 670 build_habitable 671 (struct building* building, 672 int *error_msg_printed, 673 const double height, 674 const struct dataset_cmode_2* data, 675 struct scad_geometry* floor, 676 struct scpr_intersector* overlapping_intersector, 677 struct htable_polygons* polygons, 678 struct darray_geometries* current_cad, 679 struct scad_geometry** cavity) 680 { 681 res_T res = RES_OK; 682 double e_wall = data->wall_thickness; 683 double e_ext_insulation = data->external_insulation_thickness; 684 double e_int_insulation = data->internal_insulation_thickness; 685 double e_roof = data->roof_thickness; 686 double e_roof_insulation = data->roof_insulation_thickness; 687 size_t levels_n = data->has_attic ? 688 darray_double_size_get(&building->levels_height) - 1 : 689 darray_double_size_get(&building->levels_height); 690 const double* levels = darray_double_cdata_get(&building->levels_height); 691 double e_attic = data->has_attic ? levels[levels_n] : 0; 692 double offset = 0; 693 struct scpr_polygon* pg_int = NULL; 694 struct scad_geometry* footprint = NULL; 695 struct scad_geometry* geom = NULL; 696 double d[3] = {0, 0, 0}; 697 char* cavityname = NULL; 698 struct str name; 699 const char* prefix; 700 701 ASSERT(building && error_msg_printed && data && cavity); 702 703 prefix = str_cget(&building->name); 704 str_init(building->city->allocator, &name); 705 ERR(str_set(&name, prefix)); 706 ERR(str_append(&name, "_F_levels")); 707 cavityname = str_get(&name); 708 709 offset = -(e_wall + e_ext_insulation + e_int_insulation); 710 ERR(do_offset(building, polygons, overlapping_intersector, error_msg_printed, 711 offset, &pg_int)); 712 ERR(build_footprint(pg_int, 0, &footprint)); 713 714 d[2] = height - e_roof - e_attic - e_roof_insulation; 715 ERR(scad_geometry_extrude(footprint, NULL, d, &geom)); 716 if(floor) { 717 ERR(scad_cut_geometries(cavityname, &geom, 1, &floor, 1, cavity)); 718 } else { 719 ERR(scad_geometry_copy(geom, cavityname, cavity)); 720 } 721 ERR(darray_geometries_push_back(current_cad, cavity)); 722 723 exit: 724 if(footprint) SCAD(geometry_ref_put(footprint)); 725 if(geom) SCAD(geometry_ref_put(geom)); 726 str_release(&name); 727 if(pg_int) SCPR(polygon_ref_put(pg_int)); 728 return res; 729 error: 730 goto exit; 731 } 732 733 static res_T 734 build_attic 735 (struct building* building, 736 int *error_msg_printed, 737 const double height, 738 const struct dataset_cmode_2* data, 739 struct scpr_intersector* overlapping_intersector, 740 struct htable_polygons* polygons, 741 struct darray_geometries* current_cad, 742 struct scad_geometry** attic) 743 { 744 res_T res = RES_OK; 745 double e_wall = data->wall_thickness; 746 double e_insulation = data->external_insulation_thickness; 747 double e_roof = data->roof_thickness; 748 size_t levels_n = data->has_attic ? 749 darray_double_size_get(&building->levels_height) - 1 : 750 darray_double_size_get(&building->levels_height); 751 const double* levels = darray_double_cdata_get(&building->levels_height); 752 double e_attic = data->has_attic ? levels[levels_n] : 0; 753 double offset = 0; 754 double z_attic = 0; 755 struct scpr_polygon* pg_int = NULL; 756 struct scad_geometry* footprint = NULL; 757 double d[3] = {0, 0, 0}; 758 char* atticname = NULL; 759 struct str name; 760 const char* prefix; 761 762 ASSERT(building && error_msg_printed && data && attic); 763 764 prefix = str_cget(&building->name); 765 str_init(building->city->allocator, &name); 766 ERR(str_set(&name, prefix)); 767 ERR(str_append(&name, "_F_attic")); 768 atticname = str_get(&name); 769 770 offset = -(e_wall + e_insulation); 771 ERR(do_offset(building, polygons, overlapping_intersector, error_msg_printed, 772 offset, &pg_int)); 773 774 /*footprint*/ 775 z_attic = height - e_roof - e_attic; 776 ERR(build_footprint(pg_int, z_attic, &footprint)); 777 778 d[2] = e_attic; 779 ERR(scad_geometry_extrude(footprint, atticname, d, attic)); 780 ERR(darray_geometries_push_back(current_cad, attic)); 781 782 exit: 783 if(footprint) SCAD(geometry_ref_put(footprint)); 784 str_release(&name); 785 if(pg_int) SCPR(polygon_ref_put(pg_int)); 786 return res; 787 error: 788 goto exit; 789 } 790 791 static res_T 792 build_windows 793 (const struct dataset_cmode_2* data, 794 struct data_cad_cmode_2* data_cad, 795 struct darray_geometries* current_cad, 796 struct darray_adjoining_data* adjoining_data) 797 { 798 res_T res = RES_OK; 799 size_t i, j, adjoining_n, removed_count; 800 size_t removed_windows_adj = 0, removed_windows_self = 0; 801 size_t removed_wall_sz = 0, missed_windows_ratio = 0; 802 size_t list_n = 0, array_n; 803 const char* prefix; 804 double N[3], dir[3], scale[3]; 805 double effective_ratio, total_wall_surface = 0, total_windows_surface = 0; 806 int meet_effective_ratio; 807 struct scad_geometry* surface = NULL; 808 struct scad_geometry* win_surface = NULL; 809 struct scad_geometry* surface_ = NULL; 810 struct scad_geometry* scaled = NULL; 811 struct scad_geometry* scaled_ = NULL; 812 struct scad_geometry* hole = NULL; 813 struct scad_geometry* detect = NULL; 814 struct scad_geometry** hole_list = NULL; 815 struct darray_geometries hole_array; 816 struct scad_geometry* geom = NULL; 817 struct scad_geometry* hole_adjoining_intersect = NULL; 818 struct scad_geometry* bcavity = NULL; 819 struct scad_geometry** list = NULL; 820 struct scad_geometry** adj_list = NULL; 821 struct scad_geometry* glass = NULL; 822 struct scad_geometry** glass_list = NULL; 823 struct darray_geometries glass_array; 824 struct scad_geometry* env = NULL; 825 struct scad_geometry* benv = NULL; 826 struct scad_geometry* problem = NULL; 827 double wall_height; 828 struct str name; 829 struct adjoining_data* adj; 830 struct city* city; 831 struct mem_allocator* allocator; 832 833 ASSERT(data && data_cad && adjoining_data); 834 835 city = data_cad->building->city; 836 allocator = city->allocator; 837 adjoining_n = darray_adjoining_data_size_get(adjoining_data); 838 adj = darray_adjoining_data_data_get(adjoining_data); 839 darray_geometries_init(allocator, &hole_array); 840 darray_geometries_init(allocator, &glass_array); 841 str_init(allocator, &name); 842 843 /* windows are build from the vertical faces of habitable cavities */ 844 ERR(scad_geometry_boundary("cavity_boundary", &data_cad->habitable_cavity, 1, 845 &bcavity)); 846 ERR(scad_geometry_explode(bcavity, "cavity_elt", &list, &list_n)); 847 ERR(scad_geometry_ref_put(bcavity)); 848 bcavity = NULL; 849 850 /* The boundary of the envelop of the building. 851 * To be used for windows validation */ 852 ERR(build_envelop(data_cad->building, &env)); 853 ERR(scad_geometry_boundary("envelop_boundary", &env, 1, &benv)); 854 ERR(scad_geometry_ref_put(env)); 855 env = NULL; 856 857 ERR(darray_geometries_reserve(&hole_array, list_n)); 858 ERR(darray_geometries_reserve(&glass_array, list_n)); 859 for(i = 0; i < list_n; i++) { 860 double hsz, wall_surface, wall_width, center[3]; 861 size_t center_n, count, n; 862 int removed_wall = 0; 863 double mi[3], ma[3]; 864 865 ERR(scad_geometry_get_count(list[i], ¢er_n)); 866 ASSERT(center_n == 1); 867 ERR(scad_geometry_get_centerofmass(list[i], center)); 868 ERR(str_printf(&name, "surface_%lu", (long unsigned)i)); 869 ERR(scad_geometry_normal(list[i], center, N, str_cget(&name), &surface)); 870 871 if(N[2] != 0) { 872 /* keep only vertical face */ 873 removed_wall = 1; 874 } 875 876 if(!removed_wall) { 877 double window_height, expected_total_width_from_ratio; 878 double wc, best_wc, best_wc_score, best_wc_spacing, best_wc_width; 879 d3_normalize(N, N); 880 ERR(scad_geometry_get_mass(list[i], &wall_surface)); 881 total_wall_surface += wall_surface; 882 ERR(scad_geometry_get_bounding_box(list[i], mi, ma)); 883 wall_height = ma[2] - mi[2]; 884 wall_width = wall_surface / wall_height; 885 /* The basic result is: no window */ 886 best_wc = 0; 887 best_wc_score = 0; 888 best_wc_spacing = wall_width; 889 best_wc_width = 0; 890 /* search for the count, width, spacing that best meet expectations */ 891 window_height = data->windows_height_ratio * wall_height; 892 expected_total_width_from_ratio = wall_width * data->glass_ratio; 893 wc = 1; 894 while(wall_width >= 895 wc * data->windows_min_width + (wc + 1) * data->windows_min_spacing) 896 { 897 double score; 898 double max_achievable_width = MMIN(data->windows_max_width, 899 (wall_width - (wc + 1) * data->windows_min_spacing) / wc); 900 if(max_achievable_width * wc >= expected_total_width_from_ratio 901 && data->windows_min_width * wc <= expected_total_width_from_ratio) 902 { 903 /* Expected ratio is achievable! */ 904 score = 1; 905 if(score > best_wc_score) { 906 best_wc = wc; 907 best_wc_score = score; 908 best_wc_width = expected_total_width_from_ratio / wc; 909 best_wc_spacing = (wall_width - wc * best_wc_width) / (wc + 1); 910 } 911 } else { 912 /* Compute the width that is closest to expectations */ 913 double best_width_for_wc = 914 (max_achievable_width * wc < expected_total_width_from_ratio) 915 ? max_achievable_width : data->windows_min_width; 916 score = 1 / (1 + fabs(wc * best_width_for_wc - expected_total_width_from_ratio)); 917 ASSERT(score < 1); 918 if(score > best_wc_score) { 919 best_wc = wc; 920 best_wc_score = score; 921 best_wc_width = best_width_for_wc; 922 best_wc_spacing = (wall_width - wc * best_wc_width) / (wc + 1); 923 } 924 } 925 /* Try with 1 more window */ 926 wc++; 927 } 928 if(best_wc_score == 0) { 929 /* No room for a single minimal window */ 930 removed_wall = 1; 931 removed_wall_sz++; 932 } 933 else if(best_wc_score < 1) { 934 missed_windows_ratio++; 935 } 936 937 if(!removed_wall) { 938 double sw, sh; 939 sw = MMIN(1, 1.01 * best_wc_width / wall_width); 940 sh = MMIN(1, 1.01 * window_height / wall_height); 941 d3(scale, sw, sw, sh); 942 ERR(scad_geometry_dilate(surface, center, scale, "scaled_", &scaled_)); 943 sw = best_wc_width / wall_width; 944 sh = window_height / wall_height; 945 d3(scale, sw, sw, sh); 946 ERR(scad_geometry_dilate(surface, center, scale, "scaled", &scaled)); 947 948 /* Try to create the planed windows */ 949 for(n = 0; n < (size_t)best_wc; n++) { 950 int removed_windows = 0; 951 double dxdydz[3], offset; 952 /* Compute windows positioning */ 953 offset = - 0.5 * (wall_width - best_wc_width) /* to the left border */ 954 + best_wc_spacing + (double)n * (best_wc_spacing + best_wc_width); 955 dxdydz[0] = -N[1] * offset; 956 dxdydz[1] = N[0] * offset; 957 /* Z positioning (centered) */ 958 dxdydz[2] = 0; 959 ERR(str_printf(&name, "surface+_%lu_w%lu", 960 (long unsigned)i, (long unsigned)n)); 961 /* surface_ is used to check for neighbor compatibility with a slitghly 962 * bigger size than the actual window */ 963 ERR(scad_geometry_translate(scaled_, dxdydz, str_cget(&name), &surface_)); 964 965 if(adjoining_n) { 966 /* Use the same distance used in early stages for close neighbor detection */ 967 hsz = CG2_CLOSE_NEIGHBOR_DISTANCE + data->wall_thickness + 968 data->internal_insulation_thickness + data->external_insulation_thickness; 969 d3_muld(dir, N, hsz); 970 ERR(str_printf(&name, "detect_adj_%lu_w%lu", 971 (long unsigned)i, (long unsigned)n)); 972 ERR(scad_geometry_extrude(surface_, str_cget(&name), dir, &detect)); 973 974 /* Check if detect intersects adjoining envelops */ 975 /* Push only if don't intersect */ 976 adj_list = MEM_REALLOC(allocator, adj_list, 977 adjoining_n * sizeof(struct scad_geometry*)); 978 for(j = 0; j < adjoining_n; j++) adj_list[j] = adj[j].envelop; 979 980 ERR(str_printf(&name, "adj_intersect_%lu", (long unsigned)i)); 981 ERR(scad_intersect_geometries(str_cget(&name), &detect, 1, adj_list, 982 adjoining_n, &hole_adjoining_intersect)); 983 ERR(scad_geometry_get_count(hole_adjoining_intersect, &count)); 984 if(count) { 985 removed_windows = 1; 986 removed_windows_adj++; 987 } 988 ERR(scad_geometry_ref_put(hole_adjoining_intersect)); 989 hole_adjoining_intersect = NULL; 990 ERR(scad_geometry_ref_put(detect)); 991 detect = NULL; 992 } 993 994 /* Check if the window intersects an unexpected wall of the building: 995 * - the window is too large wrt of external size of the wall (the 996 * window's size is a % of the internal size of the wall that can be 997 * larger than the external size due to corners). 998 * - another wall facing the current one at a too close distance (can be 999 * the prev/next with sharp angle, or not), 1000 * - a tiny unwanted wall created by noise at the polygon level (mainly 1001 * due to the offseting algorithm). */ 1002 if(!removed_windows) { 1003 /* Use smaller distance than the one used for neighbor detection */ 1004 hsz = 0.1 + data->wall_thickness + data->internal_insulation_thickness 1005 + data->external_insulation_thickness; 1006 d3_muld(dir, N, hsz); 1007 ERR(str_printf(&name, "detect_self_%lu", (long unsigned)i)); 1008 ERR(scad_geometry_extrude(surface_, str_cget(&name), dir, &detect)); 1009 /* Compute intersection between detect and envelop: the number of 1010 * components is expected to be 1, or the window is better removed */ 1011 ERR(str_printf(&name, "self_intersect_%lu", (long unsigned)i)); 1012 ERR(scad_intersect_geometries(str_cget(&name), &benv, 1, &detect, 1, 1013 &problem)); 1014 ERR(scad_geometry_get_count(problem, &count)); 1015 if(count != 1) { 1016 removed_windows = 1; 1017 removed_windows_self++; 1018 } 1019 ERR(scad_geometry_ref_put(detect)); 1020 detect = NULL; 1021 ERR(scad_geometry_ref_put(problem)); 1022 problem = NULL; 1023 ERR(scad_geometry_ref_put(surface_)); 1024 surface_ = NULL; 1025 } 1026 1027 if(!removed_windows) { 1028 ERR(scad_geometry_translate(scaled, dxdydz, "win_surface", &win_surface)); 1029 ERR(str_printf(&name, "hole_%lu_w%lu", (long unsigned)i, (long unsigned)n)); 1030 ERR(scad_geometry_extrude(win_surface, str_cget(&name), dir, &hole)); 1031 ERR(darray_geometries_push_back(&hole_array, &hole)); 1032 d3_muld(dir, N, CG2_GLAZING_THICKNESS); 1033 ERR(str_printf(&name, "glass_%lu_w%lu", (long unsigned)i, (long unsigned)n)); 1034 ERR(scad_geometry_extrude(win_surface, str_cget(&name), dir, &glass)); 1035 ERR(darray_geometries_push_back(&glass_array, &glass)); 1036 ERR(darray_geometries_push_back(current_cad, &glass)); 1037 ERR(scad_geometry_ref_put(hole)); 1038 hole = NULL; 1039 ERR(scad_geometry_ref_put(glass)); 1040 glass = NULL; 1041 ERR(scad_geometry_ref_put(win_surface)); 1042 win_surface = NULL; 1043 total_windows_surface += best_wc_width * window_height; 1044 } 1045 } 1046 } 1047 if(scaled) { 1048 ERR(scad_geometry_ref_put(scaled)); 1049 scaled = NULL; 1050 } 1051 if(scaled_) { 1052 ERR(scad_geometry_ref_put(scaled_)); 1053 scaled_ = NULL; 1054 } 1055 } 1056 if(hole) { 1057 ERR(scad_geometry_ref_put(hole)); 1058 hole = NULL; 1059 } 1060 if(detect) { 1061 ERR(scad_geometry_ref_put(detect)); 1062 detect = NULL; 1063 } 1064 if(win_surface) { 1065 ERR(scad_geometry_ref_put(win_surface)); 1066 win_surface = NULL; 1067 } 1068 if(surface) { 1069 ERR(scad_geometry_ref_put(surface)); 1070 surface = NULL; 1071 } 1072 if(scaled) { 1073 ERR(scad_geometry_ref_put(scaled)); 1074 scaled = NULL; 1075 } 1076 if(scaled_) { 1077 ERR(scad_geometry_ref_put(scaled_)); 1078 scaled_ = NULL; 1079 } 1080 if(surface_) { 1081 ERR(scad_geometry_ref_put(surface_)); 1082 surface_ = NULL; 1083 } 1084 } 1085 array_n = darray_geometries_size_get(&hole_array); 1086 ASSERT(array_n == darray_geometries_size_get(&glass_array)); 1087 1088 prefix = str_cget(&data_cad->building->name); 1089 if(removed_wall_sz != 0) { 1090 logger_print(city->logger, LOG_WARNING, 1091 "Building '%s' has %zu walls with no windows due to wall size.\n", 1092 prefix, removed_wall_sz); 1093 } 1094 if(missed_windows_ratio != 0) { 1095 logger_print(city->logger, LOG_WARNING, 1096 "Building '%s' has %zu walls with windows, but missing glass ratio target.\n", 1097 prefix, missed_windows_ratio); 1098 } 1099 removed_count = removed_windows_adj + removed_windows_self; 1100 if(removed_count != 0) { 1101 logger_print(city->logger, LOG_WARNING, 1102 "Building '%s' has %zu/%zu windows removed on walls with windows:\n", 1103 prefix, removed_count, removed_count + array_n); 1104 if(removed_windows_adj != 0) { 1105 logger_print(city->logger, LOG_WARNING, 1106 "- %zu windows removed due to close neighbors.\n", removed_windows_adj); 1107 } 1108 if(removed_windows_self != 0) { 1109 logger_print(city->logger, LOG_WARNING, 1110 "- %zu windows removed due to self conflicts.\n", removed_windows_self); 1111 } 1112 } else { 1113 logger_print(city->logger, LOG_OUTPUT, 1114 "Building '%s' has no window removed (out of %zu).\n", prefix, array_n); 1115 } 1116 effective_ratio = total_windows_surface / total_wall_surface; 1117 meet_effective_ratio = data->glass_ratio == 0 1118 || fabs(effective_ratio - data->glass_ratio) / data->glass_ratio < 0.01; 1119 logger_print(city->logger, (meet_effective_ratio ? LOG_OUTPUT : LOG_WARNING), 1120 "Building '%s' overall glass ratio is %.1f %% (expected is %.1f %%).\n", 1121 prefix, 100 * effective_ratio, 100 * data->glass_ratio); 1122 1123 if(array_n > 0) { 1124 hole_list = darray_geometries_data_get(&hole_array); 1125 glass_list = darray_geometries_data_get(&glass_array); 1126 1127 /* wall perforation */ 1128 ERR(scad_cut_geometries(NULL, &data_cad->wall, 1, hole_list, array_n, &geom)); 1129 ERR(scad_geometries_swap(&data_cad->wall, &geom, 1, Scad_swap_geometry)); 1130 ERR(scad_geometry_ref_put(geom)); 1131 geom = NULL; 1132 1133 /* internal insulation perforation */ 1134 if(data_cad->internal_insulation) { 1135 ERR(scad_cut_geometries(NULL, &data_cad->internal_insulation, 1, 1136 hole_list, array_n, &geom)); 1137 ERR(scad_geometries_swap(&data_cad->internal_insulation, &geom, 1, 1138 Scad_swap_geometry)); 1139 ERR(scad_geometry_ref_put(geom)); 1140 geom = NULL; 1141 } 1142 1143 /* external insulation perforation */ 1144 if(data_cad->external_insulation) { 1145 ERR(scad_cut_geometries(NULL, &data_cad->external_insulation, 1, 1146 hole_list, array_n, &geom)); 1147 ERR(scad_geometries_swap(&data_cad->external_insulation, &geom, 1, 1148 Scad_swap_geometry)); 1149 ERR(scad_geometry_ref_put(geom)); 1150 geom = NULL; 1151 } 1152 1153 /* build glass */ 1154 ERR(str_set(&name, prefix)); 1155 ERR(str_append(&name, "_S_glazing")); 1156 ERR(scad_fuse_geometries(str_cget(&name), glass_list, array_n, 1157 glass_list, array_n, &data_cad->glazing)); 1158 } 1159 1160 exit: 1161 for(i = 0 ; i < list_n; i++) SCAD(geometry_ref_put(list[i])); 1162 darray_geometries_release(&hole_array); 1163 darray_geometries_release(&glass_array); 1164 if(env) SCAD(geometry_ref_put(env)); 1165 if(benv) SCAD(geometry_ref_put(benv)); 1166 if(surface) SCAD(geometry_ref_put(surface)); 1167 if(win_surface) SCAD(geometry_ref_put(win_surface)); 1168 if(problem) SCAD(geometry_ref_put(problem)); 1169 if(hole) SCAD(geometry_ref_put(hole)); 1170 if(detect) SCAD(geometry_ref_put(detect)); 1171 if(surface_) SCAD(geometry_ref_put(surface_)); 1172 if(glass) SCAD(geometry_ref_put(glass)); 1173 if(geom) SCAD(geometry_ref_put(geom)); 1174 if(bcavity) SCAD(geometry_ref_put(bcavity)); 1175 if(hole_adjoining_intersect) SCAD(geometry_ref_put(hole_adjoining_intersect)); 1176 MEM_RM(allocator, list); 1177 MEM_RM(allocator, adj_list); 1178 str_release(&name); 1179 return res; 1180 error: 1181 goto exit; 1182 } 1183 1184 static res_T 1185 build_boundary 1186 (struct data_cad_cmode_2* data_cad, 1187 struct darray_adjoining_data* adjoining_data, 1188 struct darray_geometries* boundary) 1189 { 1190 res_T res = RES_OK; 1191 struct darray_geometries array; 1192 struct scad_geometry** list = NULL; 1193 struct scad_geometry* bound = NULL; 1194 char* boundaryname = NULL; 1195 struct str name; 1196 struct adjoining_data* adj; 1197 size_t adjoining_n, i = 0, count = 0; 1198 const char* prefix; 1199 1200 ASSERT(data_cad && adjoining_data && boundary); 1201 1202 prefix = str_cget(&data_cad->building->name); 1203 adjoining_n = darray_adjoining_data_size_get(adjoining_data); 1204 adj = darray_adjoining_data_data_get(adjoining_data); 1205 darray_geometries_init(data_cad->building->city->allocator, &array); 1206 str_init(data_cad->building->city->allocator, &name); 1207 1208 /* Ensure enough room for all geometries without error nor mem move */ 1209 ERR(darray_geometries_reserve(&array, 14 + adjoining_n)); 1210 /* Using wall here to compute boundary_wall is OK even if it doesn't take care 1211 * of conformity wrt the adjoining building. The reason is that the common 1212 * part that could end to be not conformal is not part of the boundary. As a 1213 * consequence it cannot be part of the result. */ 1214 ERR(darray_geometries_push_back(&array, &data_cad->wall)); 1215 ERR(darray_geometries_push_back(&array, &data_cad->roof)); 1216 ERR(darray_geometries_push_back(&array, &data_cad->floor)); 1217 ERR(darray_geometries_push_back(&array, &data_cad->habitable_cavity)); 1218 ERR(darray_geometries_push_back(&array, &data_cad->fake_ground)); 1219 if(data_cad->foundation) { 1220 ERR(darray_geometries_push_back(&array, &data_cad->foundation)); 1221 } 1222 if(data_cad->intermediate_floor) { 1223 ERR(darray_geometries_push_back(&array, &data_cad->intermediate_floor)); 1224 } 1225 if(data_cad->external_insulation) { 1226 ERR(darray_geometries_push_back(&array, &data_cad->external_insulation)); 1227 } 1228 if(data_cad->internal_insulation) { 1229 ERR(darray_geometries_push_back(&array, &data_cad->internal_insulation)); 1230 } 1231 if(data_cad->roof_insulation) { 1232 ERR(darray_geometries_push_back(&array, &data_cad->roof_insulation)); 1233 } 1234 if(data_cad->floor_insulation) { 1235 ERR(darray_geometries_push_back(&array, &data_cad->floor_insulation)); 1236 } 1237 if(data_cad->attic_cavity) { 1238 ERR(darray_geometries_push_back(&array, &data_cad->attic_cavity)); 1239 } 1240 if(data_cad->crawlspace_cavity) { 1241 ERR(darray_geometries_push_back(&array, &data_cad->crawlspace_cavity)); 1242 } 1243 if(data_cad->glazing) { 1244 ERR(darray_geometries_push_back(&array, &data_cad->glazing)); 1245 } 1246 for(i = 0; i < adjoining_n; i++) { 1247 /* Here we consider truly adjoining buildings, except removed ones (early 1248 * removed ones are not part of adjoining) */ 1249 if(adj[i].really_adjoining 1250 && !(adj[i].adjoining_building->event_flags & BUILDING_REMOVED)) 1251 { 1252 ERR(darray_geometries_push_back(&array, &adj[i].envelop)); 1253 } 1254 } 1255 1256 count = darray_geometries_size_get(&array); 1257 list = darray_geometries_data_get(&array); 1258 1259 /* Ensure enough room for all geometries without error nor mem move */ 1260 ERR(darray_geometries_reserve(boundary, 5+darray_geometries_size_get(boundary))); 1261 1262 ERR(str_set(&name, prefix)); 1263 ERR(str_append(&name, "_B_walls")); 1264 boundaryname = str_get(&name); 1265 ERR(scad_geometries_common_boundaries(boundaryname, list, count, 1266 &data_cad->wall, 1, &bound)); 1267 ERR(darray_geometries_push_back(boundary, &bound)); 1268 ERR(scad_geometry_ref_put(bound)); 1269 bound = NULL; 1270 1271 ERR(str_set(&name, prefix)); 1272 ERR(str_append(&name, "_B_roof")); 1273 boundaryname = str_get(&name); 1274 ERR(scad_geometries_common_boundaries(boundaryname, list, count, 1275 &data_cad->roof, 1, &bound)); 1276 ERR(darray_geometries_push_back(boundary, &bound)); 1277 ERR(scad_geometry_ref_put(bound)); 1278 bound = NULL; 1279 1280 if(data_cad->glazing) { 1281 ERR(str_set(&name, prefix)); 1282 ERR(str_append(&name, "_B_glazing")); 1283 boundaryname = str_get(&name); 1284 ERR(scad_geometries_common_boundaries(boundaryname, list, count, 1285 &data_cad->glazing, 1, &bound)); 1286 ERR(darray_geometries_push_back(boundary, &bound)); 1287 ERR(scad_geometry_ref_put(bound)); 1288 bound = NULL; 1289 } 1290 1291 if(data_cad->external_insulation) { 1292 ERR(str_set(&name, prefix)); 1293 ERR(str_append(&name, "_B_external_insulation")); 1294 boundaryname = str_get(&name); 1295 ERR(scad_geometries_common_boundaries(boundaryname, list, count, 1296 &data_cad->external_insulation, 1, &bound)); 1297 ERR(darray_geometries_push_back(boundary, &bound)); 1298 ERR(scad_geometry_ref_put(bound)); 1299 bound = NULL; 1300 } 1301 1302 if(data_cad->internal_insulation) { 1303 size_t bcount = 0; 1304 ERR(str_set(&name, prefix)); 1305 ERR(str_append(&name, "_B_internal_insulation")); 1306 boundaryname = str_get(&name); 1307 ERR(scad_geometries_common_boundaries(boundaryname, list, count, 1308 &data_cad->internal_insulation, 1, &bound)); 1309 ERR(scad_geometry_get_count(bound, &bcount)); 1310 if(bcount > 0) { 1311 ERR(darray_geometries_push_back(boundary, &bound)); 1312 } 1313 ERR(scad_geometry_ref_put(bound)); 1314 bound = NULL; 1315 } 1316 1317 exit: 1318 if(bound) SCAD(geometry_ref_put(bound)); 1319 str_release(&name); 1320 darray_geometries_release(&array); 1321 return res; 1322 error: 1323 goto exit; 1324 } 1325 1326 static res_T 1327 build_connection 1328 (struct data_cad_cmode_2* data_cad, 1329 struct darray_geometries* connection) 1330 { 1331 res_T res = RES_OK; 1332 struct scad_geometry* connect = NULL; 1333 size_t count = 0; 1334 char* cname = NULL; 1335 struct str name; 1336 const char* prefix; 1337 1338 ASSERT(data_cad && connection); 1339 1340 prefix = str_cget(&data_cad->building->name); 1341 str_init(data_cad->building->city->allocator, &name); 1342 1343 #define CREATE_CONNECT(G1,G2,SUFFIX) \ 1344 ERR(str_set(&name, prefix));\ 1345 ERR(str_append(&name, SUFFIX));\ 1346 cname = str_get(&name);\ 1347 ERR(scad_geometries_common_boundaries(cname, &data_cad->G1, 1,\ 1348 &data_cad->G2, 1, &connect));\ 1349 ERR(scad_geometry_get_count(connect, &count)); \ 1350 if(count > 0) { \ 1351 ERR(darray_geometries_push_back(connection, &connect)); \ 1352 } \ 1353 ERR(scad_geometry_ref_put(connect)); \ 1354 connect = NULL; 1355 1356 /* -------------------------------------------------------------------------*/ 1357 /* habitable cavity connections */ 1358 /* -------------------------------------------------------------------------*/ 1359 1360 /* with floor */ 1361 CREATE_CONNECT(habitable_cavity,floor,"_C_levels_floor"); 1362 1363 /* with wall */ 1364 CREATE_CONNECT(habitable_cavity,wall,"_C_levels_walls"); 1365 1366 /* with glass */ 1367 if(data_cad->glazing) { 1368 CREATE_CONNECT(habitable_cavity,glazing,"_C_levels_glazing"); 1369 } 1370 1371 /* with internal insulation */ 1372 if(data_cad->internal_insulation) { 1373 CREATE_CONNECT(habitable_cavity,internal_insulation,"_C_levels_internal_insulation"); 1374 } 1375 1376 /* with roof insulation */ 1377 if(data_cad->roof_insulation) { 1378 CREATE_CONNECT(habitable_cavity,roof_insulation,"_C_levels_roof_insulation"); 1379 } else { 1380 /* with roof */ 1381 CREATE_CONNECT(habitable_cavity,roof,"_C_levels_roof"); 1382 } 1383 1384 /* with intermediate floor */ 1385 if(data_cad->intermediate_floor) { 1386 CREATE_CONNECT(habitable_cavity,intermediate_floor,"_C_levels_intermediate_floors"); 1387 } 1388 1389 /* -------------------------------------------------------------------------*/ 1390 /* crawlspace cavity connections */ 1391 /* -------------------------------------------------------------------------*/ 1392 1393 if(data_cad->crawlspace_cavity) { 1394 /* with floor insulation */ 1395 if(data_cad->floor_insulation) { 1396 CREATE_CONNECT(crawlspace_cavity, floor_insulation,"_C_crawlspace_insulation"); 1397 } else { 1398 /* with floor */ 1399 CREATE_CONNECT(crawlspace_cavity, floor,"_C_crawlspace_floor"); 1400 } 1401 1402 /* with wall */ 1403 CREATE_CONNECT(crawlspace_cavity, foundation,"_C_crawlspace_foundation"); 1404 1405 /* with ground */ 1406 CREATE_CONNECT(crawlspace_cavity, fake_ground,"_C_crawlspace_ground"); 1407 } 1408 1409 /* -------------------------------------------------------------------------*/ 1410 /* attic cavity connections */ 1411 /* -------------------------------------------------------------------------*/ 1412 1413 if(data_cad->attic_cavity) { 1414 /* with roof */ 1415 CREATE_CONNECT(attic_cavity, roof,"_C_attic_roof"); 1416 1417 /* with roof insulation */ 1418 if(data_cad->roof_insulation) { 1419 CREATE_CONNECT(attic_cavity, roof_insulation,"_C_attic_insulation"); 1420 } 1421 1422 /* with wall */ 1423 CREATE_CONNECT(attic_cavity, wall,"_C_attic_walls"); 1424 } 1425 1426 #undef CREATE_CONNECT 1427 1428 exit: 1429 str_release(&name); 1430 return res; 1431 error: 1432 goto exit; 1433 } 1434 1435 static res_T 1436 build_fake_ground 1437 (struct data_cad_cmode_2* cad, 1438 const double depth, 1439 struct darray_geometries* current_cad, 1440 struct scad_geometry** ground) 1441 { 1442 res_T res = RES_OK; 1443 double dir[3] = {0, 0, 0}; 1444 struct scpr_polygon* pg_offset = NULL; 1445 size_t count; 1446 struct darray_geometries array; 1447 struct scad_geometry** list = NULL; 1448 struct scad_geometry* footprint = NULL; 1449 struct scad_geometry* geom = NULL; 1450 1451 ASSERT(cad && ground); 1452 1453 darray_geometries_init(cad->building->city->allocator, &array); 1454 1455 /* Ensure enough room for all geometries without error nor mem move */ 1456 ERR(darray_geometries_reserve(&array, 4)); 1457 if(cad->foundation) { 1458 ERR(darray_geometries_push_back(&array, &cad->foundation)); 1459 } 1460 if(cad->crawlspace_cavity) { 1461 ERR(darray_geometries_push_back(&array, &cad->crawlspace_cavity)); 1462 } 1463 if(cad->floor) { 1464 ERR(darray_geometries_push_back(&array, &cad->floor)); 1465 } 1466 if(cad->floor_insulation) { 1467 ERR(darray_geometries_push_back(&array, &cad->floor_insulation)); 1468 } 1469 1470 count = darray_geometries_size_get(&array); 1471 list = darray_geometries_data_get(&array); 1472 1473 ERR(scpr_polygon_create_copy(cad->building->city->scpr, cad->building->pg, &pg_offset)); 1474 ERR(scpr_offset_polygon(pg_offset, 0.1, SCPR_JOIN_MITER)); 1475 1476 ERR(build_footprint(pg_offset, 0, &footprint)); 1477 1478 dir[2] = -depth*1.1; 1479 ERR(scad_geometry_extrude(footprint, NULL, dir, &geom)); 1480 1481 ERR(scad_cut_geometries("fake_ground", &geom, 1, list, count, ground)); 1482 ERR(darray_geometries_push_back(current_cad, ground)); 1483 1484 exit: 1485 if(pg_offset) SCPR(polygon_ref_put(pg_offset)); 1486 darray_geometries_release(&array); 1487 if(footprint) SCAD(geometry_ref_put(footprint)); 1488 if(geom) SCAD(geometry_ref_put(geom)); 1489 return res; 1490 error: 1491 goto exit; 1492 } 1493 1494 static res_T 1495 building_ground_connection 1496 (struct data_cad_cmode_2* cad, 1497 struct scad_geometry** connection) 1498 { 1499 res_T res = RES_OK; 1500 char* cname = NULL; 1501 struct str name; 1502 size_t count; 1503 struct darray_geometries array; 1504 struct scad_geometry** list = NULL; 1505 struct scad_geometry* list_boundary = NULL; 1506 struct scad_geometry* footprint = NULL; 1507 const char* prefix; 1508 1509 ASSERT(cad && connection); 1510 1511 prefix = str_cget(&cad->building->name); 1512 darray_geometries_init(cad->building->city->allocator, &array); 1513 str_init(cad->building->city->allocator, &name); 1514 ERR(str_set(&name, prefix)); 1515 ERR(str_append(&name, "_C_ground")); 1516 cname = str_get(&name); 1517 1518 /* Ensure enough room for all geometries without error nor mem move */ 1519 ERR(darray_geometries_reserve(&array, 6)); 1520 if(cad->foundation) { 1521 ERR(darray_geometries_push_back(&array, &cad->foundation)); 1522 } 1523 if(cad->crawlspace_cavity) { 1524 ERR(darray_geometries_push_back(&array, &cad->crawlspace_cavity)); 1525 } 1526 if(cad->floor) { 1527 ERR(darray_geometries_push_back(&array, &cad->floor)); 1528 } 1529 if(cad->floor_insulation) { 1530 ERR(darray_geometries_push_back(&array, &cad->floor_insulation)); 1531 } 1532 if(cad->external_insulation) { 1533 ERR(darray_geometries_push_back(&array, &cad->external_insulation)); 1534 } 1535 if(cad->wall) { 1536 ERR(darray_geometries_push_back(&array, &cad->wall)); 1537 } 1538 1539 count = darray_geometries_size_get(&array); 1540 list = darray_geometries_data_get(&array); 1541 1542 ERR(scad_geometries_common_boundaries(cname, list, count, &cad->fake_ground, 1, 1543 connection)); 1544 1545 exit: 1546 darray_geometries_release(&array); 1547 str_release(&name); 1548 if(list_boundary) SCAD(geometry_ref_put(list_boundary)); 1549 if(footprint) SCAD(geometry_ref_put(footprint)); 1550 return res; 1551 error: 1552 goto exit; 1553 } 1554 1555 /*----------------------------------------------------------------------------*/ 1556 /*----------------------------------------------------------------------------*/ 1557 /*----------------------------------------------------------------------------*/ 1558 1559 res_T 1560 init_cmode_2 1561 (struct building* building, 1562 struct city* city, 1563 struct parsed_city_building* parsed_data, 1564 struct catalog* catalog, 1565 const double lower[2], 1566 const double upper[2]) 1567 { 1568 res_T res = RES_OK; 1569 struct dataset_cmode_2* data; 1570 static struct construction_mode_functors functors_2 = { 1571 &init_cmode_2, 1572 &release_cmode_2, 1573 &build_cad_cmode_2, 1574 &build_footprint_cmode_2, 1575 &save_ground_connection_triangles_2, 1576 &export_stl_cmode_2, 1577 &release_cad_cmode_2 1578 }; 1579 struct logger* logger; 1580 int has_external_insulation; 1581 double *lh; 1582 unsigned count; 1583 1584 if(!building || !city || !parsed_data || !catalog || !lower || !upper) { 1585 res = RES_BAD_ARG; 1586 goto error; 1587 } 1588 1589 logger = city->logger; 1590 building->construction_mode = mode_2; 1591 building->functors = &functors_2; 1592 1593 ERR(init_building_base(building, city, parsed_data)); 1594 1595 if(parsed_data->height != 0) { 1596 ERR(logger_print(city->logger, LOG_ERROR, 1597 "Building '%s' defines 'height' " 1598 "(feature not available in construction mode 2).\n", 1599 str_cget(&building->name))); 1600 res = RES_BAD_ARG; 1601 goto error; 1602 } 1603 1604 if(parsed_data->levels_height_count <= 0) { 1605 ERR(logger_print(city->logger, LOG_ERROR, 1606 "Building '%s' height definition is invalid " 1607 "(construction mode 2 should define the 'levels_height' field).\n", 1608 str_cget(&building->name))); 1609 res = RES_BAD_ARG; 1610 goto error; 1611 } 1612 1613 building->data = htable_dataset_cmode_2_find(&catalog->catalog_2, 1614 &building->dataset_name); 1615 if(building->data == NULL) { 1616 ERR(logger_print(logger, LOG_ERROR, 1617 "Unknown dataset name: '%s' used by building '%s'.\n", 1618 str_cget(&building->dataset_name), str_cget(&building->name))); 1619 res = RES_BAD_ARG; 1620 goto error; 1621 } 1622 data = (struct dataset_cmode_2 *)building->data; 1623 has_external_insulation = (data->external_insulation_thickness != 0); 1624 ERR(str_set(&building->external_layer_name, 1625 (has_external_insulation ? "external_insulation" : "walls"))); 1626 1627 if(data->has_attic && parsed_data->levels_height_count <= 1) { 1628 ERR(logger_print(city->logger, LOG_ERROR, 1629 "Building '%s' height definition is invalid " 1630 "(At least 2 level heights needed when there is an attic).\n", 1631 str_cget(&building->name))); 1632 res = RES_BAD_ARG; 1633 goto error; 1634 } 1635 1636 ERR(darray_double_resize(&building->levels_height, 1637 parsed_data->levels_height_count)); 1638 lh = darray_double_data_get(&building->levels_height); 1639 1640 building->total_height = 0; 1641 for(count = 0; count < parsed_data->levels_height_count; count++) { 1642 lh[count] = parsed_data->levels_height[count]; 1643 if(lh[count] <= 0) { 1644 ERR(logger_print(city->logger, LOG_ERROR, 1645 "Building '%s' height definition is invalid " 1646 "(level heights should be positive).\n", 1647 str_cget(&building->name))); 1648 res = RES_BAD_ARG; 1649 goto error; 1650 } 1651 building->total_height += lh[count]; 1652 } 1653 1654 exit: 1655 return res; 1656 error: 1657 goto exit; 1658 } 1659 1660 res_T 1661 release_cmode_2 1662 (struct building* building) 1663 { 1664 if(!building) return RES_BAD_ARG; 1665 return release_building_base(building); 1666 } 1667 1668 res_T 1669 build_cad_cmode_2 1670 (struct building* building, 1671 int dump_footprints_level, 1672 int keep_running_on_errors, 1673 struct darray_adjoining_data* adjoining_data, 1674 struct darray_geometries* current_cad, 1675 void** cad) 1676 { 1677 res_T res = RES_OK; 1678 double height = building->total_height; 1679 double depth = 0; 1680 struct dataset_cmode_2* data = (struct dataset_cmode_2 *)building->data; 1681 struct data_cad_cmode_2* data_cad = NULL; 1682 double e_attic; 1683 const double* levels; 1684 const char* name; 1685 struct scpr_intersector* overlapping_intersector = NULL; 1686 struct scpr_intersector_check_callbacks callbacks 1687 = SCPR_INTERSECTOR_CHECK_CALLBACKS_NULL__; 1688 size_t adjoining_n = 0, levels_n; 1689 struct callback_ctx ctx = CB_CTX_NULL__; 1690 int error_occured = 0, error_msg_printed = 0;; 1691 struct htable_polygons polygons; 1692 int polygons_initialized = 0; 1693 double zero = 0; 1694 struct mem_allocator* allocator; 1695 struct logger* logger = NULL; 1696 size_t i, cad_count = 0; 1697 struct scad_geometry** cur_cad = NULL; 1698 struct scad_geometry** partitioned = NULL; 1699 struct time t0, dt, tw, dtw; 1700 char buf[128]; 1701 1702 if(!building || !cad || !adjoining_data) { 1703 res = RES_BAD_ARG; 1704 goto error; 1705 } 1706 1707 time_current(&t0); 1708 name = str_cget(&building->name); 1709 allocator = building->city->allocator; 1710 logger = building->city->logger; 1711 levels = darray_double_cdata_get(&building->levels_height); 1712 levels_n = data->has_attic ? 1713 darray_double_size_get(&building->levels_height) - 1 : 1714 darray_double_size_get(&building->levels_height); 1715 e_attic = data->has_attic ? levels[levels_n] : 0; 1716 1717 logger_print(logger, LOG_OUTPUT, 1718 "Building '%s' construction mode 2, dataset '%s', %g m tall, %lu levels.\n", 1719 name, str_cget(&building->dataset_name), building->total_height, levels_n); 1720 1721 data_cad = MEM_CALLOC(allocator, 1, sizeof(struct data_cad_cmode_2)); 1722 if(!data_cad) { 1723 res = RES_MEM_ERR; 1724 goto error; 1725 } 1726 building->data_cad = data_cad; 1727 data_cad->building = building; 1728 darray_common_trg_init(allocator, &data_cad->common_trg); 1729 darray_geometries_init(allocator, &data_cad->adj_walls); 1730 darray_geometries_init(allocator, &data_cad->boundary); 1731 darray_geometries_init(allocator, &data_cad->connection); 1732 1733 htable_polygons_init(allocator, &polygons); 1734 polygons_initialized = 1; 1735 1736 ERR(scpr_intersector_create(building->city->scpr, &overlapping_intersector)); 1737 ERR(scpr_intersector_register_polygon(overlapping_intersector, building->pg)); 1738 /* An htable to associate offset polygons to offsets. Not really for 1739 * performance, but to avoid considering the same polygon more than once when 1740 * checking for polygon intersections (that would be an error). */ 1741 ERR(htable_polygons_set(&polygons, &zero, &building->pg)); 1742 1743 /* build mandatories elements : 1744 - floor 1745 - wall 1746 - roof 1747 */ 1748 1749 ERR(build_floor(building, &error_msg_printed, data, overlapping_intersector, 1750 &polygons, current_cad, &data_cad->floor)); 1751 1752 ERR(build_wall(building, &error_msg_printed, 0, "S_walls", height, data, 1753 overlapping_intersector, &polygons, current_cad, &data_cad->wall)); 1754 1755 ERR(build_roof(building, &error_msg_printed, 1756 height, data, 1757 overlapping_intersector, &polygons, current_cad, &data_cad->roof)); 1758 1759 /* build optionnal elements : 1760 - foundation 1761 - intermediate floor 1762 - external insulation 1763 - internal insulation 1764 - roof insulation 1765 - floor insulation 1766 */ 1767 1768 if(data->foundation_depth > 0) { 1769 depth = -data->foundation_depth; 1770 ERR(build_wall(building, &error_msg_printed, 1, "S_foundation", depth, 1771 data, overlapping_intersector, &polygons, current_cad, &data_cad->foundation)); 1772 } 1773 1774 if(levels_n > 1) { 1775 ERR(build_inter_floor(building, &error_msg_printed, data, 1776 overlapping_intersector, &polygons, current_cad, &data_cad->intermediate_floor)); 1777 } 1778 1779 if(data->external_insulation_thickness> 0) { 1780 ERR(build_ext_insulation(building, &error_msg_printed, height, data, 1781 overlapping_intersector, &polygons, current_cad, &data_cad->external_insulation)); 1782 } 1783 1784 if(data->internal_insulation_thickness> 0) { 1785 ERR(build_int_insulation(building, &error_msg_printed, height, data, 1786 data_cad->intermediate_floor, overlapping_intersector, &polygons, 1787 current_cad, &data_cad->internal_insulation)); 1788 } 1789 1790 if(data->roof_insulation_thickness > 0) { 1791 ERR(build_roof_insulation(building, &error_msg_printed, height, data, 1792 overlapping_intersector, &polygons, current_cad, &data_cad->roof_insulation)); 1793 } 1794 1795 if(data->floor_insulation_thickness > 0) { 1796 ERR(build_floor_insulation(building, &error_msg_printed, data, 1797 overlapping_intersector, &polygons, current_cad, &data_cad->floor_insulation)); 1798 } 1799 1800 /* build cavities : 1801 - attic 1802 - habitable 1803 - crawlspace 1804 */ 1805 1806 if(e_attic > 0) { 1807 ERR(build_attic(building, &error_msg_printed, height, data, 1808 overlapping_intersector, &polygons, current_cad, &data_cad->attic_cavity)); 1809 } 1810 1811 ERR(build_habitable(building, &error_msg_printed, height, data, 1812 data_cad->intermediate_floor, overlapping_intersector, &polygons, 1813 current_cad, &data_cad->habitable_cavity)); 1814 1815 if(data->crawl_height > 0) { 1816 ERR(build_crawlspace(building, &error_msg_printed, data, 1817 overlapping_intersector, &polygons, current_cad, &data_cad->crawlspace_cavity)); 1818 } 1819 1820 /* Check for registered polygons overlapping */ 1821 ctx.city = building->city; 1822 ctx.buildings = building; 1823 ctx.buildings_count = 1; 1824 ctx.intersection_found = &error_occured; 1825 ctx.search_type = TESTING_1_BUILDING_INTERNALS; 1826 ctx.dump_footprints_level = dump_footprints_level; 1827 ctx.keep_running_on_errors = keep_running_on_errors; 1828 callbacks.simple_intersection = simple_intersection; 1829 ERR(scpr_intersector_check(overlapping_intersector, &callbacks, &ctx)); 1830 if(error_occured) { 1831 logger_print(logger, LOG_ERROR, 1832 "Internal error generating CAD for building '%s'.\n", 1833 name); 1834 building->event_flags |= BUILDING_REMOVED; 1835 error_msg_printed = 1; 1836 res = RES_BAD_ARG; 1837 goto error; 1838 } 1839 1840 /* build adjoining envelop */ 1841 adjoining_n = htable_building_size_get(&building->close_buildings); 1842 if(adjoining_n > 0) { 1843 ERR(build_adjoining(building, 0, current_cad, adjoining_data)); 1844 } 1845 1846 /* windows */ 1847 if(data->glass_ratio > 0) { 1848 time_current(&tw); 1849 ERR(build_windows(data, data_cad, current_cad, adjoining_data)); 1850 time_sub(&dtw, time_current(&dtw), &tw); 1851 } 1852 1853 /* fake ground */ 1854 depth = MMAX(data->foundation_depth, 1855 data->floor_thickness + data->floor_insulation_thickness + data->crawl_height); 1856 ERR(build_fake_ground(data_cad, depth, current_cad, &data_cad->fake_ground)); 1857 1858 /* print partial computation time */ 1859 time_sub(&dt, time_current(&dt), &t0); 1860 time_dump(&dt, TIME_SEC | TIME_MSEC, NULL, buf, sizeof(buf)); 1861 if(data->glass_ratio > 0) { 1862 char bufw[128]; 1863 time_dump(&dtw, TIME_SEC | TIME_MSEC, NULL, bufw, sizeof(bufw)); 1864 logger_print(logger, LOG_OUTPUT, 1865 "Building '%s' CAO stage done in %s (windows creation in %s).\n", 1866 name, buf, bufw); 1867 } else { 1868 logger_print(logger, LOG_OUTPUT, 1869 "Building '%s' CAO stage done in %s.\n", name, buf); 1870 } 1871 time_current(&t0); 1872 1873 /* Partition CAD */ 1874 cad_count = darray_geometries_size_get(current_cad); 1875 partitioned = MEM_CALLOC(allocator, cad_count, sizeof(*partitioned)); 1876 if(!partitioned) { 1877 res = RES_MEM_ERR; 1878 goto error; 1879 } 1880 cur_cad = darray_geometries_data_get(current_cad); 1881 ERR(scad_geometries_partition(cur_cad, cad_count, Scad_dump_on_overlapping_error, 1882 partitioned)); 1883 /* Swap original geometry and partitioned geometry in data_cad (was 1884 * accumulated into current_cad) */ 1885 ERR(scad_geometries_swap(cur_cad, partitioned, cad_count, Scad_swap_geometry)); 1886 for(i = 0; i < cad_count; i++) { 1887 if(partitioned[i]) { 1888 ERR(scad_geometry_ref_put(partitioned[i])); 1889 } 1890 } 1891 MEM_RM(allocator, partitioned); 1892 partitioned = NULL; 1893 1894 /* print partial computation time */ 1895 time_sub(&dt, time_current(&dt), &t0); 1896 time_dump(&dt, TIME_SEC | TIME_MSEC, NULL, buf, sizeof(buf)); 1897 logger_print(logger, LOG_OUTPUT, 1898 "Building '%s' partitioning stage done in %s.\n", name, buf); 1899 time_current(&t0); 1900 1901 /* After partitioning, manage common parts with other buildings */ 1902 adjoining_n = darray_adjoining_data_size_get(adjoining_data); 1903 if(adjoining_n > 0) { 1904 size_t a, c; 1905 struct b_pair pair; 1906 struct darray_double* common; 1907 struct adjoining_data* adjoining 1908 = darray_adjoining_data_data_get(adjoining_data); 1909 struct scad_geometry* extern_most = data_cad->external_insulation 1910 ? data_cad->external_insulation : data_cad->wall; 1911 for(a = 0; a < adjoining_n; a++) { 1912 struct adjoining_data* adj = adjoining + a; 1913 ERR(scad_geometries_common_boundaries(NULL, &extern_most, 1, 1914 &adj->envelop, 1, &adj->common_geometry)); 1915 ERR(scad_geometry_get_count(adj->common_geometry, &c)); 1916 adj->really_adjoining = (c != 0); 1917 if(!adj->really_adjoining) { 1918 logger_print(logger, LOG_OUTPUT, 1919 "Building '%s': neighbor '%s' not really adjoining.\n", 1920 name, 1921 str_cget(&adj->adjoining_building->name)); 1922 continue; 1923 } 1924 make_b_pair(&pair, building, adj->adjoining_building); 1925 /* Keep track of the geometry to replace and the mesh to output instead */ 1926 ERR(darray_geometries_push_back(&data_cad->adj_walls, &adj->common_geometry)); 1927 ERR(darray_common_trg_push_back(&data_cad->common_trg, &pair)); 1928 common = htable_common_find(&building->city->common, &pair); 1929 if(!common) { 1930 /* The mesh doesn't exist yet and won't be created until a further step. 1931 * We need to store the geometry id so that the mesh can be stored when 1932 * created. */ 1933 adj->save = 1; 1934 } 1935 } 1936 } 1937 1938 /* build ground/building connection */ 1939 ERR(building_ground_connection(data_cad, &data_cad->ground_connection)); 1940 1941 /* build boundaries */ 1942 ERR(build_boundary(data_cad, adjoining_data, &data_cad->boundary)); 1943 1944 /* build connections */ 1945 ERR(build_connection(data_cad, &data_cad->connection)); 1946 1947 /* print partial computation time */ 1948 time_sub(&dt, time_current(&dt), &t0); 1949 time_dump(&dt, TIME_SEC | TIME_MSEC, NULL, buf, sizeof(buf)); 1950 logger_print(logger, LOG_OUTPUT, 1951 "Building '%s' connections stage done in %s.\n", name, buf); 1952 time_current(&t0); 1953 1954 exit: 1955 if(partitioned) { 1956 for(i = 0; i < cad_count; i++) { 1957 if(partitioned[i]) SCAD(geometry_ref_put(partitioned[i])); 1958 } 1959 MEM_RM(allocator, partitioned); 1960 } 1961 if(polygons_initialized) htable_polygons_release(&polygons); 1962 if(overlapping_intersector) SCPR(intersector_ref_put(overlapping_intersector)); 1963 if(cad) *(struct data_cad_cmode_2**)cad = data_cad; 1964 return res; 1965 error: 1966 if(logger && building && !error_msg_printed) { 1967 logger_print(logger, LOG_ERROR, 1968 "Unknown error generating CAD for building '%s'.\n", 1969 str_cget(&building->name)); 1970 } 1971 if(data_cad) CHK(RES_OK == release_cad_cmode_2(data_cad)); 1972 data_cad = NULL; 1973 goto exit; 1974 } 1975 1976 res_T 1977 build_footprint_cmode_2 1978 (struct building* building, 1979 struct scad_geometry** footprint) 1980 { 1981 res_T res = RES_OK; 1982 1983 if(!building || !footprint) { 1984 res = RES_BAD_ARG; 1985 goto error; 1986 } 1987 1988 ERR(build_footprint(building->pg, 0, footprint)); 1989 1990 exit: 1991 return res; 1992 error: 1993 goto exit; 1994 } 1995 1996 res_T save_ground_connection_triangles_2 1997 (void* cad, 1998 struct darray_double* triangles) 1999 { 2000 res_T res = RES_OK; 2001 struct data_cad_cmode_2* data_cad = cad; 2002 2003 if(!cad || !triangles) { 2004 res = RES_BAD_ARG; 2005 goto error; 2006 } 2007 2008 ERR(scad_stl_get_data(data_cad->ground_connection, triangles)); 2009 2010 exit: 2011 return res; 2012 error: 2013 goto exit; 2014 } 2015 2016 res_T 2017 export_stl_cmode_2 2018 (void* cad, 2019 const int binary) 2020 { 2021 res_T res = RES_OK; 2022 struct data_cad_cmode_2* data_cad = (struct data_cad_cmode_2 *)cad; 2023 size_t i = 0, j, common_n = 0, coord_count = 0; 2024 struct darray_double trg; 2025 struct str name; 2026 int initialized = 0; 2027 struct scad_geometry** list = NULL; 2028 struct mem_allocator* allocator = NULL; 2029 struct city* city; 2030 struct building* building; 2031 struct vertex_denoiser* denoiser; 2032 FILE* model = NULL; 2033 FILE* vars = NULL; 2034 fpos_t model_pos, vars_pos; 2035 long model_count = 0; 2036 const char* n; 2037 static int fst = 1; 2038 2039 if(!cad) { 2040 res = RES_BAD_ARG; 2041 goto error; 2042 } 2043 2044 building = data_cad->building; 2045 city = building->city; 2046 allocator = city->allocator; 2047 denoiser = city->denoiser; 2048 model = city->stardis_model; 2049 vars = city->set_vars; 2050 2051 if(!fst) fprintf(model, "\n"); 2052 fst = 0; 2053 fprintf(model, 2054 "# Building '%s' construction mode 2, dataset '%s', %g m tall\n", 2055 str_cget(&building->name), str_cget(&building->dataset_name), 2056 building->total_height); 2057 fgetpos(model, &model_pos); 2058 fprintf(vars, 2059 "\n# Building '%s' construction mode 2, dataset '%s', %g m tall\n", 2060 str_cget(&building->name), str_cget(&building->dataset_name), 2061 building->total_height); 2062 fgetpos(vars, &vars_pos); 2063 2064 if(darray_geometries_size_get(&data_cad->adj_walls) == 0) { 2065 /* wall export */ 2066 ERR(stl_export_denoised_geometry(denoiser, data_cad->wall, 2067 Scad_force_normals_outward, binary)); 2068 2069 /* external insulation export */ 2070 if(data_cad->external_insulation) { 2071 ERR(stl_export_denoised_geometry(denoiser, data_cad->external_insulation, 2072 Scad_force_normals_outward, binary)); 2073 } 2074 } else { 2075 /* There is some adjoining building(s) to manage */ 2076 struct scad_geometry* extern_most = data_cad->external_insulation 2077 ? data_cad->external_insulation : data_cad->wall; 2078 size_t common_count = darray_common_trg_size_get(&data_cad->common_trg); 2079 const char* tmp; 2080 2081 if(data_cad->external_insulation) { 2082 /* wall export is not impacted as walls are not the external layer */ 2083 ERR(stl_export_denoised_geometry(denoiser, data_cad->wall, 2084 Scad_force_normals_outward, binary)); 2085 } 2086 2087 /* The external layer must use the common triangles */ 2088 darray_double_init(allocator, &trg); 2089 str_init(allocator, &name); 2090 initialized = 1; 2091 /* Get the triangles that are not common with adjoining buildings */ 2092 ERR(scad_stl_get_data_partial(extern_most, 2093 darray_geometries_data_get(&data_cad->adj_walls), 2094 darray_geometries_size_get(&data_cad->adj_walls), &trg)); 2095 coord_count = darray_double_size_get(&trg); 2096 /* Add the triangles from adjoining buildings */ 2097 for(i = 0; i < common_count; i++) { 2098 size_t sz; 2099 struct b_pair* pair = darray_common_trg_data_get(&data_cad->common_trg)+ i; 2100 const double *t9; 2101 double* tgt; 2102 struct darray_double* common = NULL; 2103 /* Get triangles */ 2104 common = htable_common_find(&city->common, pair); 2105 if(!common) { 2106 res = RES_BAD_ARG; 2107 goto error; 2108 } 2109 t9 = darray_double_cdata_get(common); 2110 /* Add common triangles */ 2111 sz = darray_double_size_get(common); 2112 ASSERT(sz % 9 == 0); 2113 ASSERT(coord_count == darray_double_size_get(&trg)); 2114 ERR(darray_double_resize(&trg, coord_count + sz)); 2115 tgt = darray_double_data_get(&trg); 2116 for(j = 0; j < sz; j++) { 2117 tgt[coord_count + j] = t9[j]; 2118 } 2119 coord_count += sz; 2120 ASSERT(coord_count % 9 == 0); 2121 } 2122 ERR(scad_geometry_get_name(extern_most, &tmp)); 2123 ERR(str_set(&name, tmp)); 2124 ERR(str_append(&name, ".stl")); 2125 ERR(denoise_array(denoiser, &trg)); 2126 ERR(scad_stl_data_write(&trg, str_cget(&name), Scad_force_normals_outward, 2127 binary)); 2128 } 2129 2130 STARDIS_SOLID(wall); 2131 2132 if(data_cad->external_insulation) { 2133 STARDIS_SOLID(external_insulation); 2134 } 2135 2136 /* floor export */ 2137 ERR(stl_export_denoised_geometry(denoiser, data_cad->floor, 2138 Scad_force_normals_outward, binary)); 2139 2140 STARDIS_SOLID(floor); 2141 2142 /* roof export */ 2143 ERR(stl_export_denoised_geometry(denoiser, data_cad->roof, 2144 Scad_force_normals_outward, binary)); 2145 2146 STARDIS_SOLID(roof); 2147 2148 /* foundation export */ 2149 if(data_cad->foundation) { 2150 ERR(stl_export_denoised_geometry(denoiser, data_cad->foundation, 2151 Scad_force_normals_outward, binary)); 2152 2153 STARDIS_SOLID(foundation); 2154 } 2155 2156 /* glass export */ 2157 if(data_cad->glazing) { 2158 ERR(stl_export_denoised_geometry(denoiser, data_cad->glazing, 2159 Scad_force_normals_outward, binary)); 2160 2161 STARDIS_SOLID(glazing); 2162 } 2163 2164 /* intermediate floor export*/ 2165 if(data_cad->intermediate_floor) { 2166 ERR(stl_export_denoised_geometry(denoiser, data_cad->intermediate_floor, 2167 Scad_force_normals_outward, binary)); 2168 2169 STARDIS_SOLID(intermediate_floor); 2170 } 2171 2172 /* internal insulation export*/ 2173 if(data_cad->internal_insulation) { 2174 ERR(stl_export_denoised_geometry(denoiser, data_cad->internal_insulation, 2175 Scad_force_normals_outward, binary)); 2176 2177 STARDIS_SOLID(internal_insulation); 2178 } 2179 2180 /* roof insulation export*/ 2181 if(data_cad->roof_insulation) { 2182 ERR(stl_export_denoised_geometry(denoiser, data_cad->roof_insulation, 2183 Scad_force_normals_outward, binary)); 2184 2185 STARDIS_SOLID(roof_insulation); 2186 } 2187 2188 /* floor insulation export*/ 2189 if(data_cad->floor_insulation) { 2190 ERR(stl_export_denoised_geometry(denoiser, data_cad->floor_insulation, 2191 Scad_force_normals_outward, binary)); 2192 2193 STARDIS_SOLID(floor_insulation); 2194 } 2195 2196 /* attic cavity export*/ 2197 if(data_cad->attic_cavity) { 2198 ERR(stl_export_denoised_geometry(denoiser, data_cad->attic_cavity, 2199 Scad_force_normals_outward, binary)); 2200 2201 STARDIS_FLUID(attic_cavity); 2202 } 2203 2204 /* habitable cavity export*/ 2205 ERR(stl_export_denoised_geometry(denoiser, data_cad->habitable_cavity, 2206 Scad_force_normals_outward, binary)); 2207 2208 STARDIS_FLUID(habitable_cavity); 2209 2210 /* crawlspace cavity export*/ 2211 if(data_cad->crawlspace_cavity) { 2212 ERR(stl_export_denoised_geometry(denoiser, data_cad->crawlspace_cavity, 2213 Scad_force_normals_outward, binary)); 2214 2215 STARDIS_FLUID(crawlspace_cavity); 2216 } 2217 2218 /* boundary export*/ 2219 for(i = 0; i < darray_geometries_size_get(&data_cad->boundary); i++) { 2220 struct scad_geometry* b = darray_geometries_data_get(&data_cad->boundary)[i]; 2221 ERR(stl_export_denoised_geometry(denoiser, b, Scad_keep_normals_unchanged, 2222 binary)); 2223 2224 ERR(scad_geometry_get_name(b, &n)); 2225 STARDIS_H_BOUND_2(SFC, n); 2226 } 2227 2228 /* connections export*/ 2229 for(i = 0; i < darray_geometries_size_get(&data_cad->connection); i++) { 2230 struct scad_geometry* c = darray_geometries_data_get(&data_cad->connection)[i]; 2231 ERR(stl_export_denoised_geometry(denoiser, c, Scad_keep_normals_unchanged, 2232 binary)); 2233 2234 ERR(scad_geometry_get_name(c, &n)); 2235 STARDIS_SFC_2(SFC, n); 2236 } 2237 2238 /* ground/building connection export*/ 2239 ERR(stl_export_denoised_geometry(denoiser, data_cad->ground_connection, 2240 Scad_keep_normals_unchanged, binary)); 2241 2242 /* No need to describe solid-solid connections until there is a contact 2243 * resistance */ 2244 2245 exit: 2246 for(j = 0; j < common_n; j++) { 2247 if(list[j]) SCAD(geometry_ref_put(list[j])); 2248 } 2249 MEM_RM(allocator, list); 2250 if(initialized) { 2251 darray_double_release(&trg); 2252 str_release(&name); 2253 } 2254 return res; 2255 error: 2256 if(data_cad) { 2257 logger_print(data_cad->building->city->logger, LOG_ERROR, 2258 "Internal error '"__FILE__"': creating STL for building '%s'.\n", 2259 str_cget(&data_cad->building->name)); 2260 } 2261 /* Reset stardis model file */ 2262 if(model) { 2263 long l; 2264 fsetpos(model, &model_pos); 2265 for(l = 0; l < model_count; l += 40) 2266 fprintf(model, " \n"); 2267 fprintf(model, "# Building '%s' construction cancelled\n", 2268 str_cget(&building->name)); 2269 } 2270 if(vars) { 2271 fprintf(vars, "# Building '%s' construction cancelled\n", 2272 str_cget(&building->name)); 2273 } 2274 goto exit; 2275 } 2276 2277 res_T 2278 release_cad_cmode_2 2279 (void* cad) 2280 { 2281 res_T res = RES_OK; 2282 struct data_cad_cmode_2* data_cad = (struct data_cad_cmode_2 *)cad; 2283 struct mem_allocator* allocator; 2284 2285 if(!cad) { 2286 res = RES_BAD_ARG; 2287 goto error; 2288 } 2289 2290 allocator = data_cad->building->city->allocator; 2291 darray_geometries_release(&data_cad->boundary); 2292 darray_geometries_release(&data_cad->connection); 2293 darray_common_trg_release(&data_cad->common_trg); 2294 darray_geometries_release(&data_cad->adj_walls); 2295 2296 #define GDEL(Field) \ 2297 if(data_cad->Field) SCAD(geometry_ref_put(data_cad->Field)); \ 2298 /* To ease debugging, write NULL after deletion */ \ 2299 data_cad->Field = NULL 2300 GDEL(attic_cavity); 2301 GDEL(crawlspace_cavity); 2302 GDEL(external_insulation); 2303 GDEL(fake_ground); 2304 GDEL(floor); 2305 GDEL(floor_insulation); 2306 GDEL(foundation); 2307 GDEL(glazing); 2308 GDEL(ground_connection); 2309 GDEL(habitable_cavity); 2310 GDEL(intermediate_floor); 2311 GDEL(internal_insulation); 2312 GDEL(roof); 2313 GDEL(roof_insulation); 2314 GDEL(wall); 2315 #undef GDEL 2316 MEM_RM(allocator, data_cad); 2317 2318 exit: 2319 return res; 2320 error: 2321 goto exit; 2322 }