commit 0b7f78c48125a8dc05d8c09a07f3cb415196f189
parent c7576e1b9fcbd86d10927be07f288ce801b14f1f
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date: Wed, 18 Oct 2023 10:12:38 +0200
Change invalid file management; reject buildings too close to ground edge
Diffstat:
12 files changed, 280 insertions(+), 201 deletions(-)
diff --git a/README.md b/README.md
@@ -34,7 +34,11 @@ To set them one can use the -D<VAR>=<VAL> pattern. The parameters are:
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.
+ building prevents windows to be generated. Default is 2 meters, must be
+ strictly positive.
+
+- `CG2_MIN_DISTANCE_TO_MAP_LIMITS': The minimum distance between a building and
+ the ground limits. Default is 2 meters, must be strictly positive.
## Release notes
diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt
@@ -23,6 +23,8 @@ option(STL_OUTPUT_DEFAULT_IS_BINARY
"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")
+set(CG2_MIN_DISTANCE_TO_MAP_LIMITS "2" CACHE STRING
+ "Minimum distance from a building to the map limit")
if(CMAKE_HOST_UNIX)
set(CG2_DOC "TROFF" CACHE STRING
diff --git a/src/cg_args.c b/src/cg_args.c
@@ -44,7 +44,7 @@ short_help(void)
{
print_version();
printf("\nUsage:\n"
- "city_generator2 -m <FILENAME> { -c <FILENAME> }+ -[%c] [-s] [-V verbosity] [-k] [-f <NAME>] [-F][-E]\n"
+ "city_generator2 -m <FILENAME> { -c <FILENAME> }+ -[%c] [-s] [-V verbosity] [-k] [-f <NAME>] [-F <level>][-E]\n"
"city_generator2 [-h]\n"
"city_generator2 [-v]\n",
CG2_ARGS_CHANGE_BINARY_DEFAULT_OPTION
@@ -140,7 +140,6 @@ parse_args
case '?': /* Unrecognized option */
{
char* ptr = strchr(option_list, optopt);
- res = RES_BAD_ARG;
if(ptr && ptr[1] == ':') {
logger_print(logger, LOG_ERROR,
"Missing argument for option -%c\n",
@@ -227,7 +226,6 @@ parse_args
logger_print(logger, LOG_ERROR,
"Invalid argument for option -%c: %s\n",
opt, optarg);
- res = RES_BAD_ARG;
goto error;
}
break;
diff --git a/src/cg_building.c b/src/cg_building.c
@@ -138,20 +138,25 @@ build_adjoining
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));
+ ERR(darray_adjoining_data_reserve(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;
+ struct adjoining_data adj;
ASSERT(flags & CLOSE_PROXIMITY); (void)flags;
close_building = *htable_building_iterator_key_get(&it);
- adj = darray_adjoining_data_data_get(adjoining) + prev + i;
- ERR(build_envelop(close_building, &adj->envelop));
- ERR(darray_geometries_push_back(current_cad, &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);
+ if(close_building->event_flags & BUILDING_REMOVED)
+ continue;
+ adjoining_data_init(allocator, &adj);
+ ERR(build_envelop(close_building, &adj.envelop));
+ ERR(darray_geometries_push_back(current_cad, &adj.envelop));
+ ERR(scad_geometry_ref_put(adj.envelop)); /* Ownership transfer */
+ adj.main_building = building;
+ adj.adjoining_building = close_building;
+ ERR(str_append_printf(&msg, (i ? ", '%s'" : " '%s'"),
+ str_cget(&close_building->name)));
+ ERR(darray_adjoining_data_push_back(adjoining, &adj));
i++;
}
diff --git a/src/cg_building.h b/src/cg_building.h
@@ -47,7 +47,8 @@ enum building_event {
BUILDING_NO_EVENT = 0,
BUILDING_WITH_OVERLAPPING = BIT(0),
BUILDING_OUT_OF_GROUND_EXTENT = BIT(1),
- BUILDING_INSIDE_BUILDING = BIT(2),
+ BUILDING_TOO_CLOSE_TO_BORDER = BIT(2),
+ BUILDING_INSIDE_BUILDING = BIT(3),
BUILDING_REMOVED = BIT(5),
BUILDING_WITH_CLOSE_NEIGHBOR = BIT(6),
BUILDING_DUMPED_TO_OBJ = BIT(7),
diff --git a/src/cg_city.c b/src/cg_city.c
@@ -43,31 +43,8 @@
#include <string.h>
-static int
-non_overlap_is_inside
- (struct building* b1,
- struct building* b2)
-{
- size_t count, i;
- double pt[2];
- int situation;
- ASSERT(b1 && b2);
- SCPR(polygon_get_components_count(b1->pg, &count));
- ASSERT(count == 1);
- SCPR(polygon_get_components_count(b2->pg, &count));
- ASSERT(count == 1);
- SCPR(polygon_get_vertices_count(b2->pg, 0, &count));
- for(i = 0; i < count; i++) {
- SCPR(polygon_get_position(b2->pg, 0, i, pt));
- SCPR(point_in_component(b1->pg, 0, pt, &situation));
- if(situation != 0) break;
- /* If vertex is shared test the next one */
- }
- return (situation != -1);
-}
-
/* Unregister building from adjoining information of other buildings */
-static void
+void
unregister_close_building
(struct city* city,
struct building* building)
@@ -77,6 +54,8 @@ unregister_close_building
for(i = 0; i < city->allocated_buildings_count; i++) {
struct building *b = city->buildings+i;
unsigned char *ptr;
+ if(b->event_flags & BUILDING_ALREADY_UNREGISTRED) return;
+ b->event_flags |= BUILDING_ALREADY_UNREGISTRED;
ptr = htable_building_find(&b->close_buildings, &building);
if(!ptr) continue; /* No registered information */
n = htable_building_erase(&b->close_buildings, &building);
@@ -96,7 +75,7 @@ make_b_pair
}
res_T
-dump_obj
+dump_footprint_to_obj
(struct mem_allocator* allocator,
struct building* building,
struct scpr_polygon* alternate_polygon) /* Can be NULL */
@@ -113,7 +92,7 @@ dump_obj
str_init(allocator, &filename);
ERR(str_copy(&filename, &building->name));
- ERR(str_append(&filename, ".obj"));
+ ERR(str_append(&filename, "_footprint.obj"));
stream = fopen(str_cget(&filename), "w");
p = alternate_polygon ? alternate_polygon : building->pg;
ERR(scpr_polygon_dump_to_obj(p, stream));
@@ -151,6 +130,8 @@ int overlapping_segments
struct building* building = ctx->buildings + i;
const struct scpr_polygon* pg =
ctx->alternate_polygons ? ctx->alternate_polygons[i] : building->pg;
+ if(building->event_flags & BUILDING_REMOVED)
+ continue;
if(pg == segment1->polygon) {
building1 = building;
name1 = str_cget(&building->name);
@@ -238,9 +219,10 @@ int simple_intersection
}
/* Dump error polygons in OBJ files */
if(ctx->dump_footprints_level == 1) {
- ERR(dump_obj(allocator, building1, NULL));
+ ERR(dump_footprint_to_obj(allocator, building1, NULL));
}
- ERR(darray_pbuilding_push_back(&ctx->city->removed_buildings, &building1));
+ building1->event_flags |= BUILDING_REMOVED;
+ unregister_close_building(ctx->city, building1);
break;
case CLOSE_PROXIMITY:
break;
@@ -262,16 +244,17 @@ int simple_intersection
}
/* Dump error polygons in OBJ files */
if(ctx->dump_footprints_level == 1) {
- ERR(dump_obj(allocator, building1, NULL));
- ERR(dump_obj(allocator, building2, NULL));
+ ERR(dump_footprint_to_obj(allocator, building1, NULL));
+ ERR(dump_footprint_to_obj(allocator, building2, NULL));
}
- ERR(darray_pbuilding_push_back(&ctx->city->removed_buildings, &building1));
- ERR(darray_pbuilding_push_back(&ctx->city->removed_buildings, &building2));
+ building1->event_flags |= BUILDING_REMOVED;
+ building2->event_flags |= BUILDING_REMOVED;
+ unregister_close_building(ctx->city, building1);
+ unregister_close_building(ctx->city, building2);
break;
case CLOSE_PROXIMITY:
logger_print(logger, LOG_OUTPUT,
- "Buildings '%s' and '%s' are in close proximity.\n",
- name1, name2);
+ "Buildings '%s' and '%s' are in close proximity.\n", name1, name2);
break;
default: FATAL("Invalid type.");
}
@@ -354,7 +337,6 @@ create_city
city->dump_footprints_level = args->dump_footprints_level;
htable_names_init(allocator, &city->dump_footprint_names);
htable_common_init(allocator, &city->common);
- darray_pbuilding_init(allocator, &city->removed_buildings);
ground_init(allocator, &city->ground);
city->array_and_tables_initialized = 1;
/* Some specific building footprints will be dumped (command line request) */
@@ -397,14 +379,18 @@ create_city
|| (tmp_res != RES_OK && city->dump_footprints_level == 1)
|| city->dump_footprints_level == 2;
if(dump) {
- ERR(dump_obj(allocator, building, NULL));
+ ERR(dump_footprint_to_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));
- ERR(darray_pbuilding_push_back(&city->removed_buildings, &building));
+ unregister_close_building(city, building);
+ building->event_flags |= BUILDING_REMOVED;
+ /* Insert a NULL polygon to preserve indexes */
+ tmp_polygon = NULL;
+ ERR(darray_polygons_push_back(&offset_polygons, &tmp_polygon));
continue;
}
res = tmp_res;
@@ -438,7 +424,7 @@ create_city
/* Check for polygons overlapping */
ctx.city = city;
ctx.buildings = city->buildings;
- ctx.buildings_count = city->initialized_buildings_count;
+ ctx.buildings_count = city->allocated_buildings_count;
ctx.intersection_found = &error_occured;
ctx.search_type = OVERLAPPING_PROXIMITY;
ctx.dump_footprints_level = city->dump_footprints_level;
@@ -460,12 +446,14 @@ create_city
if(b1->event_flags & BUILDING_REMOVED) continue;
for(j = 0; j < i; j++) {
struct building* b2 = city->buildings + j;
- int in = 0;
+ int in1, in2;
if((b2->event_flags & BUILDING_REMOVED)) continue;
- /* b1 and b2 and 2 different valid buildings */
- if(non_overlap_is_inside(b1, b2)) {
- ASSERT(!non_overlap_is_inside(b2, b1));
- ERR(darray_pbuilding_push_back(&city->removed_buildings, &b2));
+ /* b1 and b2 are 2 different valid buildings */
+ ERR(scpr_is_component_in_component(b1->pg, 0, b2->pg, 0, &in1));
+ ERR(scpr_is_component_in_component(b2->pg, 0, b1->pg, 0, &in2));
+ ASSERT(!in1 || !in2);
+ if(in2) {
+ unregister_close_building(city, b2);
b2->event_flags |= BUILDING_INSIDE_BUILDING | BUILDING_REMOVED;
if(city->keep_running_on_errors) {
logger_print(logger, LOG_WARNING,
@@ -480,8 +468,8 @@ create_city
str_cget(&b2->name), str_cget(&b1->name));
}
}
- else if(non_overlap_is_inside(b2, b1)) {
- ERR(darray_pbuilding_push_back(&city->removed_buildings, &b1));
+ else if(in1) {
+ unregister_close_building(city, b1);
b1->event_flags |= BUILDING_INSIDE_BUILDING | BUILDING_REMOVED;
if(city->keep_running_on_errors) {
logger_print(logger, LOG_WARNING,
@@ -496,20 +484,12 @@ create_city
str_cget(&b1->name), str_cget(&b2->name));
}
}
- if(in && !city->keep_running_on_errors) {
+ if((in1 || in2) && !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];
- if(b->event_flags & BUILDING_ALREADY_UNREGISTRED)
- continue; /* Do not unregister twice! */
- b->event_flags |= BUILDING_ALREADY_UNREGISTRED;
- unregister_close_building(city, b);
- }
/* Check for polygons proximity */
ctx.search_type = CLOSE_PROXIMITY;
@@ -525,17 +505,24 @@ exit:
if(close_intersector) SCPR(intersector_ref_put(close_intersector));
htable_names_release(&names);
*out_city = city;
- time_sub(&dt, time_current(&dt), &t0);
- time_dump(&dt, TIME_SEC | TIME_MSEC, NULL, buf, sizeof(buf));
- logger_print(city->logger, LOG_OUTPUT, "Map implantation analyzed in %s.\n", buf);
+ if(res == RES_OK && city) {
+ time_sub(&dt, time_current(&dt), &t0);
+ time_dump(&dt, TIME_SEC | TIME_MSEC, NULL, buf, sizeof(buf));
+ logger_print(city->logger, LOG_OUTPUT,
+ "Map implantation analyzed in %s.\n", buf);
+ }
return res;
error:
+ time_sub(&dt, time_current(&dt), &t0);
+ time_dump(&dt, TIME_SEC | TIME_MSEC, NULL, buf, sizeof(buf));
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");
}
+ logger_print(city->logger, LOG_ERROR,
+ "Map implantation canceled after %s.\n", buf);
CHK(RES_OK == release_city(city));
city = NULL;
goto exit;
@@ -556,7 +543,6 @@ release_city(struct city* city)
if(city->array_and_tables_initialized) {
htable_common_release(&city->common);
htable_names_release(&city->dump_footprint_names);
- darray_pbuilding_release(&city->removed_buildings);
}
/* iterate on building */
for (i = 0; i < city->allocated_buildings_count; i++) {
@@ -574,6 +560,8 @@ error:
goto exit;
}
+#define ERR1(Expr) if((tmp_res = (Expr)) != RES_OK) goto error1; else (void)0
+
res_T
city_cad_build(struct city* city)
{
@@ -586,17 +574,19 @@ city_cad_build(struct city* city)
struct darray_double common;
void* cad = NULL;
struct darray_geometries current_cad;
+ struct mem_allocator* allocator;
struct time t0, dt;
char buf[128];
ASSERT(city);
- darray_geometries_init(city->allocator, ¤t_cad);
- darray_adjoining_data_init(city->allocator, &adjoining_data);
- darray_double_init(city->allocator, &common);
+ allocator = city->allocator;
+ darray_geometries_init(allocator, ¤t_cad);
+ darray_adjoining_data_init(allocator, &adjoining_data);
+ darray_double_init(allocator, &common);
/* Initialize star-cad */
- ERR(scad_initialize(city->logger, city->allocator, city->verbosisty_level));
+ ERR(scad_initialize(city->logger, allocator, city->verbosisty_level));
scad_initialized = 1;
options.Mesh.MeshSizeFromPoints = 0;
#ifdef NDEBUG
@@ -606,97 +596,121 @@ city_cad_build(struct city* city)
#endif
ERR(scad_set_options(&options));
- /* iterate on buildings */
+ /* Iterate on buildings
+ * If a building fails and keep_running is set, try hard to continue with
+ * remaining buildings. */
for(i = 0; i < city->allocated_buildings_count; i++) {
+ struct adjoining_data* adj;
+ res_T tmp_res;
building = city->buildings + i;
if(city->dump_footprints_level == 2) {
- ERR(dump_obj(city->allocator, building, NULL));
+ ERR1(dump_footprint_to_obj(allocator, building, NULL));
}
if(building->event_flags & BUILDING_REMOVED) {
+ continue;
+ }
+
+ /* create building CAD */
+ time_current(&t0);
+ logger_print(city->logger, LOG_OUTPUT,
+ "Start processing building '%s'.\n", str_cget(&building->name));
+ darray_geometries_clear(¤t_cad);
+ tmp_res = building->functors->build_cad(building, city->dump_footprints_level,
+ city->keep_running_on_errors, &adjoining_data, ¤t_cad,
+ (void**)&cad);
+ if(tmp_res != RES_OK) {
if(city->dump_footprints_level == 1) {
- ERR(dump_obj(city->allocator, building, NULL));
+ ERR1(dump_footprint_to_obj(allocator, building, NULL));
}
- } else {
- /* create building CAD */
- struct adjoining_data* adj;
- time_current(&t0);
- logger_print(city->logger, LOG_OUTPUT,
- "Start processing building '%s'.\n", str_cget(&building->name));
- darray_geometries_clear(¤t_cad);
- res = building->functors->build_cad(building, city->dump_footprints_level,
- city->keep_running_on_errors, &adjoining_data, ¤t_cad,
- (void**)&cad);
- if(res != RES_OK) {
- if(city->dump_footprints_level == 1) {
- ERR(dump_obj(city->allocator, building, NULL));
- }
- time_sub(&dt, time_current(&dt), &t0);
- time_dump(&dt, TIME_SEC | TIME_MSEC, NULL, buf, sizeof(buf));
- logger_print(city->logger, LOG_OUTPUT,
- "Building '%s': stopped after %s.\n", str_cget(&building->name), buf);
- 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);
- darray_adjoining_data_clear(&adjoining_data);
- /* 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;
- }
+ time_sub(&dt, time_current(&dt), &t0);
+ time_dump(&dt, TIME_SEC | TIME_MSEC, NULL, buf, sizeof(buf));
+ if(city->keep_running_on_errors) {
+ logger_print(city->logger, LOG_WARNING,
+ "Building '%s' will not be part of the output (aborted after %s).\n",
+ str_cget(&building->name), buf);
+ /* Unregister building from adjoining information of other buildings */
+ unregister_close_building(city, building);
+ darray_adjoining_data_clear(&adjoining_data);
+ /* 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. */
+ continue;
+ } else {
+ logger_print(city->logger, LOG_ERROR,
+ "Building '%s': stopped after %s.\n",
+ str_cget(&building->name), buf);
+ }
+ }
+ ERR1(tmp_res);
+ ERR1(scad_scene_mesh());
+ /* 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;
+ ERR1(scad_geometry_get_count(adj[a].common_geometry, &c));
+ if(c == 0) {
+ /* Not really adjoining */
+ continue;
}
- ERR(res);
- ERR(scad_scene_mesh());
+ 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(&common);
+ ERR1(scad_stl_get_data(adj[a].common_geometry, &common));
+ make_b_pair(&pair, building, adj[a].adjoining_building);
+ ASSERT(darray_double_size_get(&common) > 0
+ && darray_double_size_get(&common) % 9 == 0);
+ ERR1(htable_common_set(&city->common, &pair, &common));
+ }
+ tmp_res = building->functors->export_stl(cad, city->binary_export);
+ if(tmp_res == RES_OK) {
/* Keep ground/building's boundary triangles now as the geometry will be
* released before ground is built */
- ERR(building->functors->save_ground_connection_triangles(cad,
- &city->ground.ground_trg));
- /* 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[a].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(&common);
- ERR(scad_stl_get_data(adj[a].common_geometry, &common));
- make_b_pair(&pair, building, adj[a].adjoining_building);
- ASSERT(darray_double_size_get(&common) > 0
- && darray_double_size_get(&common) % 9 == 0);
- ERR(htable_common_set(&city->common, &pair, &common));
- }
- ERR(building->functors->export_stl(cad, city->binary_export));
+ ERR1(building->functors->save_ground_connection_triangles(cad,
+ &city->ground.ground_trg));
building->event_flags |= BUILDING_CAD_EXPORTED_TO_STL;
+ generated_buildings_count++;
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 */
+ }
+ else if(city->keep_running_on_errors) {
+ logger_print(city->logger, LOG_ERROR,
+ "Some STL files could be missing for building '%s'.\n",
+ str_cget(&building->name));
+ logger_print(city->logger, LOG_ERROR,
+ "You may want to delete any generated STL file for this building "
+ "after inspecting them.\n");
+ }
+error1:
+ /* Cannot keep any geometry from one building to the other */
+ if(cad) {
ERR(building->functors->release_cad(cad));
cad = NULL; /* Avoid double release */
- darray_adjoining_data_clear(&adjoining_data);
- darray_geometries_clear(¤t_cad);
- ERR(scad_scene_clear());
- generated_buildings_count++;
- time_sub(&dt, time_current(&dt), &t0);
- time_dump(&dt, TIME_SEC | TIME_MSEC, NULL, buf, sizeof(buf));
+ }
+ darray_adjoining_data_clear(&adjoining_data);
+ darray_geometries_clear(¤t_cad);
+ ERR(scad_scene_clear());
+ time_sub(&dt, time_current(&dt), &t0);
+ time_dump(&dt, TIME_SEC | TIME_MSEC, NULL, buf, sizeof(buf));
+ if(tmp_res == RES_OK) {
logger_print(city->logger, LOG_OUTPUT,
"Building '%s': done in %s.\n", str_cget(&building->name), buf);
}
+ else if(city->keep_running_on_errors) {
+ logger_print(city->logger, LOG_ERROR,
+ "Building '%s' canceled after %s.\n", str_cget(&building->name), buf);
+ }
+ if(!city->keep_running_on_errors) {
+ ERR(tmp_res);
+ }
}
building = NULL;
city->cad_generated_buildings_count = generated_buildings_count;
@@ -710,11 +724,15 @@ exit:
return res;
error:
if(building) {
+ time_sub(&dt, time_current(&dt), &t0);
+ time_dump(&dt, TIME_SEC | TIME_MSEC, NULL, buf, sizeof(buf));
logger_print(city->logger, LOG_ERROR,
- "Error generating CAD for building '%s'.\n", str_cget(&building->name));
+ "Error generating CAD for building '%s'; canceled after %s.\n",
+ str_cget(&building->name), buf);
}
goto exit;
}
+#undef ERR1
res_T
city_ground_build(struct city* city)
@@ -765,18 +783,25 @@ exit:
ground_clear(&city->ground);
if(scad_initialized) SCAD(finalize());
time_sub(&dt, time_current(&dt), &t0);
- time_dump(&dt, TIME_SEC | TIME_MSEC, NULL, buf, sizeof(buf));
- logger_print(city->logger, LOG_OUTPUT, "Ground done in %s.\n", buf);
+ if(res == RES_OK) {
+ time_dump(&dt, TIME_SEC | TIME_MIN, NULL, buf, sizeof(buf));
+ logger_print(city->logger, LOG_OUTPUT, "Ground done in %s.\n", buf);
+ }
return res;
error:
+ time_sub(&dt, time_current(&dt), &t0);
+ time_dump(&dt, TIME_SEC | TIME_MSEC, NULL, buf, sizeof(buf));
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");
+ logger_print(city->logger, LOG_ERROR, "Error generating ground.\n");
+ } else {
+ logger_print(city->logger, LOG_ERROR, "Unknown error generating ground.\n");
}
+ logger_print(city->logger, LOG_ERROR,
+ "Ground processing aborted after %s.\n", buf);
goto exit;
}
diff --git a/src/cg_city.h b/src/cg_city.h
@@ -56,7 +56,7 @@ 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));
+ if(*src) SCPR(polygon_ref_get(*src));
return RES_OK;
}
static FINLINE res_T
@@ -131,7 +131,6 @@ struct city {
initialized_buildings_count;
struct htable_names dump_footprint_names;
struct htable_common common;
- struct darray_pbuilding removed_buildings;
struct mem_allocator* allocator;
struct logger* logger;
struct scpr_device* scpr;
@@ -142,6 +141,11 @@ struct city {
int array_and_tables_initialized;
};
+void
+unregister_close_building
+ (struct city* city,
+ struct building* building);
+
res_T
create_city
(struct mem_allocator* allocator,
@@ -163,7 +167,7 @@ res_T
release_city(struct city* city);
res_T
-dump_obj
+dump_footprint_to_obj
(struct mem_allocator* allocator,
struct building* building,
struct scpr_polygon* alternate_polygon); /* Can be NULL */
diff --git a/src/cg_construction_mode.c b/src/cg_construction_mode.c
@@ -18,7 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "cg.h"
-#include "cg_types.h"
+#include "cg_default.h"
#include "cg_building.h"
#include "cg_city.h"
#include "cg_city_parsing_schemas.h"
@@ -27,6 +27,8 @@
#include <rsys/rsys.h>
#include <rsys/str.h>
#include <rsys/logger.h>
+#include <rsys/double2.h>
+
#include <star/scad.h>
#include <star/scpr.h>
@@ -51,15 +53,14 @@ res_T
init_building_base
(struct building* building,
struct city* city,
- struct parsed_city_building* parsed_data,
- const double lower[2],
- const double upper[2])
+ struct parsed_city_building* parsed_data)
{
int inside, cw;
size_t count;
+ double l[2], u[2], t[2];
res_T res = RES_OK;
- ASSERT(city && building && parsed_data && lower && upper);
+ ASSERT(city && building && parsed_data);
building->city = city;
building->height = parsed_data->height;
@@ -76,17 +77,26 @@ init_building_base
"Building '%s' had quasi-identical vertices that have been merged.\n",
str_cget(&building->name));
}
- ERR(scpr_polygon_in_bbox(building->pg, lower, upper, &inside));
+ /* First try with a slightly decreased bbox as buildings are not allowed to
+ * reach the bbox limit */
+ d2(t, CG2_MIN_DISTANCE_TO_MAP_LIMITS, CG2_MIN_DISTANCE_TO_MAP_LIMITS);
+ d2_add(l, city->lower, t);
+ d2_sub(u, city->upper, t);
+ ERR(scpr_polygon_in_bbox(building->pg, l, u, &inside));
if(!inside) {
+ ERR(scpr_polygon_in_bbox(building->pg, city->lower, city->upper, &inside));
logger_print(city->logger,
(city->keep_running_on_errors ? LOG_WARNING : LOG_ERROR),
- "Building '%s' is out of the ground extent.\n",
+ (inside
+ ? "Building '%s' is too close to the map limits.\n"
+ : "Building '%s' is out of the ground extent.\n"),
str_cget(&building->name));
- building->event_flags |= BUILDING_OUT_OF_GROUND_EXTENT | BUILDING_REMOVED;
+ building->event_flags
+ |= (inside ? BUILDING_TOO_CLOSE_TO_BORDER : BUILDING_OUT_OF_GROUND_EXTENT);
res = RES_BAD_ARG;
goto error;
}
- /* Force orientation so that pg's normal is downward */
+ /* Force orientation so that pg's normal is upward */
ERR(scpr_polygon_is_component_cw(building->pg, 0, &cw));
if(!cw) {
ERR(scpr_polygon_reverse_component(building->pg, 0));
diff --git a/src/cg_construction_mode.h b/src/cg_construction_mode.h
@@ -50,9 +50,7 @@ res_T
init_building_base
(struct building* building,
struct city* city,
- struct parsed_city_building* parsed_data,
- const double lower[2],
- const double upper[2]);
+ struct parsed_city_building* parsed_data);
res_T
release_building_base
diff --git a/src/cg_construction_mode_0.c b/src/cg_construction_mode_0.c
@@ -439,7 +439,7 @@ init_cmode_0
building->construction_mode = mode_0;
building->functors = &functors_0;
- ERR(init_building_base(building, city, parsed_data, lower, upper));
+ ERR(init_building_base(building, city, parsed_data));
str_init(allocator, &dataset_name);
name_initialized = 1;
@@ -534,7 +534,9 @@ build_cad_cmode_0
ERR(scpr_polygon_get_components_count(pg_int, &c));
if(c != 1) {
logger_print(logger, (keep_running_on_errors ? LOG_WARNING : LOG_ERROR),
- "Building '%s' is too small with respect to wall thickness.\n", name);
+ "Building '%s' shape is not compatible with wall thickness.\n", name);
+ building->event_flags |= BUILDING_REMOVED;
+ unregister_close_building(building->city, building);
error_msg_printed = 1;
res = RES_BAD_ARG;
goto error;
@@ -553,8 +555,9 @@ build_cad_cmode_0
ERR(scpr_intersector_check(overlapping_intersector, &callbacks, &ctx));
if(error_occured) {
logger_print(logger, LOG_ERROR,
- "Internal error building CAD for building '%s'.\n", name);
- ERR(darray_pbuilding_push_back(&building->city->removed_buildings, &building));
+ "Internal error generating CAD for building '%s'.\n", name);
+ building->event_flags |= BUILDING_REMOVED;
+ unregister_close_building(ctx.city, building);
error_msg_printed = 1;
res = RES_BAD_ARG;
goto error;
@@ -576,7 +579,6 @@ build_cad_cmode_0
adjoining_n = htable_building_size_get(&building->close_buildings);
if (adjoining_n > 0) {
ERR(build_adjoining(building, current_cad, adjoining_data));
- ASSERT(adjoining_n == darray_adjoining_data_size_get(adjoining_data));
}
/* build fake ground */
@@ -597,6 +599,7 @@ build_cad_cmode_0
ERR(scad_geometries_swap(cur_cad, partitioned, cad_count, Scad_swap_geometry));
/* After partitioning, manage common parts with other buildings */
+ adjoining_n = darray_adjoining_data_size_get(adjoining_data);
if(adjoining_n > 0) {
size_t a;
struct b_pair pair;
@@ -654,7 +657,7 @@ exit:
error:
if(logger && building && !error_msg_printed) {
logger_print(logger, LOG_ERROR,
- "Unknown error building CAD for building '%s'.\n",
+ "Unknown error generating CAD for building '%s'.\n",
str_cget(&building->name));
}
if(data_cad) CHK(RES_OK == release_cad_cmode_0(data_cad));
@@ -722,12 +725,6 @@ export_stl_cmode_0
allocator = data_cad->building->city->allocator;
- /* floor export */
- ERR(scad_stl_export(data_cad->floor, NULL, Scad_force_normals_outward, binary));
-
- /* roof export */
- ERR(scad_stl_export(data_cad->roof, NULL, Scad_force_normals_outward, binary));
-
/* wall export */
if(darray_geometries_size_get(&data_cad->adj_walls) == 0) {
ERR(scad_stl_export(data_cad->wall, NULL, Scad_force_normals_outward, binary));
@@ -778,6 +775,12 @@ export_stl_cmode_0
binary));
}
+ /* floor export */
+ ERR(scad_stl_export(data_cad->floor, NULL, Scad_force_normals_outward, binary));
+
+ /* roof export */
+ ERR(scad_stl_export(data_cad->roof, NULL, Scad_force_normals_outward, binary));
+
/* cavity export */
ERR(scad_stl_export(data_cad->cavity, NULL, Scad_force_normals_outward, binary));
@@ -804,6 +807,11 @@ exit:
}
return res;
error:
+ if(data_cad) {
+ logger_print(data_cad->building->city->logger, LOG_ERROR,
+ "Internal error '"__FILE__"': creating STL for building '%s'.\n",
+ str_cget(&data_cad->building->name));
+ }
goto exit;
}
diff --git a/src/cg_construction_mode_1.c b/src/cg_construction_mode_1.c
@@ -88,8 +88,10 @@ do_offset
ASSERT(offset < 0);
logger_print(building->city->logger,
(building->city->keep_running_on_errors ? LOG_WARNING : LOG_ERROR),
- "Building '%s' is too small with respect to wall thickness.\n",
+ "Building '%s' shape is not compatible with wall thickness.\n",
str_cget(&building->name));
+ building->event_flags |= BUILDING_REMOVED;
+ unregister_close_building(building->city, building);
*error_msg_printed = 1;
res = RES_BAD_ARG;
goto error;
@@ -905,7 +907,7 @@ build_windows
/* Check if the window intersects an unexpected wall of the building:
* - the window is too large wrt of external size of the wall (the window's
* size is a % of the internal size of the wall that can be larger than
- * the external size due to angles).
+ * the external size due to corners).
* - another wall facing the current one at a too close distance (can be the
* prev/next with sharp angle, or not),
* - a tiny unwanted wall created by noise at the polygon level (mainly due
@@ -1455,7 +1457,7 @@ init_cmode_1
building->construction_mode = mode_1;
building->functors = &functors_1;
- ERR(init_building_base(building, city, parsed_data, lower, upper));
+ ERR(init_building_base(building, city, parsed_data));
str_init(allocator, &dataset_name);
name_initialized = 1;
@@ -1511,7 +1513,7 @@ build_cad_cmode_1
double zero = 0;
struct mem_allocator* allocator;
struct logger* logger = NULL;
- size_t i, cad_count;
+ size_t i, cad_count = 0;
struct scad_geometry** cur_cad = NULL;
struct scad_geometry** partitioned = NULL;
@@ -1634,9 +1636,10 @@ build_cad_cmode_1
ERR(scpr_intersector_check(overlapping_intersector, &callbacks, &ctx));
if(error_occured) {
logger_print(logger, LOG_ERROR,
- "Internal error building CAD for building '%s'.\n",
+ "Internal error generating CAD for building '%s'.\n",
name);
- ERR(darray_pbuilding_push_back(&building->city->removed_buildings, &building));
+ building->event_flags |= BUILDING_REMOVED;
+ unregister_close_building(ctx.city, building);
error_msg_printed = 1;
res = RES_BAD_ARG;
goto error;
@@ -1646,7 +1649,6 @@ build_cad_cmode_1
adjoining_n = htable_building_size_get(&building->close_buildings);
if (adjoining_n > 0) {
ERR(build_adjoining(building, current_cad, adjoining_data));
- ASSERT(adjoining_n == darray_adjoining_data_size_get(adjoining_data));
}
/* windows */
@@ -1671,8 +1673,16 @@ build_cad_cmode_1
/* Swap original geometry and partitioned geometry in data_cad (was
* accumulated into current_cad) */
ERR(scad_geometries_swap(cur_cad, partitioned, cad_count, Scad_swap_geometry));
+ for(i = 0; i < cad_count; i++) {
+ if(partitioned[i]) {
+ ERR(scad_geometry_ref_put(partitioned[i]));
+ }
+ }
+ MEM_RM(allocator, partitioned);
+ partitioned = NULL;
/* After partitioning, manage common parts with other buildings */
+ adjoining_n = darray_adjoining_data_size_get(adjoining_data);
if(adjoining_n > 0) {
size_t a, c;
struct b_pair pair;
@@ -1731,7 +1741,7 @@ exit:
error:
if(logger && building && !error_msg_printed) {
logger_print(logger, LOG_ERROR,
- "Unknown error building CAD for building '%s'.\n",
+ "Unknown error generating CAD for building '%s'.\n",
str_cget(&building->name));
}
if(data_cad) CHK(RES_OK == release_cad_cmode_1(data_cad));
@@ -1800,25 +1810,33 @@ export_stl_cmode_1
allocator = data_cad->building->city->allocator;
- /* floor export */
- ERR(scad_stl_export(data_cad->floor, NULL, Scad_force_normals_outward, binary));
-
- /* roof export */
- ERR(scad_stl_export(data_cad->roof, NULL, Scad_force_normals_outward, binary));
-
- /* wall export */
if(darray_geometries_size_get(&data_cad->adj_walls) == 0) {
+ /* wall export */
ERR(scad_stl_export(data_cad->wall, NULL, Scad_force_normals_outward, binary));
+
+ /* external insulation export */
+ if(data_cad->external_insulation) {
+ ERR(scad_stl_export(data_cad->external_insulation, NULL,
+ Scad_force_normals_outward, binary));
+ }
} else {
/* There is some adjoining building(s) to manage */
+ struct scad_geometry* extern_most = data_cad->external_insulation
+ ? data_cad->external_insulation : data_cad->wall;
size_t common_count = darray_common_trg_size_get(&data_cad->common_trg);
const char* tmp;
+ if(data_cad->external_insulation) {
+ /* wall export is not impacted as walls are not the external layer */
+ ERR(scad_stl_export(data_cad->wall, NULL, Scad_force_normals_outward, binary));
+ }
+
+ /* The external layer must use the common triangles */
darray_double_init(allocator, &trg);
str_init(allocator, &name);
initialized = 1;
/* Get the triangles that are not common with adjoining buildings */
- ERR(scad_stl_get_data_partial(data_cad->wall,
+ ERR(scad_stl_get_data_partial(extern_most,
darray_geometries_data_get(&data_cad->adj_walls),
darray_geometries_size_get(&data_cad->adj_walls), &trg));
coord_count = darray_double_size_get(&trg);
@@ -1849,13 +1867,19 @@ export_stl_cmode_1
coord_count += sz;
ASSERT(coord_count % 9 == 0);
}
- ERR(scad_geometry_get_name(data_cad->wall, &tmp));
+ ERR(scad_geometry_get_name(extern_most, &tmp));
ERR(str_set(&name, tmp));
ERR(str_append(&name, ".stl"));
ERR(scad_stl_data_write(&trg, str_cget(&name), Scad_force_normals_outward,
binary));
}
+ /* floor export */
+ ERR(scad_stl_export(data_cad->floor, NULL, Scad_force_normals_outward, binary));
+
+ /* roof export */
+ ERR(scad_stl_export(data_cad->roof, NULL, Scad_force_normals_outward, binary));
+
/* foundation export */
if (data_cad->foundation) {
ERR(scad_stl_export(data_cad->foundation, NULL, Scad_force_normals_outward,
@@ -1880,12 +1904,6 @@ export_stl_cmode_1
Scad_force_normals_outward, binary));
}
- /* external insulation export*/
- if (data_cad->external_insulation) {
- ERR(scad_stl_export(data_cad->external_insulation, NULL,
- Scad_force_normals_outward, binary));
- }
-
/* roof insulation export*/
if (data_cad->roof_insulation) {
ERR(scad_stl_export(data_cad->roof_insulation, NULL,
@@ -1941,6 +1959,11 @@ exit:
}
return res;
error:
+ if(data_cad) {
+ logger_print(data_cad->building->city->logger, LOG_ERROR,
+ "Internal error creating STL for building '%s'.\n",
+ str_cget(&data_cad->building->name));
+ }
goto exit;
}
diff --git a/src/cg_default.h.in b/src/cg_default.h.in
@@ -22,5 +22,6 @@
#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@
+#define CG2_MIN_DISTANCE_TO_MAP_LIMITS @CG2_MIN_DISTANCE_TO_MAP_LIMITS@
#endif