Skip to content

Commit

Permalink
Fix #4069. IfcClash Blender interface now uses the new filter UI.
Browse files Browse the repository at this point in the history
  • Loading branch information
Moult committed Apr 1, 2024
1 parent efa8da8 commit b5d672b
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 135 deletions.
136 changes: 17 additions & 119 deletions src/blenderbim/blenderbim/bim/module/clash/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,138 +17,36 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.

import bpy
import ifcopenshell
import ifcopenshell.util.date
import ifcopenshell.util.classification
import json
import ifcopenshell.util.element
import blenderbim.tool as tool
from blenderbim.bim.ifc import IfcStore


def refresh():
ClassificationsData.is_loaded = False
ClassificationReferencesData.is_loaded = False
MaterialClassificationsData.is_loaded = False
CostClassificationsData.is_loaded = False
ClashData.is_loaded = False


class ClassificationsData:
class ClashData:
data = {}
is_loaded = False

@classmethod
def load(cls):
cls.is_loaded = True
cls.data["has_classification_file"] = cls.has_classification_file()
cls.data["classifications"] = cls.classifications()
cls.data["available_classifications"] = cls.available_classifications()
cls.data = {}
cls.data["saved_searches"] = cls.saved_searches()

@classmethod
def has_classification_file(cls):
return bool(IfcStore.classification_file)

@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:
def saved_searches(cls):
if not tool.Ifc.get():
return []
return [(str(e.id()), e.Name, "") for e in IfcStore.classification_file.by_type("IfcClassification")]


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 = []
element = tool.Ifc.get_entity(bpy.context.active_object)
if element:
for reference in ifcopenshell.util.classification.get_references(element):
data = reference.get_info()
del data["ReferencedSource"]
results.append(data)
return results


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):
groups = tool.Ifc.get().by_type("IfcGroup")
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
for group in groups:
try:
data = json.loads(group.Description)
if isinstance(data, dict) and data.get("type", None) == "BBIM_Search" and data.get("query", None):
results.append(group)
except:
pass
return [(str(g.id()), g.Name or "Unnamed", "") for g in sorted(results, key=lambda x: x.Name or "Unnamed")]
14 changes: 10 additions & 4 deletions src/blenderbim/blenderbim/bim/module/clash/operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,14 @@ def execute(self, context):
new_source = new.a.add()
new_source.name = clash_source["file"]
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"]
if "b" in clash_set and clash_set["b"]:
for clash_source in clash_set["b"]:
new_source = new.b.add()
new_source.name = clash_source["file"]
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"]
tool.Clash.import_active_clashes()
return {"FINISHED"}
Expand Down Expand Up @@ -209,8 +209,11 @@ class ExecuteIfcClash(bpy.types.Operator):

def invoke(self, context, event):
_, extension = os.path.splitext(self.filepath)
if extension != ".json":
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, ".bcf")
if extension != ".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.fileselect_add(self)
return {"RUNNING_MODAL"}
Expand All @@ -221,6 +224,9 @@ def execute(self, context):
self.props = context.scene.BIMClashProperties

_, 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":
self.filepath = bpy.path.ensure_ext(self.filepath, ".bcf")

Expand Down
9 changes: 5 additions & 4 deletions src/blenderbim/blenderbim/bim/module/clash/prop.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.

import bpy
from blenderbim.bim.prop import StrProperty, Attribute
from blenderbim.bim.prop import StrProperty, Attribute, BIMFilterGroup
from bpy.types import PropertyGroup
from bpy.props import (
PointerProperty,
Expand All @@ -33,11 +33,12 @@

class ClashSource(PropertyGroup):
name: StringProperty(name="File")
selector: StringProperty(name="Selector")
filter_groups: CollectionProperty(type=BIMFilterGroup, name="Filter Groups")
mode: EnumProperty(
items=[
("i", "Include", "Only the selected objects are included for clashing"),
("e", "Exclude", "All objects except the selected objects are included for clashing"),
("a", "All Elements", "All elements will be used 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",
)
Expand Down
21 changes: 15 additions & 6 deletions src/blenderbim/blenderbim/bim/module/clash/ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.

import bpy
import blenderbim.bim.helper
from bpy.types import Panel
from blenderbim.bim.module.clash.data import ClashData


class BIM_PT_ifcclash(Panel):
Expand All @@ -30,6 +32,9 @@ class BIM_PT_ifcclash(Panel):
bl_parent_id = "BIM_PT_tab_clash_detection"

def draw(self, context):
if not ClashData.is_loaded:
ClashData.load()

layout = self.layout

scene = context.scene
Expand Down Expand Up @@ -78,16 +83,18 @@ def draw(self, context):
for index, source in enumerate(clash_set.a):
row = layout.row(align=True)
row.prop(source, "name", text="")
row.prop(source, "mode", text="")
op = row.operator("bim.select_clash_source", icon="FILE_FOLDER", text="")
op.index = index
op.group = "a"
op = row.operator("bim.remove_clash_source", icon="X", text="")
op.index = index
op.group = "a"

row = layout.row(align=True)
row.prop(source, "mode", text="")
row.prop(source, "selector", text="")
if source.mode != "a":
blenderbim.bim.helper.draw_filter(
layout, source.filter_groups, ClashData, f"clash_{props.active_clash_set_index}_a_{index}"
)

row = layout.row(align=True)
row.label(text="Group B:", icon="OUTLINER_OB_POINTCLOUD")
Expand All @@ -96,16 +103,18 @@ def draw(self, context):
for index, source in enumerate(clash_set.b):
row = layout.row(align=True)
row.prop(source, "name", text="")
row.prop(source, "mode", text="")
op = row.operator("bim.select_clash_source", icon="FILE_FOLDER", text="")
op.index = index
op.group = "b"
op = row.operator("bim.remove_clash_source", icon="X", text="")
op.index = index
op.group = "b"

row = layout.row(align=True)
row.prop(source, "mode", text="")
row.prop(source, "selector", text="")
if source.mode != "a":
blenderbim.bim.helper.draw_filter(
layout, source.filter_groups, ClashData, f"clash_{props.active_clash_set_index}_b_{index}"
)

row = layout.row()
row.prop(props, "should_create_clash_snapshots")
Expand Down
5 changes: 3 additions & 2 deletions src/blenderbim/blenderbim/tool/clash.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,9 @@ def export_clash_sets(cls):
for ab in ["a", "b"]:
for data in getattr(clash_set, ab):
clash_source = {"file": data.name}
if data.selector:
clash_source["selector"] = data.selector
query = tool.Search.export_filter_query(data.filter_groups)
if query and data.mode != "a":
clash_source["selector"] = query
clash_source["mode"] = data.mode
if ab == "a":
a.append(clash_source)
Expand Down
5 changes: 5 additions & 0 deletions src/blenderbim/blenderbim/tool/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ def get_filter_groups(cls, module: str) -> bpy.types.bpy_prop_collection:
return bpy.context.active_object.data.BIMCameraProperties.include_filter_groups
elif module == "drawing_exclude":
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
def import_filter_query(cls, query: str, filter_groups: bpy.types.bpy_prop_collection) -> None:
Expand Down

0 comments on commit b5d672b

Please sign in to comment.