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 942645ffc51ca1b49ebff77b4cfd13e15a92f22c
parent 51b00620d8f6aae71415dff12f2ee916cdc68da6
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Fri,  3 Mar 2023 16:53:10 +0100

Use new star-cpr features to detect buildings neighbors

Used both for error detection and for adjoining construction

Diffstat:
Mcmake/CMakeLists.txt | 3++-
Msrc/cg.h | 8++++++++
Msrc/cg_args.c | 46+++++++++++++++++++++++++++++++++++-----------
Msrc/cg_args.h | 9++++++---
Msrc/cg_building.c | 50+++++++++++++++++++++++++++++++++-----------------
Msrc/cg_building.h | 34+++++++++++++++++++++++++++++-----
Msrc/cg_catalog_parsing.c | 8++++----
Msrc/cg_catalog_parsing.h | 6+++---
Msrc/cg_city.c | 376++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
Msrc/cg_city.h | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Asrc/cg_construction_mode.c | 123+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/cg_construction_mode.h | 56++++++++++++++++++++++++++++++++------------------------
Msrc/cg_construction_mode_0.c | 95++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Msrc/cg_construction_mode_0.h | 9+++++++++
Msrc/cg_construction_mode_1.c | 400+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Msrc/cg_construction_mode_1.h | 9+++++++++
Msrc/cg_ground.c | 1-
Msrc/cg_main.c | 12+++++++-----
18 files changed, 1041 insertions(+), 266 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -72,7 +72,7 @@ configure_file(${CG2_SOURCE_DIR}/cg_version.h.in find_package(RCMake 0.4.1 REQUIRED) find_package(RSys 0.12.1 REQUIRED) find_package(StarCAD 0.1 REQUIRED) -find_package(StarCPR 0.1.3 REQUIRED) +find_package(StarCPR 0.3 REQUIRED) find_package(libcyaml 1.3 REQUIRED) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${RCMAKE_SOURCE_DIR}) @@ -108,6 +108,7 @@ set(CG2_FILES_SRC cg_catalog_parsing.c cg_city.c cg_city_parsing.c + cg_construction_mode.c cg_construction_mode_0.c cg_construction_mode_1.c cg_ground.c diff --git a/src/cg.h b/src/cg.h @@ -25,6 +25,14 @@ #define ERR(Expr) if((res = (Expr)) != RES_OK) goto error; else (void)0 +/* + * Some constants used in city_generator + */ + +#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 log_prt_fn(const char* msg, void* ctx) { diff --git a/src/cg_args.c b/src/cg_args.c @@ -43,7 +43,7 @@ short_help(void) { print_version(); printf("\nUsage:\n" - "city_generator2 -m <FILENAME> { -c <FILENAME> }+ -[%c] [-s] [-V verbosity]\n" + "city_generator2 -m <FILENAME> { -c <FILENAME> }+ -[%c] [-s] [-V verbosity] [-k] [-f <NAME>] [-F]\n" "city_generator2 [-h]\n" "city_generator2 [-v]\n", CG2_ARGS_CHANGE_BINARY_DEFAULT_OPTION @@ -63,6 +63,14 @@ short_help(void) "-%c\n" " Set the format of output files to "STR(CG2_ARGS_STL_NON_DEFAULT_STR) " (default "STR(CG2_ARGS_STL_DEFAULT_STR)").\n" + "-k\n" + " Keep running on errors.\n" + "-f <NAME>.\n" + " Dump the footprint of the building with the given name.\n" + " Can be used more than once.\n" + "-F\n" + " Dump the footprint of any building not generated due to an error.\n" + " Process the whole file and exit if any error was found unless -k is used.\n" "-s\n" " Force single threaded execution. By default use as many threads as available.\n" "-v\n" @@ -96,32 +104,31 @@ parse_args int opt; int info_provided = 0, c_provided = 0, m_provided = 0; struct args* args; - char option_list[] = "?c:m:hvV:"; + char option_list[] = "?c:m:hkFf:vV:"; ASSERT(allocator && logger && argv && out_args); /* Patch option_list[] according to stl format default */ option_list[0] = CG2_ARGS_CHANGE_BINARY_DEFAULT_OPTION; - args = MEM_ALLOC(allocator, sizeof(*args)); + args = MEM_CALLOC(allocator, 1, sizeof(*args)); if(!args) { res = RES_MEM_ERR; goto error; } + args->allocator = allocator; args->logger = logger; + darray_names_init(allocator, &args->catalog_files); + darray_names_init(allocator, &args->dump_footprint_names); - /* Set default values */ - args->city_filename = NULL; - darray_catalog_filenames_init(allocator, &args->catalog_filenames); + /* Set non-zero default values */ args->binary_export = CG2_ARGS_BINARY_STL_DEFAULT; args->verbosity_level = CG2_ARGS_DEFAULT_VERBOSITY_LEVEL; - args->single_thread = 0; - args->print_help = 0; - args->print_version = 0; opterr = 0; /* No default error messages */ while((opt = getopt(argc, argv, option_list)) != -1) { + const char* name; switch (opt) { case '?': /* Unrecognized option */ @@ -141,7 +148,8 @@ parse_args case 'c': c_provided = 1; - ERR(darray_catalog_filenames_push_back(&args->catalog_filenames, &optarg)); + name = optarg; + ERR(darray_names_push_back(&args->catalog_files, &name)); break; case 'm': @@ -158,11 +166,26 @@ parse_args args->binary_export = !CG2_ARGS_BINARY_STL_DEFAULT; break; + /* Optional */ + + case 'F': + args->dump_footprints_on_error = 1; + break; + + case 'f': + name = optarg; + ERR(darray_names_push_back(&args->dump_footprint_names, &name)); + break; + case 'h': info_provided = 1; args->print_help = 1; break; + case 'k': + args->keep_running_on_errors = 1; + break; + case 's': info_provided = 1; args->single_thread = 1; @@ -228,6 +251,7 @@ release_args { if(!args) return; - darray_catalog_filenames_release(&args->catalog_filenames); + darray_names_release(&args->catalog_files); + darray_names_release(&args->dump_footprint_names); MEM_RM(args->allocator, args); } diff --git a/src/cg_args.h b/src/cg_args.h @@ -26,20 +26,23 @@ struct logger; struct mem_allocator; -#define DARRAY_NAME catalog_filenames -#define DARRAY_DATA char* +#define DARRAY_NAME names +#define DARRAY_DATA const char* #include <rsys/dynamic_array.h> struct args { struct mem_allocator* allocator; struct logger* logger; char* city_filename; - struct darray_catalog_filenames catalog_filenames; + struct darray_names catalog_files; + struct darray_names dump_footprint_names; int binary_export; int verbosity_level; int print_help; int print_version; int single_thread; + int keep_running_on_errors; + int dump_footprints_on_error; }; res_T diff --git a/src/cg_building.c b/src/cg_building.c @@ -18,47 +18,63 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "cg.h" +#include "cg_city.h" #include "cg_building.h" #include <rsys/stretchy_array.h> +#include <rsys/mem_allocator.h> +#include <rsys/logger.h> + #include <star/scad.h> -res_T +res_T build_adjoining (struct mem_allocator* allocator, struct logger* logger, - struct building** adjoining, - const size_t adjoining_n, + struct building* building, struct scad_geometry*** geom) { res_T res = RES_OK; - size_t i = 0; + size_t count, i = 0; struct scad_geometry** envelop_list = NULL; + struct htable_building_iterator it, end; + struct htable_building* close_buildings; - if (!adjoining || adjoining_n < 1 || !geom) { - res = RES_BAD_ARG; - goto error; - } + ASSERT(allocator && logger && building && geom); + + close_buildings = &building->close_buildings; + count = htable_building_size_get(close_buildings); - envelop_list = MEM_CALLOC(allocator, adjoining_n, sizeof(struct scad_geometry*)); + logger_print(logger, LOG_OUTPUT, + "building '%s' considering %zu close neighbor(s) when creating CAD.\n", + str_cget(&building->name), count); + + envelop_list = MEM_CALLOC(allocator, count, sizeof(struct scad_geometry*)); if(!envelop_list) { res = RES_MEM_ERR; goto error; } /* iterate over adjoining building */ - for (i=0; i<adjoining_n; i++) { - - ERR(adjoining[i]->functors->build_envelop(allocator, logger, - adjoining[i], envelop_list + i)); + htable_building_begin(close_buildings, &it); + htable_building_end(close_buildings, &end); + while(!htable_building_iterator_eq(&it, &end)) { + unsigned char flags = *htable_building_iterator_data_get(&it); + struct building* close_building; + ASSERT(flags & CLOSE_PROXIMITY); + close_building = *htable_building_iterator_key_get(&it); + ERR(close_building->functors->build_envelop(allocator, logger, close_building, + envelop_list + i++)); + htable_building_iterator_next(&it); } - + ASSERT(i == count); + exit: - if (envelop_list) *geom = envelop_list; + *geom = envelop_list; return res; error: - for (i=0; i<adjoining_n; i++) { - if (envelop_list[i]) scad_geometry_delete(envelop_list[i]); + 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; diff --git a/src/cg_building.h b/src/cg_building.h @@ -32,6 +32,24 @@ struct catalog; struct building; struct parsed_city_building; struct scpr_device; +struct building; + +/* An htable to uniquely associate flags to buildings */ +#define HTABLE_NAME building +#define HTABLE_KEY struct building* +#define HTABLE_DATA unsigned char +#include <rsys/hash_table.h> + +/* An enum to encode building events types. */ +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) +}; /* A type to store the functors of a construction mode */ struct construction_mode_functors { @@ -40,15 +58,22 @@ struct construction_mode_functors { struct mem_allocator* allocator, struct logger* logger, struct building* building, + int keep_running_on_error, 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); res_T (*build_cad) (struct scpr_device* scpr, struct mem_allocator* allocator, struct logger* logger, struct building* building, + int dump_footprints_on_error, + int keep_running_on_errors, void** cad); res_T (*build_footprint) (struct scpr_device* scpr, @@ -88,12 +113,12 @@ struct building { enum construction_mode_type construction_mode; /* generic construction mode data */ - int name_initialized; struct str name; double height; struct scpr_polygon* pg; - struct building** adjoining; - size_t adjoining_n; + struct htable_building close_buildings; /* links to other buildings */ + int structs_initialized; + unsigned int event_flags; /* specific data depending to the construction mode */ void* data; @@ -103,8 +128,7 @@ res_T build_adjoining (struct mem_allocator* allocator, struct logger* logger, - struct building** adjoining, - const size_t adjoining_n, + struct building* building, struct scad_geometry*** geom); #endif /* BUILDING_H */ diff --git a/src/cg_catalog_parsing.c b/src/cg_catalog_parsing.c @@ -45,7 +45,7 @@ get_schema_from_parsed_cmode res_T parse_catalog - (const struct darray_catalog_filenames* files_array, + (const struct darray_names* files_array, struct mem_allocator* allocator, struct logger* logger, const struct cyaml_config* config, @@ -57,7 +57,7 @@ parse_catalog struct parsed_catalog_items* items; struct parsed_catalog* parsed; struct parsed_cmode* parsed_cmode = NULL; - char* filename = NULL; + const char* filename = NULL; (void)logger; ASSERT(files_array && allocator && logger && out_parsed); @@ -71,7 +71,7 @@ parse_catalog parsed->allocator = allocator; parsed->logger = logger; - files_count = darray_catalog_filenames_size_get(files_array); + files_count = darray_names_size_get(files_array); darray_parsed_catalog_items_init(allocator, &parsed->catalog); ERR(darray_parsed_catalog_items_resize(&parsed->catalog, files_count)); @@ -80,7 +80,7 @@ parse_catalog const struct cyaml_schema_value* schema; /* Parse construction mode only */ - filename = darray_catalog_filenames_cdata_get(files_array)[i]; + filename = darray_names_cdata_get(files_array)[i]; err = cyaml_load_file(filename, config, &construction_mode_schema, (void**)&parsed_cmode, NULL); ERR(cyaml_err_to_res_T(err)); diff --git a/src/cg_catalog_parsing.h b/src/cg_catalog_parsing.h @@ -28,11 +28,11 @@ struct logger; struct mem_allocator; struct cyaml_config; -struct darray_catalog_filenames; +struct darray_names; struct parsed_catalog_items { enum parsed_cmode_type construction_mode; - char* filename; + const char* filename; void* parsed_data; }; @@ -48,7 +48,7 @@ struct parsed_catalog { res_T parse_catalog - (const struct darray_catalog_filenames* files, + (const struct darray_names* files, struct mem_allocator* allocator, struct logger* logger, const struct cyaml_config* config, diff --git a/src/cg_city.c b/src/cg_city.c @@ -33,21 +33,169 @@ #include <rsys/double4.h> #include <rsys/hash_table.h> +#include <rsys/str.h> #include <star/scpr.h> #include <string.h> -#define HTABLE_NAME names -#define HTABLE_DATA char -#define HTABLE_KEY struct str -#define HTABLE_KEY_FUNCTOR_INIT str_init -#define HTABLE_KEY_FUNCTOR_RELEASE str_release -#define HTABLE_KEY_FUNCTOR_COPY str_copy -#define HTABLE_KEY_FUNCTOR_COPY_AND_RELEASE str_copy_and_release -#define HTABLE_KEY_FUNCTOR_COPY_AND_CLEAR str_copy_and_clear -#define HTABLE_KEY_FUNCTOR_EQ str_eq -#define HTABLE_KEY_FUNCTOR_HASH str_hash -#include <rsys/hash_table.h> +res_T +dump_obj + (struct mem_allocator* allocator, + struct building* building) +{ + res_T res = RES_OK; + FILE* stream = NULL; + struct str filename; + + ASSERT(allocator && building); + + /* No need to dump twice */ + if(building->event_flags & BUILDING_DUMPED_TO_OBJ) return res; + + str_init(allocator, &filename); + 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)); + building->event_flags |= BUILDING_DUMPED_TO_OBJ; +exit: + if(stream) fclose(stream); + str_release(&filename); + return res; +error: + goto exit; +} + +#define STORE_CLOSE_INFO(Building1, Building2, Proximity) {\ + unsigned char *ptr__, tmp__; \ + ptr__ = htable_building_find(&(Building1)->close_buildings, &(Building2)); \ + tmp__ = (unsigned char)(ptr__ ? ((Proximity) | *ptr__) : (Proximity)); \ + ERR(htable_building_set(&(Building1)->close_buildings, &(Building2), &tmp__)); \ +} + +int collinear_segments + (struct scpr_callback_segment* segment1, + struct scpr_callback_segment* segment2, + void* ctx__) +{ + res_T res = RES_OK; + size_t i; + struct callback_ctx* ctx = (struct callback_ctx*)ctx__; + const char *name1 = NULL, *name2 = NULL; + struct building *building1 = NULL, *building2 = NULL; + + ASSERT(segment1 && segment2 && ctx); + + /* Search polygons in the city (slow, but OK) */ + 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) { + building1 = building; + name1 = str_cget(&building->name); + building1->event_flags |= flag; + } else if(building->pg == segment2->polygon) { + building2 = building; + name2 = str_cget(&building->name); + building2->event_flags |= flag; + } + } + CHK(name1 && name2); + + switch(ctx->search_type) { + case OVERLAPPING_PROXIMITY: + /* 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", + name1, name2); + break; + case CLOSE_PROXIMITY: + /* No action required */ + break; + default: FATAL("Invalid type."); + } + + return 0; +error: + /* True errors are not recoverable */ + return 1; +} + +int simple_intersection + (struct scpr_callback_segment* segment1, + struct scpr_callback_segment* segment2, + void* ctx__) +{ + res_T res = RES_OK; + size_t i; + struct callback_ctx* ctx = (struct callback_ctx*)ctx__; + const char *name1 = NULL, *name2 = NULL; + struct building *building1 = NULL, *building2 = NULL; + enum building_event flag = ctx->search_type == OVERLAPPING_PROXIMITY + ? BUILDING_WITH_OVERLAPPING : BUILDING_WITH_CLOSE_NEIGHBOR; + + ASSERT(segment1 && segment2 && ctx); + + 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) { + building1 = building; + name1 = str_cget(&building->name); + building1->event_flags |= flag; + } else if(building->pg == segment2->polygon) { + building2 = building; + name2 = str_cget(&building->name); + building2->event_flags |= flag; + } + } + CHK(name1 && name2); + + 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); + /* Dump the polygons in OBJ files */ + ERR(dump_obj(ctx->allocator, building1)); + ERR(dump_obj(ctx->allocator, building2)); + break; + case CLOSE_PROXIMITY: + logger_print(ctx->logger, LOG_OUTPUT, + "Buildings '%s' and '%s' are in close proximity.\n", + name1, name2); + break; + default: FATAL("Invalid type."); + } + + /* store other polygon on building information */ + 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 0; + return 1; +error: + /* True errors are not recoverable */ + return 1; +} +#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 @@ -64,10 +212,20 @@ create_city struct htable_names names; int initialized = 0; struct scpr_device_create_args scpr_args = SCPR_DEVICE_CREATE_ARGS_DEFAULT; - (void)logger; + struct scpr_intersector* overlapping_intersector = NULL; + struct scpr_intersector* close_intersector = NULL; + struct scpr_intersector_check_callbacks callbacks + = SCPR_INTERSECTOR_CHECK_CALLBACKS_NULL__; + struct callback_ctx ctx; + struct scpr_polygon* tmp_polygon = NULL; + struct str name; + int error_occured = 0; + struct building* building = NULL; ASSERT(logger && allocator && args && parsed_city && catalog && out_city); + str_init(allocator, &name); + city = MEM_CALLOC(allocator, 1, sizeof(*city)); if(!city) { res = RES_MEM_ERR; @@ -77,8 +235,8 @@ create_city htable_names_init(allocator, &names); initialized = 1; - city->buildings_count = parsed_city->city_building_list_count; - city->buildings = MEM_CALLOC(allocator, city->buildings_count, + city->allocated_buildings_count = parsed_city->city_building_list_count; + city->buildings = MEM_CALLOC(allocator, city->allocated_buildings_count, sizeof(*city->buildings)); if(!city->buildings) { res = RES_MEM_ERR; @@ -90,9 +248,20 @@ create_city scpr_args.allocator = allocator; scpr_args.logger = logger; scpr_args.verbosity_level = args->verbosity_level; + scpr_args.precision = CLIPPER_PRECISON; ERR(scpr_device_create(&scpr_args, &city->scpr)); city->verbosisty_level = args->verbosity_level; city->binary_export = args->binary_export; + 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; + 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]; + ERR(str_set(&name, pname)); + ERR(htable_names_set(&city->dump_footprint_names, &name, &one)); + } city->ground_depth = parsed_city->ground.depth; city->lower[0] = parsed_city->ground.extent[0]; @@ -100,63 +269,122 @@ create_city city->upper[0] = parsed_city->ground.extent[1]; city->upper[1] = parsed_city->ground.extent[3]; + ERR(scpr_intersector_create(city->scpr, &overlapping_intersector)); + ERR(scpr_intersector_create(city->scpr, &close_intersector)); /* create buildings depending on their construction modes */ - for (i = 0; i < city->buildings_count ; i++) { + for (i = 0; i < city->allocated_buildings_count ; i++) { struct parsed_city_building* parsed_data = parsed_city->city_building_list + i; - struct building* building = city->buildings + i; + building = city->buildings + i; char one = 1; + res_T res_tmp = RES_OK; + city->initialized_buildings_count++; switch(parsed_data->cmode_type) { case PARSED_CMODE_0: - ERR(init_cmode_0(city->scpr, allocator, logger, building, parsed_data, - catalog, city->lower, city->upper)); + ERRtmp(init_cmode_0(city->scpr, allocator, logger, building, + city->keep_running_on_errors, parsed_data, catalog, + city->lower, city->upper)); break; case PARSED_CMODE_1: - ERR(init_cmode_1(city->scpr, allocator, logger, building, parsed_data, - catalog, city->lower, city->upper)); + ERRtmp(init_cmode_1(city->scpr, allocator, logger, building, + city->keep_running_on_errors, parsed_data, catalog, + city->lower, city->upper)); break; - default: - res = RES_BAD_ARG; - goto error; + default: FATAL("Unknown construction mode"); } - /* Check name unicity. The error case is Not supposed to happen: can be + /* 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)) { logger_print(logger, LOG_ERROR, "Duplicate building name: '%s' in file '%s'.\n", - parsed_data->name, str_cget(&parsed_city->filename)); + str_cget(&building->name), str_cget(&parsed_city->filename)); res = RES_BAD_ARG; 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, + SCPR_JOIN_MITER)); + ERR(scpr_intersector_register_polygon(close_intersector, tmp_polygon)); + ERR(scpr_polygon_ref_put(tmp_polygon)); + tmp_polygon = NULL; + } + building = NULL; + ASSERT(city->initialized_buildings_count == city->allocated_buildings_count); + + /* Check for polygons overlapping and proximity */ + ctx.allocator = allocator; + ctx.logger = logger; + ctx.buildings = city->buildings; + ctx.buildings_count = city->initialized_buildings_count; + ctx.intersection_found = &error_occured; + ctx.search_type = OVERLAPPING_PROXIMITY; + ctx.dump_footprints_on_error = city->dump_footprints_on_error; + ctx.keep_running_on_errors = city->keep_running_on_errors; + callbacks.simple_intersection = simple_intersection; + callbacks.collinear_segments = collinear_segments; + 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; } exit: + str_release(&name); + 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); *out_city = city; return res; error: - release_city(city); + if(building) { + logger_print(logger, LOG_ERROR, + "Error creating building '%s'.\n", str_cget(&building->name)); + } else { + logger_print(logger, LOG_ERROR, "Error creating city.\n"); + } + CHK(RES_OK == release_city(city)); city = NULL; goto exit; } +#undef ERRtmp -void +res_T release_city(struct city* city) { size_t i; + res_T res = RES_OK; - if(!city) return; + if(!city) { + res = RES_BAD_ARG; + goto error; + } if(city->scpr) SCPR(device_ref_put(city->scpr)); + if(city->names_initialized) htable_names_release(&city->dump_footprint_names); /* iterate on building */ - for (i = 0; i < city->buildings_count; i++) { + for (i = 0; i < city->initialized_buildings_count; i++) { struct building* building = city->buildings + i; - if(building->pg) SCPR(polygon_ref_put(building->pg)); - if(building->name_initialized) str_release(&building->name); + ERR(building->functors->release(city->allocator, city->logger, building)); } MEM_RM(city->allocator, city->buildings); MEM_RM(city->allocator, city); +exit: + return res; +error: + goto exit; } res_T @@ -165,7 +393,10 @@ 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; + size_t i, generated_buildings_count = 0; + struct building *building = NULL; + + ASSERT(city); /* Initialize star-cad */ ERR(scad_initialize(city->logger, city->allocator, city->verbosisty_level)); @@ -175,37 +406,42 @@ city_cad_build(struct city* city) ERR(scad_set_options(&options)); /* iterate on buildings */ - for(i = 0; i < city->buildings_count; i++) { - struct building* building = city->buildings + i; + for(i = 0; i < city->allocated_buildings_count; i++) { + building = city->buildings + i; struct data_cad_cmode_0* cad = NULL; - size_t j = 0; - size_t count = 0; - - /* TEMPORARY HARD CODED DATA */ - /* all building are considered as adjoining */ - building->adjoining_n = city->buildings_count - 1; - building->adjoining = malloc(building->adjoining_n * sizeof(struct building*)); - for (j=0; j < city->buildings_count; j++) { - if (i != j) { - building->adjoining[count] = city->buildings + j; - count++; + + if(building->event_flags & BUILDING_OUT_OF_GROUND_EXTENT + || building->event_flags & BUILDING_WITH_OVERLAPPING) + { + /* No fix for these problems */ + building->event_flags |= BUILDING_REMOVED; + if(city->dump_footprints_on_error) { + ERR(dump_obj(city->allocator, building)); } + } 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)); + 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)); + ERR(scad_scene_clear()); + generated_buildings_count++; } - - /* create building */ - ERR(building->functors->build_cad(city->scpr, city->allocator, city->logger, - building, (void**)&cad)); - 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)); - ERR(scad_scene_clear()); } + building = NULL; + city->cad_generated_buildings_count = generated_buildings_count; exit: - if(scad_initialized) CHK(RES_OK == scad_finalize()); + if(scad_initialized) SCAD(finalize()); return res; error: + if(building) { + logger_print(city->logger, LOG_ERROR, + "Error generating CAD for building '%s'.\n", str_cget(&building->name)); + } goto exit; } @@ -216,9 +452,11 @@ city_ground_build(struct city* city) struct scad_options options = SCAD_DEFAULT_OPTIONS__; int scad_initialized = 0; struct ground ground = GROUND_NULL__; - size_t i; + size_t i = 0, g = 0; + struct building *building = NULL; - ERR(ground_init(city->allocator, city->logger, city->buildings_count, &ground)); + ERR(ground_init(city->allocator, city->logger, + city->cad_generated_buildings_count, &ground)); /* Initialize star-cad */ ERR(scad_initialize(city->logger, city->allocator, city->verbosisty_level)); @@ -228,13 +466,20 @@ city_ground_build(struct city* city) ERR(scad_set_options(&options)); /* iterate on buildings */ - for(i = 0; i < ground.footprints_count ; i++) { - struct building* building = city->buildings + i; - struct scad_geometry** footprint = ground.footprints + i; + for(i = 0; i < city->allocated_buildings_count; i++) { + building = city->buildings + i; + struct scad_geometry** footprint; + + if(building->event_flags & BUILDING_REMOVED) + continue; + + footprint = ground.footprints + g++; + /* create building footprint */ ERR(building->functors->build_footprint(city->scpr, city->allocator, city->logger, building, footprint)); } + building = NULL; ERR(ground_build_cad(city->allocator, city, &ground)); ERR(scad_scene_mesh()); @@ -242,8 +487,17 @@ city_ground_build(struct city* city) exit: ground_clear(city->allocator, &ground); - if(scad_initialized) CHK(RES_OK == scad_finalize()); + if(scad_initialized) SCAD(finalize()); return res; error: + if(building) { + logger_print(city->logger, LOG_ERROR, + "Error generating ground footprint for building '%s'.\n", + str_cget(&building->name)); + } + else if(i == city->allocated_buildings_count) { + logger_print(city->logger, LOG_ERROR, + "Error generating ground.\n"); + } goto exit; } diff --git a/src/cg_city.h b/src/cg_city.h @@ -22,6 +22,9 @@ #include <star/scad.h> +#include <rsys/hash_table.h> +#include <rsys/str.h> + struct logger; struct mem_allocator; struct building; @@ -29,18 +32,35 @@ struct args; struct parsed_city; struct catalog; struct scpr_device; +struct scpr_callback_segment; + +#define HTABLE_NAME names +#define HTABLE_DATA char +#define HTABLE_KEY struct str +#define HTABLE_KEY_FUNCTOR_INIT str_init +#define HTABLE_KEY_FUNCTOR_RELEASE str_release +#define HTABLE_KEY_FUNCTOR_COPY str_copy +#define HTABLE_KEY_FUNCTOR_COPY_AND_RELEASE str_copy_and_release +#define HTABLE_KEY_FUNCTOR_COPY_AND_CLEAR str_copy_and_clear +#define HTABLE_KEY_FUNCTOR_EQ str_eq +#define HTABLE_KEY_FUNCTOR_HASH str_hash +#include <rsys/hash_table.h> struct city { double lower[2], upper[2]; /* Bbox */ double ground_depth; struct building* buildings; /* list of buildings */ - size_t buildings_count; - int binary_export; - + size_t cad_generated_buildings_count, allocated_buildings_count, + initialized_buildings_count; + struct htable_names dump_footprint_names; struct mem_allocator* allocator; struct logger* logger; struct scpr_device* scpr; + int binary_export; int verbosisty_level; + int keep_running_on_errors; + int dump_footprints_on_error; + int names_initialized; }; res_T @@ -60,7 +80,41 @@ res_T city_ground_build (struct city* city); -void +res_T release_city(struct city* city); +res_T +dump_obj + (struct mem_allocator* allocator, + struct building* building); + +/* An enum to encode type of proximity of buildings. */ +enum building_proximity { + NO_PROXIMITY, + CLOSE_PROXIMITY = BIT(0), + OVERLAPPING_PROXIMITY = BIT(1) +}; + +/* the type of context expected by simple_intersection */ +struct callback_ctx { + struct mem_allocator* allocator; + struct logger* logger; + struct building* buildings; + size_t buildings_count; + int* intersection_found; /* Can be NULL if not to be registered */ + enum building_proximity search_type; + int dump_footprints_on_error; + int keep_running_on_errors; +}; + +int collinear_segments + (struct scpr_callback_segment* segment1, + struct scpr_callback_segment* segment2, + void* ctx__); + +int simple_intersection + (struct scpr_callback_segment* segment1, + struct scpr_callback_segment* segment2, + void* ctx__); + #endif /*CITY_H*/ diff --git a/src/cg_construction_mode.c b/src/cg_construction_mode.c @@ -0,0 +1,123 @@ +/* Copyright (C) 2022 Université de Pau et des Pays de l'Adour UPPA + * Copyright (C) 2022 CNRS + * Copyright (C) 2022 Sorbonne Université + * Copyright (C) 2022 Université Paul Sabatier + * Copyright (C) 2022 |Meso|Star> (contact@meso-star.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "cg.h" +#include "cg_building.h" +#include "cg_catalog.h" +#include "cg_city.h" +#include "cg_city_parsing_schemas.h" +#include "cg_construction_mode.h" + +#include <rsys/rsys.h> +#include <rsys/str.h> +#include <rsys/logger.h> +#include <star/scad.h> +#include <star/scpr.h> + +void +get_nverts(const size_t icomp, size_t* nverts, void* context) +{ + struct parsed_city_building* parsed_data = context; + ASSERT(icomp == 0); (void)icomp; + *nverts = parsed_data->vertice_count; +} + +void +get_pos(const size_t icomp, const size_t ivert, double pos[2], void* context) +{ + struct parsed_city_building* parsed_data = context; + ASSERT(icomp == 0); (void)icomp; + pos[0] = parsed_data->vertice[ivert*2 + 0]; + pos[1] = parsed_data->vertice[ivert*2 + 1]; +} + +void get_position_pg + (const size_t ivert, double pos[2], void* ctx) +{ + struct scpr_polygon* pg = ctx; + ASSERT(pos && pg); + CHK(scpr_polygon_get_position(pg, 0, ivert, pos) == RES_OK); +} + +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 parsed_city_building* parsed_data, + const double lower[2], + const double upper[2]) +{ + int inside; + res_T res = RES_OK; + + ASSERT(scpr && allocator && logger && building && parsed_data && lower && upper); + + building->height = parsed_data->height; + str_init(allocator, &building->name); + htable_building_init(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_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, + "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; + } + } + +exit: + return res; +error: + goto exit; +} + +res_T +release_building_base + (struct mem_allocator* allocator, + struct logger* logger, + struct building* building) +{ + res_T res = RES_OK; + (void) allocator; (void)logger; + + ASSERT(allocator && logger && building); + + if(building->structs_initialized) { + str_release(&building->name); + htable_building_release(&building->close_buildings); + } + if(building->pg) { + ERR(scpr_polygon_ref_put(building->pg)); + } + +exit: + return res; +error: + goto exit; +} diff --git a/src/cg_construction_mode.h b/src/cg_construction_mode.h @@ -25,30 +25,38 @@ #include <rsys/rsys.h> #include <star/scpr.h> -static void -get_nverts(const size_t icomp, size_t* nverts, void* context) -{ - struct parsed_city_building* parsed_data = context; - ASSERT(icomp == 0); (void)icomp; - *nverts = parsed_data->vertice_count; -} - -static void -get_pos(const size_t icomp, const size_t ivert, double pos[2], void* context) -{ - struct parsed_city_building* parsed_data = context; - ASSERT(icomp == 0); (void)icomp; - pos[0] = parsed_data->vertice[ivert*2 + 0]; - pos[1] = parsed_data->vertice[ivert*2 + 1]; -} - -static void get_position_pg - (const size_t ivert, double pos[2], void* ctx) -{ - struct scpr_polygon* pg = ctx; - ASSERT(pos && pg); - CHK(scpr_polygon_get_position(pg, 0, ivert, pos) == RES_OK); -} +struct scpr_device; +struct mem_allocator; +struct logger; +struct building; +struct parsed_city_building; +struct catalog; + +void +get_nverts(const size_t icomp, size_t* nverts, void* context); + +void +get_pos(const size_t icomp, const size_t ivert, double pos[2], void* context); + +void get_position_pg + (const size_t ivert, double pos[2], void* ctx); + +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 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); #endif diff --git a/src/cg_construction_mode_0.c b/src/cg_construction_mode_0.c @@ -369,7 +369,7 @@ build_boundary is_init = 1; count = 5 + adjoining_n; - list = MEM_ALLOC(allocator, count * sizeof(struct scad_geometry*)); + list = MEM_CALLOC(allocator, count, sizeof(struct scad_geometry*)); if(!list) { res = RES_MEM_ERR; goto error; @@ -429,10 +429,10 @@ building_ground_connection ERR(str_set(&name, prefix)); ERR(str_append(&name, "_C_ground")); cname = str_get(&name); - + list[0] = cad->wall; list[1] = cad->floor; - + ERR(scad_geometries_common_boundaries(cname, list, 2, &cad->fake_ground, 1, connection)); exit: @@ -452,18 +452,19 @@ init_cmode_0 struct mem_allocator* allocator, struct logger* logger, struct building* building, + int keep_running_on_error, struct parsed_city_building* parsed_data, struct catalog* catalog, const double lower[2], const double upper[2]) { res_T res = RES_OK; - struct dataset_cmode_0* dataset; struct str dataset_name; - int name_initialized = 0, inside; + int name_initialized = 0; static struct construction_mode_functors functors_0 = { &init_cmode_0, + &release_cmode_0, &build_cad_cmode_0, &build_footprint_cmode_0, &build_envelop_cmode_0, @@ -472,32 +473,25 @@ init_cmode_0 }; (void)parsed_data; - if(!building || !allocator || !parsed_data || !catalog) { + if(!scpr || !allocator || !logger || !building || !parsed_data || !catalog + || !lower || !upper) + { res = RES_BAD_ARG; goto error; } building->construction_mode = mode_0; building->functors = &functors_0; - building->height = parsed_data->height; - str_init(allocator, &building->name); - building->name_initialized = 1; + + ERR(init_building_base(scpr, allocator, logger, building, keep_running_on_error, + parsed_data, lower, upper)); + str_init(allocator, &dataset_name); name_initialized = 1; - ERR(str_set(&building->name, parsed_data->name)); ERR(str_set(&dataset_name, parsed_data->dataset_name)); - ERR(scpr_polygon_create(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, - "Building '%s' is out of the ground extent.\n", - str_cget(&building->name)); - } - dataset = htable_dataset_cmode_0_find(&catalog->catalog_0, &dataset_name); - if (dataset == NULL) { + building->data = htable_dataset_cmode_0_find(&catalog->catalog_0, &dataset_name); + if (building->data == NULL) { ERR(logger_print(logger, LOG_ERROR, "Unknown dataset name: '%s' used by building '%s'.\n", str_cget(&dataset_name), str_cget(&building->name))); @@ -505,8 +499,6 @@ init_cmode_0 goto error; } - building->data = dataset; - exit: if(name_initialized) str_release(&dataset_name); return res; @@ -515,11 +507,34 @@ error: } res_T +release_cmode_0 + (struct mem_allocator* allocator, + struct logger* logger, + 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; +} + +res_T build_cad_cmode_0 (struct scpr_device* scpr, struct mem_allocator* allocator, struct logger* logger, struct building* building, + int dump_footprints_on_error, + int keep_running_on_errors, void** cad) { res_T res = RES_OK; @@ -532,7 +547,12 @@ build_cad_cmode_0 const char* name; struct scad_geometry** adjoining_cad = NULL; size_t i = 0; - size_t adjoining_n = building->adjoining_n; + size_t adjoining_n; + 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; if (!building || !allocator || !cad) { res = RES_BAD_ARG; @@ -542,6 +562,7 @@ build_cad_cmode_0 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; @@ -554,9 +575,25 @@ build_cad_cmode_0 goto error; } + 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_offset_polygon(pg_int, -e_wall, SCPR_JOIN_MITER)); + ERR(scpr_intersector_register_polygon(overlapping_intersector, pg_int)); + + /* Check for polygons overlapping */ + ctx.allocator = allocator; + ctx.logger = logger; + ctx.buildings = building; + ctx.buildings_count = 1; + ctx.intersection_found = &error_occured; + 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)); /* build floor with pg_int */ name = str_cget(&building->name); @@ -574,8 +611,7 @@ build_cad_cmode_0 /* build adjoining envelop */ if (adjoining_n > 0) { - ERR(build_adjoining(allocator, logger, - building->adjoining, adjoining_n, &adjoining_cad)); + ERR(build_adjoining(allocator, logger, building, &adjoining_cad)); } /* build fake ground */ @@ -595,11 +631,14 @@ build_cad_cmode_0 exit: 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]) scad_geometry_delete(adjoining_cad[i]); + if (adjoining_cad[i]) { + ERR(scad_geometry_delete(adjoining_cad[i])); + } } - if (adjoining_cad) MEM_RM(allocator, adjoining_cad); + MEM_RM(allocator, adjoining_cad); return res; error: if(data_cad) CHK(RES_OK == release_cad_cmode_0(allocator, logger, data_cad)); diff --git a/src/cg_construction_mode_0.h b/src/cg_construction_mode_0.h @@ -68,17 +68,26 @@ init_cmode_0 struct mem_allocator* allocator, struct logger* logger, struct building* building, + int keep_running_on_error, struct parsed_city_building* parsed_data, struct catalog* catalog, const double lower[2], const double upper[2]); res_T +release_cmode_0 + (struct mem_allocator* allocator, + struct logger* logger, + struct building* building); + +res_T build_cad_cmode_0 (struct scpr_device* scpr, struct mem_allocator* allocator, struct logger* logger, struct building* building, + int dump_footprints_on_error, + int keep_running_on_errors, void** cad); res_T diff --git a/src/cg_construction_mode_1.c b/src/cg_construction_mode_1.c @@ -20,15 +20,41 @@ #include "cg.h" #include "cg_building.h" #include "cg_catalog.h" +#include "cg_city.h" #include "cg_city_parsing_schemas.h" #include "cg_construction_mode.h" #include "cg_construction_mode_1.h" #include <rsys/str.h> #include <rsys/logger.h> +#include <rsys/hash_table.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, @@ -36,6 +62,8 @@ build_floor const char* prefix, const struct scpr_polygon* pg, const struct dataset_cmode_1* data, + struct scpr_intersector* overlapping_intersector, + struct htable_polygons* polygons, struct scad_geometry** floor) { res_T res = RES_OK; @@ -44,6 +72,7 @@ build_floor double e_floor = data->floor_thickness; double offset = 0; struct scpr_polygon* pg_int = NULL; + struct scpr_polygon** ptr_p; size_t nverts = 0; struct scad_geometry* footprint = NULL; double d[3] = {0, 0, 0}; @@ -61,9 +90,16 @@ build_floor floorname = str_get(&name); } - offset = e_wall + e_insulation; - ERR(scpr_polygon_create_copy(scpr, pg, &pg_int)); - ERR(scpr_offset_polygon(pg_int, -offset, SCPR_JOIN_MITER)); + offset = -(e_wall + e_insulation); + ptr_p = htable_polygons_find(polygons, &offset); + if(ptr_p) { + pg_int = *ptr_p; + } else { + ERR(scpr_polygon_create_copy(scpr, pg, &pg_int)); + ERR(scpr_offset_polygon(pg_int, offset, SCPR_JOIN_MITER)); + ERR(scpr_intersector_register_polygon(overlapping_intersector, pg_int)); + htable_polygons_set(polygons, &offset, &pg_int); + } /*footprint*/ ERR(scpr_polygon_get_vertices_count(pg_int, 0, &nverts)); @@ -75,7 +111,6 @@ build_floor exit: SCAD(geometry_delete(footprint)); if (is_init) str_release(&name); - if (pg_int) SCPR(polygon_ref_put(pg_int)); return res; error: goto exit; @@ -90,6 +125,8 @@ build_wall const struct scpr_polygon* pg, const double height, const struct dataset_cmode_1* data, + struct scpr_intersector* overlapping_intersector, + struct htable_polygons* polygons, struct scad_geometry** wall) { res_T res = RES_OK; @@ -98,6 +135,7 @@ build_wall double offset = 0; struct scpr_polygon* pg_int = NULL; struct scpr_polygon* pg_ext = NULL; + struct scpr_polygon** ptr_p; size_t nverts = 0; struct scad_geometry* footprint = NULL; struct scad_geometry* footprint_int = NULL; @@ -121,17 +159,30 @@ build_wall } if (strcmp(suffix, "S_foundation") == 0) { - offset = e_insulation + 0.1*e_wall; + offset = -(e_insulation + 0.1*e_wall); } else { - offset = e_insulation; + offset = -e_insulation; + } + ptr_p = htable_polygons_find(polygons, &offset); + if(ptr_p) { + pg_ext = *ptr_p; + } else { + ERR(scpr_polygon_create_copy(scpr, pg, &pg_ext)); + ERR(scpr_offset_polygon(pg_ext, offset, SCPR_JOIN_MITER)); + ERR(scpr_intersector_register_polygon(overlapping_intersector, pg_ext)); + htable_polygons_set(polygons, &offset, &pg_ext); } - ERR(scpr_polygon_create_copy(scpr, pg, &pg_ext)); - ERR(scpr_offset_polygon(pg_ext, -offset, SCPR_JOIN_MITER)); - - offset = e_wall + e_insulation; - ERR(scpr_polygon_create_copy(scpr, pg, &pg_int)); - ERR(scpr_offset_polygon(pg_int, -offset, SCPR_JOIN_MITER)); + offset = -(e_wall + e_insulation); + ptr_p = htable_polygons_find(polygons, &offset); + if(ptr_p) { + pg_int = *ptr_p; + } else { + ERR(scpr_polygon_create_copy(scpr, pg, &pg_int)); + ERR(scpr_offset_polygon(pg_int, offset, SCPR_JOIN_MITER)); + ERR(scpr_intersector_register_polygon(overlapping_intersector, pg_int)); + htable_polygons_set(polygons, &offset, &pg_int); + } /*wall footprint*/ ERR(scpr_polygon_get_vertices_count(pg_ext, 0, &nverts)); @@ -153,8 +204,6 @@ exit: SCAD(geometry_delete(footprint_int)); SCAD(geometry_delete(footprint_ext)); if (is_init) str_release(&name); - if (pg_int) SCPR(polygon_ref_put(pg_int)); - if (pg_ext) SCPR(polygon_ref_put(pg_ext)); return res; error: goto exit; @@ -169,6 +218,8 @@ build_int_insulation const double height, const struct dataset_cmode_1* data, struct scad_geometry* inter_floor, + struct scpr_intersector* overlapping_intersector, + struct htable_polygons* polygons, struct scad_geometry** insulation) { res_T res = RES_OK; @@ -181,6 +232,7 @@ build_int_insulation double offset = 0; struct scpr_polygon* pg_int = NULL; struct scpr_polygon* pg_ext = NULL; + struct scpr_polygon** ptr_p; size_t nverts = 0; struct scad_geometry* footprint = NULL; struct scad_geometry* footprint_int = NULL; @@ -201,13 +253,27 @@ build_int_insulation insulationname = str_get(&name); } - offset = e_ext_insulation + e_wall; - ERR(scpr_polygon_create_copy(scpr, pg, &pg_ext)); - ERR(scpr_offset_polygon(pg_ext, -offset, SCPR_JOIN_MITER)); + offset = -(e_ext_insulation + e_wall); + ptr_p = htable_polygons_find(polygons, &offset); + if(ptr_p) { + pg_ext = *ptr_p; + } else { + ERR(scpr_polygon_create_copy(scpr, pg, &pg_ext)); + ERR(scpr_offset_polygon(pg_ext, offset, SCPR_JOIN_MITER)); + ERR(scpr_intersector_register_polygon(overlapping_intersector, pg_ext)); + htable_polygons_set(polygons, &offset, &pg_ext); + } - offset = e_ext_insulation + e_wall + e_int_insulation; - ERR(scpr_polygon_create_copy(scpr, pg, &pg_int)); - ERR(scpr_offset_polygon(pg_int, -offset, SCPR_JOIN_MITER)); + offset = -(e_ext_insulation + e_wall + e_int_insulation); + ptr_p = htable_polygons_find(polygons, &offset); + if(ptr_p) { + pg_int = *ptr_p; + } else { + ERR(scpr_polygon_create_copy(scpr, pg, &pg_int)); + ERR(scpr_offset_polygon(pg_int, offset, SCPR_JOIN_MITER)); + ERR(scpr_intersector_register_polygon(overlapping_intersector, pg_int)); + htable_polygons_set(polygons, &offset, &pg_int); + } /* insulation footprint */ ERR(scpr_polygon_get_vertices_count(pg_ext, 0, &nverts)); @@ -234,8 +300,6 @@ exit: SCAD(geometry_delete(footprint_ext)); SCAD(geometry_delete(geom)); if (is_init) str_release(&name); - if (pg_int) SCPR(polygon_ref_put(pg_int)); - if (pg_ext) SCPR(polygon_ref_put(pg_ext)); return res; error: goto exit; @@ -249,6 +313,8 @@ build_roof const struct scpr_polygon* pg, const double height, const struct dataset_cmode_1* data, + struct scpr_intersector* overlapping_intersector, + struct htable_polygons* polygons, struct scad_geometry** roof) { res_T res = RES_OK; @@ -258,6 +324,7 @@ build_roof double offset = 0; double z_roof = 0; struct scpr_polygon* pg_int = NULL; + struct scpr_polygon** ptr_p; size_t nverts = 0; struct scad_geometry* footprint = NULL; double d[3] = {0, 0, 0}; @@ -275,9 +342,16 @@ build_roof roofname = str_get(&name); } - offset = e_wall + e_insulation; - ERR(scpr_polygon_create_copy(scpr, pg, &pg_int)); - ERR(scpr_offset_polygon(pg_int, -offset, SCPR_JOIN_MITER)); + offset = -(e_wall + e_insulation); + ptr_p = htable_polygons_find(polygons, &offset); + if(ptr_p) { + pg_int = *ptr_p; + } else { + ERR(scpr_polygon_create_copy(scpr, pg, &pg_int)); + ERR(scpr_offset_polygon(pg_int, offset, SCPR_JOIN_MITER)); + ERR(scpr_intersector_register_polygon(overlapping_intersector, pg_int)); + htable_polygons_set(polygons, &offset, &pg_int); + } /*footprint*/ ERR(scpr_polygon_get_vertices_count(pg_int, 0, &nverts)); @@ -290,7 +364,6 @@ build_roof exit: SCAD(geometry_delete(footprint)); if (is_init) str_release(&name); - if (pg_int) SCPR(polygon_ref_put(pg_int)); return res; error: goto exit; @@ -304,6 +377,8 @@ build_roof_insulation const struct scpr_polygon* pg, const double height, const struct dataset_cmode_1* data, + struct scpr_intersector* overlapping_intersector, + struct htable_polygons* polygons, struct scad_geometry** insulation) { res_T res = RES_OK; @@ -315,6 +390,7 @@ build_roof_insulation double offset = 0; double z_insulation = 0; struct scpr_polygon* pg_int = NULL; + struct scpr_polygon** ptr_p; size_t nverts = 0; struct scad_geometry* footprint = NULL; double d[3] = {0, 0, 0}; @@ -332,9 +408,16 @@ build_roof_insulation insulationname = str_get(&name); } - offset = e_wall + e_insulation; - ERR(scpr_polygon_create_copy(scpr, pg, &pg_int)); - ERR(scpr_offset_polygon(pg_int, -offset, SCPR_JOIN_MITER)); + offset = -(e_wall + e_insulation); + ptr_p = htable_polygons_find(polygons, &offset); + if(ptr_p) { + pg_int = *ptr_p; + } else { + ERR(scpr_polygon_create_copy(scpr, pg, &pg_int)); + ERR(scpr_offset_polygon(pg_int, offset, SCPR_JOIN_MITER)); + ERR(scpr_intersector_register_polygon(overlapping_intersector, pg_int)); + htable_polygons_set(polygons, &offset, &pg_int); + } /*footprint*/ ERR(scpr_polygon_get_vertices_count(pg_int, 0, &nverts)); @@ -348,7 +431,6 @@ build_roof_insulation exit: SCAD(geometry_delete(footprint)); if (is_init) str_release(&name); - if (pg_int) SCPR(polygon_ref_put(pg_int)); return res; error: goto exit; @@ -361,6 +443,8 @@ build_floor_insulation const char* prefix, const struct scpr_polygon* pg, const struct dataset_cmode_1* data, + struct scpr_intersector* overlapping_intersector, + struct htable_polygons* polygons, struct scad_geometry** insulation) { res_T res = RES_OK; @@ -371,6 +455,7 @@ build_floor_insulation double offset = 0; double z_insulation = 0; struct scpr_polygon* pg_int = NULL; + struct scpr_polygon** ptr_p; size_t nverts = 0; struct scad_geometry* footprint = NULL; double d[3] = {0, 0, 0}; @@ -388,9 +473,16 @@ build_floor_insulation insulationname = str_get(&name); } - offset = e_wall + e_insulation; - ERR(scpr_polygon_create_copy(scpr, pg, &pg_int)); - ERR(scpr_offset_polygon(pg_int, -offset, SCPR_JOIN_MITER)); + offset = -(e_wall + e_insulation); + ptr_p = htable_polygons_find(polygons, &offset); + if(ptr_p) { + pg_int = *ptr_p; + } else { + ERR(scpr_polygon_create_copy(scpr, pg, &pg_int)); + ERR(scpr_offset_polygon(pg_int, offset, SCPR_JOIN_MITER)); + ERR(scpr_intersector_register_polygon(overlapping_intersector, pg_int)); + htable_polygons_set(polygons, &offset, &pg_int); + } /*footprint*/ ERR(scpr_polygon_get_vertices_count(pg_int, 0, &nverts)); @@ -404,7 +496,6 @@ build_floor_insulation exit: SCAD(geometry_delete(footprint)); if (is_init) str_release(&name); - if (pg_int) SCPR(polygon_ref_put(pg_int)); return res; error: goto exit; @@ -418,6 +509,8 @@ build_inter_floor const struct scpr_polygon* pg, const double height, const struct dataset_cmode_1* data, + struct scpr_intersector* overlapping_intersector, + struct htable_polygons* polygons, struct scad_geometry** inter_floor) { res_T res = RES_OK; @@ -433,6 +526,7 @@ build_inter_floor double z_floor = 0; double h_cavity = 0; struct scpr_polygon* pg_int = NULL; + struct scpr_polygon** ptr_p; size_t nverts = 0; struct scad_geometry** floor_list = NULL; struct darray_geometries floor_array; @@ -453,9 +547,16 @@ build_inter_floor floorname = str_get(&name); } - offset = e_wall + e_insulation; - ERR(scpr_polygon_create_copy(scpr, pg, &pg_int)); - ERR(scpr_offset_polygon(pg_int, -offset, SCPR_JOIN_MITER)); + offset = -(e_wall + e_insulation); + ptr_p = htable_polygons_find(polygons, &offset); + if(ptr_p) { + pg_int = *ptr_p; + } else { + ERR(scpr_polygon_create_copy(scpr, pg, &pg_int)); + ERR(scpr_offset_polygon(pg_int, offset, SCPR_JOIN_MITER)); + ERR(scpr_intersector_register_polygon(overlapping_intersector, pg_int)); + htable_polygons_set(polygons, &offset, &pg_int); + } ERR(scpr_polygon_get_vertices_count(pg_int, 0, &nverts)); h_cavity = height - e_roof - attic - e_roof_ins - (double)floor_n*e_floor; @@ -480,7 +581,6 @@ build_inter_floor exit: if (is_init) str_release(&name); - if (pg_int) SCPR(polygon_ref_put(pg_int)); floor_list = darray_geometries_data_get(&floor_array); if(floor_list) { for (i = 0; i < floor_n; i++) { @@ -501,6 +601,8 @@ build_ext_insulation const struct scpr_polygon* pg, const double height, const struct dataset_cmode_1* data, + struct scpr_intersector* overlapping_intersector, + struct htable_polygons* polygons, struct scad_geometry** insulation) { res_T res = RES_OK; @@ -508,6 +610,7 @@ build_ext_insulation double offset = 0; struct scpr_polygon* pg_int = NULL; struct scpr_polygon* pg_ext = NULL; + struct scpr_polygon** ptr_p; size_t nverts = 0; struct scad_geometry* footprint = NULL; struct scad_geometry* footprint_int = NULL; @@ -527,10 +630,21 @@ build_ext_insulation insulationname = str_get(&name); } - offset = e_insulation; - ERR(scpr_polygon_create_copy(scpr, pg, &pg_int)); - ERR(scpr_polygon_create_copy(scpr, pg, &pg_ext)); - ERR(scpr_offset_polygon(pg_int, -offset, SCPR_JOIN_MITER)); + offset = -e_insulation; + ptr_p = htable_polygons_find(polygons, &offset); + if(ptr_p) { + pg_int = *ptr_p; + } else { + ERR(scpr_polygon_create_copy(scpr, pg, &pg_int)); + ERR(scpr_offset_polygon(pg_int, offset, SCPR_JOIN_MITER)); + ERR(scpr_intersector_register_polygon(overlapping_intersector, pg_int)); + htable_polygons_set(polygons, &offset, &pg_int); + } + + offset = 0; + ptr_p = htable_polygons_find(polygons, &offset); + ASSERT(ptr_p); /* Offset 0 is the first to be inserted in polygons */ + pg_ext = *ptr_p; /*insulation footprint*/ ERR(scpr_polygon_get_vertices_count(pg_ext, 0, &nverts)); @@ -552,8 +666,6 @@ exit: SCAD(geometry_delete(footprint_int)); SCAD(geometry_delete(footprint_ext)); if (is_init) str_release(&name); - if (pg_int) SCPR(polygon_ref_put(pg_int)); - if (pg_ext) SCPR(polygon_ref_put(pg_ext)); return res; error: goto exit; @@ -566,6 +678,8 @@ build_crawlspace const char* prefix, const struct scpr_polygon* pg, const struct dataset_cmode_1* data, + struct scpr_intersector* overlapping_intersector, + struct htable_polygons* polygons, struct scad_geometry** crawlspace) { res_T res = RES_OK; @@ -577,6 +691,7 @@ build_crawlspace double offset = 0; double z_crawl= 0; struct scpr_polygon* pg_int = NULL; + struct scpr_polygon** ptr_p; size_t nverts = 0; struct scad_geometry* footprint = NULL; double d[3] = {0, 0, 0}; @@ -594,9 +709,16 @@ build_crawlspace crawlname = str_get(&name); } - offset = e_wall + e_insulation; - ERR(scpr_polygon_create_copy(scpr, pg, &pg_int)); - ERR(scpr_offset_polygon(pg_int, -offset, SCPR_JOIN_MITER)); + offset = -(e_wall + e_insulation); + ptr_p = htable_polygons_find(polygons, &offset); + if(ptr_p) { + pg_int = *ptr_p; + } else { + ERR(scpr_polygon_create_copy(scpr, pg, &pg_int)); + ERR(scpr_offset_polygon(pg_int, offset, SCPR_JOIN_MITER)); + ERR(scpr_intersector_register_polygon(overlapping_intersector, pg_int)); + htable_polygons_set(polygons, &offset, &pg_int); + } /*footprint*/ ERR(scpr_polygon_get_vertices_count(pg_int, 0, &nverts)); @@ -610,7 +732,6 @@ build_crawlspace exit: SCAD(geometry_delete(footprint)); if (is_init) str_release(&name); - if (pg_int) SCPR(polygon_ref_put(pg_int)); return res; error: goto exit; @@ -625,6 +746,8 @@ build_habitable const double height, const struct dataset_cmode_1* data, struct scad_geometry* floor, + struct scpr_intersector* overlapping_intersector, + struct htable_polygons* polygons, struct scad_geometry** cavity) { res_T res = RES_OK; @@ -636,6 +759,7 @@ build_habitable double e_attic = data->attic_height; double offset = 0; struct scpr_polygon* pg_int = NULL; + struct scpr_polygon** ptr_p; size_t nverts = 0; struct scad_geometry* footprint = NULL; struct scad_geometry* geom = NULL; @@ -654,9 +778,16 @@ build_habitable cavityname = str_get(&name); } - offset = e_wall + e_ext_insulation + e_int_insulation; - ERR(scpr_polygon_create_copy(scpr, pg, &pg_int)); - ERR(scpr_offset_polygon(pg_int, -offset, SCPR_JOIN_MITER)); + offset = -(e_wall + e_ext_insulation + e_int_insulation); + ptr_p = htable_polygons_find(polygons, &offset); + if(ptr_p) { + pg_int = *ptr_p; + } else { + ERR(scpr_polygon_create_copy(scpr, pg, &pg_int)); + ERR(scpr_offset_polygon(pg_int, offset, SCPR_JOIN_MITER)); + ERR(scpr_intersector_register_polygon(overlapping_intersector, pg_int)); + htable_polygons_set(polygons, &offset, &pg_int); + } /*footprint*/ ERR(scpr_polygon_get_vertices_count(pg_int, 0, &nverts)); @@ -676,7 +807,6 @@ exit: SCAD(geometry_delete(footprint)); SCAD(geometry_delete(geom)); if (is_init) str_release(&name); - if (pg_int) SCPR(polygon_ref_put(pg_int)); return res; error: goto exit; @@ -690,6 +820,8 @@ build_attic const struct scpr_polygon* pg, const double height, const struct dataset_cmode_1* data, + struct scpr_intersector* overlapping_intersector, + struct htable_polygons* polygons, struct scad_geometry** attic) { res_T res = RES_OK; @@ -700,6 +832,7 @@ build_attic double offset = 0; double z_attic = 0; struct scpr_polygon* pg_int = NULL; + struct scpr_polygon** ptr_p; size_t nverts = 0; struct scad_geometry* footprint = NULL; double d[3] = {0, 0, 0}; @@ -717,9 +850,16 @@ build_attic atticname = str_get(&name); } - offset = e_wall + e_insulation; - ERR(scpr_polygon_create_copy(scpr, pg, &pg_int)); - ERR(scpr_offset_polygon(pg_int, -offset, SCPR_JOIN_MITER)); + offset = -(e_wall + e_insulation); + ptr_p = htable_polygons_find(polygons, &offset); + if(ptr_p) { + pg_int = *ptr_p; + } else { + ERR(scpr_polygon_create_copy(scpr, pg, &pg_int)); + ERR(scpr_offset_polygon(pg_int, offset, SCPR_JOIN_MITER)); + ERR(scpr_intersector_register_polygon(overlapping_intersector, pg_int)); + htable_polygons_set(polygons, &offset, &pg_int); + } /*footprint*/ ERR(scpr_polygon_get_vertices_count(pg_int, 0, &nverts)); @@ -733,7 +873,6 @@ build_attic exit: SCAD(geometry_delete(footprint)); if (is_init) str_release(&name); - if (pg_int) SCPR(polygon_ref_put(pg_int)); return res; error: goto exit; @@ -743,6 +882,7 @@ static res_T build_windows (struct scpr_device* scpr, struct mem_allocator* allocator, + struct logger* logger, const char* prefix, const struct dataset_cmode_1* data, struct data_cad_cmode_1* data_cad, @@ -750,7 +890,7 @@ build_windows size_t adjoining_n) { res_T res = RES_OK; - size_t i = 0; + size_t i, removed_windows = 0; double N[3]; double dir[3]; double scale[3]; @@ -770,7 +910,7 @@ build_windows int is_init = 0; (void)scpr; - ASSERT(allocator && data && data_cad); + ASSERT(scpr && allocator && logger && data && data_cad); darray_geometries_init(allocator, &hole_array); darray_geometries_init(allocator, &glass_array); @@ -815,7 +955,7 @@ build_windows if (adjoining_cad) { ERR(scad_intersect_geometries(NULL, &hole, 1, adjoining_cad, adjoining_n, &hole_adjoining_intersect)); - ERR(scad_geometry_get_count(hole_adjoining_intersect, &count)); + ERR(scad_geometry_get_count(hole_adjoining_intersect, &count)); } else { count = 0; } @@ -829,6 +969,7 @@ build_windows ERR(scad_geometry_extrude(surface, NULL, dir, &glass)); ERR(darray_geometries_push_back(&glass_array, &glass)); } else { + removed_windows++; ERR(scad_geometry_delete(hole)); hole = NULL; ERR(scad_geometry_delete(hole_adjoining_intersect)); @@ -842,6 +983,12 @@ build_windows array_n = darray_geometries_size_get(&hole_array); + if(removed_windows) { + logger_print(logger, LOG_OUTPUT, + "Building '%s' has %zu/%zu windows removed due to close neighbors.\n", + prefix, removed_windows, removed_windows+array_n); + } + if (array_n > 0) { hole_list = darray_geometries_data_get(&hole_array); glass_list = darray_geometries_data_get(&glass_array); @@ -1248,6 +1395,7 @@ building_ground_connection struct scad_geometry* list_boundary = NULL; struct scad_geometry* footprint = NULL; + (void)scpr; ASSERT(scpr && allocator && prefix && cad && connection); darray_geometries_init(allocator, &array); @@ -1282,7 +1430,7 @@ building_ground_connection count = darray_geometries_size_get(&array); list = darray_geometries_data_get(&array); - ERR(scad_geometries_common_boundaries(cname, list,count, &cad->fake_ground, 1, + ERR(scad_geometries_common_boundaries(cname, list, count, &cad->fake_ground, 1, connection)); exit: @@ -1305,18 +1453,19 @@ init_cmode_1 struct mem_allocator* allocator, struct logger* logger, struct building* building, + int keep_running_on_error, struct parsed_city_building* parsed_data, struct catalog* catalog, const double lower[2], const double upper[2]) { res_T res = RES_OK; - struct dataset_cmode_1* dataset; struct str dataset_name; - int name_initialized = 0, inside; + int name_initialized = 0; static struct construction_mode_functors functors_1 = { &init_cmode_1, + &release_cmode_1, &build_cad_cmode_1, &build_footprint_cmode_1, &build_envelop_cmode_1, @@ -1325,32 +1474,25 @@ init_cmode_1 }; (void) parsed_data; - if(!building || !allocator || !parsed_data ||!catalog) { + if(!scpr || !allocator || !logger || !building || !parsed_data || !catalog + || !lower || !upper) + { res = RES_BAD_ARG; goto error; } building->construction_mode = mode_1; building->functors = &functors_1; - building->height = parsed_data->height; - str_init(allocator, &building->name); - building->name_initialized = 1; + + ERR(init_building_base(scpr, allocator, logger, building, + keep_running_on_error, parsed_data, lower, upper)); + str_init(allocator, &dataset_name); name_initialized = 1; - ERR(str_set(&building->name, parsed_data->name)); ERR(str_set(&dataset_name, parsed_data->dataset_name)); - ERR(scpr_polygon_create(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, - "Building '%s' is out of the ground extent.\n", - str_cget(&building->name)); - } - dataset = htable_dataset_cmode_1_find(&catalog->catalog_1, &dataset_name); - if (dataset == NULL) { + building->data = htable_dataset_cmode_1_find(&catalog->catalog_1, &dataset_name); + if (building->data == NULL) { ERR(logger_print(logger, LOG_ERROR, "Unknown dataset name: '%s' used by building '%s'.\n", str_cget(&dataset_name), str_cget(&building->name))); @@ -1358,8 +1500,6 @@ init_cmode_1 goto error; } - building->data = dataset; - exit: if(name_initialized) str_release(&dataset_name); return res; @@ -1368,29 +1508,62 @@ error: } res_T +release_cmode_1 + (struct mem_allocator* allocator, + struct logger* logger, + 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; +} + + +res_T build_cad_cmode_1 (struct scpr_device* scpr, struct mem_allocator* allocator, struct logger* logger, struct building* building, + int dump_footprints_on_error, + int keep_running_on_errors, void** cad) { res_T res = RES_OK; double height = building->height; double depth = 0; - struct scpr_polygon* pg = building->pg; + 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 = building->adjoining_n; + size_t adjoining_n; + struct callback_ctx ctx; + int error_occured = 0; + struct htable_polygons polygons; + int polygons_initialized = 0; + double zero = 0; if (!building || !allocator || !cad) { res = RES_BAD_ARG; goto error; } + adjoining_n = htable_building_size_get(&building->close_buildings); data_cad = MEM_CALLOC(allocator, 1, sizeof(struct data_cad_cmode_1)); if(!data_cad) { res = RES_MEM_ERR; @@ -1398,6 +1571,16 @@ build_cad_cmode_1 } darray_geometries_init(allocator, &data_cad->boundary); darray_geometries_init(allocator, &data_cad->connection); + 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)); /* build mandatories elements : - floor @@ -1406,11 +1589,14 @@ build_cad_cmode_1 */ name = str_cget(&building->name); - ERR(build_floor(scpr, allocator, name, pg, data, &data_cad->floor)); + ERR(build_floor(scpr, allocator, name, pg, data, overlapping_intersector, + &polygons, &data_cad->floor)); - ERR(build_wall(scpr, allocator, name, "S_walls", pg, height, data, &data_cad->wall)); + ERR(build_wall(scpr, allocator, name, "S_walls", pg, height, data, + overlapping_intersector, &polygons, &data_cad->wall)); - ERR(build_roof(scpr, allocator, name, pg, height, data, &data_cad->roof)); + ERR(build_roof(scpr, allocator, name, pg, height, data, + overlapping_intersector, &polygons, &data_cad->roof)); /* build optionnal elements : - foundation @@ -1424,32 +1610,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, - &data_cad->foundation)); + overlapping_intersector, &polygons, &data_cad->foundation)); } if (data->inter_floor_count > 0) { ERR(build_inter_floor(scpr, allocator, name, pg, height, data, - &data_cad->intermediate_floor)); + overlapping_intersector, &polygons, &data_cad->intermediate_floor)); } if (data->external_insulation_thickness> 0) { ERR(build_ext_insulation(scpr, allocator, name, pg, height, data, - &data_cad->external_insulation)); + overlapping_intersector, &polygons, &data_cad->external_insulation)); } if (data->internal_insulation_thickness> 0) { ERR(build_int_insulation(scpr, allocator, name, pg, height, data, - data_cad->intermediate_floor, &data_cad->internal_insulation)); + 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, - &data_cad->roof_insulation)); + overlapping_intersector, &polygons, &data_cad->roof_insulation)); } if (data->floor_insulation_thickness > 0) { ERR(build_floor_insulation(scpr, allocator, name, pg, data, - &data_cad->floor_insulation)); + overlapping_intersector, &polygons, &data_cad->floor_insulation)); } /* build cavities : @@ -1460,25 +1647,38 @@ build_cad_cmode_1 if (data->attic_height > 0) { ERR(build_attic(scpr, allocator, name, pg, height, data, - &data_cad->attic_cavity)); + overlapping_intersector, &polygons, &data_cad->attic_cavity)); } ERR(build_habitable(scpr, allocator, name, pg, height, data, - data_cad->intermediate_floor, &data_cad->habitable_cavity)); + data_cad->intermediate_floor, overlapping_intersector, &polygons, + &data_cad->habitable_cavity)); if (data->crawl_height > 0) { - ERR(build_crawlspace(scpr, allocator, name, pg, data, &data_cad->crawlspace_cavity)); + ERR(build_crawlspace(scpr, allocator, name, pg, data, + overlapping_intersector, &polygons, &data_cad->crawlspace_cavity)); } + /* Check for polygons overlapping */ + ctx.allocator = allocator; + ctx.logger = logger; + ctx.buildings = building; + ctx.buildings_count = 1; + ctx.intersection_found = &error_occured; /* build adjoining envelop */ - if (building->adjoining_n > 0) { - ERR(build_adjoining(allocator, logger, - building->adjoining, building->adjoining_n, &adjoining_cad)); + 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)); + /* windows */ if (data->glass_ratio > 0) { - ERR(build_windows(scpr, allocator, name, data, data_cad, + ERR(build_windows(scpr, allocator, logger, name, data, data_cad, adjoining_cad, adjoining_n)); } @@ -1487,7 +1687,7 @@ build_cad_cmode_1 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(scad_scene_partition()); /* build ground/building connection */ @@ -1502,6 +1702,8 @@ build_cad_cmode_1 ERR(build_connection(scpr, allocator, name, data_cad, &data_cad->connection)); 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]); diff --git a/src/cg_construction_mode_1.h b/src/cg_construction_mode_1.h @@ -93,17 +93,26 @@ init_cmode_1 struct mem_allocator* allocator, struct logger* logger, struct building* building, + int keep_running_on_error, struct parsed_city_building* parsed_data, struct catalog* catalog, const double lower[2], const double upper[2]); res_T +release_cmode_1 + (struct mem_allocator* allocator, + struct logger* logger, + struct building* building); + +res_T build_cad_cmode_1 (struct scpr_device* scpr, struct mem_allocator* allocator, struct logger* logger, struct building* building, + int dump_footprints_on_error, + int keep_running_on_errors, void** cad); res_T diff --git a/src/cg_ground.c b/src/cg_ground.c @@ -97,7 +97,6 @@ ground_build_cad } } - exit: for (i = 0; i < count; i++) { scad_geometry_delete(list[i]); diff --git a/src/cg_main.c b/src/cg_main.c @@ -135,7 +135,7 @@ int main /* Parse catalog. * No semantic validation is done at this stage */ - ERR(parse_catalog(&args->catalog_filenames, &allocator, &logger, &config, + ERR(parse_catalog(&args->catalog_files, &allocator, &logger, &config, &parsed_catalog)); /* Create catalog from parsed data. @@ -158,15 +158,14 @@ int main release_args(args); args = NULL; - ERR(city_ground_build(city)); ERR(city_cad_build(city)); + /* As buildings can be removed at the CAD generation step, ground CAD must be + * generated after buildings CAD */ + ERR(city_ground_build(city)); exit: - release_args(args); release_city(city); release_catalog(catalog); - release_parsed_catalog(&config, parsed_catalog); - release_parsed_city(&config, parsed_city); if(logger_initialized) logger_release(&logger); if(allocator_initialized) { if(check_memory_allocator(&allocator)) err = EXIT_FAILURE; @@ -175,6 +174,9 @@ exit: } return err; error: + release_args(args); + release_parsed_catalog(&config, parsed_catalog); + release_parsed_city(&config, parsed_city); err = EXIT_FAILURE; printf("City generator failed.\n"); goto exit;