Fix #4069. IfcClash Blender interface now uses the new filter UI.

This commit is contained in:
Dion Moult
2024-04-01 15:28:34 +11:00
parent efa8da83ba
commit b5d672ba42
6 changed files with 55 additions and 135 deletions
@@ -17,138 +17,36 @@
# 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 import json
import ifcopenshell.util.date import ifcopenshell.util.element
import ifcopenshell.util.classification
import blenderbim.tool as tool import blenderbim.tool as tool
from blenderbim.bim.ifc import IfcStore
def refresh(): def refresh():
ClassificationsData.is_loaded = False ClashData.is_loaded = False
ClassificationReferencesData.is_loaded = False
MaterialClassificationsData.is_loaded = False
CostClassificationsData.is_loaded = False
class ClassificationsData: class ClashData:
data = {} data = {}
is_loaded = False is_loaded = False
@classmethod @classmethod
def load(cls): def load(cls):
cls.is_loaded = True cls.is_loaded = True
cls.data["has_classification_file"] = cls.has_classification_file() cls.data = {}
cls.data["classifications"] = cls.classifications() cls.data["saved_searches"] = cls.saved_searches()
cls.data["available_classifications"] = cls.available_classifications()
@classmethod @classmethod
def has_classification_file(cls): def saved_searches(cls):
return bool(IfcStore.classification_file) if not tool.Ifc.get():
@classmethod
def classifications(cls):
results = []
for element in tool.Ifc.get().by_type("IfcClassification"):
data = element.get_info()
if tool.Ifc.get().schema == "IFC2X3" and element.EditionDate:
data["EditionDate"] = ifcopenshell.util.date.ifc2datetime(data["EditionDate"])
results.append(data)
return results
@classmethod
def available_classifications(cls):
if not IfcStore.classification_file:
return [] return []
return [(str(e.id()), e.Name, "") for e in IfcStore.classification_file.by_type("IfcClassification")] groups = tool.Ifc.get().by_type("IfcGroup")
class ReferencesData:
@classmethod
def active_classification_library(cls):
if not IfcStore.classification_file or not IfcStore.classification_file.by_type("IfcClassification"):
return False
props = bpy.context.scene.BIMClassificationProperties
name = IfcStore.classification_file.by_id(int(props.available_classifications)).Name
if name in [e.Name for e in tool.Ifc.get().by_type("IfcClassification")]:
return name
@classmethod
def classifications(cls):
return [(str(e.id()), e.Name, "") for e in tool.Ifc.get().by_type("IfcClassification")]
class ClassificationReferencesData(ReferencesData):
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.is_loaded = True
cls.data["references"] = cls.references()
cls.data["active_classification_library"] = cls.active_classification_library()
cls.data["classifications"] = cls.classifications()
cls.data["object_type"] = "OBJECT"
@classmethod
def references(cls):
results = [] results = []
element = tool.Ifc.get_entity(bpy.context.active_object) for group in groups:
if element: try:
for reference in ifcopenshell.util.classification.get_references(element): data = json.loads(group.Description)
data = reference.get_info() if isinstance(data, dict) and data.get("type", None) == "BBIM_Search" and data.get("query", None):
del data["ReferencedSource"] results.append(group)
results.append(data) except:
return results pass
return [(str(g.id()), g.Name or "Unnamed", "") for g in sorted(results, key=lambda x: x.Name or "Unnamed")]
class MaterialClassificationsData(ReferencesData):
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.is_loaded = True
cls.data["references"] = cls.references()
cls.data["active_classification_library"] = cls.active_classification_library()
cls.data["classifications"] = cls.classifications()
cls.data["object_type"] = "MATERIAL"
@classmethod
def references(cls):
results = []
element = tool.Ifc.get_entity(bpy.context.active_object.active_material)
if element:
for reference in ifcopenshell.util.classification.get_references(element):
data = reference.get_info()
del data["ReferencedSource"]
results.append(data)
return results
class CostClassificationsData(ReferencesData):
data = {}
is_loaded = False
@classmethod
def load(cls):
cls.is_loaded = True
cls.data["references"] = cls.references()
cls.data["active_classification_library"] = cls.active_classification_library()
cls.data["classifications"] = cls.classifications()
cls.data["object_type"] = "COST"
@classmethod
def references(cls):
results = []
element = tool.Ifc.get().by_id(
bpy.context.scene.BIMCostProperties.cost_items[
bpy.context.scene.BIMCostProperties.active_cost_item_index
].ifc_definition_id
)
if element:
for reference in ifcopenshell.util.classification.get_references(element):
data = reference.get_info()
del data["ReferencedSource"]
results.append(data)
return results
@@ -86,14 +86,14 @@ class ImportClashSets(bpy.types.Operator):
new_source = new.a.add() new_source = new.a.add()
new_source.name = clash_source["file"] new_source.name = clash_source["file"]
if "selector" in clash_source: if "selector" in clash_source:
new_source.selector = clash_source["selector"] tool.Search.import_filter_query(clash_source["selector"], new_source.filter_groups)
new_source.mode = clash_source["mode"] new_source.mode = clash_source["mode"]
if "b" in clash_set and clash_set["b"]: if "b" in clash_set and clash_set["b"]:
for clash_source in clash_set["b"]: for clash_source in clash_set["b"]:
new_source = new.b.add() new_source = new.b.add()
new_source.name = clash_source["file"] new_source.name = clash_source["file"]
if "selector" in clash_source: if "selector" in clash_source:
new_source.selector = clash_source["selector"] tool.Search.import_filter_query(clash_source["selector"], new_source.filter_groups)
new_source.mode = clash_source["mode"] new_source.mode = clash_source["mode"]
tool.Clash.import_active_clashes() tool.Clash.import_active_clashes()
return {"FINISHED"} return {"FINISHED"}
@@ -209,8 +209,11 @@ class ExecuteIfcClash(bpy.types.Operator):
def invoke(self, context, event): def invoke(self, context, event):
_, extension = os.path.splitext(self.filepath) _, extension = os.path.splitext(self.filepath)
if extension != ".json": if extension != ".bcf":
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".bcf") self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".json")
# TODO Temporarily until BCF support comes back
# if extension != ".json":
# self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".bcf")
WindowManager = context.window_manager WindowManager = context.window_manager
WindowManager.fileselect_add(self) WindowManager.fileselect_add(self)
return {"RUNNING_MODAL"} return {"RUNNING_MODAL"}
@@ -221,6 +224,9 @@ class ExecuteIfcClash(bpy.types.Operator):
self.props = context.scene.BIMClashProperties self.props = context.scene.BIMClashProperties
_, extension = os.path.splitext(self.filepath) _, extension = os.path.splitext(self.filepath)
if extension != ".bcf":
self.filepath = bpy.path.ensure_ext(self.filepath, ".json")
# TODO Temporarily until BCF support comes back
if extension != ".json": if extension != ".json":
self.filepath = bpy.path.ensure_ext(self.filepath, ".bcf") self.filepath = bpy.path.ensure_ext(self.filepath, ".bcf")
@@ -17,7 +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
from blenderbim.bim.prop import StrProperty, Attribute from blenderbim.bim.prop import StrProperty, Attribute, BIMFilterGroup
from bpy.types import PropertyGroup from bpy.types import PropertyGroup
from bpy.props import ( from bpy.props import (
PointerProperty, PointerProperty,
@@ -33,11 +33,12 @@ from bpy.props import (
class ClashSource(PropertyGroup): class ClashSource(PropertyGroup):
name: StringProperty(name="File") name: StringProperty(name="File")
selector: StringProperty(name="Selector") filter_groups: CollectionProperty(type=BIMFilterGroup, name="Filter Groups")
mode: EnumProperty( mode: EnumProperty(
items=[ items=[
("i", "Include", "Only the selected objects are included for clashing"), ("a", "All Elements", "All elements will be used for clashing"),
("e", "Exclude", "All objects except the selected objects are included for clashing"), ("i", "Include", "Only the selected elements are included for clashing"),
("e", "Exclude", "All elements except the selected elements are included for clashing"),
], ],
name="Mode", name="Mode",
) )
@@ -17,7 +17,9 @@
# 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 blenderbim.bim.helper
from bpy.types import Panel from bpy.types import Panel
from blenderbim.bim.module.clash.data import ClashData
class BIM_PT_ifcclash(Panel): class BIM_PT_ifcclash(Panel):
@@ -30,6 +32,9 @@ class BIM_PT_ifcclash(Panel):
bl_parent_id = "BIM_PT_tab_clash_detection" bl_parent_id = "BIM_PT_tab_clash_detection"
def draw(self, context): def draw(self, context):
if not ClashData.is_loaded:
ClashData.load()
layout = self.layout layout = self.layout
scene = context.scene scene = context.scene
@@ -78,6 +83,7 @@ class BIM_PT_ifcclash(Panel):
for index, source in enumerate(clash_set.a): for index, source in enumerate(clash_set.a):
row = layout.row(align=True) row = layout.row(align=True)
row.prop(source, "name", text="") row.prop(source, "name", text="")
row.prop(source, "mode", text="")
op = row.operator("bim.select_clash_source", icon="FILE_FOLDER", text="") op = row.operator("bim.select_clash_source", icon="FILE_FOLDER", text="")
op.index = index op.index = index
op.group = "a" op.group = "a"
@@ -85,9 +91,10 @@ class BIM_PT_ifcclash(Panel):
op.index = index op.index = index
op.group = "a" op.group = "a"
row = layout.row(align=True) if source.mode != "a":
row.prop(source, "mode", text="") blenderbim.bim.helper.draw_filter(
row.prop(source, "selector", text="") layout, source.filter_groups, ClashData, f"clash_{props.active_clash_set_index}_a_{index}"
)
row = layout.row(align=True) row = layout.row(align=True)
row.label(text="Group B:", icon="OUTLINER_OB_POINTCLOUD") row.label(text="Group B:", icon="OUTLINER_OB_POINTCLOUD")
@@ -96,6 +103,7 @@ class BIM_PT_ifcclash(Panel):
for index, source in enumerate(clash_set.b): for index, source in enumerate(clash_set.b):
row = layout.row(align=True) row = layout.row(align=True)
row.prop(source, "name", text="") row.prop(source, "name", text="")
row.prop(source, "mode", text="")
op = row.operator("bim.select_clash_source", icon="FILE_FOLDER", text="") op = row.operator("bim.select_clash_source", icon="FILE_FOLDER", text="")
op.index = index op.index = index
op.group = "b" op.group = "b"
@@ -103,9 +111,10 @@ class BIM_PT_ifcclash(Panel):
op.index = index op.index = index
op.group = "b" op.group = "b"
row = layout.row(align=True) if source.mode != "a":
row.prop(source, "mode", text="") blenderbim.bim.helper.draw_filter(
row.prop(source, "selector", text="") layout, source.filter_groups, ClashData, f"clash_{props.active_clash_set_index}_b_{index}"
)
row = layout.row() row = layout.row()
row.prop(props, "should_create_clash_snapshots") row.prop(props, "should_create_clash_snapshots")
+3 -2
View File
@@ -37,8 +37,9 @@ class Clash(blenderbim.core.tool.Clash):
for ab in ["a", "b"]: for ab in ["a", "b"]:
for data in getattr(clash_set, ab): for data in getattr(clash_set, ab):
clash_source = {"file": data.name} clash_source = {"file": data.name}
if data.selector: query = tool.Search.export_filter_query(data.filter_groups)
clash_source["selector"] = data.selector if query and data.mode != "a":
clash_source["selector"] = query
clash_source["mode"] = data.mode clash_source["mode"] = data.mode
if ab == "a": if ab == "a":
a.append(clash_source) a.append(clash_source)
+5
View File
@@ -26,6 +26,11 @@ class Search(blenderbim.core.tool.Search):
return bpy.context.active_object.data.BIMCameraProperties.include_filter_groups return bpy.context.active_object.data.BIMCameraProperties.include_filter_groups
elif module == "drawing_exclude": elif module == "drawing_exclude":
return bpy.context.active_object.data.BIMCameraProperties.exclude_filter_groups return bpy.context.active_object.data.BIMCameraProperties.exclude_filter_groups
elif module.startswith("clash"):
_, clash_set_index, ab, clash_source_index = module.split("_")
return getattr(bpy.context.scene.BIMClashProperties.clash_sets[int(clash_set_index)], ab)[
int(clash_source_index)
].filter_groups
@classmethod @classmethod
def import_filter_query(cls, query: str, filter_groups: bpy.types.bpy_prop_collection) -> None: def import_filter_query(cls, query: str, filter_groups: bpy.types.bpy_prop_collection) -> None: