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 f88835b87b
commit 40656297a6
2 changed files with 123 additions and 58 deletions
@@ -262,7 +262,7 @@ def create_bm_window(
frame_thickness, frame_thickness,
glass_thickness, glass_thickness,
position: Vector, position: Vector,
x_offsets: list = None x_offsets: list = None,
): ):
"""`lining_thickness` and `x_offsets` are expected to be defined as a list, """`lining_thickness` and `x_offsets` are expected to be defined as a list,
similarly to `create_bm_window_frame` `thickness` argument""" similarly to `create_bm_window_frame` `thickness` argument"""
@@ -334,7 +334,7 @@ def update_window_modifier_bmesh(context):
# detect transom # detect transom
has_transom = unique_rows_in_col[column_i] > 1 has_transom = unique_rows_in_col[column_i] > 1
first_row = row_i == 0 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 top_to_transom = has_transom and not first_row
bottom_to_transom = has_transom and not last_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_depth = props.frame_depth[panel_i]
frame_thickness = props.frame_thickness[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 # add window
window_lining_size = V( window_lining_size = V(
panel_width, panel_width,
@@ -375,6 +376,7 @@ def update_window_modifier_bmesh(context):
# calculate lining thickness and frame size / offset # calculate lining thickness and frame size / offset
# taking into account mullions and transoms # taking into account mullions and transoms
# fmt: off
window_lining_thickness = [ window_lining_thickness = [
mullion_thickness if right_to_mullion else lining_thickness, mullion_thickness if right_to_mullion else lining_thickness,
transom_thickness if bottom_to_transom 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, 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 base_frame_clear = lining_to_panel_offset_x + frame_thickness - lining_thickness
current_offset_x = base_frame_clear - frame_thickness + mullion_thickness current_offset_x = base_frame_clear - frame_thickness + mullion_thickness
current_offset_z = base_frame_clear - frame_thickness + transom_thickness current_offset_z = base_frame_clear - frame_thickness + transom_thickness
# fmt: off
x_offsets = [ x_offsets = [
current_offset_x if right_to_mullion else lining_to_panel_offset_x, # LEFT 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_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_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_z if top_to_transom else lining_to_panel_offset_x, # BOTTOM
] ]
# fmt: on
frame_size = window_lining_size.copy() frame_size = window_lining_size.copy()
frame_size.y = frame_depth frame_size.y = frame_depth
frame_size.x -= (x_offsets[0] + x_offsets[2]) frame_size.x -= x_offsets[0] + x_offsets[2]
frame_size.z -= (x_offsets[1] + x_offsets[3]) frame_size.z -= x_offsets[1] + x_offsets[3]
window_position = V(accumulated_width, 0, accumulated_height[column_i]) window_position = V(accumulated_width, 0, accumulated_height[column_i])
lining_verts, panel_verts, glass_verts = create_bm_window( lining_verts, panel_verts, glass_verts = create_bm_window(
@@ -411,7 +413,7 @@ def update_window_modifier_bmesh(context):
frame_thickness, frame_thickness,
glass_thickness, glass_thickness,
window_position, window_position,
x_offsets x_offsets,
) )
built_panels.append(panel_i) 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: """`thickness` of the profile is defined as list in the following order:
`(LEFT, TOP, RIGHT, BOTTOM)` `(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): if not isinstance(thickness, collections.abc.Iterable):
thickness = [thickness] * 4 thickness = [thickness] * 4
th_left, th_up, th_right, th_bottom = thickness 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) # if all lining sides are present then we can just use two rectangles
inner_rect = builder.rectangle(size=inner_rect_size.xz, position=V(th_left, th_bottom)) # 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) inner_rect_size = size - V(th_left + th_right, 0, th_bottom + th_up)
panel_extruded = builder.extrude( inner_rect = builder.rectangle(size=inner_rect_size.xz, position=V(th_left, th_bottom))
panel_profile,
size.y, panel_profile = builder.profile(panel_rect, inner_curves=inner_rect)
position_x_axis=V(1, 0, 0), return [get_extruded_profile(panel_profile)]
position_z_axis=V(0, -1, 0),
extrusion_vector=V(0, 0, -1), # if some side has zero thickness it means we cannot use inner curves
position=position, # and need to generate L/U shape or just separate rectangles
) else:
return panel_extruded
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( def window_l_shape_check(
@@ -95,7 +164,6 @@ def create_ifc_window(
frame_thickness, frame_thickness,
glass_thickness, glass_thickness,
position: Vector, position: Vector,
unit_scale, # different from bmesh `create_bm_window`
x_offsets: list = None, x_offsets: list = None,
): ):
"""`lining_thickness` and `x_offsets` are expected to be defined as a list, """`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 = lining_size.copy()
second_lining_size.y = lining_size.y - lining_to_panel_offset_y_full 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_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 second_lining_items = create_ifc_window_frame_simple(
# 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(
builder, second_lining_size, second_lining_thickness, second_lining_position 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) main_lining_items = create_ifc_window_frame_simple(builder, main_lining_size, lining_thickness)
lining_items.append(main_lining) lining_items.extend(main_lining_items)
frame_position = V( frame_position = V(
x_offsets[0], x_offsets[0],
@@ -142,10 +205,10 @@ def create_ifc_window(
x_offsets[3], 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_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 = builder.extrude(
glass_rect, glass_rect,
glass_thickness, glass_thickness,
@@ -155,7 +218,7 @@ def create_ifc_window(
position=glass_position, position=glass_position,
) )
output_items = [lining_items, [frame_extruded], [glass]] output_items = [lining_items, frame_extruded_items, [glass]]
builder.translate(chain(*output_items), position) builder.translate(chain(*output_items), position)
return output_items return output_items
@@ -369,8 +432,8 @@ class Usecase:
cur_panel_items.extend( cur_panel_items.extend(
[ [
get_lining_shape( get_lining_shape(
window_lining_thickness[0], window_lining_thickness[0],
closed=closed_lining[0], closed=closed_lining[0],
x_offset=current_offset_x if right_to_mullion else None, x_offset=current_offset_x if right_to_mullion else None,
), ),
get_lining_shape( get_lining_shape(
@@ -386,13 +449,13 @@ class Usecase:
frame_items = [] frame_items = []
frame_position = V( frame_position = V(
current_offset_x if right_to_mullion else lining_to_panel_offset_x, current_offset_x if right_to_mullion else lining_to_panel_offset_x,
lining_to_panel_offset_y_full, lining_to_panel_offset_y_full,
) )
frame_width = panel_width 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 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 right_to_mullion else lining_to_panel_offset_x
frame_vertical = builder.rectangle(size=V(frame_thickness, frame_depth)) frame_vertical = builder.rectangle(size=V(frame_thickness, frame_depth))
frame_items.extend( frame_items.extend(
@@ -443,7 +506,6 @@ class Usecase:
unique_cols = len(set(panel_row)) unique_cols = len(set(panel_row))
for column_i, panel_i in enumerate(panel_row): for column_i, panel_i in enumerate(panel_row):
# detect mullion # detect mullion
has_mullion = unique_cols > 1 has_mullion = unique_cols > 1
first_column = column_i == 0 first_column = column_i == 0
@@ -454,7 +516,7 @@ class Usecase:
# detect transom # detect transom
has_transom = unique_rows_in_col[column_i] > 1 has_transom = unique_rows_in_col[column_i] > 1
first_row = row_i == 0 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 top_to_transom = has_transom and not first_row
bottom_to_transom = has_transom and not last_row bottom_to_transom = has_transom and not last_row
@@ -490,6 +552,7 @@ class Usecase:
frame_thickness = cur_panel["FrameThickness"] frame_thickness = cur_panel["FrameThickness"]
lining_to_panel_offset_y_full = (lining_depth - frame_depth) + lining_to_panel_offset_y lining_to_panel_offset_y_full = (lining_depth - frame_depth) + lining_to_panel_offset_y
# fmt: off
# calculate lining thickness and frame size / offset # calculate lining thickness and frame size / offset
# taking into account mullions and transoms # taking into account mullions and transoms
window_lining_thickness = [ window_lining_thickness = [
@@ -499,22 +562,23 @@ class Usecase:
transom_thickness if top_to_transom else lining_thickness, 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 base_frame_clear = lining_to_panel_offset_x + frame_thickness - lining_thickness
current_offset_x = base_frame_clear - frame_thickness + mullion_thickness current_offset_x = base_frame_clear - frame_thickness + mullion_thickness
current_offset_z = base_frame_clear - frame_thickness + transom_thickness current_offset_z = base_frame_clear - frame_thickness + transom_thickness
x_offsets = [ x_offsets = [
current_offset_x if right_to_mullion else lining_to_panel_offset_x, # LEFT 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_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_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_z if top_to_transom else lining_to_panel_offset_x, # BOTTOM
] ]
# fmt: on
window_lining_size = V(panel_width, lining_depth, panel_height) window_lining_size = V(panel_width, lining_depth, panel_height)
frame_size = window_lining_size.copy() frame_size = window_lining_size.copy()
frame_size.y = frame_depth frame_size.y = frame_depth
frame_size.x -= (x_offsets[0] + x_offsets[2]) frame_size.x -= x_offsets[0] + x_offsets[2]
frame_size.z -= (x_offsets[1] + x_offsets[3]) frame_size.z -= x_offsets[1] + x_offsets[3]
window_panel_position = V(accumulated_width, 0, accumulated_height[column_i]) window_panel_position = V(accumulated_width, 0, accumulated_height[column_i])
# create window panel # create window panel
@@ -528,8 +592,7 @@ class Usecase:
frame_thickness, frame_thickness,
glass_thickness, glass_thickness,
window_panel_position, window_panel_position,
self.settings["unit_scale"], x_offsets,
x_offsets
) )
built_panels.append(panel_i) built_panels.append(panel_i)
window_items.extend(chain(*current_window_items)) window_items.extend(chain(*current_window_items))