Merge remote-tracking branch 'origin/v0.8.0' into test-ci

This commit is contained in:
Thomas Krijnen
2025-10-27 06:54:10 +01:00
25 changed files with 352 additions and 107 deletions
+1
View File
@@ -114,6 +114,7 @@ jobs:
-DPYTHON_INCLUDE_DIR:PATH=${{ env.pythonLocation }}/include/python3.11 \
-DPYTHON_LIBRARY:FILEPATH=${{ env.pythonLocation }}/lib/libpython3.11.so \
-DCOLLADA_SUPPORT=Off \
-DUSE_MMAP=On \
"-DSCHEMA_VERSIONS=2x3;4;4x3_add2" \
-DGLTF_SUPPORT=On \
-DWITH_ROCKSDB=On \
+2
View File
@@ -18,6 +18,8 @@
# #
###############################################################################
# ruff: noqa: UP035
"""
Example usage:
# Build all targets by default.
+5 -1
View File
@@ -487,7 +487,11 @@ def sync_references(
continue
if (obj := ifc.get_object(element)) and ifc.is_moved(obj):
drawing_tool.sync_object_placement(obj)
reference_element = drawing_tool.get_assigned_product(element)
if not (reference_element := drawing_tool.get_assigned_product(element)):
if obj := ifc.get_object(element):
drawing_tool.delete_object(obj)
ifc.run("root.remove_product", product=element)
continue
reference_obj = ifc.get_object(reference_element)
if reference_element not in potential_reference_elements:
# It was auto created, so it makes sense to auto delete
+9 -5
View File
@@ -786,17 +786,21 @@ class Model(bonsai.core.tool.Model):
elif material.is_a("IfcMaterialLayerSet"):
axis = ifcopenshell.util.element.get_pset(element, "EPset_Parametric", "LayerSetDirection")
if axis is None:
if element.is_a() in [
if element.is_a() in (
"IfcSlabType",
"IfcRoofType",
"IfcRampType",
"IfcPlateType",
"IfcCovering",
"IfcFurniture",
]:
"IfcSlab",
"IfcRoof",
"IfcRamp",
"IfcPlate",
):
axis = "AXIS3"
else:
elif element.is_a() in ("IfcWallType", "IfcWall"):
axis = "AXIS2"
else:
return
return f"LAYER{axis[-1]}"
elif material.is_a("IfcMaterialProfileSetUsage"):
# TODO: remove after we support editing profile usages with IfcRevolvedAreaSolid.
+2 -1
View File
@@ -24,7 +24,8 @@ import importlib
import ifcopenshell.util.selector
from pathlib import Path
from collections import defaultdict
from typing import Literal, Union, Any, Callable, TYPE_CHECKING
from typing import Literal, Union, Any, TYPE_CHECKING
from collections.abc import Callable
try:
from openpyxl import Workbook
+4
View File
@@ -367,11 +367,13 @@ void IfcGeom::Iterator::create_element_(ifcopenshell::geometry::Converter* kerne
}));
if (!brep) {
Logger::SetProduct(boost::none);
return;
}
auto elem = process_based_on_settings(settings, brep);
if (!elem) {
Logger::SetProduct(boost::none);
return;
}
@@ -394,6 +396,8 @@ void IfcGeom::Iterator::create_element_(ifcopenshell::geometry::Converter* kerne
}
}
}
Logger::SetProduct(boost::none);
}
IfcGeom::Element* IfcGeom::Iterator::process_based_on_settings(ifcopenshell::geometry::Settings settings, IfcGeom::BRepElement* elem, IfcGeom::TriangulationElement* previous)
+1
View File
@@ -43,6 +43,7 @@ Examples
Learning how to use the bSDD is best done by reading the official Swagger API docs.
.. code-block:: python
from bsdd import Client,apply_ifc_classification_properties
from pprint import pprint
@@ -17,4 +17,5 @@ capabilities of the C++ core are available in Python.
ifcopenshell-python/geometry_creation
ifcopenshell-python/geometry_tree
ifcopenshell-python/selector_syntax
ifcopenshell-python/developer_guide
ifcopenshell-python/schema_querying
ifcopenshell-python/validation
@@ -1,15 +0,0 @@
Developer Guide
===============
The core module implements low-level functionality to read and write IFC data. This includes:
- Reading IFC data from different serialisations into Python objects
- Accessing direct and indirect attributes of IFC entities
- Creating IFC entities
- Generating GlobalIds
- Removing IFC entities and all references
- Modifying IFC direct attributes
- Checking IFC class inheritance
- Validating IFC data
TODO
@@ -0,0 +1,73 @@
Schema querying
===============
Schema declarations
-------------------
IfcOpenShell can query the IFC schema itself without instantiating or loading an IFC dataset.
.. code-block:: python
import ifcopenshell
ifc4 = ifcopenshell.schema_by_name("IFC4")
A schema definition is known as a declaration. You may loop through all declarations or retrieve a declaration by name. All declarations have a name.
.. code-block:: python
for declaration in ifc4.declarations():
print(declaration.name()) # 'IfcAbsorbedDoseMeasure', 'IfcAccelerationMeasure', 'IfcActionRequest', ...
ifcwall = ifc4.declaration_by_name("IfcWall")
You can check if an entity is abstract, and retrive both the supertype and subtypes of an entity:
.. code-block:: python
print(ifcwall.is_abstract()) # False
print(ifcwall.supertype()) # <entity IfcBuildingElement>
print(ifcwall.subtypes()) # (<entity IfcWallElementedCase>, <entity IfcWallStandardCase>)
You can retrieve only the direct attributes of an entity, or all the direct attributes including inherited attributes, or inverse attributes:
.. code-block:: python
print(ifcwall.attributes())
print(ifcwall.all_attributes())
print(ifcwall.all_inverse_attributes())
buildingSMART property set templates
------------------------------------
For each IFC schema version, buildingSMART publishes built in property and quantity set templates for standardised properties. These define property names, property sets, data types, and which IFC class they are applicable to. You can query these templates.
.. code-block:: python
import ifcopenshell.util.pset
templates = ifcopenshell.util.pset.PsetQto("IFC4")
To get just the names of applicable templates for an entity:
.. code-block:: python
# ['Pset_EnvironmentalImpactIndicators', 'Pset_EnvironmentalImpactValues', 'Pset_WallCommon', 'Qto_WallBaseQuantities', ...]
print(templates.get_applicable_names("IfcWall"))
They may also be retrieved as an ``IfcPropertySetTemplate`` entity:
.. code-block:: python
print(templates.get_applicable("IfcWall"))
A single template may be retrieved by name:
.. code-block:: python
templates.get_by_name('Pset_WallCommon')
You may add your own IFC files containing pset template definitions:
.. code-block:: python
my_pset_library = ifcopenshell.open('/path/to/library.ifc')
templates.templates.append(my_pset_library)
@@ -0,0 +1,78 @@
Validation
==========
SPF syntax validation
---------------------
IfcOpenShell can validate whether or not an IFC-SPF file contains correct SPF syntax.
.. code-block::
$ python -m ifcopenshell.simple_spf path/to/model.ifc
Valid
Here are some examples of failures:
.. code-block::
$ python -m ifcopenshell.simple_spf fixtures/fail_double_comma.ifc
On line 8 column 21:
Unexpected comma (',')
Expecting one of DBLQUOTE DOT HASH INT LPAR NONE QUOTE REAL STAR UPPER
00008 | #1=IFCPERSON($,$,'',,$,$,$,$);
^
$ python -m ifcopenshell.simple_spf fixtures/fail_double_semi.ifc
On line 27 column 66:
Unexpected semicolon (';')
Expecting one of ENDSEC HASH
00027 | #20=IFCPROJECT('2AyG2X0sb16Bjd4gQc07yZ',#5,'',$,$,$,$,(#11),#19);;
^
$ python -m ifcopenshell.simple_spf fixtures/fail_duplicate_id.ifc
On line 27:
Duplicate instance name #19
00027 | #19=IFCPROJECT('2AyG2X0sb16Bjd4gQc07yZ',#5,'',$,$,$,$,(#11),#19);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
$ python -m ifcopenshell.simple_spf fixtures/fail_no_header.ifc
On line 2 column 1:
Unexpected hex ('F')
Expecting HEADER
00002 | FILE_DESCRIPTION(('ViewDefinition [CoordinationView]'),'2;1');
^
The optional ``--json`` argument may be used to instead get results in JSON.
.. code-block::
$ python -m ifcopenshell.simple_spf test.ifc
{"type": "unexpected_token", "lineno": 8, "column": 48, "found_type": "semicolon", "found_value": ";", "expected": ["ENDSEC"], "line": "#1= IFCPERSON($,'Nicht definiert',$,$,$,$,$,$);;", "message": "On line 8 column 48:\nUnexpected semicolon (';')\nExpecting ENDSEC\n00008 | #1= IFCPERSON($,'Nicht definiert',$,$,$,$,$,$);;\n ^"}
IFC schema validation
---------------------
IfcOpenShell can validate models against the IFC schema itself. It checks against attributes, entity names, data types, cardinality, and where rules.
.. code-block:: console
$ python -m ifcopenshell.validate -h
usage: validate.py [-h] [--rules] [--json] [--fields] [--spf] files [files ...]
positional arguments:
files The IFC file to validate.
options:
-h, --help show this help message and exit
--rules Run express rules.
--json Output in JSON format.
--fields Output more detailed information about failed entities (only with --json).
--spf Output entities in SPF format (only with --json).
For example:
.. code-block:: bash
python -m ifcopenshell.validate /path/to/model.ifc --rules
+21 -21
View File
@@ -54,27 +54,27 @@ IfcOpenShell is a modular ecosystem of tools that work together, where each tool
.. csv-table::
:header: "Name", "Description"
"**IfcOpenShell**", "The core library for C++ developers. The library includes the ability to parse schemas, tessellate and process implicit geometry."
"**IfcOpenShell-Python**", "Python bindings to the core IfcOpenShell C++ system, as well as high level analysis and authoring functions."
"**IfcConvert**", "A command-line application for converting IFC geometry into file formats such as OBJ, DAE, GLB, STP, IGS, XML, SVG, H5, and IFC itself."
"**Bonsai**", "A graphical add-on for Blender that lets you analyse, author, and modify IFC with Blender. Graphically create BIM models from scratch!"
"**BCF**", "BIM Collaboration Format (BCF) is a standard to manage and exchange coordination topics between disciplines collaborating on a project by changing XML files or querying an API."
"**BIMServer-Plugin**", "A plugin to the open source BIMServer CDE to allow you to use IfcOpenShell to parse, view, and audit models."
"**BIMTester**", "A utility that allows you to write Gherkin-based tests for models."
"**bSDD**", "A Python library to query the buildingSMART Data Dictionary API to search for standardised classifications and properties."
"**Ifc2CA**", "Converts IFC models to FEM structural analytical models to be used in Code_Aster."
"**Ifc4D**", "A series of utilities for converting to and from various 4D software like MS Project, PowerProject, and Oracle P6."
"**Ifc5D**", "A collection of utilities of manipulating cost-related data to and from formats, reports, and optimisation engines."
"**IfcCityJSON**", "A converter for CityJSON files and IFC. It currently only supports one-way conversion from CityJSON to IFC."
"**IfcClash**", "A CLI utility and library that lets you perform clash detection on one or more IFC models. Clashes are defined in terms of clash sets with filters using the IFC query syntax."
"**IfcCSV**", "View and edit IFC data using spreadsheets or tabular datasets, such as CSV, ODS, XLSX, Pandas DataFrames, and regular Python lists."
"**IfcDiff**", "A CLI utility and library that lets you compare the changes between two IFC models."
"**IfcFM**", "A highly standards-compliant tool (e.g. COBie 2.4, COBie 3.0, AOH-BSEM) to convert FM data in IFC databases to spreadsheets and other machine readable formats, such as ODS, XLSX, CSV, Pandas, XML, and JSON."
"**IfcMax**", "A 3ds Max importer plugin able to import the IFC file format."
"**IfcPatch**", "A CLI utility and library that lets you run and distribute predetermined modifications on an IFC file, known as a patch recipe. Useful in deploying a data pipeline or batch-fixing external models."
"**IfcSverchok**", "A node based visual programming add-on for Blender to interact with IFC and Sverchok."
"**IfcTester**", "Author and read Information Delivery Specification (IDS) files. You can validate IFC models against IDS and generate reports in multiple formats. It works from the command line, as a web app, or as a library."
"**VoxelisationToolkit**", "Converts .ifc geometry into voxels, and lets you perform voxel based geometric analysis."
"`IfcOpenShell <https://docs.ifcopenshell.org/ifcopenshell.html>`_", "The core library for C++ developers. The library includes the ability to parse schemas, tessellate and process implicit geometry."
"`IfcOpenShell-Python <https://docs.ifcopenshell.org/ifcopenshell-python.html>`_", "Python bindings to the core IfcOpenShell C++ system, as well as high level analysis and authoring functions."
"`IfcConvert <https://docs.ifcopenshell.org/ifcconvert.html>`_", "A command-line application for converting IFC geometry into file formats such as OBJ, DAE, GLB, STP, IGS, XML, SVG, H5, and IFC itself."
"`Bonsai <https://docs.ifcopenshell.org/bonsai.html>`_", "A graphical add-on for Blender that lets you analyse, author, and modify IFC with Blender. Graphically create BIM models from scratch!"
"`BCF <https://docs.ifcopenshell.org/bcf.html>`_", "BIM Collaboration Format (BCF) is a standard to manage and exchange coordination topics between disciplines collaborating on a project by changing XML files or querying an API."
"`BIMServer-Plugin <https://docs.ifcopenshell.org/bimserver-plugin.html>`_", "A plugin to the open source BIMServer CDE to allow you to use IfcOpenShell to parse, view, and audit models."
"`BIMTester <https://docs.ifcopenshell.org/bimtester.html>`_", "A utility that allows you to write Gherkin-based tests for models."
"`bSDD <https://docs.ifcopenshell.org/bsdd.html>`_", "A Python library to query the buildingSMART Data Dictionary API to search for standardised classifications and properties."
"`Ifc2CA <https://docs.ifcopenshell.org/ifc2ca.html>`_", "Converts IFC models to FEM structural analytical models to be used in Code_Aster."
"`Ifc4D <https://docs.ifcopenshell.org/ifc4d.html>`_", "A series of utilities for converting to and from various 4D software like MS Project, PowerProject, and Oracle P6."
"`Ifc5D <https://docs.ifcopenshell.org/ifc5d.html>`_", "A collection of utilities of manipulating cost-related data to and from formats, reports, and optimisation engines."
"`IfcCityJSON <https://docs.ifcopenshell.org/ifccityjson.html>`_", "A converter for CityJSON files and IFC. It currently only supports one-way conversion from CityJSON to IFC."
"`IfcClash <https://docs.ifcopenshell.org/ifcclash.html>`_", "A CLI utility and library that lets you perform clash detection on one or more IFC models. Clashes are defined in terms of clash sets with filters using the IFC query syntax."
"`IfcCSV <https://docs.ifcopenshell.org/ifccsv.html>`_", "View and edit IFC data using spreadsheets or tabular datasets, such as CSV, ODS, XLSX, Pandas DataFrames, and regular Python lists."
"`IfcDiff <https://docs.ifcopenshell.org/ifcdiff.html>`_", "A CLI utility and library that lets you compare the changes between two IFC models."
"`IfcFM <https://docs.ifcopenshell.org/ifcfm.html>`_", "A highly standards-compliant tool (e.g. COBie 2.4, COBie 3.0, AOH-BSEM) to convert FM data in IFC databases to spreadsheets and other machine readable formats, such as ODS, XLSX, CSV, Pandas, XML, and JSON."
"`IfcMax <https://docs.ifcopenshell.org/ifcmax.html>`_", "A 3ds Max importer plugin able to import the IFC file format."
"`IfcPatch <https://docs.ifcopenshell.org/ifcpatch.html>`_", "A CLI utility and library that lets you run and distribute predetermined modifications on an IFC file, known as a patch recipe. Useful in deploying a data pipeline or batch-fixing external models."
"`IfcSverchok <https://docs.ifcopenshell.org/ifcsverchok.html>`_", "A node based visual programming add-on for Blender to interact with IFC and Sverchok."
"`IfcTester <https://docs.ifcopenshell.org/ifctester.html>`_", "Author and read Information Delivery Specification (IDS) files. You can validate IFC models against IDS and generate reports in multiple formats. It works from the command line, as a web app, or as a library."
"`VoxelisationToolkit <https://github.com/opensourceBIM/voxelization_toolkit>`_", "Converts .ifc geometry into voxels, and lets you perform voxel based geometric analysis."
.. note::
+1 -1
View File
@@ -251,7 +251,7 @@ INVALID_SYNTAX = ifcopenshell_wrapper.file_open_status.INVALID_SYNTAX
try:
UNKNOWN = ifcopenshell_wrapper.file_open_status.UNKNOWN
except:
UNKNOWN = 5 # Workaround
UNKNOWN = 5 # Workaround
import struct
@@ -22,7 +22,7 @@ import os
import sys
import operator
from .. import ifcopenshell_wrapper
from .. import open, ifcopenshell_wrapper
from ..file import file
from ..entity_instance import entity_instance
@@ -307,10 +307,7 @@ class iterator(ifcopenshell_wrapper.Iterator):
self.file = file
file_or_filename = file_or_filename.wrapped_data
else:
# @todo?
self.file = None
# Makes sure people are able to use python's platform agnostic paths
file_or_filename = os.path.abspath(file_or_filename)
file_or_filename = self.file = open(file_or_filename)
if include is not None and exclude is not None:
raise ValueError("include and exclude cannot be specified simultaneously")
@@ -342,7 +339,9 @@ class iterator(ifcopenshell_wrapper.Iterator):
geometry_library, self.settings, file_or_filename, include_or_exclude, include is not None, num_threads
)
else:
ifcopenshell_wrapper.Iterator.__init__(self, geometry_library, settings, file_or_filename, num_threads)
self.this = ifcopenshell_wrapper.construct_iterator(
geometry_library, self.settings, file_or_filename, num_threads
)
if has_occ:
@@ -71,6 +71,13 @@ FILTERED_CARTESIAN_QUOTIENT: Any
EXACT_PREDICATES: Any
EXACT_CONSTRUCTIONS: Any
FT_AUTODETECT: Any
FT_IFCSPF: Any
FT_IFCXML: Any
FT_IFCZIP: Any
FT_ROCKSDB: Any
FT_UNKNOWN: Any
CURVES: Any
SURFACES_AND_SOLIDS: Any
CURVES_SURFACES_AND_SOLIDS: Any
@@ -96,12 +103,9 @@ class IfcSpfHeader:
"""
def file(self, *args): ...
@property
def file_description(self) -> ifcopenshell.entity_instance: ...
@property
def file_name(self) -> ifcopenshell.entity_instance: ...
@property
def file_schema(self) -> ifcopenshell.entity_instance: ...
def file_description_py(self): ...
def file_name_py(self): ...
def file_schema_py(self): ...
def read(self): ...
def tryRead(self): ...
def write(self, out): ...
@@ -145,6 +149,7 @@ class ConversionResult:
def Style(self): ...
def StylePtr(self): ...
def append(self, trsf): ...
def apply_transform(self, unit_scale): ...
def hasStyle(self): ...
def prepend(self, trsf): ...
def setStyle(self, newStyle): ...
@@ -157,6 +162,7 @@ class ConversionResultShape:
def axis(self): ...
def bounding_box(self, *args): ...
def box(self): ...
def concat(self, arg2): ...
def convex_decomposition(self): ...
def convex_tag(self, b): ...
def edges(self): ...
@@ -178,9 +184,11 @@ class ConversionResultShape:
def solid(self): ...
def solid_mt(self): ...
def subtract(self, arg2): ...
def surface_area_along_direction(self, tol, arg3, along_x, along_y, along_z): ...
def surface_genus(self): ...
def vertices(self): ...
def volume(self): ...
def wrap_in_compound(self): ...
class DoubleArray3:
def back(self): ...
@@ -284,11 +292,6 @@ class HdfSerializer(GeometrySerializer):
def write(self, *args): ...
def writeHeader(self): ...
class HeaderEntity:
def getArgument(self, index): ...
def getArgumentCount(self): ...
def toString(self, upper): ...
class IfcBaseEntity(entity_instance):
def declaration(self): ...
def get(self, name): ...
@@ -304,9 +307,17 @@ class IfcEntityInstanceData: ...
class IfcLateBoundEntity(IfcBaseEntity):
def declaration(self): ...
class InstanceReference:
file_offset: Any
v: Any
class InstanceStreamer:
def bypassTypes(self, type_names): ...
def bypassed_instances(self): ...
coerce_attribute_count: bool
def hasSemicolon(self): ...
def inverses(self, *args): ...
def pushPage(self, page): ...
def readInstancePy(self, type_as_declaration_instance): ...
def references(self, *args): ...
def semicolonCount(self): ...
def status(self): ...
class Iterator:
initialization_outcome_: Any
@@ -377,6 +388,18 @@ class Representation:
def settings(self): ...
class RocksDBPrefixIterator:
def key(self): ...
def next(self): ...
def valid(self): ...
def value(self): ...
class RocksDbSerializer:
def finalize(self): ...
def ready(self): ...
def setFile(self, arg2): ...
def writeHeader(self): ...
class Serialization(Representation):
@property
def brep_data(self): ...
@@ -829,6 +852,7 @@ class entity_instance:
...
def get_attribute_names(self): ...
def get_attribute_value(self, index): ...
def get_inverse(self, a): ...
def get_inverse_attribute_names(self): ...
def id(self) -> int: ...
@@ -851,6 +875,7 @@ class entity_instance:
def setArgumentAsLogical(self, i, v): ...
def setArgumentAsNull(self, i): ...
def setArgumentAsString(self, i, a): ...
def set_attribute_value(self, *args): ...
def toString(self, arg2, upper): ...
def to_string(self, valid_spf): ...
def unset_attribute_value(self, i): ...
@@ -888,14 +913,10 @@ class face:
def print_impl(self, o, indent): ...
class file:
INSTANCE_ID: Any
INSTANCE_TYPE: Any
ATTRIBUTE_INDEX: Any
guid_map_: Any
stream: Any
def FreshId(self): ...
def add(self, entity: entity_instance, id: int) -> entity_instance: ...
def addEntities(self, entities): ...
def add_type_ref(self, new_entity): ...
def batch(self) -> None:
"""Enable batch mode.
@@ -919,10 +940,15 @@ class file:
...
def build_inverses(self): ...
def build_inverses_(self, arg2): ...
def by_guid(self, guid: str) -> entity_instance: ...
def by_id(self, id: int) -> entity_instance: ...
def by_type(self, *args): ...
def by_type_excl_subtypes(self, *args): ...
def bypass_type(self, type_name): ...
calculate_unit_factors: bool
check_existance_before_adding: bool
def create(self, decl): ...
@staticmethod
def createTimestamp(): ...
def entity_names(self) -> tuple[int, ...]:
@@ -961,24 +987,27 @@ class file:
def get_total_inverses(self, e: entity_instance) -> int: ...
def good(self): ...
@staticmethod
def guid_map(*args): ...
def header(self) -> IfcSpfHeader:
def header(self, *args) -> IfcSpfHeader:
"""Internal IfcSpfHeader instance, always prefer ``ifcopenshell.file.header`` instead."""
def ifcroot_type(self) -> entity: ...
def internal_guid_map(self): ...
def load(self, entity_instance_name, entity, arg4, attribute_index): ...
def initialize(self, *args): ...
instantiate_typed_instances: bool
def key_value_store_iter(self, prefix): ...
def key_value_store_query(self, key): ...
def process_deletion_inverse(self, inst): ...
def recalculate_id_counter(self): ...
def remove(self, entity: entity_instance) -> None: ...
def remove_type_ref(self, new_entity): ...
def reset_identity_cache(self): ...
@property
def schema(self): ...
def storage_mode(self): ...
def to_string(self): ...
@staticmethod
def traverse(instance: entity_instance, max_level: int) -> tuple[entity_instance, ...]: ...
@staticmethod
def traverse_breadth_first(instance: entity_instance, max_level: int) -> tuple[entity_instance, ...]: ...
def try_read_semicolon(self): ...
def types(self) -> tuple[str, ...]:
"""Return a tuple of classes present in the file.
@@ -986,16 +1015,15 @@ class file:
"""
...
def types_begin(self): ...
def types_end(self): ...
def write(self, fn): ...
class file_open_status:
SUCCESS: Any
READ_ERROR: Any
NO_HEADER: Any
UNSUPPORTED_SCHEMA: Any
INVALID_SYNTAX: Any
SUCCESS: int
READ_ERROR: int
NO_HEADER: int
UNSUPPORTED_SCHEMA: int
INVALID_SYNTAX: int
UNKNOWN: int
def value(self): ...
class fn_evaluator:
@@ -1037,9 +1065,6 @@ class geometry_conversion_result:
products_2: Any
representation: Any
class geometry_exception:
def what(self): ...
class gradient_function(function_item):
def calc_hash(self): ...
def clone_(self): ...
@@ -1138,6 +1163,8 @@ class matrix4(item):
def is_identity(self): ...
def kind(self): ...
def translation_part(self): ...
def post_multiply_scale(self, s): ...
def pre_multiply_scale(self, s): ...
class named_type(parameter_type):
def _is(self, *args): ...
@@ -1516,13 +1543,11 @@ class sweep(geom_item):
class sweep_along_curve(sweep):
curve: Any
surface: Any
direction: Any
def calc_hash(self): ...
def clone_(self): ...
def kind(self): ...
class too_many_faces_exception(geometry_exception): ...
class topology_error: ...
class torus(surface):
radius1: Any
radius2: Any
@@ -1568,6 +1593,8 @@ class type_declaration(declaration):
def as_type_declaration(self) -> type_declaration: ...
def declared_type(self): ...
class uninitialized_tag: ...
def arrange_polygons(polygons): ...
def clear_schemas(): ...
def construct_iterator_with_include_exclude(geometry_library, settings, file, elems, include, num_threads): ...
@@ -1581,6 +1608,7 @@ def flatten(deep): ...
def get_feature(x): ...
def get_info_cpp(v, include_identifier): ...
def get_log(): ...
def guess_file_type(fn): ...
def helmert_curve_point(A0, A1, A2, s): ...
def kind_to_string(k): ...
def less(arg1, arg2): ...
@@ -1588,7 +1616,7 @@ def line_segments_to_polygons(s, eps, segments): ...
def map_shape(settings, instance): ...
def nary_union(sequence): ...
def new_IfcBaseClass(schema_identifier: str, name: str) -> entity_instance: ...
def open(fn): ...
def open(fn, readonly): ...
def parse_ifcxml(filename): ...
def polygons_to_svg(*args): ...
def read(data): ...
@@ -1599,6 +1627,7 @@ def serialise(schema_name, shape_str, advanced): ...
def set_feature(x, v): ...
def set_log_format_json(): ...
def set_log_format_text(): ...
def stream_from_string(data): ...
def svg_to_line_segments(data, class_name): ...
def svg_to_polygons(data, class_name): ...
def taxonomy_item_repr(i): ...
@@ -53,6 +53,8 @@ def get_brick_type(element: ifcopenshell.entity_instance) -> Union[str, None]:
if not result:
result = ifc4_to_brick_map.get(ifc_type_class, None)
if result:
if result.startswith("http"):
return result
return f"https://brickschema.org/schema/Brick#{result}"
# Generic fallback
if element.is_a("IfcDistributionElement"):
@@ -474,7 +474,7 @@ def get_elements_by_pset(pset: ifcopenshell.entity_instance) -> set[ifcopenshell
"""Retrieve the elements (or element types) that are using the provided property set."""
is_ifc2x3 = pset.file.schema == "IFC2X3"
elements = set()
if pset.is_a("IfcPropertySet") or pset.is_a("IfcElementQuantity"):
if pset.is_a("IfcPropertySet") or pset.is_a("IfcPreDefinedPropertySet") or pset.is_a("IfcElementQuantity"):
rels = pset.PropertyDefinitionOf if is_ifc2x3 else pset.DefinesOccurrence
for rel in rels:
elements.update(rel.RelatedObjects)
@@ -27,10 +27,9 @@
"IfcFlowMeter.WATERMETER": "Water_Meter",
"IfcElectricMotor": "Motor",
"IfcSolarDevice.SOLARPANEL": "PV_Panel",
"IfcBuilding": "Building",
"IfcBuildingStorey": "Floor",
"IfcSpace": "Space",
"IfcSpatialZone": "Zone",
"IfcSpatialZone.THERMAL": "HVAC_Zone",
"IfcSpatialZone.LIGHTING": "Lighting_Zone"
"IfcBuilding": "https://w3id.org/rec#Building",
"IfcBuildingStorey": "https://w3id.org/rec#Level",
"IfcSpace": "https://w3id.org/rec#Room",
"IfcSpatialZone": "https://w3id.org/rec#Zone",
"IfcSpatialZone.THERMAL": "https://w3id.org/rec#HVACZone"
}
@@ -856,8 +856,10 @@ class FacetTransformer(lark.Transformer):
comparison, value = args
def filter_function(element: ifcopenshell.entity_instance) -> bool:
element_value = getattr(ifcopenshell.util.element.get_type(element), "Name", None)
return self.compare(element_value, comparison, value)
element_type = ifcopenshell.util.element.get_type(element)
return self.compare(getattr(element_type, "Name", None), comparison, value) or self.compare(
getattr(element_type, "GlobalId", None), comparison, value
)
self.add_default_elements()
self.elements = set(filter(filter_function, self.elements))
@@ -940,7 +942,7 @@ class FacetTransformer(lark.Transformer):
containers = self.get_container_tree(container)
result = False if containers else None
for container in containers:
if self.compare(container.Name, "=", value):
if self.compare(container.Name, "=", value) or self.compare(container.GlobalId, "=", value):
result = True
if result is not None:
return result if comparison == "=" else not result
@@ -958,6 +960,8 @@ class FacetTransformer(lark.Transformer):
if rel.is_a("IfcRelAssignsToGroup") and rel.RelatingGroup:
if self.compare(rel.RelatingGroup.Name, "=", value):
result = True
elif self.compare(rel.RelatingGroup.GlobalId, "=", value):
result = True
return result if comparison == "=" else not result
self.add_default_elements()
@@ -969,32 +973,44 @@ class FacetTransformer(lark.Transformer):
parents = set()
for rel in self.file.by_type("IfcRelAggregates"):
parent = rel.RelatingObject
if parent and self.compare(parent.Name, comparison, value):
if parent and (
self.compare(parent.Name, comparison, value) or self.compare(parent.GlobalId, comparison, value)
):
parents.add(parent)
for rel in self.file.by_type("IfcRelContainedInSpatialStructure"):
parent = rel.RelatingStructure
if parent and self.compare(parent.Name, comparison, value):
if parent and (
self.compare(parent.Name, comparison, value) or self.compare(parent.GlobalId, comparison, value)
):
parents.add(parent)
for rel in self.file.by_type("IfcRelNests"):
parent = rel.RelatingObject
if parent and self.compare(parent.Name, comparison, value):
if parent and (
self.compare(parent.Name, comparison, value) or self.compare(parent.GlobalId, comparison, value)
):
parents.add(parent)
for rel in self.file.by_type("IfcRelVoidsElement"):
parent = rel.RelatingBuildingElement
if parent and self.compare(parent.Name, comparison, value):
if parent and (
self.compare(parent.Name, comparison, value) or self.compare(parent.GlobalId, comparison, value)
):
parents.add(parent)
for rel in self.file.by_type("IfcRelVoidsElement"):
parent = rel.RelatingBuildingElement
if parent and self.compare(parent.Name, comparison, value):
if parent and (
self.compare(parent.Name, comparison, value) or self.compare(parent.GlobalId, comparison, value)
):
parents.add(parent)
for rel in self.file.by_type("IfcRelFillsElement"):
parent = rel.RelatingOpeningElement
if parent and self.compare(parent.Name, comparison, value):
if parent and (
self.compare(parent.Name, comparison, value) or self.compare(parent.GlobalId, comparison, value)
):
parents.add(parent)
children: set[ifcopenshell.entity_instance] = set()
@@ -1,3 +1,8 @@
import functools
import itertools
import multiprocessing
import operator
import os
import pytest
import test.bootstrap
import ifcopenshell
@@ -12,6 +17,8 @@ import ifcopenshell.util.shape
from ifcopenshell.util.shape_builder import ShapeBuilder
from typing import get_args
fn = os.path.join(os.path.dirname(__file__), "fixtures/ColumnPSetsOfSets.ifc")
class TestGeomSettings:
def test_settings(self):
@@ -185,6 +192,28 @@ class TestAssignObject:
assert len(set(vs)) == 12
def test_iterator():
# just test some permutations of invocation
settings = ifcopenshell.geom.settings()
file_or_filename = [fn, ifcopenshell.open(fn)]
with_or_without_threads = [[], [multiprocessing.cpu_count()]]
includes = [
{},
{"include": ["IfcColumn"]},
{"include": [file_or_filename[1].by_type("IfcColumn")[0]]},
]
for args in itertools.product(file_or_filename, with_or_without_threads, includes):
kwargs = functools.reduce(operator.or_, (a for a in args if isinstance(a, dict)))
pargs = []
for a in (_ for _ in args if not isinstance(_, dict)):
if isinstance(a, list):
pargs.extend(a)
else:
pargs.append(a)
iterator = ifcopenshell.geom.iterator(settings, *pargs, **kwargs)
assert iterator.initialize()
if __name__ == "__main__":
import pytest
@@ -196,6 +196,7 @@ class TestFilterElements(test.bootstrap.IFC4):
assert subject.filter_elements(self.file, "IfcWall, type=Foo") == {element}
assert subject.filter_elements(self.file, 'IfcWall, type="Foo"') == {element}
assert subject.filter_elements(self.file, "IfcWall, type=/Fo.*/") == {element}
assert subject.filter_elements(self.file, f"IfcWall, type={element_type.GlobalId}") == {element}
def test_selecting_by_material(self):
element = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcWall")
@@ -273,6 +274,7 @@ class TestFilterElements(test.bootstrap.IFC4):
assert subject.filter_elements(self.file, "IfcWall, location=G") == {element, element2}
assert subject.filter_elements(self.file, "IfcWall, location=Building") == {element, element2}
assert subject.filter_elements(self.file, "IfcWall, location!=Space") == {element2}
assert subject.filter_elements(self.file, f"IfcWall, location={space.GlobalId}") == {element}
def test_selecting_by_group(self):
element = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcWall")
@@ -281,6 +283,7 @@ class TestFilterElements(test.bootstrap.IFC4):
ifcopenshell.api.group.assign_group(self.file, products=[element], group=group)
assert subject.filter_elements(self.file, "IfcWall, group=Foo") == {element}
assert subject.filter_elements(self.file, "IfcWall, group!=Foo") == {element2}
assert subject.filter_elements(self.file, f"IfcWall, group={group.GlobalId}") == {element}
def test_selecting_by_parent(self):
element = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcWall", name="Element1")
@@ -300,6 +303,7 @@ class TestFilterElements(test.bootstrap.IFC4):
assert subject.filter_elements(self.file, "IfcWall, parent=Space") == {element}
assert subject.filter_elements(self.file, "IfcWall, parent=G") == {element, element2, element3}
assert subject.filter_elements(self.file, "IfcWall, parent=Element2") == {element3}
assert subject.filter_elements(self.file, "IfcWall, parent=Space") == {element}
def test_selecting_multiple_filter_groups(self):
element = ifcopenshell.api.root.create_entity(self.file, ifc_class="IfcWall")
+5
View File
@@ -628,6 +628,7 @@ struct ShapeRTTI : public boost::static_visitor<PyObject*>
$result = boost::apply_visitor(ShapeRTTI(), (boost::variant<IfcGeom::Element*, IfcGeom::Representation::Representation*, IfcGeom::Transformation*>) $1);
}
%newobject construct_iterator;
%newobject construct_iterator_with_include_exclude;
%newobject construct_iterator_with_include_exclude_globalid;
%newobject construct_iterator_with_include_exclude_id;
@@ -635,6 +636,10 @@ struct ShapeRTTI : public boost::static_visitor<PyObject*>
// I couldn't get the vector<string> typemap to be applied when %extending Iterator constructor.
// anyway it does not matter as SWIG generates C code without actual constructors
%inline %{
IfcGeom::Iterator* construct_iterator(const std::string& geometry_library, ifcopenshell::geometry::Settings settings, IfcParse::IfcFile* file, int num_threads) {
return new IfcGeom::Iterator(ifcopenshell::geometry::kernels::construct(file, geometry_library, settings), settings, file, num_threads);
}
IfcGeom::Iterator* construct_iterator_with_include_exclude(const std::string& geometry_library, ifcopenshell::geometry::Settings settings, IfcParse::IfcFile* file, std::vector<std::string> elems, bool include, int num_threads) {
std::set<std::string> elems_set(elems.begin(), elems.end());
IfcGeom::entity_filter ef{ include, false, elems_set };
+4
View File
@@ -33,6 +33,10 @@ private:
%ignore IfcParse::IfcFile::types_end;
%ignore IfcParse::IfcFile::internal_guid_map;
%ignore IfcParse::IfcFile::storage_;
%ignore IfcParse::IfcFile::byguid_;
%ignore IfcParse::IfcFile::byid_;
%ignore IfcParse::IfcFile::byref_excl_;
%ignore IfcParse::IfcFile::types_to_bypass_loading_;
%ignore IfcParse::InstanceStreamer::InstanceStreamer(const IfcParse::schema_definition* schema, IfcParse::IfcSpfLexer* lexer);
+4
View File
@@ -93,6 +93,10 @@
%ignore curve_to_face_upgrade_impl;
%ignore loop_to_function_item_upgrade_impl;
%ignore IfcGeom::geometry_exception;
%ignore IfcGeom::too_many_faces_exception;
%ignore ifcopenshell::geometry::taxonomy::topology_error;
// settings, can this done more generally?
// GeometrySerializer.h
%ignore UseElementNames;
+1 -1
View File
@@ -3,7 +3,7 @@ from starlette.background import BackgroundTask
from starlette.responses import StreamingResponse
from fastapi.routing import APIRoute
from starlette.types import Message
from typing import Callable
from collections.abc import Callable
import logging
import httpx