city_generator2

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

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, &current_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(&current_cad);
    769     tmp_res = building->functors->build_cad(building, city->dump_footprints_level,
    770          city->keep_running_on_errors, &adjoining_data, &current_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(&current_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(&current_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(&current_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 }