city_generator2

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

commit 83d8067fcaa072ea83bab5f395cc26438357e09f
parent 5384651a6e8acb89d89b605af98c441c754e41d4
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Fri, 21 Apr 2023 16:03:18 +0200

Fix non-conforming meshes in some adjoining configurations

Also make various improvements

Diffstat:
MREADME.md | 13+++++++++++++
Mcmake/CMakeLists.txt | 4+++-
Msrc/cg.h | 1-
Msrc/cg_building.c | 82+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Msrc/cg_building.h | 90++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Msrc/cg_catalog_parsing.c | 18++++++++++++++++++
Msrc/cg_city.c | 283++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Msrc/cg_city.h | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Msrc/cg_city_parsing.c | 5+++++
Msrc/cg_construction_mode.c | 46+++++++++++++++++++++++++++-------------------
Msrc/cg_construction_mode.h | 39++++++++++++++++++++++++++++++---------
Msrc/cg_construction_mode_0.c | 350+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Msrc/cg_construction_mode_0.h | 49++++++++++++++++++++-----------------------------
Msrc/cg_construction_mode_1.c | 531+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Msrc/cg_construction_mode_1.h | 51+++++++++++++++++----------------------------------
Msrc/cg_default.h.in | 1+
Msrc/cg_ground.c | 6+++---
Msrc/cg_main.c | 8++++++--
18 files changed, 1063 insertions(+), 604 deletions(-)

