This commit is contained in:
Andrej730
2025-06-20 14:27:31 +05:00
parent 5ec77b1b1c
commit 90ab846a18
17 changed files with 278 additions and 99 deletions
+3 -2
View File
@@ -29,6 +29,7 @@ import numpy.typing as npt
import multiprocessing
import ifcopenshell
import ifcopenshell.geom
import ifcopenshell.ifcopenshell_wrapper as W
import ifcopenshell.util.unit
import ifcopenshell.util.element
import ifcopenshell.util.geolocation
@@ -1032,9 +1033,9 @@ class IfcImporter:
def create_curve(
self,
element: ifcopenshell.entity_instance,
shape: Union[ifcopenshell.geom.ShapeElementType, ifcopenshell.geom.ShapeType],
shape: Union[W.Triangulation, W.TriangulationElement],
) -> bpy.types.Curve:
if isinstance(shape, ifcopenshell.geom.ShapeElementType):
if isinstance(shape, W.TriangulationElement):
geometry = shape.geometry
else:
geometry = shape
@@ -41,7 +41,7 @@ class SearchBSDDClassifications(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
total = core.search_bsdd_class(tool.Bsdd, context.scene.BIMBSDDProperties.keyword)
total = core.search_bsdd_class(tool.Bsdd, tool.Bsdd.get_bsdd_props().keyword)
self.report({"INFO"}, f"{total} bSDD classes found.")
return {"FINISHED"}
@@ -71,7 +71,7 @@ class SearchBSDDProperties(bpy.types.Operator):
def execute(self, context):
props = tool.Bsdd.get_bsdd_props()
core.search_bsdd_properties(tool.Bsdd, context.scene.BIMBSDDProperties.keyword, self.obj, self.obj_type)
core.search_bsdd_properties(tool.Bsdd, tool.Bsdd.get_bsdd_props().keyword, self.obj, self.obj_type)
self.report({"INFO"}, f"{len(props.properties)} bSDD properties found.")
return {"FINISHED"}
+50 -6
View File
@@ -31,7 +31,7 @@ from bpy.props import (
FloatVectorProperty,
CollectionProperty,
)
from typing import Union
from typing import Union, TYPE_CHECKING, Literal
def get_active_dictionary(self, context):
@@ -54,27 +54,41 @@ def update_active_class_index(self: "BIMBSDDProperties", context: bpy.types.Cont
class BSDDDictionary(PropertyGroup):
name: StringProperty(name="Name")
uri: StringProperty(name="URI")
default_language_code: StringProperty(name="Language")
organization_name_owner: StringProperty(name="Organization")
status: StringProperty(name="Status")
version: StringProperty(name="Version")
is_active: BoolProperty(
name="Is Active", description="Enable to search with this dictionary", default=False, update=update_is_active
name="Is Active",
description="Enable to search with this dictionary",
default=False,
update=update_is_active,
)
if TYPE_CHECKING:
uri: str
default_language_code: str
organization_name_owner: str
status: str
version: str
is_active: bool
class BSDDClassification(PropertyGroup):
name: StringProperty(name="Name")
reference_code: StringProperty(name="Reference Code")
uri: StringProperty(name="URI")
dictionary_name: StringProperty(name="Dictionary Name")
dictionary_namespace_uri: StringProperty(name="Dictionary Namespace URI")
if TYPE_CHECKING:
reference_code: str
uri: str
dictionary_name: str
dictionary_namespace_uri: str
class BSDDProperty(PropertyGroup):
name: StringProperty(name="Name")
code: StringProperty(name="Code")
uri: StringProperty(name="URI")
pset: StringProperty(name="Pset")
@@ -82,11 +96,19 @@ class BSDDProperty(PropertyGroup):
name="Is Selected", description="Select to add or edit this property", default=False, update=update_is_selected
)
if TYPE_CHECKING:
code: str
uri: str
pset: str
is_selected: bool
class BSDDPset(PropertyGroup):
name: StringProperty(name="Name")
properties: CollectionProperty(name="Properties", type=Attribute)
if TYPE_CHECKING:
properties: bpy.types.bpy_prop_collection_idprop[Attribute]
class BIMBSDDProperties(PropertyGroup):
active_dictionary: StringProperty(name="Active Dictionary")
@@ -131,6 +153,28 @@ class BIMBSDDProperties(PropertyGroup):
)
classification_psets: CollectionProperty(name="Classification Psets", type=BSDDPset)
if TYPE_CHECKING:
active_dictionary: str
active_dictionary: str
active_uri: str
dictionaries: bpy.types.bpy_prop_collection_idprop[BSDDDictionary]
active_dictionary_index: int
classifications: bpy.types.bpy_prop_collection_idprop[BSDDClassification]
active_classification_index: int
property_filter_mode: Literal["CLASS", "KEYWORD"]
classes: bpy.types.bpy_prop_collection_idprop[BSDDClassification]
active_class_index: int
properties: bpy.types.bpy_prop_collection_idprop[BSDDProperty]
active_property_index: int
selected_properties: bpy.types.bpy_prop_collection_idprop[Attribute]
keyword: str
should_filter_ifc_class: bool
use_only_ifc_properties: bool
load_preview_dictionaries: bool
load_inactive_dictionaries: bool
load_test_dictionaries: bool
classification_psets: bpy.types.bpy_prop_collection_idprop[BSDDPset]
@property
def active_class(self) -> Union[BSDDClassification, None]:
return tool.Blender.get_active_uilist_element(self.classes, self.active_class_index)
+48 -6
View File
@@ -16,9 +16,15 @@
# You should have received a copy of the GNU General Public License
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
import bpy
import bonsai.tool as tool
from bonsai.bim.module.bsdd.data import BSDDData
from bpy.types import Panel, UIList
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from bonsai.bim.module.bsdd.prop import BIMBSDDProperties, BSDDDictionary, BSDDClassification, BSDDProperty
class BIM_PT_bsdd(Panel):
@@ -33,7 +39,8 @@ class BIM_PT_bsdd(Panel):
def draw(self, context):
if not BSDDData.is_loaded:
BSDDData.load()
props = context.scene.BIMBSDDProperties
props = tool.Bsdd.get_bsdd_props()
assert self.layout
layout = self.layout
if len(props.dictionaries):
row = self.layout.row()
@@ -70,9 +77,17 @@ class BIM_PT_bsdd(Panel):
class BIM_UL_bsdd_dictionaries(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
def draw_item(
self,
context,
layout: bpy.types.UILayout,
data: BIMBSDDProperties,
item: BSDDDictionary,
icon,
active_data,
active_propname,
) -> None:
if item:
props = context.scene.BIMBSDDProperties
row = layout.row(align=True)
if item.status != "Active":
row.label(
@@ -86,7 +101,16 @@ class BIM_UL_bsdd_dictionaries(UIList):
class BIM_UL_bsdd_classifications(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
def draw_item(
self,
context,
layout: bpy.types.UILayout,
data: BIMBSDDProperties,
item: BSDDClassification,
icon,
active_data,
active_propname,
) -> None:
if item:
row = layout.row(align=True)
row.label(text=item.reference_code)
@@ -95,7 +119,16 @@ class BIM_UL_bsdd_classifications(UIList):
class BIM_UL_bsdd_classes(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
def draw_item(
self,
context,
layout: bpy.types.UILayout,
data: BIMBSDDProperties,
item: BSDDClassification,
icon,
active_data,
active_propname,
) -> None:
if item:
row = layout.row(align=True)
row.label(text=item.name)
@@ -103,7 +136,16 @@ class BIM_UL_bsdd_classes(UIList):
class BIM_UL_bsdd_properties(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
def draw_item(
self,
context,
layout: bpy.types.UILayout,
data: BIMBSDDProperties,
item: BSDDProperty,
icon,
active_data,
active_propname,
) -> None:
if item:
row = layout.row(align=True)
name = item.name
@@ -83,7 +83,7 @@ class ReferencesData:
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
props = tool.Classification.get_classification_props()
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
@@ -47,7 +47,7 @@ class AddClassification(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
props = context.scene.BIMClassificationProperties
props = tool.Classification.get_classification_props()
ifcopenshell.api.classification.add_classification(
tool.Ifc.get(),
classification=IfcStore.classification_file.by_id(int(props.available_classifications)),
@@ -60,7 +60,7 @@ class AddManualClassification(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
props = context.scene.BIMClassificationProperties
props = tool.Classification.get_classification_props()
attributes = bonsai.bim.helper.export_attributes(props.classification_attributes)
classification = ifcopenshell.api.classification.add_classification(tool.Ifc.get(), classification="Unnamed")
ifcopenshell.api.classification.edit_classification(
@@ -84,7 +84,7 @@ class AddManualClassificationReference(bpy.types.Operator, tool.Ifc.Operator):
objects = [context.active_object.name]
else:
objects = [self.obj]
props = context.scene.BIMClassificationReferenceProperties
props = tool.Classification.get_classification_reference_props()
attributes = bonsai.bim.helper.export_attributes(props.reference_attributes)
products = [
tool.Ifc.get().by_id(ifc_definition_id)
@@ -110,7 +110,7 @@ class AddClassificationFromBSDD(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
cprops = context.scene.BIMClassificationProperties
cprops = tool.Classification.get_classification_props()
bprops = tool.Bsdd.get_bsdd_props()
if cprops.classification_source == "BSDD":
dictionaries = [d.uri for d in bprops.dictionaries if d.is_active]
@@ -142,7 +142,7 @@ class EnableAddingManualClassification(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
props = context.scene.BIMClassificationProperties
props = tool.Classification.get_classification_props()
props.is_adding = True
props.active_classification_id = 0
props.classification_attributes.clear()
@@ -156,7 +156,7 @@ class DisableAddingManualClassification(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
props = context.scene.BIMClassificationProperties
props = tool.Classification.get_classification_props()
props.is_adding = False
return {"FINISHED"}
@@ -167,7 +167,7 @@ class EnableAddingManualClassificationReference(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
props = context.scene.BIMClassificationReferenceProperties
props = tool.Classification.get_classification_reference_props()
props.is_adding = True
props.reference_attributes.clear()
bonsai.bim.helper.import_attributes2("IfcClassificationReference", props.reference_attributes)
@@ -180,7 +180,7 @@ class DisableAddingManualClassificationReference(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
props = context.scene.BIMClassificationReferenceProperties
props = tool.Classification.get_classification_reference_props()
props.is_adding = False
return {"FINISHED"}
@@ -203,7 +203,7 @@ class EnableEditingClassification(bpy.types.Operator):
new.string_value = "" if new.is_null else json.dumps(data[name])
return True
props = context.scene.BIMClassificationProperties
props = tool.Classification.get_classification_props()
props.classification_attributes.clear()
bonsai.bim.helper.import_attributes2(
tool.Ifc.get().by_id(self.classification), props.classification_attributes, callback
@@ -218,7 +218,8 @@ class DisableEditingClassification(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
context.scene.BIMClassificationProperties.active_classification_id = 0
props = tool.Classification.get_classification_props()
props.active_classification_id = 0
return {"FINISHED"}
@@ -246,7 +247,7 @@ class EditClassification(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"}
def _execute(self, context):
props = context.scene.BIMClassificationProperties
props = tool.Classification.get_classification_props()
def callback(attributes, prop):
if prop.name == "ReferenceTokens":
@@ -271,7 +272,7 @@ class EnableEditingClassificationReference(bpy.types.Operator):
obj: bpy.props.StringProperty()
def execute(self, context):
props = context.scene.BIMClassificationReferenceProperties
props = tool.Classification.get_classification_reference_props()
props.reference_attributes.clear()
bonsai.bim.helper.import_attributes2(tool.Ifc.get().by_id(self.reference), props.reference_attributes)
props.active_reference_id = self.reference
@@ -285,7 +286,7 @@ class DisableEditingClassificationReference(bpy.types.Operator):
obj: bpy.props.StringProperty()
def execute(self, context):
context.scene.BIMClassificationReferenceProperties.active_reference_id = 0
tool.Classification.get_classification_reference_props().active_reference_id = 0
return {"FINISHED"}
@@ -337,7 +338,7 @@ class EditClassificationReference(bpy.types.Operator, tool.Ifc.Operator):
obj: bpy.props.StringProperty()
def _execute(self, context):
props = context.scene.BIMClassificationReferenceProperties
props = tool.Classification.get_classification_reference_props()
attributes = bonsai.bim.helper.export_attributes(props.reference_attributes)
ifc_file = tool.Ifc.get()
ifcopenshell.api.classification.edit_reference(
@@ -364,7 +365,7 @@ class AddClassificationReference(bpy.types.Operator, tool.Ifc.Operator):
objects = [context.active_object.name]
else:
objects = [self.obj]
props = context.scene.BIMClassificationProperties
props = tool.Classification.get_classification_props()
classification = None
classification_name = IfcStore.classification_file.by_id(int(props.available_classifications)).Name
for element in tool.Ifc.get().by_type("IfcClassification"):
@@ -405,8 +406,7 @@ class AddClassificationReferenceFromBSDD(bpy.types.Operator, tool.Ifc.Operator):
objects = [context.active_object.name]
else:
objects = [self.obj]
props = context.scene.BIMClassificationProperties
bprops = context.scene.BIMBSDDProperties
bprops = tool.Bsdd.get_bsdd_props()
use_only_ifc_properties: bool = bprops.use_only_ifc_properties
bsdd_classification = bprops.classifications[bprops.active_classification_index]
@@ -487,7 +487,7 @@ class ChangeClassificationLevel(bpy.types.Operator):
parent_id: bpy.props.IntProperty()
def execute(self, context):
props = context.scene.BIMClassificationProperties
props = tool.Classification.get_classification_props()
props.available_library_references.clear()
for reference in IfcStore.classification_file.by_id(self.parent_id).HasReferences:
new = props.available_library_references.add()
@@ -509,6 +509,6 @@ class DisableEditingClassificationReferences(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
props = context.scene.BIMClassificationProperties
props = tool.Classification.get_classification_props()
props.available_library_references.clear()
return {"FINISHED"}
@@ -17,6 +17,7 @@
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
import bpy
import bonsai.tool as tool
from bonsai.bim.prop import Attribute
from bonsai.bim.module.classification.data import ClassificationsData, ClassificationReferencesData
from bpy.types import PropertyGroup
@@ -30,9 +31,12 @@ from bpy.props import (
FloatVectorProperty,
CollectionProperty,
)
from typing import TYPE_CHECKING
def get_available_classifications(self, context):
def get_available_classifications(
self: "BIMClassificationProperties", context: bpy.types.Context
) -> tool.Blender.BLENDER_ENUM_ITEMS:
if not ClassificationsData.is_loaded:
ClassificationsData.load()
return ClassificationsData.data["available_classifications"]
@@ -44,19 +48,26 @@ def get_classifications(self, context):
return ClassificationReferencesData.data["classifications"]
def get_classification_source(self, context):
def get_classification_source(
self: "BIMClassificationProperties", context: bpy.types.Context
) -> tool.Blender.BLENDER_ENUM_ITEMS:
if not ClassificationsData.is_loaded:
ClassificationsData.load()
return ClassificationsData.data["classification_source"]
class ClassificationReference(PropertyGroup):
name: StringProperty(name="Name")
identification: StringProperty(name="Identification")
ifc_definition_id: IntProperty(name="IFC Definition ID")
has_references: BoolProperty(name="Has References")
referenced_source: IntProperty(name="IFC Definition ID")
if TYPE_CHECKING:
identification: str
ifc_definition_id: int
has_references: bool
referenced_source: int
class BIMClassificationProperties(PropertyGroup):
is_adding: BoolProperty(name="Is Adding", default=False)
@@ -68,9 +79,25 @@ class BIMClassificationProperties(PropertyGroup):
active_library_referenced_source: IntProperty(name="Active Library Referenced Source")
active_library_reference_index: IntProperty(name="Active Library Reference Index")
if TYPE_CHECKING:
is_adding: bool
classification_source: str
available_classifications: str
classification_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
active_classification_id: int
available_library_references: bpy.types.bpy_prop_collection_idprop[ClassificationReference]
active_library_referenced_source: int
active_library_reference_index: int
class BIMClassificationReferenceProperties(PropertyGroup):
is_adding: BoolProperty(name="Is Adding", default=False)
classifications: EnumProperty(items=get_classifications, name="Classifications")
reference_attributes: CollectionProperty(name="Reference Attributes", type=Attribute)
active_reference_id: IntProperty(name="Active Reference Id")
if TYPE_CHECKING:
is_adding: bool
classifications: str
reference_attributes: bpy.types.bpy_prop_collection_idprop
active_reference_id: int
@@ -16,6 +16,7 @@
# You should have received a copy of the GNU General Public License
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
import bpy
import bonsai.bim.helper
import bonsai.tool as tool
@@ -27,6 +28,10 @@ from bonsai.bim.module.classification.data import (
MaterialClassificationsData,
CostClassificationsData,
)
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from bonsai.bim.module.classification.prop import BIMClassificationProperties, ClassificationReference
class BIM_PT_classifications(Panel):
@@ -46,7 +51,8 @@ class BIM_PT_classifications(Panel):
if not ClassificationsData.is_loaded:
ClassificationsData.load()
self.props = context.scene.BIMClassificationProperties
self.props = tool.Classification.get_classification_props()
assert self.layout
row = self.layout.row(align=True)
row.label(text="Source", icon="OUTLINER")
@@ -110,9 +116,9 @@ class ReferenceUI:
def draw_ui(self, context):
obj = context.active_object
self.sprops = context.scene.BIMClassificationProperties
self.bprops = context.scene.BIMBSDDProperties
self.props = context.scene.BIMClassificationReferenceProperties
self.sprops = tool.Classification.get_classification_props()
self.bprops = tool.Bsdd.get_bsdd_props()
self.props = tool.Classification.get_classification_reference_props()
self.file = tool.Ifc.get()
self.draw_add_ui(context)
@@ -315,7 +321,16 @@ class BIM_PT_cost_classifications(Panel, ReferenceUI):
class BIM_UL_classifications(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
def draw_item(
self,
context,
layout: bpy.types.UILayout,
data: BIMClassificationProperties,
item: ClassificationReference,
icon,
active_data,
active_propname,
):
if item:
if item.has_references:
op = layout.operator("bim.change_classification_level", text="", icon="DISCLOSURE_TRI_RIGHT")
+1 -1
View File
@@ -255,7 +255,7 @@ class BIM_PT_object_psets(Panel):
ObjectPsetsData.load()
props = context.active_object.PsetProperties
self.bprops = context.scene.BIMBSDDProperties
self.bprops = tool.Bsdd.get_bsdd_props()
row = self.layout.row(align=True)
prop_with_search(row, props, "pset_name", text="")
if props.pset_name != "BBIM_BSDD" and not props.pset_name.startswith(tool.Bsdd.identifier_url):
+4 -4
View File
@@ -26,20 +26,20 @@ if TYPE_CHECKING:
import bonsai.tool as tool
def import_bsdd_classes(bsdd: tool.Bsdd, obj, obj_type) -> int:
def import_bsdd_classes(bsdd: type[tool.Bsdd], obj, obj_type) -> int:
return bsdd.import_classes(obj, obj_type)
def search_bsdd_properties(bsdd: tool.Bsdd, keyword: str, obj, obj_type) -> int:
def search_bsdd_properties(bsdd: type[tool.Bsdd], keyword: str, obj, obj_type) -> int:
return bsdd.import_properties(obj, obj_type, keyword)
def load_bsdd(bsdd: tool.Bsdd) -> None:
def load_bsdd(bsdd: type[tool.Bsdd]) -> None:
bsdd.clear_dictionaries()
bsdd.create_dictionaries(bsdd.get_dictionaries())
def search_bsdd_class(bsdd: tool.Bsdd, keyword: str) -> int:
def search_bsdd_class(bsdd: type[tool.Bsdd], keyword: str) -> int:
bsdd.clear_classes()
related_entities = bsdd.get_related_ifc_entities()
return bsdd.search_class(keyword, related_entities)
+30 -16
View File
@@ -32,7 +32,11 @@ if TYPE_CHECKING:
def unjoin_walls(
ifc: tool.Ifc, blender: tool.Blender, geometry: tool.Geometry, joiner: DumbWallJoiner, model: tool.Model
ifc: type[tool.Ifc],
blender: type[tool.Blender],
geometry: type[tool.Geometry],
joiner: DumbWallJoiner,
model: type[tool.Model],
) -> None:
"""Unjoin selected walls."""
for obj in blender.get_selected_objects():
@@ -45,11 +49,11 @@ def unjoin_walls(
def extend_walls(
ifc: tool.Ifc,
blender: tool.Blender,
geometry: tool.Geometry,
ifc: type[tool.Ifc],
blender: type[tool.Blender],
geometry: type[tool.Geometry],
joiner: DumbWallJoiner,
model: tool.Model,
model: type[tool.Model],
target: Vector,
connection: Optional[str] = None,
) -> None:
@@ -62,11 +66,11 @@ def extend_walls(
def join_walls_LV(
ifc: tool.Ifc,
blender: tool.Blender,
geometry: tool.Geometry,
ifc: type[tool.Ifc],
blender: type[tool.Blender],
geometry: type[tool.Geometry],
joiner: DumbWallJoiner,
model: tool.Model,
model: type[tool.Model],
join_type: Literal["L", "V"] = "L",
) -> None:
selected_objs = [
@@ -86,7 +90,7 @@ def join_walls_LV(
joiner.connect(another_selected_object, active_obj)
def offset_walls(ifc: tool.Ifc, blender: tool.Blender, model: tool.Model, offset_type: OffsetType):
def offset_walls(ifc: type[tool.Ifc], blender: type[tool.Blender], model: type[tool.Model], offset_type: OffsetType):
objs = [
obj
for obj in blender.get_selected_objects()
@@ -98,7 +102,11 @@ def offset_walls(ifc: tool.Ifc, blender: tool.Blender, model: tool.Model, offset
def align_walls(
ifc: tool.Ifc, blender: tool.Blender, model: tool.Model, aligner: DumbWallAligner, align_type: AlignType
ifc: type[tool.Ifc],
blender: type[tool.Blender],
model: type[tool.Model],
aligner: DumbWallAligner,
align_type: AlignType,
):
reference_obj = blender.get_active_object(is_selected=True)
if not (e := ifc.get_entity(reference_obj) or not model.get_usage_type(e) == "LAYER2"):
@@ -122,7 +130,9 @@ def align_walls(
aligner.align_last_layer(obj)
def align_objects(blender: tool.Blender, model: tool.Model, align_type: Literal["CENTER", "POSITIVE", "NEGATIVE"]):
def align_objects(
blender: type[tool.Blender], model: type[tool.Model], align_type: Literal["CENTER", "POSITIVE", "NEGATIVE"]
):
reference_obj = blender.get_active_object(is_selected=True)
objs = [o for o in blender.get_selected_objects() if o != reference_obj]
if not reference_obj or not objs:
@@ -131,9 +141,9 @@ def align_objects(blender: tool.Blender, model: tool.Model, align_type: Literal[
def extend_wall_to_slab(
ifc: tool.Ifc,
geometry: tool.Geometry,
model: tool.Model,
ifc: type[tool.Ifc],
geometry: type[tool.Geometry],
model: type[tool.Model],
slab_obj: bpy.types.Object,
wall_objs: list[bpy.types.Object],
) -> None:
@@ -150,7 +160,11 @@ def extend_wall_to_slab(
def join_walls_TZ(
ifc: tool.Ifc, blender: tool.Blender, geometry: tool.Geometry, joiner: DumbWallJoiner, model: tool.Model
ifc: type[tool.Ifc],
blender: type[tool.Blender],
geometry: type[tool.Geometry],
joiner: DumbWallJoiner,
model: type[tool.Model],
) -> None:
selected_objs = [
o
+38 -15
View File
@@ -1,3 +1,22 @@
# Bonsai - OpenBIM Blender Add-on
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# This file is part of Bonsai.
#
# Bonsai is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Bonsai 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 General Public License
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
import bonsai.core.tool
import bonsai.tool as tool
import bpy
@@ -8,6 +27,9 @@ import ifcopenshell.util.element
import ifcopenshell.util.classification
from typing import Any, Union, Optional, TYPE_CHECKING
if TYPE_CHECKING:
from bonsai.bim.module.bsdd.prop import BIMBSDDProperties
class Bsdd(bonsai.core.tool.Bsdd):
identifier_url = "https://identifier.buildingsmart.org"
@@ -16,28 +38,29 @@ class Bsdd(bonsai.core.tool.Bsdd):
bsdd_properties: dict[str, dict] = {}
@classmethod
def get_bsdd_props(cls):
return bpy.context.scene.BIMBSDDProperties
def get_bsdd_props(cls) -> BIMBSDDProperties:
assert (scene := bpy.context.scene)
return scene.BIMBSDDProperties # pyright: ignore[reportAttributeAccessIssue]
@classmethod
def clear_class_psets(cls) -> None:
bpy.context.scene.BIMBSDDProperties.classification_psets.clear()
cls.get_bsdd_props().classification_psets.clear()
@classmethod
def clear_classes(cls) -> None:
bpy.context.scene.BIMBSDDProperties.classifications.clear()
cls.get_bsdd_props().classifications.clear()
@classmethod
def clear_properties(cls) -> None:
bpy.context.scene.BIMBSDDProperties.properties.clear()
cls.get_bsdd_props().properties.clear()
@classmethod
def clear_dictionaries(cls) -> None:
bpy.context.scene.BIMBSDDProperties.dictionaries.clear()
cls.get_bsdd_props().dictionaries.clear()
@classmethod
def create_class_psets(cls, pset_dict: dict[str, dict[str, Any]]) -> None:
props = bpy.context.scene.BIMBSDDProperties
props = cls.get_bsdd_props()
data_type_map = {
"String": "string",
"Real": "float",
@@ -60,7 +83,7 @@ class Bsdd(bonsai.core.tool.Bsdd):
@classmethod
def create_dictionaries(cls, dictionaries: list[bsdd.DictionaryContractV1]) -> None:
props = bpy.context.scene.BIMBSDDProperties
props = cls.get_bsdd_props()
for dictionary in sorted(dictionaries, key=lambda d: d["name"]):
new = props.dictionaries.add()
new.name = dictionary["name"]
@@ -72,7 +95,7 @@ class Bsdd(bonsai.core.tool.Bsdd):
@classmethod
def get_active_class_data(cls) -> Union[bsdd.ClassContractV1, dict]:
prop = bpy.context.scene.BIMBSDDProperties
prop = cls.get_bsdd_props()
bsdd_classification = prop.classifications[prop.active_classification_index]
if not bsdd_classification:
return {}
@@ -80,18 +103,18 @@ class Bsdd(bonsai.core.tool.Bsdd):
@classmethod
def get_active_dictionary_uri(cls) -> str:
return bpy.context.scene.BIMBSDDProperties.active_uri
return cls.get_bsdd_props().active_uri
@classmethod
def get_dictionary(cls, uri: str) -> bsdd.DictionaryContractV1:
props = bpy.context.scene.BIMBSDDProperties
props = cls.get_bsdd_props()
response = cls.client.get_dictionary(dictionary_uri=uri, include_test_dictionaries=props.load_test_dictionaries)
if dicts := response.get("dictionaries"):
return dicts[0]
@classmethod
def get_dictionaries(cls) -> list[bsdd.DictionaryContractV1]:
props = bpy.context.scene.BIMBSDDProperties
props = cls.get_bsdd_props()
response = cls.client.get_dictionary(include_test_dictionaries=props.load_test_dictionaries)
dicts = response.get("dictionaries") or []
statuses = ["Active"]
@@ -165,7 +188,7 @@ class Bsdd(bonsai.core.tool.Bsdd):
limit: int = 100,
should_paginate: bool = True,
):
cprops = bpy.context.scene.BIMClassificationProperties
cprops = tool.Classification.get_classification_props()
bprops = cls.get_bsdd_props()
dictionary_uris = (
[d.uri for d in bprops.dictionaries if d.is_active]
@@ -202,13 +225,13 @@ class Bsdd(bonsai.core.tool.Bsdd):
@classmethod
def set_active_bsdd(cls, name: str, uri: str) -> None:
props = bpy.context.scene.BIMBSDDProperties
props = cls.get_bsdd_props()
props.active_dictionary = name
props.active_uri = uri
@classmethod
def should_filter_ifc_class(cls) -> bool:
return bpy.context.scene.BIMBSDDProperties.should_filter_ifc_class
return cls.get_bsdd_props().should_filter_ifc_class
@classmethod
def get_bsdd_class(cls, uri: str) -> dict:
+14 -1
View File
@@ -22,10 +22,23 @@ import ifcopenshell.api
import ifcopenshell.util.classification
import bonsai.core.tool
import bonsai.tool as tool
from typing import Union, assert_never
from typing import Union, assert_never, TYPE_CHECKING
if TYPE_CHECKING:
from bonsai.bim.module.classification.prop import BIMClassificationReferenceProperties, BIMClassificationProperties
class Classification(bonsai.core.tool.Classification):
@classmethod
def get_classification_props(cls) -> BIMClassificationProperties:
assert (scene := bpy.context.scene)
return scene.BIMClassificationProperties # pyright: ignore[reportAttributeAccessIssue]
@classmethod
def get_classification_reference_props(cls) -> BIMClassificationReferenceProperties:
assert (scene := bpy.context.scene)
return scene.BIMClassificationReferenceProperties # pyright: ignore[reportAttributeAccessIssue]
@classmethod
def get_location(cls, classification: ifcopenshell.entity_instance) -> Union[str, None]:
schema = classification.file.schema
+6 -6
View File
@@ -46,8 +46,8 @@ class TestAddClassificationReferenceFromBSDD(NewFile):
bpy.ops.bim.load_bsdd_domains()
uri = "https://identifier.buildingsmart.org/uri/molio/cciconstruction/1.0"
bpy.ops.bim.set_active_bsdd_domain(name="CCI Construction", uri=uri)
props = context.scene.BIMBSDDProperties
bpy.context.scene.BIMClassificationProperties.classification_source = "BSDD"
props = tool.Bsdd.get_bsdd_props()
tool.Classification.get_classification_props().classification_source = "BSDD"
props.should_filter_ifc_class = True
props.keyword = "Room"
bpy.ops.bim.search_bsdd_classifications()
@@ -70,8 +70,8 @@ class TestAddClassificationReferenceFromBSDD(NewFile):
bpy.ops.bim.load_bsdd_domains()
uri = "https://identifier.buildingsmart.org/uri/molio/cciconstruction/1.0"
bpy.ops.bim.set_active_bsdd_domain(name="CCI Construction", uri=uri)
props = context.scene.BIMBSDDProperties
bpy.context.scene.BIMClassificationProperties.classification_source = "BSDD"
props = tool.Bsdd.get_bsdd_props()
tool.Classification.get_classification_props().classification_source = "BSDD"
props.should_filter_ifc_class = True
props.keyword = "Room"
bpy.ops.bim.search_bsdd_classifications()
@@ -103,12 +103,12 @@ class TestAddClassificationReferenceFromBSDD(NewFile):
element = tool.Ifc.get_entity(obj)
assert element
props = context.scene.BIMBSDDProperties
props = tool.Bsdd.get_bsdd_props()
props.load_preview_domains = True
bpy.ops.bim.load_bsdd_domains()
uri = "https://identifier.buildingsmart.org/uri/ifcairport/ifcairport/0.9"
bpy.ops.bim.set_active_bsdd_domain(name="IFC Airport", uri=uri)
bpy.context.scene.BIMClassificationProperties.classification_source = "BSDD"
tool.Classification.get_classification_props().classification_source = "BSDD"
props.should_filter_ifc_class = False # Important due to class mismatch.
props.keyword = "check-in conveyor"
bpy.ops.bim.search_bsdd_classifications()
+1 -1
View File
@@ -540,7 +540,7 @@ class TestSetupActiveBsddClassification(NewFile):
filepath = "test/files/temp/test.ifc"
ifc_file.write(filepath)
bpy.ops.bim.load_project(filepath=filepath)
props = bpy.context.scene.BIMBSDDProperties
props = tool.Bsdd.get_bsdd_props()
assert props.active_domain == name
assert props.active_uri == base_uri
@@ -351,19 +351,18 @@ def log_internal_cpp_errors(f: ifcopenshell.file, filename: str, logger: Logger)
logger.error(m)
entity_attribute_map: dict[tuple[str, str], tuple[entity_type, tuple[attribute]]] = {}
entity_attribute_map: dict[tuple[str, str], tuple[entity_type, tuple[attribute, ...]]] = {}
def get_entity_attributes(schema: schema_definition, entity: str) -> tuple[entity_type, tuple[attribute]]:
def get_entity_attributes(schema: schema_definition, entity: str) -> tuple[entity_type, tuple[attribute, ...]]:
cache_key = schema.name(), entity
from_cache = entity_attribute_map.get(cache_key)
if from_cache:
return from_cache
entity_attrs = (
ent := schema.declaration_by_name(entity),
ent.all_attributes(),
)
ent = schema.declaration_by_name(entity).as_entity()
assert ent
entity_attrs = (ent, ent.all_attributes())
entity_attribute_map[cache_key] = entity_attrs
return entity_attrs
+6 -5
View File
@@ -17,12 +17,14 @@
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
import ifcopenshell.guid
import ifcopenshell.util.element
import ifcpatch
import logging
from typing import Union
class Patcher:
def __init__(self, file, logger):
class Patcher(ifcpatch.BasePatcher):
def __init__(self, file: ifcopenshell.file, logger: Union[logging.Logger, None] = None):
"""Merge identical styles together
Some software may create an obscene number of styles instead of reusing
@@ -35,8 +37,7 @@ class Patcher:
ifcpatch.execute({"file": model, "recipe": "MergeStyles", "arguments": []})
"""
self.file = file
self.logger = logger
super().__init__(file, logger)
def patch(self):
for ifc_class in ("IfcColourRgb", "IfcSurfaceStyleShading", "IfcPresentationStyle"):