Black formatting window modifier

This commit is contained in:
Andrej730
2023-01-25 10:49:03 +05:00
parent a6271d0936
commit 9f0ad9be3c
9 changed files with 295 additions and 276 deletions
@@ -120,7 +120,7 @@ class EnableEditingArray(bpy.types.Operator, tool.Ifc.Operator):
props.y = data["y"] props.y = data["y"]
props.z = data["z"] props.z = data["z"]
props.is_editing = self.item props.is_editing = self.item
return {'FINISHED'} return {"FINISHED"}
class RemoveArray(bpy.types.Operator, tool.Ifc.Operator): class RemoveArray(bpy.types.Operator, tool.Ifc.Operator):
@@ -425,4 +425,4 @@ class WindowData:
parameters = psets.get("BBIM_Window", None) parameters = psets.get("BBIM_Window", None)
if parameters: if parameters:
parameters["data"] = json.loads(parameters.get("Data", "[]") or "[]") parameters["data"] = json.loads(parameters.get("Data", "[]") or "[]")
return parameters return parameters
@@ -303,7 +303,7 @@ class BIMSverchokProperties(PropertyGroup):
def window_type_prop_update(self, context): def window_type_prop_update(self, context):
panels_data = self.window_types_panels[self.window_type] panels_data = self.window_types_panels[self.window_type]
for i in range(3): for i in range(3):
try: try:
width, height = panels_data[i] width, height = panels_data[i]
except IndexError: except IndexError:
width, height = (0.0, 0.0) width, height = (0.0, 0.0)
@@ -311,51 +311,54 @@ def window_type_prop_update(self, context):
self.relative_width[i] = width self.relative_width[i] = width
self.relative_height[i] = height self.relative_height[i] = height
class BIMWindowProperties(PropertyGroup): class BIMWindowProperties(PropertyGroup):
window_types = ( window_types = (
('SINGLE_PANEL', 'SINGLE_PANEL', ''), ("SINGLE_PANEL", "SINGLE_PANEL", ""),
('DOUBLE_PANEL_HORIZONTAL', 'DOUBLE_PANEL_HORIZONTAL', ''), ("DOUBLE_PANEL_HORIZONTAL", "DOUBLE_PANEL_HORIZONTAL", ""),
('DOUBLE_PANEL_VERTICAL', 'DOUBLE_PANEL_VERTICAL', ''), ("DOUBLE_PANEL_VERTICAL", "DOUBLE_PANEL_VERTICAL", ""),
('TRIPLE_PANEL_BOTTOM', 'TRIPLE_PANEL_BOTTOM', ''), ("TRIPLE_PANEL_BOTTOM", "TRIPLE_PANEL_BOTTOM", ""),
('TRIPLE_PANEL_TOP', 'TRIPLE_PANEL_TOP', ''), ("TRIPLE_PANEL_TOP", "TRIPLE_PANEL_TOP", ""),
('TRIPLE_PANEL_LEFT', 'TRIPLE_PANEL_LEFT', ''), ("TRIPLE_PANEL_LEFT", "TRIPLE_PANEL_LEFT", ""),
('TRIPLE_PANEL_RIGHT', 'TRIPLE_PANEL_RIGHT', ''), ("TRIPLE_PANEL_RIGHT", "TRIPLE_PANEL_RIGHT", ""),
('TRIPLE_PANEL_HORIZONTAL', 'TRIPLE_PANEL_HORIZONTAL', ''), ("TRIPLE_PANEL_HORIZONTAL", "TRIPLE_PANEL_HORIZONTAL", ""),
('TRIPLE_PANEL_VERTICAL', 'TRIPLE_PANEL_VERTICAL', ''), ("TRIPLE_PANEL_VERTICAL", "TRIPLE_PANEL_VERTICAL", ""),
) )
# default panel relative dimensions # default panel relative dimensions
window_types_panels = { window_types_panels = {
'SINGLE_PANEL': ((1.0, 1.0), ), "SINGLE_PANEL": ((1.0, 1.0), ),
'DOUBLE_PANEL_HORIZONTAL': ((1.0, 0.5), (1.0, 0.5), ), "DOUBLE_PANEL_HORIZONTAL": ((1.0, 0.5), (1.0, 0.5), ),
'DOUBLE_PANEL_VERTICAL': ((0.5, 1.0), (0.5, 1.0), ), "DOUBLE_PANEL_VERTICAL": ((0.5, 1.0), (0.5, 1.0), ),
'TRIPLE_PANEL_BOTTOM': ((0.5, 0.5), (0.5, 0.5), (1.0, 0.5), ), "TRIPLE_PANEL_BOTTOM": ((0.5, 0.5), (0.5, 0.5), (1.0, 0.5), ),
'TRIPLE_PANEL_TOP': ((1.0, 0.5), (0.5, 0.5), (0.5, 0.5), ), "TRIPLE_PANEL_TOP": ((1.0, 0.5), (0.5, 0.5), (0.5, 0.5), ),
'TRIPLE_PANEL_LEFT': ((0.5, 1.0), (0.5, 0.5), (0.5, 0.5), ), "TRIPLE_PANEL_LEFT": ((0.5, 1.0), (0.5, 0.5), (0.5, 0.5), ),
'TRIPLE_PANEL_RIGHT': ((0.5, 0.5), (0.5, 1.0), (0.5, 0.5), ), "TRIPLE_PANEL_RIGHT": ((0.5, 0.5), (0.5, 1.0), (0.5, 0.5), ),
'TRIPLE_PANEL_HORIZONTAL': ((1.0, 1/3), (1.0, 1/3), (1.0, 1/3),), "TRIPLE_PANEL_HORIZONTAL": ((1.0, 1/3), (1.0, 1/3), (1.0, 1/3),),
'TRIPLE_PANEL_VERTICAL': ((1/3, 1.0), (1/3, 1.0), (1/3, 1.0),), "TRIPLE_PANEL_VERTICAL": ((1/3, 1.0), (1/3, 1.0), (1/3, 1.0),),
} }
is_editing: bpy.props.IntProperty(default=-1) is_editing: bpy.props.IntProperty(default=-1)
window_type: bpy.props.EnumProperty(name="Window Type", items=window_types, default="SINGLE_PANEL", update=window_type_prop_update) window_type: bpy.props.EnumProperty(
overall_height: bpy.props.FloatProperty(name='Overall Height', default=900) name="Window Type", items=window_types, default="SINGLE_PANEL", update=window_type_prop_update
overall_width: bpy.props.FloatProperty(name='Overall Width', default=600) )
overall_height: bpy.props.FloatProperty(name="Overall Height", default=900)
overall_width: bpy.props.FloatProperty(name="Overall Width", default=600)
# lining properties # lining properties
lining_depth: bpy.props.FloatProperty(name='Lining Depth', default=50) lining_depth: bpy.props.FloatProperty(name="Lining Depth", default=50)
lining_thickness: bpy.props.FloatProperty(name='Lining Thickness', default=50) lining_thickness: bpy.props.FloatProperty(name="Lining Thickness", default=50)
lining_offset: bpy.props.FloatProperty(name='Lining Offset', default=50) lining_offset: bpy.props.FloatProperty(name="Lining Offset", default=50)
lining_to_panel_offset_x: bpy.props.FloatProperty(name='Lining to Panel Offset X', default=25) lining_to_panel_offset_x: bpy.props.FloatProperty(name="Lining to Panel Offset X", default=25)
lining_to_panel_offset_y: bpy.props.FloatProperty(name='Lining to Panel Offset Y', default=25) lining_to_panel_offset_y: bpy.props.FloatProperty(name="Lining to Panel Offset Y", default=25)
mullion_thickness: bpy.props.FloatProperty(name='(not used) Mullion Thickness') mullion_thickness: bpy.props.FloatProperty(name="(not used) Mullion Thickness")
transom_thickness: bpy.props.FloatProperty(name='(not used) Transom Thickness') transom_thickness: bpy.props.FloatProperty(name="(not used) Transom Thickness")
# panel_properties # panel_properties
frame_depth: bpy.props.FloatVectorProperty(name='Frame Depth', size=3, default=[35]*3) frame_depth: bpy.props.FloatVectorProperty(name="Frame Depth", size=3, default=[35] * 3)
frame_thickness: bpy.props.FloatVectorProperty(name='Frame Thickness', size=3, default=[35]*3) frame_thickness: bpy.props.FloatVectorProperty(name="Frame Thickness", size=3, default=[35] * 3)
relative_width: bpy.props.FloatVectorProperty(name='Relative Width', size=3, default=[1,0,0]) relative_width: bpy.props.FloatVectorProperty(name="Relative Width", size=3, default=[1, 0, 0])
relative_height: bpy.props.FloatVectorProperty(name='Relative Height', size=3, default=[1,0,0]) relative_height: bpy.props.FloatVectorProperty(name="Relative Height", size=3, default=[1, 0, 0])
def get_general_kwargs(self): def get_general_kwargs(self):
return { return {
@@ -381,4 +384,4 @@ class BIMWindowProperties(PropertyGroup):
"frame_thickness": self.frame_thickness, "frame_thickness": self.frame_thickness,
"relative_width": self.relative_width, "relative_width": self.relative_width,
"relative_height": self.relative_height, "relative_height": self.relative_height,
} }
@@ -361,7 +361,7 @@ class EnableEditingStair(bpy.types.Operator, tool.Ifc.Operator):
setattr(props, prop_name, data[prop_name]) setattr(props, prop_name, data[prop_name])
props.is_editing = 1 props.is_editing = 1
return {'FINISHED'} return {"FINISHED"}
class RemoveStair(bpy.types.Operator, tool.Ifc.Operator): class RemoveStair(bpy.types.Operator, tool.Ifc.Operator):
@@ -424,13 +424,13 @@ class BIM_PT_window(bpy.types.Panel):
props = context.active_object.BIMWindowProperties props = context.active_object.BIMWindowProperties
if WindowData.data['parameters']: if WindowData.data["parameters"]:
row = self.layout.row(align=True) row = self.layout.row(align=True)
row.label(text="Window parameters", icon="OUTLINER_OB_LATTICE") row.label(text="Window parameters", icon="OUTLINER_OB_LATTICE")
window_data = WindowData.data['parameters']['data'] window_data = WindowData.data["parameters"]["data"]
if props.is_editing != - 1: if props.is_editing != -1:
row = self.layout.row(align=True) row = self.layout.row(align=True)
row.operator("bim.finish_editing_window", icon="CHECKMARK", text="Finish editing") row.operator("bim.finish_editing_window", icon="CHECKMARK", text="Finish editing")
row.operator("bim.cancel_editing_window", icon="CANCEL", text="") row.operator("bim.cancel_editing_window", icon="CANCEL", text="")
@@ -440,30 +440,30 @@ class BIM_PT_window(bpy.types.Panel):
self.layout.prop(props, prop) self.layout.prop(props, prop)
lining_props = props.get_lining_kwargs() lining_props = props.get_lining_kwargs()
self.layout.label(text='Lining properties') self.layout.label(text="Lining properties")
for prop in lining_props: for prop in lining_props:
self.layout.prop(props, prop) self.layout.prop(props, prop)
panel_props = props.get_panel_kwargs() panel_props = props.get_panel_kwargs()
self.layout.label(text='Panel properties') self.layout.label(text="Panel properties")
number_of_panels = len(props.window_types_panels[props.window_type]) number_of_panels = len(props.window_types_panels[props.window_type])
panel_box = self.layout.box() panel_box = self.layout.box()
row = panel_box.row() row = panel_box.row()
cols = [row.column(align=True) for i in range(number_of_panels+1)] cols = [row.column(align=True) for i in range(number_of_panels + 1)]
cols[0].label(text="")
cols[0].label(text='')
for panel_i in range(number_of_panels): for panel_i in range(number_of_panels):
r = cols[panel_i+1].row() r = cols[panel_i + 1].row()
r.alignment = 'CENTER' r.alignment = "CENTER"
r.label(text=f'#{panel_i}') r.label(text=f"#{panel_i}")
r = cols[panel_i+1].row() r = cols[panel_i + 1].row()
for prop in panel_props: for prop in panel_props:
cols[0].label(text=f"{props.bl_rna.properties[prop].name}") cols[0].label(text=f"{props.bl_rna.properties[prop].name}")
for panel_i in range(number_of_panels): for panel_i in range(number_of_panels):
cols[panel_i+1].prop(props, prop, index=panel_i, text='') cols[panel_i + 1].prop(props, prop, index=panel_i, text="")
update_window_modifier_bmesh(context) update_window_modifier_bmesh(context)
@@ -481,46 +481,47 @@ class BIM_PT_window(bpy.types.Panel):
row.label(text=str(prop_value)) row.label(text=str(prop_value))
lining_props = props.get_lining_kwargs() lining_props = props.get_lining_kwargs()
self.layout.label(text='Lining properties') self.layout.label(text="Lining properties")
lining_box = self.layout.box() lining_box = self.layout.box()
for prop in lining_props: for prop in lining_props:
prop_value = window_data['lining_properties'][prop] prop_value = window_data["lining_properties"][prop]
prop_value = round(prop_value, 5) if type(prop_value) is float else prop_value prop_value = round(prop_value, 5) if type(prop_value) is float else prop_value
row = lining_box.row(align=True) row = lining_box.row(align=True)
row.label(text=f"{props.bl_rna.properties[prop].name}") row.label(text=f"{props.bl_rna.properties[prop].name}")
row.label(text=str(prop_value)) row.label(text=str(prop_value))
panel_props = props.get_panel_kwargs() panel_props = props.get_panel_kwargs()
self.layout.label(text='Panel properties') self.layout.label(text="Panel properties")
number_of_panels = len(props.window_types_panels[props.window_type]) number_of_panels = len(props.window_types_panels[props.window_type])
panel_box = self.layout.box() panel_box = self.layout.box()
row = panel_box.row() row = panel_box.row()
cols = [row.column(align=True) for i in range(number_of_panels+1)] cols = [row.column(align=True) for i in range(number_of_panels + 1)]
cols[0].label(text='') cols[0].label(text="")
for panel_i in range(number_of_panels): for panel_i in range(number_of_panels):
r = cols[panel_i+1].row() r = cols[panel_i + 1].row()
r.alignment = 'CENTER' r.alignment = "CENTER"
r.label(text=f'#{panel_i}') r.label(text=f"#{panel_i}")
r = cols[panel_i+1].row() r = cols[panel_i + 1].row()
# TODO: align property values more evenly # TODO: align property values more evenly
for prop in panel_props: for prop in panel_props:
cols[0].row().label(text=f"{props.bl_rna.properties[prop].name}") cols[0].row().label(text=f"{props.bl_rna.properties[prop].name}")
for panel_i in range(number_of_panels): for panel_i in range(number_of_panels):
r = cols[panel_i+1].row() r = cols[panel_i + 1].row()
r.alignment = 'CENTER' r.alignment = "CENTER"
prop_value = window_data['panel_properties'][prop][panel_i] prop_value = window_data["panel_properties"][prop][panel_i]
prop_value = round(prop_value, 5) if type(prop_value) is float else prop_value prop_value = round(prop_value, 5) if type(prop_value) is float else prop_value
r.label(text=str(prop_value)) r.label(text=str(prop_value))
r = cols[panel_i+1].row() r = cols[panel_i + 1].row()
else: else:
row = self.layout.row() row = self.layout.row()
row.label(text="No Window Found") row.label(text="No Window Found")
row.operator("bim.add_window", icon="ADD", text="") row.operator("bim.add_window", icon="ADD", text="")
class BIM_MT_model(Menu): class BIM_MT_model(Menu):
bl_idname = "BIM_MT_model" bl_idname = "BIM_MT_model"
bl_label = "IFC Objects" bl_label = "IFC Objects"
@@ -40,54 +40,59 @@ import json
V = lambda *x: Vector([float(i) for i in x]) V = lambda *x: Vector([float(i) for i in x])
def update_window_modifier_representation(context): def update_window_modifier_representation(context):
obj = context.active_object obj = context.active_object
props = obj.BIMWindowProperties props = obj.BIMWindowProperties
ifc_file = tool.Ifc.get() ifc_file = tool.Ifc.get()
representation_data = { representation_data = {
'partition_type': props.window_type, "partition_type": props.window_type,
'overall_height': props.overall_height, "overall_height": props.overall_height,
'overall_width': props.overall_width, "overall_width": props.overall_width,
'lining_properties': { "lining_properties": {
'LiningDepth': props.lining_depth, "LiningDepth": props.lining_depth,
'LiningThickness': props.lining_thickness, "LiningThickness": props.lining_thickness,
'LiningOffset': props.lining_offset, "LiningOffset": props.lining_offset,
'LiningToPanelOffsetX': props.lining_to_panel_offset_x, "LiningToPanelOffsetX": props.lining_to_panel_offset_x,
'LiningToPanelOffsetY': props.lining_to_panel_offset_y, "LiningToPanelOffsetY": props.lining_to_panel_offset_y,
}, },
'panel_properties': [] "panel_properties": [],
} }
number_of_panels = len(props.window_types_panels[props.window_type]) number_of_panels = len(props.window_types_panels[props.window_type])
for panel_i in range(number_of_panels): for panel_i in range(number_of_panels):
panel_data = { panel_data = {
'FrameDepth': props.frame_depth[panel_i], "FrameDepth": props.frame_depth[panel_i],
'FrameThickness': props.frame_thickness[panel_i], "FrameThickness": props.frame_thickness[panel_i],
'RelativeWidth': props.relative_width[panel_i], "RelativeWidth": props.relative_width[panel_i],
'RelativeHeight': props.relative_height[panel_i], "RelativeHeight": props.relative_height[panel_i],
} }
representation_data['panel_properties'].append(panel_data) representation_data["panel_properties"].append(panel_data)
# ELEVATION_VIEW representation # ELEVATION_VIEW representation
ifc_context = ifcopenshell.util.representation.get_context(ifc_file, "Model", "Profile", "ELEVATION_VIEW") ifc_context = ifcopenshell.util.representation.get_context(ifc_file, "Model", "Profile", "ELEVATION_VIEW")
if not ifc_context: if not ifc_context:
model_context = ifcopenshell.util.representation.get_context(ifc_file, 'Model') model_context = ifcopenshell.util.representation.get_context(ifc_file, "Model")
ifc_context = ifcopenshell.api.run( ifc_context = ifcopenshell.api.run(
'context.add_context', "context.add_context",
ifc_file, ifc_file,
context_type='Model', context_type="Model",
context_identifier='Profile', context_identifier="Profile",
target_view='ELEVATION_VIEW', target_view="ELEVATION_VIEW",
parent=model_context) parent=model_context,
)
representation_data['context'] = ifc_context representation_data["context"] = ifc_context
elevation_representation = ifcopenshell.api.run("geometry.add_window_representation", ifc_file, **representation_data) elevation_representation = ifcopenshell.api.run(
"geometry.add_window_representation", ifc_file, **representation_data
)
replace_representation_for_object(ifc_file, ifc_context, obj, elevation_representation) replace_representation_for_object(ifc_file, ifc_context, obj, elevation_representation)
# MODEL_VIEW representation # MODEL_VIEW representation
ifc_context = ifcopenshell.util.representation.get_context(ifc_file, "Model", "Body", "MODEL_VIEW") ifc_context = ifcopenshell.util.representation.get_context(ifc_file, "Model", "Body", "MODEL_VIEW")
representation_data['context'] = ifc_context representation_data["context"] = ifc_context
model_representation = ifcopenshell.api.run("geometry.add_window_representation", ifc_file, **representation_data) model_representation = ifcopenshell.api.run("geometry.add_window_representation", ifc_file, **representation_data)
replace_representation_for_object(ifc_file, ifc_context, obj, model_representation) replace_representation_for_object(ifc_file, ifc_context, obj, model_representation)
def replace_representation_for_object(ifc_file, ifc_context, obj, new_representation): def replace_representation_for_object(ifc_file, ifc_context, obj, new_representation):
ifc_element = tool.Ifc.get_entity(obj) ifc_element = tool.Ifc.get_entity(obj)
@@ -106,24 +111,27 @@ def replace_representation_for_object(ifc_file, ifc_context, obj, new_representa
representation=new_representation, representation=new_representation,
should_reload=True, should_reload=True,
is_global=False, is_global=False,
should_sync_changes_first=True should_sync_changes_first=True,
) )
core.remove_representation(tool.Ifc, tool.Geometry, obj=obj, representation=old_representation) core.remove_representation(tool.Ifc, tool.Geometry, obj=obj, representation=old_representation)
else: else:
ifcopenshell.api.run("geometry.assign_representation", ifc_file, product=ifc_element, representation=new_representation) ifcopenshell.api.run(
"geometry.assign_representation", ifc_file, product=ifc_element, representation=new_representation
)
def create_bm_window_closed_profile(bm, size: Vector, thickness:float, position:Vector):
def create_bm_window_closed_profile(bm, size: Vector, thickness: float, position: Vector):
width, depth, height = size width, depth, height = size
verts = [ verts = [
(0, [thickness, 0.0, thickness]), (0, [thickness, 0.0, thickness]),
(1, [width-thickness, 0.0, thickness]), (1, [width - thickness, 0.0, thickness]),
(2, [thickness, 0.0, height-thickness]), (2, [thickness, 0.0, height - thickness]),
(3, [width-thickness, 0.0, height-thickness]), (3, [width - thickness, 0.0, height - thickness]),
(4, [0.0, 0.0, 0.0]), (4, [0.0, 0.0, 0.0]),
(5, [0.0, 0.0, height]), (5, [0.0, 0.0, height]),
(6, [width, 0.0, 0.0]), (6, [width, 0.0, 0.0]),
(7, [width, 0.0, height]), (7, [width, 0.0, height]),
] ]
edges = [ edges = [
@@ -142,10 +150,10 @@ def create_bm_window_closed_profile(bm, size: Vector, thickness:float, position:
] ]
faces = [ faces = [
(0, (2, 3, 7, 5)), (0, (2, 3, 7, 5)),
(1, (5, 4, 0, 2)), (1, (5, 4, 0, 2)),
(2, (1, 0, 4, 6)), (2, (1, 0, 4, 6)),
(3, (7, 3, 1, 6)), (3, (7, 3, 1, 6)),
] ]
bm.verts.index_update() bm.verts.index_update()
@@ -161,7 +169,7 @@ def create_bm_window_closed_profile(bm, size: Vector, thickness:float, position:
translate_verts = [v for v in extruded["geom"] if isinstance(v, BMVert)] translate_verts = [v for v in extruded["geom"] if isinstance(v, BMVert)]
bmesh.ops.translate(bm, vec=extrusion_vector, verts=translate_verts) bmesh.ops.translate(bm, vec=extrusion_vector, verts=translate_verts)
bmesh.ops.translate(bm, vec=position, verts=new_verts+translate_verts) bmesh.ops.translate(bm, vec=position, verts=new_verts + translate_verts)
return new_verts return new_verts
@@ -195,53 +203,52 @@ def update_window_modifier_bmesh(context):
# create lining # create lining
lining_size = V( lining_size = V(
overall_width * props.relative_width[panel_i], overall_width * props.relative_width[panel_i],
lining_depth, lining_depth,
overall_height* props.relative_height[panel_i]) overall_height * props.relative_height[panel_i],
)
thickness = props.lining_thickness * si_conversion thickness = props.lining_thickness * si_conversion
lining_verts = create_bm_window_closed_profile(bm, lining_size, thickness, V(0, 0, 0)) lining_verts = create_bm_window_closed_profile(bm, lining_size, thickness, V(0, 0, 0))
# add panel # add panel
panel_size = lining_size.copy() panel_size = lining_size.copy()
panel_size.y = frame_depth panel_size.y = frame_depth
panel_size = panel_size - V(lining_to_panel_offset_x*2, 0, lining_to_panel_offset_x*2) panel_size = panel_size - V(lining_to_panel_offset_x * 2, 0, lining_to_panel_offset_x * 2)
panel_position = V( panel_position = V(
props.lining_to_panel_offset_x * si_conversion, props.lining_to_panel_offset_x * si_conversion,
((props.lining_depth-props.frame_depth[panel_i]) + props.lining_to_panel_offset_y) * si_conversion, ((props.lining_depth - props.frame_depth[panel_i]) + props.lining_to_panel_offset_y) * si_conversion,
props.lining_to_panel_offset_x * si_conversion, props.lining_to_panel_offset_x * si_conversion,
) )
thickness = props.frame_thickness[panel_i] * si_conversion thickness = props.frame_thickness[panel_i] * si_conversion
panel_verts = create_bm_window_closed_profile(bm, panel_size, thickness, panel_position) panel_verts = create_bm_window_closed_profile(bm, panel_size, thickness, panel_position)
# add glass # add glass
glass_verts = bmesh.ops.create_cube(bm, size=1)['verts'] glass_verts = bmesh.ops.create_cube(bm, size=1)["verts"]
bmesh.ops.translate(bm, vec=-glass_verts[0].co, verts=glass_verts) bmesh.ops.translate(bm, vec=-glass_verts[0].co, verts=glass_verts)
glass_size = panel_size glass_size = panel_size
glass_size.y = glass_thickness glass_size.y = glass_thickness
glass_size = glass_size - V(frame_thickness*2, 0, frame_thickness) glass_size = glass_size - V(frame_thickness * 2, 0, frame_thickness)
glass_position = panel_position + V( glass_position = panel_position + V(
frame_thickness, frame_thickness,
frame_depth/2-glass_thickness/2, frame_depth / 2 - glass_thickness / 2,
frame_thickness, frame_thickness,
) )
bmesh.ops.scale(bm, vec=glass_size, verts=glass_verts) bmesh.ops.scale(bm, vec=glass_size, verts=glass_verts)
bmesh.ops.translate(bm, vec=glass_position, verts=glass_verts) bmesh.ops.translate(bm, vec=glass_position, verts=glass_verts)
# translate panel # translate panel
accumulated_offset = V(accumulated_width, 0, accumulated_height[column_i]) * V(overall_width, 0, overall_height) accumulated_offset = V(accumulated_width, 0, accumulated_height[column_i]) * V(
bmesh.ops.translate(bm, overall_width, 0, overall_height
vec=accumulated_offset, )
verts=lining_verts + panel_verts + glass_verts) bmesh.ops.translate(bm, vec=accumulated_offset, verts=lining_verts + panel_verts + glass_verts)
built_panels.append(panel_i) built_panels.append(panel_i)
accumulated_height[column_i] += props.relative_height[panel_i] accumulated_height[column_i] += props.relative_height[panel_i]
accumulated_width += props.relative_width[panel_i] accumulated_width += props.relative_width[panel_i]
bmesh.ops.translate(bm, bmesh.ops.translate(bm, vec=V(0, props.lining_offset * si_conversion, 0), verts=bm.verts)
vec=V(0, props.lining_offset * si_conversion, 0),
verts=bm.verts)
bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.0001) bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.0001)
if bpy.context.object.mode == "EDIT": if bpy.context.object.mode == "EDIT":
@@ -267,8 +274,8 @@ class AddWindow(bpy.types.Operator, tool.Ifc.Operator):
lining_props = props.get_lining_kwargs() lining_props = props.get_lining_kwargs()
panel_props = props.get_panel_kwargs() panel_props = props.get_panel_kwargs()
window_data['lining_properties'] = lining_props window_data["lining_properties"] = lining_props
window_data['panel_properties'] = panel_props window_data["panel_properties"] = panel_props
psets = ifcopenshell.util.element.get_psets(element) psets = ifcopenshell.util.element.get_psets(element)
pset = psets.get("BBIM_Window", None) pset = psets.get("BBIM_Window", None)
@@ -286,7 +293,7 @@ class AddWindow(bpy.types.Operator, tool.Ifc.Operator):
update_window_modifier_representation(context) update_window_modifier_representation(context)
return {"FINISHED"} return {"FINISHED"}
class CancelEditingWindow(bpy.types.Operator, tool.Ifc.Operator): class CancelEditingWindow(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.cancel_editing_window" bl_idname = "bim.cancel_editing_window"
bl_label = "Cancel editing Window" bl_label = "Cancel editing Window"
@@ -324,8 +331,8 @@ class FinishEditingWindow(bpy.types.Operator, tool.Ifc.Operator):
lining_props = props.get_lining_kwargs() lining_props = props.get_lining_kwargs()
panel_props = props.get_panel_kwargs() panel_props = props.get_panel_kwargs()
window_data['lining_properties'] = lining_props window_data["lining_properties"] = lining_props
window_data['panel_properties'] = panel_props window_data["panel_properties"] = panel_props
props.is_editing = -1 props.is_editing = -1
@@ -357,7 +364,7 @@ class EnableEditingWindow(bpy.types.Operator, tool.Ifc.Operator):
setattr(props, prop_name, data[prop_name]) setattr(props, prop_name, data[prop_name])
props.is_editing = 1 props.is_editing = 1
return {'FINISHED'} return {"FINISHED"}
class RemoveWindow(bpy.types.Operator, tool.Ifc.Operator): class RemoveWindow(bpy.types.Operator, tool.Ifc.Operator):
@@ -66,6 +66,7 @@ def simple_uses():
ifc_file.write("tmp.ifc") ifc_file.write("tmp.ifc")
def generate_desk_test(): def generate_desk_test():
ifc_file = ifcopenshell.file() ifc_file = ifcopenshell.file()
desks = [ desks = [
@@ -80,6 +81,7 @@ def generate_desk_test():
ifc_file.write("tmp.ifc") ifc_file.write("tmp.ifc")
def mirror_placement_test(): def mirror_placement_test():
ifc_file = ifcopenshell.api.run("project.create_file") ifc_file = ifcopenshell.api.run("project.create_file")
project = ifcopenshell.api.run( project = ifcopenshell.api.run(
@@ -153,6 +155,7 @@ def mirror_placement_test():
ifc_file.write("tmp.ifc") ifc_file.write("tmp.ifc")
def curve_between_two_points_test(): def curve_between_two_points_test():
ifc_file = ifcopenshell.api.run("project.create_file") ifc_file = ifcopenshell.api.run("project.create_file")
project = ifcopenshell.api.run( project = ifcopenshell.api.run(
@@ -217,6 +220,7 @@ def curve_between_two_points_test():
ifc_file.write("tmp.ifc") ifc_file.write("tmp.ifc")
def generate_simple_desk(ifc_file, width, depth, height): def generate_simple_desk(ifc_file, width, depth, height):
# > width, depth, height in mm # > width, depth, height in mm
width, depth, height = [i / 1000 for i in (width, depth, height)] width, depth, height = [i / 1000 for i in (width, depth, height)]
@@ -226,6 +230,7 @@ def generate_simple_desk(ifc_file, width, depth, height):
extruded_area = builder.extrude(profile, height) extruded_area = builder.extrude(profile, height)
return extruded_area return extruded_area
def generate_table(ifc_file, width, depth, height, countertop_thickness=20, leg_size=40, leg_offset=50): def generate_table(ifc_file, width, depth, height, countertop_thickness=20, leg_size=40, leg_offset=50):
# > width, depth, height in mm # > width, depth, height in mm
width, depth, height, countertop_thickness, leg_size, leg_offset = [ width, depth, height, countertop_thickness, leg_size, leg_offset = [
@@ -29,81 +29,81 @@ from mathutils import Vector
# - order of rows is from top of the window to bottom # - order of rows is from top of the window to bottom
DEFAULT_PANEL_SCHEMAS = { DEFAULT_PANEL_SCHEMAS = {
'SINGLE_PANEL': [[0]], "SINGLE_PANEL": [[0]],
'DOUBLE_PANEL_HORIZONTAL': [[0],[1]], "DOUBLE_PANEL_HORIZONTAL": [[0], [1]],
'DOUBLE_PANEL_VERTICAL': [[0,1]], "DOUBLE_PANEL_VERTICAL": [[0, 1]],
'TRIPLE_PANEL_BOTTOM': [[0,1],[2,2]], "TRIPLE_PANEL_BOTTOM": [[0, 1], [2, 2]],
'TRIPLE_PANEL_TOP': [[0,0], [1,2]], "TRIPLE_PANEL_TOP": [[0, 0], [1, 2]],
'TRIPLE_PANEL_LEFT': [[0,1],[0,2]], "TRIPLE_PANEL_LEFT": [[0, 1], [0, 2]],
'TRIPLE_PANEL_RIGHT': [[0,1],[2,1]], "TRIPLE_PANEL_RIGHT": [[0, 1], [2, 1]],
'TRIPLE_PANEL_HORIZONTAL': [[0],[1],[2]], "TRIPLE_PANEL_HORIZONTAL": [[0], [1], [2]],
'TRIPLE_PANEL_VERTICAL': [[0,1,2]], "TRIPLE_PANEL_VERTICAL": [[0, 1, 2]],
} }
class Usecase: class Usecase:
def __init__(self, file, **settings): def __init__(self, file, **settings):
self.file = file 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/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/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/IfcWindowLiningProperties.htm
# http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindowPanelProperties.htm # http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindowPanelProperties.htm
self.settings = { self.settings = {
"context": None, # IfcGeometricRepresentationContext "context": None, # IfcGeometricRepresentationContext
# SINGLE_PANEL, DOUBLE_PANEL_HORIZONTAL, DOUBLE_PANEL_VERTICAL, # 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 # TRIPLE_PANEL_BOTTOM, TRIPLE_PANEL_HORIZONTAL, TRIPLE_PANEL_LEFT, TRIPLE_PANEL_RIGHT, TRIPLE_PANEL_TOP, TRIPLE_PANEL_VERTICAL
"partition_type": 'SINGLE_PANEL', "partition_type": "SINGLE_PANEL",
"overall_height": 900, "overall_height": 900,
"overall_width": 600, "overall_width": 600,
"lining_properties": { "lining_properties": {
'LiningDepth': 50, "LiningDepth": 50,
'LiningThickness': 50, "LiningThickness": 50,
'LiningOffset': 50, # offset to the wall "LiningOffset": 50, # offset to the wall
'LiningToPanelOffsetX': 25, "LiningToPanelOffsetX": 25,
'LiningToPanelOffsetY': 25, "LiningToPanelOffsetY": 25,
# applies to DoublePanelVertical, TriplePanelBottom, TriplePanelTop, TriplePanelLeft, TriplePanelRight # applies to DoublePanelVertical, TriplePanelBottom, TriplePanelTop, TriplePanelLeft, TriplePanelRight
# mullion - distance between panels # mullion - distance between panels
'FirstMullionOffset': ..., # distance from the first lining to the mullion "FirstMullionOffset": ..., # distance from the first lining to the mullion
# TODO: take mullion thickness into account # TODO: take mullion thickness into account
'MullionThickness': ..., "MullionThickness": ...,
# applies to DoublePanelHorizontal, TriplePanelBottom, TriplePanelTop, TriplePanelLeft, TriplePanelRight # applies to DoublePanelHorizontal, TriplePanelBottom, TriplePanelTop, TriplePanelLeft, TriplePanelRight
# works similar way to mullion # works similar way to mullion
'FirstTransomOffset': ..., "FirstTransomOffset": ...,
'TransomThickness': ..., "TransomThickness": ...,
# applies to TriplePanelVertical # applies to TriplePanelVertical
'SecondMullionOffset': ..., # distance from the first lining to the second mullion "SecondMullionOffset": ..., # distance from the first lining to the second mullion
# applies to TriplePanelHorizontal # applies to TriplePanelHorizontal
'SecondTransomOffset': ..., "SecondTransomOffset": ...,
'ShapeAspectStyle': None, # DEPRECATED "ShapeAspectStyle": None, # DEPRECATED
}, },
"panel_properties": [ "panel_properties": [
{ {
'FrameDepth': 35, # by Y "FrameDepth": 35, # by Y
'FrameThickness': 35, # by X "FrameThickness": 35, # by X
# BOTTOM, LEFT, MIDDLE, RIGHT, TOP # BOTTOM, LEFT, MIDDLE, RIGHT, TOP
'PanelPosition': ..., "PanelPosition": ...,
# defines the basic ways to describe how window panels operate # defines the basic ways to describe how window panels operate
# how it's hanged, how it opens # how it's hanged, how it opens
'OperationType': None, "OperationType": None,
'ShapeAspectStyle': None, # DEPRECATED "ShapeAspectStyle": None, # DEPRECATED
# Custom Parameter not available in IFC # Custom Parameter not available in IFC
# dimensions of the panel relative to overall window dimensions # dimensions of the panel relative to overall window dimensions
'RelativeWidth': 1.0, "RelativeWidth": 1.0,
'RelativeHeight': 1.0, "RelativeHeight": 1.0,
}, },
] ],
} }
for key, value in settings.items(): for key, value in settings.items():
self.settings[key] = value self.settings[key] = value
self.settings["panel_schema"] = DEFAULT_PANEL_SCHEMAS[self.settings['partition_type']] self.settings["panel_schema"] = DEFAULT_PANEL_SCHEMAS[self.settings["partition_type"]]
# recalculate relative width and height to avoid errors # recalculate relative width and height to avoid errors
# TODO: rework or remove # TODO: rework or remove
# panels_data = self.settings['panel_properties'] # panels_data = self.settings['panel_properties']
# current_height = sum(p['RelativeHeight'] for p in panels_data) # current_height = sum(p['RelativeHeight'] for p in panels_data)
# current_width = sum(p['RelativeWidth'] for p in panels_data) # current_width = sum(p['RelativeWidth'] for p in panels_data)
# for p in panels_data: # for p in panels_data:
# if current_height != 1.0: # if current_height != 1.0:
# p['RelativeHeight'] = p['RelativeHeight'] / current_height # p['RelativeHeight'] = p['RelativeHeight'] / current_height
@@ -111,74 +111,70 @@ class Usecase:
# if current_width != 1.0: # if current_width != 1.0:
# p['RelativeWidth'] = p['RelativeWidth'] / current_width # p['RelativeWidth'] = p['RelativeWidth'] / current_width
def execute(self): def execute(self):
self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file) self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file)
builder = ShapeBuilder(self.file) builder = ShapeBuilder(self.file)
overall_height = self.convert_si_to_unit(self.settings['overall_height']) overall_height = self.convert_si_to_unit(self.settings["overall_height"])
overall_width = self.convert_si_to_unit(self.settings['overall_width']) overall_width = self.convert_si_to_unit(self.settings["overall_width"])
if self.settings['context'].TargetView == 'ELEVATION_VIEW': if self.settings["context"].TargetView == "ELEVATION_VIEW":
rect = builder.rectangle(V(overall_width, 0, overall_height)) rect = builder.rectangle(V(overall_width, 0, overall_height))
representation_evelevation = builder.get_representation(self.settings['context'], rect) representation_evelevation = builder.get_representation(self.settings["context"], rect)
return representation_evelevation return representation_evelevation
panel_schema = self.settings["panel_schema"]
panel_schema = self.settings['panel_schema'] panels = self.settings["panel_properties"]
panels = self.settings['panel_properties']
accumulated_height = [0] * len(panel_schema[0]) accumulated_height = [0] * len(panel_schema[0])
built_panels = [] built_panels = []
window_items = [] window_items = []
lining_thickness = self.convert_si_to_unit(self.settings['lining_properties']['LiningThickness']) lining_thickness = self.convert_si_to_unit(self.settings["lining_properties"]["LiningThickness"])
lining_depth = self.convert_si_to_unit(self.settings['lining_properties']['LiningDepth']) lining_depth = self.convert_si_to_unit(self.settings["lining_properties"]["LiningDepth"])
lining_offset = self.convert_si_to_unit(self.settings['lining_properties']['LiningOffset']) lining_offset = self.convert_si_to_unit(self.settings["lining_properties"]["LiningOffset"])
lining_panel_offset_x = self.convert_si_to_unit(self.settings['lining_properties']['LiningToPanelOffsetX']) lining_panel_offset_x = self.convert_si_to_unit(self.settings["lining_properties"]["LiningToPanelOffsetX"])
lining_panel_offset_y = self.convert_si_to_unit(self.settings['lining_properties']['LiningToPanelOffsetY']) lining_panel_offset_y = self.convert_si_to_unit(self.settings["lining_properties"]["LiningToPanelOffsetY"])
glass_thickness = self.convert_si_to_unit(10) glass_thickness = self.convert_si_to_unit(10)
for row_i, panel_row in enumerate(reversed(panel_schema)): for row_i, panel_row in enumerate(reversed(panel_schema)):
accumulated_width = 0 accumulated_width = 0
for column_i, panel_i in enumerate(panel_row): for column_i, panel_i in enumerate(panel_row):
if panel_i in built_panels: if panel_i in built_panels:
accumulated_height[column_i] += cur_panel['RelativeHeight'] accumulated_height[column_i] += cur_panel["RelativeHeight"]
accumulated_width += cur_panel['RelativeWidth'] accumulated_width += cur_panel["RelativeWidth"]
continue continue
cur_panel = panels[panel_i] cur_panel = panels[panel_i]
current_items = [] current_items = []
panel_depth = self.convert_si_to_unit(cur_panel['FrameDepth']) panel_depth = self.convert_si_to_unit(cur_panel["FrameDepth"])
panel_thickness = self.convert_si_to_unit(cur_panel['FrameThickness']) panel_thickness = self.convert_si_to_unit(cur_panel["FrameThickness"])
panel_height = cur_panel['RelativeHeight'] * overall_height panel_height = cur_panel["RelativeHeight"] * overall_height
panel_width = cur_panel['RelativeWidth'] * overall_width panel_width = cur_panel["RelativeWidth"] * overall_width
panel_actual_width = panel_width-lining_panel_offset_x*2 panel_actual_width = panel_width - lining_panel_offset_x * 2
panel_actual_height = panel_height-lining_panel_offset_x*2 panel_actual_height = panel_height - lining_panel_offset_x * 2
glass_width = panel_actual_width - panel_thickness*2 glass_width = panel_actual_width - panel_thickness * 2
glass_height = panel_actual_height - panel_thickness*2 glass_height = panel_actual_height - panel_thickness * 2
# build lining # build lining
lining_items_vertical = [] lining_items_vertical = []
lining_items = [] lining_items = []
# lining is calculated on panel level because # lining is calculated on panel level because
# panel depth is used # panel depth is used
lining_rectangle = builder.rectangle( size=V(lining_thickness, lining_depth) ) lining_rectangle = builder.rectangle(size=V(lining_thickness, lining_depth))
# need to check offsets to decide whether lining should be rectangle # need to check offsets to decide whether lining should be rectangle
# or L shaped # or L shaped
if lining_panel_offset_x >= lining_thickness \ if lining_panel_offset_x >= lining_thickness or lining_panel_offset_y >= lining_depth:
or lining_panel_offset_y >= lining_depth:
lining_vertical_polyline = ifcopenshell.util.element.copy_deep(self.file, lining_rectangle) lining_vertical_polyline = ifcopenshell.util.element.copy_deep(self.file, lining_rectangle)
lining_vertical_height = panel_height lining_vertical_height = panel_height
else: else:
lining_points = [ lining_points = [
V(0, 0), V(0, 0),
V(0, lining_depth), V(0, lining_depth),
V(lining_panel_offset_x, lining_depth), V(lining_panel_offset_x, lining_depth),
V(lining_panel_offset_x, lining_depth-(panel_depth-lining_panel_offset_y)), V(lining_panel_offset_x, lining_depth - (panel_depth - lining_panel_offset_y)),
V(lining_thickness, lining_depth-(panel_depth-lining_panel_offset_y)), V(lining_thickness, lining_depth - (panel_depth - lining_panel_offset_y)),
V(lining_thickness, 0), V(lining_thickness, 0),
] ]
@@ -186,61 +182,70 @@ class Usecase:
lining_vertical_polyline = builder.polyline(lining_points, closed=True) lining_vertical_polyline = builder.polyline(lining_points, closed=True)
lining_vertical_height = panel_height - lining_panel_offset_x * 2 lining_vertical_height = panel_height - lining_panel_offset_x * 2
# if lining panel X offset is present # if lining panel X offset is present
# then we also need to add two more box shapes # then we also need to add two more box shapes
# to finish the lining after the panel ends # to finish the lining after the panel ends
if lining_panel_offset_x > 0: if lining_panel_offset_x > 0:
lining_vertical_addition = builder.extrude(builder.deep_copy(lining_rectangle), lining_panel_offset_x) lining_vertical_addition = builder.extrude(
builder.deep_copy(lining_rectangle), lining_panel_offset_x
)
lining_items_vertical.extend([ lining_items_vertical.extend(
lining_vertical_addition, [
builder.translate(lining_vertical_addition, V(0,0,panel_height - lining_panel_offset_x), create_copy=True) lining_vertical_addition,
]) builder.translate(
lining_vertical_addition,
V(0, 0, panel_height - lining_panel_offset_x),
create_copy=True,
),
]
)
# horizontal lining # horizontal lining
lining_horizontal_polyline = builder.deep_copy(lining_vertical_polyline) lining_horizontal_polyline = builder.deep_copy(lining_vertical_polyline)
lining_horizontal_extruded = builder.extrude( lining_horizontal_extruded = builder.extrude(
lining_horizontal_polyline, lining_horizontal_polyline,
magnitude=panel_width-2*lining_thickness, magnitude=panel_width - 2 * lining_thickness,
extrusion_vector=V(0,0,-1), extrusion_vector=V(0, 0, -1),
position_z_axis=V(-1,0,0), position_z_axis=V(-1, 0, 0),
position_x_axis=V(0,0,1), position_x_axis=V(0, 0, 1),
position=V(lining_thickness, 0, 0) position=V(lining_thickness, 0, 0),
) )
# TODO: should implement mirror by Z for more readability # TODO: should implement mirror by Z for more readability
# TODO: investigate meaning of mirror axes in case of custom x/y/z space # TODO: investigate meaning of mirror axes in case of custom x/y/z space
# lining_horizontal_mirrored = builder.mirror( # lining_horizontal_mirrored = builder.mirror(
# lining_horizontal_extruded, # lining_horizontal_extruded,
# mirror_point=V(0, panel_height/2), # mirror_point=V(0, panel_height/2),
# mirror_axes=V(0,1), # mirror_axes=V(0,1),
# create_copy=True # create_copy=True
# ) # )
lining_horizontal_polyline_mirrored = builder.mirror( lining_horizontal_polyline_mirrored = builder.mirror(
lining_horizontal_polyline, lining_horizontal_polyline,
mirror_axes=V(1,0), mirror_axes=V(1, 0),
mirror_point=V(lining_thickness,0), mirror_point=V(lining_thickness, 0),
create_copy=True create_copy=True,
) )
lining_horizontal_mirrored = builder.extrude( lining_horizontal_mirrored = builder.extrude(
lining_horizontal_polyline_mirrored, lining_horizontal_polyline_mirrored,
magnitude=panel_width-2*lining_thickness, magnitude=panel_width - 2 * lining_thickness,
extrusion_vector=V(0,0,-1), extrusion_vector=V(0, 0, -1),
position_z_axis=V(-1,0,0), position_z_axis=V(-1, 0, 0),
position_x_axis=V(0,0,1), position_x_axis=V(0, 0, 1),
position=V(lining_thickness, 0, panel_height - lining_thickness*2) position=V(lining_thickness, 0, panel_height - lining_thickness * 2),
) )
lining_items.extend([lining_horizontal_extruded, lining_horizontal_mirrored]) lining_items.extend([lining_horizontal_extruded, lining_horizontal_mirrored])
extrusion_position = V(0,0,lining_panel_offset_x) extrusion_position = V(0, 0, lining_panel_offset_x)
lining_vertical_extruded = builder.extrude(lining_vertical_polyline, lining_vertical_height, position=extrusion_position) lining_vertical_extruded = builder.extrude(
lining_vertical_polyline, lining_vertical_height, position=extrusion_position
)
lining_items_vertical.append(lining_vertical_extruded) lining_items_vertical.append(lining_vertical_extruded)
lining_items_vertical_mirrored = builder.mirror( lining_items_vertical_mirrored = builder.mirror(
lining_items_vertical, mirror_point=V(panel_width/2, 0), lining_items_vertical, mirror_point=V(panel_width / 2, 0), mirror_axes=V(1, 0), create_copy=True
mirror_axes=V(1,0), )
create_copy=True)
lining_items.extend(lining_items_vertical) lining_items.extend(lining_items_vertical)
lining_items.extend(lining_items_vertical_mirrored) lining_items.extend(lining_items_vertical_mirrored)
@@ -250,51 +255,48 @@ class Usecase:
panel_items = [] panel_items = []
panel_position = V( panel_position = V(
lining_panel_offset_x, lining_panel_offset_x, (lining_depth - panel_depth) + lining_panel_offset_y, lining_panel_offset_x
(lining_depth-panel_depth) + lining_panel_offset_y, )
lining_panel_offset_x)
panel_rect = builder.rectangle(size=V(panel_actual_width, 0, panel_actual_height)) panel_rect = builder.rectangle(size=V(panel_actual_width, 0, panel_actual_height))
glass_rect = builder.rectangle( glass_rect = builder.rectangle(
size=V(glass_width, 0, glass_height), size=V(glass_width, 0, glass_height), position=V(panel_thickness, 0, panel_thickness)
position=V(panel_thickness, 0, panel_thickness)) )
panel_profile = builder.profile(panel_rect, inner_curves=glass_rect) panel_profile = builder.profile(panel_rect, inner_curves=glass_rect)
panel_extruded = builder.extrude( panel_extruded = builder.extrude(
panel_profile, panel_profile, panel_depth, extrusion_vector=V(0, 1, 0), position=panel_position
panel_depth, )
extrusion_vector=V(0,1,0),
position=panel_position)
panel_items.append(panel_extruded) panel_items.append(panel_extruded)
current_items.extend(panel_items) current_items.extend(panel_items)
# add glass # add glass
glass_position = panel_position + V(0, panel_depth/2-glass_thickness/2, 0) glass_position = panel_position + V(0, panel_depth / 2 - glass_thickness / 2, 0)
glass_rect = builder.deep_copy(glass_rect) glass_rect = builder.deep_copy(glass_rect)
glass = builder.extrude( glass = builder.extrude(
glass_rect, glass_rect, glass_thickness, extrusion_vector=V(0, 1, 0), position=glass_position
glass_thickness,
extrusion_vector=V(0,1,0),
position=glass_position
) )
current_items.append(glass) current_items.append(glass)
# translate panel # translate panel
accumulated_offset = V(accumulated_width, 0, accumulated_height[column_i]) * V(overall_width, 0, overall_height) accumulated_offset = V(accumulated_width, 0, accumulated_height[column_i]) * V(
overall_width, 0, overall_height
)
builder.translate(current_items, accumulated_offset) builder.translate(current_items, accumulated_offset)
built_panels.append(panel_i) built_panels.append(panel_i)
window_items.extend(current_items) window_items.extend(current_items)
accumulated_height[column_i] += cur_panel['RelativeHeight']
accumulated_width += cur_panel['RelativeWidth']
builder.translate(window_items, V(0, lining_offset, 0)) # wall offset accumulated_height[column_i] += cur_panel["RelativeHeight"]
representation = builder.get_representation(self.settings['context'], window_items) accumulated_width += cur_panel["RelativeWidth"]
builder.translate(window_items, V(0, lining_offset, 0)) # wall offset
representation = builder.get_representation(self.settings["context"], window_items)
return representation return representation
def convert_si_to_unit(self, value): def convert_si_to_unit(self, value):
return value * 0.001 / self.settings["unit_scale"] return value * 0.001 / self.settings["unit_scale"]
# TODO: remove test at the end # TODO: remove test at the end
if __name__ == "__main__": if __name__ == "__main__":
ifc_file = ifcopenshell.file() ifc_file = ifcopenshell.file()
@@ -337,14 +339,13 @@ if __name__ == "__main__":
), ),
} }
settings = { settings = {
'context': representations['body'], "context": representations["body"],
# SINGLE_PANEL, DOUBLE_PANEL_HORIZONTAL, DOUBLE_PANEL_VERTICAL, # 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 # TRIPLE_PANEL_BOTTOM, TRIPLE_PANEL_HORIZONTAL, TRIPLE_PANEL_LEFT, TRIPLE_PANEL_RIGHT, TRIPLE_PANEL_TOP, TRIPLE_PANEL_VERTICAL
"partition_type": 'TRIPLE_PANEL_RIGHT', "partition_type": "TRIPLE_PANEL_RIGHT",
"overall_height": 900, "overall_height": 900,
"overall_width": 600*3, "overall_width": 600 * 3,
# "lining_properties": { # "lining_properties": {
# 'LiningDepth': 50, # 'LiningDepth': 50,
# 'LiningThickness': 50, # 'LiningThickness': 50,
@@ -358,34 +359,34 @@ if __name__ == "__main__":
# 'MullionThickness': ..., # 'MullionThickness': ...,
# # applies to DoublePanelHorizontal, TriplePanelBottom, TriplePanelTop, TriplePanelLeft, TriplePanelRight # # applies to DoublePanelHorizontal, TriplePanelBottom, TriplePanelTop, TriplePanelLeft, TriplePanelRight
# # works similar way to mullion # # works similar way to mullion
# 'FirstTransomOffset': ..., # 'FirstTransomOffset': ...,
# 'TransomThickness': ..., # 'TransomThickness': ...,
# # applies to TriplePanelVertical # # applies to TriplePanelVertical
# 'SecondMullionOffset': ..., # distance from the first lining to the second mullion # 'SecondMullionOffset': ..., # distance from the first lining to the second mullion
# # applies to TriplePanelHorizontal # # applies to TriplePanelHorizontal
# 'SecondTransomOffset': ..., # 'SecondTransomOffset': ...,
# 'ShapeAspectStyle': None, # DEPRECATED # 'ShapeAspectStyle': None, # DEPRECATED
# }, # },
"panel_properties": [ "panel_properties": [
{ {
'FrameDepth': 35, # by Y "FrameDepth": 35, # by Y
'FrameThickness': 35, # by X "FrameThickness": 35, # by X
'RelativeWidth': 1.0/2, "RelativeWidth": 1.0 / 2,
'RelativeHeight': 1.0/2, "RelativeHeight": 1.0 / 2,
}, },
{ {
'FrameDepth': 35, # by Y "FrameDepth": 35, # by Y
'FrameThickness': 35, # by X "FrameThickness": 35, # by X
'RelativeWidth': 1.0/2, "RelativeWidth": 1.0 / 2,
'RelativeHeight': 1.0, "RelativeHeight": 1.0,
}, },
{ {
'FrameDepth': 35, # by Y "FrameDepth": 35, # by Y
'FrameThickness': 35, # by X "FrameThickness": 35, # by X
'RelativeWidth': 1.0/2, "RelativeWidth": 1.0 / 2,
'RelativeHeight': 1.0/2, "RelativeHeight": 1.0 / 2,
}, },
] ],
} }
# builder = ShapeBuilder(ifc_file) # builder = ShapeBuilder(ifc_file)
# points = [V(0,0), V(5,1)] # points = [V(0,0), V(5,1)]
@@ -405,9 +406,9 @@ if __name__ == "__main__":
representation = use_case.execute() representation = use_case.execute()
print(representation) print(representation)
settings['context'] = representations['elevation'] settings["context"] = representations["elevation"]
use_case = Usecase(ifc_file, **settings) use_case = Usecase(ifc_file, **settings)
representation_2d = use_case.execute() representation_2d = use_case.execute()
print(representation_2d) print(representation_2d)
ifc_file.write("tmp.ifc") ifc_file.write("tmp.ifc")
@@ -30,6 +30,7 @@ sign = lambda x: x and (1, -1)[x < 0]
# is applied twice during one run to the same element # is applied twice during one run to the same element
# which might produce undesirable results # which might produce undesirable results
class ShapeBuilder: class ShapeBuilder:
def __init__(self, ifc_file): def __init__(self, ifc_file):
self.ifc = ifc_file self.ifc = ifc_file
@@ -60,7 +61,7 @@ class ShapeBuilder:
dimensions = len(size) dimensions = len(size)
if not position: if not position:
position = Vector([0]*dimensions) position = Vector([0] * dimensions)
# adds support both 2d and 3d sizes # adds support both 2d and 3d sizes
non_empty_coords = [i for i, v in enumerate(size) if v] non_empty_coords = [i for i, v in enumerate(size) if v]
@@ -70,7 +71,7 @@ class ShapeBuilder:
position, position,
position + size * id_matrix[non_empty_coords[0]], position + size * id_matrix[non_empty_coords[0]],
position + size, position + size,
position + size * id_matrix[non_empty_coords[1]] position + size * id_matrix[non_empty_coords[1]],
] ]
return points return points
@@ -132,7 +133,7 @@ class ShapeBuilder:
V(0, -y_axis_radius), V(0, -y_axis_radius),
) )
if position_offset: if position_offset:
trim_points = [points[i]+position_offset for i in trim_points_mask] trim_points = [points[i] + position_offset for i in trim_points_mask]
else: else:
trim_points = [points[i] for i in trim_points_mask] trim_points = [points[i] for i in trim_points_mask]
return trim_points return trim_points
@@ -148,7 +149,7 @@ class ShapeBuilder:
): ):
""" """
Ellipse trimming points should be specified in counter clockwise order. Ellipse trimming points should be specified in counter clockwise order.
For example, if you need to get the part of the ellipse ABOVE y-axis, you need to use mask (0,2). Below y-axis - (2,0) For example, if you need to get the part of the ellipse ABOVE y-axis, you need to use mask (0,2). Below y-axis - (2,0)
For more information about trim_points_mask check builder.get_trim_points_from_mask For more information about trim_points_mask check builder.get_trim_points_from_mask
@@ -157,7 +158,9 @@ class ShapeBuilder:
for further extrusion. for further extrusion.
""" """
direction = self.ifc.createIfcDirection(ref_x_direction) direction = self.ifc.createIfcDirection(ref_x_direction)
ifc_position = self.ifc.createIfcAxis2Placement2D(self.ifc.createIfcCartesianPoint(position), RefDirection=direction) ifc_position = self.ifc.createIfcAxis2Placement2D(
self.ifc.createIfcCartesianPoint(position), RefDirection=direction
)
ifc_ellipse = self.ifc.createIfcEllipse(Position=ifc_position, SemiAxis1=x_axis_radius, SemiAxis2=y_axis_radius) ifc_ellipse = self.ifc.createIfcEllipse(Position=ifc_position, SemiAxis1=x_axis_radius, SemiAxis2=y_axis_radius)
if not trim_points: if not trim_points:
@@ -239,11 +242,10 @@ class ShapeBuilder:
return processed_objects if multiple_objects else processed_objects[0] return processed_objects if multiple_objects else processed_objects[0]
def rotate_2d_point(self, point_2d:Vector, angle=90, def rotate_2d_point(
pivot_point:Vector = Vector( (0., 0.)).freeze(), self, point_2d: Vector, angle=90, pivot_point: Vector = Vector((0.0, 0.0)).freeze(), counter_clockwise=False
counter_clockwise=False
): ):
# > angle - in degrees # > angle - in degrees
# < rotated Vector # < rotated Vector
@@ -345,7 +347,7 @@ class ShapeBuilder:
placement_matrix=None, placement_matrix=None,
): ):
"""mirror_axes - along which axes mirror will be applied """mirror_axes - along which axes mirror will be applied
For example, mirroring A(1,0) by axis (1,0) will result in A'(-1,0) For example, mirroring A(1,0) by axis (1,0) will result in A'(-1,0)
""" """
# > curve_or_item - could be a list of curves or items # > curve_or_item - could be a list of curves or items
@@ -415,7 +417,7 @@ class ShapeBuilder:
# TODO: add support for Z-axis too # TODO: add support for Z-axis too
# mirror point is ignored for extrusion direction # mirror point is ignored for extrusion direction
new_direction = self.mirror_2d_point(extruded_direction.to_2d(), mirror_axes, mirror_point=V(0,0)) new_direction = self.mirror_2d_point(extruded_direction.to_2d(), mirror_axes, mirror_point=V(0, 0))
new_direction = new_direction.to_3d() new_direction = new_direction.to_3d()
new_direction.z = extruded_direction.z new_direction.z = extruded_direction.z
@@ -500,4 +502,4 @@ class ShapeBuilder:
return representation return representation
def deep_copy(self, element): def deep_copy(self, element):
return ifcopenshell.util.element.copy_deep(self.ifc, element) return ifcopenshell.util.element.copy_deep(self.ifc, element)