This commit is contained in:
Andrej730
2024-09-24 10:44:10 +05:00
parent 7045fba9c0
commit 4e3d6f1773
4 changed files with 49 additions and 27 deletions
+3 -1
View File
@@ -880,7 +880,9 @@ class Loader(bonsai.core.tool.Loader):
return Matrix(matrix.tolist()) return Matrix(matrix.tolist())
@classmethod @classmethod
def convert_geometry_to_mesh(cls, geometry, mesh: bpy.types.Mesh, verts=None) -> bpy.types.Mesh: def convert_geometry_to_mesh(
cls, geometry: ifcopenshell.geom.ShapeType, mesh: bpy.types.Mesh, verts=None
) -> bpy.types.Mesh:
if verts is None: if verts is None:
verts = geometry.verts verts = geometry.verts
if geometry.faces: if geometry.faces:
@@ -59,7 +59,7 @@ import sys
import zipfile import zipfile
import tempfile import tempfile
from pathlib import Path from pathlib import Path
from typing import Optional, Union, TYPE_CHECKING, Any from typing import Optional, Union, TYPE_CHECKING, Any, overload, Literal
if TYPE_CHECKING: if TYPE_CHECKING:
import ifcopenshell.express.schema_class import ifcopenshell.express.schema_class
@@ -119,9 +119,24 @@ class SchemaError(Error):
pass pass
def open(path: Union[os.PathLike, str], format: Optional[str] = None, should_stream: bool = False) -> file: @overload
def open(
path: Union[os.PathLike, str], format: Optional[str] = None, *, should_stream: Literal[False] = False
) -> Union[file, sqlite]: ...
@overload
def open(path: Union[os.PathLike, str], format: Optional[str] = None, *, should_stream: Literal[True]) -> stream: ...
@overload
def open(
path: Union[os.PathLike, str], format: Optional[str] = None, *, should_stream: bool
) -> Union[file, sqlite, stream]: ...
def open(
path: Union[os.PathLike, str], format: Optional[str] = None, should_stream: bool = False
) -> Union[file, sqlite, stream]:
"""Loads an IFC dataset from a filepath """Loads an IFC dataset from a filepath
:param should_stream: Whether to open the file in streaming mode. Could be useful
for reading large files.
You can specify a file format. If no format is given, it is guessed from You can specify a file format. If no format is given, it is guessed from
its extension. Currently supported specified format: .ifc | .ifcZIP | its extension. Currently supported specified format: .ifc | .ifcZIP |
.ifcXML. .ifcXML.
+12 -9
View File
@@ -19,6 +19,7 @@
from __future__ import annotations from __future__ import annotations
try: try:
import os
import re import re
import ifcopenshell.util.attribute import ifcopenshell.util.attribute
@@ -31,7 +32,7 @@ try:
from typing import Any, NoReturn, Union, Optional from typing import Any, NoReturn, Union, Optional
class StreamTransformer(Transformer): class StreamTransformer(Transformer):
file: "file" file: file
def string(self, items): def string(self, items):
return str(items[0])[1:-1] return str(items[0])[1:-1]
@@ -98,12 +99,12 @@ try:
self.filepath = filepath self.filepath = filepath
self.file = open(filepath, "r") self.file = open(filepath, "r")
self.id_map = {} self.id_map: dict[int, str] = {}
self.class_map = {} self.class_map: dict[str, list[int]] = {}
self.id_offset = {} self.id_offset: dict[int, int] = {}
self.reference_pattern = re.compile(r"#(\d+)") self.reference_pattern = re.compile(r"#(\d+)")
self.entity_cache: dict[int, stream_entity] = {} self.entity_cache: dict[int, stream_entity] = {}
self.inverses = {} self.inverses: dict[int, list[int]] = {}
# common.INT doesn't support negative integers. # common.INT doesn't support negative integers.
grammar = r""" grammar = r"""
@@ -175,7 +176,7 @@ try:
self.id_offset[step_id] = offset self.id_offset[step_id] = offset
elif line.startswith("FILE_SCHEMA"): elif line.startswith("FILE_SCHEMA"):
self.schema = line.split("'")[1] self.schema = line.split("'")[1]
self.ifc_schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(self.schema) self.ifc_schema = ifcopenshell.schema_by_name(self.schema)
for ifc_class in exclude_classes: for ifc_class in exclude_classes:
declaration = self.ifc_schema.declaration_by_name(ifc_class) declaration = self.ifc_schema.declaration_by_name(ifc_class)
exclude.update([st.name().upper() for st in ifcopenshell.util.schema.get_subtypes(declaration)]) exclude.update([st.name().upper() for st in ifcopenshell.util.schema.get_subtypes(declaration)])
@@ -186,7 +187,7 @@ try:
def preprocess_schema(self) -> None: def preprocess_schema(self) -> None:
self.ifc_class_names = {} self.ifc_class_names = {}
self.ifc_class_subtypes = {} self.ifc_class_subtypes = {}
self.ifc_class_attributes = {} self.ifc_class_attributes: dict[str, dict[str, ifcopenshell_wrapper.attribute]] = {}
self.ifc_class_inverse_attributes = {} self.ifc_class_inverse_attributes = {}
self.ifc_class_references = {} self.ifc_class_references = {}
self.ifc_class_inverses = {} self.ifc_class_inverses = {}
@@ -289,6 +290,8 @@ try:
pass pass
class stream_entity(entity_instance): class stream_entity(entity_instance):
stream_wrapper: stream_wrapper
def __init__(self, id: int, ifc_class: str, file: stream = None): def __init__(self, id: int, ifc_class: str, file: stream = None):
if not ifc_class: if not ifc_class:
print(id, ifc_class, file) print(id, ifc_class, file)
@@ -401,11 +404,11 @@ try:
self.file = file self.file = file
self.attributes = self.file.ifc_class_attributes[self.ifc_class] self.attributes = self.file.ifc_class_attributes[self.ifc_class]
self.inverse_attributes = self.file.ifc_class_inverse_attributes[self.ifc_class] self.inverse_attributes = self.file.ifc_class_inverse_attributes[self.ifc_class]
self.attribute_cache = {} self.attribute_cache: dict[str, Any] = {}
self.inverse_attribute_cache = {} self.inverse_attribute_cache = {}
def __repr__(self) -> str: def __repr__(self) -> str:
return "todo" return f"stream_wrapper '#{self.id}={self.ifc_class}(...)'"
except ImportError as e: except ImportError as e:
import sys import sys
+17 -15
View File
@@ -28,11 +28,13 @@ import numpy as np
import multiprocessing import multiprocessing
import ifcopenshell import ifcopenshell
import ifcopenshell.geom import ifcopenshell.geom
import ifcopenshell.util.unit
import ifcopenshell.util.shape
import ifcopenshell.util.schema
import ifcopenshell.util.attribute import ifcopenshell.util.attribute
import ifcopenshell.util.element
import ifcopenshell.util.placement import ifcopenshell.util.placement
import ifcopenshell.util.schema
import ifcopenshell.util.shape
import ifcopenshell.util.unit
from typing import Any
SQLTypes = typing.Literal["SQLite", "MySQL"] SQLTypes = typing.Literal["SQLite", "MySQL"]
@@ -103,7 +105,7 @@ class Patcher:
self.password = password self.password = password
self.database = database self.database = database
def patch(self): def patch(self) -> None:
self.full_schema = True # Set true for ifcopenshell.sqlite self.full_schema = True # Set true for ifcopenshell.sqlite
self.is_strict = False self.is_strict = False
self.should_expand = False # Set false for ifcopenshell.sqlite self.should_expand = False # Set false for ifcopenshell.sqlite
@@ -173,7 +175,7 @@ class Patcher:
self.db.commit() self.db.commit()
self.db.close() self.db.close()
def create_geometry(self): def create_geometry(self) -> None:
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file) self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file)
self.shape_rows = {} self.shape_rows = {}
@@ -239,7 +241,7 @@ class Patcher:
break break
print("Done creating geometry") print("Done creating geometry")
def create_id_map(self): def create_id_map(self) -> None:
if self.sql_type == "sqlite": if self.sql_type == "sqlite":
statement = ( statement = (
"CREATE TABLE IF NOT EXISTS id_map (ifc_id integer PRIMARY KEY NOT NULL UNIQUE, ifc_class text);" "CREATE TABLE IF NOT EXISTS id_map (ifc_id integer PRIMARY KEY NOT NULL UNIQUE, ifc_class text);"
@@ -254,7 +256,7 @@ class Patcher:
""" """
self.c.execute(statement) self.c.execute(statement)
def create_metadata(self): def create_metadata(self) -> None:
# There is no "standard" SQL serialisation, so we propose a convention # There is no "standard" SQL serialisation, so we propose a convention
# of a "metadata" table to hold high level metadata. This includes the # of a "metadata" table to hold high level metadata. This includes the
# preprocessor field to uniquely identify the "variant" of SQL schema # preprocessor field to uniquely identify the "variant" of SQL schema
@@ -278,7 +280,7 @@ class Patcher:
self.c.execute(statement) self.c.execute(statement)
self.c.execute("INSERT INTO metadata VALUES (%s, %s, %s);", metadata) self.c.execute("INSERT INTO metadata VALUES (%s, %s, %s);", metadata)
def create_pset_table(self): def create_pset_table(self) -> None:
statement = """ statement = """
CREATE TABLE IF NOT EXISTS psets ( CREATE TABLE IF NOT EXISTS psets (
ifc_id integer NOT NULL, ifc_id integer NOT NULL,
@@ -289,7 +291,7 @@ class Patcher:
""" """
self.c.execute(statement) self.c.execute(statement)
def create_geometry_table(self): def create_geometry_table(self) -> None:
statement = """ statement = """
CREATE TABLE IF NOT EXISTS shape ( CREATE TABLE IF NOT EXISTS shape (
ifc_id integer NOT NULL, ifc_id integer NOT NULL,
@@ -319,7 +321,7 @@ class Patcher:
self.c.execute(statement) self.c.execute(statement)
def create_sqlite_table(self, ifc_class, declaration): def create_sqlite_table(self, ifc_class: str, declaration: ifcopenshell.ifcopenshell_wrapper.declaration) -> None:
statement = f"CREATE TABLE IF NOT EXISTS {ifc_class} (" statement = f"CREATE TABLE IF NOT EXISTS {ifc_class} ("
if self.should_expand: if self.should_expand:
@@ -360,7 +362,7 @@ class Patcher:
print(statement) print(statement)
self.c.execute(statement) self.c.execute(statement)
def create_mysql_table(self, ifc_class, declaration): def create_mysql_table(self, ifc_class: str, declaration: ifcopenshell.ifcopenshell_wrapper.declaration) -> None:
declaration = self.schema.declaration_by_name(ifc_class) declaration = self.schema.declaration_by_name(ifc_class)
statement = f"CREATE TABLE IF NOT EXISTS {ifc_class} (" statement = f"CREATE TABLE IF NOT EXISTS {ifc_class} ("
statement += "`ifc_id` int(10) unsigned NOT NULL," statement += "`ifc_id` int(10) unsigned NOT NULL,"
@@ -404,7 +406,7 @@ class Patcher:
print(statement) print(statement)
self.c.execute(statement) self.c.execute(statement)
def insert_data(self, ifc_class): def insert_data(self, ifc_class: str) -> None:
print("Extracting data for", ifc_class) print("Extracting data for", ifc_class)
elements = self.file.by_type(ifc_class, include_subtypes=False) elements = self.file.by_type(ifc_class, include_subtypes=False)
@@ -478,14 +480,14 @@ class Patcher:
if pset_rows: if pset_rows:
self.c.executemany("INSERT INTO psets VALUES (%s, %s, %s, %s);", pset_rows) self.c.executemany("INSERT INTO psets VALUES (%s, %s, %s, %s);", pset_rows)
def serialise_value(self, element, value): def serialise_value(self, element: ifcopenshell.entity_instance, value: Any) -> Any:
return element.walk( return element.walk(
lambda v: isinstance(v, ifcopenshell.entity_instance), lambda v: isinstance(v, ifcopenshell.entity_instance),
lambda v: v.id() if v.id() else {"type": v.is_a(), "value": v.wrappedValue}, lambda v: v.id() if v.id() else {"type": v.is_a(), "value": v.wrappedValue},
value, value,
) )
def get_permutations(self, lst, indexes): def get_permutations(self, lst: list[Any], indexes: list[int]) -> list[Any]:
nested_lists = [lst[i] for i in indexes] nested_lists = [lst[i] for i in indexes]
# Generate the Cartesian product of the nested lists # Generate the Cartesian product of the nested lists
@@ -501,7 +503,7 @@ class Patcher:
return final_lists return final_lists
def is_entity_list(self, attribute): def is_entity_list(self, attribute: ifcopenshell.ifcopenshell_wrapper.attribute) -> bool:
attribute = str(attribute.type_of_attribute()) attribute = str(attribute.type_of_attribute())
if (attribute.startswith("<list") or attribute.startswith("<set")) and "<entity" in attribute: if (attribute.startswith("<list") or attribute.startswith("<set")) and "<entity" in attribute:
for data_type in re.findall("<(.*?) .*?>", attribute): for data_type in re.findall("<(.*?) .*?>", attribute):