mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +00:00
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:
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user