mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-25 09:32:37 +00:00
typing
This commit is contained in:
@@ -1170,7 +1170,7 @@ class IfcImportSettings:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def factory(context=None, input_file=None, logger=None):
|
def factory(context=None, input_file=None, logger=None):
|
||||||
scene_diff = bpy.context.scene.DiffProperties
|
scene_diff = tool.Blender.get_diff_props()
|
||||||
props = tool.Project.get_project_props()
|
props = tool.Project.get_project_props()
|
||||||
settings = IfcImportSettings()
|
settings = IfcImportSettings()
|
||||||
settings.input_file = input_file
|
settings.input_file = input_file
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ import isodate
|
|||||||
import bonsai.core.sequence as core
|
import bonsai.core.sequence as core
|
||||||
import bonsai.tool as tool
|
import bonsai.tool as tool
|
||||||
import bonsai.bim.module.sequence.helper as helper
|
import bonsai.bim.module.sequence.helper as helper
|
||||||
|
import ifcopenshell.api.spatial
|
||||||
|
import ifcopenshell.geom
|
||||||
import ifcopenshell.util.sequence
|
import ifcopenshell.util.sequence
|
||||||
import ifcopenshell.util.selector
|
import ifcopenshell.util.selector
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@@ -59,8 +61,6 @@ class ImportAlignmentCSV(bpy.types.Operator, tool.Ifc.Operator, ImportHelper):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
import ifcopenshell.api.alignment
|
|
||||||
|
|
||||||
self.file = tool.Ifc.get()
|
self.file = tool.Ifc.get()
|
||||||
start = time.time()
|
start = time.time()
|
||||||
alignment = ifcopenshell.api.alignment.create_alignment_from_csv(self.file, self.filepath)
|
alignment = ifcopenshell.api.alignment.create_alignment_from_csv(self.file, self.filepath)
|
||||||
|
|||||||
@@ -60,8 +60,9 @@ class ClashDecorator:
|
|||||||
unselected_elements_color = self.addon_prefs.decorator_color_unselected
|
unselected_elements_color = self.addon_prefs.decorator_color_unselected
|
||||||
special_elements_color = self.addon_prefs.decorator_color_special
|
special_elements_color = self.addon_prefs.decorator_color_special
|
||||||
|
|
||||||
text = context.scene.BIMClashProperties.active_clash_text
|
props = tool.Clash.get_clash_props()
|
||||||
p = context.scene.BIMClashProperties.p1.lerp(context.scene.BIMClashProperties.p2, 0.5)
|
text = props.active_clash_text
|
||||||
|
p = props.p1.lerp(props.p2, 0.5)
|
||||||
|
|
||||||
font_id = 0
|
font_id = 0
|
||||||
blf.size(font_id, 12)
|
blf.size(font_id, 12)
|
||||||
@@ -92,7 +93,8 @@ class ClashDecorator:
|
|||||||
# general shader
|
# general shader
|
||||||
self.shader = gpu.shader.from_builtin("UNIFORM_COLOR")
|
self.shader = gpu.shader.from_builtin("UNIFORM_COLOR")
|
||||||
|
|
||||||
selected_vertices = [context.scene.BIMClashProperties.p1, context.scene.BIMClashProperties.p2]
|
props = tool.Clash.get_clash_props()
|
||||||
|
selected_vertices = [props.p1, props.p2]
|
||||||
selected_edges = []
|
selected_edges = []
|
||||||
if selected_vertices[0] != selected_vertices[1]:
|
if selected_vertices[0] != selected_vertices[1]:
|
||||||
selected_edges = [[0, 1]]
|
selected_edges = [[0, 1]]
|
||||||
|
|||||||
@@ -69,9 +69,10 @@ class ImportClashSets(bpy.types.Operator):
|
|||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
tool.Clash.load_clash_sets(self.filepath)
|
tool.Clash.load_clash_sets(self.filepath)
|
||||||
context.scene.BIMClashProperties.clash_sets.clear()
|
props = tool.Clash.get_clash_props()
|
||||||
|
props.clash_sets.clear()
|
||||||
for clash_set in tool.Clash.get_clash_sets():
|
for clash_set in tool.Clash.get_clash_sets():
|
||||||
new = context.scene.BIMClashProperties.clash_sets.add()
|
new = props.clash_sets.add()
|
||||||
new.name = clash_set["name"]
|
new.name = clash_set["name"]
|
||||||
new.mode = clash_set["mode"]
|
new.mode = clash_set["mode"]
|
||||||
if new.mode == "intersection":
|
if new.mode == "intersection":
|
||||||
@@ -106,7 +107,8 @@ class AddClashSet(bpy.types.Operator):
|
|||||||
bl_description = "Add a clash set"
|
bl_description = "Add a clash set"
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
new = context.scene.BIMClashProperties.clash_sets.add()
|
props = tool.Clash.get_clash_props()
|
||||||
|
new = props.clash_sets.add()
|
||||||
new.name = "New Clash Set"
|
new.name = "New Clash Set"
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
@@ -119,7 +121,8 @@ class RemoveClashSet(bpy.types.Operator):
|
|||||||
index: bpy.props.IntProperty()
|
index: bpy.props.IntProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
context.scene.BIMClashProperties.clash_sets.remove(self.index)
|
props = tool.Clash.get_clash_props()
|
||||||
|
props.clash_sets.remove(self.index)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
@@ -131,7 +134,8 @@ class AddClashSource(bpy.types.Operator):
|
|||||||
group: bpy.props.StringProperty()
|
group: bpy.props.StringProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
clash_set = context.scene.BIMClashProperties.active_clash_set
|
props = tool.Clash.get_clash_props()
|
||||||
|
clash_set = props.active_clash_set
|
||||||
source = getattr(clash_set, self.group).add()
|
source = getattr(clash_set, self.group).add()
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
@@ -145,7 +149,8 @@ class RemoveClashSource(bpy.types.Operator):
|
|||||||
group: bpy.props.StringProperty()
|
group: bpy.props.StringProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
clash_set = context.scene.BIMClashProperties.active_clash_set
|
props = tool.Clash.get_clash_props()
|
||||||
|
clash_set = props.active_clash_set
|
||||||
getattr(clash_set, self.group).remove(self.index)
|
getattr(clash_set, self.group).remove(self.index)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
@@ -161,7 +166,8 @@ class SelectClashSource(bpy.types.Operator):
|
|||||||
group: bpy.props.StringProperty()
|
group: bpy.props.StringProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
clash_set = context.scene.BIMClashProperties.active_clash_set
|
props = tool.Clash.get_clash_props()
|
||||||
|
clash_set = props.active_clash_set
|
||||||
getattr(clash_set, self.group)[self.index].name = self.filepath
|
getattr(clash_set, self.group)[self.index].name = self.filepath
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
@@ -178,7 +184,8 @@ class SelectClashResults(bpy.types.Operator):
|
|||||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
context.scene.BIMClashProperties.clash_results_path = self.filepath
|
props = tool.Clash.get_clash_props()
|
||||||
|
props.clash_results_path = self.filepath
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
@@ -194,7 +201,8 @@ class SelectSmartGroupedClashesPath(bpy.types.Operator):
|
|||||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
context.scene.BIMClashProperties.smart_grouped_clashes_path = self.filepath
|
props = tool.Clash.get_clash_props()
|
||||||
|
props.smart_grouped_clashes_path = self.filepath
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
@@ -218,7 +226,7 @@ class ExecuteIfcClash(bpy.types.Operator):
|
|||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
from ifcclash import ifcclash
|
from ifcclash import ifcclash
|
||||||
|
|
||||||
self.props = context.scene.BIMClashProperties
|
self.props = tool.Clash.get_clash_props()
|
||||||
|
|
||||||
_, extension = os.path.splitext(self.filepath)
|
_, extension = os.path.splitext(self.filepath)
|
||||||
if extension != ".bcf":
|
if extension != ".bcf":
|
||||||
@@ -306,7 +314,9 @@ class SelectIfcClashResults(bpy.types.Operator):
|
|||||||
self.filepath = bpy.path.ensure_ext(self.filepath, ".json")
|
self.filepath = bpy.path.ensure_ext(self.filepath, ".json")
|
||||||
with open(self.filepath) as f:
|
with open(self.filepath) as f:
|
||||||
clash_sets = json.load(f)
|
clash_sets = json.load(f)
|
||||||
clash_set_name = context.scene.BIMClashProperties.active_clash_set.name
|
clash_props = tool.Clash.get_clash_props()
|
||||||
|
assert clash_props.active_clash_set
|
||||||
|
clash_set_name = clash_props.active_clash_set.name
|
||||||
global_ids = []
|
global_ids = []
|
||||||
for clash_set in clash_sets:
|
for clash_set in clash_sets:
|
||||||
if clash_set["name"] != clash_set_name:
|
if clash_set["name"] != clash_set_name:
|
||||||
@@ -358,7 +368,7 @@ class SelectClash(bpy.types.Operator):
|
|||||||
index: bpy.props.IntProperty()
|
index: bpy.props.IntProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
self.props = context.scene.BIMClashProperties
|
self.props = tool.Clash.get_clash_props()
|
||||||
clash_set = tool.Clash.get_clash_set(self.props.active_clash_set.name)
|
clash_set = tool.Clash.get_clash_set(self.props.active_clash_set.name)
|
||||||
active_clash = self.props.active_clash
|
active_clash = self.props.active_clash
|
||||||
clash = tool.Clash.get_clash(clash_set, active_clash.a_global_id, active_clash.b_global_id)
|
clash = tool.Clash.get_clash(clash_set, active_clash.a_global_id, active_clash.b_global_id)
|
||||||
@@ -392,13 +402,15 @@ class SmartClashGroup(bpy.types.Operator):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
return context.scene.BIMClashProperties.clash_results_path
|
props = tool.Clash.get_clash_props()
|
||||||
|
return bool(props.clash_results_path)
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
from ifcclash import ifcclash
|
from ifcclash import ifcclash
|
||||||
|
|
||||||
settings = ifcclash.ClashSettings()
|
settings = ifcclash.ClashSettings()
|
||||||
self.filepath = bpy.path.ensure_ext(context.scene.BIMClashProperties.clash_results_path, ".json")
|
props = tool.Clash.get_clash_props()
|
||||||
|
self.filepath = bpy.path.ensure_ext(props.clash_results_path, ".json")
|
||||||
settings.output = self.filepath
|
settings.output = self.filepath
|
||||||
settings.logger = logging.getLogger("Clash")
|
settings.logger = logging.getLogger("Clash")
|
||||||
settings.logger.setLevel(logging.DEBUG)
|
settings.logger.setLevel(logging.DEBUG)
|
||||||
@@ -408,19 +420,18 @@ class SmartClashGroup(bpy.types.Operator):
|
|||||||
clash_sets = json.load(f)
|
clash_sets = json.load(f)
|
||||||
|
|
||||||
# execute the smart grouping
|
# execute the smart grouping
|
||||||
save_path = bpy.path.ensure_ext(context.scene.BIMClashProperties.smart_grouped_clashes_path, ".json")
|
save_path = bpy.path.ensure_ext(props.smart_grouped_clashes_path, ".json")
|
||||||
smart_grouped_clashes = ifc_clasher.smart_group_clashes(
|
smart_grouped_clashes = ifc_clasher.smart_group_clashes(clash_sets, props.smart_clash_grouping_max_distance)
|
||||||
clash_sets, context.scene.BIMClashProperties.smart_clash_grouping_max_distance
|
|
||||||
)
|
|
||||||
|
|
||||||
# save smart_groups to json
|
# save smart_groups to json
|
||||||
with open(save_path, "w") as f:
|
with open(save_path, "w") as f:
|
||||||
f.write(json.dumps(smart_grouped_clashes))
|
f.write(json.dumps(smart_grouped_clashes))
|
||||||
|
|
||||||
clash_set_name = context.scene.BIMClashProperties.active_clash_set.name
|
assert props.active_clash_set
|
||||||
|
clash_set_name = props.active_clash_set.name
|
||||||
|
|
||||||
# Reset the list of smart_clash_groups for the UI
|
# Reset the list of smart_clash_groups for the UI
|
||||||
context.scene.BIMClashProperties.smart_clash_groups.clear()
|
props.smart_clash_groups.clear()
|
||||||
|
|
||||||
for clash_set, smart_groups in smart_grouped_clashes.items():
|
for clash_set, smart_groups in smart_grouped_clashes.items():
|
||||||
# Only select the clashes that correspond to the actively selected IFC Clash Set
|
# Only select the clashes that correspond to the actively selected IFC Clash Set
|
||||||
@@ -428,7 +439,7 @@ class SmartClashGroup(bpy.types.Operator):
|
|||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
for smart_group, global_id_pairs in smart_groups[0].items():
|
for smart_group, global_id_pairs in smart_groups[0].items():
|
||||||
new_group = context.scene.BIMClashProperties.smart_clash_groups.add()
|
new_group = props.smart_clash_groups.add()
|
||||||
new_group.number = f"{smart_group}"
|
new_group.number = f"{smart_group}"
|
||||||
|
|
||||||
for pair in global_id_pairs:
|
for pair in global_id_pairs:
|
||||||
@@ -446,18 +457,21 @@ class LoadSmartGroupsForActiveClashSet(bpy.types.Operator):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
return context.scene.BIMClashProperties.active_clash_set
|
props = tool.Clash.get_clash_props()
|
||||||
|
return bool(props.active_clash_set)
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
smart_groups_path = bpy.path.ensure_ext(context.scene.BIMClashProperties.smart_grouped_clashes_path, ".json")
|
props = tool.Clash.get_clash_props()
|
||||||
|
smart_groups_path = bpy.path.ensure_ext(props.smart_grouped_clashes_path, ".json")
|
||||||
|
|
||||||
clash_set_name = context.scene.BIMClashProperties.active_clash_set.name
|
assert props.active_clash_set
|
||||||
|
clash_set_name = props.active_clash_set.name
|
||||||
|
|
||||||
with open(smart_groups_path) as f:
|
with open(smart_groups_path) as f:
|
||||||
smart_grouped_clashes = json.load(f)
|
smart_grouped_clashes = json.load(f)
|
||||||
|
|
||||||
# Reset the list of smart_clash_groups for the UI
|
# Reset the list of smart_clash_groups for the UI
|
||||||
context.scene.BIMClashProperties.smart_clash_groups.clear()
|
props.smart_clash_groups.clear()
|
||||||
|
|
||||||
for clash_set, smart_groups in smart_grouped_clashes.items():
|
for clash_set, smart_groups in smart_grouped_clashes.items():
|
||||||
# Only select the clashes that correspond to the actively selected IFC Clash Set
|
# Only select the clashes that correspond to the actively selected IFC Clash Set
|
||||||
@@ -465,7 +479,7 @@ class LoadSmartGroupsForActiveClashSet(bpy.types.Operator):
|
|||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
for smart_group, global_id_pairs in smart_groups[0].items():
|
for smart_group, global_id_pairs in smart_groups[0].items():
|
||||||
new_group = context.scene.BIMClashProperties.smart_clash_groups.add()
|
new_group = props.smart_clash_groups.add()
|
||||||
new_group.number = f"{smart_group}"
|
new_group.number = f"{smart_group}"
|
||||||
for pair in global_id_pairs:
|
for pair in global_id_pairs:
|
||||||
for guid in pair:
|
for guid in pair:
|
||||||
@@ -482,11 +496,14 @@ class SelectSmartGroup(bpy.types.Operator):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
return tool.Ifc.get() and context.visible_objects and context.scene.BIMClashProperties.active_smart_group
|
props = tool.Clash.get_clash_props()
|
||||||
|
return tool.Ifc.get() and context.visible_objects and props.active_smart_group
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
selected_smart_group = context.scene.BIMClashProperties.active_smart_group
|
props = tool.Clash.get_clash_props()
|
||||||
products = []
|
selected_smart_group = props.active_smart_group
|
||||||
|
assert selected_smart_group
|
||||||
|
products: list[ifcopenshell.entity_instance] = []
|
||||||
for global_id in selected_smart_group.global_ids:
|
for global_id in selected_smart_group.global_ids:
|
||||||
try:
|
try:
|
||||||
products.append(tool.Ifc.get().by_guid(global_id.guid))
|
products.append(tool.Ifc.get().by_guid(global_id.guid))
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
|
import bonsai.tool as tool
|
||||||
from bonsai.bim.prop import StrProperty, Attribute, BIMFilterGroup
|
from bonsai.bim.prop import StrProperty, Attribute, BIMFilterGroup
|
||||||
from bpy.types import PropertyGroup
|
from bpy.types import PropertyGroup
|
||||||
from bpy.props import (
|
from bpy.props import (
|
||||||
@@ -29,6 +30,8 @@ from bpy.props import (
|
|||||||
FloatVectorProperty,
|
FloatVectorProperty,
|
||||||
CollectionProperty,
|
CollectionProperty,
|
||||||
)
|
)
|
||||||
|
from mathutils import Vector
|
||||||
|
from typing import TYPE_CHECKING, Literal, Union
|
||||||
|
|
||||||
|
|
||||||
class ClashSource(PropertyGroup):
|
class ClashSource(PropertyGroup):
|
||||||
@@ -43,6 +46,10 @@ class ClashSource(PropertyGroup):
|
|||||||
name="Mode",
|
name="Mode",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
filter_groups: bpy.types.bpy_prop_collection_idprop[BIMFilterGroup]
|
||||||
|
mode: Literal["a", "i", "e"]
|
||||||
|
|
||||||
|
|
||||||
class Clash(PropertyGroup):
|
class Clash(PropertyGroup):
|
||||||
a_global_id: StringProperty(name="A")
|
a_global_id: StringProperty(name="A")
|
||||||
@@ -51,9 +58,15 @@ class Clash(PropertyGroup):
|
|||||||
b_name: StringProperty(name="B Name")
|
b_name: StringProperty(name="B Name")
|
||||||
status: BoolProperty(name="Status", default=False)
|
status: BoolProperty(name="Status", default=False)
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
a_global_id: str
|
||||||
|
b_global_id: str
|
||||||
|
a_name: str
|
||||||
|
b_name: str
|
||||||
|
status: bool
|
||||||
|
|
||||||
|
|
||||||
class ClashSet(PropertyGroup):
|
class ClashSet(PropertyGroup):
|
||||||
name: StringProperty(name="Name")
|
|
||||||
mode: EnumProperty(
|
mode: EnumProperty(
|
||||||
items=[
|
items=[
|
||||||
(
|
(
|
||||||
@@ -76,11 +89,25 @@ class ClashSet(PropertyGroup):
|
|||||||
b: CollectionProperty(name="Group B", type=ClashSource)
|
b: CollectionProperty(name="Group B", type=ClashSource)
|
||||||
clashes: CollectionProperty(name="Clashes", type=Clash)
|
clashes: CollectionProperty(name="Clashes", type=Clash)
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
mode: Literal["intersection", "collision", "clearance"]
|
||||||
|
tolerance: float
|
||||||
|
clearance: float
|
||||||
|
allow_touching: bool
|
||||||
|
check_all: bool
|
||||||
|
a: bpy.types.bpy_prop_collection_idprop[ClashSource]
|
||||||
|
b: bpy.types.bpy_prop_collection_idprop[ClashSource]
|
||||||
|
clashes: bpy.types.bpy_prop_collection_idprop[Clash]
|
||||||
|
|
||||||
|
|
||||||
class SmartClashGroup(PropertyGroup):
|
class SmartClashGroup(PropertyGroup):
|
||||||
number: StringProperty(name="Number")
|
number: StringProperty(name="Number")
|
||||||
global_ids: CollectionProperty(name="GlobalIDs", type=StrProperty)
|
global_ids: CollectionProperty(name="GlobalIDs", type=StrProperty)
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
number: str
|
||||||
|
global_ids: bpy.types.bpy_prop_collection_idprop[StrProperty]
|
||||||
|
|
||||||
|
|
||||||
class BIMClashProperties(PropertyGroup):
|
class BIMClashProperties(PropertyGroup):
|
||||||
blender_clash_set_a: CollectionProperty(name="Blender Clash Set A", type=StrProperty)
|
blender_clash_set_a: CollectionProperty(name="Blender Clash Set A", type=StrProperty)
|
||||||
@@ -107,17 +134,33 @@ class BIMClashProperties(PropertyGroup):
|
|||||||
subtype="FILE_PATH",
|
subtype="FILE_PATH",
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
if TYPE_CHECKING:
|
||||||
def active_clash_set(self):
|
blender_clash_set_a: bpy.types.bpy_prop_collection_idprop[StrProperty]
|
||||||
if self.active_clash_set_index < len(self.clash_sets):
|
blender_clash_set_b: bpy.types.bpy_prop_collection_idprop[StrProperty]
|
||||||
return self.clash_sets[self.active_clash_set_index]
|
clash_sets: bpy.types.bpy_prop_collection_idprop[ClashSet]
|
||||||
|
should_create_clash_snapshots: bool
|
||||||
|
clash_results_path: str
|
||||||
|
smart_grouped_clashes_path: str
|
||||||
|
active_clash_set_index: int
|
||||||
|
active_clash_index: int
|
||||||
|
smart_clash_groups: bpy.types.bpy_prop_collection_idprop[SmartClashGroup]
|
||||||
|
active_smart_group_index: int
|
||||||
|
smart_clash_grouping_max_distance: int
|
||||||
|
p1: Vector
|
||||||
|
p2: Vector
|
||||||
|
active_clash_text: str
|
||||||
|
export_path: str
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def active_smart_group(self):
|
def active_clash_set(self) -> Union[ClashSet, None]:
|
||||||
if self.active_smart_group_index < len(self.smart_clash_groups):
|
return tool.Blender.get_active_uilist_element(self.clash_sets, self.active_clash_set_index)
|
||||||
return self.smart_clash_groups[self.active_smart_group_index]
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def active_clash(self):
|
def active_smart_group(self) -> Union[SmartClashGroup, None]:
|
||||||
if self.active_clash_index < len(self.active_clash_set.clashes):
|
return tool.Blender.get_active_uilist_element(self.smart_clash_groups, self.active_smart_group_index)
|
||||||
return self.active_clash_set.clashes[self.active_clash_index]
|
|
||||||
|
@property
|
||||||
|
def active_clash(self) -> Union[Clash, None]:
|
||||||
|
if not (clash_set := self.active_clash_set):
|
||||||
|
return None
|
||||||
|
return tool.Blender.get_active_uilist_element(clash_set.clashes, self.active_clash_index)
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
|
import bonsai.tool as tool
|
||||||
import bonsai.bim.helper
|
import bonsai.bim.helper
|
||||||
from bpy.types import Panel
|
from bpy.types import Panel
|
||||||
from bonsai.bim.module.clash.data import ClashData
|
from bonsai.bim.module.clash.data import ClashData
|
||||||
@@ -36,9 +37,7 @@ class BIM_PT_ifcclash(Panel):
|
|||||||
ClashData.load()
|
ClashData.load()
|
||||||
|
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
|
props = tool.Clash.get_clash_props()
|
||||||
scene = context.scene
|
|
||||||
props = scene.BIMClashProperties
|
|
||||||
|
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
row.operator("bim.add_clash_set")
|
row.operator("bim.add_clash_set")
|
||||||
@@ -157,7 +156,7 @@ class BIM_PT_smart_clash_manager(Panel):
|
|||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
props = context.scene.BIMClashProperties
|
props = tool.Clash.get_clash_props()
|
||||||
|
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
layout.label(text="Select clash results to group:")
|
layout.label(text="Select clash results to group:")
|
||||||
|
|||||||
@@ -42,7 +42,8 @@ class AddCsvAttribute(bpy.types.Operator):
|
|||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
attribute = context.scene.CsvProperties.csv_attributes.add()
|
props = tool.Blender.get_csv_props()
|
||||||
|
props.csv_attributes.add()
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
@@ -53,7 +54,8 @@ class RemoveCsvAttribute(bpy.types.Operator):
|
|||||||
index: bpy.props.IntProperty()
|
index: bpy.props.IntProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
context.scene.CsvProperties.csv_attributes.remove(self.index)
|
props = tool.Blender.get_csv_props()
|
||||||
|
props.csv_attributes.remove(self.index)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
@@ -64,7 +66,8 @@ class RemoveAllCsvAttributes(bpy.types.Operator):
|
|||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
context.scene.CsvProperties.csv_attributes.clear()
|
props = tool.Blender.get_csv_props()
|
||||||
|
props.csv_attributes.clear()
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
@@ -76,8 +79,9 @@ class ReorderCsvAttribute(bpy.types.Operator):
|
|||||||
new_index: bpy.props.IntProperty()
|
new_index: bpy.props.IntProperty()
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
old = context.scene.CsvProperties.csv_attributes[self.old_index]
|
props = tool.Blender.get_csv_props()
|
||||||
new = context.scene.CsvProperties.csv_attributes[self.new_index]
|
old = props.csv_attributes[self.old_index]
|
||||||
|
new = props.csv_attributes[self.new_index]
|
||||||
props = ["name", "header", "sort", "group", "varies_value", "summary", "formatting"]
|
props = ["name", "header", "sort", "group", "varies_value", "summary", "formatting"]
|
||||||
for prop in props:
|
for prop in props:
|
||||||
value = getattr(new, prop)
|
value = getattr(new, prop)
|
||||||
@@ -95,7 +99,7 @@ class ImportCsvAttributes(bpy.types.Operator):
|
|||||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
props = context.scene.CsvProperties
|
props = tool.Blender.get_csv_props()
|
||||||
data = json.load(open(self.filepath))
|
data = json.load(open(self.filepath))
|
||||||
tool.Search.import_filter_query(data["query"], props.filter_groups)
|
tool.Search.import_filter_query(data["query"], props.filter_groups)
|
||||||
|
|
||||||
@@ -136,7 +140,7 @@ class ExportCsvAttributes(bpy.types.Operator):
|
|||||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
props = context.scene.CsvProperties
|
props = tool.Blender.get_csv_props()
|
||||||
|
|
||||||
settings = {}
|
settings = {}
|
||||||
for prop in [
|
for prop in [
|
||||||
@@ -191,14 +195,14 @@ class ExportIfcCsv(bpy.types.Operator):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
props = context.scene.CsvProperties
|
props = tool.Blender.get_csv_props()
|
||||||
if not props.should_load_from_memory and not props.csv_ifc_file:
|
if not props.should_load_from_memory and not props.csv_ifc_file:
|
||||||
cls.poll_message_set("Select an IFC file or use 'load from memory' if it's loaded in Bonsai.")
|
cls.poll_message_set("Select an IFC file or use 'load from memory' if it's loaded in Bonsai.")
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
props = context.scene.CsvProperties
|
props = tool.Blender.get_csv_props()
|
||||||
if props.format == "web":
|
if props.format == "web":
|
||||||
return self.execute(context)
|
return self.execute(context)
|
||||||
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, f".{props.format}")
|
self.filepath = bpy.path.ensure_ext(bpy.data.filepath, f".{props.format}")
|
||||||
@@ -209,7 +213,7 @@ class ExportIfcCsv(bpy.types.Operator):
|
|||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
import ifccsv
|
import ifccsv
|
||||||
|
|
||||||
props = context.scene.CsvProperties
|
props = tool.Blender.get_csv_props()
|
||||||
self.filepath = bpy.path.ensure_ext(self.filepath, f".{props.format}")
|
self.filepath = bpy.path.ensure_ext(self.filepath, f".{props.format}")
|
||||||
if props.should_load_from_memory:
|
if props.should_load_from_memory:
|
||||||
ifc_file = tool.Ifc.get()
|
ifc_file = tool.Ifc.get()
|
||||||
@@ -298,7 +302,7 @@ class ImportIfcCsv(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
props = context.scene.CsvProperties
|
props = tool.Blender.get_csv_props()
|
||||||
if not props.should_load_from_memory and not props.csv_ifc_file:
|
if not props.should_load_from_memory and not props.csv_ifc_file:
|
||||||
cls.poll_message_set("Select an IFC file or use 'load from memory' if it's loaded in Bonsai.")
|
cls.poll_message_set("Select an IFC file or use 'load from memory' if it's loaded in Bonsai.")
|
||||||
return False
|
return False
|
||||||
@@ -313,7 +317,7 @@ class ImportIfcCsv(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
import ifccsv
|
import ifccsv
|
||||||
|
|
||||||
props = context.scene.CsvProperties
|
props = tool.Blender.get_csv_props()
|
||||||
if props.should_load_from_memory:
|
if props.should_load_from_memory:
|
||||||
ifc_file = tool.Ifc.get()
|
ifc_file = tool.Ifc.get()
|
||||||
else:
|
else:
|
||||||
@@ -349,7 +353,8 @@ class SelectCsvIfcFile(bpy.types.Operator):
|
|||||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
context.scene.CsvProperties.csv_ifc_file = self.filepath
|
props = tool.Blender.get_csv_props()
|
||||||
|
props.csv_ifc_file = self.filepath
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ from bpy.props import (
|
|||||||
FloatVectorProperty,
|
FloatVectorProperty,
|
||||||
CollectionProperty,
|
CollectionProperty,
|
||||||
)
|
)
|
||||||
|
from typing import TYPE_CHECKING, Literal
|
||||||
|
|
||||||
|
|
||||||
class CsvAttribute(PropertyGroup):
|
class CsvAttribute(PropertyGroup):
|
||||||
@@ -59,6 +60,14 @@ class CsvAttribute(PropertyGroup):
|
|||||||
)
|
)
|
||||||
formatting: StringProperty(default="{{value}}", name="Formatting")
|
formatting: StringProperty(default="{{value}}", name="Formatting")
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
header: str
|
||||||
|
sort: Literal["NONE", "ASC", "DESC"]
|
||||||
|
group: Literal["NONE", "GROUP", "CONCAT", "VARIES", "SUM", "AVERAGE", "MIN", "MAX"]
|
||||||
|
varies_value: str
|
||||||
|
summary: Literal["NONE", "SUM", "AVERAGE", "MIN", "MAX"]
|
||||||
|
formatting: str
|
||||||
|
|
||||||
|
|
||||||
class CsvProperties(PropertyGroup):
|
class CsvProperties(PropertyGroup):
|
||||||
csv_ifc_file: StringProperty(default="", name="IFC File")
|
csv_ifc_file: StringProperty(default="", name="IFC File")
|
||||||
@@ -104,3 +113,26 @@ class CsvProperties(PropertyGroup):
|
|||||||
name="Load from Memory",
|
name="Load from Memory",
|
||||||
description="Use IFC file currently loaded in Bonsai",
|
description="Use IFC file currently loaded in Bonsai",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
csv_ifc_file: str
|
||||||
|
ifc_selector: str
|
||||||
|
filter_groups: bpy.types.bpy_prop_collection_idprop[BIMFilterGroup]
|
||||||
|
csv_attributes: bpy.types.bpy_prop_collection_idprop[CsvAttribute]
|
||||||
|
should_generate_svg: bool
|
||||||
|
should_preserve_existing: bool
|
||||||
|
include_global_id: bool
|
||||||
|
null_value: str
|
||||||
|
empty_value: str
|
||||||
|
true_value: str
|
||||||
|
false_value: str
|
||||||
|
concat_value: str
|
||||||
|
csv_delimiter: Literal["NONE", "ASC", "DESC"]
|
||||||
|
format: Literal["csv", "xlsx", "ods", "web"]
|
||||||
|
csv_custom_delimiter: str
|
||||||
|
should_show_settings: bool
|
||||||
|
should_show_sort: bool
|
||||||
|
should_show_group: bool
|
||||||
|
should_show_summary: bool
|
||||||
|
should_show_formatting: bool
|
||||||
|
should_load_from_memory: bool
|
||||||
|
|||||||
@@ -33,9 +33,7 @@ class BIM_PT_ifccsv(Panel):
|
|||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
|
props = tool.Blender.get_csv_props()
|
||||||
scene = context.scene
|
|
||||||
props = scene.CsvProperties
|
|
||||||
|
|
||||||
if tool.Ifc.get():
|
if tool.Ifc.get():
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ class DiffData:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def diff_json(cls):
|
def diff_json(cls):
|
||||||
props = bpy.context.scene.DiffProperties
|
props = tool.Blender.get_diff_props()
|
||||||
if not props.diff_json_file:
|
if not props.diff_json_file:
|
||||||
cls.diff = None
|
cls.diff = None
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -36,7 +36,8 @@ class SelectDiffJsonFile(bpy.types.Operator):
|
|||||||
filter_glob: bpy.props.StringProperty(default="*.json", options={"HIDDEN"})
|
filter_glob: bpy.props.StringProperty(default="*.json", options={"HIDDEN"})
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
context.scene.DiffProperties.diff_json_file = self.filepath
|
props = tool.Blender.get_diff_props()
|
||||||
|
props.diff_json_file = self.filepath
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
@@ -51,7 +52,8 @@ class VisualiseDiff(bpy.types.Operator):
|
|||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
ifc_file = tool.Ifc.get()
|
ifc_file = tool.Ifc.get()
|
||||||
with open(context.scene.DiffProperties.diff_json_file, "r") as file:
|
props = tool.Blender.get_diff_props()
|
||||||
|
with open(props.diff_json_file, "r") as file:
|
||||||
diff = json.load(file)
|
diff = json.load(file)
|
||||||
for obj in context.visible_objects:
|
for obj in context.visible_objects:
|
||||||
obj.color = (1.0, 1.0, 1.0, 1.0)
|
obj.color = (1.0, 1.0, 1.0, 1.0)
|
||||||
@@ -111,7 +113,8 @@ class SelectDiffOldFile(bpy.types.Operator):
|
|||||||
filter_glob: bpy.props.StringProperty(default="*.ifc", options={"HIDDEN"})
|
filter_glob: bpy.props.StringProperty(default="*.ifc", options={"HIDDEN"})
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
context.scene.DiffProperties.old_file = self.filepath
|
props = tool.Blender.get_diff_props()
|
||||||
|
props.old_file = self.filepath
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
@@ -128,7 +131,8 @@ class SelectDiffNewFile(bpy.types.Operator):
|
|||||||
filter_glob: bpy.props.StringProperty(default="*.ifc", options={"HIDDEN"})
|
filter_glob: bpy.props.StringProperty(default="*.ifc", options={"HIDDEN"})
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
context.scene.DiffProperties.new_file = self.filepath
|
props = tool.Blender.get_diff_props()
|
||||||
|
props.new_file = self.filepath
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
@@ -153,7 +157,7 @@ class ExecuteIfcDiff(bpy.types.Operator):
|
|||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
import ifcdiff
|
import ifcdiff
|
||||||
|
|
||||||
self.props = context.scene.DiffProperties
|
self.props = tool.Blender.get_diff_props()
|
||||||
|
|
||||||
if tool.Ifc.get():
|
if tool.Ifc.get():
|
||||||
if self.props.active_file == "NONE":
|
if self.props.active_file == "NONE":
|
||||||
@@ -242,7 +246,8 @@ class SelectDiffObjects(bpy.types.Operator):
|
|||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
ifc_file = tool.Ifc.get()
|
ifc_file = tool.Ifc.get()
|
||||||
with open(context.scene.DiffProperties.diff_json_file, "r") as file:
|
props = tool.Blender.get_diff_props()
|
||||||
|
with open(props.diff_json_file, "r") as file:
|
||||||
diff = json.load(file)
|
diff = json.load(file)
|
||||||
for obj in context.visible_objects:
|
for obj in context.visible_objects:
|
||||||
obj.select_set(False)
|
obj.select_set(False)
|
||||||
|
|||||||
@@ -30,18 +30,25 @@ from bpy.props import (
|
|||||||
FloatVectorProperty,
|
FloatVectorProperty,
|
||||||
CollectionProperty,
|
CollectionProperty,
|
||||||
)
|
)
|
||||||
|
from typing import TYPE_CHECKING, Literal, get_args
|
||||||
|
|
||||||
|
|
||||||
def update_diff_json_file(self, context):
|
def update_diff_json_file(self: "DiffProperties", context: bpy.types.Context) -> None:
|
||||||
DiffData.data["diff_json"] = DiffData.diff_json()
|
DiffData.data["diff_json"] = DiffData.diff_json()
|
||||||
|
|
||||||
|
|
||||||
|
RelationshipType = Literal["type", "property", "container", "aggregate", "classification"]
|
||||||
|
|
||||||
|
|
||||||
class Relationships(PropertyGroup):
|
class Relationships(PropertyGroup):
|
||||||
relationship: EnumProperty(
|
relationship: EnumProperty(
|
||||||
name="Relationship",
|
name="Relationship",
|
||||||
items=[(r, r.capitalize(), r) for r in ["type", "property", "container", "aggregate", "classification"]],
|
items=[(r, r.capitalize(), r) for r in get_args(RelationshipType)],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
relationship: RelationshipType
|
||||||
|
|
||||||
|
|
||||||
class DiffProperties(PropertyGroup):
|
class DiffProperties(PropertyGroup):
|
||||||
diff_json_file: StringProperty(default="", name="JSON Output", update=update_diff_json_file)
|
diff_json_file: StringProperty(default="", name="JSON Output", update=update_diff_json_file)
|
||||||
@@ -59,3 +66,12 @@ class DiffProperties(PropertyGroup):
|
|||||||
name="Active File",
|
name="Active File",
|
||||||
default="NEW",
|
default="NEW",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
diff_json_file: str
|
||||||
|
old_file: str
|
||||||
|
new_file: str
|
||||||
|
diff_relationships: bpy.types.bpy_prop_collection_idprop[Relationships]
|
||||||
|
filter_groups: bpy.types.bpy_prop_collection_idprop[BIMFilterGroup]
|
||||||
|
should_load_changed_elements: bool
|
||||||
|
active_file: Literal["NONE", "OLD", "NEW"]
|
||||||
|
|||||||
@@ -37,9 +37,7 @@ class BIM_PT_diff(Panel):
|
|||||||
|
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
layout.use_property_split = True
|
layout.use_property_split = True
|
||||||
|
props = tool.Blender.get_diff_props()
|
||||||
scene = context.scene
|
|
||||||
props = scene.DiffProperties
|
|
||||||
|
|
||||||
layout.label(text="IFC Diff Setup:")
|
layout.label(text="IFC Diff Setup:")
|
||||||
|
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ class Search(Operator):
|
|||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
if self.property_group == "CsvProperties":
|
if self.property_group == "CsvProperties":
|
||||||
props = context.scene.CsvProperties
|
props = tool.Blender.get_csv_props()
|
||||||
elif self.property_group == "BIMSearchProperties":
|
elif self.property_group == "BIMSearchProperties":
|
||||||
props = tool.Search.get_search_props()
|
props = tool.Search.get_search_props()
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ from typing_extensions import assert_never
|
|||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from bonsai.bim.prop import BIMProperties, BIMObjectProperties
|
from bonsai.bim.prop import BIMProperties, BIMObjectProperties
|
||||||
|
from bonsai.bim.module.csv.prop import CsvProperties
|
||||||
|
from bonsai.bim.module.diff.prop import DiffProperties
|
||||||
|
|
||||||
T = TypeVar("T")
|
T = TypeVar("T")
|
||||||
|
|
||||||
@@ -1600,6 +1602,14 @@ class Blender(bonsai.core.tool.Blender):
|
|||||||
dct = {cls.bl_idname: cls.ifc_element_type for cls in (BimTool.__subclasses__())}
|
dct = {cls.bl_idname: cls.ifc_element_type for cls in (BimTool.__subclasses__())}
|
||||||
return types.MappingProxyType(dct)
|
return types.MappingProxyType(dct)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_csv_props(cls) -> CsvProperties:
|
||||||
|
return bpy.context.scene.CsvProperties
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_diff_props(cls) -> DiffProperties:
|
||||||
|
return bpy.context.scene.DiffProperties
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_bim_props(cls, scene: Optional[bpy.types.Scene] = None) -> BIMProperties:
|
def get_bim_props(cls, scene: Optional[bpy.types.Scene] = None) -> BIMProperties:
|
||||||
if scene is None:
|
if scene is None:
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
# 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 Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
import os
|
import os
|
||||||
import bpy
|
import bpy
|
||||||
import json
|
import json
|
||||||
@@ -25,14 +26,23 @@ import bonsai.tool as tool
|
|||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from mathutils import Vector
|
from mathutils import Vector
|
||||||
from ifcclash import ifcclash
|
from ifcclash import ifcclash
|
||||||
|
from typing import TYPE_CHECKING, Union
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from bonsai.bim.module.clash.prop import BIMClashProperties
|
||||||
|
|
||||||
|
|
||||||
class Clash(bonsai.core.tool.Clash):
|
class Clash(bonsai.core.tool.Clash):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_clash_props(cls) -> BIMClashProperties:
|
||||||
|
return bpy.context.scene.BIMClashProperties
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def export_clash_sets(cls) -> list[ifcclash.ClashSet]:
|
def export_clash_sets(cls) -> list[ifcclash.ClashSet]:
|
||||||
clash_sets = []
|
clash_sets: list[ifcclash.ClashSet] = []
|
||||||
for clash_set in bpy.context.scene.BIMClashProperties.clash_sets:
|
props = cls.get_clash_props()
|
||||||
|
for clash_set in props.clash_sets:
|
||||||
a = []
|
a = []
|
||||||
b = []
|
b = []
|
||||||
for ab in ["a", "b"]:
|
for ab in ["a", "b"]:
|
||||||
@@ -46,7 +56,7 @@ class Clash(bonsai.core.tool.Clash):
|
|||||||
a.append(clash_source)
|
a.append(clash_source)
|
||||||
elif ab == "b":
|
elif ab == "b":
|
||||||
b.append(clash_source)
|
b.append(clash_source)
|
||||||
clash_set_data = {"name": clash_set.name, "mode": clash_set.mode, "a": a, "b": b}
|
clash_set_data = ifcclash.ClashSet(name=clash_set.name, mode=clash_set.mode, a=a, b=b)
|
||||||
if clash_set.mode == "intersection":
|
if clash_set.mode == "intersection":
|
||||||
clash_set_data["tolerance"] = clash_set.tolerance
|
clash_set_data["tolerance"] = clash_set.tolerance
|
||||||
clash_set_data["check_all"] = clash_set.check_all
|
clash_set_data["check_all"] = clash_set.check_all
|
||||||
@@ -55,33 +65,37 @@ class Clash(bonsai.core.tool.Clash):
|
|||||||
elif clash_set.mode == "clearance":
|
elif clash_set.mode == "clearance":
|
||||||
clash_set_data["clearance"] = clash_set.clearance
|
clash_set_data["clearance"] = clash_set.clearance
|
||||||
clash_set_data["check_all"] = clash_set.check_all
|
clash_set_data["check_all"] = clash_set.check_all
|
||||||
clash_sets.append(clash_set_data)
|
clash_sets.append(ifcclash.ClashSet(**clash_set_data))
|
||||||
return clash_sets
|
return clash_sets
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_clash(cls, clash_set, a_global_id, b_global_id):
|
def get_clash(
|
||||||
|
cls, clash_set: ifcclash.ClashSet, a_global_id: str, b_global_id: str
|
||||||
|
) -> Union[ifcclash.ClashResult, None]:
|
||||||
clashes = clash_set.get("clashes", None)
|
clashes = clash_set.get("clashes", None)
|
||||||
if not clashes:
|
if not clashes:
|
||||||
return
|
return
|
||||||
return clashes.get(f"{a_global_id}-{b_global_id}", None)
|
return clashes.get(f"{a_global_id}-{b_global_id}", None)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_clash_set(cls, name):
|
def get_clash_set(cls, name: str) -> Union[ifcclash.ClashSet, None]:
|
||||||
for clash_set in ClashStore.clash_sets:
|
for clash_set in ClashStore.clash_sets:
|
||||||
if clash_set["name"] == name:
|
if clash_set["name"] == name:
|
||||||
return clash_set
|
return clash_set
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_clash_sets(cls):
|
def get_clash_sets(cls) -> list[ifcclash.ClashSet]:
|
||||||
return ClashStore.clash_sets
|
return ClashStore.clash_sets
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def import_active_clashes(cls):
|
def import_active_clashes(cls) -> None:
|
||||||
clash_set = bpy.context.scene.BIMClashProperties.active_clash_set
|
props = cls.get_clash_props()
|
||||||
|
clash_set = props.active_clash_set
|
||||||
if not clash_set:
|
if not clash_set:
|
||||||
return
|
return
|
||||||
clash_set.clashes.clear()
|
clash_set.clashes.clear()
|
||||||
result = tool.Clash.get_clash_set(clash_set.name)
|
result = tool.Clash.get_clash_set(clash_set.name)
|
||||||
|
assert result is not None
|
||||||
for clash in sorted(result.get("clashes", {}).values(), key=lambda x: x["distance"]):
|
for clash in sorted(result.get("clashes", {}).values(), key=lambda x: x["distance"]):
|
||||||
blender_clash = clash_set.clashes.add()
|
blender_clash = clash_set.clashes.add()
|
||||||
blender_clash.a_global_id = clash["a_global_id"]
|
blender_clash.a_global_id = clash["a_global_id"]
|
||||||
@@ -91,17 +105,19 @@ class Clash(bonsai.core.tool.Clash):
|
|||||||
blender_clash.status = False if not "status" in clash.keys() else clash["status"]
|
blender_clash.status = False if not "status" in clash.keys() else clash["status"]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def load_clash_sets(cls, fn):
|
def load_clash_sets(cls, fn: str) -> None:
|
||||||
with open(fn) as f:
|
with open(fn) as f:
|
||||||
ClashStore.clash_sets = json.load(f)
|
ClashStore.clash_sets = json.load(f)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def look_at(cls, target, location):
|
def look_at(cls, target: Vector, location: Vector) -> None:
|
||||||
camera_location = location
|
camera_location = location
|
||||||
area = next(area for area in bpy.context.screen.areas if area.type == "VIEW_3D")
|
area = next(area for area in bpy.context.screen.areas if area.type == "VIEW_3D")
|
||||||
region = next(region for region in area.regions if region.type == "WINDOW")
|
region = next(region for region in area.regions if region.type == "WINDOW")
|
||||||
space = next(space for space in area.spaces if space.type == "VIEW_3D")
|
space = next(space for space in area.spaces if space.type == "VIEW_3D")
|
||||||
override = {"area": area, "region": region, "space_data": space}
|
override = {"area": area, "region": region, "space_data": space}
|
||||||
|
assert isinstance(space, bpy.types.SpaceView3D)
|
||||||
|
assert space.region_3d
|
||||||
space.region_3d.view_location = target
|
space.region_3d.view_location = target
|
||||||
space.region_3d.view_rotation = Vector((camera_location - target)).to_track_quat("Z", "Y")
|
space.region_3d.view_rotation = Vector((camera_location - target)).to_track_quat("Z", "Y")
|
||||||
space.region_3d.view_distance = (camera_location - target).length
|
space.region_3d.view_distance = (camera_location - target).length
|
||||||
@@ -109,10 +125,8 @@ class Clash(bonsai.core.tool.Clash):
|
|||||||
|
|
||||||
|
|
||||||
class ClashStore:
|
class ClashStore:
|
||||||
clash_sets = None
|
clash_sets: list[ifcclash.ClashSet] = []
|
||||||
path = None
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def purge():
|
def purge():
|
||||||
ClashStore.clash_sets = None
|
ClashStore.clash_sets = []
|
||||||
ClashStore.path = None
|
|
||||||
|
|||||||
@@ -43,22 +43,21 @@ class Search(bonsai.core.tool.Search):
|
|||||||
return json.loads(group.Description)["query"]
|
return json.loads(group.Description)["query"]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_filter_groups(cls, module: str) -> bpy.types.bpy_prop_collection_idprop[BIMFilterGroup]:
|
def get_filter_groups(cls, module: FilterModule) -> bpy.types.bpy_prop_collection_idprop[BIMFilterGroup]:
|
||||||
if module == "search":
|
if module == "search":
|
||||||
return cls.get_search_props().filter_groups
|
return cls.get_search_props().filter_groups
|
||||||
elif module == "csv":
|
elif module == "csv":
|
||||||
return bpy.context.scene.CsvProperties.filter_groups
|
return tool.Blender.get_csv_props().filter_groups
|
||||||
elif module == "diff":
|
elif module == "diff":
|
||||||
return bpy.context.scene.DiffProperties.filter_groups
|
return tool.Blender.get_diff_props().filter_groups
|
||||||
elif module == "drawing_include":
|
elif module == "drawing_include":
|
||||||
return bpy.context.scene.camera.data.BIMCameraProperties.include_filter_groups
|
return bpy.context.scene.camera.data.BIMCameraProperties.include_filter_groups
|
||||||
elif module == "drawing_exclude":
|
elif module == "drawing_exclude":
|
||||||
return bpy.context.scene.camera.data.BIMCameraProperties.exclude_filter_groups
|
return bpy.context.scene.camera.data.BIMCameraProperties.exclude_filter_groups
|
||||||
elif module.startswith("clash"):
|
elif module.startswith("clash"):
|
||||||
_, clash_set_index, ab, clash_source_index = module.split("_")
|
_, clash_set_index, ab, clash_source_index = module.split("_")
|
||||||
return getattr(bpy.context.scene.BIMClashProperties.clash_sets[int(clash_set_index)], ab)[
|
props = tool.Clash.get_clash_props()
|
||||||
int(clash_source_index)
|
return getattr(props.clash_sets[int(clash_set_index)], ab)[int(clash_source_index)].filter_groups
|
||||||
].filter_groups
|
|
||||||
assert False, f"Unsupported module: {module}"
|
assert False, f"Unsupported module: {module}"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import ifcopenshell.api.alignment
|
import ifcopenshell.api.alignment
|
||||||
|
import ifcopenshell.api.geometry
|
||||||
from ifcopenshell import entity_instance
|
from ifcopenshell import entity_instance
|
||||||
|
|
||||||
import math
|
import math
|
||||||
|
|||||||
Reference in New Issue
Block a user