cg_city.c (35534B)
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_default.h" 22 #include "cg_city.h" 23 #include "cg_construction_mode_0.h" 24 #include "cg_construction_mode_1.h" 25 #include "cg_construction_mode_2.h" 26 #include "cg_ground.h" 27 #include "cg_catalog_parsing.h" 28 #include "cg_city_parsing.h" 29 #include "cg_building.h" 30 #include "cg_args.h" 31 #include "cg_city_parsing_schemas.h" 32 #include "cg_vertex_denoiser.h" 33 #include "cg_stardis_model.h" 34 35 #include <rsys/rsys.h> 36 #include <rsys/logger.h> 37 #include <rsys/mem_allocator.h> 38 #include <rsys/double4.h> 39 #include <rsys/hash_table.h> 40 #include <rsys/dynamic_array.h> 41 #include <rsys/clock_time.h> 42 #include <rsys/str.h> 43 44 #include <star/scad.h> 45 #include <star/scpr.h> 46 47 #include <string.h> 48 49 res_T 50 darray_double_merge 51 (struct darray_double* dest, 52 const struct darray_double* src) 53 { 54 res_T res = RES_OK; 55 size_t count, i; 56 const double* data; 57 58 if(!dest || !src) { 59 res = RES_BAD_ARG; 60 goto error; 61 } 62 63 count = darray_double_size_get(src); 64 data = darray_double_cdata_get(src); 65 ERR(darray_double_reserve(dest, darray_double_size_get(dest) + count)); 66 for(i = 0; i < count; i++) { 67 ERR(darray_double_push_back(dest, data + i)); 68 } 69 70 exit: 71 return res; 72 error: 73 goto exit; 74 } 75 76 void 77 make_b_pair 78 (struct b_pair* pair, 79 struct building* b1, 80 struct building* b2) 81 { 82 ASSERT(pair && b1 && b2 && b1 != b2); 83 pair->b1 = MMIN(b1, b2); 84 pair->b2 = MMAX(b1, b2); 85 } 86 87 res_T 88 dump_footprint_to_obj 89 (struct mem_allocator* allocator, 90 struct building* building, 91 struct scpr_polygon* alternate_polygon) /* Can be NULL */ 92 { 93 res_T res = RES_OK; 94 FILE* stream = NULL; 95 struct str filename; 96 struct scpr_polygon* p; 97 98 ASSERT(allocator && building); 99 100 /* No need to dump twice */ 101 if(building->event_flags & BUILDING_DUMPED_TO_OBJ) return res; 102 103 str_init(allocator, &filename); 104 ERR(str_copy(&filename, &building->name)); 105 ERR(str_append(&filename, "_footprint.obj")); 106 stream = fopen(str_cget(&filename), "w"); 107 p = alternate_polygon ? alternate_polygon : building->pg; 108 ERR(scpr_polygon_dump_to_obj(p, stream)); 109 building->event_flags |= BUILDING_DUMPED_TO_OBJ; 110 exit: 111 if(stream) fclose(stream); 112 str_release(&filename); 113 return res; 114 error: 115 goto exit; 116 } 117 118 #define STORE_CLOSE_INFO(Building1, Building2, Proximity) {\ 119 unsigned char *ptr__, tmp__; \ 120 ASSERT((Building1) != (Building2)); \ 121 ptr__ = htable_building_find(&(Building1)->close_buildings, &(Building2)); \ 122 tmp__ = (unsigned char)(ptr__ ? ((Proximity) | *ptr__) : (Proximity)); \ 123 ERR(htable_building_set(&(Building1)->close_buildings, &(Building2), &tmp__)); \ 124 } 125 126 int overlapping_segments 127 (struct scpr_callback_segment* segment1, 128 struct scpr_callback_segment* segment2, 129 void* ctx__) 130 { 131 res_T res = RES_OK; 132 size_t i; 133 struct callback_ctx* ctx = (struct callback_ctx*)ctx__; 134 const char *name1 = NULL, *name2 = NULL; 135 struct building *building1 = NULL, *building2 = NULL; 136 int with_removed = 0; 137 138 ASSERT(segment1 && segment2 && ctx); 139 140 /* Search polygons in the city (slow, but OK) */ 141 for(i = 0; i < ctx->buildings_count; i++) { 142 struct building* building = ctx->buildings + i; 143 const struct scpr_polygon* pg = 144 ctx->alternate_polygons ? ctx->alternate_polygons[i] : building->pg; 145 int removed = (building->event_flags & BUILDING_REMOVED_EARLY); 146 if(pg == segment1->polygon) { 147 building1 = building; 148 name1 = str_cget(&building->name); 149 if(removed) { 150 with_removed = 1; 151 break; 152 } 153 building1->event_flags |= BUILDING_WITH_CLOSE_NEIGHBOR; 154 } 155 if(pg == segment2->polygon) { 156 building2 = building; 157 name2 = str_cget(&building->name); 158 if(removed) { 159 with_removed = 1; 160 break; 161 } 162 building2->event_flags |= BUILDING_WITH_CLOSE_NEIGHBOR; 163 } 164 if(name1 && name2) break; 165 } 166 167 if(!with_removed) { 168 CHK(name1 && name2); 169 switch(ctx->search_type) { 170 case OVERLAPPING_PROXIMITY: 171 /* store other polygon on building information */ 172 STORE_CLOSE_INFO(building1, building2, CLOSE_PROXIMITY); 173 STORE_CLOSE_INFO(building2, building1, CLOSE_PROXIMITY); 174 break; 175 case CLOSE_PROXIMITY: 176 /* No action required */ 177 break; 178 default: FATAL("Invalid type."); 179 } 180 } 181 182 return 0; 183 error: 184 /* True errors are not recoverable */ 185 return 1; 186 } 187 188 int simple_intersection 189 (struct scpr_callback_segment* segment1, 190 struct scpr_callback_segment* segment2, 191 void* ctx__) 192 { 193 res_T res = RES_OK; 194 size_t i; 195 struct callback_ctx* ctx = (struct callback_ctx*)ctx__; 196 const char *name1 = NULL, *name2 = NULL; 197 struct building *building1 = NULL, *building2 = NULL; 198 const enum building_event flag = (ctx->search_type == OVERLAPPING_PROXIMITY) 199 ? BUILDING_WITH_OVERLAPPING : BUILDING_WITH_CLOSE_NEIGHBOR; 200 struct logger* logger; 201 struct mem_allocator* allocator; 202 203 ASSERT(segment1 && segment2 && ctx); 204 logger = ctx->city->logger; 205 allocator = ctx->city->allocator; 206 207 if(ctx->intersection_found) *ctx->intersection_found = 1; 208 209 if(ctx->search_type == TESTING_1_BUILDING_INTERNALS) { 210 /* There is no point in searching which building is involved. 211 * We noted the fact that an intersection was found, thats all. */ 212 ASSERT(ctx->buildings_count == 1); 213 return 0; 214 } 215 216 /* Search polygons in the city (slow, but OK) */ 217 for(i = 0; i < ctx->buildings_count; i++) { 218 struct building* building = ctx->buildings + i; 219 const struct scpr_polygon* pg = 220 ctx->alternate_polygons ? ctx->alternate_polygons[i] : building->pg; 221 if(pg == segment1->polygon) { 222 building1 = building; 223 name1 = str_cget(&building->name); 224 building1->event_flags |= flag; 225 } 226 if(pg == segment2->polygon) { 227 building2 = building; 228 name2 = str_cget(&building->name); 229 building2->event_flags |= flag; 230 } 231 } 232 CHK(name1 && name2); 233 234 if(building1 == building2) { 235 switch(ctx->search_type) { 236 case OVERLAPPING_PROXIMITY: 237 if(ctx->keep_running_on_errors) { 238 logger_print(logger, LOG_WARNING, 239 "Self intersection detected for building '%s'.\n", name1); 240 logger_print(logger, LOG_WARNING, 241 "Building will not be part of the output.\n"); 242 } else { 243 logger_print(logger, LOG_ERROR, 244 "Self intersection detected for building '%s'.\n", name1); 245 } 246 /* Dump error polygons in OBJ files */ 247 if(ctx->dump_footprints_level == 1) { 248 ERR(dump_footprint_to_obj(allocator, building1, NULL)); 249 } 250 building1->event_flags |= BUILDING_REMOVED_EARLY; 251 break; 252 case CLOSE_PROXIMITY: 253 break; 254 default: FATAL("Invalid type."); 255 } 256 } else { 257 switch(ctx->search_type) { 258 case OVERLAPPING_PROXIMITY: 259 if(ctx->keep_running_on_errors) { 260 logger_print(logger, LOG_WARNING, 261 "Intersection detected between buildings '%s' and '%s'.\n", 262 name1, name2); 263 logger_print(logger, LOG_WARNING, 264 "Buildings will not be part of the output.\n"); 265 } else { 266 logger_print(logger, LOG_ERROR, 267 "Intersection detected between buildings '%s' and '%s'.\n", 268 name1, name2); 269 } 270 /* Dump error polygons in OBJ files */ 271 if(ctx->dump_footprints_level == 1) { 272 ERR(dump_footprint_to_obj(allocator, building1, NULL)); 273 ERR(dump_footprint_to_obj(allocator, building2, NULL)); 274 } 275 building1->event_flags |= BUILDING_REMOVED_EARLY; 276 building2->event_flags |= BUILDING_REMOVED_EARLY; 277 break; 278 case CLOSE_PROXIMITY: 279 /* store other polygon on building information */ 280 STORE_CLOSE_INFO(building1, building2, ctx->search_type); 281 STORE_CLOSE_INFO(building2, building1, ctx->search_type); 282 logger_print(logger, LOG_OUTPUT, 283 "Buildings '%s' and '%s' are in close proximity.\n", name1, name2); 284 break; 285 default: FATAL("Invalid type."); 286 } 287 } 288 289 /* Return 1 to stop the process unless whe are in proximity search or the user 290 * asked to go further */ 291 if(ctx->search_type == CLOSE_PROXIMITY 292 || ctx->keep_running_on_errors || ctx->dump_footprints_level >= 1) 293 return 0; 294 return 1; 295 error: 296 /* True errors are not recoverable */ 297 return 1; 298 } 299 #undef STORE_CLOSE_INFO 300 301 res_T 302 save_ground_hole_patch_triangles 303 (struct building* building, 304 struct darray_double* ground_trg) 305 { 306 res_T res = RES_OK; 307 struct scad_geometry* footprint = NULL; 308 struct scad_geometry** env = NULL; 309 struct scad_geometry** partitioned = NULL; 310 struct htable_building_iterator it, end; 311 struct mem_allocator* allocator; 312 size_t count, i, n = 0, nverts = 0; 313 314 ASSERT(building && ground_trg); 315 316 allocator = building->city->allocator; 317 count = htable_building_size_get(&building->close_buildings); 318 env = MEM_CALLOC(allocator, 1 + count, sizeof(*env)); 319 partitioned = MEM_CALLOC(allocator, 1 + count, sizeof(*partitioned)); 320 if(!env || !partitioned) { 321 res = RES_MEM_ERR; 322 goto error; 323 } 324 325 /* Get building's footprint */ 326 ERR(scpr_polygon_get_vertices_count(building->pg, 0, &nverts)); 327 ERR(scad_add_polygon(NULL, get_position_pg, building->pg, 0, nverts, env)); 328 n = 1; 329 330 /* Get the footprints of all the close buildings in order to partition */ 331 htable_building_begin(&building->close_buildings, &it); 332 htable_building_end(&building->close_buildings, &end); 333 while(!htable_building_iterator_eq(&it, &end)) { 334 struct building* close_building = *htable_building_iterator_key_get(&it); 335 htable_building_iterator_next(&it); 336 if(close_building->event_flags & BUILDING_REMOVED_EARLY) 337 continue; 338 ERR(scpr_polygon_get_vertices_count(close_building->pg, 0, &nverts)); 339 ERR(scad_add_polygon(NULL, get_position_pg, close_building->pg, 0, nverts, 340 env + n)); 341 n++; 342 } 343 344 /* Clean and mesh footprint */ 345 ERR(scad_geometries_partition(env, n, 0, partitioned)); 346 footprint = partitioned[0]; 347 ERR(scad_geometry_ref_get(footprint)); 348 for(i = 0; i < n; i++) { 349 SCAD(geometry_ref_put(env[i])); env[i] = NULL; 350 SCAD(geometry_ref_put(partitioned[i])); partitioned[i] = NULL; 351 } 352 ERR(scad_scene_mesh()); 353 ERR(scad_stl_get_data(footprint, ground_trg)); 354 355 exit: 356 if(footprint) SCAD(geometry_ref_put(footprint)); 357 for(i = 0; i < n; i++) { 358 if(env[i]) SCAD(geometry_ref_put(env[i])); 359 if(partitioned[i]) SCAD(geometry_ref_put(partitioned[i])); 360 } 361 MEM_RM(allocator, env); 362 MEM_RM(allocator, partitioned); 363 return res; 364 error: 365 goto exit; 366 } 367 368 res_T 369 create_city 370 (struct mem_allocator* allocator, 371 struct logger* logger, 372 const struct args* args, 373 struct parsed_city* parsed_city, 374 struct catalog* catalog, 375 struct city** out_city) 376 { 377 res_T res = RES_OK; 378 size_t i = 0; 379 struct city* city = NULL; 380 struct htable_names names; 381 struct scpr_device_create_args scpr_args = SCPR_DEVICE_CREATE_ARGS_DEFAULT; 382 struct scpr_intersector* overlapping_intersector = NULL; 383 struct scpr_intersector* close_intersector = NULL; 384 struct scpr_intersector_check_callbacks callbacks 385 = SCPR_INTERSECTOR_CHECK_CALLBACKS_NULL__; 386 struct callback_ctx ctx = CB_CTX_NULL__; 387 struct scpr_polygon* tmp_polygon = NULL; 388 struct str name, filename; 389 int error_occured = 0; 390 struct building* building = NULL; 391 struct darray_polygons offset_polygons; 392 struct time t0, dt; 393 char buf[128]; 394 395 ASSERT(logger && allocator && args && parsed_city && catalog && out_city); 396 397 time_current(&t0); 398 str_init(allocator, &name); 399 str_init(allocator, &filename); 400 darray_polygons_init(allocator, &offset_polygons); 401 htable_names_init(allocator, &names); 402 403 city = MEM_CALLOC(allocator, 1, sizeof(*city)); 404 if(!city) { 405 res = RES_MEM_ERR; 406 goto error; 407 } 408 409 city->allocated_buildings_count = parsed_city->city_building_list_count; 410 city->buildings = MEM_CALLOC(allocator, city->allocated_buildings_count, 411 sizeof(*city->buildings)); 412 if(!city->buildings) { 413 res = RES_MEM_ERR; 414 goto error; 415 } 416 417 city->allocator = allocator; 418 city->logger = logger; 419 scpr_args.allocator = allocator; 420 scpr_args.logger = logger; 421 scpr_args.verbosity_level = args->verbosity_level; 422 scpr_args.precision = CLIPPER_PRECISON; 423 ERR(scpr_device_create(&scpr_args, &city->scpr)); 424 city->verbosisty_level = args->verbosity_level; 425 city->single_thread = args->single_thread; 426 city->binary_export = args->binary_export; 427 city->keep_running_on_errors = args->keep_running_on_errors; 428 city->dump_footprints_level = args->dump_footprints_level; 429 htable_names_init(allocator, &city->dump_footprint_names); 430 htable_common_init(allocator, &city->common); 431 ERR(str_printf(&filename, "%s.txt", args->stardis_basename)); 432 city->stardis_model = fopen(str_cget(&filename), "w"); 433 if(!city->stardis_model) { 434 logger_print(logger, LOG_ERROR, 435 "Cannot create stardis model file: '%s'.\n", 436 str_cget(&filename)); 437 res = RES_IO_ERR; 438 goto error; 439 } 440 ERR(str_printf(&filename, "%s.sh", args->stardis_basename)); 441 city->set_vars = fopen(str_cget(&filename), "w"); 442 if(!city->set_vars) { 443 logger_print(logger, LOG_ERROR, 444 "Cannot create stardis shell script file: '%s'.\n", 445 str_cget(&filename)); 446 res = RES_IO_ERR; 447 goto error; 448 } 449 fprintf(city->set_vars, "#!/bin/sh\n\n"); 450 fprintf(city->set_vars, GENERIC_INTERNAL_INSULATION_VARS); 451 fprintf(city->set_vars, GENERIC_GROUND_VARS); 452 fprintf(city->set_vars, GENERIC_B_GROUND_VARS); 453 fprintf(city->set_vars, GENERIC_WALL_VARS); 454 fprintf(city->set_vars, GENERIC_FLOOR_VARS); 455 fprintf(city->set_vars, GENERIC_INTERMEDIATE_FLOOR_VARS); 456 fprintf(city->set_vars, GENERIC_ROOF_VARS); 457 fprintf(city->set_vars, GENERIC_FOUNDATION_VARS); 458 fprintf(city->set_vars, GENERIC_INTERNAL_INSULATION_VARS); 459 fprintf(city->set_vars, GENERIC_EXTERNAL_INSULATION_VARS); 460 fprintf(city->set_vars, GENERIC_ROOF_INSULATION_VARS); 461 fprintf(city->set_vars, GENERIC_FLOOR_INSULATION_VARS); 462 fprintf(city->set_vars, GENERIC_GLAZING_VARS); 463 fprintf(city->set_vars, GENERIC_CAVITY_VARS); 464 fprintf(city->set_vars, GENERIC_ATTIC_CAVITY_VARS); 465 fprintf(city->set_vars, GENERIC_HABITABLE_CAVITY_VARS); 466 fprintf(city->set_vars, GENERIC_CRAWLSPACE_CAVITY_VARS); 467 fprintf(city->set_vars, GENERIC_SFC_VARS); 468 fprintf(city->set_vars, GENERIC_B_WALL_VARS); 469 fprintf(city->set_vars, GENERIC_B_ROOF_VARS); 470 471 ground_init(allocator, &city->ground); 472 ERR(vertex_denoiser_create(allocator, pow(10, -(CLIPPER_PRECISON+2)), 473 &city->denoiser)); 474 city->array_and_tables_initialized = 1; 475 /* Some specific building footprints will be dumped (command line request) */ 476 for(i = 0; i < darray_names_size_get(&args->dump_footprint_names); i++) { 477 char one = 1; 478 const char* pname = darray_names_cdata_get(&args->dump_footprint_names)[i]; 479 ERR(str_set(&name, pname)); 480 ERR(htable_names_set(&city->dump_footprint_names, &name, &one)); 481 } 482 483 city->ground_depth = parsed_city->ground.depth; 484 city->lower[0] = parsed_city->ground.extent[0]; 485 city->lower[1] = parsed_city->ground.extent[2]; 486 city->upper[0] = parsed_city->ground.extent[1]; 487 city->upper[1] = parsed_city->ground.extent[3]; 488 489 ERR(scpr_intersector_create(city->scpr, &overlapping_intersector)); 490 ERR(scpr_intersector_create(city->scpr, &close_intersector)); 491 ERR(darray_polygons_reserve(&offset_polygons, city->allocated_buildings_count)); 492 /* create buildings depending on their construction modes */ 493 for(i = 0; i < city->allocated_buildings_count ; i++) { 494 res_T tmp_res; 495 struct parsed_city_building* parsed_data = parsed_city->city_building_list + i; 496 char one = 1; 497 int dump; 498 building = city->buildings + i; 499 switch(parsed_data->cmode_type) { 500 case PARSED_CMODE_0: 501 tmp_res = init_cmode_0(building, city, parsed_data, catalog, 502 city->lower, city->upper); 503 break; 504 case PARSED_CMODE_1: 505 tmp_res = init_cmode_1(building, city, parsed_data, catalog, 506 city->lower, city->upper); 507 break; 508 case PARSED_CMODE_2: 509 tmp_res = init_cmode_2(building, city, parsed_data, catalog, 510 city->lower, city->upper); 511 break; 512 default: FATAL("Unknown construction mode"); 513 } 514 /* Dump polygon if required */ 515 dump = htable_names_find(&city->dump_footprint_names, &building->name) 516 || (tmp_res != RES_OK && city->dump_footprints_level == 1) 517 || city->dump_footprints_level == 2; 518 if(dump) { 519 ERR(dump_footprint_to_obj(allocator, building, NULL)); 520 } 521 if(tmp_res != RES_OK) { 522 if(city->keep_running_on_errors) { 523 logger_print(city->logger, LOG_WARNING, 524 "Building '%s' will not be part of the output.\n", 525 str_cget(&building->name)); 526 building->event_flags |= BUILDING_REMOVED_EARLY; 527 /* Insert a NULL polygon to preserve indexes */ 528 tmp_polygon = NULL; 529 ERR(darray_polygons_push_back(&offset_polygons, &tmp_polygon)); 530 continue; 531 } 532 res = tmp_res; 533 goto error; 534 } 535 /* Check name unicity. The error case is not supposed to happen: can be 536 * tested after building creation as this simplifies the process */ 537 if(htable_names_find(&names, &building->name)) { 538 building->event_flags |= BUILDING_REMOVED_EARLY; 539 logger_print(logger, LOG_ERROR, 540 "Duplicate building name: '%s' in file '%s'.\n", 541 str_cget(&building->name), str_cget(&parsed_city->filename)); 542 /* Cannot keep going in this specific case */ 543 res = RES_BAD_ARG; 544 goto error; 545 } 546 ERR(htable_names_set(&names, &building->name, &one)); 547 /* Register the exterior building polygon for further overlapping detection */ 548 ERR(scpr_intersector_register_polygon(overlapping_intersector, building->pg)); 549 /* Register offset exterior building polygon for further proximity detection */ 550 ERR(scpr_polygon_create_copy(city->scpr, building->pg, &tmp_polygon)); 551 ERR(scpr_offset_polygon(tmp_polygon, CG2_CLOSE_NEIGHBOR_DISTANCE, 552 SCPR_JOIN_MITER)); 553 ERR(scpr_intersector_register_polygon(close_intersector, tmp_polygon)); 554 ERR(darray_polygons_push_back(&offset_polygons, &tmp_polygon)); 555 ERR(scpr_polygon_ref_put(tmp_polygon)); 556 tmp_polygon = NULL; 557 building->event_flags |= BUILDING_CREATED; 558 city->initialized_buildings_count++; 559 } 560 building = NULL; 561 562 /* Check for polygons overlapping */ 563 ctx.city = city; 564 ctx.buildings = city->buildings; 565 ctx.buildings_count = city->allocated_buildings_count; 566 ctx.intersection_found = &error_occured; 567 ctx.search_type = OVERLAPPING_PROXIMITY; 568 ctx.dump_footprints_level = city->dump_footprints_level; 569 ctx.keep_running_on_errors = city->keep_running_on_errors; 570 callbacks.simple_intersection = simple_intersection; 571 callbacks.overlapping_segments = overlapping_segments; 572 ERR(scpr_intersector_check(overlapping_intersector, &callbacks, &ctx)); 573 if(error_occured && !city->keep_running_on_errors) { 574 res = RES_BAD_ARG; 575 goto error; 576 } 577 /* Check for polygons in polygons. 578 * As remaining polygons do not overlap, we can use component_in_component */ 579 for(i = 0; i < city->allocated_buildings_count ; i++) { 580 struct building* b1 = city->buildings + i; 581 size_t j; 582 if(b1->event_flags & BUILDING_REMOVED_EARLY) continue; 583 for(j = 0; j < i; j++) { 584 struct building* b2 = city->buildings + j; 585 int in1, in2; 586 if((b2->event_flags & BUILDING_REMOVED_EARLY)) continue; 587 /* b1 and b2 are 2 different valid buildings */ 588 ERR(scpr_is_component_in_component(b1->pg, 0, b2->pg, 0, &in1)); 589 ERR(scpr_is_component_in_component(b2->pg, 0, b1->pg, 0, &in2)); 590 ASSERT(!in1 || !in2); 591 if(in2) { 592 b2->event_flags |= BUILDING_INSIDE_BUILDING; 593 if(city->keep_running_on_errors) { 594 logger_print(logger, LOG_WARNING, 595 "Building '%s' is inside building '%s'.\n", 596 str_cget(&b2->name), str_cget(&b1->name)); 597 logger_print(logger, LOG_WARNING, 598 "Building '%s' will not be part of the output.\n", 599 str_cget(&b2->name)); 600 } else { 601 logger_print(logger, LOG_ERROR, 602 "Building '%s' is inside building '%s'.\n", 603 str_cget(&b2->name), str_cget(&b1->name)); 604 } 605 } 606 else if(in1) { 607 b1->event_flags |= BUILDING_INSIDE_BUILDING; 608 if(city->keep_running_on_errors) { 609 logger_print(logger, LOG_WARNING, 610 "Building '%s' is inside building '%s'.\n", 611 str_cget(&b1->name), str_cget(&b2->name)); 612 logger_print(logger, LOG_WARNING, 613 "Building '%s' will not be part of the output.\n", 614 str_cget(&b1->name)); 615 } else { 616 logger_print(logger, LOG_ERROR, 617 "Building '%s' is inside building '%s'.\n", 618 str_cget(&b1->name), str_cget(&b2->name)); 619 } 620 } 621 if((in1 || in2) && !city->keep_running_on_errors) { 622 res = RES_BAD_ARG; 623 goto error; 624 } 625 } 626 } 627 628 /* Check for polygons proximity */ 629 ctx.search_type = CLOSE_PROXIMITY; 630 ctx.alternate_polygons = darray_polygons_data_get(&offset_polygons); 631 ctx.intersection_found = NULL; /* Not an error in this case */ 632 ERR(scpr_intersector_check(close_intersector, &callbacks, &ctx)); 633 634 exit: 635 str_release(&filename); 636 str_release(&name); 637 darray_polygons_release(&offset_polygons); 638 if(tmp_polygon) SCPR(polygon_ref_put(tmp_polygon)); 639 if(overlapping_intersector) SCPR(intersector_ref_put(overlapping_intersector)); 640 if(close_intersector) SCPR(intersector_ref_put(close_intersector)); 641 htable_names_release(&names); 642 *out_city = city; 643 if(res == RES_OK && city) { 644 time_sub(&dt, time_current(&dt), &t0); 645 time_dump(&dt, TIME_SEC | TIME_MSEC, NULL, buf, sizeof(buf)); 646 logger_print(city->logger, LOG_OUTPUT, 647 "Map implantation analyzed in %s.\n", buf); 648 } 649 return res; 650 error: 651 time_sub(&dt, time_current(&dt), &t0); 652 time_dump(&dt, TIME_SEC | TIME_MSEC, NULL, buf, sizeof(buf)); 653 if(building) { 654 logger_print(logger, LOG_ERROR, 655 "Error creating building '%s'.\n", str_cget(&building->name)); 656 } else { 657 logger_print(logger, LOG_ERROR, "Error creating city.\n"); 658 } 659 logger_print(city->logger, LOG_ERROR, 660 "Map implantation canceled after %s.\n", buf); 661 CHK(RES_OK == release_city(city)); 662 city = NULL; 663 goto exit; 664 } 665 666 res_T 667 release_city(struct city* city) 668 { 669 size_t i; 670 res_T res = RES_OK; 671 672 if(!city) { 673 res = RES_BAD_ARG; 674 goto error; 675 } 676 if(city->stardis_model) { 677 fclose(city->stardis_model); 678 city->stardis_model = NULL; 679 } 680 if(city->set_vars) { 681 fclose(city->set_vars); 682 city->set_vars = NULL; 683 } 684 if(city->scpr) SCPR(device_ref_put(city->scpr)); 685 if(city->denoiser) vertex_denoiser_release(city->denoiser); 686 if(city->array_and_tables_initialized) { 687 htable_common_release(&city->common); 688 htable_names_release(&city->dump_footprint_names); 689 } 690 ground_clear(&city->ground); 691 /* iterate on building */ 692 for (i = 0; i < city->allocated_buildings_count; i++) { 693 struct building* building = city->buildings + i; 694 if(building->functors) { 695 ERR(building->functors->release(building)); 696 } 697 } 698 699 MEM_RM(city->allocator, city->buildings); 700 MEM_RM(city->allocator, city); 701 exit: 702 return res; 703 error: 704 goto exit; 705 } 706 707 #define ERR1(Expr) if((tmp_res = (Expr)) != RES_OK) goto error1; else (void)0 708 709 res_T 710 city_cad_build(struct city* city) 711 { 712 res_T res = RES_OK; 713 struct scad_options options = SCAD_DEFAULT_OPTIONS__; 714 int scad_initialized = 0; 715 size_t i, a, generated_buildings_count = 0; 716 struct building *building = NULL; 717 struct darray_adjoining_data adjoining_data; 718 struct darray_double common; 719 void* cad = NULL; 720 struct scad_geometry* my_envelop = NULL; 721 struct str filename; 722 struct darray_geometries current_cad; 723 struct mem_allocator* allocator; 724 struct scad_geometry** partitioned = NULL; 725 struct time t0, dt; 726 char buf[128]; 727 728 ASSERT(city); 729 730 allocator = city->allocator; 731 darray_geometries_init(allocator, ¤t_cad); 732 darray_adjoining_data_init(allocator, &adjoining_data); 733 darray_double_init(allocator, &common); 734 str_init(allocator, &filename); 735 736 /* Initialize star-cad */ 737 ERR(scad_initialize(city->logger, allocator, city->verbosisty_level)); 738 scad_initialized = 1; 739 options.Mesh.MeshSizeFromPoints = 0; 740 options.Mesh.MeshSizeMin = pow(10, -(CLIPPER_PRECISON+1)); 741 options.Geometry.OCCParallel = !city->single_thread; 742 #ifdef NDEBUG 743 options.Misc.LogRefCounting = Scad_log_dimTags_only_undeleted; 744 #else 745 options.Misc.LogRefCounting = Scad_log_dimTags_all | Scad_log_geometry; 746 #endif 747 ERR(scad_set_options(&options)); 748 749 /* Iterate on buildings 750 * If a building fails and keep_running is set, try hard to continue with 751 * remaining buildings. */ 752 for(i = 0; i < city->allocated_buildings_count; i++) { 753 struct adjoining_data* adj; 754 res_T tmp_res; 755 756 building = city->buildings + i; 757 if(city->dump_footprints_level == 2) { 758 ERR1(dump_footprint_to_obj(allocator, building, NULL)); 759 } 760 if(building->event_flags & BUILDING_REMOVED_EARLY) { 761 continue; 762 } 763 764 /* create building CAD */ 765 time_current(&t0); 766 logger_print(city->logger, LOG_OUTPUT, 767 "Start processing building '%s'.\n", str_cget(&building->name)); 768 darray_geometries_clear(¤t_cad); 769 tmp_res = building->functors->build_cad(building, city->dump_footprints_level, 770 city->keep_running_on_errors, &adjoining_data, ¤t_cad, 771 (void**)&cad); 772 773 if(tmp_res != RES_OK) { 774 /* Call to build_cad failed (cad is NULL). 775 * Any needed geometry must be created from here */ 776 struct htable_building_iterator it, end; 777 res_T keep_res = tmp_res; 778 ASSERT(cad == NULL); 779 darray_geometries_clear(¤t_cad); 780 /* Help the user to close the hole in the ground: save it */ 781 ERR1(save_ground_hole_patch_triangles(building, &city->ground.ground_patches)); 782 building->event_flags |= BUILDING_GROUND_PATCH_CREATED; 783 /* For every adjoining building that has considered this building, there 784 * is a hole in its boundary just where this building should have been 785 * adjoining. This hole must be closed. This situation can be detected 786 * simply by searching some common triangles between the 2 buildings. */ 787 htable_building_begin(&building->close_buildings, &it); 788 htable_building_end(&building->close_buildings, &end); 789 while(!htable_building_iterator_eq(&it, &end)) { 790 struct building* close_building = *htable_building_iterator_key_get(&it); 791 struct b_pair pair; 792 struct darray_double* p_common; 793 htable_building_iterator_next(&it); 794 make_b_pair(&pair, building, close_building); 795 p_common = htable_common_find(&city->common, &pair); 796 if(p_common) { 797 /* The adjoining building has allready been processed. 798 * Need to create the boundary patch */ 799 ERR1(str_copy(&filename, &close_building->name)); 800 ERR1(str_append_printf(&filename, "_B_%s_complement_%s.stl", 801 str_cget(&building->external_layer_name), 802 str_cget(&building->name))); 803 ERR1(scad_stl_data_write(p_common, str_cget(&filename), 804 Scad_keep_normals_unchanged, city->binary_export)); 805 } 806 } 807 808 if(city->dump_footprints_level == 1) { 809 ERR1(dump_footprint_to_obj(allocator, building, NULL)); 810 } 811 time_sub(&dt, time_current(&dt), &t0); 812 time_dump(&dt, TIME_SEC | TIME_MSEC, NULL, buf, sizeof(buf)); 813 if(city->keep_running_on_errors) { 814 logger_print(city->logger, LOG_WARNING, 815 "Building '%s' will not be part of the output (aborted after %s).\n", 816 str_cget(&building->name), buf); 817 darray_adjoining_data_clear(&adjoining_data); 818 /* FIXME: adjoining buildings may have been built already, taking this 819 * building into account. There is no simple way to undo this other 820 * than rebuild them after this building removal. The visible effects 821 * are on walls' meshes being uselessly refined due to conformity with 822 * the now-removed building, and possible window removal that should 823 * now be reversed. */ 824 continue; 825 } else { 826 tmp_res = keep_res; 827 } 828 } 829 ERR1(tmp_res); 830 ERR1(scad_scene_mesh()); 831 /* Keep the mesh of some geometry if planned */ 832 adj = darray_adjoining_data_data_get(&adjoining_data); 833 for(a = 0; a < darray_adjoining_data_size_get(&adjoining_data); a++) { 834 struct b_pair pair; 835 size_t c; 836 ERR1(scad_geometry_get_count(adj[a].common_geometry, &c)); 837 if(c == 0) { 838 /* Not really adjoining */ 839 continue; 840 } 841 if(!adj[a].save || building != adj[a].main_building) { 842 /* Only the building that created the record can do the job 843 * (as geom lifetime is limited to a single iteration) */ 844 continue; 845 } 846 darray_double_clear(&common); 847 ERR1(scad_stl_get_data(adj[a].common_geometry, &common)); 848 ASSERT(darray_double_size_get(&common) > 0 849 && darray_double_size_get(&common) % 9 == 0); 850 make_b_pair(&pair, building, adj[a].adjoining_building); 851 ERR1(denoise_array(city->denoiser, &common)); 852 ERR1(htable_common_set(&city->common, &pair, &common)); 853 } 854 tmp_res = building->functors->export_stl(cad, city->binary_export); 855 if(tmp_res == RES_OK) { 856 /* Keep ground/building's boundary triangles now as the geometry will be 857 * released before ground is built */ 858 ERR1(building->functors->save_ground_connection_triangles(cad, 859 &city->ground.ground_building_connections)); 860 building->event_flags |= BUILDING_CAD_EXPORTED_TO_STL; 861 generated_buildings_count++; 862 logger_print(city->logger, LOG_OUTPUT, 863 "Building '%s': %s STL files created.\n", 864 str_cget(&building->name), (city->binary_export ? "binary" : "ascii")); 865 } 866 else if(city->keep_running_on_errors) { 867 logger_print(city->logger, LOG_ERROR, 868 "Some STL files could be missing for building '%s'.\n", 869 str_cget(&building->name)); 870 logger_print(city->logger, LOG_ERROR, 871 "You may want to delete any generated STL file for this building " 872 "after inspecting them.\n"); 873 } 874 error1: 875 /* Cannot keep any geometry from one building to the other */ 876 if(cad) { 877 ERR(building->functors->release_cad(cad)); 878 cad = NULL; /* Avoid double release */ 879 } 880 darray_adjoining_data_clear(&adjoining_data); 881 darray_geometries_clear(¤t_cad); 882 if(my_envelop) SCAD(geometry_ref_put(my_envelop)); 883 ERR(scad_scene_clear()); 884 time_sub(&dt, time_current(&dt), &t0); 885 time_dump(&dt, TIME_SEC | TIME_MSEC, NULL, buf, sizeof(buf)); 886 if(tmp_res == RES_OK) { 887 logger_print(city->logger, LOG_OUTPUT, 888 "Building '%s': done in %s.\n", str_cget(&building->name), buf); 889 } 890 else if(city->keep_running_on_errors) { 891 logger_print(city->logger, LOG_ERROR, 892 "Building '%s' canceled after %s.\n", str_cget(&building->name), buf); 893 } 894 if(!city->keep_running_on_errors) { 895 ERR(tmp_res); 896 } 897 } 898 building = NULL; 899 city->cad_generated_buildings_count = generated_buildings_count; 900 901 exit: 902 if(partitioned) { 903 for(i = 0; i < 2; i++) { 904 if(partitioned[i]) SCAD(geometry_ref_put(partitioned[i])); 905 } 906 MEM_RM(allocator, partitioned); 907 } 908 str_release(&filename); 909 if(my_envelop) SCAD(geometry_ref_put(my_envelop)); 910 darray_geometries_release(¤t_cad); 911 darray_adjoining_data_release(&adjoining_data); 912 if(cad) CHK(RES_OK == building->functors->release_cad(cad)); 913 if(scad_initialized) SCAD(finalize()); 914 darray_double_release(&common); 915 return res; 916 error: 917 if(building) { 918 time_sub(&dt, time_current(&dt), &t0); 919 time_dump(&dt, TIME_SEC | TIME_MSEC, NULL, buf, sizeof(buf)); 920 logger_print(city->logger, LOG_ERROR, 921 "Error generating CAD for building '%s'; canceled after %s.\n", 922 str_cget(&building->name), buf); 923 } 924 goto exit; 925 } 926 #undef ERR1 927 928 res_T 929 city_ground_build(struct city* city) 930 { 931 res_T res = RES_OK; 932 struct scad_options options = SCAD_DEFAULT_OPTIONS__; 933 int scad_initialized = 0; 934 size_t i = 0; 935 struct building *building = NULL; 936 struct time t0, dt; 937 char buf[128]; 938 939 time_current(&t0); 940 logger_print(city->logger, LOG_OUTPUT, "Start processing ground.\n"); 941 /* Initialize star-cad */ 942 ERR(scad_initialize(city->logger, city->allocator, city->verbosisty_level)); 943 scad_initialized = 1; 944 options.Mesh.MeshSizeFromPoints = 0; 945 options.Mesh.MeshSizeMin = pow(10, -(CLIPPER_PRECISON+1)); 946 options.Geometry.OCCParallel = !city->single_thread; 947 #ifdef NDEBUG 948 options.Misc.LogRefCounting = Scad_log_dimTags_only_undeleted; 949 #else 950 options.Misc.LogRefCounting = Scad_log_dimTags_all | Scad_log_geometry; 951 #endif 952 ERR(scad_set_options(&options)); 953 954 /* iterate on buildings */ 955 ERR(darray_geometries_reserve(&city->ground.footprints, 956 city->allocated_buildings_count)); 957 for(i = 0; i < city->allocated_buildings_count; i++) { 958 struct scad_geometry* footprint; 959 960 building = city->buildings + i; 961 if(!(building->event_flags 962 & (BUILDING_CAD_EXPORTED_TO_STL | BUILDING_GROUND_PATCH_CREATED))) 963 continue; 964 965 /* create building footprint */ 966 ERR(building->functors->build_footprint(building, &footprint)); 967 ERR(darray_geometries_push_back(&city->ground.footprints, &footprint)); 968 ERR(scad_geometry_ref_put(footprint)); /* Ownership transfered */ 969 } 970 building = NULL; 971 ERR(ground_build_cad(city->allocator, city)); 972 973 ERR(scad_scene_mesh()); 974 ERR(ground_export_stl(city)); 975 976 exit: 977 ground_clear(&city->ground); 978 if(scad_initialized) SCAD(finalize()); 979 time_sub(&dt, time_current(&dt), &t0); 980 if(res == RES_OK) { 981 time_dump(&dt, TIME_SEC | TIME_MIN, NULL, buf, sizeof(buf)); 982 logger_print(city->logger, LOG_OUTPUT, "Ground done in %s.\n", buf); 983 } 984 return res; 985 error: 986 time_sub(&dt, time_current(&dt), &t0); 987 time_dump(&dt, TIME_SEC | TIME_MSEC, NULL, buf, sizeof(buf)); 988 if(building) { 989 logger_print(city->logger, LOG_ERROR, 990 "Error generating ground footprint for building '%s'.\n", 991 str_cget(&building->name)); 992 } 993 else if(i == city->allocated_buildings_count) { 994 logger_print(city->logger, LOG_ERROR, "Error generating ground.\n"); 995 } else { 996 logger_print(city->logger, LOG_ERROR, "Unknown error generating ground.\n"); 997 } 998 logger_print(city->logger, LOG_ERROR, 999 "Ground processing aborted after %s.\n", buf); 1000 goto exit; 1001 }