cg_construction_mode_0.c (26834B)
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_building.h" 23 #include "cg_catalog.h" 24 #include "cg_city.h" 25 #include "cg_city_parsing_schemas.h" 26 #include "cg_construction_mode_0.h" 27 #include "cg_construction_mode.h" 28 #include "cg_vertex_denoiser.h" 29 #include "cg_stardis_model.h" 30 31 #include <rsys/rsys.h> 32 #include <rsys/str.h> 33 #include <rsys/logger.h> 34 #include <rsys/double3.h> 35 36 #include <star/scad.h> 37 #include <star/scpr.h> 38 39 static res_T 40 build_footprint 41 (struct scpr_polygon* pg, 42 const double z, 43 struct scad_geometry** footprint) 44 { 45 res_T res = RES_OK; 46 size_t nverts = 0; 47 48 ASSERT(pg && footprint); 49 50 ERR(scpr_polygon_get_vertices_count(pg, 0, &nverts)); 51 ERR(scad_add_polygon(NULL, get_position_pg, pg, z, nverts, footprint)); 52 53 exit: 54 return res; 55 error: 56 goto exit; 57 } 58 59 static res_T 60 build_floor 61 (const char* prefix, 62 struct scpr_polygon* pg, 63 struct building* b, 64 struct darray_geometries* current_cad, 65 struct scad_geometry** floor) 66 { 67 res_T res = RES_OK; 68 struct dataset_cmode_0* data; 69 struct scad_geometry* footprint = NULL; 70 double d[3] = {0, 0, 0}; 71 char* floorname = NULL; 72 struct str name; 73 74 ASSERT(prefix && pg && b && floor); 75 76 str_init(NULL, &name); 77 ERR(str_set(&name, prefix)); 78 ERR(str_append(&name, "_S_floor")); 79 floorname = str_get(&name); 80 81 ERR(build_footprint(pg, 0, &footprint)); 82 83 data = (struct dataset_cmode_0*)b->data; 84 d[2] = data->floor_thickness; 85 ERR(scad_geometry_extrude(footprint, floorname, d, floor)); 86 ERR(darray_geometries_push_back(current_cad, floor)); 87 88 exit: 89 if(footprint) SCAD(geometry_ref_put(footprint)); 90 str_release(&name); 91 return res; 92 error: 93 goto exit; 94 } 95 96 static res_T 97 build_fake_ground 98 (struct scpr_polygon* pg, 99 struct scad_geometry** fake_ground) 100 { 101 res_T res = RES_OK; 102 double d[3] = {0, 0, -1}; 103 struct scad_geometry* footprint = NULL; 104 105 ASSERT(pg && fake_ground); 106 107 ERR(build_footprint(pg, 0, &footprint)); 108 ERR(scad_geometry_extrude(footprint, "fake_ground", d, fake_ground)); 109 110 exit: 111 if(footprint) SCAD(geometry_ref_put(footprint)); 112 return res; 113 error: 114 goto exit; 115 } 116 117 static res_T 118 build_roof 119 (const char* prefix, 120 struct building* b, 121 const struct scad_geometry* floor, 122 struct darray_geometries* current_cad, 123 struct scad_geometry** roof) 124 { 125 res_T res = RES_OK; 126 double height; 127 double e; 128 double d[3] = {0, 0, 0}; 129 struct dataset_cmode_0* data; 130 char* roofname = NULL; 131 struct str name; 132 133 ASSERT(prefix && b && floor && roof); 134 135 str_init(NULL, &name); 136 ERR(str_set(&name, prefix)); 137 ERR(str_append(&name, "_S_roof")); 138 roofname = str_get(&name); 139 140 height = b->total_height; 141 data = (struct dataset_cmode_0*)b->data; 142 e = data->floor_thickness; 143 d[2] = height - e ; 144 ERR(scad_geometry_translate(floor, d, roofname, roof)); 145 ERR(darray_geometries_push_back(current_cad, roof)); 146 147 exit: 148 str_release(&name); 149 return res; 150 error: 151 goto exit; 152 } 153 154 static res_T 155 build_wall_footprint 156 (struct scpr_polygon* pg, 157 struct scpr_polygon* pg_int, 158 struct scad_geometry** footprint) 159 { 160 res_T res = RES_OK; 161 struct scad_geometry* polygon = NULL; 162 struct scad_geometry* polygon_int = NULL; 163 164 ASSERT(pg && pg_int && footprint); 165 166 ERR(build_footprint(pg, 0, &polygon)); 167 ERR(build_footprint(pg_int, 0, &polygon_int)); 168 ERR(scad_cut_geometries(NULL, &polygon, 1, &polygon_int, 1, footprint)); 169 170 exit: 171 if(polygon) SCAD(geometry_ref_put(polygon)); 172 if(polygon_int) SCAD(geometry_ref_put(polygon_int)); 173 return res; 174 error: 175 goto exit; 176 } 177 178 static res_T 179 build_wall 180 (const char* prefix, 181 struct scpr_polygon* pg, 182 struct scpr_polygon* pg_int, 183 struct building* b, 184 struct darray_geometries* current_cad, 185 struct scad_geometry** wall) 186 { 187 res_T res = RES_OK; 188 struct scad_geometry* footprint = NULL; 189 double d[3] = {0, 0, 0}; 190 char* wallname = NULL; 191 struct str name; 192 193 ASSERT(prefix && pg && pg_int && b && wall); 194 195 str_init(NULL, &name); 196 ERR(str_set(&name, prefix)); 197 ERR(str_append(&name, "_S_walls")); 198 wallname = str_get(&name); 199 200 ERR(build_wall_footprint(pg, pg_int, &footprint)); 201 202 d[2] = b->total_height; 203 ERR(scad_geometry_extrude(footprint, wallname, d, wall)); 204 ERR(darray_geometries_push_back(current_cad, wall)); 205 206 exit: 207 if(footprint) SCAD(geometry_ref_put(footprint)); 208 str_release(&name); 209 return res; 210 error: 211 goto exit; 212 } 213 214 static res_T 215 build_cavity 216 (const char* prefix, 217 struct scpr_polygon* pg, 218 struct building* b, 219 struct darray_geometries* current_cad, 220 struct scad_geometry** cavity) 221 { 222 res_T res = RES_OK; 223 double e, height; 224 struct dataset_cmode_0* data; 225 double d[3] = {0, 0, 0}; 226 struct scad_geometry* polygon = NULL; 227 char* cavityname = NULL; 228 struct str name; 229 230 ASSERT(prefix && pg && b && cavity); 231 232 height = b->total_height; 233 data = (struct dataset_cmode_0*)b->data; 234 e = data->floor_thickness; 235 236 str_init(NULL, &name); 237 ERR(str_set(&name, prefix)); 238 ERR(str_append(&name, "_F_internal")); 239 cavityname = str_get(&name); 240 241 ERR(build_footprint(pg, e, &polygon)); 242 243 d[2] = height - 2*e; 244 ERR(scad_geometry_extrude(polygon, cavityname, d, cavity)); 245 ERR(darray_geometries_push_back(current_cad, cavity)); 246 247 exit: 248 if(polygon) SCAD(geometry_ref_put(polygon)); 249 str_release(&name); 250 return res; 251 error: 252 goto exit; 253 } 254 255 static res_T 256 build_connection 257 (const char* prefix, 258 struct mem_allocator* allocator, 259 struct data_cad_cmode_0* cad) 260 { 261 res_T res = RES_OK; 262 char* cname = NULL; 263 struct str name; 264 int is_init = 0; 265 266 ASSERT(prefix && allocator && cad); 267 268 cad->connection = MEM_CALLOC(allocator, 3, sizeof(struct scad_geometry*)); 269 if(!cad->connection) { 270 res = RES_MEM_ERR; 271 goto error; 272 } 273 cad->n_connection = 3; 274 275 /* cavity/floor connection */ 276 str_init(allocator, &name); 277 is_init = 1; 278 ERR(str_set(&name, prefix)); 279 ERR(str_append(&name, "_C_internal_floor")); 280 cname = str_get(&name); 281 ERR(scad_geometries_common_boundaries(cname, &cad->cavity, 1, &cad->floor, 1, 282 &cad->connection[0])); 283 284 /* cavity/wall connection */ 285 ERR(str_set(&name, prefix)); 286 ERR(str_append(&name, "_C_internal_walls")); 287 cname = str_get(&name); 288 ERR(scad_geometries_common_boundaries(cname, &cad->cavity, 1, &cad->wall, 1, 289 &cad->connection[1])); 290 291 /* cavity/roof connection */ 292 ERR(str_set(&name, prefix)); 293 ERR(str_append(&name, "_C_internal_roof")); 294 cname = str_get(&name); 295 ERR(scad_geometries_common_boundaries(cname, &cad->cavity, 1, &cad->roof, 1, 296 &cad->connection[2])); 297 298 exit: 299 if(is_init) str_release(&name); 300 return res; 301 error: 302 goto exit; 303 } 304 305 static res_T 306 build_boundary 307 (const char* prefix, 308 struct mem_allocator* allocator, 309 struct darray_adjoining_data* adjoining_data, 310 struct data_cad_cmode_0* data_cad) 311 { 312 res_T res = RES_OK; 313 struct scad_geometry** list = NULL; 314 char* boundaryname = NULL; 315 struct str name; 316 int is_init = 0; 317 struct adjoining_data* adj; 318 size_t adjoining_n, i = 0, maxc, count = 0; 319 320 ASSERT(prefix && allocator && adjoining_data && data_cad); 321 322 adjoining_n = darray_adjoining_data_size_get(adjoining_data); 323 adj = darray_adjoining_data_data_get(adjoining_data); 324 str_init(allocator, &name); 325 is_init = 1; 326 327 maxc = 5 + adjoining_n; 328 list = MEM_CALLOC(allocator, maxc, sizeof(struct scad_geometry*)); 329 if(!list) { 330 res = RES_MEM_ERR; 331 goto error; 332 } 333 /* Using wall here to compute boundary_wall is OK even if it doesn't take care 334 * of conformity wrt the adjoining buildings. The reason is that the common 335 * part that could end to be not conformal is not part of the boundary. As a 336 * consequence it cannot be part of the result. 337 * However, if the adjoining building is later removed, this part must then be 338 * created as an additional file. */ 339 count = 5; 340 list[0] = data_cad->floor; 341 list[1] = data_cad->roof; 342 list[2] = data_cad->wall; 343 list[3] = data_cad->cavity; 344 list[4] = data_cad->fake_ground; 345 for(i = 0; i < adjoining_n; i++) { 346 /* Here we consider truly adjoining buildings, except removed ones (early 347 * removed ones are not part of adjoining) */ 348 if(adj[i].really_adjoining 349 && !(adj[i].adjoining_building->event_flags & BUILDING_REMOVED)) 350 { 351 ASSERT(maxc > count); 352 list[count++] = adj[i].envelop; 353 } 354 } 355 356 ERR(str_set(&name, prefix)); 357 ERR(str_append(&name, "_B_walls")); 358 boundaryname = str_get(&name); 359 ERR(scad_geometries_common_boundaries(boundaryname, list, count, 360 &data_cad->wall, 1, &data_cad->boundary_wall)); 361 362 ERR(str_set(&name, prefix)); 363 ERR(str_append(&name, "_B_roof")); 364 boundaryname = str_get(&name); 365 ERR(scad_geometries_common_boundaries(boundaryname, list, count, 366 &data_cad->roof, 1, &data_cad->boundary_roof)); 367 368 exit: 369 MEM_RM(allocator, list); 370 if(is_init) str_release(&name); 371 return res; 372 error: 373 goto exit; 374 } 375 376 static res_T 377 building_ground_connection 378 (struct mem_allocator* allocator, 379 const char* prefix, 380 struct data_cad_cmode_0* cad, 381 struct scad_geometry** connection) 382 { 383 res_T res = RES_OK; 384 struct scad_geometry* list[2] = { NULL, NULL }; 385 char* cname = NULL; 386 struct str name; 387 int is_init = 0; 388 389 ASSERT(allocator && prefix && connection); 390 391 str_init(allocator, &name); 392 is_init = 1; 393 ERR(str_set(&name, prefix)); 394 ERR(str_append(&name, "_C_ground")); 395 cname = str_get(&name); 396 397 list[0] = cad->wall; 398 list[1] = cad->floor; 399 400 ERR(scad_geometries_common_boundaries(cname, list, 2, &cad->fake_ground, 1, 401 connection)); 402 403 exit: 404 if(is_init) str_release(&name); 405 return res; 406 error: 407 goto exit; 408 } 409 410 /*----------------------------------------------------------------------------*/ 411 /*----------------------------------------------------------------------------*/ 412 /*----------------------------------------------------------------------------*/ 413 414 res_T 415 init_cmode_0 416 (struct building* building, 417 struct city* city, 418 struct parsed_city_building* parsed_data, 419 struct catalog* catalog, 420 const double lower[2], 421 const double upper[2]) 422 { 423 res_T res = RES_OK; 424 struct str dataset_name; 425 int name_initialized = 0; 426 static struct construction_mode_functors functors_0 = { 427 &init_cmode_0, 428 &release_cmode_0, 429 &build_cad_cmode_0, 430 &build_footprint_cmode_0, 431 &save_ground_connection_triangles_0, 432 &export_stl_cmode_0, 433 &release_cad_cmode_0 434 }; 435 struct mem_allocator* allocator; 436 struct logger* logger; 437 438 if(!city || !building || !parsed_data || !catalog || !lower || !upper) { 439 res = RES_BAD_ARG; 440 goto error; 441 } 442 443 allocator = city->allocator; 444 logger = city->logger; 445 building->construction_mode = mode_0; 446 building->functors = &functors_0; 447 448 ERR(init_building_base(building, city, parsed_data)); 449 450 if(parsed_data->levels_height_count != 0) { 451 ERR(logger_print(city->logger, LOG_ERROR, 452 "Building '%s' defines 'levels_height' " 453 "(feature not available in construction mode 0).\n", 454 str_cget(&building->name))); 455 res = RES_BAD_ARG; 456 goto error; 457 } 458 459 if(parsed_data->height <= 0) { 460 ERR(logger_print(city->logger, LOG_ERROR, 461 "Building '%s' height definition is invalid " 462 "(construction mode 0 should define a positive height through the 'height' field).\n", 463 str_cget(&building->name))); 464 res = RES_BAD_ARG; 465 goto error; 466 } 467 building->total_height = parsed_data->height; 468 469 str_init(allocator, &dataset_name); 470 name_initialized = 1; 471 ERR(str_set(&dataset_name, parsed_data->dataset_name)); 472 473 building->data = htable_dataset_cmode_0_find(&catalog->catalog_0, &dataset_name); 474 if(building->data == NULL) { 475 ERR(logger_print(logger, LOG_ERROR, 476 "Unknown dataset name: '%s' used by building '%s'.\n", 477 str_cget(&dataset_name), str_cget(&building->name))); 478 res = RES_BAD_ARG; 479 goto error; 480 } 481 ERR(str_set(&building->external_layer_name, "walls")); 482 483 exit: 484 if(name_initialized) str_release(&dataset_name); 485 return res; 486 error: 487 goto exit; 488 } 489 490 res_T 491 release_cmode_0 492 (struct building* building) 493 { 494 if(!building) return RES_BAD_ARG; 495 return release_building_base(building); 496 } 497 498 res_T 499 build_cad_cmode_0 500 (struct building* building, 501 int dump_footprints_level, 502 int keep_running_on_errors, 503 struct darray_adjoining_data* adjoining_data, 504 struct darray_geometries* current_cad, 505 void** cad) 506 { 507 res_T res = RES_OK; 508 double height; 509 struct scpr_polygon* pg_int = NULL; 510 struct dataset_cmode_0* data = NULL; 511 struct data_cad_cmode_0* data_cad = NULL; 512 double e_wall; 513 const char* name; 514 size_t adjoining_n = 0; 515 struct scpr_intersector* overlapping_intersector = NULL; 516 struct scpr_intersector_check_callbacks callbacks 517 = SCPR_INTERSECTOR_CHECK_CALLBACKS_NULL__; 518 struct callback_ctx ctx = CB_CTX_NULL__; 519 int error_occured = 0, error_msg_printed = 0; 520 struct scpr_device* scpr; 521 struct mem_allocator* allocator; 522 struct logger* logger = NULL; 523 size_t i, c, cad_count; 524 struct scad_geometry** cur_cad = NULL; 525 struct scad_geometry** partitioned = NULL; 526 527 if(!building || !cad || !adjoining_data) { 528 res = RES_BAD_ARG; 529 goto error; 530 } 531 532 name = str_cget(&building->name); 533 scpr = building->city->scpr; 534 allocator = building->city->allocator; 535 logger = building->city->logger; 536 height = building->total_height; 537 data = (struct dataset_cmode_0 *)building->data; 538 539 logger_print(logger, LOG_OUTPUT, 540 "Building '%s' construction mode 0, dataset '%s', %g m tall.\n", 541 name, str_cget(&building->dataset_name), building->total_height); 542 543 if(height <= 0 || data->wall_thickness <= 0 || data->floor_thickness <= 0) { 544 res = RES_BAD_ARG; 545 goto error; 546 } 547 548 data_cad = MEM_CALLOC(allocator, 1, sizeof(struct data_cad_cmode_0)); 549 if(!data_cad) { 550 res = RES_MEM_ERR; 551 goto error; 552 } 553 building->data_cad = data_cad; 554 data_cad->building = building; 555 darray_common_trg_init(allocator, &data_cad->common_trg); 556 darray_geometries_init(allocator, &data_cad->adj_walls); 557 558 ERR(scpr_intersector_create(scpr, &overlapping_intersector)); 559 ERR(scpr_intersector_register_polygon(overlapping_intersector, building->pg)); 560 561 e_wall = data->wall_thickness; 562 ERR(scpr_polygon_create_copy(scpr, building->pg, &pg_int)); 563 ERR(scpr_offset_polygon(pg_int, -e_wall, SCPR_JOIN_MITER)); 564 ERR(scpr_polygon_get_components_count(pg_int, &c)); 565 if(c != 1) { 566 logger_print(logger, (keep_running_on_errors ? LOG_WARNING : LOG_ERROR), 567 "Building '%s' shape is not compatible with wall thickness.\n", name); 568 building->event_flags |= BUILDING_REMOVED; 569 error_msg_printed = 1; 570 res = RES_BAD_ARG; 571 goto error; 572 } 573 ERR(scpr_intersector_register_polygon(overlapping_intersector, pg_int)); 574 575 /* Check for registered polygons overlapping */ 576 ctx.city = building->city; 577 ctx.buildings = building; 578 ctx.buildings_count = 1; 579 ctx.intersection_found = &error_occured; 580 ctx.search_type = TESTING_1_BUILDING_INTERNALS; 581 ctx.dump_footprints_level = dump_footprints_level; 582 ctx.keep_running_on_errors = keep_running_on_errors; 583 callbacks.simple_intersection = simple_intersection; 584 ERR(scpr_intersector_check(overlapping_intersector, &callbacks, &ctx)); 585 if(error_occured) { 586 logger_print(logger, LOG_ERROR, 587 "Internal error generating CAD for building '%s'.\n", name); 588 building->event_flags |= BUILDING_REMOVED; 589 error_msg_printed = 1; 590 res = RES_BAD_ARG; 591 goto error; 592 } 593 594 /* build floor with pg_int */ 595 ERR(build_floor(name, pg_int, building, current_cad, &data_cad->floor)); 596 597 /* roof is a translated copy of floor */ 598 ERR(build_roof(name, building, data_cad->floor, current_cad, &data_cad->roof)); 599 600 /* build wall with pg and pg_int */ 601 ERR(build_wall(name, building->pg, pg_int, building, current_cad, &data_cad->wall)); 602 603 /* build cavity */ 604 ERR(build_cavity(name, pg_int, building, current_cad, &data_cad->cavity)); 605 606 /* build adjoining envelop */ 607 adjoining_n = htable_building_size_get(&building->close_buildings); 608 if(adjoining_n > 0) { 609 ERR(build_adjoining(building, 0, current_cad, adjoining_data)); 610 } 611 612 /* build fake ground */ 613 ERR(build_fake_ground(building->pg, &data_cad->fake_ground)); 614 ERR(darray_geometries_push_back(current_cad, &data_cad->fake_ground)); 615 616 /* Partition CAD */ 617 cad_count = darray_geometries_size_get(current_cad); 618 partitioned = MEM_CALLOC(allocator, cad_count, sizeof(*partitioned)); 619 if(!partitioned) { 620 res = RES_MEM_ERR; 621 goto error; 622 } 623 cur_cad = darray_geometries_data_get(current_cad); 624 ERR(scad_geometries_partition(cur_cad, cad_count, Scad_dump_on_overlapping_error, 625 partitioned)); 626 /* Swap original geometry and partitioned geometry in data_cad (was 627 * accumulated into current_cad) */ 628 ERR(scad_geometries_swap(cur_cad, partitioned, cad_count, Scad_swap_geometry)); 629 630 /* After partitioning, manage common parts with other buildings */ 631 adjoining_n = darray_adjoining_data_size_get(adjoining_data); 632 if(adjoining_n > 0) { 633 size_t a; 634 struct b_pair pair; 635 struct darray_double* common; 636 struct adjoining_data* adjoining 637 = darray_adjoining_data_data_get(adjoining_data); 638 for(a = 0; a < adjoining_n; a++) { 639 struct adjoining_data* adj = adjoining + a; 640 ERR(scad_geometries_common_boundaries(NULL, &data_cad->wall, 1, 641 &adj->envelop, 1, &adj->common_geometry)); 642 ERR(scad_geometry_get_count(adj->common_geometry, &c)); 643 adj->really_adjoining = (c != 0); 644 if(!adj->really_adjoining) { 645 logger_print(logger, LOG_OUTPUT, 646 "Building '%s': neighbor '%s' not really adjoining.\n", 647 str_cget(&building->name), 648 str_cget(&adj->adjoining_building->name)); 649 continue; 650 } 651 make_b_pair(&pair, building, adj->adjoining_building); 652 /* Keep track of the geometry to replace and the mesh to output instead */ 653 ERR(darray_geometries_push_back(&data_cad->adj_walls, &adj->common_geometry)); 654 ERR(darray_common_trg_push_back(&data_cad->common_trg, &pair)); 655 common = htable_common_find(&building->city->common, &pair); 656 if(!common) { 657 /* The mesh doesn't exist yet and won't be created until a further step. 658 * We need to store the geometry id so that the mesh can be stored when 659 * created. */ 660 adj->save = 1; 661 } 662 } 663 } 664 665 /* build ground/building connection */ 666 ERR(building_ground_connection(allocator, name, data_cad, 667 &data_cad->ground_connection)); 668 669 /* build boundary */ 670 ERR(build_boundary(name, allocator, adjoining_data, data_cad)); 671 672 /* build cavity/floor connectiona*/ 673 ERR(build_connection(name, allocator, data_cad)); 674 675 exit: 676 if(partitioned) { 677 for(i = 0; i < cad_count; i++) { 678 if(partitioned[i]) SCAD(geometry_ref_put(partitioned[i])); 679 } 680 MEM_RM(allocator, partitioned); 681 } 682 if(pg_int) SCPR(polygon_ref_put(pg_int)); 683 if(overlapping_intersector) SCPR(intersector_ref_put(overlapping_intersector)); 684 if(cad) *(struct data_cad_cmode_0**)cad = data_cad; 685 return res; 686 error: 687 if(logger && building && !error_msg_printed) { 688 logger_print(logger, LOG_ERROR, 689 "Unknown error generating CAD for building '%s'.\n", 690 str_cget(&building->name)); 691 } 692 if(data_cad) CHK(RES_OK == release_cad_cmode_0(data_cad)); 693 data_cad = NULL; 694 goto exit; 695 } 696 697 res_T 698 build_footprint_cmode_0 699 (struct building* building, 700 struct scad_geometry** footprint) 701 { 702 res_T res = RES_OK; 703 704 if(!building || ! footprint) { 705 res = RES_BAD_ARG; 706 goto error; 707 } 708 709 ERR(build_footprint(building->pg, 0, footprint)); 710 711 exit: 712 return res; 713 error: 714 goto exit; 715 } 716 717 res_T save_ground_connection_triangles_0 718 (void* cad, 719 struct darray_double* triangles) 720 { 721 res_T res = RES_OK; 722 struct data_cad_cmode_0* data_cad = cad; 723 724 if(!cad || !triangles) { 725 res = RES_BAD_ARG; 726 goto error; 727 } 728 729 ERR(scad_stl_get_data(data_cad->ground_connection, triangles)); 730 731 exit: 732 return res; 733 error: 734 goto exit; 735 } 736 737 res_T 738 export_stl_cmode_0 739 (void* cad, 740 const int binary) 741 { 742 res_T res = RES_OK; 743 struct data_cad_cmode_0* data_cad = (struct data_cad_cmode_0*)cad; 744 size_t i, j, coord_count = 0; 745 struct darray_double trg; 746 struct str name; 747 int initialized = 0; 748 struct mem_allocator* allocator = NULL; 749 struct city* city; 750 struct building* building; 751 struct vertex_denoiser* denoiser; 752 FILE* model = NULL; 753 FILE* vars = NULL; 754 fpos_t model_pos, vars_pos; 755 long model_count = 0; 756 const char* n; 757 static int fst = 1; 758 759 if(!cad) { 760 res = RES_BAD_ARG; 761 goto error; 762 } 763 764 building = data_cad->building; 765 city = building->city; 766 allocator = city->allocator; 767 denoiser = city->denoiser; 768 model = city->stardis_model; 769 vars = city->set_vars; 770 771 if(!fst) fprintf(model, "\n"); 772 fst = 0; 773 fprintf(model, 774 "# Building '%s' construction mode 0, dataset '%s', %g m tall\n", 775 str_cget(&building->name), str_cget(&building->dataset_name), 776 building->total_height); 777 fgetpos(model, &model_pos); 778 fprintf(vars, 779 "\n# Building '%s' construction mode 0, dataset '%s', %g m tall\n", 780 str_cget(&building->name), str_cget(&building->dataset_name), 781 building->total_height); 782 fgetpos(vars, &vars_pos); 783 784 /* wall export */ 785 if(darray_geometries_size_get(&data_cad->adj_walls) == 0) { 786 ERR(stl_export_denoised_geometry(denoiser, data_cad->wall, 787 Scad_force_normals_outward, binary)); 788 } else { 789 /* There is some adjoining building(s) to manage */ 790 size_t common_count = darray_common_trg_size_get(&data_cad->common_trg); 791 const char* tmp; 792 793 darray_double_init(allocator, &trg); 794 str_init(allocator, &name); 795 initialized = 1; 796 /* Get the triangles that are not common with adjoining buildings */ 797 ERR(scad_stl_get_data_partial(data_cad->wall, 798 darray_geometries_data_get(&data_cad->adj_walls), 799 darray_geometries_size_get(&data_cad->adj_walls), &trg)); 800 coord_count = darray_double_size_get(&trg); 801 /* Add the triangles from adjoining buildings */ 802 for(i = 0; i < common_count; i++) { 803 size_t sz; 804 struct b_pair* pair = darray_common_trg_data_get(&data_cad->common_trg)+ i; 805 const double *t9; 806 double* tgt; 807 struct darray_double* common = NULL; 808 /* Get triangles */ 809 common = htable_common_find(&city->common, pair); 810 if(!common) { 811 res = RES_BAD_ARG; 812 goto error; 813 } 814 t9 = darray_double_cdata_get(common); 815 /* Add common triangles */ 816 sz = darray_double_size_get(common); 817 ASSERT(sz % 9 == 0); 818 ASSERT(coord_count == darray_double_size_get(&trg)); 819 ERR(darray_double_resize(&trg, coord_count + sz)); 820 tgt = darray_double_data_get(&trg); 821 for(j = 0; j < sz; j++) { 822 tgt[coord_count + j] = t9[j]; 823 } 824 coord_count += sz; 825 ASSERT(coord_count % 9 == 0); 826 } 827 ERR(scad_geometry_get_name(data_cad->wall, &tmp)); 828 ERR(str_set(&name, tmp)); 829 ERR(str_append(&name, ".stl")); 830 ERR(denoise_array(denoiser, &trg)); 831 ERR(scad_stl_data_write(&trg, str_cget(&name), Scad_force_normals_outward, 832 binary)); 833 } 834 835 STARDIS_SOLID(wall); 836 837 /* floor export */ 838 ERR(stl_export_denoised_geometry(denoiser, data_cad->floor, 839 Scad_force_normals_outward, binary)); 840 841 STARDIS_SOLID(floor); 842 843 /* roof export */ 844 ERR(stl_export_denoised_geometry(denoiser, data_cad->roof, 845 Scad_force_normals_outward, binary)); 846 847 STARDIS_SOLID(roof); 848 849 /* cavity export */ 850 ERR(stl_export_denoised_geometry(denoiser, data_cad->cavity, 851 Scad_force_normals_outward, binary)); 852 853 STARDIS_FLUID(cavity); 854 855 /* connection export */ 856 for(i = 0; i < data_cad->n_connection; i++) { 857 struct scad_geometry* c = data_cad->connection[i]; 858 ERR(stl_export_denoised_geometry(denoiser, c, 859 Scad_keep_normals_unchanged, binary)); 860 861 ERR(scad_geometry_get_name(c, &n)); 862 STARDIS_SFC_2(SFC, n); 863 } 864 865 /* boundary export */ 866 ERR(stl_export_denoised_geometry(denoiser, data_cad->boundary_wall, 867 Scad_keep_normals_unchanged, binary)); 868 STARDIS_H_BOUND(boundary_wall); 869 870 ERR(stl_export_denoised_geometry(denoiser, data_cad->boundary_roof, 871 Scad_keep_normals_unchanged, binary)); 872 873 STARDIS_H_BOUND(boundary_roof); 874 875 /* ground/building connection export*/ 876 ERR(stl_export_denoised_geometry(denoiser, data_cad->ground_connection, 877 Scad_keep_normals_unchanged, binary)); 878 879 /* No need to describe solid-solid connections until there is a contact 880 * resistance */ 881 882 exit: 883 if(initialized) { 884 darray_double_release(&trg); 885 str_release(&name); 886 } 887 return res; 888 error: 889 if(data_cad) { 890 logger_print(data_cad->building->city->logger, LOG_ERROR, 891 "Internal error '"__FILE__"': creating STL for building '%s'.\n", 892 str_cget(&data_cad->building->name)); 893 } 894 /* Reset stardis model file */ 895 if(model) { 896 long l; 897 fsetpos(model, &model_pos); 898 for(l = 0; l < model_count; l += 40) 899 fprintf(model, " \n"); 900 fprintf(model, "# Building '%s' construction cancelled\n", 901 str_cget(&building->name)); 902 } 903 if(vars) { 904 fprintf(vars, "# Building '%s' construction cancelled\n", 905 str_cget(&building->name)); 906 } 907 goto exit; 908 } 909 910 res_T 911 release_cad_cmode_0 912 (void* cad) 913 { 914 res_T res = RES_OK; 915 struct data_cad_cmode_0* data_cad = (struct data_cad_cmode_0 *)cad; 916 struct mem_allocator* allocator; 917 size_t i; 918 919 if(!cad) { 920 res = RES_BAD_ARG; 921 goto error; 922 } 923 924 allocator = data_cad->building->city->allocator; 925 darray_common_trg_release(&data_cad->common_trg); 926 darray_geometries_release(&data_cad->adj_walls); 927 928 #define GDEL(Field) \ 929 if(data_cad->Field) SCAD(geometry_ref_put(data_cad->Field)); \ 930 /* To ease debugging, set to NULL after deletion */ \ 931 data_cad->Field = NULL 932 GDEL(cavity); 933 GDEL(floor); 934 GDEL(ground_connection); 935 GDEL(roof); 936 GDEL(wall); 937 GDEL(fake_ground); 938 GDEL(boundary_wall); 939 GDEL(boundary_roof); 940 for(i = 0; i < data_cad->n_connection; i++) { 941 GDEL(connection[i]); 942 } 943 #undef GDEL 944 MEM_RM(allocator, data_cad->connection); 945 MEM_RM(allocator, data_cad); 946 947 exit: 948 return res; 949 error: 950 goto exit; 951 }