diff --git a/src/Mod/BIM/CMakeLists.txt b/src/Mod/BIM/CMakeLists.txt
index 14e80c6841..8b71a2cf24 100644
--- a/src/Mod/BIM/CMakeLists.txt
+++ b/src/Mod/BIM/CMakeLists.txt
@@ -187,6 +187,7 @@ SET(nativeifc_SRCS
nativeifc/ifc_viewproviders.py
nativeifc/__init__.py
nativeifc/ifc_openshell.py
+ nativeifc/ifc_types.py
)
SOURCE_GROUP("" FILES ${Arch_SRCS})
diff --git a/src/Mod/BIM/Resources/Arch.qrc b/src/Mod/BIM/Resources/Arch.qrc
index 48a769697c..360eb6d3ca 100644
--- a/src/Mod/BIM/Resources/Arch.qrc
+++ b/src/Mod/BIM/Resources/Arch.qrc
@@ -1,23 +1,5 @@
- icons/IFC/IfcBeam.svg
- icons/IFC/IfcBuilding.svg
- icons/IFC/IfcBuildingStorey.svg
- icons/IFC/IfcColumn.svg
- icons/IFC/IfcCovering.svg
- icons/IFC/IfcDoor.svg
- icons/IFC/IfcFooting.svg
- icons/IFC/IfcMember.svg
- icons/IFC/IfcPile.svg
- icons/IFC/IfcPlate.svg
- icons/IFC/IfcRailing.svg
- icons/IFC/IfcRamp.svg
- icons/IFC/IfcRoof.svg
- icons/IFC/IfcSite.svg
- icons/IFC/IfcSlab.svg
- icons/IFC/IfcStair.svg
- icons/IFC/IfcWall.svg
- icons/IFC/IfcWindow.svg
icons/Arch_3Views.svg
icons/Arch_Add.svg
icons/Arch_Axis.svg
@@ -163,66 +145,24 @@
icons/techdraw-ArchView.svg
icons/techdraw-PageDefault.svg
icons/warning.svg
- ui/ArchMaterial.ui
- ui/ArchMultiMaterial.ui
- ui/ArchNest.ui
- ui/ArchSchedule.ui
- ui/BimServerTaskPanel.ui
- ui/DialogBimServerLogin.ui
- ui/DialogDisplayText.ui
- ui/GitTaskPanel.ui
- ui/ParametersBeam.svg
- ui/ParametersDent.svg
- ui/ParametersDoorGlass.svg
- ui/ParametersDoorSimple.svg
- ui/ParametersIbeam.svg
- ui/ParametersOpening.svg
- ui/ParametersPanel.svg
- ui/ParametersPillar.svg
- ui/ParametersSlab.svg
- ui/ParametersStairs.svg
- ui/ParametersWindowDouble.svg
- ui/ParametersWindowFixed.svg
- ui/ParametersWindowSimple.svg
- ui/ParametersWindowStash.svg
- ui/dialogAddPSet.ui
- ui/dialogAddProperty.ui
- ui/dialogClasses.ui
- ui/dialogClassification.ui
- ui/dialogConvertDocument.ui
- ui/dialogCreateProject.ui
- ui/dialogCustomProperties.ui
- ui/dialogDiff.ui
- ui/dialogExport.ui
- ui/dialogIfcElements.ui
- ui/dialogIfcProperties.ui
- ui/dialogIfcPropertiesRedux.ui
- ui/dialogIfcQuantities.ui
- ui/dialogImport.ui
- ui/dialogLayersIFC.ui
- ui/dialogLibrary.ui
- ui/dialogListWidget.ui
- ui/dialogMaterialChooser.ui
- ui/dialogNudgeValue.ui
- ui/dialogPhases.ui
- ui/dialogPreflight.ui
- ui/dialogPreflightResults.ui
- ui/dialogProjectManager.ui
- ui/dialogQuantitySurveying.ui
- ui/dialogReorder.ui
- ui/dialogSetup.ui
- ui/dialogSpaces.ui
- ui/dialogTree.ui
- ui/dialogTutorial.ui
- ui/dialogViews.ui
- ui/dialogWelcome.ui
- ui/dialogWindows.ui
- ui/preferences-arch.ui
- ui/preferences-archdefaults.ui
- ui/preferences-dae.ui
- ui/preferences-ifc-export.ui
- ui/preferences-ifc.ui
- ui/preferencesNativeIFC.ui
+ icons/IFC/IfcBeam.svg
+ icons/IFC/IfcBuilding.svg
+ icons/IFC/IfcBuildingStorey.svg
+ icons/IFC/IfcColumn.svg
+ icons/IFC/IfcCovering.svg
+ icons/IFC/IfcDoor.svg
+ icons/IFC/IfcFooting.svg
+ icons/IFC/IfcMember.svg
+ icons/IFC/IfcPile.svg
+ icons/IFC/IfcPlate.svg
+ icons/IFC/IfcRailing.svg
+ icons/IFC/IfcRamp.svg
+ icons/IFC/IfcRoof.svg
+ icons/IFC/IfcSite.svg
+ icons/IFC/IfcSlab.svg
+ icons/IFC/IfcStair.svg
+ icons/IFC/IfcWall.svg
+ icons/IFC/IfcWindow.svg
translations/Arch_af.qm
translations/Arch_ar.qm
translations/Arch_be.qm
@@ -266,5 +206,66 @@
translations/Arch_vi.qm
translations/Arch_zh-CN.qm
translations/Arch_zh-TW.qm
+ ui/ArchMaterial.ui
+ ui/ArchMultiMaterial.ui
+ ui/ArchNest.ui
+ ui/ArchSchedule.ui
+ ui/BimServerTaskPanel.ui
+ ui/DialogBimServerLogin.ui
+ ui/DialogDisplayText.ui
+ ui/GitTaskPanel.ui
+ ui/ParametersBeam.svg
+ ui/ParametersDent.svg
+ ui/ParametersDoorGlass.svg
+ ui/ParametersDoorSimple.svg
+ ui/ParametersIbeam.svg
+ ui/ParametersOpening.svg
+ ui/ParametersPanel.svg
+ ui/ParametersPillar.svg
+ ui/ParametersSlab.svg
+ ui/ParametersStairs.svg
+ ui/ParametersWindowDouble.svg
+ ui/ParametersWindowFixed.svg
+ ui/ParametersWindowSimple.svg
+ ui/ParametersWindowStash.svg
+ ui/dialogAddPSet.ui
+ ui/dialogAddProperty.ui
+ ui/dialogClasses.ui
+ ui/dialogClassification.ui
+ ui/dialogConvertDocument.ui
+ ui/dialogConvertType.ui
+ ui/dialogCreateProject.ui
+ ui/dialogCustomProperties.ui
+ ui/dialogDiff.ui
+ ui/dialogExport.ui
+ ui/dialogIfcElements.ui
+ ui/dialogIfcProperties.ui
+ ui/dialogIfcPropertiesRedux.ui
+ ui/dialogIfcQuantities.ui
+ ui/dialogImport.ui
+ ui/dialogLayersIFC.ui
+ ui/dialogLibrary.ui
+ ui/dialogListWidget.ui
+ ui/dialogMaterialChooser.ui
+ ui/dialogNudgeValue.ui
+ ui/dialogPhases.ui
+ ui/dialogPreflight.ui
+ ui/dialogPreflightResults.ui
+ ui/dialogProjectManager.ui
+ ui/dialogQuantitySurveying.ui
+ ui/dialogReorder.ui
+ ui/dialogSetup.ui
+ ui/dialogSpaces.ui
+ ui/dialogTree.ui
+ ui/dialogTutorial.ui
+ ui/dialogViews.ui
+ ui/dialogWelcome.ui
+ ui/dialogWindows.ui
+ ui/preferences-arch.ui
+ ui/preferences-archdefaults.ui
+ ui/preferences-dae.ui
+ ui/preferences-ifc-export.ui
+ ui/preferences-ifc.ui
+ ui/preferencesNativeIFC.ui
diff --git a/src/Mod/BIM/Resources/ui/dialogConvertType.ui b/src/Mod/BIM/Resources/ui/dialogConvertType.ui
new file mode 100644
index 0000000000..4466ceacff
--- /dev/null
+++ b/src/Mod/BIM/Resources/ui/dialogConvertType.ui
@@ -0,0 +1,87 @@
+
+
+ Dialog
+
+
+
+ 0
+ 0
+ 448
+ 161
+
+
+
+ Convert to IFC type
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ This object will be converted to a %1 type. Types can be used to give common attributes and properties to several objects at once.
+
+
+ true
+
+
+
+ -
+
+
+ Keep original object. The object will adopt the new type
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ Dialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ Dialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/src/Mod/BIM/nativeifc/ifc_objects.py b/src/Mod/BIM/nativeifc/ifc_objects.py
index aa7c0c0462..5f22990942 100644
--- a/src/Mod/BIM/nativeifc/ifc_objects.py
+++ b/src/Mod/BIM/nativeifc/ifc_objects.py
@@ -22,6 +22,9 @@
"""This module contains IFC object definitions"""
+import FreeCAD
+translate = FreeCAD.Qt.translate
+
class ifc_object:
"""Base class for all IFC-based objects"""
@@ -52,6 +55,8 @@ class ifc_object:
self.rebuild_classlist(obj, setprops=True)
elif prop == "Schema":
self.edit_schema(obj, obj.Schema)
+ elif prop == "Type":
+ self.edit_type(obj)
elif prop == "Group":
self.edit_group(obj)
elif obj.getGroupOfProperty(prop) == "IFC":
@@ -70,7 +75,7 @@ class ifc_object:
obj.ViewObject.signalChangeIcon()
elif obj.getGroupOfProperty(prop) == "Geometry":
self.edit_geometry(obj, prop)
- elif obj.getGroupOfProperty(prop) not in ["Base", "IFC", "", "Geometry"]:
+ elif obj.getGroupOfProperty(prop) not in ["Base", "IFC", "", "Geometry", "PhysicalProperties"]:
# Treat all property groups outside the default ones as Psets
# print("DEBUG: editinog pset prop",prop)
self.edit_pset(obj, prop)
@@ -246,6 +251,42 @@ class ifc_object:
if newlist != obj.Group:
obj.Group = newlist
+ def edit_type(self, obj):
+ """Edits the type of this object"""
+
+ from nativeifc import ifc_tools # lazy import
+ from nativeifc import ifc_types
+
+ element = ifc_tools.get_ifc_element(obj)
+ ifcfile = ifc_tools.get_ifcfile(obj)
+ if not element or not ifcfile:
+ return
+ typerel = getattr(element, "IsTypedBy", None)
+ if obj.Type:
+ # verify the type is compatible -ex IFcWall in IfcWallType
+ if obj.Type.Class != element.is_a() + "Type":
+ t = translate("BIM","Error: Incompatible type")
+ FreeCAD.Console.PrintError(obj.Label+": "+t+": "+obj.Type.Class+"\n")
+ obj.Type = None
+ return
+ # change type
+ new_type = ifc_tools.get_ifc_element(obj.Type)
+ if not new_type:
+ return
+ for rel in typerel:
+ if rel.RelatingType == new_type:
+ return
+ # assign the new type
+ ifc_tools.api_run("type.assign_type",
+ ifcfile,
+ related_objects=[element],
+ relating_type=new_type
+ )
+ elif typerel:
+ # TODO remove type?
+ # Not doing anything right now because an unset Type property could screw the ifc file
+ pass
+
class document_object:
"""Holder for the document's IFC objects"""
diff --git a/src/Mod/BIM/nativeifc/ifc_tools.py b/src/Mod/BIM/nativeifc/ifc_tools.py
index 0f1cc44bec..00e92de8ef 100644
--- a/src/Mod/BIM/nativeifc/ifc_tools.py
+++ b/src/Mod/BIM/nativeifc/ifc_tools.py
@@ -525,6 +525,8 @@ def add_properties(
obj.ShapeMode = shapemode
if not obj.isDerivedFrom("Part::Feature"):
obj.setPropertyStatus("ShapeMode", "Hidden")
+ if ifcentity.is_a("IfcProduct"):
+ obj.addProperty("App::PropertyLink", "Type", "IFC")
attr_defs = ifcentity.wrapped_data.declaration().as_entity().all_attributes()
try:
info_ifcentity = ifcentity.get_info()
@@ -1190,7 +1192,7 @@ def get_subvolume(obj):
def create_relationship(old_obj, obj, parent, element, ifcfile, mode=None):
"""Creates a relationship between an IFC object and a parent IFC object"""
- if isinstance(parent, FreeCAD.DocumentObject):
+ if isinstance(parent, (FreeCAD.DocumentObject, FreeCAD.Document)):
parent_element = get_ifc_element(parent)
else:
parent_element = parent
@@ -1373,8 +1375,9 @@ def migrate_schema(ifcfile, schema):
return newfile, table
-def remove_ifc_element(obj):
- """removes the IFC data associated with an object"""
+def remove_ifc_element(obj,delete_obj=False):
+ """removes the IFC data associated with an object.
+ If delete_obj is True, the FreeCAD object is also deleted"""
# This function can become pure IFC
@@ -1382,6 +1385,8 @@ def remove_ifc_element(obj):
element = get_ifc_element(obj)
if ifcfile and element:
api_run("root.remove_product", ifcfile, product=element)
+ if delete_obj:
+ obj.Document.removeObject(obj.Name)
return True
return False
diff --git a/src/Mod/BIM/nativeifc/ifc_types.py b/src/Mod/BIM/nativeifc/ifc_types.py
new file mode 100644
index 0000000000..f02303cb3a
--- /dev/null
+++ b/src/Mod/BIM/nativeifc/ifc_types.py
@@ -0,0 +1,93 @@
+# ***************************************************************************
+# * *
+# * Copyright (c) 2023 Yorik van Havre *
+# * *
+# * This program is free software; you can redistribute it and/or modify *
+# * it under the terms of the GNU General Public License (GPL) *
+# * as published by the Free Software Foundation; either version 3 of *
+# * the License, or (at your option) any later version. *
+# * for detail see the LICENCE text file. *
+# * *
+# * This program is distributed in the hope that it will be useful, *
+# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+# * GNU General Public License for more details. *
+# * *
+# * You should have received a copy of the GNU Library General Public *
+# * License along with this program; if not, write to the Free Software *
+# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
+# * USA *
+# * *
+# ***************************************************************************
+
+"""Diffing tool for NativeIFC project objects"""
+
+
+import FreeCAD
+from nativeifc import ifc_tools
+
+translate = FreeCAD.Qt.translate
+
+
+def show_type(obj):
+ """Adds the types of that object as FreeCAD objects"""
+
+ element = ifc_tools.get_ifc_element(obj)
+ typerel = getattr(element, "IsTypedBy", None)
+ project = ifc_tools.get_project(obj)
+ ifcfile = ifc_tools.get_ifcfile(obj)
+ if not all([element, typerel, project, ifcfile]):
+ return
+ for rel in typerel:
+ typelt = rel.RelatingType
+ if typelt:
+ typeobj = ifc_tools.create_object(typelt, obj.Document, ifcfile)
+ ifc_tools.get_group(project, "IfcTypesGroup").addObject(typeobj)
+ obj.Type = typeobj
+
+
+def is_typable(obj):
+ """Checks if an object can become a type"""
+
+ element = ifc_tools.get_ifc_element(obj)
+ ifcfile = ifc_tools.get_ifcfile(obj)
+ if not element or not ifcfile:
+ return False
+ type_class = element.is_a() + "Type"
+ schema = ifcfile.wrapped_data.schema_name()
+ schema = ifc_tools.ifcopenshell.ifcopenshell_wrapper.schema_by_name(schema)
+ try:
+ declaration = schema.declaration_by_name(type_class)
+ except RuntimeError:
+ return False
+ return True
+
+
+def convert_to_type(obj, keep_object=False):
+ """Converts an object to a type. If keep_object is
+ True, the original object is kept (and adopts the new type)."""
+
+ if not is_typable(obj):
+ return
+ if not getattr(obj, "Shape", None):
+ return
+ if FreeCAD.GuiUp:
+ import FreeCADGui
+ dlg = FreeCADGui.PySideUic.loadUi(":/ui/dialogConvertType.ui")
+ result = dlg.exec_()
+ if not result:
+ return
+ keep_object = dlg.checkKeepObject.isChecked()
+ element = ifc_tools.get_ifc_element(obj)
+ ifcfile = ifc_tools.get_ifcfile(obj)
+ project = ifc_tools.get_project(obj)
+ if not element or not ifcfile or not project:
+ return
+ type_element = ifc_tools.api_run("root.copy_class", ifcfile, product=element)
+ type_element = ifc_tools.api_run("root.reassign_class", ifcfile, product=type_element, ifc_class=obj.Class+"Type")
+ type_obj = ifc_tools.create_object(type_element, obj.Document, ifcfile)
+ if keep_object:
+ obj.Type = type_obj
+ else:
+ ifc_tools.remove_ifc_element(obj, delete_obj=True)
+ ifc_tools.get_group(project, "IfcTypesGroup").addObject(type_obj)
diff --git a/src/Mod/BIM/nativeifc/ifc_viewproviders.py b/src/Mod/BIM/nativeifc/ifc_viewproviders.py
index ee7ac59fc2..3890b417cb 100644
--- a/src/Mod/BIM/nativeifc/ifc_viewproviders.py
+++ b/src/Mod/BIM/nativeifc/ifc_viewproviders.py
@@ -71,8 +71,8 @@ class ifc_vp_object:
def getIcon(self):
from PySide import QtCore, QtGui # lazy import
-
- rclass = self.Object.IfcClass.replace("StandardCase","")
+
+ rclass = self.Object.IfcClass.replace("StandardCase","")
ifcicon = ":/icons/IFC/" + rclass + ".svg"
if QtCore.QFile.exists(ifcicon):
if getattr(self, "ifcclass", "") != rclass:
@@ -95,6 +95,7 @@ class ifc_vp_object:
from nativeifc import ifc_tools # lazy import
from nativeifc import ifc_psets
from nativeifc import ifc_materials
+ from nativeifc import ifc_types
from PySide import QtCore, QtGui # lazy import
if FreeCADGui.activeWorkbench().name() != 'BIMWorkbench':
@@ -150,11 +151,14 @@ class ifc_vp_object:
action_material = QtGui.QAction(icon, "Load material",menu)
action_material.triggered.connect(self.addMaterial)
actions.append(action_material)
+ if ifc_types.is_typable(self.Object):
+ action_type = QtGui.QAction(icon, "Convert to type", menu)
+ action_type.triggered.connect(self.convertToType)
+ actions.append(action_type)
if actions:
ifc_menu = QtGui.QMenu("IFC")
ifc_menu.setIcon(icon)
- for a in actions:
- ifc_menu.addAction(a)
+ ifc_menu.addActions(actions)
menu.addMenu(ifc_menu)
# generic actions
@@ -362,6 +366,11 @@ class ifc_vp_object:
self.Object.Document.recompute()
def doubleClicked(self, vobj):
+ """On double-click"""
+
+ self.expandProperties(vobj)
+
+ def expandProperties(self, vobj):
"""Expands everything that needs to be expanded"""
from nativeifc import ifc_geometry # lazy import
@@ -369,12 +378,14 @@ class ifc_vp_object:
from nativeifc import ifc_psets # lazy import
from nativeifc import ifc_materials # lazy import
from nativeifc import ifc_layers # lazy import
+ from nativeifc import ifc_types # lazy import
# generic data loading
ifc_geometry.add_geom_properties(vobj.Object)
ifc_psets.show_psets(vobj.Object)
ifc_materials.show_material(vobj.Object)
ifc_layers.add_layers(vobj.Object)
+ ifc_types.show_type(vobj.Object)
# expand children
if self.hasChildren(vobj.Object):
@@ -390,6 +401,16 @@ class ifc_vp_object:
return True
return None
+ def convertToType(self):
+ """Converts this object to a type"""
+
+ if not hasattr(self, "Object"):
+ return
+ from nativeifc import ifc_types
+ ifc_types.convert_to_type(self.Object)
+ self.Object.Document.recompute()
+
+
class ifc_vp_document(ifc_vp_object):
"""View provider for the IFC document object"""