mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-22 20:02:30 +00:00
Fix #4069. IfcClash Blender interface now uses the new filter UI.
This commit is contained in:
@@ -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")
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user