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:
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], ¢er_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);