Ifc Roof modifier

Added Ifc Roof modifier (also added it to shift-a menu and type manager). It works similar way to railing modifier - you define roof profile and then roof is generated.

Currently it has only 1 type of roof - HIP_ROOF (it utilizes bim.generate_hipped_roof operator).
This commit is contained in:
Andrej730
2023-03-07 10:44:24 +05:00
parent 5a6ae57833
commit daee666735
7 changed files with 546 additions and 66 deletions
@@ -37,6 +37,7 @@ from . import (
sverchok_modifier,
door,
railing,
roof,
)
classes = (
@@ -101,6 +102,7 @@ classes = (
prop.BIMWindowProperties,
prop.BIMDoorProperties,
prop.BIMRailingProperties,
prop.BIMRoofProperties,
ui.BIM_PT_authoring,
ui.BIM_PT_array,
ui.BIM_PT_stair,
@@ -108,6 +110,7 @@ classes = (
ui.BIM_PT_window,
ui.BIM_PT_door,
ui.BIM_PT_railing,
ui.BIM_PT_roof,
ui.LaunchTypeManager,
ui.HelpConstrTypes,
ui.BIM_MT_model,
@@ -150,6 +153,15 @@ classes = (
railing.FinishEditingRailingPath,
railing.EnableEditingRailingPath,
railing.RemoveRailing,
roof.BIM_OT_add_roof,
roof.AddRoof,
roof.CancelEditingRoof,
roof.FinishEditingRoof,
roof.EnableEditingRoof,
roof.CancelEditingRoofPath,
roof.FinishEditingRoofPath,
roof.EnableEditingRoofPath,
roof.RemoveRoof,
)
addon_keymaps = []
@@ -165,11 +177,13 @@ def register():
bpy.types.Object.BIMWindowProperties = bpy.props.PointerProperty(type=prop.BIMWindowProperties)
bpy.types.Object.BIMDoorProperties = bpy.props.PointerProperty(type=prop.BIMDoorProperties)
bpy.types.Object.BIMRailingProperties = bpy.props.PointerProperty(type=prop.BIMRailingProperties)
bpy.types.Object.BIMRoofProperties = bpy.props.PointerProperty(type=prop.BIMRoofProperties)
bpy.types.VIEW3D_MT_mesh_add.append(grid.add_object_button)
bpy.types.VIEW3D_MT_mesh_add.append(stair.add_object_button)
bpy.types.VIEW3D_MT_mesh_add.append(window.add_object_button)
bpy.types.VIEW3D_MT_mesh_add.append(door.add_object_button)
bpy.types.VIEW3D_MT_mesh_add.append(railing.add_object_button)
bpy.types.VIEW3D_MT_mesh_add.append(roof.add_object_button)
bpy.types.VIEW3D_MT_add.append(ui.add_menu)
bpy.app.handlers.load_post.append(handler.load_post)
wm = bpy.context.window_manager
@@ -190,12 +204,14 @@ def unregister():
del bpy.types.Object.BIMWindowProperties
del bpy.types.Object.BIMDoorProperties
del bpy.types.Object.BIMRailingProperties
del bpy.types.Object.BIMRoofProperties
bpy.app.handlers.load_post.remove(handler.load_post)
bpy.types.VIEW3D_MT_mesh_add.remove(grid.add_object_button)
bpy.types.VIEW3D_MT_mesh_add.remove(stair.add_object_button)
bpy.types.VIEW3D_MT_mesh_add.remove(window.add_object_button)
bpy.types.VIEW3D_MT_mesh_add.remove(door.add_object_button)
bpy.types.VIEW3D_MT_mesh_add.remove(railing.add_object_button)
bpy.types.VIEW3D_MT_mesh_add.remove(roof.add_object_button)
bpy.types.VIEW3D_MT_add.remove(ui.add_menu)
wm = bpy.context.window_manager
kc = wm.keyconfigs.addon
@@ -36,6 +36,7 @@ def refresh():
WindowData.is_loaded = False
DoorData.is_loaded = False
RailingData.is_loaded = False
RoofData.is_loaded = False
class AuthoringData:
@@ -483,3 +484,23 @@ class RailingData:
if parameters:
parameters["data_dict"] = json.loads(parameters.get("Data", "[]") or "[]")
return parameters
class RoofData:
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.is_loaded = True
cls.data = {"parameters": cls.parameters()}
@classmethod
def parameters(cls):
element = tool.Ifc.get_entity(bpy.context.active_object)
if element:
psets = ifcopenshell.util.element.get_psets(element)
parameters = psets.get("BBIM_Roof", None)
if parameters:
parameters["data_dict"] = json.loads(parameters.get("Data", "[]") or "[]")
return parameters
@@ -237,6 +237,7 @@ class BIMModelProperties(PropertyGroup):
("DOOR", "Door", ""),
("STAIR", "Stair", ""),
("RAILING", "Railing", ""),
("ROOF", "Roof", ""),
),
name="Type Template",
default="MESH",
@@ -564,3 +565,33 @@ class BIMRailingProperties(PropertyGroup):
"thickness": self.thickness,
"spacing": self.spacing,
}
class BIMRoofProperties(PropertyGroup):
roof_types = (("HIP_ROOF", "HIP_ROOF", ""),)
roof_generation_methods = (
("HEIGHT", "HEIGHT", ""),
("ANGLE", "ANGLE", ""),
)
roof_added_previously: bpy.props.BoolProperty(default=False)
is_editing: bpy.props.IntProperty(default=-1)
is_editing_path: bpy.props.BoolProperty(default=False)
roof_type: bpy.props.EnumProperty(name="Roof Type", items=roof_types, default="HIP_ROOF")
generation_method: bpy.props.EnumProperty(
name="Roof Generation Method", items=roof_generation_methods, default="HEIGHT"
)
height: bpy.props.FloatProperty(name="Height", default=1.0)
angle: bpy.props.FloatProperty(name="Slope Angle", default=10, description="In degrees")
def get_general_kwargs(self):
kwargs = {
"roof_type": self.roof_type,
"generation_method": self.generation_method,
}
if self.generation_method == "HEIGHT":
kwargs["height"] = self.height
else:
kwargs["angle"] = self.angle
return kwargs
@@ -243,7 +243,7 @@ class AddRailing(bpy.types.Operator, tool.Ifc.Operator):
# need to make sure all default props will have correct units
if not props.railing_added_previously:
skip_props = ("is_editing", "railing_type", "railing_added_previously", "path_data")
skip_props = ("is_editing", "railing_type", "railing_added_previously")
convert_property_group_from_si(props, skip_props=skip_props)
railing_data = props.get_general_kwargs()
@@ -260,6 +260,7 @@ class AddRailing(bpy.types.Operator, tool.Ifc.Operator):
railing_data["path_data"] = path_data
update_bbim_railing_pset(element, railing_data)
refresh()
update_railing_modifier_ifc_data(context)
update_railing_modifier_bmesh(context)
return {"FINISHED"}
@@ -1,5 +1,5 @@
# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>
# Copyright (C) 2023 Dion Moult <dion@thinkmoult.com>, @Andrej730
#
# This file is part of BlenderBIM Add-on.
#
@@ -16,14 +16,26 @@
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
from bpy.types import Operator
import bmesh
import shapely
import ifcopenshell
import blenderbim
import blenderbim.tool as tool
from blenderbim.bim.helper import convert_property_group_from_si
from blenderbim.bim.module.model.door import bm_sort_out_geom
from blenderbim.bim.module.model.data import RoofData, refresh
import json
from math import tan, radians
from mathutils import Vector, Matrix
from bpypolyskel import bpypolyskel
import shapely
# reference:
# https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcRoof.htm
# https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcRoofType.htm
class GenerateHippedRoof(bpy.types.Operator, tool.Ifc.Operator):
@@ -37,75 +49,393 @@ class GenerateHippedRoof(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context):
obj = bpy.context.active_object
if not obj:
return
self.report({"ERROR"}, "Need to select some object first.")
return {"CANCELLED"}
boundary_lines = []
bpy.ops.mesh.dissolve_limited()
generate_hipped_roof(obj, self.mode, self.height, self.angle)
return {"FINISHED"}
for edge in obj.data.edges:
boundary_lines.append(
shapely.LineString([obj.data.vertices[edge.vertices[0]].co, obj.data.vertices[edge.vertices[1]].co])
)
unioned_boundaries = shapely.union_all(shapely.GeometryCollection(boundary_lines))
closed_polygons = shapely.polygonize(unioned_boundaries.geoms)
def generate_hipped_roof(obj, mode="ANGLE", height=1.0, angle=10):
boundary_lines = []
roof_polygon = None
biggest_area = 0
for polygon in closed_polygons.geoms:
area = polygon.area
if area > biggest_area:
roof_polygon = polygon
biggest_area = area
roof_polygon = shapely.force_3d(roof_polygon)
if not shapely.is_ccw(roof_polygon):
roof_polygon = roof_polygon.reverse()
# Define vertices for the base footprint of the building at height 0.0
# counterclockwise order
verts = [Vector(v) for v in roof_polygon.exterior.coords[0:-1]]
total_exterior_verts = len(verts)
next_index = total_exterior_verts
inner_loops = None
for interior in roof_polygon.interiors:
if inner_loops is None:
inner_loops = []
loop = interior.coords[0:-1]
total_verts = len(loop)
verts.extend([Vector(v) for v in loop])
inner_loops.append((next_index, total_verts))
next_index += total_verts
unit_vectors = None # we have no unit vectors, let them computed by polygonize()
start_exterior_index = 0
faces = []
if self.mode == "HEIGHT":
height = self.height
angle = 0.0
else:
angle = tan(radians(round(self.angle, 4)))
height = 0.0
faces = bpypolyskel.polygonize(
verts, start_exterior_index, total_exterior_verts, inner_loops, height, angle, faces, unit_vectors
for edge in obj.data.edges:
boundary_lines.append(
shapely.LineString([obj.data.vertices[edge.vertices[0]].co, obj.data.vertices[edge.vertices[1]].co])
)
edges = []
mesh = bpy.data.meshes.new(name="Building_with_Roof")
mesh.from_pydata(verts, edges, faces)
unioned_boundaries = shapely.union_all(shapely.GeometryCollection(boundary_lines))
closed_polygons = shapely.polygonize(unioned_boundaries.geoms)
bm = bmesh.new()
bm.from_mesh(mesh)
roof_polygon = None
biggest_area = 0
for polygon in closed_polygons.geoms:
area = polygon.area
if area > biggest_area:
roof_polygon = polygon
biggest_area = area
extrusion = bmesh.ops.extrude_face_region(bm, geom=bm.faces)
extruded_verts = [g for g in extrusion["geom"] if isinstance(g, bmesh.types.BMVert)]
bmesh.ops.translate(bm, vec=[0.0, 0.0, 0.1], verts=extruded_verts)
roof_polygon = shapely.force_3d(roof_polygon)
bm.to_mesh(mesh)
bm.free()
if not shapely.is_ccw(roof_polygon):
roof_polygon = roof_polygon.reverse()
obj.data = mesh
# Define vertices for the base footprint of the building at height 0.0
# counterclockwise order
verts = [Vector(v) for v in roof_polygon.exterior.coords[0:-1]]
total_exterior_verts = len(verts)
next_index = total_exterior_verts
inner_loops = None
for interior in roof_polygon.interiors:
if inner_loops is None:
inner_loops = []
loop = interior.coords[0:-1]
total_verts = len(loop)
verts.extend([Vector(v) for v in loop])
inner_loops.append((next_index, total_verts))
next_index += total_verts
unit_vectors = None # we have no unit vectors, let them computed by polygonize()
start_exterior_index = 0
faces = []
if mode == "HEIGHT":
height = height
angle = 0.0
else:
angle = tan(radians(round(angle, 4)))
height = 0.0
faces = bpypolyskel.polygonize(
verts, start_exterior_index, total_exterior_verts, inner_loops, height, angle, faces, unit_vectors
)
edges = []
bm = tool.Blender.get_bmesh_for_mesh(obj.data, clean=True)
new_verts = [bm.verts.new(v) for v in verts]
new_edges = [bm.edges.new([new_verts[vi] for vi in edge]) for edge in edges]
new_faces = [bm.faces.new([new_verts[vi] for vi in face]) for face in faces]
extrusion_geom = bmesh.ops.extrude_face_region(bm, geom=bm.faces)["geom"]
extruded_verts = bm_sort_out_geom(extrusion_geom)["verts"]
bmesh.ops.translate(bm, vec=[0.0, 0.0, 0.1], verts=extruded_verts)
tool.Blender.apply_bmesh(obj.data, bm)
def bm_get_indices(sequence):
return [i.index for i in sequence]
def update_roof_modifier_ifc_data(context):
obj = context.active_object
props = obj.BIMRoofProperties
element = tool.Ifc.get_entity(obj)
ifc_file = tool.Ifc.get()
# type attributes
element.PredefinedType = props.roof_type
# occurences attributes
# occurences = tool.Ifc.get_all_element_occurences(element)
def update_bbim_roof_pset(element, roof_data):
pset = tool.Pset.get_element_pset(element, "BBIM_Roof")
if not pset:
pset = ifcopenshell.api.run("pset.add_pset", tool.Ifc.get(), product=element, name="BBIM_Roof")
roof_data = json.dumps(roof_data, default=list)
ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties={"Data": roof_data})
def update_roof_modifier_bmesh(context):
obj = context.object
props = obj.BIMRoofProperties
if not RoofData.is_loaded:
RoofData.load()
path_data = RoofData.data["parameters"]["data_dict"]["path_data"]
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
# need to make sure we support edit mode
# since users will probably be in edit mode when they'll be changing roof path
bm = tool.Blender.get_bmesh_for_mesh(obj.data, clean=True)
# generating roof path
new_verts = [bm.verts.new(Vector(v) * si_conversion) for v in path_data["verts"]]
new_edges = [bm.edges.new((new_verts[e[0]], new_verts[e[1]])) for e in path_data["edges"]]
if props.is_editing_path:
tool.Blender.apply_bmesh(obj.data, bm)
return
# apply dissolve limit seems to get more correct results with `generate_hipped_roof`
# argument values are the defaults for `bpy.ops.mesh.dissolve_limited`
bmesh.ops.dissolve_limit(
bm, angle_limit=0.0872665, use_dissolve_boundaries=False, delimit={"NORMAL"}, verts=bm.verts[:]
)
tool.Blender.apply_bmesh(obj.data, bm)
height = props.height * si_conversion
angle = props.angle * si_conversion
generation_method = props.generation_method
generate_hipped_roof(obj, generation_method, height, angle)
def get_path_data(obj):
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
if obj.mode == "EDIT":
# otherwise mesh may not contain all changes
# added in edit mode
obj.update_from_editmode()
bm = tool.Blender.get_bmesh_for_mesh(obj.data)
# remove internal edges and faces
# adding missing faces so we could rely on `e.is_boundary` later
bmesh.ops.contextual_create(bm, geom=bm.edges[:])
edges_to_dissolve = [e for e in bm.edges if not e.is_boundary]
bmesh.ops.dissolve_edges(bm, edges=edges_to_dissolve)
bmesh.ops.delete(bm, geom=bm.faces[:], context="FACES_ONLY")
path_data = dict()
path_data["edges"] = [bm_get_indices(e.verts) for e in bm.edges]
path_data["verts"] = [v.co / si_conversion for v in bm.verts]
if not path_data["edges"] or not path_data["verts"]:
return None
return path_data
class BIM_OT_add_roof(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "mesh.add_roof"
bl_label = "Roof"
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
ifc_file = tool.Ifc.get()
if not ifc_file:
self.report({"ERROR"}, "You need to start IFC project first to create a roof.")
return {"CANCELLED"}
if context.object is not None:
spawn_location = context.object.location.copy()
context.object.select_set(False)
else:
spawn_location = bpy.context.scene.cursor.location.copy()
mesh = bpy.data.meshes.new("IfcRoof")
obj = bpy.data.objects.new("IfcRoof", mesh)
obj.location = spawn_location
body_context = ifcopenshell.util.representation.get_context(ifc_file, "Model", "Body", "MODEL_VIEW")
blenderbim.core.root.assign_class(
tool.Ifc,
tool.Collector,
tool.Root,
obj=obj,
ifc_class="IfcRoof",
should_add_representation=True,
context=body_context,
)
bpy.ops.object.select_all(action="DESELECT")
bpy.context.view_layer.objects.active = None
bpy.context.view_layer.objects.active = obj
obj.select_set(True)
bpy.ops.bim.add_roof()
return {"FINISHED"}
# UI operators
class AddRoof(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.add_roof"
bl_label = "Add Roof"
bl_options = {"REGISTER"}
def _execute(self, context):
obj = context.active_object
element = tool.Ifc.get_entity(obj)
props = obj.BIMRoofProperties
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
if element.is_a() not in ("IfcRoof", "IfcRoofType"):
self.report({"ERROR"}, "Object has to be IfcRoof/IfcRoofType type to add a roof.")
return {"CANCELLED"}
# need to make sure all default props will have correct units
if not props.roof_added_previously:
skip_props = ("is_editing", "roof_type", "roof_added_previously", "generation_method")
convert_property_group_from_si(props, skip_props=skip_props)
roof_data = props.get_general_kwargs()
path_data = get_path_data(obj)
if not path_data:
path_data = {
"edges": [[0, 1], [1, 2], [2, 3], [3, 0]],
"verts": [
Vector([-5.0, -5.0, 0.0]) / si_conversion,
Vector([-5.0, 5.0, 0.0]) / si_conversion,
Vector([5.0, 5.0, 0.0]) / si_conversion,
Vector([5.0, -5.0, 0.0]) / si_conversion,
],
}
roof_data["path_data"] = path_data
update_bbim_roof_pset(element, roof_data)
update_roof_modifier_ifc_data(context)
refresh()
update_roof_modifier_bmesh(context)
return {"FINISHED"}
class EnableEditingRoof(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.enable_editing_roof"
bl_label = "Enable Editing Roof"
bl_options = {"REGISTER"}
def _execute(self, context):
obj = context.active_object
props = obj.BIMRoofProperties
element = tool.Ifc.get_entity(obj)
data = json.loads(ifcopenshell.util.element.get_pset(element, "BBIM_Roof", "Data"))
data["path_data"] = json.dumps(data["path_data"])
# required since we could load pset from .ifc and BIMRoofProperties won't be set
for prop_name in data:
setattr(props, prop_name, data[prop_name])
# need to make sure all props that weren't used before
# will have correct units
skip_props = ("is_editing", "roof_type", "roof_added_previously", "generation_method")
skip_props += tuple(data.keys())
convert_property_group_from_si(props, skip_props=skip_props)
props.is_editing = 1
return {"FINISHED"}
class CancelEditingRoof(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.cancel_editing_roof"
bl_label = "Cancel editing Roof"
bl_options = {"REGISTER"}
def _execute(self, context):
obj = context.active_object
element = tool.Ifc.get_entity(obj)
data = json.loads(ifcopenshell.util.element.get_pset(element, "BBIM_Roof", "Data"))
props = obj.BIMRoofProperties
# restore previous settings since editing was canceled
for prop_name in data:
setattr(props, prop_name, data[prop_name])
body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
blenderbim.core.geometry.switch_representation(
tool.Ifc,
tool.Geometry,
obj=obj,
representation=body,
should_reload=True,
is_global=True,
should_sync_changes_first=False,
)
props.is_editing = -1
return {"FINISHED"}
class FinishEditingRoof(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.finish_editing_roof"
bl_label = "Finish editing roof"
bl_options = {"REGISTER"}
def _execute(self, context):
obj = context.active_object
element = tool.Ifc.get_entity(obj)
props = obj.BIMRoofProperties
if not RoofData.is_loaded:
RoofData.load()
path_data = RoofData.data["parameters"]["data_dict"]["path_data"]
roof_data = props.get_general_kwargs()
roof_data["path_data"] = path_data
props.is_editing = -1
update_bbim_roof_pset(element, roof_data)
update_roof_modifier_ifc_data(context)
return {"FINISHED"}
class EnableEditingRoofPath(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.enable_editing_roof_path"
bl_label = "Enable Editing Roof Path"
bl_options = {"REGISTER"}
def _execute(self, context):
obj = context.active_object
props = obj.BIMRoofProperties
element = tool.Ifc.get_entity(obj)
props.is_editing_path = True
update_roof_modifier_bmesh(context)
return {"FINISHED"}
class CancelEditingRoofPath(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.cancel_editing_roof_path"
bl_label = "Cancel Editing Roof Path"
bl_options = {"REGISTER"}
def _execute(self, context):
obj = context.active_object
element = tool.Ifc.get_entity(obj)
props = obj.BIMRoofProperties
update_roof_modifier_bmesh(context)
props.is_editing_path = False
return {"FINISHED"}
class FinishEditingRoofPath(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.finish_editing_roof_path"
bl_label = "Finish Editing Roof Path"
bl_options = {"REGISTER"}
def _execute(self, context):
obj = context.active_object
element = tool.Ifc.get_entity(obj)
props = obj.BIMRoofProperties
roof_data = props.get_general_kwargs()
path_data = get_path_data(obj)
roof_data["path_data"] = path_data
props.is_editing_path = False
update_bbim_roof_pset(element, roof_data)
refresh() # need RoofData to be updated before update_roof_modifier_bmesh
update_roof_modifier_bmesh(context)
return {"FINISHED"}
class RemoveRoof(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.remove_roof"
bl_label = "Remove Roof"
bl_options = {"REGISTER"}
def _execute(self, context):
obj = context.active_object
props = obj.BIMRoofProperties
element = tool.Ifc.get_entity(obj)
obj.BIMRoofProperties.is_editing = -1
pset = tool.Pset.get_element_pset(element, "BBIM_Roof")
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), pset=pset)
props.roof_added_previously = True
return {"FINISHED"}
def add_object_button(self, context):
self.layout.operator(BIM_OT_add_roof.bl_idname, icon="PLUGIN")
@@ -27,12 +27,14 @@ from blenderbim.bim.module.model.data import (
WindowData,
DoorData,
RailingData,
RoofData,
)
from blenderbim.bim.module.model.prop import get_ifc_class
from blenderbim.bim.module.model.stair import update_stair_modifier
from blenderbim.bim.module.model.window import update_window_modifier_bmesh
from blenderbim.bim.module.model.door import update_door_modifier_bmesh
from blenderbim.bim.module.model.railing import update_railing_modifier_bmesh
from blenderbim.bim.module.model.roof import update_roof_modifier_bmesh
from blenderbim.bim.helper import prop_with_search
@@ -597,6 +599,65 @@ class BIM_PT_railing(bpy.types.Panel):
row.operator("bim.add_railing", icon="ADD", text="")
class BIM_PT_roof(bpy.types.Panel):
bl_label = "IFC Roof"
bl_idname = "BIM_PT_roof"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "modifier"
bl_options = {"DEFAULT_CLOSED"}
@classmethod
def poll(cls, context):
# always display modifier if it's IFC object
return tool.Ifc.get() and tool.Ifc.get_entity(context.active_object)
def draw(self, context):
if not RoofData.is_loaded:
RoofData.load()
props = context.active_object.BIMRoofProperties
if RoofData.data["parameters"]:
row = self.layout.row(align=True)
row.label(text="Roof parameters", icon="OUTLINER_OB_LATTICE")
roof_data = RoofData.data["parameters"]["data_dict"]
if props.is_editing != -1:
row = self.layout.row(align=True)
row.operator("bim.finish_editing_roof", icon="CHECKMARK", text="Finish editing")
row.operator("bim.cancel_editing_roof", icon="CANCEL", text="")
general_props = props.get_general_kwargs()
for prop in general_props:
self.layout.prop(props, prop)
update_roof_modifier_bmesh(context)
elif props.is_editing_path:
row.operator("bim.finish_editing_roof_path", icon="CHECKMARK", text="Finish editing path")
row.operator("bim.cancel_editing_roof_path", icon="CANCEL", text="")
else:
row.operator("bim.enable_editing_roof", icon="GREASEPENCIL", text="")
row.operator("bim.enable_editing_roof_path", icon="ANIM", text="")
row.operator("bim.remove_roof", icon="X", text="")
box = self.layout.box()
general_props = props.get_general_kwargs()
for prop in general_props:
prop_value = roof_data[prop]
prop_value = round(prop_value, 5) if type(prop_value) is float else prop_value
row = box.row(align=True)
row.label(text=f"{props.bl_rna.properties[prop].name}")
row.label(text=str(prop_value))
else:
row = self.layout.row()
row.label(text="No Roof Found")
row.operator("bim.add_roof", icon="ADD", text="")
class BIM_MT_model(Menu):
bl_idname = "BIM_MT_model"
bl_label = "IFC Objects"
@@ -279,6 +279,7 @@ class AddType(bpy.types.Operator, tool.Ifc.Operator):
context=body,
ifc_representation_class=None,
)
elif template == "WINDOW":
mesh = bpy.data.meshes.new("IfcWindow")
obj = bpy.data.objects.new("TYPEX", mesh)
@@ -353,6 +354,25 @@ class AddType(bpy.types.Operator, tool.Ifc.Operator):
obj.select_set(True)
bpy.ops.bim.add_railing()
elif template == "ROOF":
mesh = bpy.data.meshes.new("IfcRoof")
obj = bpy.data.objects.new("TYPEX", mesh)
element = blenderbim.core.root.assign_class(
tool.Ifc,
tool.Collector,
tool.Root,
obj=obj,
predefined_type=predefined_type,
ifc_class="IfcRoofType",
should_add_representation=True,
context=body,
)
bpy.ops.object.select_all(action="DESELECT")
bpy.context.view_layer.objects.active = None
bpy.context.view_layer.objects.active = obj
obj.select_set(True)
bpy.ops.bim.add_roof()
bpy.ops.bim.load_type_thumbnails(ifc_class=ifc_class)
return {"FINISHED"}