diff --git a/README.md b/README.md @@ -23,6 +23,19 @@ resulting project can be edited, built, tested and installed as any CMake project. Refer to the [CMake documentation](https://cmake.org/documentation) for further informations on CMake. +Some parameters that change the program behaviour can be set at the cmake stage. +To set them one can use the -D<VAR>=<VAL> pattern. The parameters are: + +- `CG2_ARGS_DEFAULT_VERBOSITY_LEVEL`: the default verbosity level if the `-V` + option is not used. Default is 1 (errors only). + +- `STL_OUTPUT_DEFAULT_IS_BINARY`: The default format for output STL files. + Default is ascii. Depending of this option, the command line options allow + either -a the change the type to ascii, or -b to change it to binary. + +- `CG2_CLOSE_NEIGHBOR_DISTANCE`: The distance up to which a close neighbor + building prevents windows to be generated. Default is 2 meters. + ## Copyright notice Copyright (C) 2022 Université de Pau et des Pays de l'Adour UPPA. diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -20,7 +20,9 @@ enable_testing() set(CG2_SOURCE_DIR ${PROJECT_SOURCE_DIR}/../src) option(NO_TEST "Disable the test" OFF) option(STL_OUTPUT_DEFAULT_IS_BINARY - "Default is to use binary format for output STL files" ON) + "Default is to use binary format for output STL files" OFF) +set(CG2_CLOSE_NEIGHBOR_DISTANCE "2" CACHE STRING + "Distance up to which windows are not generated") if(CMAKE_HOST_UNIX) set(CG2_DOC "TROFF" CACHE STRING diff --git a/src/cg.h b/src/cg.h @@ -30,7 +30,6 @@ */ #define CLIPPER_PRECISON 3 /* Input footprints are rounded to mm */ -#define CLOSE_NEIGHBOR_DISTANCE 0.1 /* Neighbors are considered close <= 0.1 m */ /* Utility functions */ static INLINE void diff --git a/src/cg_building.c b/src/cg_building.c @@ -22,38 +22,74 @@ #include "cg_building.h" #include <rsys/str.h> -#include <rsys/stretchy_array.h> #include <rsys/mem_allocator.h> #include <rsys/logger.h> #include <star/scad.h> +void +adjoining_data_release(struct adjoining_data* data) +{ + ASSERT(data); + if(data->envelop) { + SCAD(geometry_ref_put(data->envelop)); + data->envelop = NULL; + } + if(data->common_geometry) { + SCAD(geometry_ref_put(data->common_geometry)); + data->common_geometry = NULL; + } +} + +res_T +adjoining_data_copy + (struct adjoining_data* dst, const struct adjoining_data* src) +{ + ASSERT(dst && src); + dst->adjoining_building = src->adjoining_building; + dst->main_building = src->main_building; + dst->common_geometry = src->common_geometry; + if(dst->common_geometry) SCAD(geometry_ref_get(dst->common_geometry)); + dst->envelop = src->envelop; + if(dst->envelop) SCAD(geometry_ref_get(dst->envelop)); + dst->save = src->save; + return RES_OK; +} + +res_T +adjoining_data_copy_and_release + (struct adjoining_data* dst, const struct adjoining_data* src) +{ + ASSERT(dst && src); + dst->adjoining_building = src->adjoining_building; + dst->main_building = src->main_building; + dst->common_geometry = src->common_geometry; + dst->envelop = src->envelop; + dst->save = src->save; + return RES_OK; +} + res_T build_adjoining - (struct mem_allocator* allocator, - struct logger* logger, - struct building* building, - struct scad_geometry*** geom) + (struct building* building, + struct darray_adjoining_data* adjoining) { res_T res = RES_OK; - size_t count, i = 0; - struct scad_geometry** envelop_list = NULL; + size_t count, prev, i = 0; struct htable_building_iterator it, end; struct htable_building* close_buildings; struct str msg; + struct mem_allocator* allocator; + struct logger* logger; - ASSERT(allocator && logger && building && geom); + ASSERT(building && adjoining); + allocator = building->city->allocator; + logger = building->city->logger; str_init(allocator, &msg); close_buildings = &building->close_buildings; count = htable_building_size_get(close_buildings); - envelop_list = MEM_CALLOC(allocator, count, sizeof(struct scad_geometry*)); - if(!envelop_list) { - res = RES_MEM_ERR; - goto error; - } - ERR(str_printf(&msg, "building '%s' considering close neighbor(s) when creating CAD:", str_cget(&building->name))); @@ -61,29 +97,29 @@ build_adjoining /* iterate over adjoining building */ htable_building_begin(close_buildings, &it); htable_building_end(close_buildings, &end); + prev = darray_adjoining_data_size_get(adjoining); + ERR(darray_adjoining_data_resize(adjoining, prev + count)); while(!htable_building_iterator_eq(&it, &end)) { unsigned char flags = *htable_building_iterator_data_get(&it); struct building* close_building; + struct adjoining_data* adj; ASSERT(flags & CLOSE_PROXIMITY); (void)flags; close_building = *htable_building_iterator_key_get(&it); - ERR(close_building->functors->build_envelop(allocator, logger, close_building, - envelop_list + i++)); + adj = darray_adjoining_data_data_get(adjoining) + prev + i; + ERR(close_building->functors->build_envelop(close_building, &adj->envelop)); + adj->main_building = building; + adj->adjoining_building = close_building; ERR(str_append_printf(&msg, " '%s'", str_cget(&close_building->name))); htable_building_iterator_next(&it); + i++; } - ASSERT(i == count); logger_print(logger, LOG_OUTPUT, "%s.\n", str_cget(&msg)); exit: str_release(&msg); - *geom = envelop_list; return res; error: - for (i = 0; i < count; i++) { - if(envelop_list[i]) SCAD(geometry_delete(envelop_list[i])); - } - if (envelop_list) MEM_RM(allocator, envelop_list); - envelop_list = NULL; + darray_adjoining_data_clear(adjoining); goto exit; } diff --git a/src/cg_building.h b/src/cg_building.h @@ -22,17 +22,17 @@ #include <rsys/rsys.h> #include <rsys/str.h> +#include <rsys/dynamic_array.h> +#include <rsys/dynamic_array.h> #include <rsys/hash_table.h> struct scpr_polygon; struct scad_geometry; -struct mem_allocator; -struct logger; struct catalog; struct building; struct parsed_city_building; -struct scpr_device; -struct building; +struct city; +struct darray_adjoining_data; /* An htable to uniquely associate flags to buildings */ #define HTABLE_NAME building @@ -45,54 +45,40 @@ enum building_event { BUILDING_NO_EVENT, BUILDING_WITH_OVERLAPPING = BIT(0), BUILDING_WITH_CLOSE_NEIGHBOR = BIT(1), - BUILDING_WITH_INTERNAL_ERROR = BIT(2), - BUILDING_OUT_OF_GROUND_EXTENT = BIT(3), - BUILDING_REMOVED = BIT(4), - BUILDING_DUMPED_TO_OBJ = BIT(5) + BUILDING_OUT_OF_GROUND_EXTENT = BIT(2), + BUILDING_DUMPED_TO_OBJ = BIT(3), + BUILDING_CREATED = BIT(4), + BUILDING_CAD_EXPORTED_TO_STL = BIT(5) }; /* A type to store the functors of a construction mode */ struct construction_mode_functors { res_T (*init) - (struct scpr_device* scpr, - struct mem_allocator* allocator, - struct logger* logger, - struct building* building, - int keep_running_on_error, + (struct building* building, + struct city* city, struct parsed_city_building* parsed_data, struct catalog* catalog, const double lower[2], const double upper[2]); res_T (*release) - (struct mem_allocator* allocator, - struct logger* logger, - struct building* building); + (struct building* building); res_T (*build_cad) - (struct scpr_device* scpr, - struct mem_allocator* allocator, - struct logger* logger, - struct building* building, + (struct building* building, int dump_footprints_on_error, int keep_running_on_errors, + struct darray_adjoining_data* adjoining_data, void** cad); res_T (*build_footprint) - (struct scpr_device* scpr, - struct mem_allocator* allocator, - struct logger* logger, - struct building* building, + (struct building* building, struct scad_geometry** footprint); res_T (*build_envelop) - (struct mem_allocator* allocator, - struct logger* logger, - struct building* building, + (struct building* building, struct scad_geometry** envelop); res_T (*export_stl) - (struct mem_allocator* allocator, - struct logger* logger, - void* cad, + (void* cad, const int binary); res_T (*release_cad) - (struct mem_allocator* allocator, struct logger* logger, void* cad); + (void* cad); }; /* A type to give an ID to construction modes. @@ -114,6 +100,7 @@ struct building { /* generic construction mode data */ struct str name; + struct city* city; double height; struct scpr_polygon* pg; struct htable_building close_buildings; /* links to other buildings */ @@ -124,11 +111,44 @@ struct building { void* data; }; +#define DARRAY_NAME pbuilding +#define DARRAY_DATA struct building* +#include <rsys/dynamic_array.h> + +struct adjoining_data { + struct building* adjoining_building; + struct building* main_building; + /* Only valid for building save */ + struct scad_geometry* envelop; + struct scad_geometry* common_geometry; + int save; +}; +static FINLINE void +adjoining_data_init(struct mem_allocator* alloc, struct adjoining_data* data) +{ + ASSERT(data); (void)alloc; + data->adjoining_building = NULL; + data->main_building = NULL; + data->envelop = NULL; + data->common_geometry = NULL; + data->save = 0; +} +void adjoining_data_release(struct adjoining_data* data); +res_T adjoining_data_copy + (struct adjoining_data* dst, const struct adjoining_data* src); +res_T adjoining_data_copy_and_release + (struct adjoining_data* dst, const struct adjoining_data* src); +#define DARRAY_NAME adjoining_data +#define DARRAY_DATA struct adjoining_data +#define DARRAY_FUNCTOR_INIT adjoining_data_init +#define DARRAY_FUNCTOR_COPY adjoining_data_copy +#define DARRAY_FUNCTOR_RELEASE adjoining_data_release +#define DARRAY_FUNCTOR_COPY_AND_RELEASE adjoining_data_copy_and_release +#include <rsys/dynamic_array.h> + res_T build_adjoining - (struct mem_allocator* allocator, - struct logger* logger, - struct building* building, - struct scad_geometry*** geom); + (struct building* building, + struct darray_adjoining_data* adjoining); #endif /* BUILDING_H */ diff --git a/src/cg_catalog_parsing.c b/src/cg_catalog_parsing.c @@ -20,6 +20,7 @@ #include "cg.h" #include "cg_args.h" #include "cg_catalog_parsing.h" +#include "cg_construction_mode_0.h" #include "cg_construction_mode_0_parsing_schemas.h" #include "cg_construction_mode_1_parsing_schemas.h" #include "cg_city_parsing_schemas.h" @@ -79,6 +80,7 @@ parse_catalog items = darray_parsed_catalog_items_data_get(&parsed->catalog); for(i = 0; i < files_count; i++) { const struct cyaml_schema_value* schema; + size_t set_count; /* Parse construction mode only */ filename = darray_names_cdata_get(files_array)[i]; @@ -95,6 +97,22 @@ parse_catalog items[i].filename = filename; items[i].construction_mode = parsed_cmode->cmode_type; + /* Log outcome */ + switch(items[i].construction_mode) { + case PARSED_CMODE_0: + set_count = ((struct parsed_catalog_cmode_0*)items[i].parsed_data)->datasets_count; + break; + case PARSED_CMODE_1: + set_count = ((struct parsed_catalog_cmode_1*)items[i].parsed_data)->datasets_count; + break; + default: FATAL("Invalid enum value.\n"); + } + logger_print(logger, LOG_OUTPUT, + "Catalog file '%s' parsed: mode '%s', %zu dataset(s) read.\n", + filename, + city_building_types_strings[items[i].construction_mode].str, + set_count); + /* Free tmp struct */ err = cyaml_free(config, &construction_mode_schema, parsed_cmode, 1); CHK(RES_OK == cyaml_err_to_res_T(err)); diff --git a/src/cg_city.c b/src/cg_city.c @@ -18,6 +18,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "cg.h" +#include "cg_default.h" #include "cg_city.h" #include "cg_construction_mode_0.h" #include "cg_construction_mode_1.h" @@ -32,20 +33,34 @@ #include <rsys/mem_allocator.h> #include <rsys/double4.h> #include <rsys/hash_table.h> +#include <rsys/dynamic_array.h> #include <rsys/str.h> +#include <star/scad.h> #include <star/scpr.h> #include <string.h> +void +make_b_pair + (struct b_pair* pair, + struct building* b1, + struct building* b2) +{ + ASSERT(pair && b1 && b2 && b1 != b2); + pair->b1 = MMIN(b1, b2); + pair->b2 = MMAX(b1, b2); +} res_T dump_obj (struct mem_allocator* allocator, - struct building* building) + struct building* building, + struct scpr_polygon* alternate_polygon) /* Can be NULL */ { res_T res = RES_OK; FILE* stream = NULL; struct str filename; + struct scpr_polygon* p; ASSERT(allocator && building); @@ -56,7 +71,8 @@ dump_obj ERR(str_copy(&filename, &building->name)); ERR(str_append(&filename, ".obj")); stream = fopen(str_cget(&filename), "w"); - ERR(scpr_polygon_dump_to_obj(building->pg, stream)); + p = alternate_polygon ? alternate_polygon : building->pg; + ERR(scpr_polygon_dump_to_obj(p, stream)); building->event_flags |= BUILDING_DUMPED_TO_OBJ; exit: if(stream) fclose(stream); @@ -66,6 +82,24 @@ error: goto exit; } +/* Unregister building from adjoining information of other buildings */ +static void +unregister_close_building + (struct city* city, + struct building* building) +{ + size_t i, n; + ASSERT(city && building); + for(i = 0; i < city->allocated_buildings_count; i++) { + struct building *b = city->buildings+i; + unsigned char *ptr; + ptr = htable_building_find(&b->close_buildings, &building); + if(!ptr) continue; /* No registered information */ + n = htable_building_erase(&b->close_buildings, &building); + ASSERT(n == 1); + } +} + #define STORE_CLOSE_INFO(Building1, Building2, Proximity) {\ unsigned char *ptr__, tmp__; \ ptr__ = htable_building_find(&(Building1)->close_buildings, &(Building2)); \ @@ -90,11 +124,14 @@ int overlapping_segments for(i = 0; i < ctx->buildings_count; i++) { struct building* building = ctx->buildings + i; enum building_event flag = BUILDING_WITH_CLOSE_NEIGHBOR; - if(building->pg == segment1->polygon) { + const struct scpr_polygon* pg = + ctx->alternate_polygons ? ctx->alternate_polygons[i] : building->pg; + if(pg == segment1->polygon) { building1 = building; name1 = str_cget(&building->name); building1->event_flags |= flag; - } else if(building->pg == segment2->polygon) { + } + if(pg == segment2->polygon) { building2 = building; name2 = str_cget(&building->name); building2->event_flags |= flag; @@ -107,8 +144,8 @@ int overlapping_segments /* store other polygon on building information */ STORE_CLOSE_INFO(building1, building2, CLOSE_PROXIMITY); STORE_CLOSE_INFO(building2, building1, CLOSE_PROXIMITY); - logger_print(ctx->logger, LOG_OUTPUT, - "Common segments detected between buildings '%s' and '%s'.\n", + logger_print(ctx->city->logger, LOG_OUTPUT, + "Overlapping segment detected between buildings '%s' and '%s'.\n", name1, name2); break; case CLOSE_PROXIMITY: @@ -135,19 +172,25 @@ int simple_intersection struct building *building1 = NULL, *building2 = NULL; enum building_event flag = ctx->search_type == OVERLAPPING_PROXIMITY ? BUILDING_WITH_OVERLAPPING : BUILDING_WITH_CLOSE_NEIGHBOR; + struct logger* logger; + struct mem_allocator* allocator; ASSERT(segment1 && segment2 && ctx); + logger = ctx->city->logger; + allocator = ctx->city->allocator; if(ctx->intersection_found) *ctx->intersection_found = 1; /* Search polygons in the city (slow, but OK) */ for(i = 0; i < ctx->buildings_count; i++) { struct building* building = ctx->buildings + i; - if(building->pg == segment1->polygon) { + const struct scpr_polygon* pg = + ctx->alternate_polygons ? ctx->alternate_polygons[i] : building->pg; + if(pg == segment1->polygon) { building1 = building; name1 = str_cget(&building->name); building1->event_flags |= flag; - } else if(building->pg == segment2->polygon) { + } else if(pg == segment2->polygon) { building2 = building; name2 = str_cget(&building->name); building2->event_flags |= flag; @@ -157,16 +200,27 @@ int simple_intersection switch(ctx->search_type) { case OVERLAPPING_PROXIMITY: - logger_print(ctx->logger, - (ctx->keep_running_on_errors ? LOG_WARNING : LOG_ERROR), - "Intersection detected between buildings '%s' and '%s'.\n", - name1, name2); + if(ctx->keep_running_on_errors) { + logger_print(logger, LOG_WARNING, + "Intersection detected between buildings '%s' and '%s'.\n", + name1, name2); + logger_print(logger, LOG_WARNING, + "Buildings will not be part of the output.\n"); + } else { + logger_print(logger, LOG_ERROR, + "Intersection detected between buildings '%s' and '%s'.\n", + name1, name2); + } /* Dump the polygons in OBJ files */ - ERR(dump_obj(ctx->allocator, building1)); - ERR(dump_obj(ctx->allocator, building2)); + if(ctx->dump_footprints_on_error) { + ERR(dump_obj(allocator, building1, NULL)); + ERR(dump_obj(allocator, building2, NULL)); + } + ERR(darray_pbuilding_push_back(&ctx->city->removed_buildings, &building1)); + ERR(darray_pbuilding_push_back(&ctx->city->removed_buildings, &building2)); break; case CLOSE_PROXIMITY: - logger_print(ctx->logger, LOG_OUTPUT, + logger_print(logger, LOG_OUTPUT, "Buildings '%s' and '%s' are in close proximity.\n", name1, name2); break; @@ -177,8 +231,10 @@ int simple_intersection STORE_CLOSE_INFO(building1, building2, ctx->search_type); STORE_CLOSE_INFO(building2, building1, ctx->search_type); - /* Return 1 to stop the process unless user asked to go further */ - if(ctx->keep_running_on_errors || ctx->dump_footprints_on_error) + /* Return 1 to stop the process unless whe are in proximity search or the user + * asked to go further */ + if(ctx->search_type == CLOSE_PROXIMITY + || ctx->keep_running_on_errors || ctx->dump_footprints_on_error) return 0; return 1; error: @@ -187,16 +243,6 @@ error: } #undef STORE_CLOSE_INFO -#define ERRtmp(Expr) \ -if((error_occured |= ((res_tmp = (Expr)) != RES_OK)) \ - && !city->dump_footprints_on_error) \ -{ \ - logger_print(logger, LOG_ERROR, \ - "Error initializing building '%s'.\n", str_cget(&building->name)); \ - res = res_tmp; \ - goto error;\ -} else (void)0 - res_T create_city (struct mem_allocator* allocator, @@ -210,7 +256,6 @@ create_city size_t i = 0; struct city* city = NULL; struct htable_names names; - int initialized = 0; struct scpr_device_create_args scpr_args = SCPR_DEVICE_CREATE_ARGS_DEFAULT; struct scpr_intersector* overlapping_intersector = NULL; struct scpr_intersector* close_intersector = NULL; @@ -221,10 +266,13 @@ create_city struct str name; int error_occured = 0; struct building* building = NULL; + struct darray_polygons offset_polygons; ASSERT(logger && allocator && args && parsed_city && catalog && out_city); str_init(allocator, &name); + darray_polygons_init(allocator, &offset_polygons); + htable_names_init(allocator, &names); city = MEM_CALLOC(allocator, 1, sizeof(*city)); if(!city) { @@ -232,9 +280,7 @@ create_city goto error; } - htable_names_init(allocator, &names); - initialized = 1; - + darray_pbuilding_init(allocator, &city->removed_buildings); city->allocated_buildings_count = parsed_city->city_building_list_count; city->buildings = MEM_CALLOC(allocator, city->allocated_buildings_count, sizeof(*city->buildings)); @@ -255,7 +301,9 @@ create_city city->keep_running_on_errors = args->keep_running_on_errors; city->dump_footprints_on_error = args->dump_footprints_on_error; htable_names_init(allocator, &city->dump_footprint_names); - city->names_initialized = 1; + htable_common_triangles_init(allocator, &city->common_triangles); + city->tables_initialized = 1; + /* Some specific building footprints will be dumped (command line request) */ for(i = 0; i < darray_names_size_get(&args->dump_footprint_names); i++) { char one = 1; const char* pname = darray_names_cdata_get(&args->dump_footprint_names)[i]; @@ -271,26 +319,41 @@ create_city ERR(scpr_intersector_create(city->scpr, &overlapping_intersector)); ERR(scpr_intersector_create(city->scpr, &close_intersector)); + ERR(darray_polygons_reserve(&offset_polygons, city->allocated_buildings_count)); /* create buildings depending on their construction modes */ - for (i = 0; i < city->allocated_buildings_count ; i++) { + for(i = 0; i < city->allocated_buildings_count ; i++) { + res_T tmp_res; struct parsed_city_building* parsed_data = parsed_city->city_building_list + i; - building = city->buildings + i; char one = 1; - res_T res_tmp = RES_OK; - city->initialized_buildings_count++; + int dump; + building = city->buildings + i; switch(parsed_data->cmode_type) { case PARSED_CMODE_0: - ERRtmp(init_cmode_0(city->scpr, allocator, logger, building, - city->keep_running_on_errors, parsed_data, catalog, - city->lower, city->upper)); + tmp_res = init_cmode_0(building, city, parsed_data, catalog, + city->lower, city->upper); break; case PARSED_CMODE_1: - ERRtmp(init_cmode_1(city->scpr, allocator, logger, building, - city->keep_running_on_errors, parsed_data, catalog, - city->lower, city->upper)); + tmp_res = init_cmode_1(building, city, parsed_data, catalog, + city->lower, city->upper); break; default: FATAL("Unknown construction mode"); } + /* Dump polygon if required */ + dump = htable_names_find(&city->dump_footprint_names, &building->name) + || (tmp_res != RES_OK && city->dump_footprints_on_error); + if(dump) { + ERR(dump_obj(allocator, building, NULL)); + } + if(tmp_res != RES_OK) { + if(city->keep_running_on_errors) { + logger_print(city->logger, LOG_WARNING, + "Building '%s' will not be part of the output.\n", + str_cget(&building->name)); + continue; + } + res = tmp_res; + goto error; + } /* Check name unicity. The error case is not supposed to happen: can be * tested after building creation as this simplifies the process */ if(htable_names_find(&names, &building->name)) { @@ -301,26 +364,23 @@ create_city goto error; } ERR(htable_names_set(&names, &building->name, &one)); - /* Dump polygon if in the list */ - if(htable_names_find(&city->dump_footprint_names, &building->name)) { - ERR(dump_obj(allocator, building)); - } /* Register the exterior building polygon for further overlapping detection */ ERR(scpr_intersector_register_polygon(overlapping_intersector, building->pg)); /* Register offset exterior building polygon for further proximity detection */ ERR(scpr_polygon_create_copy(city->scpr, building->pg, &tmp_polygon)); - ERR(scpr_offset_polygon(tmp_polygon, CLOSE_NEIGHBOR_DISTANCE, + ERR(scpr_offset_polygon(tmp_polygon, CG2_CLOSE_NEIGHBOR_DISTANCE, SCPR_JOIN_MITER)); ERR(scpr_intersector_register_polygon(close_intersector, tmp_polygon)); + ERR(darray_polygons_push_back(&offset_polygons, &tmp_polygon)); ERR(scpr_polygon_ref_put(tmp_polygon)); tmp_polygon = NULL; + building->event_flags |= BUILDING_CREATED; + city->initialized_buildings_count++; } building = NULL; - ASSERT(city->initialized_buildings_count == city->allocated_buildings_count); - /* Check for polygons overlapping and proximity */ - ctx.allocator = allocator; - ctx.logger = logger; + /* Check for polygons overlapping */ + ctx.city = city; ctx.buildings = city->buildings; ctx.buildings_count = city->initialized_buildings_count; ctx.intersection_found = &error_occured; @@ -329,22 +389,31 @@ create_city ctx.keep_running_on_errors = city->keep_running_on_errors; callbacks.simple_intersection = simple_intersection; callbacks.overlapping_segments = overlapping_segments; + ctx.alternate_polygons = NULL; ERR(scpr_intersector_check(overlapping_intersector, &callbacks, &ctx)); - ctx.search_type = CLOSE_PROXIMITY; - ctx.intersection_found = NULL; /* Not an error in this case */ - ERR(scpr_intersector_check(overlapping_intersector, &callbacks, &ctx)); - if(error_occured && !city->keep_running_on_errors) { res = RES_BAD_ARG; goto error; } + /* Remove removed buildings from proximity information of other buildings */ + for(i = 0; i < darray_pbuilding_size_get(&city->removed_buildings); i++) { + struct building* b = darray_pbuilding_data_get(&city->removed_buildings)[i]; + unregister_close_building(city, b); + } + + /* Check for polygons proximity */ + ctx.search_type = CLOSE_PROXIMITY; + ctx.alternate_polygons = darray_polygons_data_get(&offset_polygons); + ctx.intersection_found = NULL; /* Not an error in this case */ + ERR(scpr_intersector_check(close_intersector, &callbacks, &ctx)); exit: str_release(&name); + darray_polygons_release(&offset_polygons); if(tmp_polygon) SCPR(polygon_ref_put(tmp_polygon)); if(overlapping_intersector) SCPR(intersector_ref_put(overlapping_intersector)); if(close_intersector) SCPR(intersector_ref_put(close_intersector)); - if(initialized) htable_names_release(&names); + htable_names_release(&names); *out_city = city; return res; error: @@ -358,7 +427,6 @@ error: city = NULL; goto exit; } -#undef ERRtmp res_T release_city(struct city* city) @@ -372,13 +440,17 @@ release_city(struct city* city) } if(city->scpr) SCPR(device_ref_put(city->scpr)); - if(city->names_initialized) htable_names_release(&city->dump_footprint_names); + if(city->tables_initialized) { + htable_common_triangles_release(&city->common_triangles); + htable_names_release(&city->dump_footprint_names); + } /* iterate on building */ - for (i = 0; i < city->initialized_buildings_count; i++) { + for (i = 0; i < city->allocated_buildings_count; i++) { struct building* building = city->buildings + i; - ERR(building->functors->release(city->allocator, city->logger, building)); + ERR(building->functors->release(building)); } + darray_pbuilding_release(&city->removed_buildings); MEM_RM(city->allocator, city->buildings); MEM_RM(city->allocator, city); exit: @@ -393,11 +465,17 @@ city_cad_build(struct city* city) res_T res = RES_OK; struct scad_options options = SCAD_DEFAULT_OPTIONS__; int scad_initialized = 0; - size_t i, generated_buildings_count = 0; + size_t i, a, generated_buildings_count = 0; struct building *building = NULL; + struct darray_adjoining_data adjoining_data; + struct darray_double trg; + struct data_cad_cmode_0* cad = NULL; ASSERT(city); + darray_adjoining_data_init(city->allocator, &adjoining_data); + darray_double_init(city->allocator, &trg); + /* Initialize star-cad */ ERR(scad_initialize(city->logger, city->allocator, city->verbosisty_level)); scad_initialized = 1; @@ -408,34 +486,90 @@ city_cad_build(struct city* city) /* iterate on buildings */ for(i = 0; i < city->allocated_buildings_count; i++) { building = city->buildings + i; - struct data_cad_cmode_0* cad = NULL; - if(building->event_flags & BUILDING_OUT_OF_GROUND_EXTENT - || building->event_flags & BUILDING_WITH_OVERLAPPING) + if(building->event_flags & BUILDING_WITH_OVERLAPPING + || !(building->event_flags & BUILDING_CREATED)) { /* No fix for these problems */ - building->event_flags |= BUILDING_REMOVED; if(city->dump_footprints_on_error) { - ERR(dump_obj(city->allocator, building)); + ERR(dump_obj(city->allocator, building, NULL)); } } else { /* create building */ - ERR(building->functors->build_cad(city->scpr, city->allocator, - city->logger, building, city->dump_footprints_on_error, - city->keep_running_on_errors, (void**)&cad)); + struct adjoining_data* adj; + res = building->functors->build_cad(building, city->dump_footprints_on_error, + city->keep_running_on_errors, &adjoining_data, (void**)&cad); + if(res != RES_OK) { + if(city->dump_footprints_on_error) { + ERR(dump_obj(city->allocator, building, NULL)); + } + if(city->keep_running_on_errors) { + logger_print(city->logger, LOG_WARNING, + "Building '%s' will not be part of the output.\n", + str_cget(&building->name)); + /* Unregister building from adjoining information of other buildings */ + unregister_close_building(city, building); + /* FIXME: adjoining buildings may have been built already, taking this + * building into account. There is no simple way to undo this other + * than rebuild them after this building removal. The visible effects + * are on walls' meshes being uselessly refined due to conformity with + * the now-removed building, and possible window removal that should + * now be reversed. */ + res = RES_OK; + continue; + } + } + ERR(res); ERR(scad_scene_mesh()); - ERR(building->functors->export_stl(city->allocator, city->logger, cad, - city->binary_export)); - ERR(building->functors->release_cad(city->allocator, city->logger, cad)); + /* Keep the mesh of some geometry if planned */ + adj = darray_adjoining_data_data_get(&adjoining_data); + for(a = 0; a < darray_adjoining_data_size_get(&adjoining_data); a++) { + struct b_pair pair; + size_t c; + ERR(scad_geometry_get_count(adj->common_geometry, &c)); + if(c == 0) { + /* Not really adjoining */ + continue; + } + if(!adj[a].save || building != adj[a].main_building) { + /* Only the building that created the record can do the job + * (as geom lifetime is limited to a single iteration) */ + continue; + } + darray_double_clear(&trg); + /* Warning: one could think that reversing the triangles here would do + * the job of having them correctly oriented when reused from the + * other building's point of view. The fact is that this lead to a + * non-consistent orientation. As a consequence, triangle's orientation + * has to be set when the mesh is reused, based on the CAD normal. */ + ERR(scad_stl_get_data(adj[a].common_geometry, &trg)); + make_b_pair(&pair, building, adj[a].adjoining_building); + ERR(htable_common_triangles_set(&building->city->common_triangles, + &pair, &trg)); + } + ERR(building->functors->export_stl(cad, city->binary_export)); + building->event_flags |= BUILDING_CAD_EXPORTED_TO_STL; + logger_print(city->logger, LOG_OUTPUT, + "Building '%s': %s STL files created.\n", + str_cget(&building->name), (city->binary_export ? "binary" : "ascii")); + /* Cannot keep any geometry from one building to the other */ + ERR(building->functors->release_cad(cad)); + cad = NULL; /* Avoid double release */ + darray_adjoining_data_clear(&adjoining_data); ERR(scad_scene_clear()); generated_buildings_count++; + logger_print(city->logger, LOG_OUTPUT, + "Building '%s': done.\n", str_cget(&building->name)); } } building = NULL; city->cad_generated_buildings_count = generated_buildings_count; exit: + darray_adjoining_data_release(&adjoining_data); + if(cad) CHK(RES_OK == building->functors->release_cad(cad)); if(scad_initialized) SCAD(finalize()); + darray_double_release(&trg); return res; error: if(building) { @@ -470,14 +604,13 @@ city_ground_build(struct city* city) building = city->buildings + i; struct scad_geometry** footprint; - if(building->event_flags & BUILDING_REMOVED) + if(!(building->event_flags & BUILDING_CAD_EXPORTED_TO_STL)) continue; footprint = ground.footprints + g++; /* create building footprint */ - ERR(building->functors->build_footprint(city->scpr, city->allocator, - city->logger, building, footprint)); + ERR(building->functors->build_footprint(building, footprint)); } building = NULL; ERR(ground_build_cad(city->allocator, city, &ground)); diff --git a/src/cg_city.h b/src/cg_city.h @@ -20,20 +20,67 @@ #ifndef CITY_H #define CITY_H +#include "cg_building.h" + +#include <star/scpr.h> #include <star/scad.h> -#include <rsys/hash_table.h> #include <rsys/str.h> +#include <rsys/hash_table.h> +#include <rsys/dynamic_array.h> +#include <rsys/dynamic_array_double.h> struct logger; struct mem_allocator; -struct building; struct args; struct parsed_city; struct catalog; struct scpr_device; struct scpr_callback_segment; +static FINLINE void +ppoly_init + (struct mem_allocator* alloc, struct scpr_polygon** data) +{ + ASSERT(data); (void)alloc; + *data = NULL; +} +static INLINE void +ppoly_release(struct scpr_polygon** data) { + ASSERT(data); + if(*data) SCPR(polygon_ref_put(*data)); +} +static INLINE res_T +ppoly_copy(struct scpr_polygon** dst, struct scpr_polygon* const* src) { + ASSERT(dst && src); + *dst = *src; + SCPR(polygon_ref_get(*src)); + return RES_OK; +} +static FINLINE res_T +ppoly_copy_and_release + (struct scpr_polygon** dst, struct scpr_polygon* const* src) +{ + ASSERT(dst && src); + *dst = *src; + return RES_OK; +} +#define DARRAY_NAME polygons +#define DARRAY_DATA struct scpr_polygon* +#define DARRAY_FUNCTOR_RELEASE ppoly_release +#define DARRAY_FUNCTOR_COPY ppoly_copy +#define DARRAY_FUNCTOR_COPY_AND_RELEASE ppoly_copy_and_release +#include <rsys/dynamic_array.h> + +#define HTABLE_NAME polygons +#define HTABLE_KEY double +#define HTABLE_DATA struct scpr_polygon* +#define HTABLE_DATA_FUNCTOR_INIT ppoly_init +#define HTABLE_DATA_FUNCTOR_RELEASE ppoly_release +#define HTABLE_DATA_FUNCTOR_COPY ppoly_copy +#define HTABLE_DATA_FUNCTOR_COPY_AND_RELEASE ppoly_copy_and_release +#include <rsys/hash_table.h> + #define HTABLE_NAME names #define HTABLE_DATA char #define HTABLE_KEY struct str @@ -46,6 +93,34 @@ struct scpr_callback_segment; #define HTABLE_KEY_FUNCTOR_HASH str_hash #include <rsys/hash_table.h> +/* A table to link a bunch of triangles to a pair of buildings. + * Used to store the common triangles to ensure conformity despite the fact + * that buildings are meshed during differents star-cad sessions. */ +struct b_pair { + struct building* b1; + struct building* b2; +}; +static INLINE char +b_pair_eq(const struct b_pair* p0, const struct b_pair* p1) +{ + return p0->b1 == p1->b1 && p0->b2 == p1->b2; +} +void +make_b_pair + (struct b_pair* pair, + struct building* b1, + struct building* b2); +#define HTABLE_NAME common_triangles +#define HTABLE_DATA struct darray_double +#define HTABLE_DATA_FUNCTOR_INIT darray_double_init +#define HTABLE_DATA_FUNCTOR_RELEASE darray_double_release +#define HTABLE_DATA_FUNCTOR_COPY darray_double_copy +#define HTABLE_DATA_FUNCTOR_COPY_AND_RELEASE darray_double_copy_and_release +#define HTABLE_DATA_FUNCTOR_COPY_AND_CLEAR darray_double_copy_and_clear +#define HTABLE_KEY struct b_pair +#define HTABLE_KEY_FUNCTOR_EQ b_pair_eq +#include <rsys/hash_table.h> + struct city { double lower[2], upper[2]; /* Bbox */ double ground_depth; @@ -53,6 +128,8 @@ struct city { size_t cad_generated_buildings_count, allocated_buildings_count, initialized_buildings_count; struct htable_names dump_footprint_names; + struct htable_common_triangles common_triangles; + struct darray_pbuilding removed_buildings; struct mem_allocator* allocator; struct logger* logger; struct scpr_device* scpr; @@ -60,7 +137,7 @@ struct city { int verbosisty_level; int keep_running_on_errors; int dump_footprints_on_error; - int names_initialized; + int tables_initialized; }; res_T @@ -86,7 +163,8 @@ release_city(struct city* city); res_T dump_obj (struct mem_allocator* allocator, - struct building* building); + struct building* building, + struct scpr_polygon* alternate_polygon); /* Can be NULL */ /* An enum to encode type of proximity of buildings. */ enum building_proximity { @@ -97,9 +175,9 @@ enum building_proximity { /* the type of context expected by simple_intersection */ struct callback_ctx { - struct mem_allocator* allocator; - struct logger* logger; + struct city* city; struct building* buildings; + struct scpr_polygon** alternate_polygons; size_t buildings_count; int* intersection_found; /* Can be NULL if not to be registered */ enum building_proximity search_type; diff --git a/src/cg_city_parsing.c b/src/cg_city_parsing.c @@ -55,6 +55,11 @@ parse_city str_init(allocator, &parsed->filename); ERR(str_set(&parsed->filename, filename)); + /* Log outcome */ + logger_print(logger, LOG_OUTPUT, + "City map file '%s' parsed: %u building(s) read.\n", + filename, ((struct parsed_city*)parsed)->city_building_list_count); + exit: *out_parsed = parsed; return res; diff --git a/src/cg_construction_mode.c b/src/cg_construction_mode.c @@ -31,6 +31,20 @@ #include <star/scpr.h> void +pgeom_release(struct scad_geometry** data) { + ASSERT(data); + SCAD(geometry_ref_put(*data)); +} + +res_T +pgeom_copy(struct scad_geometry** dst, struct scad_geometry* const* src) { + ASSERT(dst && src); + *dst = *src; + SCAD(geometry_ref_get(*src)); + return RES_OK; +} + +void get_nverts(const size_t icomp, size_t* nverts, void* context) { struct parsed_city_building* parsed_data = context; @@ -57,11 +71,8 @@ void get_position_pg res_T init_building_base - (struct scpr_device* scpr, - struct mem_allocator* allocator, - struct logger* logger, - struct building* building, - int keep_running_on_error, + (struct building* building, + struct city* city, struct parsed_city_building* parsed_data, const double lower[2], const double upper[2]) @@ -69,26 +80,26 @@ init_building_base int inside; res_T res = RES_OK; - ASSERT(scpr && allocator && logger && building && parsed_data && lower && upper); + ASSERT(city && building && parsed_data && lower && upper); + building->city = city; building->height = parsed_data->height; - str_init(allocator, &building->name); - htable_building_init(allocator, &building->close_buildings); + str_init(city->allocator, &building->name); + htable_building_init(city->allocator, &building->close_buildings); building->structs_initialized = 1; ERR(str_set(&building->name, parsed_data->name)); - ERR(scpr_polygon_create(scpr, &building->pg)); + ERR(scpr_polygon_create(city->scpr, &building->pg)); ERR(scpr_polygon_setup_indexed_vertices(building->pg, 1, get_nverts, get_pos, parsed_data)); ERR(scpr_polygon_in_bbox(building->pg, lower, upper, &inside)); if(!inside) { - logger_print(logger, LOG_ERROR, + logger_print(city->logger, + (city->keep_running_on_errors ? LOG_WARNING : LOG_ERROR), "Building '%s' is out of the ground extent.\n", str_cget(&building->name)); building->event_flags |= BUILDING_OUT_OF_GROUND_EXTENT; - if(!keep_running_on_error) { - res = RES_BAD_ARG; - goto error; - } + res = RES_BAD_ARG; + goto error; } exit: @@ -99,14 +110,11 @@ error: res_T release_building_base - (struct mem_allocator* allocator, - struct logger* logger, - struct building* building) + (struct building* building) { res_T res = RES_OK; - (void) allocator; (void)logger; - ASSERT(allocator && logger && building); + ASSERT(building); if(building->structs_initialized) { str_release(&building->name); diff --git a/src/cg_construction_mode.h b/src/cg_construction_mode.h @@ -24,13 +24,39 @@ #include <rsys/rsys.h> #include <star/scpr.h> +#include <rsys/dynamic_array.h> -struct scpr_device; +struct city; struct mem_allocator; struct logger; struct building; struct parsed_city_building; struct catalog; +struct scad_geometry; +struct darray_double; + +#define DARRAY_NAME common_trg +#define DARRAY_DATA struct darray_double* +#include <rsys/dynamic_array.h> + +void +pgeom_release(struct scad_geometry** data); +res_T +pgeom_copy(struct scad_geometry** dst, struct scad_geometry* const* src); +static FINLINE res_T +pgeom_copy_and_release + (struct scad_geometry** dst, struct scad_geometry* const* src) +{ + ASSERT(dst && src); + *dst = *src; + return RES_OK; +} +#define DARRAY_NAME geometries +#define DARRAY_DATA struct scad_geometry* +#define DARRAY_FUNCTOR_RELEASE pgeom_release +#define DARRAY_FUNCTOR_COPY pgeom_copy +#define DARRAY_FUNCTOR_COPY_AND_RELEASE pgeom_copy_and_release +#include <rsys/dynamic_array.h> void get_nverts(const size_t icomp, size_t* nverts, void* context); @@ -43,20 +69,15 @@ void get_position_pg res_T init_building_base - (struct scpr_device* scpr, - struct mem_allocator* allocator, - struct logger* logger, - struct building* building, - int keep_running_on_error, + (struct building* building, + struct city* city, struct parsed_city_building* parsed_data, const double lower[2], const double upper[2]); res_T release_building_base - (struct mem_allocator* allocator, - struct logger* logger, - struct building* building); + (struct building* building); #endif diff --git a/src/cg_construction_mode_0.c b/src/cg_construction_mode_0.c @@ -28,8 +28,13 @@ #include <rsys/rsys.h> #include <rsys/str.h> #include <rsys/logger.h> +#include <rsys/double3.h> + #include <star/scad.h> #include <star/scpr.h> +#include <star/sstl.h> + +#include <limits.h> static res_T build_floor_footprint @@ -84,7 +89,7 @@ build_floor ERR(scad_geometry_extrude(footprint, floorname, d, floor)); exit: - SCAD(geometry_delete(footprint)); + SCAD(geometry_ref_put(footprint)); if (is_init) str_release(&name); return res; error: @@ -126,7 +131,7 @@ build_fake_ground ERR(scad_geometry_extrude(footprint, NULL, d, fake_ground)); exit: - SCAD(geometry_delete(footprint)); + SCAD(geometry_ref_put(footprint)); return res; error: goto exit; @@ -195,8 +200,8 @@ build_wall_footprint ERR(scad_cut_geometries(NULL, &polygon, 1, &polygon_int, 1, footprint)); exit: - if(polygon) SCAD(geometry_delete(polygon)); - if(polygon_int) SCAD(geometry_delete(polygon_int)); + if(polygon) SCAD(geometry_ref_put(polygon)); + if(polygon_int) SCAD(geometry_ref_put(polygon_int)); return res; error: goto exit; @@ -236,7 +241,7 @@ build_wall ERR(scad_geometry_extrude(footprint, wallname, d, wall)); exit: - if(footprint) SCAD(geometry_delete(footprint)); + if(footprint) SCAD(geometry_ref_put(footprint)); if (is_init) str_release(&name); return res; error: @@ -281,7 +286,7 @@ build_cavity ERR(scad_geometry_extrude(polygon, cavityname, d, cavity)); exit: - if(polygon) SCAD(geometry_delete(polygon)); + if(polygon) SCAD(geometry_ref_put(polygon)); if (is_init) str_release(&name); return res; error: @@ -351,20 +356,21 @@ static res_T build_boundary (const char* prefix, struct mem_allocator* allocator, - struct scad_geometry** adjoining_cad, - size_t adjoining_n, + struct darray_adjoining_data* adjoining_data, struct data_cad_cmode_0* data_cad) { res_T res = RES_OK; - size_t count = 0; struct scad_geometry** list = NULL; char* boundaryname = NULL; struct str name; int is_init = 0; - size_t i = 0; + struct adjoining_data* adj; + size_t adjoining_n, i = 0, count = 0; - ASSERT(allocator && prefix && data_cad); + ASSERT(allocator && prefix && adjoining_data && data_cad); + adjoining_n = darray_adjoining_data_size_get(adjoining_data); + adj = darray_adjoining_data_data_get(adjoining_data); str_init(allocator, &name); is_init = 1; @@ -374,32 +380,28 @@ build_boundary res = RES_MEM_ERR; goto error; } + /* Using wall here to compute boundary_wall is OK even if it doesn't take care + * of conformity wrt the adjoining building. The reason is that the common + * part that could end to be not conformal is not part of the boundary. As a + * consequence it cannot be part of the result. */ list[0] = data_cad->floor; - list[1] = data_cad->wall; - list[2] = data_cad->roof; + list[1] = data_cad->roof; + list[2] = data_cad->wall; list[3] = data_cad->cavity; list[4] = data_cad->fake_ground; - for (i=0; i<adjoining_n; i++) { - list[5+i] = adjoining_cad[i]; - } - - data_cad->boundary = MEM_ALLOC(allocator, 2 * sizeof(struct scad_geometry*)); - if(!data_cad->boundary) { - res = RES_MEM_ERR; - goto error; - } + for (i=0; i<adjoining_n; i++) list[5+i] = adj[i].envelop; ERR(str_set(&name, prefix)); ERR(str_append(&name, "_B_walls")); boundaryname = str_get(&name); ERR(scad_geometries_common_boundaries(boundaryname, list, count, - &data_cad->wall, 1, &data_cad->boundary[0])); + &data_cad->wall, 1, &data_cad->boundary_wall)); ERR(str_set(&name, prefix)); ERR(str_append(&name, "_B_roof")); boundaryname = str_get(&name); ERR(scad_geometries_common_boundaries(boundaryname, list, count, - &data_cad->roof, 1, &data_cad->boundary[1])); + &data_cad->roof, 1, &data_cad->boundary_roof)); exit: MEM_RM(allocator, list); @@ -448,11 +450,8 @@ error: res_T init_cmode_0 - (struct scpr_device* scpr, - struct mem_allocator* allocator, - struct logger* logger, - struct building* building, - int keep_running_on_error, + (struct building* building, + struct city* city, struct parsed_city_building* parsed_data, struct catalog* catalog, const double lower[2], @@ -461,8 +460,7 @@ init_cmode_0 res_T res = RES_OK; struct str dataset_name; int name_initialized = 0; - static struct construction_mode_functors functors_0 - = { + static struct construction_mode_functors functors_0 = { &init_cmode_0, &release_cmode_0, &build_cad_cmode_0, @@ -471,20 +469,21 @@ init_cmode_0 &export_stl_cmode_0, &release_cad_cmode_0 }; + struct mem_allocator* allocator; + struct logger* logger; (void)parsed_data; - if(!scpr || !allocator || !logger || !building || !parsed_data || !catalog - || !lower || !upper) - { + if(!city || !building || !parsed_data || !catalog || !lower || !upper) { res = RES_BAD_ARG; goto error; } + allocator = city->allocator; + logger = city->logger; building->construction_mode = mode_0; building->functors = &functors_0; - ERR(init_building_base(scpr, allocator, logger, building, keep_running_on_error, - parsed_data, lower, upper)); + ERR(init_building_base(building, city, parsed_data, lower, upper)); str_init(allocator, &dataset_name); name_initialized = 1; @@ -508,61 +507,49 @@ error: res_T release_cmode_0 - (struct mem_allocator* allocator, - struct logger* logger, - struct building* building) + (struct building* building) { - res_T res = RES_OK; - - if(!allocator || !logger || !building) { - res = RES_BAD_ARG; - goto error; - } - - ERR(release_building_base(allocator, logger, building)); - -exit: - return res; -error: - goto exit; + if(!building) return RES_BAD_ARG; + return release_building_base(building); } res_T build_cad_cmode_0 - (struct scpr_device* scpr, - struct mem_allocator* allocator, - struct logger* logger, - struct building* building, + (struct building* building, int dump_footprints_on_error, int keep_running_on_errors, + struct darray_adjoining_data* adjoining_data, void** cad) { res_T res = RES_OK; double height; - struct scpr_polygon* pg = NULL; struct scpr_polygon* pg_int = NULL; struct dataset_cmode_0* data = NULL; struct data_cad_cmode_0* data_cad = NULL; double e_wall; const char* name; - struct scad_geometry** adjoining_cad = NULL; - size_t i = 0; + struct scad_geometry *tmp = NULL; size_t adjoining_n = 0; struct scpr_intersector* overlapping_intersector = NULL; struct scpr_intersector_check_callbacks callbacks = SCPR_INTERSECTOR_CHECK_CALLBACKS_NULL__; struct callback_ctx ctx; - int error_occured = 0; + int error_occured = 0, error_msg_printed = 0; + struct scpr_device* scpr; + struct mem_allocator* allocator; + struct logger* logger; + size_t c; - if (!building || !allocator || !cad) { + if (!building || !cad || !adjoining_data) { res = RES_BAD_ARG; goto error; } + scpr = building->city->scpr; + allocator = building->city->allocator; + logger = building->city->logger; height = building->height; - pg = building->pg; data = (struct dataset_cmode_0 *)building->data; - adjoining_n = htable_building_size_get(&building->close_buildings); if (height <= 0 || data->wall_thickness <= 0 || data->floor_thickness <= 0) { res = RES_BAD_ARG; @@ -574,18 +561,30 @@ build_cad_cmode_0 res = RES_MEM_ERR; goto error; } + data_cad->allocator = allocator; + darray_common_trg_init(allocator, &data_cad->common_trg); + darray_geometries_init(allocator, &data_cad->adj_walls); ERR(scpr_intersector_create(scpr, &overlapping_intersector)); ERR(scpr_intersector_register_polygon(overlapping_intersector, building->pg)); e_wall = data->wall_thickness; - ERR(scpr_polygon_create_copy(scpr, pg, &pg_int)); + ERR(scpr_polygon_create_copy(scpr, building->pg, &pg_int)); ERR(scpr_offset_polygon(pg_int, -e_wall, SCPR_JOIN_MITER)); + ERR(scpr_polygon_get_components_count(pg_int, &c)); + if(c == 0) { + /* Building is too small wrt wall thickness */ + logger_print(logger, (keep_running_on_errors ? LOG_WARNING : LOG_ERROR), + "Building '%s' is too small with respect to wall thickness.\n", + str_cget(&building->name)); + error_msg_printed = 1; + res = RES_BAD_ARG; + goto error; + } ERR(scpr_intersector_register_polygon(overlapping_intersector, pg_int)); - /* Check for polygons overlapping */ - ctx.allocator = allocator; - ctx.logger = logger; + /* Check for registered polygons overlapping */ + ctx.city = building->city; ctx.buildings = building; ctx.buildings_count = 1; ctx.intersection_found = &error_occured; @@ -594,6 +593,15 @@ build_cad_cmode_0 ctx.keep_running_on_errors = keep_running_on_errors; callbacks.simple_intersection = simple_intersection; ERR(scpr_intersector_check(overlapping_intersector, &callbacks, &ctx)); + if(error_occured) { + logger_print(logger, LOG_ERROR, + "Internal error building CAD for building '%s'.\n", + str_cget(&building->name)); + ERR(darray_pbuilding_push_back(&building->city->removed_buildings, &building)); + error_msg_printed = 1; + res = RES_BAD_ARG; + goto error; + } /* build floor with pg_int */ name = str_cget(&building->name); @@ -603,59 +611,95 @@ build_cad_cmode_0 ERR(build_roof(name, building, data_cad->floor, &data_cad->roof)); /* build wall with pg and pg_int */ - ERR(build_wall(name, pg, pg_int, building, &data_cad->wall)); + ERR(build_wall(name, building->pg, pg_int, building, &data_cad->wall)); /* build cavity */ ERR(build_cavity(name, pg_int, building, &data_cad->cavity)); - /* build adjoining envelop */ + adjoining_n = htable_building_size_get(&building->close_buildings); if (adjoining_n > 0) { - ERR(build_adjoining(allocator, logger, building, &adjoining_cad)); + ERR(build_adjoining(building, adjoining_data)); + ASSERT(adjoining_n == darray_adjoining_data_size_get(adjoining_data)); } /* build fake ground */ - ERR(build_fake_ground(pg, &data_cad->fake_ground)); + ERR(build_fake_ground(building->pg, &data_cad->fake_ground)); ERR(scad_scene_partition()); + /* After partitioning, manage common parts with other buildings */ + if(adjoining_n > 0) { + size_t a; + struct b_pair pair; + struct darray_double* common_trg; + struct adjoining_data* adjoining + = darray_adjoining_data_data_get(adjoining_data); + for(a = 0; a < adjoining_n; a++) { + struct adjoining_data* adj = adjoining + a; + ERR(scad_geometries_common_boundaries(NULL, &data_cad->wall, 1, + &adj->envelop, 1, &adj->common_geometry)); + ERR(scad_geometry_get_count(adj->common_geometry, &c)); + if(c == 0) { + /* Not really adjoining */ + logger_print(logger, LOG_OUTPUT, + "building '%s': neighbor '%s' not really adjoining.\n", + str_cget(&building->name), + str_cget(&adj->adjoining_building->name)); + continue; + } + make_b_pair(&pair, building, adj->adjoining_building); + common_trg + = htable_common_triangles_find(&building->city->common_triangles, &pair); + if(common_trg) { + /* The common geometry has already been processed when creating a + * previous building, and the very same mesh must be used for this one. + * Keep track of the geometry to replace and the mesh to output instead */ + ERR(darray_geometries_push_back(&data_cad->adj_walls, + &adj->common_geometry)); + ERR(darray_common_trg_push_back(&data_cad->common_trg, &common_trg)); + } else { + /* The mesh doesn't exist yet and won't be created until a further step. + * We need to store the geometry id so that the mesh can be stored when + * created. */ + adjoining[a].save = 1; + } + } + } + /* build ground/building connection */ ERR(building_ground_connection(allocator, name, data_cad, &data_cad->ground_connection)); /* build boundary */ - ERR(build_boundary(name, allocator, adjoining_cad, adjoining_n, data_cad)); + ERR(build_boundary(name, allocator, adjoining_data, data_cad)); /* build cavity/floor connectiona*/ ERR(build_connection(name, allocator, data_cad)); exit: + if(tmp) SCAD(geometry_ref_put(tmp)); if(pg_int) SCPR(polygon_ref_put(pg_int)); if(overlapping_intersector) SCPR(intersector_ref_put(overlapping_intersector)); - *(struct data_cad_cmode_0**)cad = data_cad; - for (i=0 ; i<adjoining_n; i++) { - if (adjoining_cad[i]) { - ERR(scad_geometry_delete(adjoining_cad[i])); - } - } - MEM_RM(allocator, adjoining_cad); + if(cad) *(struct data_cad_cmode_0**)cad = data_cad; return res; error: - if(data_cad) CHK(RES_OK == release_cad_cmode_0(allocator, logger, data_cad)); + if(building && !error_msg_printed) { + logger_print(logger, LOG_ERROR, + "Unknown error building CAD for building '%s'.\n", + str_cget(&building->name)); + } + if(data_cad) CHK(RES_OK == release_cad_cmode_0(data_cad)); data_cad = NULL; goto exit; } res_T build_footprint_cmode_0 - (struct scpr_device* scpr, - struct mem_allocator* allocator, - struct logger* logger, - struct building* building, + (struct building* building, struct scad_geometry** footprint) { res_T res = RES_OK; - (void)scpr; (void)allocator; (void)logger; if(!building || ! footprint) { res = RES_BAD_ARG; @@ -672,9 +716,7 @@ error: res_T build_envelop_cmode_0 - (struct mem_allocator* allocator, - struct logger* logger, - struct building* building, + (struct building* building, struct scad_geometry** envelop) { res_T res = RES_OK; @@ -684,8 +726,6 @@ build_envelop_cmode_0 struct scpr_polygon* pg = building->pg; struct scad_geometry* footprint = NULL; - (void)allocator; (void)logger; - ERR(scpr_polygon_get_vertices_count(pg, 0, &nverts)); ERR(scad_add_polygon(NULL, get_position_pg, pg, 0, nverts, &footprint)); @@ -693,7 +733,7 @@ build_envelop_cmode_0 ERR(scad_geometry_extrude(footprint, NULL, d, envelop)); exit: - if (footprint) scad_geometry_delete(footprint); + if (footprint) SCAD(geometry_ref_put(footprint)); return res; error: goto exit; @@ -701,15 +741,16 @@ error: res_T export_stl_cmode_0 - (struct mem_allocator* allocator, - struct logger* logger, - void* cad, + (void* cad, const int binary) { res_T res = RES_OK; struct data_cad_cmode_0* data_cad = (struct data_cad_cmode_0*)cad; - size_t i; - (void)allocator; (void)logger; + size_t i, j, common_n = 0, coord_count = 0; + struct darray_double trg; + struct str name; + int initialized = 0; + struct scad_geometry** list = NULL; if(!cad) { res = RES_BAD_ARG; @@ -717,30 +758,110 @@ export_stl_cmode_0 } /* floor export */ - ERR(scad_stl_export(data_cad->floor, NULL, 0, binary)); + ERR(scad_stl_export(data_cad->floor, NULL, binary)); /* roof export */ - ERR(scad_stl_export(data_cad->roof, NULL, 0, binary)); + ERR(scad_stl_export(data_cad->roof, NULL, binary)); /* wall export */ - ERR(scad_stl_export(data_cad->wall, NULL, 0, binary)); + if(darray_geometries_size_get(&data_cad->adj_walls) == 0) { + ERR(scad_stl_export(data_cad->wall, NULL, binary)); + } else { + /* There is some adjoining building(s) to manage */ + struct darray_double **common + = darray_common_trg_data_get(&data_cad->common_trg); + size_t common_count = darray_common_trg_size_get(&data_cad->common_trg); + const char* tmp; + + darray_double_init(data_cad->allocator, &trg); + str_init(data_cad->allocator, &name); + initialized = 1; + /* Get the triangles that are not common with adjoining buildings */ + ERR(scad_stl_get_data_partial(data_cad->wall, + darray_geometries_data_get(&data_cad->adj_walls), + darray_geometries_size_get(&data_cad->adj_walls), + &trg)); + coord_count = darray_double_size_get(&trg); + /* Add the triangles from adjoining buildings */ + for(i = 0; i < common_count; i++) { + size_t sz; + int found = 0; + const double* t9 = darray_double_cdata_get(common[i]); + double* tgt; + struct scad_geometry* common_g + = darray_geometries_data_get(&data_cad->adj_walls)[i]; + double n[3], e1[3], e2[3], reverse; + /* Determine the normal orientation for common triangles */ + d3_sub(e1, t9 + 3, t9); + d3_sub(e2, t9 + 6, t9); + d3_cross(n, e1, e2); + ERR(scad_geometry_explode(common_g, NULL, &list, &common_n)); + for(j = 0; j < common_n; j++) { + double center[3], N[3]; + struct scad_geometry* surface; + ERR(scad_geometry_get_centerofmass(list[j], center)); + ERR(scad_geometry_normal(list[j], center, N, NULL, &surface)); + ERR(scad_geometry_ref_put(surface)); + reverse = d3_dot(N, n); + if(fabs(reverse) > 0.99*d3_len(n)*d3_len(N)) { + found = 1; + break; + } + } + for(j = 0; j < common_n; j++) { + ERR(scad_geometry_ref_put(list[j])); + } + MEM_RM(data_cad->allocator, list); + common_n = 0; list = NULL; /* Avoid double free */ + if(!found) { + res = RES_BAD_ARG; + goto error; + } + /* Add common triangles */ + sz = darray_double_size_get(common[i]); + ASSERT(sz % 9 == 0); + ASSERT(coord_count == darray_double_size_get(&trg)); + ERR(darray_double_resize(&trg, coord_count + sz)); + tgt = darray_double_data_get(&trg); + for(j = 0; j < sz; j += 9) { + d3_set(tgt + coord_count + j+0, t9 + j+0); + d3_set(tgt + coord_count + j+3, t9 + j+(reverse>0 ? 3 : 6)); + d3_set(tgt + coord_count + j+6, t9 + j+(reverse>0 ? 6 : 3)); + } + coord_count += sz; + ASSERT(coord_count % 9 == 0); + } + ERR(scad_geometry_get_name(data_cad->wall, &tmp)); + ERR(str_set(&name, tmp)); + ERR(str_append(&name, ".stl")); + ERR(scad_stl_data_write(&trg, str_cget(&name), binary)); + } /* cavity export */ - ERR(scad_stl_export(data_cad->cavity, NULL, 0, binary)); + ERR(scad_stl_export(data_cad->cavity, NULL, binary)); /* connection export */ for (i = 0; i < data_cad->n_connection; i++) { - ERR(scad_stl_export(data_cad->connection[i], NULL, 0, binary)); + ERR(scad_stl_export(data_cad->connection[i], NULL, binary)); } /* boundary export */ - ERR(scad_stl_export(data_cad->boundary[0], NULL, 0, binary)); - ERR(scad_stl_export(data_cad->boundary[1], NULL, 0, binary)); + ERR(scad_stl_export(data_cad->boundary_wall, NULL, binary)); + ERR(scad_stl_export(data_cad->boundary_roof, NULL, binary)); /* footprint export */ - ERR(scad_stl_export(data_cad->ground_connection, NULL, 1, binary)); + ERR(scad_geometry_reverse(data_cad->ground_connection)); + ERR(scad_stl_export(data_cad->ground_connection, NULL, binary)); exit: + for(j = 0; j < common_n; j++) { + if(list[j]) SCAD(geometry_ref_put(list[j])); + } + MEM_RM(data_cad->allocator, list); + if(initialized) { + darray_double_release(&trg); + str_release(&name); + } return res; error: goto exit; @@ -748,22 +869,24 @@ error: res_T release_cad_cmode_0 - (struct mem_allocator* allocator, - struct logger* logger, - void* cad) + (void* cad) { res_T res = RES_OK; struct data_cad_cmode_0* data_cad = (struct data_cad_cmode_0 *)cad; + struct mem_allocator* allocator; size_t i; - (void)logger; - if(!allocator || ! cad) { + if(!cad) { res = RES_BAD_ARG; goto error; } + allocator = data_cad->allocator; + darray_common_trg_release(&data_cad->common_trg); + darray_geometries_release(&data_cad->adj_walls); + #define GDEL(Field) \ - if(data_cad->Field) SCAD(geometry_delete(data_cad->Field)); \ + if(data_cad->Field) SCAD(geometry_ref_put(data_cad->Field)); \ /* To ease debugging, set to NULL after deletion */ \ data_cad->Field = NULL GDEL(cavity); @@ -772,15 +895,14 @@ release_cad_cmode_0 GDEL(roof); GDEL(wall); GDEL(fake_ground); - GDEL(boundary[0]); - GDEL(boundary[1]); + GDEL(boundary_wall); + GDEL(boundary_roof); for(i = 0; i < data_cad->n_connection; i++) { GDEL(connection[i]); } - MEM_RM(allocator, data_cad->boundary); +#undef GDEL MEM_RM(allocator, data_cad->connection); MEM_RM(allocator, data_cad); -#undef GDEL exit: return res; diff --git a/src/cg_construction_mode_0.h b/src/cg_construction_mode_0.h @@ -20,17 +20,20 @@ #ifndef Construction_MODE_0_H #define Construction_MODE_0_H +#include "cg_construction_mode.h" + #include <rsys/rsys.h> #include <rsys/str.h> +#include <rsys/dynamic_array.h> #include <rsys/hash_table.h> struct scad_geometry; struct building; -struct mem_allocator; -struct logger; struct parsed_city_building; struct catalog; -struct scpr_device; +struct city; +struct darray_double; +struct darray_adjoining_data; /* specific data for construction mode 0 */ struct dataset_cmode_0 { @@ -51,12 +54,16 @@ struct dataset_cmode_0 { #include <rsys/hash_table.h> struct data_cad_cmode_0 { + struct mem_allocator* allocator; + struct darray_common_trg common_trg; + struct darray_geometries adj_walls; struct scad_geometry* wall; struct scad_geometry* roof; struct scad_geometry* floor; struct scad_geometry* cavity; struct scad_geometry* fake_ground; - struct scad_geometry** boundary; + struct scad_geometry* boundary_wall; + struct scad_geometry* boundary_roof; struct scad_geometry** connection; struct scad_geometry* ground_connection; size_t n_connection; @@ -64,11 +71,8 @@ struct data_cad_cmode_0 { res_T init_cmode_0 - (struct scpr_device* scpr, - struct mem_allocator* allocator, - struct logger* logger, - struct building* building, - int keep_running_on_error, + (struct building* building, + struct city* city, struct parsed_city_building* parsed_data, struct catalog* catalog, const double lower[2], @@ -76,46 +80,33 @@ init_cmode_0 res_T release_cmode_0 - (struct mem_allocator* allocator, - struct logger* logger, - struct building* building); + (struct building* building); res_T build_cad_cmode_0 - (struct scpr_device* scpr, - struct mem_allocator* allocator, - struct logger* logger, - struct building* building, + (struct building* building, int dump_footprints_on_error, int keep_running_on_errors, + struct darray_adjoining_data* adjoining_data, void** cad); res_T build_footprint_cmode_0 - (struct scpr_device* scpr, - struct mem_allocator* allocator, - struct logger* logger, - struct building* building, + (struct building* building, struct scad_geometry** footprint); res_T build_envelop_cmode_0 - (struct mem_allocator* allocator, - struct logger* logger, - struct building* building, + (struct building* building, struct scad_geometry** envelop); res_T export_stl_cmode_0 - (struct mem_allocator* allocator, - struct logger* logger, - void* cad, + (void* cad, const int binary); res_T release_cad_cmode_0 - (struct mem_allocator* allocator, - struct logger* logger, - void* cad); + (void* cad); #endif /* Construction_MODE_0_H */ diff --git a/src/cg_construction_mode_1.c b/src/cg_construction_mode_1.c @@ -18,6 +18,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "cg.h" +#include "cg_default.h" #include "cg_building.h" #include "cg_catalog.h" #include "cg_city.h" @@ -28,37 +29,17 @@ #include <rsys/str.h> #include <rsys/logger.h> #include <rsys/hash_table.h> +#include <rsys/double3.h> #include <star/scad.h> #include <star/scpr.h> -/* An htable to associate offset polygons to offsets. - * Not really to avoid offseting more than once to the same offset, but to avoid - * considering the same polygon more than once when checking for polygon - * intersections (that are internal errors). */ -static FINLINE void -ptr_polygon_init - (struct mem_allocator* alloc, struct scpr_polygon** data) -{ - ASSERT(data); (void)alloc; - *data = NULL; -} -static FINLINE void -ptr_polygon_release(struct scpr_polygon** data) -{ - ASSERT(data); - if(*data) SCPR(polygon_ref_put(*data)); -} -#define HTABLE_NAME polygons -#define HTABLE_KEY double -#define HTABLE_DATA struct scpr_polygon* -#define HTABLE_DATA_FUNCTOR_INIT ptr_polygon_init -#define HTABLE_DATA_FUNCTOR_RELEASE ptr_polygon_release -#include <rsys/hash_table.h> - static res_T build_floor (struct scpr_device* scpr, struct mem_allocator* allocator, + struct logger* logger, + int *error_msg_printed, + int keep_running_on_errors, const char* prefix, const struct scpr_polygon* pg, const struct dataset_cmode_1* data, @@ -95,8 +76,20 @@ build_floor if(ptr_p) { pg_int = *ptr_p; } else { + size_t c; ERR(scpr_polygon_create_copy(scpr, pg, &pg_int)); ERR(scpr_offset_polygon(pg_int, offset, SCPR_JOIN_MITER)); + ERR(scpr_polygon_get_components_count(pg_int, &c)); + if(c == 0) { + /* Building is too small wrt wall thickness */ + logger_print(logger, + (keep_running_on_errors ? LOG_WARNING : LOG_ERROR), + "Building '%s' is too small with respect to wall thickness.\n", + prefix); + *error_msg_printed = 1; + res = RES_BAD_ARG; + goto error; + } ERR(scpr_intersector_register_polygon(overlapping_intersector, pg_int)); htable_polygons_set(polygons, &offset, &pg_int); } @@ -109,7 +102,8 @@ build_floor ERR(scad_geometry_extrude(footprint, floorname, d, floor)); exit: - SCAD(geometry_delete(footprint)); + if(footprint) SCAD(geometry_ref_put(footprint)); + if(!ptr_p) SCPR(polygon_ref_put(pg_int)); if (is_init) str_release(&name); return res; error: @@ -135,7 +129,8 @@ build_wall double offset = 0; struct scpr_polygon* pg_int = NULL; struct scpr_polygon* pg_ext = NULL; - struct scpr_polygon** ptr_p; + struct scpr_polygon** ptr_pi = NULL; + struct scpr_polygon** ptr_pe = NULL; size_t nverts = 0; struct scad_geometry* footprint = NULL; struct scad_geometry* footprint_int = NULL; @@ -163,9 +158,9 @@ build_wall } else { offset = -e_insulation; } - ptr_p = htable_polygons_find(polygons, &offset); - if(ptr_p) { - pg_ext = *ptr_p; + ptr_pe = htable_polygons_find(polygons, &offset); + if(ptr_pe) { + pg_ext = *ptr_pe; } else { ERR(scpr_polygon_create_copy(scpr, pg, &pg_ext)); ERR(scpr_offset_polygon(pg_ext, offset, SCPR_JOIN_MITER)); @@ -174,9 +169,9 @@ build_wall } offset = -(e_wall + e_insulation); - ptr_p = htable_polygons_find(polygons, &offset); - if(ptr_p) { - pg_int = *ptr_p; + ptr_pi = htable_polygons_find(polygons, &offset); + if(ptr_pi) { + pg_int = *ptr_pi; } else { ERR(scpr_polygon_create_copy(scpr, pg, &pg_int)); ERR(scpr_offset_polygon(pg_int, offset, SCPR_JOIN_MITER)); @@ -200,9 +195,11 @@ build_wall ERR(scad_geometry_extrude(footprint, wallname, d, wall)); exit: - SCAD(geometry_delete(footprint)); - SCAD(geometry_delete(footprint_int)); - SCAD(geometry_delete(footprint_ext)); + if(!ptr_pi) SCPR(polygon_ref_put(pg_int)); + if(!ptr_pe) SCPR(polygon_ref_put(pg_ext)); + if(footprint) SCAD(geometry_ref_put(footprint)); + if(footprint_int) SCAD(geometry_ref_put(footprint_int)); + if(footprint_ext) SCAD(geometry_ref_put(footprint_ext)); if (is_init) str_release(&name); return res; error: @@ -232,7 +229,8 @@ build_int_insulation double offset = 0; struct scpr_polygon* pg_int = NULL; struct scpr_polygon* pg_ext = NULL; - struct scpr_polygon** ptr_p; + struct scpr_polygon** ptr_pi = NULL; + struct scpr_polygon** ptr_pe = NULL; size_t nverts = 0; struct scad_geometry* footprint = NULL; struct scad_geometry* footprint_int = NULL; @@ -254,9 +252,9 @@ build_int_insulation } offset = -(e_ext_insulation + e_wall); - ptr_p = htable_polygons_find(polygons, &offset); - if(ptr_p) { - pg_ext = *ptr_p; + ptr_pe = htable_polygons_find(polygons, &offset); + if(ptr_pe) { + pg_ext = *ptr_pe; } else { ERR(scpr_polygon_create_copy(scpr, pg, &pg_ext)); ERR(scpr_offset_polygon(pg_ext, offset, SCPR_JOIN_MITER)); @@ -265,9 +263,9 @@ build_int_insulation } offset = -(e_ext_insulation + e_wall + e_int_insulation); - ptr_p = htable_polygons_find(polygons, &offset); - if(ptr_p) { - pg_int = *ptr_p; + ptr_pi = htable_polygons_find(polygons, &offset); + if(ptr_pi) { + pg_int = *ptr_pi; } else { ERR(scpr_polygon_create_copy(scpr, pg, &pg_int)); ERR(scpr_offset_polygon(pg_int, offset, SCPR_JOIN_MITER)); @@ -295,10 +293,12 @@ build_int_insulation } exit: - SCAD(geometry_delete(footprint)); - SCAD(geometry_delete(footprint_int)); - SCAD(geometry_delete(footprint_ext)); - SCAD(geometry_delete(geom)); + if(!ptr_pi) SCPR(polygon_ref_put(pg_int)); + if(!ptr_pe) SCPR(polygon_ref_put(pg_ext)); + if(footprint) SCAD(geometry_ref_put(footprint)); + if(footprint_int) SCAD(geometry_ref_put(footprint_int)); + if(footprint_ext) SCAD(geometry_ref_put(footprint_ext)); + if(geom) SCAD(geometry_ref_put(geom)); if (is_init) str_release(&name); return res; error: @@ -362,7 +362,7 @@ build_roof ERR(scad_geometry_extrude(footprint, roofname, d, roof)); exit: - SCAD(geometry_delete(footprint)); + if(footprint) SCAD(geometry_ref_put(footprint)); if (is_init) str_release(&name); return res; error: @@ -429,7 +429,7 @@ build_roof_insulation ERR(scad_geometry_extrude(footprint, insulationname, d, insulation)); exit: - SCAD(geometry_delete(footprint)); + if(footprint) SCAD(geometry_ref_put(footprint)); if (is_init) str_release(&name); return res; error: @@ -494,7 +494,7 @@ build_floor_insulation ERR(scad_geometry_extrude(footprint, insulationname, d, insulation)); exit: - SCAD(geometry_delete(footprint)); + if(footprint) SCAD(geometry_ref_put(footprint)); if (is_init) str_release(&name); return res; error: @@ -530,6 +530,7 @@ build_inter_floor size_t nverts = 0; struct scad_geometry** floor_list = NULL; struct darray_geometries floor_array; + struct scad_geometry *footprint = NULL, *floor = NULL; double d[3] = {0, 0, 0}; char* floorname = NULL; struct str name; @@ -563,14 +564,14 @@ build_inter_floor z_floor = h_cavity/(double)(1 + floor_n); d[2] = e_floor; for (i = 0; i < floor_n; i++) { - struct scad_geometry* floor = NULL; - struct scad_geometry* footprint = NULL; - - ERR(scad_add_polygon( - NULL, get_position_pg, pg_int, z_floor, nverts, &footprint)); + ERR(scad_add_polygon( NULL, get_position_pg, pg_int, z_floor, nverts, + &footprint)); ERR(scad_geometry_extrude(footprint, NULL, d, &floor)); + ERR(scad_geometry_ref_put(footprint)); + footprint = NULL; /* Avoid double free */ ERR(darray_geometries_push_back(&floor_array, &floor)); - ERR(scad_geometry_delete(footprint)); + ERR(scad_geometry_ref_put(floor)); + floor = NULL; /* Avoid double free */ z_floor += h_cavity/(double)(1 + floor_n) + e_floor; } ASSERT(darray_geometries_size_get(&floor_array) == floor_n); @@ -581,13 +582,9 @@ build_inter_floor exit: if (is_init) str_release(&name); - floor_list = darray_geometries_data_get(&floor_array); - if(floor_list) { - for (i = 0; i < floor_n; i++) { - SCAD(geometry_delete(floor_list[i])); - } - darray_geometries_release(&floor_array); - } + if(footprint) SCAD(geometry_ref_put(footprint)); + if(floor) SCAD(geometry_ref_put(floor)); + if(floor_list) darray_geometries_release(&floor_array); return res; error: goto exit; @@ -662,9 +659,9 @@ build_ext_insulation ERR(scad_geometry_extrude(footprint, insulationname, d, insulation)); exit: - SCAD(geometry_delete(footprint)); - SCAD(geometry_delete(footprint_int)); - SCAD(geometry_delete(footprint_ext)); + if(footprint) SCAD(geometry_ref_put(footprint)); + if(footprint_int) SCAD(geometry_ref_put(footprint_int)); + if(footprint_ext) SCAD(geometry_ref_put(footprint_ext)); if (is_init) str_release(&name); return res; error: @@ -730,7 +727,7 @@ build_crawlspace ERR(scad_geometry_extrude(footprint, crawlname, d, crawlspace)); exit: - SCAD(geometry_delete(footprint)); + if(footprint) SCAD(geometry_ref_put(footprint)); if (is_init) str_release(&name); return res; error: @@ -804,8 +801,8 @@ build_habitable } exit: - SCAD(geometry_delete(footprint)); - SCAD(geometry_delete(geom)); + if(footprint) SCAD(geometry_ref_put(footprint)); + if(geom) SCAD(geometry_ref_put(geom)); if (is_init) str_release(&name); return res; error: @@ -871,7 +868,7 @@ build_attic ERR(scad_geometry_extrude(footprint, atticname, d, attic)); exit: - SCAD(geometry_delete(footprint)); + if(footprint) SCAD(geometry_ref_put(footprint)); if (is_init) str_release(&name); return res; error: @@ -886,11 +883,10 @@ build_windows const char* prefix, const struct dataset_cmode_1* data, struct data_cad_cmode_1* data_cad, - struct scad_geometry** adjoining_cad, - size_t adjoining_n) + struct darray_adjoining_data* adjoining_data) { res_T res = RES_OK; - size_t i, removed_windows = 0; + size_t i, j, adjoining_n, removed_windows = 0; double N[3]; double dir[3]; double scale[3]; @@ -902,60 +898,64 @@ build_windows struct scad_geometry* hole_adjoining_intersect = NULL; struct scad_geometry* bcavity = NULL; struct scad_geometry** list = NULL; + struct scad_geometry** adj_list = NULL; struct scad_geometry* glass = NULL; struct scad_geometry** glass_list = NULL; struct darray_geometries glass_array; size_t list_n = 0, array_n; struct str gname; int is_init = 0; + struct adjoining_data* adj; (void)scpr; - ASSERT(scpr && allocator && logger && data && data_cad); + ASSERT(scpr && allocator && logger && data && data_cad && adjoining_data); + adjoining_n = darray_adjoining_data_size_get(adjoining_data); + adj = darray_adjoining_data_data_get(adjoining_data); darray_geometries_init(allocator, &hole_array); darray_geometries_init(allocator, &glass_array); - scale[0] = sqrt(data->glass_ratio); - scale[1] = scale[0]; - scale[2] = scale[0]; + d3_splat(scale, sqrt(data->glass_ratio)); /* windows are build from the vertical faces of habitable cavities */ ERR(scad_geometry_boundary(NULL, &data_cad->habitable_cavity, 1, &bcavity)); ERR(scad_geometry_explode(bcavity, NULL, &list, &list_n)); for (i = 0; i < list_n; i++) { - double center[3]; + double hsz, center[3]; size_t center_n; size_t count; ERR(scad_geometry_get_count(list[i], &center_n)); ASSERT(center_n == 1); ERR(scad_geometry_get_centerofmass(list[i], center)); - ERR(scad_geometry_normal(list[i], center, N, NULL, &surface)); if (N[2] != 0) { - ERR(scad_geometry_delete(surface)); + ERR(scad_geometry_ref_put(surface)); surface = NULL; continue; /* keep only vertical face */ } ERR(scad_geometry_dilate(surface, center, scale)); - dir[0] = 1.1*N[0] * (data->wall_thickness - + data->internal_insulation_thickness + data->external_insulation_thickness); - dir[1] = 1.1*N[1] * (data->wall_thickness - + data->internal_insulation_thickness + data->external_insulation_thickness); - dir[2] = 1.1*N[2] * (data->wall_thickness - + data->internal_insulation_thickness + data->external_insulation_thickness); + /* Use the same distance used in early stages for close neighbor detection */ + hsz = CG2_CLOSE_NEIGHBOR_DISTANCE + data->wall_thickness + + data->internal_insulation_thickness + data->external_insulation_thickness; + d3_muld(dir, N, hsz); ERR(scad_geometry_extrude(surface, NULL, dir, &hole)); - /* check if hole intersect adjoining_cad */ - /* push only if no intersect */ - if (adjoining_cad) { - ERR(scad_intersect_geometries(NULL, &hole, 1, adjoining_cad, adjoining_n, + /* Check if hole intersects adjoining envelops */ + /* Push only if don't intersect */ + if (adjoining_n) { + adj_list = MEM_REALLOC(allocator, adj_list, + adjoining_n * sizeof(struct scad_geometry*)); + for(j=0; j<adjoining_n; j++) adj_list[j] = adj[j].envelop; + ERR(scad_intersect_geometries(NULL, &hole, 1, adj_list, adjoining_n, &hole_adjoining_intersect)); ERR(scad_geometry_get_count(hole_adjoining_intersect, &count)); + ERR(scad_geometry_ref_put(hole_adjoining_intersect)); + hole_adjoining_intersect = NULL; } else { count = 0; } @@ -968,14 +968,14 @@ build_windows dir[2] = N[2] * 0.024; ERR(scad_geometry_extrude(surface, NULL, dir, &glass)); ERR(darray_geometries_push_back(&glass_array, &glass)); + ERR(scad_geometry_ref_put(glass)); + glass = NULL; } else { removed_windows++; - ERR(scad_geometry_delete(hole)); - hole = NULL; - ERR(scad_geometry_delete(hole_adjoining_intersect)); - hole_adjoining_intersect = NULL; } - ERR(scad_geometry_delete(surface)); + ERR(scad_geometry_ref_put(hole)); + hole = NULL; + ERR(scad_geometry_ref_put(surface)); surface = NULL; } ASSERT(darray_geometries_size_get(&hole_array) @@ -997,7 +997,7 @@ build_windows ERR(scad_cut_geometries(NULL, &data_cad->wall, 1, hole_list, array_n, &geom)); ERR(scad_geometry_swap_names(data_cad->wall, geom)); - ERR(scad_geometry_delete(data_cad->wall)); + ERR(scad_geometry_ref_put(data_cad->wall)); data_cad->wall = geom; geom = NULL; @@ -1006,7 +1006,7 @@ build_windows ERR(scad_cut_geometries(NULL, &data_cad->internal_insulation, 1, hole_list, array_n, &geom)); ERR(scad_geometry_swap_names(data_cad->internal_insulation, geom)); - ERR(scad_geometry_delete(data_cad->internal_insulation)); + ERR(scad_geometry_ref_put(data_cad->internal_insulation)); data_cad->internal_insulation = geom; geom = NULL; } @@ -1016,7 +1016,7 @@ build_windows ERR(scad_cut_geometries(NULL, &data_cad->external_insulation, 1, hole_list, array_n, &geom)); ERR(scad_geometry_swap_names(data_cad->external_insulation, geom)); - ERR(scad_geometry_delete(data_cad->external_insulation)); + ERR(scad_geometry_ref_put(data_cad->external_insulation)); data_cad->external_insulation = geom; geom = NULL; } @@ -1034,25 +1034,18 @@ build_windows } exit: - glass_list = darray_geometries_data_get(&glass_array); for (i = 0 ; i < list_n; i++) { - SCAD(geometry_delete(list[i])); - } - for (i = 0 ; i < darray_geometries_size_get(&hole_array); i++) { - struct scad_geometry* h = darray_geometries_data_get(&hole_array)[i]; - SCAD(geometry_delete(h)); - } - for (i = 0 ; i <darray_geometries_size_get(&glass_array); i++) { - struct scad_geometry* g = darray_geometries_data_get(&glass_array)[i]; - SCAD(geometry_delete(g)); + if(list[i]) SCAD(geometry_ref_put(list[i])); } darray_geometries_release(&hole_array); darray_geometries_release(&glass_array); - if (surface) SCAD(geometry_delete(surface)); - if (geom) SCAD(geometry_delete(geom)); - if (bcavity) SCAD(geometry_delete(bcavity)); - if (hole_adjoining_intersect) SCAD(geometry_delete(hole_adjoining_intersect)); + if (surface) SCAD(geometry_ref_put(surface)); + if (glass) SCAD(geometry_ref_put(glass)); + if (geom) SCAD(geometry_ref_put(geom)); + if (bcavity) SCAD(geometry_ref_put(bcavity)); + if (hole_adjoining_intersect) SCAD(geometry_ref_put(hole_adjoining_intersect)); MEM_RM(allocator, list); + MEM_RM(allocator, adj_list); if (is_init) str_release(&gname); return res; error: @@ -1065,32 +1058,34 @@ build_boundary struct mem_allocator* allocator, const char* prefix, struct data_cad_cmode_1* data_cad, - struct scad_geometry** adjoining_cad, - size_t adjoining_n, + struct darray_adjoining_data* adjoining_data, struct darray_geometries* boundary) { res_T res = RES_OK; - size_t count; struct darray_geometries array; struct scad_geometry** list = NULL; - struct darray_geometries bound_array; struct scad_geometry* bound = NULL; char* boundaryname = NULL; struct str name; int is_init = 0; - size_t i = 0; + struct adjoining_data* adj; + size_t adjoining_n, i = 0, count = 0; (void)scpr; - ASSERT(allocator && prefix && data_cad && boundary); + ASSERT(allocator && prefix && data_cad && adjoining_data && boundary); + adjoining_n = darray_adjoining_data_size_get(adjoining_data); + adj = darray_adjoining_data_data_get(adjoining_data); darray_geometries_init(allocator, &array); - darray_geometries_init(allocator, &bound_array); - str_init(allocator, &name); is_init = 1; /* Ensure enough room for all geometries without error nor mem move */ ERR(darray_geometries_reserve(&array, 14 + adjoining_n)); + /* Using wall here to compute boundary_wall is OK even if it doesn't take care + * of conformity wrt the adjoining building. The reason is that the common + * part that could end to be not conformal is not part of the boundary. As a + * consequence it cannot be part of the result. */ ERR(darray_geometries_push_back(&array, &data_cad->wall)); ERR(darray_geometries_push_back(&array, &data_cad->roof)); ERR(darray_geometries_push_back(&array, &data_cad->floor)); @@ -1124,7 +1119,7 @@ build_boundary ERR(darray_geometries_push_back(&array, &data_cad->glass)); } for (i=0; i<adjoining_n; i++) { - ERR(darray_geometries_push_back(&array, adjoining_cad + i)); + ERR(darray_geometries_push_back(&array, &adj[i].envelop)); } count = darray_geometries_size_get(&array); @@ -1139,6 +1134,8 @@ build_boundary ERR(scad_geometries_common_boundaries(boundaryname, list, count, &data_cad->wall, 1, &bound)); ERR(darray_geometries_push_back(boundary, &bound)); + ERR(scad_geometry_ref_put(bound)); + bound = NULL; ERR(str_set(&name, prefix)); ERR(str_append(&name, "_B_roof")); @@ -1146,6 +1143,8 @@ build_boundary ERR(scad_geometries_common_boundaries(boundaryname, list, count, &data_cad->roof, 1, &bound)); ERR(darray_geometries_push_back(boundary, &bound)); + ERR(scad_geometry_ref_put(bound)); + bound = NULL; if (data_cad->glass) { ERR(str_set(&name, prefix)); @@ -1154,6 +1153,8 @@ build_boundary ERR(scad_geometries_common_boundaries(boundaryname, list, count, &data_cad->glass, 1, &bound)); ERR(darray_geometries_push_back(boundary, &bound)); + ERR(scad_geometry_ref_put(bound)); + bound = NULL; } if (data_cad->external_insulation) { @@ -1163,6 +1164,8 @@ build_boundary ERR(scad_geometries_common_boundaries(boundaryname, list, count, &data_cad->external_insulation, 1, &bound)); ERR(darray_geometries_push_back(boundary, &bound)); + ERR(scad_geometry_ref_put(bound)); + bound = NULL; } if (data_cad->internal_insulation) { @@ -1175,12 +1178,13 @@ build_boundary ERR(scad_geometry_get_count(bound, &bcount)); if (bcount > 0) { ERR(darray_geometries_push_back(boundary, &bound)); - } else { - SCAD(geometry_delete(bound)); } + ERR(scad_geometry_ref_put(bound)); + bound = NULL; } exit: + if(bound) SCAD(geometry_ref_put(bound)); if (is_init) str_release(&name); darray_geometries_release(&array); return res; @@ -1217,9 +1221,9 @@ build_connection ERR(scad_geometry_get_count(connect, &count)); \ if (count > 0) { \ ERR(darray_geometries_push_back(connection, &connect)); \ - } else { \ - SCAD(geometry_delete(connect)); \ - } + } \ + ERR(scad_geometry_ref_put(connect)); \ + connect = NULL; /* -------------------------------------------------------------------------*/ /* habitable cavity connections */ @@ -1370,8 +1374,8 @@ build_fake_ground exit: if (pg_offset) SCPR(polygon_ref_put(pg_offset)); darray_geometries_release(&array); - if (footprint) SCAD(geometry_delete(footprint)); - if (geom) SCAD(geometry_delete(geom)); + if (footprint) SCAD(geometry_ref_put(footprint)); + if (geom) SCAD(geometry_ref_put(geom)); return res; error: goto exit; @@ -1436,8 +1440,8 @@ building_ground_connection exit: darray_geometries_release(&array); if (is_init) str_release(&name); - if (list_boundary) SCAD(geometry_delete(list_boundary)); - if (footprint) SCAD(geometry_delete(footprint)); + if (list_boundary) SCAD(geometry_ref_put(list_boundary)); + if (footprint) SCAD(geometry_ref_put(footprint)); return res; error: goto exit; @@ -1449,11 +1453,8 @@ error: res_T init_cmode_1 - (struct scpr_device* scpr, - struct mem_allocator* allocator, - struct logger* logger, - struct building* building, - int keep_running_on_error, + (struct building* building, + struct city* city, struct parsed_city_building* parsed_data, struct catalog* catalog, const double lower[2], @@ -1462,8 +1463,7 @@ init_cmode_1 res_T res = RES_OK; struct str dataset_name; int name_initialized = 0; - static struct construction_mode_functors functors_1 - = { + static struct construction_mode_functors functors_1 = { &init_cmode_1, &release_cmode_1, &build_cad_cmode_1, @@ -1472,20 +1472,21 @@ init_cmode_1 &export_stl_cmode_1, &release_cad_cmode_1 }; + struct mem_allocator* allocator; + struct logger* logger; (void) parsed_data; - if(!scpr || !allocator || !logger || !building || !parsed_data || !catalog - || !lower || !upper) - { + if(!building || !city || !parsed_data || !catalog || !lower || !upper) { res = RES_BAD_ARG; goto error; } + allocator = city->allocator; + logger = city->logger; building->construction_mode = mode_1; building->functors = &functors_1; - ERR(init_building_base(scpr, allocator, logger, building, - keep_running_on_error, parsed_data, lower, upper)); + ERR(init_building_base(building, city, parsed_data, lower, upper)); str_init(allocator, &dataset_name); name_initialized = 1; @@ -1509,78 +1510,66 @@ error: res_T release_cmode_1 - (struct mem_allocator* allocator, - struct logger* logger, - struct building* building) + (struct building* building) { - res_T res = RES_OK; - - if(!allocator || !logger || !building) { - res = RES_BAD_ARG; - goto error; - } - - ERR(release_building_base(allocator, logger, building)); - -exit: - return res; -error: - goto exit; + if(!building) return RES_BAD_ARG; + return release_building_base(building); } - res_T build_cad_cmode_1 - (struct scpr_device* scpr, - struct mem_allocator* allocator, - struct logger* logger, - struct building* building, + (struct building* building, int dump_footprints_on_error, int keep_running_on_errors, + struct darray_adjoining_data* adjoining_data, void** cad) { res_T res = RES_OK; double height = building->height; double depth = 0; - struct scpr_polygon* pg = NULL; struct dataset_cmode_1* data = (struct dataset_cmode_1 *)building->data; struct data_cad_cmode_1* data_cad = NULL; const char* name; struct scpr_intersector* overlapping_intersector = NULL; struct scpr_intersector_check_callbacks callbacks = SCPR_INTERSECTOR_CHECK_CALLBACKS_NULL__; - struct scad_geometry** adjoining_cad = NULL; - size_t i = 0; size_t adjoining_n = 0; struct callback_ctx ctx; - int error_occured = 0; + int error_occured = 0, error_msg_printed = 0;; struct htable_polygons polygons; int polygons_initialized = 0; double zero = 0; + struct scpr_device* scpr; + struct mem_allocator* allocator; + struct logger* logger; - if (!building || !allocator || !cad) { + if (!building || !cad || !adjoining_data) { res = RES_BAD_ARG; goto error; } - adjoining_n = htable_building_size_get(&building->close_buildings); + scpr = building->city->scpr; + allocator = building->city->allocator; + logger = building->city->logger; data_cad = MEM_CALLOC(allocator, 1, sizeof(struct data_cad_cmode_1)); if(!data_cad) { res = RES_MEM_ERR; goto error; } + data_cad->allocator = allocator; darray_geometries_init(allocator, &data_cad->boundary); darray_geometries_init(allocator, &data_cad->connection); + darray_common_trg_init(allocator, &data_cad->common_trg); + darray_geometries_init(allocator, &data_cad->adj_walls); htable_polygons_init(allocator, &polygons); polygons_initialized = 1; ERR(scpr_intersector_create(scpr, &overlapping_intersector)); - - /* Register the original polygon with offset 0; as polygons in the htable are - * ref_put, register a copy! */ - ERR(scpr_polygon_create_copy(scpr, building->pg, &pg)); - ERR(scpr_intersector_register_polygon(overlapping_intersector, pg)); - ERR(htable_polygons_set(&polygons, &zero, &pg)); + ERR(scpr_intersector_register_polygon(overlapping_intersector, building->pg)); + /* An htable to associate offset polygons to offsets. Not really for + * performance, but to avoid considering the same polygon more than once when + * checking for polygon intersections (that would be an error). */ + ERR(htable_polygons_set(&polygons, &zero, &building->pg)); /* build mandatories elements : - floor @@ -1589,13 +1578,14 @@ build_cad_cmode_1 */ name = str_cget(&building->name); - ERR(build_floor(scpr, allocator, name, pg, data, overlapping_intersector, + ERR(build_floor(scpr, allocator, logger, &error_msg_printed, + keep_running_on_errors, name, building->pg, data, overlapping_intersector, &polygons, &data_cad->floor)); - ERR(build_wall(scpr, allocator, name, "S_walls", pg, height, data, + ERR(build_wall(scpr, allocator, name, "S_walls", building->pg, height, data, overlapping_intersector, &polygons, &data_cad->wall)); - ERR(build_roof(scpr, allocator, name, pg, height, data, + ERR(build_roof(scpr, allocator, name, building->pg, height, data, overlapping_intersector, &polygons, &data_cad->roof)); /* build optionnal elements : @@ -1609,33 +1599,33 @@ build_cad_cmode_1 if (data->foundation_depth > 0) { depth = -data->foundation_depth; - ERR(build_wall(scpr, allocator, name, "S_foundation", pg, depth, data, + ERR(build_wall(scpr, allocator, name, "S_foundation", building->pg, depth, data, overlapping_intersector, &polygons, &data_cad->foundation)); } if (data->inter_floor_count > 0) { - ERR(build_inter_floor(scpr, allocator, name, pg, height, data, + ERR(build_inter_floor(scpr, allocator, name, building->pg, height, data, overlapping_intersector, &polygons, &data_cad->intermediate_floor)); } if (data->external_insulation_thickness> 0) { - ERR(build_ext_insulation(scpr, allocator, name, pg, height, data, + ERR(build_ext_insulation(scpr, allocator, name, building->pg, height, data, overlapping_intersector, &polygons, &data_cad->external_insulation)); } if (data->internal_insulation_thickness> 0) { - ERR(build_int_insulation(scpr, allocator, name, pg, height, data, + ERR(build_int_insulation(scpr, allocator, name, building->pg, height, data, data_cad->intermediate_floor, overlapping_intersector, &polygons, &data_cad->internal_insulation)); } if (data->roof_insulation_thickness > 0) { - ERR(build_roof_insulation(scpr, allocator, name, pg, height, data, + ERR(build_roof_insulation(scpr, allocator, name, building->pg, height, data, overlapping_intersector, &polygons, &data_cad->roof_insulation)); } if (data->floor_insulation_thickness > 0) { - ERR(build_floor_insulation(scpr, allocator, name, pg, data, + ERR(build_floor_insulation(scpr, allocator, name, building->pg, data, overlapping_intersector, &polygons, &data_cad->floor_insulation)); } @@ -1646,56 +1636,107 @@ build_cad_cmode_1 */ if (data->attic_height > 0) { - ERR(build_attic(scpr, allocator, name, pg, height, data, + ERR(build_attic(scpr, allocator, name, building->pg, height, data, overlapping_intersector, &polygons, &data_cad->attic_cavity)); } - ERR(build_habitable(scpr, allocator, name, pg, height, data, + ERR(build_habitable(scpr, allocator, name, building->pg, height, data, data_cad->intermediate_floor, overlapping_intersector, &polygons, &data_cad->habitable_cavity)); if (data->crawl_height > 0) { - ERR(build_crawlspace(scpr, allocator, name, pg, data, + ERR(build_crawlspace(scpr, allocator, name, building->pg, data, overlapping_intersector, &polygons, &data_cad->crawlspace_cavity)); } - /* Check for polygons overlapping */ - ctx.allocator = allocator; - ctx.logger = logger; + /* Check for registered polygons overlapping */ + ctx.city = building->city; ctx.buildings = building; ctx.buildings_count = 1; ctx.intersection_found = &error_occured; - /* build adjoining envelop */ - if (adjoining_n > 0) { - ERR(build_adjoining(allocator, logger, building, &adjoining_cad)); - } - ctx.search_type = OVERLAPPING_PROXIMITY; ctx.dump_footprints_on_error = dump_footprints_on_error; ctx.keep_running_on_errors = keep_running_on_errors; callbacks.simple_intersection = simple_intersection; ERR(scpr_intersector_check(overlapping_intersector, &callbacks, &ctx)); + if(error_occured) { + logger_print(logger, LOG_ERROR, + "Internal error building CAD for building '%s'.\n", + str_cget(&building->name)); + ERR(darray_pbuilding_push_back(&building->city->removed_buildings, &building)); + error_msg_printed = 1; + res = RES_BAD_ARG; + goto error; + } + + /* build adjoining envelop */ + adjoining_n = htable_building_size_get(&building->close_buildings); + if (adjoining_n > 0) { + ERR(build_adjoining(building, adjoining_data)); + ASSERT(adjoining_n == darray_adjoining_data_size_get(adjoining_data)); + } /* windows */ if (data->glass_ratio > 0) { - ERR(build_windows(scpr, allocator, logger, name, data, data_cad, - adjoining_cad, adjoining_n)); + ERR(build_windows(scpr, allocator, logger, name, data, data_cad, adjoining_data)); } /* fake ground */ depth = MMAX(data->foundation_depth, data->floor_thickness + data->floor_insulation_thickness + data->crawl_height); - ERR(build_fake_ground(scpr, allocator, data_cad, pg, depth, &data_cad->fake_ground)); + ERR(build_fake_ground(scpr, allocator, data_cad, building->pg, depth, + &data_cad->fake_ground)); ERR(scad_scene_partition()); + /* After partitioning, manage common parts with other buildings */ + if(adjoining_n > 0) { + size_t a, c; + struct b_pair pair; + struct darray_double* common_trg; + struct adjoining_data* adjoining + = darray_adjoining_data_data_get(adjoining_data); + struct scad_geometry* extern_most = data_cad->external_insulation + ? data_cad->external_insulation : data_cad->wall; + for(a = 0; a < adjoining_n; a++) { + struct adjoining_data* adj = adjoining + a; + ERR(scad_geometries_common_boundaries(NULL, &extern_most, 1, + &adj->envelop, 1, &adj->common_geometry)); + ERR(scad_geometry_get_count(adj->common_geometry, &c)); + if(c == 0) { + /* Not really adjoining */ + logger_print(logger, LOG_OUTPUT, + "building '%s': neighbor '%s' not really adjoining.\n", + str_cget(&building->name), + str_cget(&adj->adjoining_building->name)); + continue; + } + make_b_pair(&pair, building, adj->adjoining_building); + common_trg + = htable_common_triangles_find(&building->city->common_triangles, &pair); + if(common_trg) { + /* The common geometry has already been processed when creating a + * previous building, and the very same mesh must be used for this one. + * Keep track of the geometry to replace and the mesh to output instead */ + ERR(darray_geometries_push_back(&data_cad->adj_walls, + &adj->common_geometry)); + ERR(darray_common_trg_push_back(&data_cad->common_trg, &common_trg)); + } else { + /* The mesh doesn't exist yet and won't be created until a further step. + * We need to store the geometry id so that the mesh can be stored when + * created. */ + adjoining[a].save = 1; + } + } + } + /* build ground/building connection */ ERR(building_ground_connection(scpr, allocator, name, data_cad, &data_cad->ground_connection)); /* build boundaries */ - ERR(build_boundary(scpr, allocator, name, data_cad, adjoining_cad, adjoining_n, + ERR(build_boundary(scpr, allocator, name, data_cad, adjoining_data, &data_cad->boundary)); /* build connections */ @@ -1704,28 +1745,25 @@ build_cad_cmode_1 exit: if(polygons_initialized) htable_polygons_release(&polygons); if(overlapping_intersector) SCPR(intersector_ref_put(overlapping_intersector)); - *(struct data_cad_cmode_1**)cad = data_cad; - for (i=0; i<adjoining_n; i++) { - if (adjoining_cad[i]) scad_geometry_delete(adjoining_cad[i]); - } - if (adjoining_cad) MEM_RM(allocator, adjoining_cad); + if(cad) *(struct data_cad_cmode_1**)cad = data_cad; return res; error: - if(data_cad) CHK(RES_OK == release_cad_cmode_1(allocator, logger, data_cad)); + if(building && !error_msg_printed) { + logger_print(logger, LOG_ERROR, + "Unknown error building CAD for building '%s'.\n", + str_cget(&building->name)); + } + if(data_cad) CHK(RES_OK == release_cad_cmode_1(data_cad)); data_cad = NULL; goto exit; } res_T build_footprint_cmode_1 - (struct scpr_device* scpr, - struct mem_allocator* allocator, - struct logger* logger, - struct building* building, + (struct building* building, struct scad_geometry** footprint) { res_T res = RES_OK; - (void)scpr; (void)allocator; (void)logger; if (!building || !footprint) { res = RES_BAD_ARG; @@ -1742,9 +1780,7 @@ error: res_T build_envelop_cmode_1 - (struct mem_allocator* allocator, - struct logger* logger, - struct building* building, + (struct building* building, struct scad_geometry** envelop) { res_T res = RES_OK; @@ -1754,8 +1790,6 @@ build_envelop_cmode_1 struct scpr_polygon* pg = building->pg; struct scad_geometry* footprint = NULL; - (void)allocator; (void)logger; - ERR(scpr_polygon_get_vertices_count(pg, 0, &nverts)); ERR(scad_add_polygon(NULL, get_position_pg, pg, 0, nverts, &footprint)); @@ -1764,7 +1798,7 @@ build_envelop_cmode_1 ERR(scad_geometry_extrude(footprint, NULL, d, envelop)); exit: - if (footprint) scad_geometry_delete(footprint); + if (footprint) SCAD(geometry_ref_put(footprint)); return res; error: goto exit; @@ -1772,15 +1806,12 @@ error: res_T export_stl_cmode_1 - (struct mem_allocator* allocator, - struct logger* logger, - void* cad, + (void* cad, const int binary) { res_T res = RES_OK; struct data_cad_cmode_1* data_cad = (struct data_cad_cmode_1 *)cad; size_t i = 0; - (void)allocator; (void)logger; if (!cad) { res = RES_BAD_ARG; @@ -1788,76 +1819,77 @@ export_stl_cmode_1 } /* floor export */ - ERR(scad_stl_export(data_cad->floor, NULL, 0, binary)); + ERR(scad_stl_export(data_cad->floor, NULL, binary)); /* wall export */ - ERR(scad_stl_export(data_cad->wall, NULL, 0, binary)); + ERR(scad_stl_export(data_cad->wall, NULL, binary)); /* roof export */ - ERR(scad_stl_export(data_cad->roof, NULL, 0, binary)); + ERR(scad_stl_export(data_cad->roof, NULL, binary)); /* foundation export */ if (data_cad->foundation) { - ERR(scad_stl_export(data_cad->foundation, NULL, 0, binary)); + ERR(scad_stl_export(data_cad->foundation, NULL, binary)); } /* glass export */ if (data_cad->glass) { - ERR(scad_stl_export(data_cad->glass, NULL, 0, binary)); + ERR(scad_stl_export(data_cad->glass, NULL, binary)); } /* intermediate floor export*/ if (data_cad->intermediate_floor) { - ERR(scad_stl_export(data_cad->intermediate_floor, NULL, 0, binary)); + ERR(scad_stl_export(data_cad->intermediate_floor, NULL, binary)); } /* internal insulation export*/ if (data_cad->internal_insulation) { - ERR(scad_stl_export(data_cad->internal_insulation, NULL, 0, binary)); + ERR(scad_stl_export(data_cad->internal_insulation, NULL, binary)); } /* external insulation export*/ if (data_cad->external_insulation) { - ERR(scad_stl_export(data_cad->external_insulation, NULL, 0, binary)); + ERR(scad_stl_export(data_cad->external_insulation, NULL, binary)); } /* roof insulation export*/ if (data_cad->roof_insulation) { - ERR(scad_stl_export(data_cad->roof_insulation, NULL, 0, binary)); + ERR(scad_stl_export(data_cad->roof_insulation, NULL, binary)); } /* floor insulation export*/ if (data_cad->floor_insulation) { - ERR(scad_stl_export(data_cad->floor_insulation, NULL, 0, binary)); + ERR(scad_stl_export(data_cad->floor_insulation, NULL, binary)); } /* attic cavity export*/ if (data_cad->attic_cavity) { - ERR(scad_stl_export(data_cad->attic_cavity, NULL, 0, binary)); + ERR(scad_stl_export(data_cad->attic_cavity, NULL, binary)); } /* habitable cavity export*/ - ERR(scad_stl_export(data_cad->habitable_cavity, NULL, 0, binary)); + ERR(scad_stl_export(data_cad->habitable_cavity, NULL, binary)); /* crawlspace cavity export*/ if (data_cad->crawlspace_cavity) { - ERR(scad_stl_export(data_cad->crawlspace_cavity, NULL, 0, binary)); + ERR(scad_stl_export(data_cad->crawlspace_cavity, NULL, binary)); } /* boundary export*/ for(i = 0; i < darray_geometries_size_get(&data_cad->boundary); i++) { struct scad_geometry* b = darray_geometries_data_get(&data_cad->boundary)[i]; - ERR(scad_stl_export(b, NULL, 0, binary)); + ERR(scad_stl_export(b, NULL, binary)); } /* connections export*/ for(i = 0; i < darray_geometries_size_get(&data_cad->connection); i++) { struct scad_geometry* c = darray_geometries_data_get(&data_cad->connection)[i]; - ERR(scad_stl_export(c, NULL, 0, binary)); + ERR(scad_stl_export(c, NULL, binary)); } /* ground/building connection export*/ - ERR(scad_stl_export(data_cad->ground_connection, NULL, 1, binary)); + ERR(scad_geometry_reverse(data_cad->ground_connection)); + ERR(scad_stl_export(data_cad->ground_connection, NULL, binary)); exit: return res; @@ -1867,22 +1899,25 @@ error: res_T release_cad_cmode_1 - (struct mem_allocator* allocator, - struct logger* logger, - void* cad) + (void* cad) { res_T res = RES_OK; struct data_cad_cmode_1* data_cad = (struct data_cad_cmode_1 *)cad; - size_t i; - (void)logger; + struct mem_allocator* allocator; - if (!allocator || !cad) { + if (!cad) { res = RES_BAD_ARG; goto error; } + allocator = data_cad->allocator; + darray_geometries_release(&data_cad->boundary); + darray_geometries_release(&data_cad->connection); + darray_common_trg_release(&data_cad->common_trg); + darray_geometries_release(&data_cad->adj_walls); + #define GDEL(Field) \ - if(data_cad->Field) SCAD(geometry_delete(data_cad->Field)); \ + if(data_cad->Field) SCAD(geometry_ref_put(data_cad->Field)); \ /* To ease debugging, write NULL after deletion */ \ data_cad->Field = NULL GDEL(attic_cavity); @@ -1900,18 +1935,8 @@ release_cad_cmode_1 GDEL(roof); GDEL(roof_insulation); GDEL(wall); - for(i = 0; i < darray_geometries_size_get(&data_cad->boundary); i++) { - struct scad_geometry* b = darray_geometries_data_get(&data_cad->boundary)[i]; - ERR(scad_geometry_delete(b)); - } - for(i = 0; i < darray_geometries_size_get(&data_cad->connection); i++) { - struct scad_geometry* c = darray_geometries_data_get(&data_cad->connection)[i]; - ERR(scad_geometry_delete(c)); - } - darray_geometries_release(&data_cad->boundary); - darray_geometries_release(&data_cad->connection); - MEM_RM(allocator, data_cad); #undef GDEL + MEM_RM(allocator, data_cad); exit: return res; diff --git a/src/cg_construction_mode_1.h b/src/cg_construction_mode_1.h @@ -20,6 +20,8 @@ #ifndef Construction_MODE_1_H #define Construction_MODE_1_H +#include "cg_construction_mode.h" + #include <rsys/rsys.h> #include <rsys/str.h> #include <rsys/hash_table.h> @@ -27,15 +29,9 @@ struct scad_geometry; struct building; -struct mem_allocator; -struct logger; +struct city; struct parsed_city_building; struct catalog; -struct scpr_device; - -#define DARRAY_NAME geometries -#define DARRAY_DATA struct scad_geometry* -#include <rsys/dynamic_array.h> /* specific data for construction mode 1 */ struct dataset_cmode_1 { @@ -67,6 +63,11 @@ struct dataset_cmode_1 { #include <rsys/hash_table.h> struct data_cad_cmode_1 { + struct mem_allocator* allocator; + struct darray_geometries boundary; + struct darray_geometries connection; + struct darray_common_trg common_trg; + struct darray_geometries adj_walls; struct scad_geometry* wall; struct scad_geometry* roof; struct scad_geometry* floor; @@ -82,18 +83,13 @@ struct data_cad_cmode_1 { struct scad_geometry* glass; struct scad_geometry* fake_ground;/*not exported, used for ground connection*/ struct scad_geometry* ground_connection; - struct darray_geometries boundary; - struct darray_geometries connection; size_t n_connection; }; res_T init_cmode_1 - (struct scpr_device* device, - struct mem_allocator* allocator, - struct logger* logger, - struct building* building, - int keep_running_on_error, + (struct building* building, + struct city* city, struct parsed_city_building* parsed_data, struct catalog* catalog, const double lower[2], @@ -101,46 +97,33 @@ init_cmode_1 res_T release_cmode_1 - (struct mem_allocator* allocator, - struct logger* logger, - struct building* building); + (struct building* building); res_T build_cad_cmode_1 - (struct scpr_device* scpr, - struct mem_allocator* allocator, - struct logger* logger, - struct building* building, + (struct building* building, int dump_footprints_on_error, int keep_running_on_errors, + struct darray_adjoining_data* adjoining_data, void** cad); res_T build_footprint_cmode_1 - (struct scpr_device* scpr, - struct mem_allocator* allocator, - struct logger* logger, - struct building* building, + (struct building* building, struct scad_geometry** footprint); res_T build_envelop_cmode_1 - (struct mem_allocator* allocator, - struct logger* logger, - struct building* building, + (struct building* building, struct scad_geometry** envelop); res_T export_stl_cmode_1 - (struct mem_allocator* allocator, - struct logger* logger, - void* cad, + (void* cad, const int binary); res_T release_cad_cmode_1 - (struct mem_allocator* allocator, - struct logger* logger, - void* cad); + (void* cad); #endif /* Construction_MODE_1_H */ diff --git a/src/cg_default.h.in b/src/cg_default.h.in @@ -21,5 +21,6 @@ #define CG2_ARGS_STL_DEFAULT_STR @CG2_ARGS_STL_DEFAULT_STR@ #define CG2_ARGS_STL_NON_DEFAULT_STR @CG2_ARGS_STL_NON_DEFAULT_STR@ #define CG2_ARGS_CHANGE_BINARY_DEFAULT_OPTION '@CG2_ARGS_CHANGE_BINARY_DEFAULT_OPTION@' +#define CG2_CLOSE_NEIGHBOR_DISTANCE @CG2_CLOSE_NEIGHBOR_DISTANCE@ #endif diff --git a/src/cg_ground.c b/src/cg_ground.c @@ -25,7 +25,7 @@ #include <star/scad.h> #define GDEL(Tgt) \ - if(Tgt) SCAD(geometry_delete(Tgt)); \ + if(Tgt) SCAD(geometry_ref_put(Tgt)); \ /* To avoid double delete, set to NULL after deletion */ \ Tgt = NULL @@ -45,7 +45,7 @@ ground_build_cad double origin[3]; double extent[3]; struct scad_geometry* bound = NULL; - struct scad_geometry** list = NULL; + struct scad_geometry** list; struct scad_geometry* surface = NULL; size_t i, count = 0; @@ -131,7 +131,7 @@ ground_export_stl(const struct ground* ground, const int binary) size_t i = 0; for(i = 0; i < 6; i++) { - ERR(scad_stl_export(ground->boundary[i], NULL, 0, binary)); + ERR(scad_stl_export(ground->boundary[i], NULL, binary)); } exit: diff --git a/src/cg_main.c b/src/cg_main.c @@ -93,7 +93,7 @@ int main struct parsed_city* parsed_city = NULL; struct parsed_catalog* parsed_catalog = NULL; struct catalog* catalog = NULL; - int logger_initialized = 0, allocator_initialized = 0; + int keep, logger_initialized = 0, allocator_initialized = 0; const struct cyaml_config config = { .log_fn = cg_log, .log_ctx = &logger, @@ -124,6 +124,7 @@ int main print_version(); goto exit; } + keep = args->keep_running_on_errors; /* Deactivate some logging according to the -V arg */ if(args->verbosity_level < 1) @@ -178,6 +179,9 @@ error: release_parsed_catalog(&config, parsed_catalog); release_parsed_city(&config, parsed_city); err = EXIT_FAILURE; - printf("City generator failed.\n"); + if(logger_initialized) { + logger_print(&logger, LOG_ERROR, "City generator failed.\n"); + if(!keep) logger_print(&logger, LOG_ERROR, "Try to re-run with option -k.\n"); + } goto exit; }