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_0.c (26834B)


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