This commit is contained in:
Andrej730
2024-04-22 17:01:28 +05:00
parent d33fdbba69
commit f94aecbbff
7 changed files with 92 additions and 51 deletions
+11 -9
View File
@@ -199,7 +199,7 @@ class MaterialCreator:
class IfcImporter: class IfcImporter:
def __init__(self, ifc_import_settings): def __init__(self, ifc_import_settings: IfcImportSettings):
self.ifc_import_settings = ifc_import_settings self.ifc_import_settings = ifc_import_settings
self.diff = None self.diff = None
self.file: ifcopenshell.file = None self.file: ifcopenshell.file = None
@@ -722,7 +722,7 @@ class IfcImporter:
if len(subelement.Coordinates) == 3 and self.is_point_far_away(subelement, is_meters=False): if len(subelement.Coordinates) == 3 and self.is_point_far_away(subelement, is_meters=False):
return True return True
def apply_blender_offset_to_matrix_world(self, obj, matrix): def apply_blender_offset_to_matrix_world(self, obj: bpy.types.Object, matrix: np.ndarray) -> mathutils.Matrix:
props = bpy.context.scene.BIMGeoreferenceProperties props = bpy.context.scene.BIMGeoreferenceProperties
if props.has_blender_offset: if props.has_blender_offset:
if obj.data and obj.data.get("has_cartesian_point_offset", None): if obj.data and obj.data.get("has_cartesian_point_offset", None):
@@ -950,7 +950,9 @@ class IfcImporter:
self.create_product(element, mesh=mesh) self.create_product(element, mesh=mesh)
def create_products( def create_products(
self, products, settings: Optional[ifcopenshell.geom.main.settings] = None self,
products: set[ifcopenshell.entity_instance],
settings: Optional[ifcopenshell.geom.main.settings] = None,
) -> set[ifcopenshell.entity_instance]: ) -> set[ifcopenshell.entity_instance]:
results = set() results = set()
if not products: if not products:
@@ -1804,7 +1806,7 @@ class IfcImporter:
if rel.is_a("IfcRelAssignsToGroup") and rel.RelatingGroup.ObjectType == "DRAWING": if rel.is_a("IfcRelAssignsToGroup") and rel.RelatingGroup.ObjectType == "DRAWING":
return rel.RelatingGroup return rel.RelatingGroup
def get_element_matrix(self, element: ifcopenshell.entity_instance) -> np.array: def get_element_matrix(self, element: ifcopenshell.entity_instance) -> np.ndarray:
if isinstance(element, ifcopenshell.sqlite_entity): if isinstance(element, ifcopenshell.sqlite_entity):
result = self.geometry_cache["shapes"][element.id()]["matrix"] result = self.geometry_cache["shapes"][element.id()]["matrix"]
else: else:
@@ -1950,14 +1952,14 @@ class IfcImporter:
print(traceback.format_exc()) print(traceback.format_exc())
def a2p(self, o, z, x): def a2p(self, o: mathutils.Vector, z: mathutils.Vector, x: mathutils.Vector) -> mathutils.Matrix:
y = z.cross(x) y = z.cross(x)
r = mathutils.Matrix((x, y, z, o)) r = mathutils.Matrix((x, y, z, o))
r.resize_4x4() r.resize_4x4()
r.transpose() r.transpose()
return r return r
def get_axis2placement(self, plc): def get_axis2placement(self, plc: ifcopenshell.entity_instance) -> mathutils.Matrix:
if plc.is_a("IfcAxis2Placement3D"): if plc.is_a("IfcAxis2Placement3D"):
z = mathutils.Vector(plc.Axis.DirectionRatios if plc.Axis else (0, 0, 1)) z = mathutils.Vector(plc.Axis.DirectionRatios if plc.Axis else (0, 0, 1))
x = mathutils.Vector(plc.RefDirection.DirectionRatios if plc.RefDirection else (1, 0, 0)) x = mathutils.Vector(plc.RefDirection.DirectionRatios if plc.RefDirection else (1, 0, 0))
@@ -1977,7 +1979,7 @@ class IfcImporter:
o = plc.LocalOrigin.Coordinates o = plc.LocalOrigin.Coordinates
return self.a2p(o, z, x) return self.a2p(o, z, x)
def get_local_placement(self, plc): def get_local_placement(self, plc: Optional[ifcopenshell.entity_instance] = None) -> mathutils.Matrix:
if plc is None: if plc is None:
return mathutils.Matrix() return mathutils.Matrix()
if plc.PlacementRelTo is None: if plc.PlacementRelTo is None:
@@ -1992,11 +1994,11 @@ class IfcImporter:
bpy.context.scene.BIMRootProperties.contexts = str(subcontext.id()) bpy.context.scene.BIMRootProperties.contexts = str(subcontext.id())
break break
def link_element(self, element, obj): def link_element(self, element: ifcopenshell.entity_instance, obj: IFC_CONNECTED_TYPE) -> None:
self.added_data[element.id()] = obj self.added_data[element.id()] = obj
tool.Ifc.link(element, obj) tool.Ifc.link(element, obj)
def set_matrix_world(self, obj, matrix_world): def set_matrix_world(self, obj: bpy.types.Object, matrix_world: mathutils.Matrix) -> None:
obj.matrix_world = matrix_world obj.matrix_world = matrix_world
tool.Geometry.record_object_position(obj) tool.Geometry.record_object_position(obj)
+1 -1
View File
@@ -622,7 +622,7 @@ class Geometry(blenderbim.core.tool.Geometry):
obj.data.BIMMeshProperties.material_checksum = str([s.id() for s in cls.get_styles(obj) if s]) obj.data.BIMMeshProperties.material_checksum = str([s.id() for s in cls.get_styles(obj) if s])
@classmethod @classmethod
def record_object_position(cls, obj): def record_object_position(cls, obj: bpy.types.Object) -> None:
# These are recorded separately because they have different numerical tolerances # These are recorded separately because they have different numerical tolerances
obj.BIMObjectProperties.location_checksum = repr(np.array(obj.matrix_world.translation).tobytes()) obj.BIMObjectProperties.location_checksum = repr(np.array(obj.matrix_world.translation).tobytes())
obj.BIMObjectProperties.rotation_checksum = repr(np.array(obj.matrix_world.to_3x3()).tobytes()) obj.BIMObjectProperties.rotation_checksum = repr(np.array(obj.matrix_world.to_3x3()).tobytes())
@@ -39,6 +39,7 @@ import sys
import tempfile import tempfile
import zipfile import zipfile
from pathlib import Path from pathlib import Path
from typing import Optional
import ifcopenshell.util.file import ifcopenshell.util.file
@@ -197,12 +198,14 @@ def register_schema(schema):
register_schema_attributes(schema.schema) register_schema_attributes(schema.schema)
def schema_by_name(schema=None, schema_version=None): def schema_by_name(
schema: Optional[str] = None, schema_version: Optional[tuple[int, ...]] = None
) -> ifcopenshell_wrapper.schema_definition:
"""Returns an object allowing you to query the IFC schema itself """Returns an object allowing you to query the IFC schema itself
:param schema: Which IFC schema to use, chosen from "IFC2X3", "IFC4", :param schema: Which IFC schema to use, chosen from "IFC2X3", "IFC4",
or "IFC4X3". These refer to the ISO approved versions of IFC. or "IFC4X3". These refer to the ISO approved versions of IFC.
:type schema: string :type schema: string, optional
:param schema_version: If you want to specify an exact version of IFC :param schema_version: If you want to specify an exact version of IFC
that may not be an ISO approved version, use this argument instead that may not be an ISO approved version, use this argument instead
of ``schema``. IFC versions on technical.buildingsmart.org are of ``schema``. IFC versions on technical.buildingsmart.org are
@@ -211,7 +214,9 @@ def schema_by_name(schema=None, schema_version=None):
ADD2 TC1, which is the official version approved by ISO when people ADD2 TC1, which is the official version approved by ISO when people
refer to "IFC4". Generally you should not use this argument unless refer to "IFC4". Generally you should not use this argument unless
you are testing non-ISO IFC releases. you are testing non-ISO IFC releases.
:type schema_version: tuple[int] :type schema_version: tuple[int, ...], optional
:return: Schema definition object.
:rtype: ifocpenshell_wrapper.schema_definition
""" """
if schema_version: if schema_version:
prefixes = ("IFC", "X", "_ADD", "_TC") prefixes = ("IFC", "X", "_ADD", "_TC")
@@ -17,12 +17,19 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>. # along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import datetime import datetime
import ifcopenshell.util.constraint
import ifcopenshell.util.date import ifcopenshell.util.date
import ifcopenshell.util.sequence import ifcopenshell.util.sequence
from typing import Any, Optional
class Usecase: class Usecase:
def __init__(self, file, task_time=None, attributes=None): def __init__(
self,
file: ifcopenshell.file,
task_time: ifcopenshell.entity_instance,
attributes: Optional[dict[str, Any]] = None,
):
"""Edits the attributes of an IfcTaskTime """Edits the attributes of an IfcTaskTime
For more information about the attributes and data types of an For more information about the attributes and data types of an
@@ -55,7 +62,7 @@ class Usecase:
self.file = file self.file = file
self.settings = {"task_time": task_time, "attributes": attributes or {}} self.settings = {"task_time": task_time, "attributes": attributes or {}}
def execute(self): def execute(self) -> None:
self.task = self.get_task() self.task = self.get_task()
self.calendar = ifcopenshell.util.sequence.derive_calendar(self.task) self.calendar = ifcopenshell.util.sequence.derive_calendar(self.task)
@@ -169,12 +176,12 @@ class Usecase:
duration, "IfcDuration" duration, "IfcDuration"
) )
def get_task(self): def get_task(self) -> ifcopenshell.entity_instance:
return [ return next(
e e
for e in self.file.get_inverse(self.settings["task_time"]) for e in self.file.get_inverse(self.settings["task_time"])
if e.is_a("IfcTask") if e.is_a("IfcTask")
][0] )
def handle_resource_calculation(self): def handle_resource_calculation(self):
resources = ifcopenshell.util.sequence.get_task_resources(self.task, is_deep=False) resources = ifcopenshell.util.sequence.get_task_resources(self.task, is_deep=False)
@@ -17,10 +17,16 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>. # along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell.util.date import ifcopenshell.util.date
from typing import Any, Optional
class Usecase: class Usecase:
def __init__(self, file, work_time=None, attributes=None): def __init__(
self,
file: ifcopenshell.file,
work_time: ifcopenshell.entity_instance,
attributes: Optional[dict[str, Any]] = None,
):
"""Edits the attributes of an IfcWorkTime """Edits the attributes of an IfcWorkTime
For more information about the attributes and data types of an For more information about the attributes and data types of an
@@ -53,13 +59,15 @@ class Usecase:
self.file = file self.file = file
self.settings = {"work_time": work_time, "attributes": attributes or {}} self.settings = {"work_time": work_time, "attributes": attributes or {}}
def execute(self): def execute(self) -> None:
for name, value in self.settings["attributes"].items(): for name, value in self.settings["attributes"].items():
if name in ("Start", "StartDate"): if name in ("Start", "StartDate"):
value = ifcopenshell.util.date.datetime2ifc(value, "IfcDate") value = ifcopenshell.util.date.datetime2ifc(value, "IfcDate")
# 4 IfcWorktime Start
self.settings["work_time"][4] = value self.settings["work_time"][4] = value
elif name in ("Finish", "FinishDate"): elif name in ("Finish", "FinishDate"):
value = ifcopenshell.util.date.datetime2ifc(value, "IfcDate") value = ifcopenshell.util.date.datetime2ifc(value, "IfcDate")
# 5 IfcWorktime Finish
self.settings["work_time"][5] = value self.settings["work_time"][5] = value
else: else:
setattr(self.settings["work_time"], name, value) setattr(self.settings["work_time"], name, value)
+3 -3
View File
@@ -28,7 +28,7 @@ import numbers
import zipfile import zipfile
import functools import functools
from pathlib import Path from pathlib import Path
from typing import List, Optional from typing import Optional, Any
import ifcopenshell.util.element import ifcopenshell.util.element
import ifcopenshell.util.file import ifcopenshell.util.file
@@ -52,7 +52,7 @@ class Transaction:
self.batch_delete_ids = set() self.batch_delete_ids = set()
self.batch_inverses = [] self.batch_inverses = []
def serialise_entity_instance(self, element): def serialise_entity_instance(self, element: ifcopenshell.entity_instance) -> dict[str, Any]:
info = element.get_info() info = element.get_info()
for key, value in info.items(): for key, value in info.items():
info[key] = self.serialise_value(element, value) info[key] = self.serialise_value(element, value)
@@ -103,7 +103,7 @@ class Transaction:
} }
) )
def store_delete(self, element): def store_delete(self, element: ifcopenshell.entity_instance) -> None:
inverses = {} inverses = {}
if self.is_batched: if self.is_batched:
if element.id() not in self.batch_delete_ids: if element.id() not in self.batch_delete_ids:
@@ -20,7 +20,20 @@ import datetime
import ifcopenshell.util.date import ifcopenshell.util.date
from math import floor from math import floor
from functools import lru_cache from functools import lru_cache
from collections import namedtuple from typing import Union, Literal, Optional, Iterator
DURATION_TYPE = Literal["ELAPSEDTIME", "WORKTIME", "NOTDEFINED"]
RECURRENCE_TYPE = Literal[
"BY_DAY_COUNT",
"BY_WEEKDAY_COUNT",
"DAILY",
"MONTHLY_BY_DAY_OF_MONTH",
"MONTHLY_BY_POSITION",
"WEEKLY",
"YEARLY_BY_DAY_OF_MONTH",
"YEARLY_BY_POSITION",
]
def derive_date(task, attribute_name, date=None, is_earliest=False, is_latest=False): def derive_date(task, attribute_name, date=None, is_earliest=False, is_latest=False):
@@ -49,7 +62,7 @@ def derive_date(task, attribute_name, date=None, is_earliest=False, is_latest=Fa
return date return date
def derive_calendar(task): def derive_calendar(task: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
calendar = get_calendar(task) calendar = get_calendar(task)
if calendar: if calendar:
return calendar return calendar
@@ -57,7 +70,7 @@ def derive_calendar(task):
return derive_calendar(rel.RelatingObject) return derive_calendar(rel.RelatingObject)
def get_calendar(task): def get_calendar(task: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
calendar = [ calendar = [
rel.RelatingControl rel.RelatingControl
for rel in task.HasAssignments or [] for rel in task.HasAssignments or []
@@ -68,7 +81,7 @@ def get_calendar(task):
return calendar[0] return calendar[0]
def count_working_days(start, finish, calendar): def count_working_days(start, finish, calendar: ifcopenshell.entity_instance) -> int:
result = 0 result = 0
if start == finish: if start == finish:
return 0 return 0
@@ -88,7 +101,11 @@ def count_working_days(start, finish, calendar):
def get_start_or_finish_date( def get_start_or_finish_date(
start, duration, duration_type, calendar, date_type="FINISH" start,
duration,
duration_type: DURATION_TYPE,
calendar: ifcopenshell.entity_instance,
date_type: Literal["START", "FINISH"] = "FINISH",
): ):
if not duration.days: if not duration.days:
# Typically a milestone will have zero duration, so the start == finish # Typically a milestone will have zero duration, so the start == finish
@@ -107,7 +124,7 @@ def get_start_or_finish_date(
return datetime.datetime.combine(result, datetime.time(17)) return datetime.datetime.combine(result, datetime.time(17))
def offset_date(start, duration, duration_type, calendar): def offset_date(start, duration, duration_type: DURATION_TYPE, calendar: ifcopenshell.entity_instance):
current_date = start current_date = start
months = getattr(duration, "months", 0) months = getattr(duration, "months", 0)
years = getattr(duration, "years", 0) years = getattr(duration, "years", 0)
@@ -129,7 +146,7 @@ def offset_date(start, duration, duration_type, calendar):
return current_date return current_date
def get_soonest_working_day(start, duration_type, calendar): def get_soonest_working_day(start, duration_type: DURATION_TYPE, calendar: ifcopenshell.entity_instance):
if duration_type == "ELAPSEDTIME" or not is_calendar_applicable(start, calendar): if duration_type == "ELAPSEDTIME" or not is_calendar_applicable(start, calendar):
return start return start
while not is_working_day(start, calendar): while not is_working_day(start, calendar):
@@ -139,7 +156,7 @@ def get_soonest_working_day(start, duration_type, calendar):
return start return start
def get_recent_working_day(start, duration_type, calendar): def get_recent_working_day(start, duration_type: DURATION_TYPE, calendar: ifcopenshell.entity_instance):
if duration_type == "ELAPSEDTIME" or not is_calendar_applicable(start, calendar): if duration_type == "ELAPSEDTIME" or not is_calendar_applicable(start, calendar):
return start return start
while not is_working_day(start, calendar): while not is_working_day(start, calendar):
@@ -150,7 +167,7 @@ def get_recent_working_day(start, duration_type, calendar):
@lru_cache(maxsize=None) @lru_cache(maxsize=None)
def is_working_day(day, calendar): def is_working_day(day, calendar: ifcopenshell.entity_instance) -> bool:
is_working_day = False is_working_day = False
for work_time in calendar.WorkingTimes or []: for work_time in calendar.WorkingTimes or []:
if is_work_time_applicable_to_day(work_time, day): if is_work_time_applicable_to_day(work_time, day):
@@ -166,7 +183,7 @@ def is_working_day(day, calendar):
@lru_cache(maxsize=None) @lru_cache(maxsize=None)
def is_calendar_applicable(day, calendar): def is_calendar_applicable(day, calendar: ifcopenshell.entity_instance) -> bool:
if not calendar or not calendar.WorkingTimes: if not calendar or not calendar.WorkingTimes:
return False return False
is_applicable = False is_applicable = False
@@ -177,7 +194,7 @@ def is_calendar_applicable(day, calendar):
return is_applicable return is_applicable
def is_day_in_work_time(day, work_time): def is_day_in_work_time(day, work_time: ifcopenshell.entity_instance) -> bool:
is_day_in_work_time = True is_day_in_work_time = True
if isinstance(day, datetime.datetime): if isinstance(day, datetime.datetime):
day = datetime.date(day.year, day.month, day.day) day = datetime.date(day.year, day.month, day.day)
@@ -198,7 +215,7 @@ def is_day_in_work_time(day, work_time):
return is_day_in_work_time return is_day_in_work_time
def is_work_time_applicable_to_day(work_time, day): def is_work_time_applicable_to_day(work_time: ifcopenshell.entity_instance, day) -> bool:
if not is_day_in_work_time(day, work_time): if not is_day_in_work_time(day, work_time):
return False return False
if not work_time.RecurrencePattern: if not work_time.RecurrencePattern:
@@ -249,7 +266,7 @@ def is_work_time_applicable_to_day(work_time, day):
return False # TODO return False # TODO
def get_task_work_schedule(task): def get_task_work_schedule(task: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
parent_task = get_parent_task(task) parent_task = get_parent_task(task)
if parent_task: if parent_task:
return get_task_work_schedule(parent_task) or get_task_work_schedule(task) return get_task_work_schedule(parent_task) or get_task_work_schedule(task)
@@ -262,23 +279,23 @@ def get_task_work_schedule(task):
return None return None
def get_nested_tasks(task): def get_nested_tasks(task: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
return [object for rel in task.IsNestedBy or [] for object in rel.RelatedObjects] return [object for rel in task.IsNestedBy or [] for object in rel.RelatedObjects]
def get_parent_task(task): def get_parent_task(task: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
nests = task.Nests nests = task.Nests
if nests and (obj := nests[0].RelatingObject).is_a("IfcTask"): if nests and (obj := nests[0].RelatingObject).is_a("IfcTask"):
return obj return obj
def get_all_nested_tasks(task): def get_all_nested_tasks(task: ifcopenshell.entity_instance) -> Iterator[ifcopenshell.entity_instance]:
for nested_task in get_nested_tasks(task): for nested_task in get_nested_tasks(task):
yield nested_task yield nested_task
yield from get_all_nested_tasks(nested_task) yield from get_all_nested_tasks(nested_task)
def get_work_schedule_tasks(work_schedule): def get_work_schedule_tasks(work_schedule: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
tasks = [] tasks = []
for root_task in get_root_tasks(work_schedule): for root_task in get_root_tasks(work_schedule):
nested_tasks = get_all_nested_tasks(root_task) nested_tasks = get_all_nested_tasks(root_task)
@@ -286,7 +303,7 @@ def get_work_schedule_tasks(work_schedule):
return tasks return tasks
def get_root_tasks(work_schedule): def get_root_tasks(work_schedule: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
return [ return [
obj obj
for rel in work_schedule.Controls for rel in work_schedule.Controls
@@ -295,7 +312,7 @@ def get_root_tasks(work_schedule):
] ]
def get_root_tasks_ids(work_schedule): def get_root_tasks_ids(work_schedule: ifcopenshell.entity_instance) -> list[int]:
return [ return [
obj.id() obj.id()
for rel in work_schedule.Controls for rel in work_schedule.Controls
@@ -304,7 +321,7 @@ def get_root_tasks_ids(work_schedule):
] ]
def guess_date_range(work_schedule): def guess_date_range(work_schedule: ifcopenshell.entity_instance):
earliest = None earliest = None
latest = None latest = None
root_tasks = get_root_tasks(work_schedule) root_tasks = get_root_tasks(work_schedule)
@@ -326,7 +343,7 @@ def guess_date_range(work_schedule):
return earliest, latest return earliest, latest
def get_direct_task_outputs(task): def get_direct_task_outputs(task: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
return [ return [
rel.RelatingProduct rel.RelatingProduct
for rel in task.HasAssignments for rel in task.HasAssignments
@@ -334,7 +351,7 @@ def get_direct_task_outputs(task):
] ]
def get_task_outputs(task, is_deep=False): def get_task_outputs(task: ifcopenshell.entity_instance, is_deep=False):
if not is_deep: if not is_deep:
return get_direct_task_outputs(task) return get_direct_task_outputs(task)
else: else:
@@ -345,7 +362,7 @@ def get_task_outputs(task, is_deep=False):
] ]
def get_task_inputs(task, is_deep=False): def get_task_inputs(task: ifcopenshell.entity_instance, is_deep=False):
if not is_deep: if not is_deep:
return [ return [
object object
@@ -368,7 +385,7 @@ def get_task_inputs(task, is_deep=False):
] ]
def get_task_resources(task, is_deep=False): def get_task_resources(task: ifcopenshell.entity_instance, is_deep=False):
if not is_deep: if not is_deep:
return [ return [
object object
@@ -391,15 +408,17 @@ def get_task_resources(task, is_deep=False):
] ]
def has_task_outputs(task): def has_task_outputs(task: ifcopenshell.entity_instance) -> bool:
return len(get_task_outputs(task)) > 0 return len(get_task_outputs(task)) > 0
def has_task_inputs(task): def has_task_inputs(task: ifcopenshell.entity_instance) -> bool:
return len(get_task_inputs(task)) > 0 return len(get_task_inputs(task)) > 0
def get_tasks_for_product(product, schedule=None): def get_tasks_for_product(
product: ifcopenshell.entity_instance, schedule: Optional[ifcopenshell.entity_instance] = None
) -> tuple[list[ifcopenshell.entity_instance], list[ifcopenshell.entity_instance]]:
""" """
Get all tasks assigned to or referenced by the given product. Get all tasks assigned to or referenced by the given product.
@@ -441,7 +460,7 @@ def get_tasks_for_product(product, schedule=None):
return inputs, outputs return inputs, outputs
def get_sequence_assignment(task, sequence="successor"): def get_sequence_assignment(task: ifcopenshell.entity_instance, sequence="successor"):
if sequence == "successor": if sequence == "successor":
relationship_attr = "IsPredecessorTo" relationship_attr = "IsPredecessorTo"
elif sequence == "predecessor": elif sequence == "predecessor":