mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-19 14:41:25 +00:00
Merge pull request #2315 from cvillagrasa/v0.7.0
Addition of Construction Type Browser
This commit is contained in:
@@ -118,6 +118,17 @@ def prop_with_search(layout, data, prop_name, **kwargs):
|
|||||||
op.prop_name = prop_name
|
op.prop_name = prop_name
|
||||||
|
|
||||||
|
|
||||||
|
def layout_with_margins(layout, margin_left=0.025, margin_right=None):
|
||||||
|
margin_right = margin_left if margin_right is None else margin_right
|
||||||
|
split = layout.split(factor=margin_left, align=True)
|
||||||
|
cols = [split.column() for _ in range(2)]
|
||||||
|
cols[0].label(text="")
|
||||||
|
subsplit = cols[-1].split(factor=(1. - margin_right), align=True)
|
||||||
|
subcol = subsplit.column()
|
||||||
|
subsplit.column().label(text="")
|
||||||
|
return subcol
|
||||||
|
|
||||||
|
|
||||||
def get_enum_items(data, prop_name, context):
|
def get_enum_items(data, prop_name, context):
|
||||||
# Retrieve items from a dynamic EnumProperty, which is otherwise not supported
|
# Retrieve items from a dynamic EnumProperty, which is otherwise not supported
|
||||||
# Or throws an error in the console when the items callback returns an empty list
|
# Or throws an error in the console when the items callback returns an empty list
|
||||||
|
|||||||
@@ -17,11 +17,14 @@
|
|||||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
from . import handler, prop, ui, grid, product, wall, slab, stair, opening, pie, workspace
|
from . import handler, prop, ui, grid, product, wall, slab, stair, opening, pie, workspace, profile
|
||||||
|
|
||||||
classes = (
|
classes = (
|
||||||
product.AddEmptyType,
|
product.AddEmptyType,
|
||||||
product.AddTypeInstance,
|
product.AddConstrTypeInstance,
|
||||||
|
product.DisplayConstrTypes,
|
||||||
|
product.SelectConstructionType,
|
||||||
|
product.HelpConstrTypes,
|
||||||
product.AlignProduct,
|
product.AlignProduct,
|
||||||
product.DynamicallyVoidProduct,
|
product.DynamicallyVoidProduct,
|
||||||
workspace.Hotkey,
|
workspace.Hotkey,
|
||||||
@@ -32,8 +35,8 @@ classes = (
|
|||||||
opening.AddElementOpening,
|
opening.AddElementOpening,
|
||||||
profile.ExtendProfile,
|
profile.ExtendProfile,
|
||||||
prop.BIMModelProperties,
|
prop.BIMModelProperties,
|
||||||
|
prop.ConstrTypeInfo,
|
||||||
ui.BIM_PT_authoring,
|
ui.BIM_PT_authoring,
|
||||||
ui.BIM_PT_authoring_architectural,
|
|
||||||
grid.BIM_OT_add_object,
|
grid.BIM_OT_add_object,
|
||||||
stair.BIM_OT_add_object,
|
stair.BIM_OT_add_object,
|
||||||
opening.BIM_OT_add_object,
|
opening.BIM_OT_add_object,
|
||||||
@@ -51,6 +54,7 @@ def register():
|
|||||||
if not bpy.app.background:
|
if not bpy.app.background:
|
||||||
bpy.utils.register_tool(workspace.BimTool, after={"builtin.scale_cage"}, separator=True, group=True)
|
bpy.utils.register_tool(workspace.BimTool, after={"builtin.scale_cage"}, separator=True, group=True)
|
||||||
bpy.types.Scene.BIMModelProperties = bpy.props.PointerProperty(type=prop.BIMModelProperties)
|
bpy.types.Scene.BIMModelProperties = bpy.props.PointerProperty(type=prop.BIMModelProperties)
|
||||||
|
bpy.types.Scene.ConstrTypeInfo = bpy.props.CollectionProperty(type=prop.ConstrTypeInfo)
|
||||||
bpy.types.VIEW3D_MT_mesh_add.append(grid.add_object_button)
|
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(stair.add_object_button)
|
||||||
bpy.types.VIEW3D_MT_mesh_add.append(opening.add_object_button)
|
bpy.types.VIEW3D_MT_mesh_add.append(opening.add_object_button)
|
||||||
@@ -68,6 +72,7 @@ def unregister():
|
|||||||
if not bpy.app.background:
|
if not bpy.app.background:
|
||||||
bpy.utils.unregister_tool(workspace.BimTool)
|
bpy.utils.unregister_tool(workspace.BimTool)
|
||||||
del bpy.types.Scene.BIMModelProperties
|
del bpy.types.Scene.BIMModelProperties
|
||||||
|
del bpy.types.Scene.ConstrTypeInfo
|
||||||
bpy.app.handlers.load_post.remove(handler.load_post)
|
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(grid.add_object_button)
|
||||||
bpy.types.VIEW3D_MT_mesh_add.remove(stair.add_object_button)
|
bpy.types.VIEW3D_MT_mesh_add.remove(stair.add_object_button)
|
||||||
|
|||||||
@@ -16,8 +16,14 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# 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/>.
|
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import functools
|
||||||
import bpy
|
import bpy
|
||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
|
||||||
|
|
||||||
|
preview_icon_ids = {}
|
||||||
|
attempts = 0
|
||||||
|
|
||||||
|
|
||||||
def refresh():
|
def refresh():
|
||||||
@@ -31,10 +37,29 @@ class AuthoringData:
|
|||||||
@classmethod
|
@classmethod
|
||||||
def load(cls):
|
def load(cls):
|
||||||
cls.is_loaded = True
|
cls.is_loaded = True
|
||||||
cls.data = {
|
if not hasattr(cls, "data"):
|
||||||
"ifc_classes": cls.ifc_classes(),
|
cls.data = {}
|
||||||
"relating_types": cls.relating_types(),
|
cls.props = bpy.context.scene.BIMModelProperties
|
||||||
}
|
cls.load_ifc_classes()
|
||||||
|
cls.load_relating_types()
|
||||||
|
cls.load_relating_types_browser()
|
||||||
|
cls.load_preview_constr_types()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def load_ifc_classes(cls):
|
||||||
|
cls.data["ifc_classes"] = cls.ifc_classes()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def load_relating_types(cls):
|
||||||
|
cls.data["relating_types_ids"] = cls.relating_types()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def load_relating_types_browser(cls):
|
||||||
|
cls.data["relating_types_ids_browser"] = cls.relating_types_browser()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def load_preview_constr_types(cls):
|
||||||
|
cls.data["preview_constr_types"] = preview_icon_ids
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def ifc_classes(cls):
|
def ifc_classes(cls):
|
||||||
@@ -49,16 +74,131 @@ class AuthoringData:
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def relating_types(cls):
|
def constr_class_entities(cls, ifc_class=None):
|
||||||
ifc_classes = cls.ifc_classes()
|
ifc_classes = cls.data["ifc_classes"]
|
||||||
if not ifc_classes:
|
if not ifc_classes:
|
||||||
return []
|
return []
|
||||||
results = []
|
results = []
|
||||||
ifc_class = bpy.context.scene.BIMModelProperties.ifc_class
|
if ifc_class is None:
|
||||||
|
ifc_class = cls.props.ifc_class
|
||||||
if not ifc_class and ifc_classes:
|
if not ifc_class and ifc_classes:
|
||||||
ifc_class = ifc_classes[0][0]
|
ifc_class = ifc_classes[0][0]
|
||||||
if ifc_class:
|
if ifc_class:
|
||||||
elements = [(str(e.id()), e.Name, e.Description or "") for e in tool.Ifc.get().by_type(ifc_class)]
|
elements = sorted(tool.Ifc.get().by_type(ifc_class), key=lambda s: s.Name)
|
||||||
results.extend(sorted(elements, key=lambda s: s[1]))
|
results.extend(elements)
|
||||||
return results
|
return results
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def relating_types(cls, ifc_class=None):
|
||||||
|
return [
|
||||||
|
(str(e.id()), e.Name, e.Description or "") for e in cls.constr_class_entities(ifc_class=ifc_class)
|
||||||
|
]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def relating_types_browser(cls):
|
||||||
|
return cls.relating_types(ifc_class=cls.props.ifc_class_browser)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def new_relating_type_info(ifc_class):
|
||||||
|
relating_type_info = bpy.context.scene.ConstrTypeInfo.add()
|
||||||
|
relating_type_info.name = ifc_class
|
||||||
|
return relating_type_info
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def assetize_constr_class(cls, ifc_class=None):
|
||||||
|
if ifc_class is None:
|
||||||
|
ifc_class = cls.props.ifc_class
|
||||||
|
relating_type_info = cls.relating_type_info(ifc_class)
|
||||||
|
_ = cls.new_relating_type_info(ifc_class) if relating_type_info is None else relating_type_info
|
||||||
|
constr_class_occurrences = cls.constr_class_entities(ifc_class)
|
||||||
|
preview_constr_types = cls.data["preview_constr_types"]
|
||||||
|
for constr_class_entity in constr_class_occurrences:
|
||||||
|
|
||||||
|
### handle asset regeneration when library entity is updated ¿?
|
||||||
|
if (ifc_class not in preview_constr_types
|
||||||
|
or str(constr_class_entity.id()) not in preview_constr_types[ifc_class]):
|
||||||
|
obj = tool.Ifc.get_object(constr_class_entity)
|
||||||
|
cls.assetize_object(obj, ifc_class, constr_class_entity)
|
||||||
|
relating_type_info = cls.relating_type_info(ifc_class)
|
||||||
|
relating_type_info.fully_loaded = True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def assetize_object(cls, obj, ifc_class, ifc_class_entity, from_selection=False):
|
||||||
|
relating_type_id = ifc_class_entity.id()
|
||||||
|
to_be_deleted = False
|
||||||
|
if obj.type == 'EMPTY':
|
||||||
|
kwargs = {}
|
||||||
|
if not from_selection:
|
||||||
|
kwargs.update({'ifc_class': ifc_class, 'relating_type_id': relating_type_id})
|
||||||
|
new_obj = cls.new_relating_type(**kwargs)
|
||||||
|
if new_obj is not None:
|
||||||
|
to_be_deleted = True
|
||||||
|
obj = new_obj
|
||||||
|
obj.asset_mark()
|
||||||
|
obj.asset_generate_preview()
|
||||||
|
icon_id = obj.preview.icon_id
|
||||||
|
if ifc_class not in cls.data["preview_constr_types"]:
|
||||||
|
cls.data["preview_constr_types"][ifc_class] = {}
|
||||||
|
cls.data["preview_constr_types"][ifc_class][str(relating_type_id)] = {"icon_id": icon_id, "object": obj}
|
||||||
|
if to_be_deleted:
|
||||||
|
for col in obj.users_collection:
|
||||||
|
col.objects.unlink(obj)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def assetize_relating_type_from_selection(cls):
|
||||||
|
ifc_class_browser = cls.props.ifc_class_browser
|
||||||
|
relating_type_id_browser = cls.props.relating_type_id_browser
|
||||||
|
constr_class_occurrences = cls.constr_class_entities(ifc_class=ifc_class_browser)
|
||||||
|
constr_class_occurrences = [
|
||||||
|
entity for entity in constr_class_occurrences if entity.id() == int(relating_type_id_browser)
|
||||||
|
]
|
||||||
|
if len(constr_class_occurrences) == 0:
|
||||||
|
return False
|
||||||
|
constr_class_entity = constr_class_occurrences[0]
|
||||||
|
obj = tool.Ifc.get_object(constr_class_entity)
|
||||||
|
if obj is None:
|
||||||
|
return False
|
||||||
|
cls.assetize_object(obj, ifc_class_browser, constr_class_entity, from_selection=True)
|
||||||
|
return True
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def relating_type_info(ifc_class):
|
||||||
|
relating_type_infos = [element for element in bpy.context.scene.ConstrTypeInfo if element.name == ifc_class]
|
||||||
|
return None if len(relating_type_infos) == 0 else relating_type_infos[0]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def new_relating_type(cls, ifc_class=None, relating_type_id=None):
|
||||||
|
if ifc_class is None:
|
||||||
|
bpy.ops.bim.add_constr_type_instance(
|
||||||
|
ifc_class=cls.props.ifc_class_browser, relating_type_id=int(cls.props.relating_type_id_browser)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
cls.props.ifc_class = ifc_class
|
||||||
|
cls.props.relating_type_id = str(relating_type_id)
|
||||||
|
bpy.ops.bim.add_constr_type_instance()
|
||||||
|
return bpy.context.selected_objects[-1]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def relating_type_name_by_id(ifc_class, relating_type_id):
|
||||||
|
file = IfcStore.get_file()
|
||||||
|
try:
|
||||||
|
constr_class_entity = file.by_id(int(relating_type_id))
|
||||||
|
except (RuntimeError, ValueError):
|
||||||
|
return None
|
||||||
|
return constr_class_entity.Name if constr_class_entity.is_a() == ifc_class else None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def relating_type_id_by_name(cls, ifc_class, relating_type):
|
||||||
|
relating_types = [ct[0] for ct in cls.relating_types(ifc_class=ifc_class) if ct[1] == relating_type]
|
||||||
|
return None if len(relating_types) == 0 else relating_types[0]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def consolidate_relating_type(cls):
|
||||||
|
cls.props.ifc_class = cls.props.ifc_class_browser
|
||||||
|
cls.props.relating_type_id = cls.props.relating_type_id_browser
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setup_relating_type_browser(cls):
|
||||||
|
cls.props.ifc_class_browser = cls.props.ifc_class
|
||||||
|
cls.props.relating_type_id_browser = cls.props.relating_type_id
|
||||||
|
|||||||
@@ -114,5 +114,13 @@ class MepGenerator:
|
|||||||
tool.Ifc.run("system.assign_port", element=element, port=port)
|
tool.Ifc.run("system.assign_port", element=element, port=port)
|
||||||
tool.Ifc.run("geometry.edit_object_placement", product=port, matrix=obj.matrix_world @ mat, is_si=True)
|
tool.Ifc.run("geometry.edit_object_placement", product=port, matrix=obj.matrix_world @ mat, is_si=True)
|
||||||
|
|
||||||
obj.select_set(True)
|
try:
|
||||||
|
obj.select_set(True)
|
||||||
|
except RuntimeError:
|
||||||
|
def msg(self, context):
|
||||||
|
txt = "The created object could not be assigned to a collection. "
|
||||||
|
txt += "Has any IfcSpatialElement been deleted?"
|
||||||
|
self.layout.label(text=txt)
|
||||||
|
|
||||||
|
bpy.context.window_manager.popup_menu(msg, title="Error", icon="ERROR")
|
||||||
return obj
|
return obj
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
|
import math
|
||||||
import mathutils
|
import mathutils
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import ifcopenshell.api
|
import ifcopenshell.api
|
||||||
@@ -29,9 +30,12 @@ import blenderbim.core.type
|
|||||||
import blenderbim.core.geometry
|
import blenderbim.core.geometry
|
||||||
from . import wall, slab, profile, mep
|
from . import wall, slab, profile, mep
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
from blenderbim.bim.module.model.data import AuthoringData
|
||||||
|
from blenderbim.bim.helper import prop_with_search, layout_with_margins, close_operator_panel
|
||||||
from ifcopenshell.api.pset.data import Data as PsetData
|
from ifcopenshell.api.pset.data import Data as PsetData
|
||||||
from mathutils import Vector, Matrix
|
from mathutils import Vector, Matrix
|
||||||
from bpy_extras.object_utils import AddObjectHelper
|
from bpy_extras.object_utils import AddObjectHelper
|
||||||
|
from . import prop
|
||||||
|
|
||||||
|
|
||||||
class AddEmptyType(bpy.types.Operator, AddObjectHelper):
|
class AddEmptyType(bpy.types.Operator, AddObjectHelper):
|
||||||
@@ -53,13 +57,19 @@ def add_empty_type_button(self, context):
|
|||||||
self.layout.operator(AddEmptyType.bl_idname, icon="FILE_3D")
|
self.layout.operator(AddEmptyType.bl_idname, icon="FILE_3D")
|
||||||
|
|
||||||
|
|
||||||
class AddTypeInstance(bpy.types.Operator):
|
class AddConstrTypeInstance(bpy.types.Operator):
|
||||||
bl_idname = "bim.add_type_instance"
|
bl_idname = "bim.add_constr_type_instance"
|
||||||
bl_label = "Add Type Instance"
|
bl_label = "Add"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
bl_description = "Add the selected Type Instance to the model"
|
bl_description = "Add Type Instance to the model"
|
||||||
ifc_class: bpy.props.StringProperty()
|
ifc_class: bpy.props.StringProperty()
|
||||||
relating_type: bpy.props.IntProperty()
|
relating_type_id: bpy.props.IntProperty()
|
||||||
|
from_invoke: bpy.props.BoolProperty(default=False)
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
if self.from_invoke:
|
||||||
|
close_operator_panel(event)
|
||||||
|
return self.execute(context)
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
return IfcStore.execute_ifc_operator(self, context)
|
return IfcStore.execute_ifc_operator(self, context)
|
||||||
@@ -67,11 +77,15 @@ class AddTypeInstance(bpy.types.Operator):
|
|||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
props = context.scene.BIMModelProperties
|
props = context.scene.BIMModelProperties
|
||||||
ifc_class = self.ifc_class or props.ifc_class
|
ifc_class = self.ifc_class or props.ifc_class
|
||||||
relating_type_id = self.relating_type or props.relating_type
|
relating_type_id = self.relating_type_id or props.relating_type_id
|
||||||
|
|
||||||
if not ifc_class or not relating_type_id:
|
if not ifc_class or not relating_type_id:
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
if self.from_invoke:
|
||||||
|
props.ifc_class = self.ifc_class
|
||||||
|
props.relating_type_id = str(self.relating_type_id)
|
||||||
|
|
||||||
self.file = IfcStore.get_file()
|
self.file = IfcStore.get_file()
|
||||||
instance_class = ifcopenshell.util.type.get_applicable_entities(ifc_class, self.file.schema)[0]
|
instance_class = ifcopenshell.util.type.get_applicable_entities(ifc_class, self.file.schema)[0]
|
||||||
relating_type = self.file.by_id(int(relating_type_id))
|
relating_type = self.file.by_id(int(relating_type_id))
|
||||||
@@ -150,7 +164,8 @@ class AddTypeInstance(bpy.types.Operator):
|
|||||||
context.view_layer.objects.active = obj
|
context.view_layer.objects.active = obj
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
def generate_layered_element(self, ifc_class, relating_type):
|
@staticmethod
|
||||||
|
def generate_layered_element(ifc_class, relating_type):
|
||||||
layer_set_direction = None
|
layer_set_direction = None
|
||||||
|
|
||||||
parametric = ifcopenshell.util.element.get_psets(relating_type).get("EPset_Parametric")
|
parametric = ifcopenshell.util.element.get_psets(relating_type).get("EPset_Parametric")
|
||||||
@@ -172,6 +187,217 @@ class AddTypeInstance(bpy.types.Operator):
|
|||||||
pass # Dumb block generator? Eh? :)
|
pass # Dumb block generator? Eh? :)
|
||||||
|
|
||||||
|
|
||||||
|
class DisplayConstrTypes(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.display_relating_types"
|
||||||
|
bl_label = "Browse Construction Types"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
bl_description = "Display all available Construction Types to add new instances"
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
if not AuthoringData.is_loaded:
|
||||||
|
AuthoringData.load()
|
||||||
|
AuthoringData.setup_relating_type_browser()
|
||||||
|
props = context.scene.BIMModelProperties
|
||||||
|
if props.unfold_relating_types:
|
||||||
|
ifc_class = props.ifc_class_browser
|
||||||
|
relating_type_info = AuthoringData.relating_type_info(ifc_class)
|
||||||
|
if relating_type_info is None or not relating_type_info.fully_loaded:
|
||||||
|
AuthoringData.assetize_constr_class(ifc_class)
|
||||||
|
else:
|
||||||
|
prop.update_relating_type_browser(props, context)
|
||||||
|
min_width = 250
|
||||||
|
width_scaling = 5 ** -1
|
||||||
|
width = max([min_width, int(width_scaling * context.region.width)])
|
||||||
|
return context.window_manager.invoke_popup(self, width=width)
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
props = context.scene.BIMModelProperties
|
||||||
|
header_data = self.draw_header(props)
|
||||||
|
if props.unfold_relating_types:
|
||||||
|
self.draw_by_ifc_class(props, header_data)
|
||||||
|
else:
|
||||||
|
self.draw_by_ifc_class_and_type(props, header_data)
|
||||||
|
|
||||||
|
def draw_header(self, props):
|
||||||
|
layout = self.layout
|
||||||
|
inner_layout = layout_with_margins(layout, margin_left=0.004)
|
||||||
|
inner_layout.row().separator(factor=0.75)
|
||||||
|
split = inner_layout.split(align=True, factor=2./3)
|
||||||
|
col1 = split.column(align=True)
|
||||||
|
row = col1.row()
|
||||||
|
row.prop(data=props, property="unfold_relating_types", text="Preview All Construction Types")
|
||||||
|
col1.row().separator(factor=1)
|
||||||
|
row = col1.row()
|
||||||
|
row.label(text="Select Construction Type:")
|
||||||
|
col1.row().separator(factor=1.5)
|
||||||
|
enabled = True
|
||||||
|
if AuthoringData.data["ifc_classes"]:
|
||||||
|
subsplit = col1.split(factor=1./3)
|
||||||
|
subsplit.column().row().label(text="Construction Class:", icon="FILE_VOLUME")
|
||||||
|
prop_with_search(subsplit.column(), props, "ifc_class_browser", text="")
|
||||||
|
col1.row().separator()
|
||||||
|
else:
|
||||||
|
enabled = False
|
||||||
|
col2 = split.column(align=True)
|
||||||
|
subsplit = col2.split(factor=0.9)
|
||||||
|
subcol = [subsplit.column() for _ in range(2)][-1]
|
||||||
|
subcol.operator("bim.help_relating_types", text="", icon="QUESTION")
|
||||||
|
col2.row().separator(factor=1)
|
||||||
|
return {"enabled": enabled, "layout": inner_layout, "col1": col1, "col2": col2}
|
||||||
|
|
||||||
|
def draw_by_ifc_class(self, props, header_data):
|
||||||
|
enabled, layout = [header_data[key] for key in ["enabled", "layout"]]
|
||||||
|
ifc_class_browser = props.ifc_class_browser
|
||||||
|
num_cols = 3
|
||||||
|
layout.row().separator(factor=0.25)
|
||||||
|
layout.row().label(text="Construction Types:", icon="FILE_3D")
|
||||||
|
layout.row().separator(factor=0.25)
|
||||||
|
flow = layout.grid_flow(row_major=True, columns=num_cols, even_columns=True, even_rows=True, align=True)
|
||||||
|
relating_types_browser = AuthoringData.relating_types_browser()
|
||||||
|
num_types = len(relating_types_browser)
|
||||||
|
for idx, (relating_type_id_browser, name, desc) in enumerate(relating_types_browser):
|
||||||
|
outer_col = flow.column()
|
||||||
|
box = outer_col.box()
|
||||||
|
row = box.row()
|
||||||
|
row.label(text=name, icon="FILE_3D")
|
||||||
|
row.alignment = "CENTER"
|
||||||
|
row = box.row()
|
||||||
|
if enabled:
|
||||||
|
preview_constr_types = AuthoringData.data["preview_constr_types"]
|
||||||
|
if ifc_class_browser in preview_constr_types:
|
||||||
|
preview_ifc_class = preview_constr_types[ifc_class_browser]
|
||||||
|
if relating_type_id_browser in preview_ifc_class:
|
||||||
|
icon_id = preview_ifc_class[relating_type_id_browser]["icon_id"]
|
||||||
|
row.template_icon(icon_value=icon_id, scale=6.)
|
||||||
|
box.row().separator(factor=0.2)
|
||||||
|
row = box.row()
|
||||||
|
split = row.split(factor=0.5)
|
||||||
|
col = split.column()
|
||||||
|
op = col.operator("bim.select_construction_type", icon="RIGHTARROW_THIN")
|
||||||
|
op.ifc_class = ifc_class_browser
|
||||||
|
op.relating_type_id = relating_type_id_browser
|
||||||
|
col = split.column()
|
||||||
|
op = col.operator("bim.add_constr_type_instance", icon="ADD")
|
||||||
|
op.from_invoke = True
|
||||||
|
op.ifc_class = ifc_class_browser
|
||||||
|
if relating_type_id_browser.isnumeric():
|
||||||
|
op.relating_type_id = int(relating_type_id_browser)
|
||||||
|
factor = 2 if idx + 1 < math.ceil(num_types / num_cols) else 1.5
|
||||||
|
outer_col.row().separator(factor=factor)
|
||||||
|
last_row_cols = num_types % num_cols
|
||||||
|
if last_row_cols != 0:
|
||||||
|
for _ in range(num_cols - last_row_cols):
|
||||||
|
flow.column()
|
||||||
|
|
||||||
|
def draw_by_ifc_class_and_type(self, props, header_data):
|
||||||
|
enabled, col1, col2 = [header_data[key] for key in ["enabled", "col1", "col2"]]
|
||||||
|
ifc_class_browser = props.ifc_class_browser
|
||||||
|
relating_type_id_browser = props.relating_type_id_browser
|
||||||
|
if AuthoringData.data["relating_types_ids_browser"]:
|
||||||
|
subsplit = col1.split(factor=1. / 3)
|
||||||
|
subsplit.column().row().label(text="Construction Type:", icon="FILE_3D")
|
||||||
|
prop_with_search(subsplit.column(), props, "relating_type_id_browser", text="")
|
||||||
|
col1.row().separator()
|
||||||
|
else:
|
||||||
|
enabled = False
|
||||||
|
col1.row().separator(factor=4.75)
|
||||||
|
row = col1.row()
|
||||||
|
row.enabled = enabled
|
||||||
|
op = row.operator("bim.select_construction_type", icon="RIGHTARROW_THIN")
|
||||||
|
op.ifc_class = ifc_class_browser
|
||||||
|
op.relating_type_id = relating_type_id_browser
|
||||||
|
op = row.operator("bim.add_constr_type_instance", icon="ADD")
|
||||||
|
op.from_invoke = True
|
||||||
|
op.ifc_class = ifc_class_browser
|
||||||
|
if relating_type_id_browser.isnumeric():
|
||||||
|
op.relating_type_id = int(relating_type_id_browser)
|
||||||
|
col2.row().separator(factor=1.25)
|
||||||
|
split = col2.split(factor=0.025)
|
||||||
|
col = [split.column() for _ in range(2)][-1]
|
||||||
|
box = col.box()
|
||||||
|
if enabled:
|
||||||
|
box.template_icon(icon_value=props.icon_id, scale=5.6)
|
||||||
|
col1.row().separator(factor=1)
|
||||||
|
|
||||||
|
|
||||||
|
class SelectConstructionType(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.select_construction_type"
|
||||||
|
bl_label = "Select"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
bl_description = "Pick Type Instance as selection for subsequent operations"
|
||||||
|
ifc_class: bpy.props.StringProperty()
|
||||||
|
relating_type_id: bpy.props.StringProperty()
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
close_operator_panel(event)
|
||||||
|
return self.execute(context)
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
props = context.scene.BIMModelProperties
|
||||||
|
if self.ifc_class != "":
|
||||||
|
props.ifc_class = self.ifc_class
|
||||||
|
AuthoringData.load_relating_types()
|
||||||
|
if self.relating_type_id != "":
|
||||||
|
props.relating_type_id = self.relating_type_id
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class HelpConstrTypes(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.help_relating_types"
|
||||||
|
bl_label = "Construction Types Help"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
bl_description = "Click to read some contextual help"
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
return context.window_manager.invoke_popup(self, width=510)
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
layout.row().separator(factor=0.5)
|
||||||
|
row = layout.row()
|
||||||
|
row.alignment = "CENTER"
|
||||||
|
row.label(text="BlenderBIM Help", icon="BLENDER")
|
||||||
|
layout.row().separator(factor=0.5)
|
||||||
|
|
||||||
|
row = layout_with_margins(layout.row()).row()
|
||||||
|
row.label(text="Overview:", icon="KEYTYPE_MOVING_HOLD_VEC")
|
||||||
|
self.draw_lines(layout, self.message_summary)
|
||||||
|
layout.row().separator()
|
||||||
|
|
||||||
|
row = layout_with_margins(layout.row()).row()
|
||||||
|
row.label(text="Further support:", icon="KEYTYPE_MOVING_HOLD_VEC")
|
||||||
|
layout.row().separator(factor=0.5)
|
||||||
|
row = layout_with_margins(layout).row()
|
||||||
|
op = row.operator("bim.open_upstream", text="Homepage", icon="HOME")
|
||||||
|
op.page = "home"
|
||||||
|
op = row.operator("bim.open_upstream", text="Docs", icon="DOCUMENTS")
|
||||||
|
op.page = "docs"
|
||||||
|
op = row.operator("bim.open_upstream", text="Wiki", icon="CURRENT_FILE")
|
||||||
|
op.page = "wiki"
|
||||||
|
op = row.operator("bim.open_upstream", text="Community", icon="COMMUNITY")
|
||||||
|
op.page = "community"
|
||||||
|
layout.row().separator()
|
||||||
|
|
||||||
|
def draw_lines(self, layout, lines):
|
||||||
|
box = layout_with_margins(layout).box()
|
||||||
|
for line in lines:
|
||||||
|
row = box.row()
|
||||||
|
row.label(text=f" {line}")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def message_summary(self):
|
||||||
|
return [
|
||||||
|
'The Construction Type Browser allows to preview and add new instances to the model.',
|
||||||
|
'For further support, please click on the Documentation link below.'
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class AlignProduct(bpy.types.Operator):
|
class AlignProduct(bpy.types.Operator):
|
||||||
bl_idname = "bim.align_product"
|
bl_idname = "bim.align_product"
|
||||||
bl_label = "Align Product"
|
bl_label = "Align Product"
|
||||||
|
|||||||
@@ -162,7 +162,15 @@ class DumbProfileGenerator:
|
|||||||
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="EPset_Parametric")
|
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="EPset_Parametric")
|
||||||
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"Engine": "BlenderBIM.DumbProfile"})
|
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"Engine": "BlenderBIM.DumbProfile"})
|
||||||
MaterialData.load(self.file)
|
MaterialData.load(self.file)
|
||||||
obj.select_set(True)
|
try:
|
||||||
|
obj.select_set(True)
|
||||||
|
except RuntimeError:
|
||||||
|
def msg(self, context):
|
||||||
|
txt = "The created object could not be assigned to a collection. "
|
||||||
|
txt += "Has any IfcSpatialElement been deleted?"
|
||||||
|
self.layout.label(text=txt)
|
||||||
|
|
||||||
|
bpy.context.window_manager.popup_menu(msg, title="Error", icon="ERROR")
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -17,21 +17,8 @@
|
|||||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
import ifcopenshell.util.type
|
|
||||||
from blenderbim.bim.module.model.data import AuthoringData
|
from blenderbim.bim.module.model.data import AuthoringData
|
||||||
from blenderbim.bim.prop import StrProperty, Attribute
|
|
||||||
from blenderbim.bim.ifc import IfcStore
|
|
||||||
from bpy.types import PropertyGroup
|
from bpy.types import PropertyGroup
|
||||||
from bpy.props import (
|
|
||||||
PointerProperty,
|
|
||||||
StringProperty,
|
|
||||||
EnumProperty,
|
|
||||||
BoolProperty,
|
|
||||||
IntProperty,
|
|
||||||
FloatProperty,
|
|
||||||
FloatVectorProperty,
|
|
||||||
CollectionProperty,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def get_ifc_class(self, context):
|
def get_ifc_class(self, context):
|
||||||
@@ -43,18 +30,110 @@ def get_ifc_class(self, context):
|
|||||||
def get_relating_type(self, context):
|
def get_relating_type(self, context):
|
||||||
if not AuthoringData.is_loaded:
|
if not AuthoringData.is_loaded:
|
||||||
AuthoringData.load()
|
AuthoringData.load()
|
||||||
return AuthoringData.data["relating_types"]
|
return AuthoringData.data["relating_types_ids"]
|
||||||
|
|
||||||
|
|
||||||
|
def get_relating_type_browser(self, context):
|
||||||
|
if not AuthoringData.is_loaded:
|
||||||
|
AuthoringData.load()
|
||||||
|
return AuthoringData.data["relating_types_ids_browser"]
|
||||||
|
|
||||||
|
|
||||||
|
def update_icon_id(self, context):
|
||||||
|
ifc_class_browser = self.ifc_class_browser
|
||||||
|
relating_type_id_browser = self.relating_type_id_browser
|
||||||
|
relating_type_browser = AuthoringData.relating_type_name_by_id(ifc_class_browser, relating_type_id_browser)
|
||||||
|
if ((ifc_class_browser not in AuthoringData.data["preview_constr_types"]
|
||||||
|
or relating_type_id_browser not in AuthoringData.data["preview_constr_types"][ifc_class_browser])
|
||||||
|
and relating_type_browser is not None):
|
||||||
|
if not AuthoringData.assetize_relating_type_from_selection():
|
||||||
|
return
|
||||||
|
self.icon_id = AuthoringData.data["preview_constr_types"][ifc_class_browser][relating_type_id_browser]["icon_id"]
|
||||||
|
|
||||||
|
|
||||||
def update_ifc_class(self, context):
|
def update_ifc_class(self, context):
|
||||||
AuthoringData.is_loaded = False
|
AuthoringData.load_ifc_classes()
|
||||||
|
AuthoringData.load_relating_types()
|
||||||
|
self.relating_type_id = AuthoringData.data["relating_types_ids"][0][0]
|
||||||
|
|
||||||
|
|
||||||
|
def update_ifc_class_browser(self, context):
|
||||||
|
AuthoringData.load_ifc_classes()
|
||||||
|
AuthoringData.load_relating_types_browser()
|
||||||
|
props = context.scene.BIMModelProperties
|
||||||
|
if props.unfold_relating_types:
|
||||||
|
ifc_class_browser = props.ifc_class_browser
|
||||||
|
relating_type_info = AuthoringData.relating_type_info(ifc_class_browser)
|
||||||
|
if relating_type_info is None or not relating_type_info.fully_loaded:
|
||||||
|
curr_selection = props.ifc_class, props.relating_type_id
|
||||||
|
AuthoringData.assetize_constr_class(ifc_class_browser)
|
||||||
|
props.ifc_class, props.relating_type_id = curr_selection
|
||||||
|
else:
|
||||||
|
self.relating_type_id_browser = AuthoringData.data["relating_types_ids_browser"][0][0]
|
||||||
|
|
||||||
|
|
||||||
|
def update_relating_type(self, context):
|
||||||
|
AuthoringData.load_relating_types()
|
||||||
|
|
||||||
|
|
||||||
|
def update_relating_type_by_name(self, context):
|
||||||
|
AuthoringData.load_relating_types()
|
||||||
|
relating_type_id = AuthoringData.relating_type_id_by_name(self.ifc_class, self.relating_type)
|
||||||
|
if relating_type_id is not None:
|
||||||
|
self.relating_type_id = relating_type_id
|
||||||
|
|
||||||
|
|
||||||
|
def update_relating_type_browser_by_name(self, context):
|
||||||
|
AuthoringData.load_relating_types_browser()
|
||||||
|
relating_type_id_browser = AuthoringData.relating_type_id_by_name(self.ifc_class_browser, self.relating_type_browser)
|
||||||
|
if relating_type_id_browser is not None:
|
||||||
|
self.relating_type_id_browser = relating_type_id_browser
|
||||||
|
|
||||||
|
|
||||||
|
def update_relating_type_browser(self, context):
|
||||||
|
AuthoringData.load_relating_types_browser()
|
||||||
|
update_icon_id(self, context)
|
||||||
|
|
||||||
|
|
||||||
|
def update_unfold_relating_type(self, context):
|
||||||
|
update_ifc_class_browser(self, context)
|
||||||
|
|
||||||
|
|
||||||
class BIMModelProperties(PropertyGroup):
|
class BIMModelProperties(PropertyGroup):
|
||||||
ifc_class: bpy.props.EnumProperty(items=get_ifc_class, name="IFC Class", update=update_ifc_class)
|
ifc_class: bpy.props.EnumProperty(items=get_ifc_class, name="Construction Class", update=update_ifc_class)
|
||||||
relating_type: bpy.props.EnumProperty(items=get_relating_type, name="Relating Type")
|
ifc_class_browser: bpy.props.EnumProperty(
|
||||||
|
items=get_ifc_class, name="Construction Class", update=update_ifc_class_browser
|
||||||
|
)
|
||||||
|
relating_type: bpy.props.StringProperty(update=update_relating_type_by_name)
|
||||||
|
relating_type_id: bpy.props.EnumProperty(
|
||||||
|
items=get_relating_type, name="Construction Type", update=update_relating_type
|
||||||
|
)
|
||||||
|
relating_type_browser: bpy.props.StringProperty(update=update_relating_type_browser_by_name)
|
||||||
|
relating_type_id_browser: bpy.props.EnumProperty(
|
||||||
|
items=get_relating_type_browser, name="Construction Type", update=update_relating_type_browser
|
||||||
|
)
|
||||||
|
icon_id: bpy.props.IntProperty()
|
||||||
|
unfold_relating_types: bpy.props.BoolProperty(update=update_unfold_relating_type)
|
||||||
occurrence_name_style: bpy.props.EnumProperty(
|
occurrence_name_style: bpy.props.EnumProperty(
|
||||||
items=[("CLASS", "By Class", ""), ("TYPE", "By Type", ""), ("CUSTOM", "Custom", "")],
|
items=[("CLASS", "By Class", ""), ("TYPE", "By Type", ""), ("CUSTOM", "Custom", "")],
|
||||||
name="Occurrence Name Style",
|
name="Occurrence Name Style",
|
||||||
)
|
)
|
||||||
occurrence_name_function: bpy.props.StringProperty(name="Occurrence Name Function")
|
occurrence_name_function: bpy.props.StringProperty(name="Occurrence Name Function")
|
||||||
|
getter_enum = {
|
||||||
|
"ifc_class_browser": get_ifc_class,
|
||||||
|
"relating_type_browser": get_relating_type_browser
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_relating_type_info(self, context):
|
||||||
|
return AuthoringData.relating_types(ifc_class=self.name)
|
||||||
|
|
||||||
|
|
||||||
|
class ConstrTypeInfo(PropertyGroup):
|
||||||
|
name: bpy.props.StringProperty(name="Construction class")
|
||||||
|
relating_type: bpy.props.EnumProperty(
|
||||||
|
name="Construction type", items=get_relating_type_info
|
||||||
|
)
|
||||||
|
fully_loaded: bpy.props.BoolProperty(default=False)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ def calculate_quantities(usecase_path, ifc_file, settings):
|
|||||||
obj = settings["blender_object"]
|
obj = settings["blender_object"]
|
||||||
product = ifc_file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
product = ifc_file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||||
parametric = ifcopenshell.util.element.get_psets(product).get("EPset_Parametric")
|
parametric = ifcopenshell.util.element.get_psets(product).get("EPset_Parametric")
|
||||||
if not parametric or parametric["Engine"] != "BlenderBIM.DumbLayer3":
|
if not parametric or "Engine" not in parametric or parametric["Engine"] != "BlenderBIM.DumbLayer3":
|
||||||
return
|
return
|
||||||
qto = ifcopenshell.api.run(
|
qto = ifcopenshell.api.run(
|
||||||
"pset.add_qto", ifc_file, should_run_listeners=False, product=product, name="Qto_SlabBaseQuantities"
|
"pset.add_qto", ifc_file, should_run_listeners=False, product=product, name="Qto_SlabBaseQuantities"
|
||||||
@@ -306,7 +306,15 @@ class DumbSlabGenerator:
|
|||||||
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="EPset_Parametric")
|
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="EPset_Parametric")
|
||||||
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"Engine": "BlenderBIM.DumbLayer3"})
|
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"Engine": "BlenderBIM.DumbLayer3"})
|
||||||
MaterialData.load(self.file)
|
MaterialData.load(self.file)
|
||||||
obj.select_set(True)
|
try:
|
||||||
|
obj.select_set(True)
|
||||||
|
except RuntimeError:
|
||||||
|
def msg(self, context):
|
||||||
|
txt = "The created object could not be assigned to a collection. "
|
||||||
|
txt += "Has any IfcSpatialElement been deleted?"
|
||||||
|
self.layout.label(text=txt)
|
||||||
|
|
||||||
|
bpy.context.window_manager.popup_menu(msg, title="Error", icon="ERROR")
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
|
||||||
@@ -342,6 +350,23 @@ class DumbSlabPlaner:
|
|||||||
return
|
return
|
||||||
new_thickness = sum([l.LayerThickness for l in new_material.MaterialLayers])
|
new_thickness = sum([l.LayerThickness for l in new_material.MaterialLayers])
|
||||||
material = ifcopenshell.util.element.get_material(settings["related_object"])
|
material = ifcopenshell.util.element.get_material(settings["related_object"])
|
||||||
|
|
||||||
|
relating_type = settings["relating_type"]
|
||||||
|
if hasattr(relating_type, "HasPropertySets"):
|
||||||
|
psets = relating_type.HasPropertySets
|
||||||
|
if psets is not None:
|
||||||
|
for pset in psets:
|
||||||
|
if hasattr(pset, "HasProperties"):
|
||||||
|
pset_props = pset.HasProperties
|
||||||
|
if pset_props is not None:
|
||||||
|
for prop in pset_props:
|
||||||
|
if prop.Name == "LayerSetDirection":
|
||||||
|
if hasattr(prop, "NominalValue"):
|
||||||
|
nominal_value = prop.NominalValue
|
||||||
|
if hasattr(nominal_value, "wrappedValue"):
|
||||||
|
if nominal_value.wrappedValue == "AXIS2":
|
||||||
|
return
|
||||||
|
|
||||||
if material and material.is_a("IfcMaterialLayerSetUsage") and material.LayerSetDirection == "AXIS3":
|
if material and material.is_a("IfcMaterialLayerSetUsage") and material.LayerSetDirection == "AXIS3":
|
||||||
self.change_thickness(settings["related_object"], new_thickness)
|
self.change_thickness(settings["related_object"], new_thickness)
|
||||||
|
|
||||||
|
|||||||
@@ -16,54 +16,15 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# 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/>.
|
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import blenderbim.bim.module.type.prop as type_prop
|
|
||||||
from bpy.types import Panel
|
from bpy.types import Panel
|
||||||
from blenderbim.bim.ifc import IfcStore
|
|
||||||
from blenderbim.bim.module.model.data import AuthoringData
|
|
||||||
|
|
||||||
|
|
||||||
class BIM_PT_authoring(Panel):
|
class BIM_PT_authoring(Panel):
|
||||||
bl_idname = "BIM_PT_authoring"
|
|
||||||
bl_label = "Authoring"
|
|
||||||
bl_space_type = "VIEW_3D"
|
|
||||||
bl_region_type = "UI"
|
|
||||||
bl_category = "BlenderBIM"
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def poll(cls, context):
|
|
||||||
return IfcStore.get_file()
|
|
||||||
|
|
||||||
def draw(self, context):
|
|
||||||
if not AuthoringData.is_loaded:
|
|
||||||
AuthoringData.load()
|
|
||||||
|
|
||||||
props = context.scene.BIMModelProperties
|
|
||||||
col = self.layout.column(align=True)
|
|
||||||
enabled = True
|
|
||||||
|
|
||||||
if AuthoringData.data["ifc_classes"]:
|
|
||||||
col.prop(props, "ifc_class", text="", icon="FILE_VOLUME")
|
|
||||||
else:
|
|
||||||
col.label(text="No IFC Class", icon="FILE_VOLUME")
|
|
||||||
enabled = False
|
|
||||||
if AuthoringData.data["relating_types"]:
|
|
||||||
col.prop(props, "relating_type", text="", icon="FILE_3D")
|
|
||||||
else:
|
|
||||||
col.label(text="No Relating Type", icon="FILE_3D")
|
|
||||||
enabled = False
|
|
||||||
row = col.row()
|
|
||||||
row.operator("bim.add_type_instance", icon="ADD")
|
|
||||||
row.enabled = enabled
|
|
||||||
|
|
||||||
|
|
||||||
class BIM_PT_authoring_architectural(Panel):
|
|
||||||
bl_label = "Architectural"
|
bl_label = "Architectural"
|
||||||
bl_idname = "BIM_PT_authoring_architectural"
|
bl_idname = "BIM_PT_authoring"
|
||||||
bl_options = {"DEFAULT_CLOSED"}
|
|
||||||
bl_space_type = "VIEW_3D"
|
bl_space_type = "VIEW_3D"
|
||||||
bl_region_type = "UI"
|
bl_region_type = "UI"
|
||||||
bl_category = "BlenderBIM"
|
bl_category = "BlenderBIM"
|
||||||
bl_parent_id = "BIM_PT_authoring"
|
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
|
|||||||
@@ -817,7 +817,15 @@ class DumbWallGenerator:
|
|||||||
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="EPset_Parametric")
|
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="EPset_Parametric")
|
||||||
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"Engine": "BlenderBIM.DumbLayer2"})
|
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"Engine": "BlenderBIM.DumbLayer2"})
|
||||||
MaterialData.load(self.file)
|
MaterialData.load(self.file)
|
||||||
obj.select_set(True)
|
try:
|
||||||
|
obj.select_set(True)
|
||||||
|
except RuntimeError:
|
||||||
|
def msg(self, context):
|
||||||
|
txt = "The created object could not be assigned to a collection. "
|
||||||
|
txt += "Has any IfcSpatialElement been deleted?"
|
||||||
|
self.layout.label(text=txt)
|
||||||
|
|
||||||
|
bpy.context.window_manager.popup_menu(msg, title="Error", icon="ERROR")
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
|
||||||
@@ -861,7 +869,7 @@ def calculate_quantities(usecase_path, ifc_file, settings):
|
|||||||
obj = settings["blender_object"]
|
obj = settings["blender_object"]
|
||||||
product = ifc_file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
product = ifc_file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||||
parametric = ifcopenshell.util.element.get_psets(product).get("EPset_Parametric")
|
parametric = ifcopenshell.util.element.get_psets(product).get("EPset_Parametric")
|
||||||
if not parametric or parametric["Engine"] != "BlenderBIM.DumbLayer2":
|
if not parametric or "Engine" not in parametric or parametric["Engine"] != "BlenderBIM.DumbLayer2":
|
||||||
return
|
return
|
||||||
qto = ifcopenshell.api.run(
|
qto = ifcopenshell.api.run(
|
||||||
"pset.add_qto", ifc_file, should_run_listeners=False, product=product, name="Qto_WallBaseQuantities"
|
"pset.add_qto", ifc_file, should_run_listeners=False, product=product, name="Qto_WallBaseQuantities"
|
||||||
@@ -941,6 +949,23 @@ class DumbWallPlaner:
|
|||||||
return
|
return
|
||||||
new_thickness = sum([l.LayerThickness for l in new_material.MaterialLayers])
|
new_thickness = sum([l.LayerThickness for l in new_material.MaterialLayers])
|
||||||
material = ifcopenshell.util.element.get_material(settings["related_object"])
|
material = ifcopenshell.util.element.get_material(settings["related_object"])
|
||||||
|
|
||||||
|
relating_type = settings["relating_type"]
|
||||||
|
if hasattr(relating_type, "HasPropertySets"):
|
||||||
|
psets = relating_type.HasPropertySets
|
||||||
|
if psets is not None:
|
||||||
|
for pset in psets:
|
||||||
|
if hasattr(pset, "HasProperties"):
|
||||||
|
pset_props = pset.HasProperties
|
||||||
|
if pset_props is not None:
|
||||||
|
for prop in pset_props:
|
||||||
|
if prop.Name == "LayerSetDirection":
|
||||||
|
if hasattr(prop, "NominalValue"):
|
||||||
|
nominal_value = prop.NominalValue
|
||||||
|
if hasattr(nominal_value, "wrappedValue"):
|
||||||
|
if nominal_value.wrappedValue == "AXIS3":
|
||||||
|
return
|
||||||
|
|
||||||
if material and material.is_a("IfcMaterialLayerSetUsage") and material.LayerSetDirection == "AXIS2":
|
if material and material.is_a("IfcMaterialLayerSetUsage") and material.LayerSetDirection == "AXIS2":
|
||||||
self.change_thickness(settings["related_object"], new_thickness)
|
self.change_thickness(settings["related_object"], new_thickness)
|
||||||
|
|
||||||
|
|||||||
@@ -55,28 +55,56 @@ class BimTool(WorkSpaceTool):
|
|||||||
if not AuthoringData.is_loaded and IfcStore.get_file():
|
if not AuthoringData.is_loaded and IfcStore.get_file():
|
||||||
AuthoringData.load()
|
AuthoringData.load()
|
||||||
|
|
||||||
|
props = context.scene.BIMModelProperties
|
||||||
|
is_tool_header = context.region.type == "TOOL_HEADER"
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
if not IfcStore.get_file():
|
if not IfcStore.get_file():
|
||||||
row.label(text="No IFC Project", icon="ERROR")
|
row.label(text="No IFC Project", icon="ERROR")
|
||||||
return
|
return
|
||||||
props = context.scene.BIMModelProperties
|
|
||||||
if AuthoringData.data["ifc_classes"]:
|
|
||||||
row.prop(props, "ifc_class", text="")
|
|
||||||
else:
|
|
||||||
row.label(text="No IFC Class")
|
|
||||||
if AuthoringData.data["relating_types"]:
|
|
||||||
row.prop(props, "relating_type", text="")
|
|
||||||
else:
|
|
||||||
row.label(text="No Relating Type")
|
|
||||||
|
|
||||||
row.label(text="", icon="BLANK1")
|
ifc_classes = AuthoringData.data["ifc_classes"]
|
||||||
|
relating_types_ids = AuthoringData.data["relating_types_ids"]
|
||||||
|
|
||||||
row = layout.row(align=True)
|
if is_tool_header:
|
||||||
row.label(text="", icon="EVENT_SHIFT")
|
row.operator("bim.help_relating_types", text="", icon="QUESTION")
|
||||||
row.label(text="Add Type Instance", icon="EVENT_A")
|
|
||||||
|
if ifc_classes and is_tool_header:
|
||||||
|
row.label(text="", icon="BLANK1")
|
||||||
|
row.operator("bim.display_relating_types", icon="COLLAPSEMENU")
|
||||||
|
|
||||||
|
ifc_class = props.ifc_class
|
||||||
|
relating_type_id = props.relating_type_id
|
||||||
|
relating_type = AuthoringData.relating_type_name_by_id(ifc_class, relating_type_id)
|
||||||
|
|
||||||
|
if is_tool_header:
|
||||||
|
row.label(text="", icon="BLANK1")
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.label(text="", icon="EVENT_SHIFT")
|
||||||
|
row.label(text="", icon="EVENT_A")
|
||||||
|
if ifc_classes:
|
||||||
|
row.label(text=f" Add")
|
||||||
|
row.label(text="", icon="FILE_VOLUME")
|
||||||
|
row.label(text=ifc_class)
|
||||||
|
row.label(text="", icon="FILE_3D")
|
||||||
|
row.label(text=f"{relating_type} ")
|
||||||
|
else:
|
||||||
|
row.label(text=f" Add instance")
|
||||||
|
else:
|
||||||
|
txt_ifc_class = ifc_class if ifc_classes else "No Construction Class"
|
||||||
|
txt_relating_type = relating_type if relating_types_ids else "No Construction Type"
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.label(text="Selected Construction Type:")
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.label(text=txt_ifc_class, icon="FILE_VOLUME")
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.label(text=txt_relating_type, icon="FILE_3D")
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.label(text="", icon="EVENT_SHIFT")
|
||||||
|
row.label(text="", icon="EVENT_A")
|
||||||
|
row.label(text=f" Add Type Instance")
|
||||||
|
|
||||||
if AuthoringData.data["ifc_classes"]:
|
if AuthoringData.data["ifc_classes"]:
|
||||||
if props.ifc_class == "IfcWallType":
|
if ifc_class == "IfcWallType":
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.label(text="Join")
|
row.label(text="Join")
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
@@ -98,7 +126,7 @@ class BimTool(WorkSpaceTool):
|
|||||||
row.label(text="", icon="EVENT_SHIFT")
|
row.label(text="", icon="EVENT_SHIFT")
|
||||||
row.label(text="Split", icon="EVENT_S")
|
row.label(text="Split", icon="EVENT_S")
|
||||||
|
|
||||||
if props.ifc_class in ("IfcColumnType", "IfcBeamType", "IfcMemberType"):
|
if ifc_class in ("IfcColumnType", "IfcBeamType", "IfcMemberType"):
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.label(text="Join")
|
row.label(text="Join")
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
@@ -156,7 +184,7 @@ class Hotkey(bpy.types.Operator):
|
|||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
def hotkey_S_A(self):
|
def hotkey_S_A(self):
|
||||||
bpy.ops.bim.add_type_instance()
|
bpy.ops.bim.add_constr_type_instance()
|
||||||
|
|
||||||
def hotkey_S_C(self):
|
def hotkey_S_C(self):
|
||||||
if self.has_ifc_class and self.props.ifc_class == "IfcWallType":
|
if self.has_ifc_class and self.props.ifc_class == "IfcWallType":
|
||||||
|
|||||||
@@ -88,4 +88,4 @@ class BIM_PT_type(Panel):
|
|||||||
|
|
||||||
|
|
||||||
def add_object_button(self, context):
|
def add_object_button(self, context):
|
||||||
self.layout.operator("bim.add_type_instance", icon="PLUGIN")
|
self.layout.operator("bim.add_constr_type_instance", icon="PLUGIN")
|
||||||
|
|||||||
@@ -82,7 +82,8 @@ class Collector(blenderbim.core.tool.Collector):
|
|||||||
if obj.users_collection != (object_collection,):
|
if obj.users_collection != (object_collection,):
|
||||||
for collection in obj.users_collection:
|
for collection in obj.users_collection:
|
||||||
collection.objects.unlink(obj)
|
collection.objects.unlink(obj)
|
||||||
object_collection.objects.link(obj)
|
if object_collection is not None:
|
||||||
|
object_collection.objects.link(obj)
|
||||||
|
|
||||||
if collection_collection and collection_collection.children.find(object_collection.name) == -1:
|
if collection_collection and collection_collection.children.find(object_collection.name) == -1:
|
||||||
if bpy.context.scene.collection.children.find(object_collection.name) != -1:
|
if bpy.context.scene.collection.children.find(object_collection.name) != -1:
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ Scenario: Add representation - add a new representation to a typed instance
|
|||||||
And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType"
|
And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType"
|
||||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
|
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
|
||||||
And I press "bim.assign_class"
|
And I press "bim.assign_class"
|
||||||
And I press "bim.add_type_instance"
|
And I press "bim.add_constr_type_instance"
|
||||||
And I press "bim.add_type_instance"
|
And I press "bim.add_constr_type_instance"
|
||||||
Then the object "IfcWall/Wall" data is a "Tessellation" representation of "Model/Body/MODEL_VIEW"
|
Then the object "IfcWall/Wall" data is a "Tessellation" representation of "Model/Body/MODEL_VIEW"
|
||||||
And the object "IfcWall/Wall.001" data is a "Tessellation" representation of "Model/Body/MODEL_VIEW"
|
And the object "IfcWall/Wall.001" data is a "Tessellation" representation of "Model/Body/MODEL_VIEW"
|
||||||
When the object "IfcWall/Wall" is selected
|
When the object "IfcWall/Wall" is selected
|
||||||
@@ -146,9 +146,9 @@ Scenario: Remove representation - remove an instanced representation from an act
|
|||||||
And I press "bim.assign_class"
|
And I press "bim.assign_class"
|
||||||
And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType"
|
And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType"
|
||||||
And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()"
|
And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()"
|
||||||
And I set "scene.BIMModelProperties.relating_type" to "{cube}"
|
And I set "scene.BIMModelProperties.relating_type_id" to "{cube}"
|
||||||
And I press "bim.add_type_instance"
|
And I press "bim.add_constr_type_instance"
|
||||||
And I press "bim.add_type_instance"
|
And I press "bim.add_constr_type_instance"
|
||||||
And the object "IfcWallType/Cube" is selected
|
And the object "IfcWallType/Cube" is selected
|
||||||
When the variable "representation" is "{ifc}.by_type('IfcWallType')[0].RepresentationMaps[1].MappedRepresentation.id()"
|
When the variable "representation" is "{ifc}.by_type('IfcWallType')[0].RepresentationMaps[1].MappedRepresentation.id()"
|
||||||
And I press "bim.remove_representation(representation_id={representation})"
|
And I press "bim.remove_representation(representation_id={representation})"
|
||||||
@@ -165,9 +165,9 @@ Scenario: Remove representation - remove an instanced representation from an act
|
|||||||
And I press "bim.assign_class"
|
And I press "bim.assign_class"
|
||||||
And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType"
|
And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType"
|
||||||
And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()"
|
And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()"
|
||||||
And I set "scene.BIMModelProperties.relating_type" to "{cube}"
|
And I set "scene.BIMModelProperties.relating_type_id" to "{cube}"
|
||||||
And I press "bim.add_type_instance"
|
And I press "bim.add_constr_type_instance"
|
||||||
And I press "bim.add_type_instance"
|
And I press "bim.add_constr_type_instance"
|
||||||
And the object "IfcWall/Wall" is selected
|
And the object "IfcWall/Wall" is selected
|
||||||
When the variable "representation" is "{ifc}.by_type('IfcWall')[0].Representation.Representations[1].id()"
|
When the variable "representation" is "{ifc}.by_type('IfcWall')[0].Representation.Representations[1].id()"
|
||||||
And I press "bim.remove_representation(representation_id={representation})"
|
And I press "bim.remove_representation(representation_id={representation})"
|
||||||
@@ -339,8 +339,8 @@ Scenario: Override duplicate move - copying a type instance with a representatio
|
|||||||
And I press "bim.assign_class"
|
And I press "bim.assign_class"
|
||||||
And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType"
|
And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType"
|
||||||
And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()"
|
And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()"
|
||||||
And I set "scene.BIMModelProperties.relating_type" to "{cube}"
|
And I set "scene.BIMModelProperties.relating_type_id" to "{cube}"
|
||||||
And I press "bim.add_type_instance"
|
And I press "bim.add_constr_type_instance"
|
||||||
And the object "IfcWall/Wall" is selected
|
And the object "IfcWall/Wall" is selected
|
||||||
When I press "object.duplicate_move"
|
When I press "object.duplicate_move"
|
||||||
Then the object "IfcWall/Wall.001" exists
|
Then the object "IfcWall/Wall.001" exists
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ Scenario: Add type instance - add from a mesh
|
|||||||
And I press "bim.assign_class"
|
And I press "bim.assign_class"
|
||||||
And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType"
|
And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType"
|
||||||
And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()"
|
And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()"
|
||||||
And I set "scene.BIMModelProperties.relating_type" to "{cube}"
|
And I set "scene.BIMModelProperties.relating_type_id" to "{cube}"
|
||||||
When I press "bim.add_type_instance"
|
When I press "bim.add_constr_type_instance"
|
||||||
Then the object "IfcWall/Wall" exists
|
Then the object "IfcWall/Wall" exists
|
||||||
|
|
||||||
Scenario: Add type instance - add from an empty
|
Scenario: Add type instance - add from an empty
|
||||||
@@ -23,8 +23,8 @@ Scenario: Add type instance - add from an empty
|
|||||||
And I press "bim.assign_class"
|
And I press "bim.assign_class"
|
||||||
And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType"
|
And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType"
|
||||||
And the variable "empty" is "{ifc}.by_type('IfcWallType')[0].id()"
|
And the variable "empty" is "{ifc}.by_type('IfcWallType')[0].id()"
|
||||||
And I set "scene.BIMModelProperties.relating_type" to "{empty}"
|
And I set "scene.BIMModelProperties.relating_type_id" to "{empty}"
|
||||||
When I press "bim.add_type_instance"
|
When I press "bim.add_constr_type_instance"
|
||||||
Then the object "IfcWall/Wall" exists
|
Then the object "IfcWall/Wall" exists
|
||||||
|
|
||||||
Scenario: Add type instance - add a mesh where existing instances have changed context
|
Scenario: Add type instance - add a mesh where existing instances have changed context
|
||||||
@@ -36,18 +36,51 @@ Scenario: Add type instance - add a mesh where existing instances have changed c
|
|||||||
And I press "bim.assign_class"
|
And I press "bim.assign_class"
|
||||||
And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType"
|
And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType"
|
||||||
And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()"
|
And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()"
|
||||||
And I set "scene.BIMModelProperties.relating_type" to "{cube}"
|
And I set "scene.BIMModelProperties.relating_type_id" to "{cube}"
|
||||||
And I press "bim.add_type_instance"
|
And I press "bim.add_constr_type_instance"
|
||||||
And the object "IfcWall/Wall" data is a "Tessellation" representation of "Model/Body/MODEL_VIEW"
|
And the object "IfcWall/Wall" data is a "Tessellation" representation of "Model/Body/MODEL_VIEW"
|
||||||
And the object "IfcWall/Wall" is selected
|
And the object "IfcWall/Wall" is selected
|
||||||
And the variable "context" is "[c for c in {ifc}.by_type('IfcGeometricRepresentationSubContext') if c.TargetView == 'PLAN_VIEW'][0].id()"
|
And the variable "context" is "[c for c in {ifc}.by_type('IfcGeometricRepresentationSubContext') if c.TargetView == 'PLAN_VIEW'][0].id()"
|
||||||
And I set "scene.BIMRootProperties.contexts" to "{context}"
|
And I set "scene.BIMRootProperties.contexts" to "{context}"
|
||||||
And I press "bim.add_representation"
|
And I press "bim.add_representation"
|
||||||
And the object "IfcWall/Wall" data is a "Annotation2D" representation of "Plan/Annotation/PLAN_VIEW"
|
And the object "IfcWall/Wall" data is a "Annotation2D" representation of "Plan/Annotation/PLAN_VIEW"
|
||||||
When I press "bim.add_type_instance"
|
When I press "bim.add_constr_type_instance"
|
||||||
Then the object "IfcWall/Wall" data is a "Annotation2D" representation of "Plan/Annotation/PLAN_VIEW"
|
Then the object "IfcWall/Wall" data is a "Annotation2D" representation of "Plan/Annotation/PLAN_VIEW"
|
||||||
And the object "IfcWall/Wall.001" data is a "Annotation2D" representation of "Plan/Annotation/PLAN_VIEW"
|
And the object "IfcWall/Wall.001" data is a "Annotation2D" representation of "Plan/Annotation/PLAN_VIEW"
|
||||||
|
|
||||||
|
Scenario: Preview one type on the Construction Type Browser
|
||||||
|
Given an empty IFC project
|
||||||
|
And I load the demo construction library
|
||||||
|
When I display the construction type browser
|
||||||
|
And I preview only one asset on the construction type browser
|
||||||
|
And I set "scene.BIMModelProperties.ifc_class_browser" to "IfcColumnType"
|
||||||
|
And I set "scene.BIMModelProperties.relating_type_browser" to "DEMO2"
|
||||||
|
And I select the browser construction type
|
||||||
|
Then "scene.BIMModelProperties.ifc_class" is "IfcColumnType"
|
||||||
|
And construction type is DEMO2
|
||||||
|
And objects starting with "IfcColumn/" do not exist
|
||||||
|
And the construction type "IfcColumnType"/"DEMO2" has a preview
|
||||||
|
|
||||||
|
Scenario: Preview one class on the construction type browser
|
||||||
|
Given an empty IFC project
|
||||||
|
And I load the demo construction library
|
||||||
|
When I display the construction type browser
|
||||||
|
And I preview all available assets on the construction type browser
|
||||||
|
And I set "scene.BIMModelProperties.ifc_class_browser" to "IfcWallType"
|
||||||
|
Then "scene.BIMModelProperties.ifc_class_browser" is "IfcWallType"
|
||||||
|
And objects starting with "IfcWall/" do not exist
|
||||||
|
And all construction types for "IfcWallType" have a preview
|
||||||
|
|
||||||
|
Scenario: Add one type from the Construction Type Browser
|
||||||
|
Given an empty IFC project
|
||||||
|
And I load the demo construction library
|
||||||
|
When I display the construction type browser
|
||||||
|
And I preview only one asset on the construction type browser
|
||||||
|
And I set "scene.BIMModelProperties.ifc_class_browser" to "IfcColumnType"
|
||||||
|
And I set "scene.BIMModelProperties.relating_type_browser" to "DEMO2"
|
||||||
|
And I add the browser construction type
|
||||||
|
Then the object "IfcColumn/Column" exists
|
||||||
|
|
||||||
Scenario: Add grid
|
Scenario: Add grid
|
||||||
Given an empty IFC project
|
Given an empty IFC project
|
||||||
When I press "mesh.add_grid"
|
When I press "mesh.add_grid"
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import ifcopenshell
|
|||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
import blenderbim.bim
|
import blenderbim.bim
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
|
from blenderbim.bim.module.model.data import AuthoringData
|
||||||
from pytest_bdd import scenarios, given, when, then, parsers
|
from pytest_bdd import scenarios, given, when, then, parsers
|
||||||
from mathutils import Vector
|
from mathutils import Vector
|
||||||
|
|
||||||
@@ -108,6 +109,7 @@ def i_add_a_new_collection_item(collection):
|
|||||||
assert False, "Collection does not exist"
|
assert False, "Collection does not exist"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@given(parsers.parse('the material "{name}" colour is set to "{colour}"'))
|
@given(parsers.parse('the material "{name}" colour is set to "{colour}"'))
|
||||||
@when(parsers.parse('the material "{name}" colour is set to "{colour}"'))
|
@when(parsers.parse('the material "{name}" colour is set to "{colour}"'))
|
||||||
def the_material_name_colour_is_set_to_colour(name, colour):
|
def the_material_name_colour_is_set_to_colour(name, colour):
|
||||||
@@ -507,7 +509,16 @@ def the_collection_name1_is_in_the_collection_name2(name1, name2):
|
|||||||
|
|
||||||
@then(parsers.parse('the object "{name}" does not exist'))
|
@then(parsers.parse('the object "{name}" does not exist'))
|
||||||
def the_object_name_does_not_exist(name):
|
def the_object_name_does_not_exist(name):
|
||||||
assert bpy.data.objects.get(name) is None, "Object exists"
|
obj = bpy.data.objects.get(name)
|
||||||
|
assert obj is None or len(obj.users_collection) == 0, "Object exists"
|
||||||
|
|
||||||
|
|
||||||
|
@then(parsers.parse('objects starting with "{name}" do not exist'))
|
||||||
|
def objects_not_exist_starting_with(name):
|
||||||
|
objs = [obj for obj in bpy.data.objects if obj.name.startswith(name) and len(obj.users_collection) > 0]
|
||||||
|
if len(objs) > 0:
|
||||||
|
assert False, f'{len(objs)} objects starting with "{name}" exist'
|
||||||
|
assert True
|
||||||
|
|
||||||
|
|
||||||
@then(parsers.parse('the object "{name}" is at "{location}"'))
|
@then(parsers.parse('the object "{name}" is at "{location}"'))
|
||||||
@@ -562,3 +573,108 @@ def the_file_name_should_contain_value(name, value):
|
|||||||
@then(parsers.parse('the object "{name}" has no modifiers'))
|
@then(parsers.parse('the object "{name}" has no modifiers'))
|
||||||
def the_object_name_has_no_modifiers(name):
|
def the_object_name_has_no_modifiers(name):
|
||||||
assert len(the_object_name_exists(name).modifiers) == 0
|
assert len(the_object_name_exists(name).modifiers) == 0
|
||||||
|
|
||||||
|
|
||||||
|
@then(parsers.parse('the construction type "{ifc_class}"/"{relating_type}" has a preview'))
|
||||||
|
def the_construction_type_has_a_preview(ifc_class, relating_type):
|
||||||
|
if "preview_constr_types" not in AuthoringData.data:
|
||||||
|
assert False, 'There are no previews loaded'
|
||||||
|
preview_constr_types = AuthoringData.data["preview_constr_types"]
|
||||||
|
if ifc_class not in preview_constr_types:
|
||||||
|
assert False, f'Construction class {ifc_class} has no available previews'
|
||||||
|
relating_type_id = AuthoringData.relating_type_id_by_name(ifc_class, relating_type)
|
||||||
|
if relating_type_id is None:
|
||||||
|
assert False, f'No construction type {ifc_class}/{relating_type} was found'
|
||||||
|
if relating_type_id not in preview_constr_types[ifc_class]:
|
||||||
|
assert False, f'Construction type {ifc_class}/{relating_type} has no available previews'
|
||||||
|
preview_data = preview_constr_types[ifc_class][relating_type_id]
|
||||||
|
if 'icon_id' not in preview_data:
|
||||||
|
assert False, f'Construction type {ifc_class}/{relating_type} has a preview, but no assigned icon_id'
|
||||||
|
icon_id = preview_data["icon_id"]
|
||||||
|
if not isinstance(icon_id, int):
|
||||||
|
assert False, f'Construction type {ifc_class}/{relating_type} has an invalid icon_id {icon_id}'
|
||||||
|
# Note: icon_id must be > 0 in UI mode, but asset_generate_preview() doesn't work headlessly -> skipping for now
|
||||||
|
# if icon_id == 0:
|
||||||
|
# assert False, f'Construction type {ifc_class}/{relating_type} has the default null value for icon_id'
|
||||||
|
assert True
|
||||||
|
|
||||||
|
|
||||||
|
@then("there is a Construction Type preview")
|
||||||
|
def there_is_a_construction_type_preview():
|
||||||
|
props = bpy.context.scene.BIMModelProperties
|
||||||
|
assert props.icon_id > 0, f"There isn't a Construction Type preview"
|
||||||
|
|
||||||
|
|
||||||
|
@then(parsers.parse('all construction types for "{ifc_class}" have a preview'))
|
||||||
|
def all_construction_types_have_a_preview(ifc_class):
|
||||||
|
if "preview_constr_types" not in AuthoringData.data:
|
||||||
|
assert False, 'There are no previews loaded'
|
||||||
|
preview_constr_types = AuthoringData.data["preview_constr_types"]
|
||||||
|
if ifc_class not in preview_constr_types:
|
||||||
|
assert False, f'Construction class {ifc_class} has no available previews'
|
||||||
|
constr_class_occurrences = AuthoringData.constr_class_entities(ifc_class)
|
||||||
|
for constr_class_entity in constr_class_occurrences:
|
||||||
|
the_construction_type_has_a_preview(ifc_class, constr_class_entity.Name)
|
||||||
|
|
||||||
|
|
||||||
|
@given("I load the demo construction library")
|
||||||
|
@when("I load the demo construction library")
|
||||||
|
def i_add_a_construction_library():
|
||||||
|
lib_path = './blenderbim/bim/data/libraries/IFC4 Demo Library.ifc'
|
||||||
|
bpy.ops.bim.select_library_file(filepath=lib_path, append_all=True)
|
||||||
|
|
||||||
|
|
||||||
|
@given("I display the construction type browser")
|
||||||
|
@when("I display the construction type browser")
|
||||||
|
def i_display_the_construction_type_browser():
|
||||||
|
bpy.ops.bim.display_relating_types('INVOKE_DEFAULT')
|
||||||
|
|
||||||
|
|
||||||
|
@given("I preview only one asset on the construction type browser")
|
||||||
|
@when("I preview only one asset on the construction type browser")
|
||||||
|
def i_preview_one_construction_type():
|
||||||
|
bpy.context.scene.BIMModelProperties.unfold_relating_types = False
|
||||||
|
|
||||||
|
|
||||||
|
@given("I preview all available assets on the construction type browser")
|
||||||
|
@when("I preview all available assets on the construction type browser")
|
||||||
|
def i_preview_all_construction_types():
|
||||||
|
bpy.context.scene.BIMModelProperties.unfold_relating_types = True
|
||||||
|
|
||||||
|
|
||||||
|
@given("I select the browser construction type")
|
||||||
|
@when("I select the browser construction type")
|
||||||
|
def i_select_the_active_construction_type():
|
||||||
|
props = bpy.context.scene.BIMModelProperties
|
||||||
|
bpy.ops.bim.select_construction_type(
|
||||||
|
ifc_class=props.ifc_class_browser, relating_type_id=props.relating_type_id_browser
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@given("I add the browser construction type")
|
||||||
|
@when("I add the browser construction type")
|
||||||
|
def i_add_the_active_construction_type():
|
||||||
|
props = bpy.context.scene.BIMModelProperties
|
||||||
|
bpy.ops.bim.add_constr_type_instance(
|
||||||
|
ifc_class=props.ifc_class_browser, relating_type_id=int(props.relating_type_id_browser)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@then(parsers.parse("browser construction type is {relating_type_name}"))
|
||||||
|
def browser_construction_type(relating_type_name):
|
||||||
|
props = bpy.context.scene.BIMModelProperties
|
||||||
|
relating_type_browser = AuthoringData.relating_type_name_by_id(props.ifc_class_browser, props.relating_type_id_browser)
|
||||||
|
assert relating_type_browser == relating_type_name, (f"Construction Type is a {relating_type_browser}, not " +
|
||||||
|
f"a {relating_type_name}")
|
||||||
|
|
||||||
|
|
||||||
|
@then(parsers.parse("construction type is {relating_type_name}"))
|
||||||
|
def construction_type(relating_type_name):
|
||||||
|
props = bpy.context.scene.BIMModelProperties
|
||||||
|
relating_type = AuthoringData.relating_type_name_by_id(props.ifc_class, props.relating_type_id)
|
||||||
|
assert relating_type == relating_type_name, f"Construction Type is a {relating_type}, not a {relating_type_name}"
|
||||||
|
|
||||||
|
|
||||||
|
@when("I move the cursor to the bottom left corner")
|
||||||
|
def move_cursor_bottom_left():
|
||||||
|
bpy.context.window.cursor_warp(10, 10)
|
||||||
|
|||||||
Reference in New Issue
Block a user