Second pass, manual traversal
This commit is contained in:
@@ -2,26 +2,34 @@ from typing import cast
|
||||
|
||||
from ifcopenshell.entity_instance import entity_instance
|
||||
from ifcopenshell.ifcopenshell_wrapper import Triangulation, TriangulationElement
|
||||
from specklepy.objects.base import Base
|
||||
from specklepy.objects.data_objects import DataObject
|
||||
|
||||
from speckleifc.converter.geometry_converter import geometry_to_speckle
|
||||
|
||||
|
||||
def data_object_to_speckle(
|
||||
shape: TriangulationElement, step_element: entity_instance
|
||||
shape: TriangulationElement | None,
|
||||
step_element: entity_instance,
|
||||
children: list[Base],
|
||||
) -> DataObject:
|
||||
if shape:
|
||||
geometry = cast(Triangulation, shape.geometry)
|
||||
display_value = geometry_to_speckle(geometry)
|
||||
else:
|
||||
display_value = []
|
||||
|
||||
guid = cast(str, step_element.GlobalId)
|
||||
name = cast(str, step_element.Name or guid)
|
||||
|
||||
data_object = DataObject(
|
||||
applicationId=cast(str, shape.guid),
|
||||
applicationId=guid,
|
||||
properties={},
|
||||
name=cast(str, shape.name) or cast(str, shape.guid),
|
||||
name=name or guid,
|
||||
displayValue=display_value,
|
||||
)
|
||||
# TODO: children as "elements"
|
||||
# data_object["@elements"] = children_converter.convert_children(shape, ifc_model)
|
||||
|
||||
data_object["@elements"] = children
|
||||
data_object["ifcType"] = step_element.is_a()
|
||||
data_object["expressId"] = step_element.id()
|
||||
data_object["description"] = cast(str | None, step_element.Description)
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
from typing import cast
|
||||
|
||||
from ifcopenshell.entity_instance import entity_instance
|
||||
from ifcopenshell.ifcopenshell_wrapper import Triangulation
|
||||
from ifcopenshell.ifcopenshell_wrapper import Triangulation, TriangulationElement
|
||||
from specklepy.objects.base import Base
|
||||
from specklepy.objects.data_objects import DataObject
|
||||
from specklepy.objects.models.collections.collection import Collection
|
||||
|
||||
from speckleifc.converter.geometry_converter import geometry_to_speckle
|
||||
from speckleifc.ifc_geometry_processing import try_get_shape
|
||||
|
||||
|
||||
def spatial_element_to_speckle(
|
||||
shape: TriangulationElement | None,
|
||||
step_element: entity_instance,
|
||||
relational_children: list[Base],
|
||||
) -> Collection:
|
||||
|
||||
direct_geometry = _convert_as_data_object(step_element)
|
||||
direct_geometry = _convert_as_data_object(shape, step_element)
|
||||
all_children = [direct_geometry] + relational_children
|
||||
|
||||
guid = cast(str, step_element.GlobalId)
|
||||
@@ -28,12 +28,13 @@ def spatial_element_to_speckle(
|
||||
return data_object
|
||||
|
||||
|
||||
def _convert_as_data_object(step_element: entity_instance) -> DataObject:
|
||||
def _convert_as_data_object(
|
||||
shape: TriangulationElement | None, step_element: entity_instance
|
||||
) -> DataObject:
|
||||
|
||||
# Some types of SpatialElements, like IfcSite have a geometry representation
|
||||
# Using get_shape is not as efficient as the using the geometry iterator,
|
||||
# like is used for most of the geometry conversion, but for a few IfcSites is fine.
|
||||
shape = try_get_shape(step_element)
|
||||
if shape is not None:
|
||||
geometry = cast(Triangulation, shape.geometry)
|
||||
display_value = geometry_to_speckle(geometry)
|
||||
|
||||
@@ -41,7 +41,16 @@ def create_geometry_iterator(ifc_file: file | sqlite) -> iterator:
|
||||
|
||||
|
||||
def try_get_shape(element: entity_instance) -> TriangulationElement | None:
|
||||
if element.Representation is None:
|
||||
representation = getattr(element, "Representation", None)
|
||||
if representation is None:
|
||||
return None
|
||||
|
||||
has_body = any(
|
||||
getattr(r, "RepresentationIdentifier", "").lower() == "body"
|
||||
for r in representation.Representations
|
||||
)
|
||||
|
||||
if not has_body:
|
||||
return None
|
||||
|
||||
shape = create_shape(_IFC_SETTINGS, element)
|
||||
|
||||
@@ -1,9 +1,32 @@
|
||||
from collections.abc import Generator, Iterable
|
||||
from itertools import chain
|
||||
from typing import cast
|
||||
|
||||
from ifcopenshell.entity_instance import entity_instance
|
||||
|
||||
|
||||
def get_aggregates(step_element: entity_instance) -> Generator[entity_instance]:
|
||||
for relation in cast(Iterable[entity_instance], step_element.IsDecomposedBy):
|
||||
def get_children(step_element: entity_instance) -> Generator[entity_instance]:
|
||||
|
||||
yield from chain(
|
||||
get_spatial_children(step_element), get_aggregate_children(step_element)
|
||||
)
|
||||
|
||||
|
||||
def get_spatial_children(step_element: entity_instance) -> Generator[entity_instance]:
|
||||
spatial_relations = cast(
|
||||
Iterable[entity_instance] | None,
|
||||
getattr(step_element, "ContainsElements", None),
|
||||
)
|
||||
if spatial_relations is not None:
|
||||
for relation in spatial_relations:
|
||||
yield from cast(Iterable[entity_instance], relation.RelatedElements)
|
||||
|
||||
|
||||
def get_aggregate_children(step_element: entity_instance) -> Generator[entity_instance]:
|
||||
aggregate_relations = cast(
|
||||
Iterable[entity_instance] | None,
|
||||
getattr(step_element, "IsDecomposedBy", None),
|
||||
)
|
||||
if aggregate_relations is not None:
|
||||
for relation in aggregate_relations:
|
||||
yield from cast(Iterable[entity_instance], relation.RelatedObjects)
|
||||
|
||||
+23
-73
@@ -1,18 +1,13 @@
|
||||
from collections.abc import Iterable
|
||||
from typing import cast
|
||||
|
||||
from ifcopenshell.entity_instance import entity_instance
|
||||
from ifcopenshell.geom import file
|
||||
from ifcopenshell.ifcopenshell_wrapper import TriangulationElement
|
||||
from specklepy.logging.exceptions import SpeckleException
|
||||
from specklepy.objects import Base
|
||||
from specklepy.objects.models.collections.collection import Collection
|
||||
|
||||
from speckleifc.converter.data_object_converter import data_object_to_speckle
|
||||
from speckleifc.converter.project_converter import project_to_speckle
|
||||
from speckleifc.converter.spatial_element_converter import spatial_element_to_speckle
|
||||
from speckleifc.ifc_geometry_processing import create_geometry_iterator
|
||||
from speckleifc.ifc_openshell_helpers import get_aggregates
|
||||
from speckleifc.ifc_geometry_processing import try_get_shape
|
||||
from speckleifc.ifc_openshell_helpers import get_children
|
||||
from speckleifc.root_object_builder import RootObjectBuilder
|
||||
|
||||
|
||||
@@ -21,79 +16,34 @@ class ImportJob:
|
||||
self._ifc_file = ifc_file
|
||||
self.builder = RootObjectBuilder()
|
||||
|
||||
def convert(self) -> Collection:
|
||||
# we're doing a bit of a hybrid approach to traversing the IFC graph.
|
||||
# First we convert the aggregates graph of spatial elements using a depth first
|
||||
# traversal of the aggregate relationships, starting from the project.
|
||||
# This will convert the IfcProject, IfcSite, IfcBuilding, IfcBuildingStorey etc..
|
||||
# Note that some of these, like IfcSite may still have geometry...
|
||||
#
|
||||
# This DFS approach is similar to how the v2 and v3 web-ifc based importers worked
|
||||
# But here, is only doing Spatial elements, not walls
|
||||
root = self._convert_project_tree()
|
||||
def convert_element(self, step_element: entity_instance) -> Base:
|
||||
children = self._convert_children(step_element)
|
||||
shape = try_get_shape(step_element)
|
||||
|
||||
# Then, geometry is converted using the geometry iterator, this is efficient
|
||||
# but returns objects in a non-reliable order, so we use the RootObjectBuilder
|
||||
# to differ building the rest of the Speckle objects tree
|
||||
self._convert_geometry()
|
||||
self.builder.build_commit_object(root)
|
||||
if step_element.is_a("IfcProject"):
|
||||
return project_to_speckle(step_element, children)
|
||||
elif step_element.is_a("IfcSpatialStructureElement"):
|
||||
return spatial_element_to_speckle(shape, step_element, children)
|
||||
else:
|
||||
return data_object_to_speckle(shape, step_element, children)
|
||||
|
||||
def _convert_children(self, step_element: entity_instance) -> list[Base]:
|
||||
return [self.convert_element(i) for i in get_children(step_element)]
|
||||
|
||||
## OLD
|
||||
|
||||
def convert(self) -> Base:
|
||||
|
||||
root = self._convert_project_tree()
|
||||
|
||||
return root
|
||||
|
||||
def _convert_geometry(self) -> None:
|
||||
geometry_iterator = create_geometry_iterator(self._ifc_file)
|
||||
|
||||
if not geometry_iterator.initialize():
|
||||
raise SpeckleException("Iterator failed to initialize")
|
||||
|
||||
while True:
|
||||
shape = geometry_iterator.get()
|
||||
assert isinstance(shape, TriangulationElement)
|
||||
|
||||
step_id = cast(int, shape.id)
|
||||
step_element = self._ifc_file.by_id(step_id)
|
||||
|
||||
converted = self.convert_geometry_element(shape, step_element)
|
||||
self.builder.include_object(converted, step_element, shape)
|
||||
|
||||
if not geometry_iterator.next():
|
||||
break
|
||||
|
||||
def _convert_spatial_elements_tree(
|
||||
self, step_element: entity_instance
|
||||
) -> Collection:
|
||||
|
||||
children = self._convert_aggregates(step_element)
|
||||
|
||||
result = spatial_element_to_speckle(step_element, children)
|
||||
|
||||
# Include object in the converted dictionary,
|
||||
# but no need to set relationships, since we're correctly handling those already
|
||||
# the the spatial elements traversed here
|
||||
self.builder.converted[step_element.id()] = result
|
||||
return result
|
||||
|
||||
def _convert_project_tree(self) -> Collection:
|
||||
def _convert_project_tree(self) -> Base:
|
||||
projects = self._ifc_file.by_type("IfcProject", False)
|
||||
if len(projects) != 1:
|
||||
raise SpeckleException("Expected exactly one IfcProject in file")
|
||||
project = projects[0]
|
||||
|
||||
children = self._convert_aggregates(project)
|
||||
result = project_to_speckle(project, children)
|
||||
tree = self.convert_element(project)
|
||||
|
||||
self.builder.converted[project.id()] = result
|
||||
|
||||
return result
|
||||
|
||||
def _convert_aggregates(self, step_element: entity_instance) -> list[Base]:
|
||||
return [
|
||||
self._convert_spatial_elements_tree(i) for i in get_aggregates(step_element)
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def convert_geometry_element(
|
||||
geometry_element: TriangulationElement, step_element: entity_instance
|
||||
) -> Base:
|
||||
# step_entity = ifc_model.by_id(geometry_element.id())
|
||||
return data_object_to_speckle(geometry_element, step_element)
|
||||
return tree
|
||||
|
||||
Reference in New Issue
Block a user