Now we do not rely on small offset to keep using inner curves to create linings, if some part of the lining is not present (which happens on mullions and transoms) we'll create U or L shape extrusion instead of rectangle with inner curve.

Long story short - this should keep ifc for windows valid.
This commit is contained in:
Andrej730
2023-06-28 15:48:21 +05:00
parent e3b77613d3
commit 8d18a8a023
2 changed files with 123 additions and 58 deletions
@@ -262,7 +262,7 @@ def create_bm_window(
frame_thickness,
glass_thickness,
position: Vector,
x_offsets: list = None
x_offsets: list = None,
):
"""`lining_thickness` and `x_offsets` are expected to be defined as a list,
similarly to `create_bm_window_frame` `thickness` argument"""
@@ -334,7 +334,7 @@ def update_window_modifier_bmesh(context):
# detect transom
has_transom = unique_rows_in_col[column_i] > 1
first_row = row_i == 0
last_row = row_i == unique_rows_in_col[column_i] - 1
last_row = row_i == unique_rows_in_col[column_i] - 1
top_to_transom = has_transom and not first_row
bottom_to_transom = has_transom and not last_row
@@ -366,6 +366,7 @@ def update_window_modifier_bmesh(context):
frame_depth = props.frame_depth[panel_i]
frame_thickness = props.frame_thickness[panel_i]
lining_to_panel_offset_y_full = (lining_depth - frame_depth) + lining_to_panel_offset_y
# add window
window_lining_size = V(
panel_width,
@@ -375,6 +376,7 @@ def update_window_modifier_bmesh(context):
# calculate lining thickness and frame size / offset
# taking into account mullions and transoms
# fmt: off
window_lining_thickness = [
mullion_thickness if right_to_mullion else lining_thickness,
transom_thickness if bottom_to_transom else lining_thickness,
@@ -382,23 +384,23 @@ def update_window_modifier_bmesh(context):
transom_thickness if top_to_transom else lining_thickness,
]
lining_to_panel_offset_y_full = (lining_depth - frame_depth) + lining_to_panel_offset_y
# x offsets can differ if there are mullions or transoms because we're trying to maintain symmetry
# x offsets can differ if there are mullions or transoms because we're trying to maintain symmetry
base_frame_clear = lining_to_panel_offset_x + frame_thickness - lining_thickness
current_offset_x = base_frame_clear - frame_thickness + mullion_thickness
current_offset_z = base_frame_clear - frame_thickness + transom_thickness
# fmt: off
x_offsets = [
current_offset_x if right_to_mullion else lining_to_panel_offset_x, # LEFT
current_offset_z if bottom_to_transom else lining_to_panel_offset_x, # TOP
current_offset_x if left_to_mullion else lining_to_panel_offset_x, # RIGHT
current_offset_z if top_to_transom else lining_to_panel_offset_x, # BOTTOM
current_offset_x if right_to_mullion else lining_to_panel_offset_x, # LEFT
current_offset_z if bottom_to_transom else lining_to_panel_offset_x, # TOP
current_offset_x if left_to_mullion else lining_to_panel_offset_x, # RIGHT
current_offset_z if top_to_transom else lining_to_panel_offset_x, # BOTTOM
]
# fmt: on
frame_size = window_lining_size.copy()
frame_size.y = frame_depth
frame_size.x -= (x_offsets[0] + x_offsets[2])
frame_size.z -= (x_offsets[1] + x_offsets[3])
frame_size.x -= x_offsets[0] + x_offsets[2]
frame_size.z -= x_offsets[1] + x_offsets[3]
window_position = V(accumulated_width, 0, accumulated_height[column_i])
lining_verts, panel_verts, glass_verts = create_bm_window(
@@ -411,7 +413,7 @@ def update_window_modifier_bmesh(context):
frame_thickness,
glass_thickness,
window_position,
x_offsets
x_offsets,
)
built_panels.append(panel_i)
@@ -42,7 +42,9 @@ DEFAULT_PANEL_SCHEMAS = {
}
def create_ifc_window_frame_simple(builder, size: Vector, thickness: list, position: Vector = V(0, 0, 0).freeze()):
def create_ifc_window_frame_simple(
builder: ShapeBuilder, size: Vector, thickness: list, position: Vector = V(0, 0, 0).freeze()
):
"""`thickness` of the profile is defined as list in the following order:
`(LEFT, TOP, RIGHT, BOTTOM)`
@@ -51,24 +53,91 @@ def create_ifc_window_frame_simple(builder, size: Vector, thickness: list, posit
if not isinstance(thickness, collections.abc.Iterable):
thickness = [thickness] * 4
th_left, th_up, th_right, th_bottom = thickness
panel_rect = builder.rectangle(size=size.xz)
def get_extruded_profile(profile):
return builder.extrude(
profile,
size.y,
position_x_axis=V(1, 0, 0),
position_z_axis=V(0, -1, 0),
extrusion_vector=V(0, 0, -1),
position=position,
)
inner_rect_size = size - V(th_left + th_right, 0, th_bottom + th_up)
inner_rect = builder.rectangle(size=inner_rect_size.xz, position=V(th_left, th_bottom))
# if all lining sides are present then we can just use two rectangles
# as inner and outer curves of the profile
if thickness.count(0) == 0:
panel_rect = builder.rectangle(size=size.xz)
panel_profile = builder.profile(panel_rect, inner_curves=inner_rect)
panel_extruded = builder.extrude(
panel_profile,
size.y,
position_x_axis=V(1, 0, 0),
position_z_axis=V(0, -1, 0),
extrusion_vector=V(0, 0, -1),
position=position,
)
return panel_extruded
inner_rect_size = size - V(th_left + th_right, 0, th_bottom + th_up)
inner_rect = builder.rectangle(size=inner_rect_size.xz, position=V(th_left, th_bottom))
panel_profile = builder.profile(panel_rect, inner_curves=inner_rect)
return [get_extruded_profile(panel_profile)]
# if some side has zero thickness it means we cannot use inner curves
# and need to generate L/U shape or just separate rectangles
else:
def get_segments_from_thickness():
nonlocal thickness
segments = []
cur_segment = []
for i, thickness in enumerate(thickness):
if thickness == 0:
if cur_segment:
segments.append(tuple(cur_segment))
cur_segment = []
else:
cur_segment.append(i)
if cur_segment:
if len(segments) > 0 and segments[0][0] == 0:
segments[0] = tuple(cur_segment) + segments[0]
else:
segments.append(tuple(cur_segment))
return segments
# prepare coords to build a lining
# fmt: off
outer_coords = [
(V(0, 0), V(0, size.z)),
(V(0, size.z), V(size.x, size.z)),
(V(size.x, size.z), V(size.x, 0)),
(V(size.x, 0), V(0, 0)),
]
inner_coords = [
(V(th_left, th_bottom), V(th_left, size.z - th_up)),
(V(th_left, size.z - th_up), V(size.x - th_right, size.z - th_up)),
(V(size.x - th_right, size.z - th_up), V(size.x - th_right, th_bottom)),
(V(size.x - th_right, th_bottom), V(th_left, th_bottom)),
]
# fmt: on
def get_points(segment):
points = []
for side in segment:
outer = outer_coords[side]
if side == segment[0]: # first segment
points.append(outer[0])
points.append(outer[1])
for side in reversed(segment):
inner = inner_coords[side]
if side == segment[-1]: # last non zero segment
points.append(inner[1])
points.append(inner[0])
return points
segments = get_segments_from_thickness()
segments_items = []
for seg in segments:
polyline = builder.polyline(points=get_points(seg), closed=True)
panel_profile = builder.profile(polyline)
segments_items.append(get_extruded_profile(panel_profile))
return segments_items
def window_l_shape_check(
@@ -95,7 +164,6 @@ def create_ifc_window(
frame_thickness,
glass_thickness,
position: Vector,
unit_scale, # different from bmesh `create_bm_window`
x_offsets: list = None,
):
"""`lining_thickness` and `x_offsets` are expected to be defined as a list,
@@ -121,20 +189,15 @@ def create_ifc_window(
second_lining_size = lining_size.copy()
second_lining_size.y = lining_size.y - lining_to_panel_offset_y_full
second_lining_position = V(0, lining_to_panel_offset_y_full, 0)
second_lining_thickness = [min(th, x_offset) for th, x_offset in zip(lining_thickness, x_offsets, strict=True)]
# we're using some safe thickness so thickness won't end = 0
# resulting in errors `create_ifc_window_frame_simple`
# because it's using inner curves to create lining
safe_thickness = 0.00001 / unit_scale
second_lining_thickness = [max(min(th, x_offset), safe_thickness) for th, x_offset in zip(lining_thickness, x_offsets, strict=True)]
second_lining = create_ifc_window_frame_simple(
second_lining_items = create_ifc_window_frame_simple(
builder, second_lining_size, second_lining_thickness, second_lining_position
)
lining_items.append(second_lining)
lining_items.extend(second_lining_items)
main_lining = create_ifc_window_frame_simple(builder, main_lining_size, lining_thickness)
lining_items.append(main_lining)
main_lining_items = create_ifc_window_frame_simple(builder, main_lining_size, lining_thickness)
lining_items.extend(main_lining_items)
frame_position = V(
x_offsets[0],
@@ -142,10 +205,10 @@ def create_ifc_window(
x_offsets[3],
)
frame_extruded = create_ifc_window_frame_simple(builder, frame_size, frame_thickness, frame_position)
frame_extruded_items = create_ifc_window_frame_simple(builder, frame_size, frame_thickness, frame_position)
glass_position = frame_position + V(0, frame_size.y / 2 - glass_thickness / 2, 0)
glass_rect = builder.deep_copy(frame_extruded.SweptArea.InnerCurves[0])
glass_rect = builder.deep_copy(frame_extruded_items[0].SweptArea.InnerCurves[0])
glass = builder.extrude(
glass_rect,
glass_thickness,
@@ -155,7 +218,7 @@ def create_ifc_window(
position=glass_position,
)
output_items = [lining_items, [frame_extruded], [glass]]
output_items = [lining_items, frame_extruded_items, [glass]]
builder.translate(chain(*output_items), position)
return output_items
@@ -369,8 +432,8 @@ class Usecase:
cur_panel_items.extend(
[
get_lining_shape(
window_lining_thickness[0],
closed=closed_lining[0],
window_lining_thickness[0],
closed=closed_lining[0],
x_offset=current_offset_x if right_to_mullion else None,
),
get_lining_shape(
@@ -386,13 +449,13 @@ class Usecase:
frame_items = []
frame_position = V(
current_offset_x if right_to_mullion else lining_to_panel_offset_x,
lining_to_panel_offset_y_full,
current_offset_x if right_to_mullion else lining_to_panel_offset_x,
lining_to_panel_offset_y_full,
)
frame_width = panel_width
frame_width -= current_offset_x if left_to_mullion else lining_to_panel_offset_x
frame_width -= current_offset_x if right_to_mullion else lining_to_panel_offset_x
frame_width -= current_offset_x if left_to_mullion else lining_to_panel_offset_x
frame_width -= current_offset_x if right_to_mullion else lining_to_panel_offset_x
frame_vertical = builder.rectangle(size=V(frame_thickness, frame_depth))
frame_items.extend(
@@ -443,7 +506,6 @@ class Usecase:
unique_cols = len(set(panel_row))
for column_i, panel_i in enumerate(panel_row):
# detect mullion
has_mullion = unique_cols > 1
first_column = column_i == 0
@@ -454,7 +516,7 @@ class Usecase:
# detect transom
has_transom = unique_rows_in_col[column_i] > 1
first_row = row_i == 0
last_row = row_i == unique_rows_in_col[column_i] - 1
last_row = row_i == unique_rows_in_col[column_i] - 1
top_to_transom = has_transom and not first_row
bottom_to_transom = has_transom and not last_row
@@ -490,6 +552,7 @@ class Usecase:
frame_thickness = cur_panel["FrameThickness"]
lining_to_panel_offset_y_full = (lining_depth - frame_depth) + lining_to_panel_offset_y
# fmt: off
# calculate lining thickness and frame size / offset
# taking into account mullions and transoms
window_lining_thickness = [
@@ -499,22 +562,23 @@ class Usecase:
transom_thickness if top_to_transom else lining_thickness,
]
# x offsets can differ if there are mullions or transoms because we're trying to maintain symmetry
# x offsets can differ if there are mullions or transoms because we're trying to maintain symmetry
base_frame_clear = lining_to_panel_offset_x + frame_thickness - lining_thickness
current_offset_x = base_frame_clear - frame_thickness + mullion_thickness
current_offset_z = base_frame_clear - frame_thickness + transom_thickness
x_offsets = [
current_offset_x if right_to_mullion else lining_to_panel_offset_x, # LEFT
current_offset_z if bottom_to_transom else lining_to_panel_offset_x, # TOP
current_offset_x if left_to_mullion else lining_to_panel_offset_x, # RIGHT
current_offset_z if top_to_transom else lining_to_panel_offset_x, # BOTTOM
current_offset_x if right_to_mullion else lining_to_panel_offset_x, # LEFT
current_offset_z if bottom_to_transom else lining_to_panel_offset_x, # TOP
current_offset_x if left_to_mullion else lining_to_panel_offset_x, # RIGHT
current_offset_z if top_to_transom else lining_to_panel_offset_x, # BOTTOM
]
# fmt: on
window_lining_size = V(panel_width, lining_depth, panel_height)
frame_size = window_lining_size.copy()
frame_size.y = frame_depth
frame_size.x -= (x_offsets[0] + x_offsets[2])
frame_size.z -= (x_offsets[1] + x_offsets[3])
frame_size.x -= x_offsets[0] + x_offsets[2]
frame_size.z -= x_offsets[1] + x_offsets[3]
window_panel_position = V(accumulated_width, 0, accumulated_height[column_i])
# create window panel
@@ -528,8 +592,7 @@ class Usecase:
frame_thickness,
glass_thickness,
window_panel_position,
self.settings["unit_scale"],
x_offsets
x_offsets,
)
built_panels.append(panel_i)
window_items.extend(chain(*current_window_items))