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