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 2606834ffe80150207818df9ee1192113d144859
parent 1a24b187e3a9f0032d591e3f5926cc4f4fc29295
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Wed, 16 Oct 2024 16:17:54 +0200

Modify mode_2 to create multiple windows by wall segment

At this stage the process is guided by #defined parameters (not from
user-defined values).
Also, the current algorithm does not try hard to meet the various
requirements, including the one on windows/wall surface ratio.

Diffstat:
Msrc/cg_construction_mode_2.c | 260++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
1 file changed, 164 insertions(+), 96 deletions(-)

diff --git a/src/cg_construction_mode_2.c b/src/cg_construction_mode_2.c @@ -783,16 +783,18 @@ build_windows struct darray_adjoining_data* adjoining_data) { res_T res = RES_OK; - size_t i, j, adjoining_n, removed_count, removed_hor = 0; + size_t i, j, adjoining_n, removed_count; size_t removed_windows_sz = 0, removed_windows_adj = 0, removed_windows_self = 0; size_t list_n = 0, array_n; const char* prefix; double N[3]; double dir[3]; - double scale[3], scale_[3]; + double scale[3], sc; struct scad_geometry* surface = NULL; struct scad_geometry* win_surface = NULL; struct scad_geometry* surface_ = NULL; + struct scad_geometry* scaled = NULL; + struct scad_geometry* scaled_ = NULL; struct scad_geometry* hole = NULL; struct scad_geometry* detect = NULL; struct scad_geometry** hole_list = NULL; @@ -809,12 +811,11 @@ build_windows struct scad_geometry* benv = NULL; struct scad_geometry* problem = NULL; size_t floor_n; - double e_roof, e_roof_ins, attic, h_wall, e_floor; + double e_roof, e_roof_ins, attic, wall_height, e_floor; struct str name; struct adjoining_data* adj; struct city* city; struct mem_allocator* allocator; - (void)removed_hor; ASSERT(data && data_cad && adjoining_data); @@ -822,6 +823,7 @@ build_windows allocator = city->allocator; adjoining_n = darray_adjoining_data_size_get(adjoining_data); adj = darray_adjoining_data_data_get(adjoining_data); + sc = sqrt(data->glass_ratio); /* The glass fraction of walls' height and width */ darray_geometries_init(allocator, &hole_array); darray_geometries_init(allocator, &glass_array); str_init(allocator, &name); @@ -832,12 +834,9 @@ build_windows e_roof_ins = data->roof_insulation_thickness; attic = data->attic_height; e_floor = data->inter_floor_thickness; - h_wall = (data_cad->building->height- e_roof - attic - e_roof_ins + wall_height = (data_cad->building->height - e_roof - attic - e_roof_ins - (double)floor_n*e_floor) / (double)(floor_n + 1); - d3_splat(scale, sqrt(data->glass_ratio)); - d3_splat(scale_, MMIN(1, 1.05 * sqrt(data->glass_ratio))); - /* windows are build from the vertical faces of habitable cavities */ ERR(scad_geometry_boundary("cavity_boundary", &data_cad->habitable_cavity, 1, &bcavity)); @@ -855,9 +854,9 @@ build_windows ERR(darray_geometries_reserve(&hole_array, list_n)); ERR(darray_geometries_reserve(&glass_array, list_n)); for(i = 0; i < list_n; i++) { - double hsz, mass, center[3]; + double hsz, wall_surface, wall_width, center[3]; size_t center_n; - size_t count; + size_t count, windows_count, n; int removed = 0; ERR(scad_geometry_get_count(list[i], &center_n)); @@ -867,104 +866,165 @@ build_windows ERR(scad_geometry_normal(list[i], center, N, str_cget(&name), &surface)); if(N[2] != 0) { - ERR(scad_geometry_ref_put(surface)); - surface = NULL; - removed_hor++; - continue; /* keep only vertical face */ - } - - ERR(scad_geometry_get_mass(list[i], &mass)); - if(mass < CG2_MIN_WINDOWS_WIDTH * h_wall) { - /* this window would be too small */ - ERR(scad_geometry_ref_put(surface)); - surface = NULL; + /* keep only vertical face */ removed = 1; - removed_windows_sz++; } + d3_normalize(N, N); - if(!removed) { - /* Used to check for validity with a slitghly bigger size */ - ERR(str_printf(&name, "surface+_%lu", (long unsigned)i)); - ERR(scad_geometry_dilate(surface, center, scale_, str_cget(&name), - &surface_)); - } + /* TODO: put it in parameters */ +#define CG2_MODE2_WINDOWS_WIDTH_HINT 1.2 +#define CG2_MODE2_WINDOWS_SPACING_HINT 0.4 +#define CG2_MODE2_WINDOWS_HEIGHT_DEFAULT_RATIO 0.6 - if(!removed && adjoining_n) { - /* Use the same distance used in early stages for close neighbor detection */ - hsz = CG2_CLOSE_NEIGHBOR_DISTANCE + data->wall_thickness + - data->internal_insulation_thickness + data->external_insulation_thickness; - d3_muld(dir, N, hsz); - ERR(str_printf(&name, "detect_adj_%lu", (long unsigned)i)); - ERR(scad_geometry_extrude(surface_, str_cget(&name), dir, &detect)); - - /* Check if detect intersects adjoining envelops */ - /* Push only if don't intersect */ - adj_list = MEM_REALLOC(allocator, adj_list, - adjoining_n * sizeof(struct scad_geometry*)); - for(j = 0; j < adjoining_n; j++) adj_list[j] = adj[j].envelop; - - ERR(str_printf(&name, "adj_intersect_%lu", (long unsigned)i)); - ERR(scad_intersect_geometries(str_cget(&name), &detect, 1, adj_list, - adjoining_n, &hole_adjoining_intersect)); - ERR(scad_geometry_get_count(hole_adjoining_intersect, &count)); - if(count) { + if(!removed) { + ERR(scad_geometry_get_mass(list[i], &wall_surface)); + wall_width = wall_surface / wall_height; + if(wall_width < CG2_MIN_WINDOWS_WIDTH + CG2_MODE2_WINDOWS_SPACING_HINT * 2) { + /* No room for a single minimal window */ removed = 1; - removed_windows_adj++; + removed_windows_sz++; } - ERR(scad_geometry_ref_put(hole_adjoining_intersect)); - hole_adjoining_intersect = NULL; - ERR(scad_geometry_ref_put(detect)); - detect = NULL; } - /* 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 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 - * to the offseting algorithm). */ if(!removed) { - /* Use smaller distance than the one used for neighbor detection */ - hsz = 0.1 + data->wall_thickness + data->internal_insulation_thickness - + data->external_insulation_thickness; - d3_muld(dir, N, hsz); - ERR(str_printf(&name, "detect_self_%lu", (long unsigned)i)); - ERR(scad_geometry_extrude(surface_, str_cget(&name), dir, &detect)); - /* Compute intersection between detect and envelop: the number of - * components is expected to be 1, or the window is better removed */ - ERR(str_printf(&name, "self_intersect_%lu", (long unsigned)i)); - ERR(scad_intersect_geometries(str_cget(&name), &benv, 1, &detect, 1, - &problem)); - ERR(scad_geometry_get_count(problem, &count)); - if(count != 1) { - removed = 1; - removed_windows_self++; + double window_width, window_height, window_spacing; + double windows_total_width_from_ratio, max_window_width, total_spacing; + double sw, sh, wc; + /* Compute a number of windows to fit in this wall and meet the ratio + * target */ + window_height = CG2_MODE2_WINDOWS_HEIGHT_DEFAULT_RATIO * wall_height; + wc = (wall_width - CG2_MODE2_WINDOWS_SPACING_HINT) + / (CG2_MODE2_WINDOWS_SPACING_HINT + CG2_MODE2_WINDOWS_WIDTH_HINT); + windows_count = MMAX(1, (size_t)floor(wc)); + wc = (double)windows_count; + /* Compute the effective windows width */ + windows_total_width_from_ratio = + wall_surface * data->glass_ratio / window_height; + max_window_width = + (wall_width - (wc + 1) * CG2_MODE2_WINDOWS_SPACING_HINT) / wc; + window_width = MMIN(windows_total_width_from_ratio / wc, max_window_width); + ASSERT(window_width > CG2_MIN_WINDOWS_WIDTH); + total_spacing = wall_width - wc * window_width; + window_spacing = total_spacing / (wc + 1); + sw = MMIN(1, 1.01 * window_width / wall_width); + sh = MMIN(1, 1.01 * window_height / wall_height); + d3(scale, sw, sw, sh); + ERR(scad_geometry_dilate(surface, center, scale, "scaled_", &scaled_)); + sw = window_width / wall_width; + sh = window_height / wall_height; + d3(scale, sw, sw, sh); + ERR(scad_geometry_dilate(surface, center, scale, "scaled", &scaled)); + + /* Try to create the planed windows */ + for(n = 0; n < windows_count; n++) { + double dxdydz[3], offset; + /* Compute windows positioning */ + offset = - 0.5 * (wall_width - window_width) /* to the left border */ + + window_spacing + (double)n * (window_spacing + window_width); + ASSERT(offset + window_width < wall_width * 0.5); + dxdydz[0] = -N[1] * offset; + dxdydz[1] = N[0] * offset; + /* Windows are in the upper part of the wall (not vertically centered) */ + dxdydz[2] = (wall_height - window_height) * 0.4 * (1 - sc); + ERR(str_printf(&name, "surface+_%lu_w%lu", + (long unsigned)i, (long unsigned)n)); + /* surface_ is used to check for neighbor compatibility with a slitghly + * bigger size than the actual window */ + ERR(scad_geometry_translate(scaled_, dxdydz, str_cget(&name), &surface_)); + + if(adjoining_n) { + /* Use the same distance used in early stages for close neighbor detection */ + hsz = CG2_CLOSE_NEIGHBOR_DISTANCE + data->wall_thickness + + data->internal_insulation_thickness + data->external_insulation_thickness; + d3_muld(dir, N, hsz); + ERR(str_printf(&name, "detect_adj_%lu_w%lu", + (long unsigned)i, (long unsigned)n)); + ERR(scad_geometry_extrude(surface_, str_cget(&name), dir, &detect)); + + /* Check if detect intersects adjoining envelops */ + /* Push only if don't intersect */ + adj_list = MEM_REALLOC(allocator, adj_list, + adjoining_n * sizeof(struct scad_geometry*)); + for(j = 0; j < adjoining_n; j++) adj_list[j] = adj[j].envelop; + + ERR(str_printf(&name, "adj_intersect_%lu", (long unsigned)i)); + ERR(scad_intersect_geometries(str_cget(&name), &detect, 1, adj_list, + adjoining_n, &hole_adjoining_intersect)); + ERR(scad_geometry_get_count(hole_adjoining_intersect, &count)); + if(count) { + removed = 1; + removed_windows_adj++; + } + ERR(scad_geometry_ref_put(hole_adjoining_intersect)); + hole_adjoining_intersect = NULL; + ERR(scad_geometry_ref_put(detect)); + detect = NULL; + } + + /* 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 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 to the offseting algorithm). */ + if(!removed) { + /* Use smaller distance than the one used for neighbor detection */ + hsz = 0.1 + data->wall_thickness + data->internal_insulation_thickness + + data->external_insulation_thickness; + d3_muld(dir, N, hsz); + ERR(str_printf(&name, "detect_self_%lu", (long unsigned)i)); + ERR(scad_geometry_extrude(surface_, str_cget(&name), dir, &detect)); + /* Compute intersection between detect and envelop: the number of + * components is expected to be 1, or the window is better removed */ + ERR(str_printf(&name, "self_intersect_%lu", (long unsigned)i)); + ERR(scad_intersect_geometries(str_cget(&name), &benv, 1, &detect, 1, + &problem)); + ERR(scad_geometry_get_count(problem, &count)); + if(count != 1) { + removed = 1; + removed_windows_self++; + } + ERR(scad_geometry_ref_put(detect)); + detect = NULL; + ERR(scad_geometry_ref_put(problem)); + problem = NULL; + ERR(scad_geometry_ref_put(surface_)); + surface_ = NULL; + } + + if(!removed) { + ERR(scad_geometry_translate(scaled, dxdydz, "win_surface", &win_surface)); + ERR(str_printf(&name, "hole_%lu_w%lu", (long unsigned)i, (long unsigned)n)); + ERR(scad_geometry_extrude(win_surface, str_cget(&name), dir, &hole)); + ERR(darray_geometries_push_back(&hole_array, &hole)); + d3_muld(dir, N, 0.024); + ERR(str_printf(&name, "glass_%lu_w%lu", (long unsigned)i, (long unsigned)n)); + ERR(scad_geometry_extrude(win_surface, str_cget(&name), dir, &glass)); + ERR(darray_geometries_push_back(&glass_array, &glass)); + ERR(darray_geometries_push_back(current_cad, &glass)); + ERR(scad_geometry_ref_put(hole)); + hole = NULL; + ERR(scad_geometry_ref_put(glass)); + glass = NULL; + ERR(scad_geometry_ref_put(win_surface)); + win_surface = NULL; + } } - ERR(scad_geometry_ref_put(detect)); - detect = NULL; - ERR(scad_geometry_ref_put(problem)); - problem = NULL; + ERR(scad_geometry_ref_put(scaled)); + scaled = NULL; + ERR(scad_geometry_ref_put(scaled_)); + scaled_ = NULL; } - - if(!removed) { - ERR(scad_geometry_dilate(surface, center, scale, NULL, &win_surface)); - ERR(str_printf(&name, "hole_%lu", (long unsigned)i)); - ERR(scad_geometry_extrude(win_surface, str_cget(&name), dir, &hole)); - ERR(darray_geometries_push_back(&hole_array, &hole)); + if(hole) { ERR(scad_geometry_ref_put(hole)); hole = NULL; - d3_muld(dir, N, 0.024); - ERR(str_printf(&name, "glass_%lu", (long unsigned)i)); - ERR(scad_geometry_extrude(win_surface, str_cget(&name), dir, &glass)); - ERR(darray_geometries_push_back(&glass_array, &glass)); - ERR(darray_geometries_push_back(current_cad, &glass)); - ERR(scad_geometry_ref_put(glass)); - glass = NULL; } - ASSERT(hole == NULL); - ASSERT(detect == NULL); + if(detect) { + ERR(scad_geometry_ref_put(detect)); + detect = NULL; + } if(win_surface) { ERR(scad_geometry_ref_put(win_surface)); win_surface = NULL; @@ -973,6 +1033,14 @@ build_windows ERR(scad_geometry_ref_put(surface)); surface = NULL; } + if(scaled) { + ERR(scad_geometry_ref_put(scaled)); + scaled = NULL; + } + if(scaled_) { + ERR(scad_geometry_ref_put(scaled_)); + scaled_ = NULL; + } if(surface_) { ERR(scad_geometry_ref_put(surface_)); surface_ = NULL; @@ -981,7 +1049,6 @@ build_windows removed_count = removed_windows_sz + removed_windows_adj + removed_windows_self; array_n = darray_geometries_size_get(&hole_array); ASSERT(array_n == darray_geometries_size_get(&glass_array)); - ASSERT(array_n + removed_hor + removed_count == list_n); prefix = str_cget(&data_cad->building->name); if(removed_count != 0) { @@ -1053,6 +1120,7 @@ exit: if(problem) SCAD(geometry_ref_put(problem)); if(hole) SCAD(geometry_ref_put(hole)); if(detect) SCAD(geometry_ref_put(detect)); + if(surface_) SCAD(geometry_ref_put(surface_)); if(glass) SCAD(geometry_ref_put(glass)); if(geom) SCAD(geometry_ref_put(geom)); if(bcavity) SCAD(geometry_ref_put(bcavity)); @@ -1330,7 +1398,7 @@ build_fake_ground struct scad_geometry* footprint = NULL; struct scad_geometry* geom = NULL; - ASSERT(cad && ground ); + ASSERT(cad && ground); darray_geometries_init(cad->building->city->allocator, &array);