This commit is contained in:
Andrej
2025-06-02 10:58:39 +05:00
parent 5a211c67eb
commit a614fac71a
8 changed files with 59 additions and 55 deletions
-3
View File
@@ -74,9 +74,6 @@ class Loader(bonsai.core.tool.Loader):
def get_representation_id_from_shape(cls, geometry: ifcopenshell.geom.ShapeType) -> int: def get_representation_id_from_shape(cls, geometry: ifcopenshell.geom.ShapeType) -> int:
representation_id: str = geometry.id representation_id: str = geometry.id
if "-" in representation_id: if "-" in representation_id:
# Example: 2432-openings-2468, where
# 2432 is mapped representation id
# and 2468 is IFCRELVOIDSELEMENT
representation_id = re.sub(r"\D", "", representation_id.split("-")[0]) representation_id = re.sub(r"\D", "", representation_id.split("-")[0])
else: else:
representation_id = re.sub(r"\D", "", representation_id) representation_id = re.sub(r"\D", "", representation_id)
@@ -38,17 +38,12 @@ def add_application(
:param application_developer: The IfcOrganization responsible for :param application_developer: The IfcOrganization responsible for
creating the application. Defaults to generating an IfcOpenShell creating the application. Defaults to generating an IfcOpenShell
organisation if none is provided. organisation if none is provided.
:type application_developer: ifcopenshell.entity_instance, optional
:param version: The version of the application. Defaults to the :param version: The version of the application. Defaults to the
ifcopenshell.version data if not specified. ifcopenshell.version data if not specified.
:type version: str, optional
:param application_full_name: The name of the application :param application_full_name: The name of the application
:type application_full_name: str, optional
:param application_identifier: An identification string for the :param application_identifier: An identification string for the
application intended for computers to read. application intended for computers to read.
:type application_identifier: str, optional
:return: The newly created IfcApplication :return: The newly created IfcApplication
:rtype: ifcopenshell.entity_instance
Example: Example:
@@ -218,7 +218,7 @@ class Element:
@property @property
def guid(self): ... def guid(self): ...
@property @property
def id(self): ... def id(self) -> int: ...
@property @property
def name(self): ... def name(self): ...
@property @property
@@ -227,9 +227,9 @@ class Element:
@property @property
def product(self): ... def product(self): ...
@property @property
def transformation(self): ... def transformation(self) -> Transformation: ...
@property @property
def transformation_buffer(self): ... def transformation_buffer(self) -> bytes: ...
@property @property
def type(self): ... def type(self): ...
@property @property
@@ -342,7 +342,13 @@ class OpaqueNumber:
class Representation: class Representation:
def entity(self): ... def entity(self): ...
@property @property
def id(self): ... def id(self) -> str:
"""
Example: `2432-openings-2468`, where
- 2432 - mapped representation id
- 2468 - IfcRelVoidsElement
"""
def settings(self): ... def settings(self): ...
class Serialization(Representation): class Serialization(Representation):
@@ -458,34 +464,34 @@ class Triangulation(Representation):
@property @property
def colors_buffer(self): ... def colors_buffer(self): ...
@property @property
def edges(self): ... def edges(self) -> tuple[int, ...]: ...
@property @property
def edges_buffer(self): ... def edges_buffer(self) -> bytes: ...
def edges_item_ids(self): ... def edges_item_ids(self) -> tuple[int, ...]: ...
@property @property
def edges_item_ids_buffer(self): ... def edges_item_ids_buffer(self) -> bytes: ...
@staticmethod @staticmethod
def empty(settings): ... def empty(settings): ...
@property @property
def faces(self): ... def faces(self) -> tuple[int, ...]: ...
@property @property
def faces_buffer(self): ... def faces_buffer(self) -> bytes: ...
@property @property
def faces_tri(self): ... def faces_tri(self) -> tuple[int, ...]: ...
@property @property
def item_ids(self): ... def item_ids(self) -> tuple[int, ...]: ...
@property @property
def item_ids_buffer(self): ... def item_ids_buffer(self) -> bytes: ...
@property @property
def material_ids(self): ... def material_ids(self) -> tuple[int, ...]: ...
@property @property
def material_ids_buffer(self): ... def material_ids_buffer(self) -> bytes: ...
@property @property
def materials(self): ... def materials(self) -> tuple[style, ...]: ...
@property @property
def normals(self): ... def normals(self): ...
@property @property
def normals_buffer(self): ... def normals_buffer(self) -> bytes: ...
@property @property
def polyhedral_faces_with_holes(self): ... def polyhedral_faces_with_holes(self): ...
@property @property
@@ -497,13 +503,13 @@ class Triangulation(Representation):
def uvs(self): ... def uvs(self): ...
def uvs_ref(self): ... def uvs_ref(self): ...
@property @property
def verts(self): ... def verts(self) -> tuple[float, ...]: ...
@property @property
def verts_buffer(self): ... def verts_buffer(self) -> bytes: ...
class TriangulationElement(Element): class TriangulationElement(Element):
@property @property
def geometry(self): ... def geometry(self) -> Triangulation: ...
def geometry_pointer(self): ... def geometry_pointer(self): ...
class TriangulationType: class TriangulationType:
@@ -1183,7 +1189,19 @@ class style(item):
def get_color(self): ... def get_color(self): ...
def has_specularity(self): ... def has_specularity(self): ...
def has_transparency(self): ... def has_transparency(self): ...
def instance_id(self): ... def instance_id(self) -> int:
"""
Possible values for `instance_id`:
- IFC style id if style assigned to the representation items directly
or through material with a style;
- IFC material id if both true:
- element has a material without a style;
- there are parts of the geometry that has no other style assigned to them;
- -1 in case if there is no material;
- 0 in case if there are default materials used.
"""
...
def kind(self): ... def kind(self): ...
class surface(geom_item): ... class surface(geom_item): ...
@@ -277,17 +277,7 @@ def get_normals(geometry: ShapeType) -> npt.NDArray[np.float64]:
def get_shape_material_styles(geometry: ShapeType) -> tuple[W.style, ...]: def get_shape_material_styles(geometry: ShapeType) -> tuple[W.style, ...]:
"""Get list of material styles. """Get list of material styles."""
Possible values for `style.instance_id()`:
- IFC style id if style assigned to the representation items directly
or through material with a style;
- IFC material id if both true:
- element has a material without a style;
- there are parts of the geometry that has no other style assigned to them;
- -1 in case if there is no material;
- 0 in case if there are default materials used.
"""
return geometry.materials return geometry.materials
+2 -4
View File
@@ -29,6 +29,7 @@ import collections
import importlib import importlib
import importlib.util import importlib.util
import re import re
from pathlib import Path
from typing import Union, Iterable, Optional, Any, TypedDict, Literal, Sequence from typing import Union, Iterable, Optional, Any, TypedDict, Literal, Sequence
from typing_extensions import NotRequired from typing_extensions import NotRequired
@@ -106,19 +107,16 @@ def execute(args: ArgumentsDict) -> Union[ifcopenshell.file, str]:
return output return output
def write(output: Union[ifcopenshell.file, str], filepath: str) -> None: def write(output: Union[ifcopenshell.file, str], filepath: Union[Path, str]) -> None:
"""Write the output of an IFC patch to a file """Write the output of an IFC patch to a file
Typically a patch output would be a patched IFC model file object, or as a Typically a patch output would be a patched IFC model file object, or as a
string. This function lets you agnostically write that output to a filepath. string. This function lets you agnostically write that output to a filepath.
:param output: The results from ifcpatch.execute() :param output: The results from ifcpatch.execute()
:type output: ifcopenshell.file.file,str
:param filepath: A filepath to where the results of the patched model should :param filepath: A filepath to where the results of the patched model should
be written to. be written to.
:type filepath: str
:return: None :return: None
:rtype: None
""" """
if output is None: if output is None:
return return
@@ -63,7 +63,6 @@ class Patcher:
Allowed imperial units include INCH, FOOT, MILE. Allowed imperial units include INCH, FOOT, MILE.
:param unit: The name of the desired unit, defaults to "METER" :param unit: The name of the desired unit, defaults to "METER"
:type unit: LengthUnit
Example: Example:
+17 -9
View File
@@ -29,6 +29,7 @@ import numpy as np
import multiprocessing import multiprocessing
import ifcopenshell import ifcopenshell
import ifcopenshell.geom import ifcopenshell.geom
import ifcopenshell.ifcopenshell_wrapper as W
import ifcopenshell.util.attribute import ifcopenshell.util.attribute
import ifcopenshell.util.element import ifcopenshell.util.element
import ifcopenshell.util.placement import ifcopenshell.util.placement
@@ -36,8 +37,8 @@ import ifcopenshell.util.schema
import ifcopenshell.util.shape import ifcopenshell.util.shape
import ifcopenshell.util.unit import ifcopenshell.util.unit
from pathlib import Path from pathlib import Path
from ifcopenshell.geom import ShapeType from typing import Any, TYPE_CHECKING, Literal, Union
from typing import Any, TYPE_CHECKING, Literal from typing_extensions import assert_never
SQLTypes = typing.Literal["SQLite", "MySQL"] SQLTypes = typing.Literal["SQLite", "MySQL"]
@@ -132,6 +133,9 @@ class Patcher:
self.should_get_geometry = should_get_geometry self.should_get_geometry = should_get_geometry
self.should_skip_geometry_data = should_skip_geometry_data self.should_skip_geometry_data = should_skip_geometry_data
geometry_rows: dict[str, tuple[str, bytes, bytes, bytes, bytes, str]]
shape_rows: dict[int, tuple[int, list[float], list[float], list[float], bytes, str]]
def patch(self) -> None: def patch(self) -> None:
suffix = ".db" if self.sql_type == "SQLite" else ".sqlite" suffix = ".db" if self.sql_type == "SQLite" else ".sqlite"
database = Path(self.database) database = Path(self.database)
@@ -153,6 +157,8 @@ class Patcher:
host=self.host, user=self.username, password=self.password, database=str(database) host=self.host, user=self.username, password=self.password, database=str(database)
) )
self.c = self.db.cursor() self.c = self.db.cursor()
else:
assert False
self.create_id_map() self.create_id_map()
self.create_metadata() self.create_metadata()
@@ -165,7 +171,9 @@ class Patcher:
self.create_geometry() self.create_geometry()
if self.full_schema: if self.full_schema:
ifc_classes = [d.name() for d in self.schema.declarations() if str(d).startswith("<entity")] ifc_classes = [
d.name() for d in self.schema.declarations() if isinstance(d, ifcopenshell.ifcopenshell_wrapper.entity)
]
else: else:
ifc_classes = self.file.wrapped_data.types() ifc_classes = self.file.wrapped_data.types()
@@ -249,22 +257,22 @@ class Patcher:
checkpoint = time.time() checkpoint = time.time()
shape = iterator.get() shape = iterator.get()
if shape: if shape:
assert isinstance(shape, W.TriangulationElement)
shape_id = shape.id shape_id = shape.id
geometry_id = shape.geometry.id geometry = shape.geometry
geometry_id = geometry.id
if geometry_id not in self.geometry_rows: if geometry_id not in self.geometry_rows:
geometry: ShapeType
geometry = shape.geometry
v = geometry.verts_buffer v = geometry.verts_buffer
e = geometry.edges_buffer e = geometry.edges_buffer
f = geometry.faces_buffer f = geometry.faces_buffer
mids = geometry.material_ids_buffer mids = geometry.material_ids_buffer
m = json.dumps([m.instance_id() for m in shape.geometry.materials]) m = json.dumps([m.instance_id() for m in geometry.materials])
self.geometry_rows[geometry_id] = [geometry_id, v, e, f, mids, m] self.geometry_rows[geometry_id] = (geometry_id, v, e, f, mids, m)
# Copy required since otherwise it is read-only # Copy required since otherwise it is read-only
m = ifcopenshell.util.shape.get_shape_matrix(shape).copy() m = ifcopenshell.util.shape.get_shape_matrix(shape).copy()
m[:3, 3] /= self.unit_scale m[:3, 3] /= self.unit_scale
x, y, z = m[:, 3][0:3].tolist() x, y, z = m[:, 3][0:3].tolist()
self.shape_rows[shape_id] = [shape_id, x, y, z, m.tobytes(), geometry_id] self.shape_rows[shape_id] = (shape_id, x, y, z, m.tobytes(), geometry_id)
if not iterator.next(): if not iterator.next():
break break
-1
View File
@@ -35,7 +35,6 @@ class Patcher:
possible. Upgrading to IFC4 is more stable than downgrading to IFC2X3. possible. Upgrading to IFC4 is more stable than downgrading to IFC2X3.
:param schema: The schema identifier of the IFC version to migrate to. :param schema: The schema identifier of the IFC version to migrate to.
:type schema: str
Example: Example: