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_construction_mode_2.c (76737B)


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