mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-12 22:43:41 +00:00
typing
This commit is contained in:
@@ -982,14 +982,17 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
self.set_boundary_name(parent_boundary)
|
self.set_boundary_name(parent_boundary)
|
||||||
return parent_boundary
|
return parent_boundary
|
||||||
|
|
||||||
def get_face_matrix(self, p1, p2, p3):
|
def get_face_matrix(self, p1: Vector, p2: Vector, p3: Vector) -> Matrix:
|
||||||
edge1 = p2 - p1
|
edge1 = p2 - p1
|
||||||
edge2 = p3 - p1
|
edge2 = p3 - p1
|
||||||
normal = edge1.cross(edge2)
|
normal = edge1.cross(edge2)
|
||||||
|
assert isinstance(normal, Vector)
|
||||||
|
|
||||||
z_axis = normal.normalized()
|
z_axis = normal.normalized()
|
||||||
x_axis = p2 - p1
|
x_axis = p2 - p1
|
||||||
x_axis.normalize()
|
x_axis.normalize()
|
||||||
y_axis = z_axis.cross(x_axis)
|
y_axis = z_axis.cross(x_axis)
|
||||||
|
assert isinstance(y_axis, Vector)
|
||||||
|
|
||||||
mat = Matrix()
|
mat = Matrix()
|
||||||
mat.col[0][:3] = x_axis
|
mat.col[0][:3] = x_axis
|
||||||
@@ -1059,7 +1062,7 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
|
|
||||||
return surface
|
return surface
|
||||||
|
|
||||||
def set_boundary_name(self, boundary):
|
def set_boundary_name(self, boundary: ifcopenshell.entity_instance):
|
||||||
"""
|
"""
|
||||||
By convention 1stLevel and 2ndLevel boundary have specific name and description
|
By convention 1stLevel and 2ndLevel boundary have specific name and description
|
||||||
See https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcRelSpaceBoundary.htm
|
See https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcRelSpaceBoundary.htm
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import bonsai.tool as tool
|
|||||||
from bpy_extras.io_utils import ImportHelper, ExportHelper
|
from bpy_extras.io_utils import ImportHelper, ExportHelper
|
||||||
import bonsai.tool as tool
|
import bonsai.tool as tool
|
||||||
import bonsai.core.cost as core
|
import bonsai.core.cost as core
|
||||||
from typing import get_args, TYPE_CHECKING
|
from typing import get_args, TYPE_CHECKING, Literal
|
||||||
|
|
||||||
|
|
||||||
class AddCostSchedule(bpy.types.Operator, tool.Ifc.Operator):
|
class AddCostSchedule(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
@@ -695,6 +695,9 @@ class ExportCostSchedules(bpy.types.Operator, ExportHelper):
|
|||||||
default=True,
|
default=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
format: Literal["CSV", "XLSX", "ODS"]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def filename_ext(self) -> str:
|
def filename_ext(self) -> str:
|
||||||
return f".{self.format.lower()}"
|
return f".{self.format.lower()}"
|
||||||
|
|||||||
@@ -24,11 +24,13 @@ import ifcopenshell.util.representation
|
|||||||
import ifcopenshell.util.type
|
import ifcopenshell.util.type
|
||||||
import ifcopenshell.util.unit
|
import ifcopenshell.util.unit
|
||||||
import ifcopenshell.api
|
import ifcopenshell.api
|
||||||
|
import ifcopenshell.api.type
|
||||||
import bonsai.bim.helper
|
import bonsai.bim.helper
|
||||||
import bonsai.tool as tool
|
import bonsai.tool as tool
|
||||||
import bonsai.core.geometry
|
import bonsai.core.geometry
|
||||||
import bonsai.core.type as core
|
import bonsai.core.type as core
|
||||||
import bonsai.core.root
|
import bonsai.core.root
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
|
||||||
class AssignType(bpy.types.Operator, tool.Ifc.Operator):
|
class AssignType(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
@@ -38,15 +40,18 @@ class AssignType(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
relating_type: bpy.props.IntProperty()
|
relating_type: bpy.props.IntProperty()
|
||||||
related_object: bpy.props.StringProperty()
|
related_object: bpy.props.StringProperty()
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
relating_type: int
|
||||||
|
related_object: str
|
||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
relating_type = tool.Ifc.get().by_id(
|
relating_type = tool.Ifc.get().by_id(
|
||||||
self.relating_type or int(context.active_object.BIMTypeProperties.relating_type)
|
self.relating_type or int(context.active_object.BIMTypeProperties.relating_type)
|
||||||
)
|
)
|
||||||
related_objects = (
|
if self.related_object:
|
||||||
[bpy.data.objects.get(self.related_object)]
|
related_objects = [bpy.data.objects[self.related_object]]
|
||||||
if self.related_object
|
else:
|
||||||
else context.selected_objects or [context.active_object]
|
related_objects = tool.Blender.get_selected_objects()
|
||||||
)
|
|
||||||
model_props = tool.Model.get_model_props()
|
model_props = tool.Model.get_model_props()
|
||||||
for obj in related_objects:
|
for obj in related_objects:
|
||||||
element = tool.Ifc.get_entity(obj)
|
element = tool.Ifc.get_entity(obj)
|
||||||
@@ -63,17 +68,24 @@ class UnassignType(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
related_object: bpy.props.StringProperty()
|
related_object: bpy.props.StringProperty()
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
related_object: str
|
||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
def exclude_callback(attribute):
|
def exclude_callback(attribute):
|
||||||
return attribute.is_a("IfcProfileDef") and attribute.ProfileName
|
return attribute.is_a("IfcProfileDef") and attribute.ProfileName
|
||||||
|
|
||||||
self.file = tool.Ifc.get()
|
self.file = tool.Ifc.get()
|
||||||
objs = [bpy.data.objects.get(self.related_object)] if self.related_object else context.selected_objects
|
if self.related_object:
|
||||||
for obj in objs:
|
related_objects = [bpy.data.objects[self.related_object]]
|
||||||
|
else:
|
||||||
|
related_objects = tool.Blender.get_selected_objects()
|
||||||
|
|
||||||
|
for obj in related_objects:
|
||||||
element = tool.Ifc.get_entity(obj)
|
element = tool.Ifc.get_entity(obj)
|
||||||
if not element or not element.is_a("IfcObject"):
|
if not element or not element.is_a("IfcObject"):
|
||||||
continue
|
continue
|
||||||
ifcopenshell.api.run("type.unassign_type", self.file, related_objects=[element])
|
ifcopenshell.api.type.unassign_type(self.file, related_objects=[element])
|
||||||
|
|
||||||
if element.Representation:
|
if element.Representation:
|
||||||
new_active_representation = None
|
new_active_representation = None
|
||||||
|
|||||||
@@ -337,7 +337,10 @@ def calculate_cost_item_resource_value(ifc: tool.Ifc, cost_item: ifcopenshell.en
|
|||||||
|
|
||||||
|
|
||||||
def export_cost_schedules(
|
def export_cost_schedules(
|
||||||
cost: tool.Cost, filepath: str, format: str, cost_schedule: Union[ifcopenshell.entity_instance, None] = None
|
cost: tool.Cost,
|
||||||
|
filepath: str,
|
||||||
|
format: Literal["CSV", "ODS", "XLSX"],
|
||||||
|
cost_schedule: Union[ifcopenshell.entity_instance, None] = None,
|
||||||
) -> Union[str, None]:
|
) -> Union[str, None]:
|
||||||
cost.play_sound()
|
cost.play_sound()
|
||||||
return cost.export_cost_schedules(filepath, format, cost_schedule)
|
return cost.export_cost_schedules(filepath, format, cost_schedule)
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import bonsai.bim.helper
|
|||||||
import json
|
import json
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional, Any, Generator, Union, Literal, TYPE_CHECKING
|
from typing import Optional, Any, Generator, Union, Literal, TYPE_CHECKING
|
||||||
|
from typing_extensions import assert_never
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from bonsai.bim.module.cost.prop import BIMCostProperties, CostItemQuantity
|
from bonsai.bim.module.cost.prop import BIMCostProperties, CostItemQuantity
|
||||||
@@ -652,7 +653,10 @@ class Cost(bonsai.core.tool.Cost):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def export_cost_schedules(
|
def export_cost_schedules(
|
||||||
cls, filepath: str, format: str, cost_schedule: Optional[ifcopenshell.entity_instance] = None
|
cls,
|
||||||
|
filepath: str,
|
||||||
|
format: Literal["CSV", "ODS", "XLSX"],
|
||||||
|
cost_schedule: Optional[ifcopenshell.entity_instance] = None,
|
||||||
) -> Union[str, None]:
|
) -> Union[str, None]:
|
||||||
import subprocess
|
import subprocess
|
||||||
import os
|
import os
|
||||||
@@ -680,6 +684,8 @@ class Cost(bonsai.core.tool.Cost):
|
|||||||
|
|
||||||
writer = Ifc5DXlsxWriter(file=tool.Ifc.get(), output=path, cost_schedule=cost_schedule)
|
writer = Ifc5DXlsxWriter(file=tool.Ifc.get(), output=path, cost_schedule=cost_schedule)
|
||||||
writer.write()
|
writer.write()
|
||||||
|
else:
|
||||||
|
assert_never(format)
|
||||||
try:
|
try:
|
||||||
if path:
|
if path:
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
|
|||||||
@@ -92,18 +92,14 @@ def assign_type(
|
|||||||
ambiguous, unknown or are so bespoke as to have no logical type.
|
ambiguous, unknown or are so bespoke as to have no logical type.
|
||||||
|
|
||||||
:param related_objects: The IfcElement occurrences.
|
:param related_objects: The IfcElement occurrences.
|
||||||
:type related_objects: list[ifcopenshell.entity_instance]
|
|
||||||
:param relating_type: The IfcElementType type.
|
:param relating_type: The IfcElementType type.
|
||||||
:type relating_type: ifcopenshell.entity_instance
|
|
||||||
:param should_map_representations: If a type has a representation map,
|
:param should_map_representations: If a type has a representation map,
|
||||||
IFC requires all occurrences to map those representations. Some IFC
|
IFC requires all occurrences to map those representations. Some IFC
|
||||||
vendors might disobey this, or you might want to handle it
|
vendors might disobey this, or you might want to handle it
|
||||||
yourusecase. In this scenario, you may set this to False.
|
yourusecase. In this scenario, you may set this to False.
|
||||||
This also enabled adding material usages mapping.
|
This also enabled adding material usages mapping.
|
||||||
:type should_map_representations: bool
|
|
||||||
:return: The IfcRelDefinesByType relationship
|
:return: The IfcRelDefinesByType relationship
|
||||||
or `None` if `related_objects` was empty list.
|
or `None` if `related_objects` was empty list.
|
||||||
:rtype: Union[ifcopenshell.entity_instance, None]
|
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
@@ -177,29 +173,26 @@ def assign_type(
|
|||||||
"""
|
"""
|
||||||
usecase = Usecase()
|
usecase = Usecase()
|
||||||
usecase.file = file
|
usecase.file = file
|
||||||
usecase.settings = {
|
return usecase.execute(related_objects, relating_type, should_map_representations)
|
||||||
"related_objects": related_objects,
|
|
||||||
"relating_type": relating_type,
|
|
||||||
"should_map_representations": should_map_representations,
|
|
||||||
}
|
|
||||||
return usecase.execute()
|
|
||||||
|
|
||||||
|
|
||||||
class Usecase:
|
class Usecase:
|
||||||
file: ifcopenshell.file
|
file: ifcopenshell.file
|
||||||
settings: dict[str, Any]
|
|
||||||
|
|
||||||
def execute(self):
|
def execute(
|
||||||
if not self.settings["related_objects"]:
|
self,
|
||||||
|
related_objects: list[ifcopenshell.entity_instance],
|
||||||
|
relating_type: ifcopenshell.entity_instance,
|
||||||
|
should_map_representations: bool,
|
||||||
|
):
|
||||||
|
if not related_objects:
|
||||||
return
|
return
|
||||||
|
|
||||||
related_objects = set(self.settings["related_objects"])
|
related_objects_set = set(related_objects)
|
||||||
relating_type = self.settings["relating_type"]
|
|
||||||
|
|
||||||
ifc2x3 = self.file.schema == "IFC2X3"
|
ifc2x3 = self.file.schema == "IFC2X3"
|
||||||
|
|
||||||
related_objects = set(self.settings["related_objects"])
|
related_objects_set = set(related_objects)
|
||||||
relating_type = self.settings["relating_type"]
|
|
||||||
if ifc2x3:
|
if ifc2x3:
|
||||||
types = next(iter(relating_type.ObjectTypeOf), None)
|
types = next(iter(relating_type.ObjectTypeOf), None)
|
||||||
else:
|
else:
|
||||||
@@ -210,20 +203,20 @@ class Usecase:
|
|||||||
objects_with_types: list[ifcopenshell.entity_instance] = []
|
objects_with_types: list[ifcopenshell.entity_instance] = []
|
||||||
|
|
||||||
# check if there is anything to change
|
# check if there is anything to change
|
||||||
for object in related_objects:
|
for obj in related_objects_set:
|
||||||
if ifc2x3:
|
if ifc2x3:
|
||||||
object_rel = next((i for i in object.IsDefinedBy if i.is_a("IfcRelDefinesByType")), None)
|
object_rel = next((i for i in obj.IsDefinedBy if i.is_a("IfcRelDefinesByType")), None)
|
||||||
else:
|
else:
|
||||||
object_rel = next(iter(object.IsTypedBy), None)
|
object_rel = next(iter(obj.IsTypedBy), None)
|
||||||
|
|
||||||
if object_rel is None:
|
if object_rel is None:
|
||||||
objects_without_types.append(object)
|
objects_without_types.append(obj)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# either rel doesn't exist or product is part of different rel
|
# either rel doesn't exist or product is part of different rel
|
||||||
if object_rel != types:
|
if object_rel != types:
|
||||||
previous_types_rels.add(object_rel)
|
previous_types_rels.add(object_rel)
|
||||||
objects_with_types.append(object)
|
objects_with_types.append(obj)
|
||||||
|
|
||||||
objects_to_change = objects_without_types + objects_with_types
|
objects_to_change = objects_without_types + objects_with_types
|
||||||
# nothing to change
|
# nothing to change
|
||||||
@@ -232,7 +225,7 @@ class Usecase:
|
|||||||
|
|
||||||
# unassign from previous types
|
# unassign from previous types
|
||||||
for is_typed_by in previous_types_rels:
|
for is_typed_by in previous_types_rels:
|
||||||
cur_related_objects = set(is_typed_by.RelatedObjects) - related_objects
|
cur_related_objects = set(is_typed_by.RelatedObjects) - related_objects_set
|
||||||
if cur_related_objects:
|
if cur_related_objects:
|
||||||
is_typed_by.RelatedObjects = list(cur_related_objects)
|
is_typed_by.RelatedObjects = list(cur_related_objects)
|
||||||
ifcopenshell.api.owner.update_owner_history(self.file, **{"element": is_typed_by})
|
ifcopenshell.api.owner.update_owner_history(self.file, **{"element": is_typed_by})
|
||||||
@@ -244,18 +237,18 @@ class Usecase:
|
|||||||
|
|
||||||
# assign objects to a new type
|
# assign objects to a new type
|
||||||
if types:
|
if types:
|
||||||
types.RelatedObjects = list(set(types.RelatedObjects) | related_objects)
|
types.RelatedObjects = list(set(types.RelatedObjects) | related_objects_set)
|
||||||
ifcopenshell.api.owner.update_owner_history(self.file, **{"element": types})
|
ifcopenshell.api.owner.update_owner_history(self.file, **{"element": types})
|
||||||
else:
|
else:
|
||||||
types = self.file.create_entity(
|
types = self.file.create_entity(
|
||||||
"IfcRelDefinesByType",
|
"IfcRelDefinesByType",
|
||||||
GlobalId=ifcopenshell.guid.new(),
|
GlobalId=ifcopenshell.guid.new(),
|
||||||
OwnerHistory=ifcopenshell.api.owner.create_owner_history(self.file),
|
OwnerHistory=ifcopenshell.api.owner.create_owner_history(self.file),
|
||||||
RelatedObjects=list(related_objects),
|
RelatedObjects=list(related_objects_set),
|
||||||
RelatingType=relating_type,
|
RelatingType=relating_type,
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.settings["should_map_representations"]:
|
if should_map_representations:
|
||||||
if getattr(relating_type, "RepresentationMaps", None):
|
if getattr(relating_type, "RepresentationMaps", None):
|
||||||
for related_object in objects_to_change:
|
for related_object in objects_to_change:
|
||||||
ifcopenshell.api.type.map_type_representations(
|
ifcopenshell.api.type.map_type_representations(
|
||||||
@@ -263,11 +256,13 @@ class Usecase:
|
|||||||
related_object=related_object,
|
related_object=related_object,
|
||||||
relating_type=relating_type,
|
relating_type=relating_type,
|
||||||
)
|
)
|
||||||
self.map_material_usages(objects_to_change)
|
self.map_material_usages(objects_to_change, relating_type)
|
||||||
return types
|
return types
|
||||||
|
|
||||||
def map_material_usages(self, related_objects: Iterable[ifcopenshell.entity_instance]) -> None:
|
def map_material_usages(
|
||||||
type_material = ifcopenshell.util.element.get_material(self.settings["relating_type"])
|
self, related_objects: list[ifcopenshell.entity_instance], relating_type: ifcopenshell.entity_instance
|
||||||
|
) -> None:
|
||||||
|
type_material = ifcopenshell.util.element.get_material(relating_type)
|
||||||
if not type_material:
|
if not type_material:
|
||||||
return
|
return
|
||||||
ifc_class = type_material.is_a()
|
ifc_class = type_material.is_a()
|
||||||
|
|||||||
Reference in New Issue
Block a user