mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 01:41:57 +00:00
8d18a8a023
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.
609 lines
26 KiB
Python
609 lines
26 KiB
Python
# IfcOpenShell - IFC toolkit and geometry engine
|
|
# Copyright (C) 2023 @Andrej730
|
|
#
|
|
# This file is part of IfcOpenShell.
|
|
#
|
|
# IfcOpenShell is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU Lesser General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# IfcOpenShell is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU Lesser General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Lesser General Public License
|
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import ifcopenshell.util.unit
|
|
from ifcopenshell.util.shape_builder import ShapeBuilder, V
|
|
from itertools import chain
|
|
from mathutils import Vector
|
|
import collections
|
|
|
|
|
|
# SCHEMAS describe panels setup
|
|
# where:
|
|
# - schema rows represent window X axis
|
|
# - schema columns represent window Y axis
|
|
# - order of rows is from top of the window to bottom
|
|
|
|
DEFAULT_PANEL_SCHEMAS = {
|
|
"SINGLE_PANEL": [[0]],
|
|
"DOUBLE_PANEL_HORIZONTAL": [[0], [1]],
|
|
"DOUBLE_PANEL_VERTICAL": [[0, 1]],
|
|
"TRIPLE_PANEL_BOTTOM": [[0, 1], [2, 2]],
|
|
"TRIPLE_PANEL_TOP": [[0, 0], [1, 2]],
|
|
"TRIPLE_PANEL_LEFT": [[0, 1], [0, 2]],
|
|
"TRIPLE_PANEL_RIGHT": [[0, 1], [2, 1]],
|
|
"TRIPLE_PANEL_HORIZONTAL": [[0], [1], [2]],
|
|
"TRIPLE_PANEL_VERTICAL": [[0, 1, 2]],
|
|
}
|
|
|
|
|
|
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)`
|
|
|
|
`thickness` can be also defined just as 1 float value.
|
|
"""
|
|
|
|
if not isinstance(thickness, collections.abc.Iterable):
|
|
thickness = [thickness] * 4
|
|
th_left, th_up, th_right, th_bottom = thickness
|
|
|
|
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,
|
|
)
|
|
|
|
# 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)
|
|
|
|
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(
|
|
lining_to_panel_offset_y_full,
|
|
lining_depth,
|
|
lining_to_panel_offset_x: list,
|
|
lining_thickness: list,
|
|
):
|
|
"""`lining_thickness` and `lining_to_panel_offset_x` expected to be defined as a list,
|
|
similarly to `create_ifc_window_frame_simple` `thickness` argument"""
|
|
l_shape_check = lining_to_panel_offset_y_full < lining_depth and any(
|
|
x_offset < th for th, x_offset in zip(lining_thickness, lining_to_panel_offset_x, strict=True)
|
|
)
|
|
return l_shape_check
|
|
|
|
|
|
def create_ifc_window(
|
|
builder,
|
|
lining_size: Vector,
|
|
lining_thickness: list,
|
|
lining_to_panel_offset_x,
|
|
lining_to_panel_offset_y_full,
|
|
frame_size: Vector,
|
|
frame_thickness,
|
|
glass_thickness,
|
|
position: Vector,
|
|
x_offsets: list = None,
|
|
):
|
|
"""`lining_thickness` and `x_offsets` are expected to be defined as a list,
|
|
similarly to `create_ifc_window_frame_simple` `thickness` argument"""
|
|
lining_items = []
|
|
main_lining_size = lining_size
|
|
|
|
if x_offsets is None:
|
|
x_offsets = [lining_to_panel_offset_x] * 4
|
|
# need to check offsets to decide whether lining should be rectangle
|
|
# or L shaped
|
|
l_shape_check = window_l_shape_check(
|
|
lining_to_panel_offset_y_full,
|
|
lining_size.y,
|
|
x_offsets,
|
|
lining_thickness,
|
|
)
|
|
|
|
if l_shape_check:
|
|
main_lining_size = lining_size.copy()
|
|
main_lining_size.y = lining_to_panel_offset_y_full
|
|
|
|
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)]
|
|
|
|
second_lining_items = create_ifc_window_frame_simple(
|
|
builder, second_lining_size, second_lining_thickness, second_lining_position
|
|
)
|
|
lining_items.extend(second_lining_items)
|
|
|
|
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],
|
|
lining_to_panel_offset_y_full,
|
|
x_offsets[3],
|
|
)
|
|
|
|
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_items[0].SweptArea.InnerCurves[0])
|
|
glass = builder.extrude(
|
|
glass_rect,
|
|
glass_thickness,
|
|
position_x_axis=V(1, 0, 0),
|
|
position_z_axis=V(0, -1, 0),
|
|
extrusion_vector=V(0, 0, -1),
|
|
position=glass_position,
|
|
)
|
|
|
|
output_items = [lining_items, frame_extruded_items, [glass]]
|
|
builder.translate(chain(*output_items), position)
|
|
|
|
return output_items
|
|
|
|
|
|
class Usecase:
|
|
def __init__(self, file, **settings):
|
|
"""units in settings expected to be in ifc project units"""
|
|
self.file = file
|
|
# http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindow.htm
|
|
# http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindowTypePartitioningEnum.htm
|
|
# http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindowLiningProperties.htm
|
|
# http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindowPanelProperties.htm
|
|
self.settings = {"unit_scale": ifcopenshell.util.unit.calculate_unit_scale(self.file)}
|
|
self.settings.update(
|
|
{
|
|
"context": None, # IfcGeometricRepresentationContext
|
|
# SINGLE_PANEL, DOUBLE_PANEL_HORIZONTAL, DOUBLE_PANEL_VERTICAL,
|
|
# TRIPLE_PANEL_BOTTOM, TRIPLE_PANEL_HORIZONTAL, TRIPLE_PANEL_LEFT,
|
|
# TRIPLE_PANEL_RIGHT, TRIPLE_PANEL_TOP, TRIPLE_PANEL_VERTICAL
|
|
"partition_type": "SINGLE_PANEL",
|
|
"overall_height": self.convert_si_to_unit(0.9),
|
|
"overall_width": self.convert_si_to_unit(0.6),
|
|
"lining_properties": {
|
|
"LiningDepth": self.convert_si_to_unit(0.050),
|
|
"LiningThickness": self.convert_si_to_unit(0.050),
|
|
"LiningOffset": self.convert_si_to_unit(0.050), # offset to the wall
|
|
# offset from the wall
|
|
"LiningToPanelOffsetX": self.convert_si_to_unit(0.025),
|
|
# offset from the lining
|
|
# that way it allows you to define overall_depth constant between all panels
|
|
# and still have panels with different size:
|
|
# overall_depth = lining_depth + offset_y
|
|
# full offset from X axis = overall_depth - frame_depth
|
|
"LiningToPanelOffsetY": self.convert_si_to_unit(0.025),
|
|
# applies to DoublePanelVertical, TriplePanelBottom, TriplePanelTop,
|
|
# TriplePanelLeft, TriplePanelRight
|
|
# mullion - horizontal distance between panels
|
|
"MullionThickness": self.convert_si_to_unit(0.050),
|
|
# distance from the first lining to the mullion center
|
|
"FirstMullionOffset": self.convert_si_to_unit(0.3),
|
|
# applies to TriplePanelVertical
|
|
# distance from the first lining to the second mullion center
|
|
"SecondMullionOffset": self.convert_si_to_unit(0.45),
|
|
# applies to DoublePanelHorizontal, TriplePanelBottom, TriplePanelTop,
|
|
# TriplePanelLeft, TriplePanelRight
|
|
# works similar way to mullion
|
|
"TransomThickness": self.convert_si_to_unit(0.050),
|
|
"FirstTransomOffset": self.convert_si_to_unit(0.3),
|
|
# applies to TriplePanelHorizontal
|
|
"SecondTransomOffset": self.convert_si_to_unit(0.6),
|
|
"ShapeAspectStyle": None, # DEPRECATED
|
|
},
|
|
"panel_properties": [
|
|
{
|
|
"FrameDepth": self.convert_si_to_unit(0.035), # by Y
|
|
"FrameThickness": self.convert_si_to_unit(0.035), # by X
|
|
# BOTTOM, LEFT, MIDDLE, RIGHT, TOP
|
|
"PanelPosition": ..., # NEVER USED
|
|
# defines the basic ways to describe how window panels operate
|
|
# how it's hanged, how it opens
|
|
"OperationType": None, # NEVER USED
|
|
"ShapeAspectStyle": None, # DEPRECATED
|
|
},
|
|
],
|
|
}
|
|
)
|
|
|
|
for key, value in settings.items():
|
|
self.settings[key] = value
|
|
self.settings["panel_schema"] = DEFAULT_PANEL_SCHEMAS[self.settings["partition_type"]]
|
|
|
|
def execute(self):
|
|
builder = ShapeBuilder(self.file)
|
|
overall_height = self.settings["overall_height"]
|
|
overall_width = self.settings["overall_width"]
|
|
|
|
if self.settings["context"].TargetView == "ELEVATION_VIEW":
|
|
rect = builder.rectangle(V(overall_width, 0, overall_height))
|
|
representation_evelevation = builder.get_representation(self.settings["context"], rect)
|
|
return representation_evelevation
|
|
|
|
panel_schema = self.settings["panel_schema"]
|
|
panels = self.settings["panel_properties"]
|
|
accumulated_height = [0] * len(panel_schema[0])
|
|
built_panels = []
|
|
window_items = []
|
|
|
|
lining_props = self.settings["lining_properties"]
|
|
lining_thickness = lining_props["LiningThickness"]
|
|
lining_depth = lining_props["LiningDepth"]
|
|
lining_offset = lining_props["LiningOffset"]
|
|
lining_to_panel_offset_x = lining_props["LiningToPanelOffsetX"]
|
|
lining_to_panel_offset_y = lining_props["LiningToPanelOffsetY"]
|
|
overall_depth = lining_depth + lining_to_panel_offset_y
|
|
|
|
mullion_thickness = lining_props["MullionThickness"] / 2
|
|
first_mullion_offset = lining_props["FirstMullionOffset"]
|
|
second_mullion_offset = lining_props["SecondMullionOffset"]
|
|
transom_thickness = lining_props["TransomThickness"] / 2
|
|
first_transom_offset = lining_props["FirstTransomOffset"]
|
|
second_transom_offset = lining_props["SecondTransomOffset"]
|
|
glass_thickness = self.convert_si_to_unit(0.01)
|
|
|
|
panel_schema = list(reversed(panel_schema))
|
|
|
|
# create 2d representation
|
|
def create_ifc_window_2d_representation():
|
|
items_2d = []
|
|
|
|
top_row = panel_schema[-1]
|
|
unique_cols = len(set(top_row))
|
|
built_panels = []
|
|
accumulated_width = 0
|
|
|
|
for column_i, panel_i in enumerate(top_row):
|
|
cur_panel_items = []
|
|
|
|
# lists represent left and right linings
|
|
window_lining_thickness = [lining_thickness] * 2
|
|
closed_lining = [True] * 2
|
|
|
|
if panel_i in built_panels:
|
|
continue
|
|
|
|
# detect mullion
|
|
has_mullion = unique_cols > 1
|
|
first_column = column_i == 0
|
|
last_column = column_i == unique_cols - 1
|
|
left_to_mullion = has_mullion and not last_column
|
|
right_to_mullion = has_mullion and not first_column
|
|
|
|
if has_mullion:
|
|
if first_column:
|
|
panel_width = first_mullion_offset
|
|
elif last_column:
|
|
panel_width = overall_width - accumulated_width
|
|
else:
|
|
panel_width = second_mullion_offset - accumulated_width
|
|
|
|
# mullion thickness
|
|
if not first_column:
|
|
window_lining_thickness[0] = mullion_thickness # left column
|
|
closed_lining[0] = False
|
|
if not last_column:
|
|
window_lining_thickness[1] = mullion_thickness # right column
|
|
closed_lining[1] = False
|
|
else:
|
|
panel_width = overall_width
|
|
|
|
frame_depth = panels[panel_i]["FrameDepth"]
|
|
frame_thickness = panels[panel_i]["FrameThickness"]
|
|
lining_to_panel_offset_y_full = (lining_depth - frame_depth) + lining_to_panel_offset_y
|
|
base_frame_clear = lining_to_panel_offset_x + frame_thickness - lining_thickness
|
|
current_offset_x = base_frame_clear - frame_thickness + mullion_thickness
|
|
|
|
# add lining
|
|
cur_panel_items.append(
|
|
builder.polyline(
|
|
[
|
|
V(window_lining_thickness[0], 0),
|
|
V(panel_width - window_lining_thickness[1], 0),
|
|
]
|
|
)
|
|
)
|
|
|
|
def get_lining_shape(lining_thickness, closed=True, mirror=False, x_offset=None):
|
|
if x_offset is None:
|
|
x_offset = lining_to_panel_offset_x
|
|
l_shape_check = window_l_shape_check(
|
|
lining_to_panel_offset_y_full,
|
|
lining_depth,
|
|
[x_offset],
|
|
[lining_thickness],
|
|
)
|
|
if l_shape_check:
|
|
lining_shape = builder.polyline(
|
|
[
|
|
V(0, lining_depth),
|
|
V(x_offset, lining_depth),
|
|
V(
|
|
x_offset,
|
|
lining_to_panel_offset_y_full,
|
|
),
|
|
V(lining_thickness, lining_to_panel_offset_y_full),
|
|
V(lining_thickness, 0),
|
|
V(0, 0),
|
|
],
|
|
closed=closed,
|
|
)
|
|
else:
|
|
lining_shape = builder.polyline(
|
|
[
|
|
V(0, lining_depth),
|
|
V(lining_thickness, lining_depth),
|
|
V(lining_thickness, 0),
|
|
V(0, 0),
|
|
],
|
|
closed=closed,
|
|
)
|
|
|
|
if mirror:
|
|
builder.mirror(
|
|
lining_shape,
|
|
mirror_axes=V(1, 0),
|
|
mirror_point=V(panel_width / 2, 0),
|
|
)
|
|
|
|
return lining_shape
|
|
|
|
cur_panel_items.extend(
|
|
[
|
|
get_lining_shape(
|
|
window_lining_thickness[0],
|
|
closed=closed_lining[0],
|
|
x_offset=current_offset_x if right_to_mullion else None,
|
|
),
|
|
get_lining_shape(
|
|
window_lining_thickness[1],
|
|
closed=closed_lining[1],
|
|
x_offset=current_offset_x if left_to_mullion else None,
|
|
mirror=True,
|
|
),
|
|
]
|
|
)
|
|
|
|
# add frame
|
|
frame_items = []
|
|
|
|
frame_position = V(
|
|
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_vertical = builder.rectangle(size=V(frame_thickness, frame_depth))
|
|
frame_items.extend(
|
|
[
|
|
frame_vertical,
|
|
builder.mirror(
|
|
frame_vertical,
|
|
mirror_axes=V(1, 0),
|
|
mirror_point=V(frame_width / 2, 0),
|
|
create_copy=True,
|
|
),
|
|
]
|
|
)
|
|
|
|
frame_horizontal = builder.polyline([V(frame_thickness, 0), V(frame_width - frame_thickness, 0)])
|
|
frame_items.extend(
|
|
[
|
|
frame_horizontal,
|
|
builder.translate(frame_horizontal, V(0, frame_depth), create_copy=True),
|
|
]
|
|
)
|
|
# glass
|
|
frame_items.append(builder.translate(frame_horizontal, V(0, frame_depth / 2), create_copy=True))
|
|
|
|
builder.translate(frame_items, frame_position)
|
|
cur_panel_items.extend(frame_items)
|
|
|
|
builder.translate(cur_panel_items, V(accumulated_width, 0))
|
|
|
|
accumulated_width += panel_width
|
|
built_panels.append(panel_i)
|
|
items_2d.extend(cur_panel_items)
|
|
|
|
builder.translate(items_2d, V(0, lining_offset))
|
|
representation_2d = builder.get_representation(self.settings["context"], items_2d)
|
|
return representation_2d
|
|
|
|
if self.settings["context"].TargetView == "PLAN_VIEW":
|
|
return create_ifc_window_2d_representation()
|
|
|
|
# TODO: need more readable way to define panel width and height
|
|
unique_rows_in_col = [
|
|
len(set(row[column_i] for row in panel_schema)) for column_i in range(len(panel_schema[0]))
|
|
]
|
|
|
|
for row_i, panel_row in enumerate(panel_schema):
|
|
accumulated_width = 0
|
|
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
|
|
last_column = column_i == unique_cols - 1
|
|
left_to_mullion = has_mullion and not last_column
|
|
right_to_mullion = has_mullion and not first_column
|
|
|
|
# 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
|
|
top_to_transom = has_transom and not first_row
|
|
bottom_to_transom = has_transom and not last_row
|
|
|
|
# calculate current panel dimensions
|
|
if has_mullion:
|
|
# panel_width
|
|
if first_column:
|
|
panel_width = first_mullion_offset
|
|
elif last_column:
|
|
panel_width = overall_width - accumulated_width
|
|
else:
|
|
panel_width = second_mullion_offset - accumulated_width
|
|
else:
|
|
panel_width = overall_width
|
|
|
|
if has_transom:
|
|
if first_row:
|
|
panel_height = first_transom_offset
|
|
elif last_row:
|
|
panel_height = overall_height - accumulated_height[column_i]
|
|
else:
|
|
panel_height = second_transom_offset - accumulated_height[column_i]
|
|
else:
|
|
panel_height = overall_height
|
|
|
|
if panel_i in built_panels:
|
|
accumulated_height[column_i] += panel_height
|
|
accumulated_width += panel_width
|
|
continue
|
|
|
|
cur_panel = panels[panel_i]
|
|
frame_depth = cur_panel["FrameDepth"]
|
|
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 = [
|
|
mullion_thickness if right_to_mullion else lining_thickness,
|
|
transom_thickness if bottom_to_transom else lining_thickness,
|
|
mullion_thickness if left_to_mullion 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
|
|
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
|
|
]
|
|
# 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]
|
|
|
|
window_panel_position = V(accumulated_width, 0, accumulated_height[column_i])
|
|
# create window panel
|
|
current_window_items = create_ifc_window(
|
|
builder,
|
|
window_lining_size,
|
|
window_lining_thickness,
|
|
lining_to_panel_offset_x,
|
|
lining_to_panel_offset_y_full,
|
|
frame_size,
|
|
frame_thickness,
|
|
glass_thickness,
|
|
window_panel_position,
|
|
x_offsets,
|
|
)
|
|
built_panels.append(panel_i)
|
|
window_items.extend(chain(*current_window_items))
|
|
|
|
accumulated_height[column_i] += panel_height
|
|
accumulated_width += panel_width
|
|
|
|
builder.translate(window_items, V(0, lining_offset, 0)) # wall offset
|
|
representation = builder.get_representation(self.settings["context"], window_items)
|
|
return representation
|
|
|
|
def convert_si_to_unit(self, value):
|
|
return value / self.settings["unit_scale"]
|