Compare commits

...

20 Commits

Author SHA1 Message Date
Bruno Postle 92f9bd5938 Fix null reference bind in header parsing
references_to_resolve is never set while parsing header
entities, so binding a reference to it was UB, caught by
UBSan on any file with a header.

Generated with the assistance of an AI coding tool.
2026-07-18 00:39:25 +01:00
Andrej730 71a598e63a ifcopenshell.file: small wording fix 2026-07-17 21:55:22 +05:00
Andrej730 3d8115ebc5 ifcopenshell.file: drop workarounds for older builds
Introduced in aeed371 and it's been a while.
2026-07-17 21:55:22 +05:00
Ryan Schultz b5a0f1fc74 Bonsai: allow cross-family class reassignment for spatial elements with geometry (#8665)
The Reassign Class operator refused to reassign an element to a different
IFC product family unless it was an IfcElement <-> IfcElementType swap, so a
piece of geometry mistakenly hosted on IfcSite could not be turned into
IfcFurniture even though root.reassign_class handles it fine.

Loosen the guard: only block the case that actually matters - a spatial
element (IfcSpatialElement / IfcSpatialStructureElement for IFC2X3) with no
geometry, which would be a real containment-hierarchy container rather than
a stray modelled object. Everything else reassigns freely.

Closes #8664

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 18:57:11 -05:00
Petru Conduraru 25441bd816 Bonsai: make 'has openings' representation error actionable (#8108)
When converting a wall representation to a parametric extrusion via the
Representation Utilities buttons, an element that has openings would report
"has openings - representation cannot be updated" and stop, without telling
the user there is an ALT+click path that bakes the openings into the new
representation. Point the message at that path so the error is actionable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 21:35:46 +02:00
Petru Conduraru 65811ac7c9 Bonsai: place auto-generated opening boundaries at their real position (#8237) (#8311)
* Bonsai: place auto-generated opening boundaries at their real position #8237

auto_generate_boundaries (single-space mode) built each opening/filling boundary
from the opening's LOCAL geometry (get_vertices) but first did
mat.translation = (0, 0, 0) on its placement matrix. Because the vertices are
local, that placement translation is exactly what carries the opening to its
real location, so zeroing it collapsed every window/door boundary onto the
origin. This is why the auto path misplaced window boundaries while the
single-element path (create_element_boundary) placed them correctly, as
@MDHering observed with the two modes. Keep the full placement matrix.

Verified on the reporter's file: the opening's real placement is (0.1, 1.5, 1.0);
a vertex went from (0.6, 0, 0) under the old code to (0.7, 1.5, 1.0) with the fix,
i.e. moved by exactly the (0.1, 1.5, 1.0) that was being discarded.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Remove superfluous comment from #8237 fix

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: CyrilWaechter <cyril@biminsight.ch>
2026-07-16 20:17:02 +02:00
Andrej730 d9d1824886 test-package: drop stale comment
This information is already documented in maintanence.rst.
2026-07-16 19:03:35 +05:00
Andrej730 16e5f18553 Bump build 3e7b739 -> 821cf7b
Just to test everything is working with the changes from the last month.
2026-07-16 18:59:44 +05:00
Andrej730 b7a9b7bc5a test-package: assert BUILD_COMMIT is a 7-char short SHA 2026-07-16 18:56:44 +05:00
Andrej730 e14397058d test-package: verify build URLs with HEAD requests instead of scraping listing page 2026-07-16 18:53:54 +05:00
Andrej730 f0117c60b3 stub: sync added/removed symbols 2026-07-16 18:09:32 +05:00
Andrej730 0e5223a30d stub: add missing arrange_polygon_settings (158756e921) 2026-07-16 18:00:55 +05:00
Andrej730 bc41ff78f4 stub: drop abstract_arrangement (158756e921)
And also gnore delete_same_facet_edge_pairs as it's more of an interanl API.
2026-07-16 18:00:47 +05:00
Andrej730 9123d8c183 stub: add missing entity.inverse_attributes 2026-07-16 17:35:52 +05:00
Andrej730 ffd939508c ci-lint: run ty-bonsai and ty-ios as separate steps
So if one fails, it wouldn't block another.
Noticed by Stephen in d5e890bccd
2026-07-16 17:28:24 +05:00
Andrej730 9213b31235 logger: reuse logger_or_root, dedupe optional-logger-arg pattern 2026-07-16 17:28:24 +05:00
Andrej730 2155e3206f logger: use Logger* instead of Logger& to propagate signature using swig
See the comment in IfcLogger.h explaining this.
2026-07-16 17:28:24 +05:00
Andrej730 9e0c6cf524 util.schema: dedupe inline schema resolution logic 2026-07-16 17:28:23 +05:00
Andrej730 d5dc069b2f util.schema: fix geometry_classes_introduced_after using wrong IFC4X3 schema
It was passing `IFC4X3` directly to `schema_by_name` which is expecting
schema identifier (e.g. IFC4X3_ADD2, not IFC4X3 allowed by `IFC_SCHEMA`
- IFC4X3 is one of the IFC4X3 iterations while it was in development,
not the final one).

Noticed by tests failing:
FAILED
test/util/test_schema.py::TestGeometryClassesIntroducedAfter::test_ifc4x3_to_ifc2x3_is_superset_of_ifc4_to_ifc2x3
- RuntimeError: No schema named IFC4X3
FAILED
test/util/test_schema.py::TestGeometryClassesIntroducedAfter::test_ifc4_to_ifc4x3_is_empty
- RuntimeError: No schema named IFC4X3
2026-07-16 17:28:23 +05:00
Andrej730 821cf7b671 ifcparse: replace std::to_chars to fix mac build (ee2b357d7) 2026-07-16 11:57:37 +05:00
34 changed files with 362 additions and 168 deletions
+16 -7
View File
@@ -55,11 +55,17 @@ jobs:
black --diff --check . | black-codeclimate | python .github/workflows/black_to_github_annotations.py
continue-on-error: true
- name: ty check
id: ty
run: |
poe ty-venv
poe ty
- name: ty check (venv setup)
run: poe ty-venv
- name: ty check (bonsai)
id: ty-bonsai
run: poe ty-bonsai
continue-on-error: true
- name: ty check (ios)
id: ty-ios
run: poe ty-ios
continue-on-error: true
- name: Ruff check
@@ -109,7 +115,10 @@ jobs:
if [ "${{ steps.ruff.outcome }}" != "success" ]; then
echo "::error::Ruff check failed, see Summary or 'ruff' step for the details." && ERROR=1
fi
if [ "${{ steps.ty.outcome }}" != "success" ]; then
echo "::error::ty check failed, see 'ty check' step for the details." && ERROR=1
if [ "${{ steps.ty-bonsai.outcome }}" != "success" ]; then
echo "::error::ty check (bonsai) failed, see 'ty check (bonsai)' step for the details." && ERROR=1
fi
if [ "${{ steps.ty-ios.outcome }}" != "success" ]; then
echo "::error::ty check (ios) failed, see 'ty check (ios)' step for the details." && ERROR=1
fi
exit $ERROR
@@ -843,7 +843,6 @@ class AddBoundary(bpy.types.Operator, tool.Ifc.Operator):
settings = ifcopenshell.geom.settings()
shape = ifcopenshell.geom.create_shape(settings, opening)
mat = Matrix(ifcopenshell.util.shape.get_shape_matrix(shape))
mat.translation = (0, 0, 0)
opening_bm = bmesh.new()
verts = ifcopenshell.util.shape.get_vertices(shape.geometry)
for vert in verts:
@@ -581,7 +581,11 @@ class UpdateRepresentation(bpy.types.Operator, tool.Ifc.Operator):
if has_openings and not self.apply_openings:
# Meshlike things with openings can only be updated without openings applied.
if self.from_ui:
self.report({"ERROR"}, f"Object '{obj.name}' has openings - representation cannot be updated.")
self.report(
{"ERROR"},
f"Object '{obj.name}' has openings. "
"ALT+click the button to bake the openings into the new representation.",
)
return
if not product.is_a("IfcGridAxis"):
+19 -6
View File
@@ -27,6 +27,7 @@ import ifcopenshell.api.material
import ifcopenshell.api.pset
import ifcopenshell.api.root
import ifcopenshell.util.element
import ifcopenshell.util.representation
import ifcopenshell.util.schema
import ifcopenshell.util.shape_builder
import ifcopenshell.util.type
@@ -129,13 +130,25 @@ class ReassignClass(bpy.types.Operator, tool.Ifc.Operator):
same_ifc_product = element.is_a(ifc_product)
if not same_ifc_product:
if not (element.is_a("IfcElement") and ifc_product == "IfcElementType") and not (
element.is_a("IfcElementType") and ifc_product == "IfcElement"
):
self.report(
{"ERROR"}, f"Not supported class reassignment for object '{obj.name}' -> {ifc_product}."
# A spatial element (e.g. IfcSite) anchors the containment
# hierarchy, so only allow reassigning it to another family when
# it actually carries geometry - i.e. it's a real modelled thing
# (a bench dropped onto IfcSite -> IfcFurniture) rather than an
# empty spatial container we'd be turning into a loose element.
# IfcSpatialStructureElement covers IFC2X3, which has no
# IfcSpatialElement supertype.
is_spatial = element.is_a("IfcSpatialElement") or element.is_a("IfcSpatialStructureElement")
if is_spatial:
has_geometry = (
next(ifcopenshell.util.representation.get_representations_iter(element), None) is not None
)
return {"CANCELLED"}
if not has_geometry:
self.report(
{"ERROR"},
f"Cannot reassign '{obj.name}' ({element.is_a()}) to {ifc_product}: "
"a spatial element can only be reassigned to another class when it has geometry.",
)
return {"CANCELLED"}
props = tool.Blender.get_object_bim_props(obj)
props.is_reassigning_class = False
+10 -10
View File
@@ -675,7 +675,7 @@ int main(int argc, char** argv) {
time_t start, end;
time(&start);
if (output_extension == XML) {
XmlSerializer s(ifc_file, IfcUtil::path::to_utf8(output_temp_filename), logger);
XmlSerializer s(ifc_file, IfcUtil::path::to_utf8(output_temp_filename), &logger);
logger.Status("Writing XML output...");
s.finalize();
} else {
@@ -834,14 +834,14 @@ int main(int argc, char** argv) {
if (output_extension == OBJ) {
// Do not use temp file for MTL as it's such a small file.
const path_t mtl_filename = change_extension(output_filename, MTL);
serializer = boost::make_shared<WaveFrontOBJSerializer>(IfcUtil::path::to_utf8(output_temp_filename), IfcUtil::path::to_utf8(mtl_filename), geometry_settings, serializer_settings, logger);
serializer = boost::make_shared<WaveFrontOBJSerializer>(IfcUtil::path::to_utf8(output_temp_filename), IfcUtil::path::to_utf8(mtl_filename), geometry_settings, serializer_settings, &logger);
#ifdef WITH_OPENCOLLADA
} else if (output_extension == DAE) {
serializer = boost::make_shared<ColladaSerializer>(IfcUtil::path::to_utf8(output_temp_filename), geometry_settings, serializer_settings, logger);
serializer = boost::make_shared<ColladaSerializer>(IfcUtil::path::to_utf8(output_temp_filename), geometry_settings, serializer_settings, &logger);
#endif
#ifdef WITH_GLTF
} else if (output_extension == GLB) {
serializer = boost::make_shared<GltfSerializer>(IfcUtil::path::to_utf8(output_temp_filename), geometry_settings, serializer_settings, logger);
serializer = boost::make_shared<GltfSerializer>(IfcUtil::path::to_utf8(output_temp_filename), geometry_settings, serializer_settings, &logger);
#endif
#ifdef WITH_USD
} else if (output_extension == USD || output_extension == USDA || output_extension == USDC) {
@@ -858,15 +858,15 @@ int main(int argc, char** argv) {
serializer = boost::make_shared<IgesSerializer>(IfcUtil::path::to_utf8(output_temp_filename), geometry_settings, serializer_settings, logger);
} else if (output_extension == SVG) {
geometry_settings.get<ifcopenshell::geometry::settings::IteratorOutput>().value = ifcopenshell::geometry::settings::NATIVE;
serializer = boost::make_shared<SvgSerializer>(IfcUtil::path::to_utf8(output_temp_filename), geometry_settings, serializer_settings, logger);
serializer = boost::make_shared<SvgSerializer>(IfcUtil::path::to_utf8(output_temp_filename), geometry_settings, serializer_settings, &logger);
#ifdef WITH_HDF5
} else if (output_extension == HDF) {
geometry_settings.get<ifcopenshell::geometry::settings::IteratorOutput>().value = ifcopenshell::geometry::settings::NATIVE;
serializer = boost::make_shared<HdfSerializer>(IfcUtil::path::to_utf8(output_temp_filename), geometry_settings, serializer_settings, false, logger);
serializer = boost::make_shared<HdfSerializer>(IfcUtil::path::to_utf8(output_temp_filename), geometry_settings, serializer_settings, false, &logger);
#endif
#endif
#endif
} else if (output_extension == TTL) {
serializer = boost::make_shared<TtlWktSerializer>(IfcUtil::path::to_utf8(output_temp_filename), geometry_settings, serializer_settings, logger);
serializer = boost::make_shared<TtlWktSerializer>(IfcUtil::path::to_utf8(output_temp_filename), geometry_settings, serializer_settings, &logger);
} else {
cerr_ << "[Error] Unknown output filename extension '" << output_extension << "'\n";
write_log(!quiet);
@@ -1011,7 +1011,7 @@ int main(int argc, char** argv) {
if (!vmap.count("cache-file")) {
cache_file = input_filename + CACHE + HDF;
}
cache.reset(new HdfSerializer(IfcUtil::path::to_utf8(cache_file), geometry_settings, serializer_settings, false, logger));
cache.reset(new HdfSerializer(IfcUtil::path::to_utf8(cache_file), geometry_settings, serializer_settings, false, &logger));
context_iterator->set_cache(cache.get());
}
#endif
@@ -1290,7 +1290,7 @@ bool init_input_file(const std::string& filename, IfcParse::IfcFile*& ifc_file,
#ifdef WITH_IFCXML
if (boost::ends_with(boost::to_lower_copy(filename), ".ifcxml")) {
ifc_file = IfcParse::parse_ifcxml(filename, logger);
ifc_file = IfcParse::parse_ifcxml(filename, &logger);
} else
#endif
{
+1 -1
View File
@@ -55,7 +55,7 @@ PLATFORMTAG:=win_amd64
endif
BINARY_VERSION:=0.8.6
BUILD_COMMIT:=3e7b739
BUILD_COMMIT:=821cf7b
IOS_URL:=https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-python-$(PYNUMBER)-v$(BINARY_VERSION)-$(BUILD_COMMIT)-$(PLATFORM).zip
IFCCONVERT_URL:=https://s3.amazonaws.com/ifcopenshell-builds/IfcConvert-v$(BINARY_VERSION)-$(BUILD_COMMIT)-$(PLATFORM).zip
@@ -97,7 +97,19 @@ from .file import file as _file
from .sql import sqlite, sqlite_entity
get_log = ifcopenshell_wrapper.get_log
logger = getattr(ifcopenshell_wrapper, "logger", None)
logger = ifcopenshell_wrapper.logger if hasattr(ifcopenshell_wrapper, "logger") else None
if hasattr(ifcopenshell_wrapper, "logger_or_root"):
logger_or_root = ifcopenshell_wrapper.logger_or_root
else:
def logger_or_root(_logger: ifcopenshell_wrapper.logger | None) -> None:
return None
# TODO: drop this function and all callsites after we migrate to the new build.
def optional_logger_args(logger: ifcopenshell_wrapper.logger | None) -> tuple[logger] | tuple[()]:
return (logger,) if logger is not None else ()
# explicitly specify available imported symbols
# (it's a requirement for a typed library)
@@ -194,10 +206,9 @@ def open(
raise FileNotFoundError(f"Path does not exist: '{path}'.")
if format is None:
format = guess_format(path)
if logger is None and (logger_type := getattr(ifcopenshell_wrapper, "logger", None)):
logger = logger_type.Root()
logger = logger_or_root(logger)
if format == ".ifcXML":
f = ifcopenshell_wrapper.parse_ifcxml(str(path.absolute()), *((logger,) if logger is not None else ()))
f = ifcopenshell_wrapper.parse_ifcxml(str(path.absolute()), *optional_logger_args(logger))
if f:
return file(f)
raise OSError(f"Failed to parse .ifcXML file from {path}")
@@ -214,11 +225,9 @@ def open(
if should_stream:
return stream(path)
if readonly: # Temporary conditional see #7131. Remove once newer builds don't segfault on Linux.
f = ifcopenshell_wrapper.open(str(path.absolute()), readonly, *((logger,) if logger is not None else ()))
f = ifcopenshell_wrapper.open(str(path.absolute()), readonly, *optional_logger_args(logger))
elif bypass_types:
f = ifcopenshell_wrapper.file(
ifcopenshell_wrapper.uninitialized_tag(), *((logger,) if logger is not None else ())
)
f = ifcopenshell_wrapper.file(ifcopenshell_wrapper.uninitialized_tag(), *optional_logger_args(logger))
for ty in bypass_types:
f.bypass_type(ty)
if mmap:
@@ -233,7 +242,7 @@ def open(
kwargs["logger"] = logger
f = ifcopenshell_wrapper.open(str(path.absolute()), **kwargs)
else:
f = ifcopenshell_wrapper.open(str(path.absolute()), False, *((logger,) if logger is not None else ()))
f = ifcopenshell_wrapper.open(str(path.absolute()), False, *optional_logger_args(logger))
return file(f)
@@ -305,12 +314,13 @@ def schema_by_name(
you are testing non-ISO IFC releases.
:return: Schema definition object.
"""
import ifcopenshell.util.schema
assert schema_version or schema, "Either schema or schema_version must be specified."
if schema_version:
prefixes = ("IFC", "X", "_ADD", "_TC")
schema = "".join("".join(map(str, t)) if t[1] else "" for t in zip(prefixes, schema_version))
schema = ifcopenshell.util.schema.get_schema_name_from_version(schema_version)
else:
schema = {"IFC4X3": "IFC4X3_ADD2"}.get(schema, schema)
schema = ifcopenshell.util.schema.get_schema_identifier(schema)
return ifcopenshell_wrapper.schema_by_name(schema)
+1 -2
View File
@@ -107,8 +107,7 @@ def main(
progress_function: Callable = DO_NOTHING,
logger=None,
):
if logger is None and ifcopenshell.logger is not None:
logger = ifcopenshell.logger.Root()
logger = ifcopenshell.logger_or_root(logger)
def by_guid(g):
for f in files:
+7 -17
View File
@@ -23,7 +23,6 @@ import numbers
import os
import re
import time
import types
import weakref
import zipfile
from collections.abc import Callable, Generator
@@ -244,19 +243,14 @@ file_dict: dict[int, tuple[weakref.ReferenceType[file], int]] = {}
"""Mapping of internal IfcFile pointer address to existing ``ifcopenshell.file``
and the timestamp when it was created.
Needed only to quickly access related from ``entity_instance`` it's ``file``.
Needed only to quickly access from ``entity_instance`` its ``file``.
"""
READ_ERROR = ifcopenshell_wrapper.file_open_status.READ_ERROR
NO_HEADER = ifcopenshell_wrapper.file_open_status.NO_HEADER
UNSUPPORTED_SCHEMA = ifcopenshell_wrapper.file_open_status.UNSUPPORTED_SCHEMA
INVALID_SYNTAX = ifcopenshell_wrapper.file_open_status.INVALID_SYNTAX
# TODO: Workaround for old builds, remove after build stabilizes.
try:
UNKNOWN = ifcopenshell_wrapper.file_open_status.UNKNOWN
except:
UNKNOWN = 5 # Workaround
UNKNOWN = ifcopenshell_wrapper.file_open_status.UNKNOWN
import struct
@@ -594,11 +588,12 @@ class file:
# A poweruser testing out a particular version of IFC4X3
model = ifcopenshell.file(schema_version=(4, 3, 0, 1))
"""
import ifcopenshell.util.schema
if schema_version:
prefixes = ("IFC", "X", "_ADD", "_TC")
schema = "".join("".join(map(str, t)) if t[1] else "" for t in zip(prefixes, schema_version))
schema = ifcopenshell.util.schema.get_schema_name_from_version(schema_version)
else:
schema = {"IFC4X3": "IFC4X3_ADD2"}.get(schema, schema)
schema = ifcopenshell.util.schema.get_schema_identifier(schema)
if f is not None:
self.wrapped_data = f
if not f.good():
@@ -1065,13 +1060,8 @@ class file:
@property
def header(self) -> file_header:
# TODO: Workaround for old builds, remove after build stabilizes.
# TODO: No need for `wrapped_data.header` to be a method - should use `@property`?
header = self.wrapped_data.header
if isinstance(header, types.MethodType):
return file_header(self, self.wrapped_data.header())
else:
return self.wrapped_data.header
return file_header(self, self.wrapped_data.header())
@property
def storage(self) -> Optional[rocksdb_file_storage]:
@@ -22,6 +22,8 @@ from __future__ import annotations
from collections.abc import Generator, Iterable
from typing import TYPE_CHECKING, Any, Literal, Optional, TypeVar, Union, cast, overload
import ifcopenshell
from .. import ifcopenshell_wrapper, open
from ..entity_instance import entity_instance
from ..file import file
@@ -302,8 +304,7 @@ class iterator(ifcopenshell_wrapper.Iterator):
logger=None,
):
self.settings = settings
if logger is None and (logger_type := getattr(ifcopenshell_wrapper, "logger", None)):
logger = logger_type.Root()
logger = ifcopenshell.logger_or_root(logger)
if isinstance(file_or_filename, file):
self.file = file
file_or_filename = file_or_filename.wrapped_data
@@ -344,10 +345,10 @@ class iterator(ifcopenshell_wrapper.Iterator):
include is not None,
num_threads,
)
self.this = initializer(*args, *((logger,) if logger is not None else ()))
self.this = initializer(*args, *ifcopenshell.optional_logger_args(logger))
else:
args = (geometry_library, self.settings, file_or_filename, num_threads)
self.this = ifcopenshell_wrapper.construct_iterator(*args, *((logger,) if logger is not None else ()))
self.this = ifcopenshell_wrapper.construct_iterator(*args, *ifcopenshell.optional_logger_args(logger))
if has_occ:
@@ -16,7 +16,7 @@
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
from typing import Any, Literal, Union
from typing import Any, Literal, Sequence, Union
from typing_extensions import Self
@@ -105,10 +105,12 @@ class IfcSpfHeader:
"""
def __init__(self, *args): ...
def assign(self, other): ...
def file(self, *args): ...
def file_description_py(self): ...
def file_name_py(self): ...
def file_schema_py(self): ...
def logger(self) -> logger: ...
def read(self): ...
def tryRead(self): ...
def write(self, out): ...
@@ -138,7 +140,7 @@ class BRepElement(Element):
def volume(self): ...
class ColladaSerializer(WriteOnlyGeometrySerializer):
def __init__(self, dae_filename, geometry_settings, settings): ...
def __init__(self, dae_filename, geometry_settings, settings, logger=None): ...
def finalize(self): ...
def isTesselated(self): ...
def object_id(self, o): ...
@@ -194,6 +196,7 @@ class ConversionResultShape:
def subtract(self, arg2): ...
def surface_area_along_direction(self, tol, arg3, along_x, along_y, along_z): ...
def surface_genus(self): ...
def type(self) -> str: ...
def vertices(self): ...
def volume(self): ...
def wrap_in_compound(self): ...
@@ -284,7 +287,7 @@ class GeometrySerializer:
def write(self, *args): ...
class GltfSerializer(WriteOnlyGeometrySerializer):
def __init__(self, filename, geometry_settings, settings): ...
def __init__(self, filename, geometry_settings, settings, logger=None): ...
def finalize(self): ...
def isTesselated(self): ...
def ready(self): ...
@@ -294,7 +297,7 @@ class GltfSerializer(WriteOnlyGeometrySerializer):
def writeHeader(self): ...
class HdfSerializer(GeometrySerializer):
def __init__(self, hdf_filename, geometry_settings, settings, read_only=False): ...
def __init__(self, hdf_filename, geometry_settings, settings, read_only=False, logger=None): ...
def finalize(self): ...
def isTesselated(self): ...
def read(self, *args): ...
@@ -370,7 +373,7 @@ class Iterator:
...
def process_concurrently(self): ...
def process_finished_rep(self, rep): ...
def process_finished_rep(self, rep, kernel=None): ...
def progress(self) -> int:
"""Return current progress (0-100).
@@ -390,25 +393,41 @@ class JsonSerializer:
def setFile(self, arg2): ...
def writeHeader(self): ...
# TODO: MakeVolume is ignored in SWIG, remove from stub once build is bumped.
class MakeVolume:
defaultvalue: Any
description: Any
name: Any
class OpaqueCoordinate_3:
def __init__(self, *args): ...
def dot(self, other): ...
def get(self, i): ...
def get_double(self, i): ...
def norm(self): ...
def normalized(self): ...
def normalized_by_max_abs(self): ...
def scale(self, scalar): ...
def set(self, i, n): ...
def size(self): ...
def to_double(self): ...
class OpaqueCoordinate_4:
def __init__(self, *args): ...
def dot(self, other): ...
def get(self, i): ...
def get_double(self, i): ...
def norm(self): ...
def normalized(self): ...
def normalized_by_max_abs(self): ...
def scale(self, scalar): ...
def set(self, i, n): ...
def size(self): ...
def to_double(self): ...
class OpaqueNumber:
def __init__(self, *args, **kwargs): ...
def clone(self): ...
def abs(self): ...
def add(self, other): ...
def divide(self, other): ...
def empty(self): ...
def equals(self, other): ...
def less_than(self, other): ...
def multiply(self, other): ...
def negated(self): ...
def same_type(self, *args): ...
def subtract(self, other): ...
def to_double(self): ...
def to_string(self): ...
@@ -466,7 +485,7 @@ class Settings:
def setting_names(self): ...
class SvgSerializer(WriteOnlyGeometrySerializer):
def __init__(self, out_filename, geometry_settings, settings): ...
def __init__(self, out_filename, geometry_settings, settings, logger=None): ...
SH_NONE: Any
SH_FULL: Any
SH_LEFT: Any
@@ -609,7 +628,7 @@ class TriangulationElement(Element):
def geometry_pointer(self): ...
class TtlWktSerializer(WriteOnlyGeometrySerializer):
def __init__(self, filename, geometry_settings, settings): ...
def __init__(self, filename, geometry_settings, settings, logger=None): ...
def finalize(self): ...
def isTesselated(self): ...
def ready(self): ...
@@ -620,7 +639,7 @@ class TtlWktSerializer(WriteOnlyGeometrySerializer):
def writeHeader(self): ...
class WaveFrontOBJSerializer(WriteOnlyGeometrySerializer):
def __init__(self, obj_filename, mtl_filename, geometry_settings, settings): ...
def __init__(self, obj_filename, mtl_filename, geometry_settings, settings, logger=None): ...
def finalize(self): ...
def isTesselated(self): ...
def ready(self): ...
@@ -635,7 +654,7 @@ class WriteOnlyGeometrySerializer(GeometrySerializer):
def read(self, *args): ...
class XmlSerializer:
def __init__(self, file, xml_filename): ...
def __init__(self, file, xml_filename, logger=None): ...
def finalize(self): ...
def ready(self): ...
def setFile(self, arg2): ...
@@ -643,14 +662,6 @@ class XmlSerializer:
class _SwigNonDynamicMeta(type): ...
class abstract_arrangement:
def __init__(self, *args, **kwargs): ...
def get_face_pairs(self): ...
def merge(self, edge_indices): ...
def num_edges(self): ...
def num_faces(self): ...
def write(self, polygons, progress): ...
class aggregation_type(parameter_type):
def __init__(self, type_of_aggregation, bound1, bound2, type_of_element): ...
array_type: Any
@@ -664,6 +675,15 @@ class aggregation_type(parameter_type):
def type_of_aggregation_string(self): ...
def type_of_element(self) -> parameter_type: ...
class arrange_polygon_settings:
debug_output: bool
line_cleaning_algo: int
outer_perimiter_algo: int
perform_cleanup: bool
polygon_offset_distance: float
subdivision_factor: float
topology_reconstruction_algo: int
class attribute:
def __init__(self, name, type_of_attribute, optional): ...
def name(self) -> str: ...
@@ -854,8 +874,22 @@ class ellipse(curve):
class entity(declaration):
def __init__(self, name, is_abstract, index_in_schema, supertype): ...
def all_attributes(self) -> tuple[attribute, ...]: ...
def all_inverse_attributes(self) -> tuple[inverse_attribute, ...]: ...
def all_attributes(self) -> tuple[attribute, ...]:
"""Get a tuple of attributes, including those inherited from supertypes."""
...
def attributes(self) -> tuple[attribute, ...]:
"""Get a tuple of direct attributes."""
...
def all_inverse_attributes(self) -> tuple[inverse_attribute, ...]:
"""Get a tuple of inverse attributes, including those inherited from supertypes."""
...
def inverse_attributes(self) -> tuple[inverse_attribute, ...]:
"""Get a tuple of direct inverse attributes."""
...
def argument_types(self) -> tuple[str, ...]:
"""Get a tuple of types for each attribute in ``all_attributes()``."""
...
@@ -869,7 +903,6 @@ class entity(declaration):
"""
...
def attributes(self) -> tuple[attribute, ...]: ...
def derived(self) -> tuple[bool, ...]:
"""Return a tuple of booleans indicating whether each direct attribute is derived."""
...
@@ -1058,6 +1091,7 @@ class file:
instantiate_typed_instances: bool
def key_value_store_iter(self, prefix): ...
def key_value_store_query(self, key): ...
def logger(self) -> logger: ...
def process_deletion_inverse(self, inst): ...
def recalculate_id_counter(self): ...
def remove(self, entity: entity_instance) -> None: ...
@@ -1201,6 +1235,51 @@ class line_segment:
def size(self): ...
def swap(self, v): ...
class log_message:
code: Any
instance: Any
message: Any
product: Any
severity: Any
timestamp: Any
def __init__(self, severity, code_prefix, code_number, timestamp, message, inst=None, current_product=None): ...
@property
def severity_string(self): ...
def to_dict(self): ...
def to_tuple(self): ...
class logger:
FMT_PLAIN: Literal[0]
FMT_JSON: Literal[1]
FMT_INMEMORY: Literal[2]
LOG_PERF: Literal[0]
LOG_DEBUG: Literal[1]
LOG_NOTICE: Literal[2]
LOG_WARNING: Literal[3]
LOG_ERROR: Literal[4]
@staticmethod
def Root() -> logger: ...
def Append(self, logger): ...
def ClearLog(self): ...
def Error(self, *args): ...
def GetLog(self): ...
def MaxSeverity(self): ...
def Message(self, *args): ...
def Notice(self, *args): ...
def OutputFormat(self, *args): ...
def PrintPerformanceStats(self): ...
def PrintPerformanceStatsOnElement(self, *args): ...
def ProgressBar(self, progress): ...
def SetOutput(self, *args): ...
def SetProduct(self, product): ...
def Status(self, message, new_line=True): ...
def Verbosity(self, *args): ...
def Warning(self, *args): ...
def log_messages(self) -> tuple[log_message, ...]: ...
class loft:
axis: Any
def calc_hash(self): ...
@@ -1695,17 +1774,19 @@ class type_declaration(declaration):
class uninitialized_tag: ...
def arrange_polygons(settings, polygons): ...
def arrange_polygons(
settings: arrange_polygon_settings, polygons: Sequence[polygon_2], logger: logger | None = None
) -> tuple[polygon_2, ...]: ...
def clear_schemas(): ...
def construct_iterator(geometry_library, settings, file, num_threads, logger=...): ...
def construct_iterator(geometry_library, settings, file, num_threads, logger=None): ...
def construct_iterator_with_include_exclude(
geometry_library, settings, file, elems, include, num_threads, logger=...
geometry_library, settings, file, elems, include, num_threads, logger=None
): ...
def construct_iterator_with_include_exclude_globalid(
geometry_library, settings, file, elems, include, num_threads, logger=...
geometry_library, settings, file, elems, include, num_threads, logger=None
): ...
def construct_iterator_with_include_exclude_id(
geometry_library, settings, file, elems, include, num_threads, logger=...
geometry_library, settings, file, elems, include, num_threads, logger=None
): ...
def convert_loop_to_function_item(loop): ...
def create_box(*args): ...
@@ -1721,10 +1802,11 @@ def kind_to_string(k): ...
def less(arg1, arg2): ...
def line_segments_to_polygons(s, eps, segments): ...
def map_shape(settings, instance): ...
def logger_or_root(logger) -> logger: ...
def nary_union(sequence): ...
def new_IfcBaseClass(schema_identifier: str, name: str) -> entity_instance: ...
def open(fn: str, readonly: bool = False, logger=...) -> file: ...
def parse_ifcxml(filename, logger=...): ...
def open(fn: str, readonly: bool = False, logger=None) -> file: ...
def parse_ifcxml(filename, logger=None): ...
def polygons_to_svg(*args): ...
def read(data): ...
def register_schema(arg1): ...
@@ -50,6 +50,21 @@ def get_fallback_schema(version: str) -> IFC_SCHEMA:
return version
def get_schema_identifier(schema: IFC_SCHEMA) -> str:
"""Resolve a general schema name to the specific identifier used internally
(as in ``file.schema_identifier``).
E.g. ``IFC4X3`` -> ``IFC4X3_ADD2``.
"""
return {"IFC4X3": "IFC4X3_ADD2"}.get(schema, schema)
def get_schema_name_from_version(schema_version: tuple[int, ...]) -> str:
"""Build a schema name from a version tuple, e.g. (4, 3, 0, 1) -> "IFC4X3_TC1"."""
prefixes = ("IFC", "X", "_ADD", "_TC")
return "".join("".join(map(str, t)) if t[1] else "" for t in zip(prefixes, schema_version))
def get_declaration(element: ifcopenshell.entity_instance):
"""Get the schema declaration of an actively used entity instance
@@ -175,8 +190,8 @@ def geometry_classes_introduced_after(target_schema: IFC_SCHEMA, source_schema:
B-splines, advanced surfaces, alignment curves on IFC4X3 2X3, ) or
purge. Defaults match the IFC4 IFC2X3 case for backwards compatibility
with the original caller."""
source = ifcopenshell_wrapper.schema_by_name(source_schema)
target = ifcopenshell_wrapper.schema_by_name(target_schema)
source = ifcopenshell.schema_by_name(source_schema)
target = ifcopenshell.schema_by_name(target_schema)
target_names = {decl.name() for decl in target.entities()}
result: set[str] = set()
for decl in source.entities():
@@ -217,6 +217,7 @@ def test_iterator():
def test_logging():
assert ifcopenshell.logger
logger = ifcopenshell.logger()
logger.OutputFormat(logger.FMT_INMEMORY)
settings = ifcopenshell.geom.settings()
+48 -27
View File
@@ -17,21 +17,11 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import http.client
from collections.abc import Sequence
from pathlib import Path
from urllib.parse import urlparse
from typing_extensions import assert_never
try:
from bs4 import BeautifulSoup
except:
pass
# Where it's also reflected:
# - .github/workflows/ci-ifcopenshell-python.yml
# - .github/workflows/ci-ifcopenshell-python-pypi.yml
# - src/ifcopenshell-python/Makefile (PYVERSION check)
SUPPORTED_PY_VERSIONS = ("310", "311", "312", "313", "314")
SUPPORTED_PLATFORMS = ("win64", "linux64", "macos64", "macosm164")
@@ -41,24 +31,22 @@ WASM_TEMPLATE = "https://s3.amazonaws.com/ifcopenshell-builds/ifcopenshell-{BINA
class TestPackageSupportedPlatforms:
def test_run(self) -> None:
@staticmethod
def get_required_urls() -> list[str]:
IOS_REPO = Path(__file__).parents[3]
makefile = IOS_REPO / "src/ifcopenshell-python/Makefile"
text = makefile.read_text()
# We don't use requests in ifcopenshell, so we use Python builtin stuff.
parsed = urlparse("https://builds.ifcopenshell.org")
conn = http.client.HTTPSConnection(parsed.netloc)
conn.request("GET", parsed.path)
response = conn.getresponse()
build_html = response.read().decode("utf-8")
def find_make_var(var_name: str) -> str:
line = next(l for l in text.splitlines() if l.startswith(f"{var_name}:="))
return line.partition(":=")[2]
BINARY_VERSION = find_make_var("BINARY_VERSION")
BUILD_COMMIT = find_make_var("BUILD_COMMIT")
# Build workflows upload artifacts using a 7-char short SHA.
assert (
l := len(BUILD_COMMIT)
) == 7, f"BUILD_COMMIT must be a 7-char short SHA, got {BUILD_COMMIT!r} (length {l})"
required_urls: list[str] = []
@@ -97,14 +85,47 @@ class TestPackageSupportedPlatforms:
)
required_urls.append(url)
# Verify all required URLs are present in the build HTML.
missing_urls: Sequence[str]
if "BeautifulSoup" in globals():
missing_urls = set(required_urls) - set(a["href"] for a in BeautifulSoup(build_html).find_all("a"))
else:
missing_urls = []
for url in required_urls:
if url not in build_html:
missing_urls.append(url)
return required_urls
@staticmethod
def get_missing_urls_fast(urls: list[str]) -> list[str]:
"""Check `urls` against the build listing page.
Fast, but the listing page can lag behind what's actually on S3, so this may report URLs as
missing that do exist.
"""
# We don't use requests in ifcopenshell, so we use Python builtin stuff.
parsed = urlparse("https://builds.ifcopenshell.org")
conn = http.client.HTTPSConnection(parsed.netloc)
conn.request("GET", parsed.path)
response = conn.getresponse()
build_html = response.read().decode("utf-8")
conn.close()
return [url for url in urls if url not in build_html]
@staticmethod
def get_missing_urls_slow(urls: list[str]) -> list[str]:
"""Check `urls` directly with a HEAD request each.
Slow, but more reliable.
"""
missing_urls: list[str] = []
for url in urls:
parsed = urlparse(url)
conn = http.client.HTTPSConnection(parsed.netloc)
conn.request("HEAD", parsed.path)
response = conn.getresponse()
response.read()
conn.close()
if response.status != 200:
missing_urls.append(url)
return missing_urls
def test_run(self) -> None:
required_urls = self.get_required_urls()
maybe_missing_urls = self.get_missing_urls_fast(required_urls)
missing_urls = self.get_missing_urls_slow(maybe_missing_urls)
assert not missing_urls
+1 -1
View File
@@ -447,7 +447,7 @@ public:
};
#ifdef WITH_IFCXML
IFC_PARSE_API IfcFile* parse_ifcxml(const std::string& filename, Logger& logger = Logger::Root());
IFC_PARSE_API IfcFile* parse_ifcxml(const std::string& filename, Logger* logger = nullptr);
#endif
namespace impl {
+7
View File
@@ -142,6 +142,13 @@ class IFC_PARSE_API Logger {
const std::vector<log_message>& log_messages() const { return log_messages_; }
};
// SWIG couldn't represent `Logger::Root()` default value using Python,
// so when translating signature it represents it just as `fn(*args)`, losing information about args.
// Using `Logger * = nullptr` instead of `&Logger = Logger::Root` helps,
// since `nullptr` is convertable Python's `None`.
// `logger_or_root` is just covering the boilerplate for this pattern.
inline Logger& logger_or_root(Logger* logger) { return logger ? *logger : Logger::Root(); }
#define PERF(x) \
\
Logger::Root().Message(Logger::LOG_PERF, "SYS", 1, x); \
+37 -5
View File
@@ -39,7 +39,6 @@
#include <stdlib.h>
#include <string>
#include <iomanip>
#include <charconv>
#ifdef USE_MMAP
#include <boost/filesystem/path.hpp>
@@ -733,6 +732,41 @@ void IfcParse::impl::rocks_db_file_storage::remove_type_ref(IfcUtil::IfcBaseClas
}
namespace {
// Shortest decimal representation of 'd' that round-trips back to the
// exact same double (like std::to_chars, or Python's repr).
// Using actual `std::to_chars` requires macOS 13.3+, so we implement this manually,
// until we drop support for older targets.
//
// Mirrors libstdc++'s notation-choice bounds (floating_to_chars.cc,
// __floating_to_chars_shortest) to pick whichever of fixed/scientific is
// shorter for a given digit count and exponent.
static inline void format_double_shortest(char (&buf)[64], double d) {
char sci[64];
int mantissa_length = 17;
for (int prec = 1; prec <= 17; ++prec) {
snprintf(sci, sizeof(sci), "%.*e", prec - 1, d);
if (strtod(sci, nullptr) == d) {
mantissa_length = prec;
break;
}
}
const char* exp_str = strchr(sci, 'e');
const int scientific_exponent = exp_str ? atoi(exp_str + 1) : 0;
const int fd_exponent = scientific_exponent - (mantissa_length - 1);
int lower_bound = -(mantissa_length + 3);
int upper_bound = 5;
if (mantissa_length == 1) {
++lower_bound;
--upper_bound;
}
if (fd_exponent >= lower_bound && fd_exponent <= upper_bound) {
const int fixed_precision = fd_exponent < 0 ? -fd_exponent : 0;
snprintf(buf, 64, "%.*f", fixed_precision, d);
} else {
snprintf(buf, 64, "%.*e", mantissa_length - 1, d);
}
}
class StringBuilderVisitor : public boost::static_visitor<void> {
private:
StringBuilderVisitor(const StringBuilderVisitor&); //N/A
@@ -759,11 +793,9 @@ namespace {
// values with noise digits (0.0174532925199433 -> 0.017453292519943299),
// which rewrote every REAL and produced huge diffs when a file was
// re-saved. See #7696.
// std::to_chars is locale-independent, so no ostringstream/imbue is
// needed here.
char buf[64];
const auto res = std::to_chars(buf, buf + sizeof(buf), d);
const std::string str(buf, res.ptr);
format_double_shortest(buf, d);
const std::string str(buf);
std::string::size_type e = str.find('e');
if (e == std::string::npos) {
e = str.find('E');
+7 -1
View File
@@ -35,7 +35,13 @@ namespace {
parse_context pc;
storage->tokens->Next();
storage->load(-1, nullptr, pc, -1);
return pc.construct(boost::none, *storage->references_to_resolve, decl, decl->as_entity()->attribute_count(), -1, logger);
// references_to_resolve is unset while reading the header (header
// entities such as FILE_DESCRIPTION never reference other
// instances), so fall back to a throwaway list instead of
// dereferencing a null pointer.
unresolved_references no_references;
unresolved_references& references = storage->references_to_resolve ? *storage->references_to_resolve : no_references;
return pc.construct(boost::none, references, decl, decl->as_entity()->attribute_count(), -1, logger);
} else {
// std::unreachable();
return IfcEntityInstanceData(in_memory_attribute_storage(10));
+2 -2
View File
@@ -695,10 +695,10 @@ end:
return;
}
IFC_PARSE_API IfcParse::IfcFile* IfcParse::parse_ifcxml(const std::string& filename, Logger& logger) {
IFC_PARSE_API IfcParse::IfcFile* IfcParse::parse_ifcxml(const std::string& filename, Logger* logger) {
throw std::runtime_error("IFC-XML import temporarily disabled");
ifcxml_parse_state state(logger);
ifcxml_parse_state state(logger_or_root(logger));
xmlSAXHandler handler;
memset(&handler, 0, sizeof(xmlSAXHandler));
+15 -10
View File
@@ -630,29 +630,33 @@ 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, Logger& logger = Logger::Root()) {
return new IfcGeom::Iterator(ifcopenshell::geometry::kernels::construct(file, geometry_library, settings, logger), settings, file, num_threads, logger);
IfcGeom::Iterator* construct_iterator(const std::string& geometry_library, ifcopenshell::geometry::Settings settings, IfcParse::IfcFile* file, int num_threads, Logger* logger = nullptr) {
Logger& logger_ = logger_or_root(logger);
return new IfcGeom::Iterator(ifcopenshell::geometry::kernels::construct(file, geometry_library, settings, logger_), settings, file, num_threads, logger_);
}
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, Logger& logger = Logger::Root()) {
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, Logger* logger = nullptr) {
Logger& logger_ = logger_or_root(logger);
std::set<std::string> elems_set(elems.begin(), elems.end());
IfcGeom::entity_filter ef{ include, false, elems_set };
return new IfcGeom::Iterator(ifcopenshell::geometry::kernels::construct(file, geometry_library, settings, logger), settings, file, {ef}, num_threads, logger);
return new IfcGeom::Iterator(ifcopenshell::geometry::kernels::construct(file, geometry_library, settings, logger_), settings, file, {ef}, num_threads, logger_);
}
IfcGeom::Iterator* construct_iterator_with_include_exclude_globalid(const std::string& geometry_library, ifcopenshell::geometry::Settings settings, IfcParse::IfcFile* file, std::vector<std::string> elems, bool include, int num_threads, Logger& logger = Logger::Root()) {
IfcGeom::Iterator* construct_iterator_with_include_exclude_globalid(const std::string& geometry_library, ifcopenshell::geometry::Settings settings, IfcParse::IfcFile* file, std::vector<std::string> elems, bool include, int num_threads, Logger* logger = nullptr) {
Logger& logger_ = logger_or_root(logger);
std::set<std::string> elems_set(elems.begin(), elems.end());
IfcGeom::attribute_filter af;
af.attribute_name = "GlobalId";
af.populate(elems_set);
af.include = include;
return new IfcGeom::Iterator(ifcopenshell::geometry::kernels::construct(file, geometry_library, settings, logger), settings, file, {af}, num_threads, logger);
return new IfcGeom::Iterator(ifcopenshell::geometry::kernels::construct(file, geometry_library, settings, logger_), settings, file, {af}, num_threads, logger_);
}
IfcGeom::Iterator* construct_iterator_with_include_exclude_id(const std::string& geometry_library, ifcopenshell::geometry::Settings settings, IfcParse::IfcFile* file, std::vector<int> elems, bool include, int num_threads, Logger& logger = Logger::Root()) {
IfcGeom::Iterator* construct_iterator_with_include_exclude_id(const std::string& geometry_library, ifcopenshell::geometry::Settings settings, IfcParse::IfcFile* file, std::vector<int> elems, bool include, int num_threads, Logger* logger = nullptr) {
Logger& logger_ = logger_or_root(logger);
std::set<int> elems_set(elems.begin(), elems.end());
IfcGeom::instance_id_filter af(include, false, elems_set);
return new IfcGeom::Iterator(ifcopenshell::geometry::kernels::construct(file, geometry_library, settings, logger), settings, file, {af}, num_threads, logger);
return new IfcGeom::Iterator(ifcopenshell::geometry::kernels::construct(file, geometry_library, settings, logger_), settings, file, {af}, num_threads, logger_);
}
%}
@@ -1162,6 +1166,7 @@ ifcopenshell::geometry::taxonomy::item::ptr try_upcast(PyObject* obj0, swig_type
%ignore svgfill::svg_to_polygons;
%ignore svgfill::arrange_polygons;
%ignore svgfill::abstract_arrangement;
%ignore svgfill::context::delete_same_facet_edge_pairs;
%template(svg_line_segments) std::vector<std::array<svgfill::point_2, 2>>;
%template(svg_groups_of_line_segments) std::vector<std::vector<std::array<svgfill::point_2, 2>>>;
@@ -1295,9 +1300,9 @@ ifcopenshell::geometry::taxonomy::item::ptr try_upcast(PyObject* obj0, swig_type
}
}
std::vector<svgfill::polygon_2> arrange_polygons(svgfill::arrange_polygon_settings settings, const std::vector<svgfill::polygon_2>& polygons, Logger& logger = Logger::Root()) {
std::vector<svgfill::polygon_2> arrange_polygons(svgfill::arrange_polygon_settings settings, const std::vector<svgfill::polygon_2>& polygons, Logger* logger = nullptr) {
std::vector<svgfill::polygon_2> r;
if (svgfill::arrange_polygons(settings, polygons, r, logger)) {
if (svgfill::arrange_polygons(settings, polygons, r, logger_or_root(logger))) {
return r;
} else {
throw std::runtime_error("Failed to arrange polygons");
+2 -2
View File
@@ -752,10 +752,10 @@ private:
%newobject stream_from_string;
%inline %{
IfcParse::IfcFile* open(const std::string& fn, bool readonly=false, Logger& logger=Logger::Root()) {
IfcParse::IfcFile* open(const std::string& fn, bool readonly=false, Logger* logger=nullptr) {
IfcParse::IfcFile* f;
Py_BEGIN_ALLOW_THREADS;
f = new IfcParse::IfcFile(fn, IfcParse::FT_AUTODETECT, readonly, logger);
f = new IfcParse::IfcFile(fn, IfcParse::FT_AUTODETECT, readonly, logger_or_root(logger));
Py_END_ALLOW_THREADS;
return f;
}
+2 -2
View File
@@ -219,8 +219,8 @@ private:
std::string unit_name;
float unit_magnitude;
public:
ColladaSerializer(const std::string& dae_filename, const ifcopenshell::geometry::Settings& geometry_settings, const ifcopenshell::geometry::SerializerSettings& settings, Logger& logger = Logger::Root())
: WriteOnlyGeometrySerializer(geometry_settings, settings, logger)
ColladaSerializer(const std::string& dae_filename, const ifcopenshell::geometry::Settings& geometry_settings, const ifcopenshell::geometry::SerializerSettings& settings, Logger* logger = nullptr)
: WriteOnlyGeometrySerializer(geometry_settings, settings, logger_or_root(logger))
, exporter("IfcOpenShell", dae_filename, this, settings.get<ifcopenshell::geometry::settings::FloatingPointDigits>().get() >= 15)
{
exporter.serializer = this;
+2 -2
View File
@@ -53,8 +53,8 @@ static const uint32_t PRIM_TRIANGLE_FAN = 6;
static const uint32_t ELEMENT_ARRAY_BUFFER = 34963;
static const uint32_t ARRAY_BUFFER = 34962;
GltfSerializer::GltfSerializer(const std::string& filename, const ifcopenshell::geometry::Settings& geometry_settings, const ifcopenshell::geometry::SerializerSettings& settings, Logger& logger)
: WriteOnlyGeometrySerializer(geometry_settings, settings, logger)
GltfSerializer::GltfSerializer(const std::string& filename, const ifcopenshell::geometry::Settings& geometry_settings, const ifcopenshell::geometry::SerializerSettings& settings, Logger* logger)
: WriteOnlyGeometrySerializer(geometry_settings, settings, logger_or_root(logger))
, filename_(filename)
, tmp_filename1_(filename + ".indices.tmp")
, tmp_filename2_(filename + ".vertices.tmp")
+1 -1
View File
@@ -43,7 +43,7 @@ private:
int writeMaterial(const ifcopenshell::geometry::taxonomy::style::ptr style);
public:
GltfSerializer(const std::string& filename, const ifcopenshell::geometry::Settings& geometry_settings, const ifcopenshell::geometry::SerializerSettings& settings, Logger& logger = Logger::Root());
GltfSerializer(const std::string& filename, const ifcopenshell::geometry::Settings& geometry_settings, const ifcopenshell::geometry::SerializerSettings& settings, Logger* logger = nullptr);
virtual ~GltfSerializer();
bool ready();
void writeHeader();
+2 -2
View File
@@ -55,8 +55,8 @@ herr_t print_stack(hid_t /*estack*/, void*) {
return 0;
}
HdfSerializer::HdfSerializer(const std::string& hdf_filename, const ifcopenshell::geometry::Settings& geometry_settings, const ifcopenshell::geometry::SerializerSettings& settings, bool read_only, Logger& logger)
: GeometrySerializer(geometry_settings, settings, logger)
HdfSerializer::HdfSerializer(const std::string& hdf_filename, const ifcopenshell::geometry::Settings& geometry_settings, const ifcopenshell::geometry::SerializerSettings& settings, bool read_only, Logger* logger)
: GeometrySerializer(geometry_settings, settings, logger_or_root(logger))
, hdf_filename(hdf_filename)
, settings_(settings)
{
+1 -1
View File
@@ -96,7 +96,7 @@ private:
void write_style(surface_style_serialization& data, const ifcopenshell::geometry::taxonomy::style::ptr& s);
public:
HdfSerializer(const std::string& hdf_filename, const ifcopenshell::geometry::Settings& geometry_settings, const ifcopenshell::geometry::SerializerSettings& settings, bool read_only=false, Logger& logger = Logger::Root());
HdfSerializer(const std::string& hdf_filename, const ifcopenshell::geometry::Settings& geometry_settings, const ifcopenshell::geometry::SerializerSettings& settings, bool read_only=false, Logger* logger = nullptr);
virtual ~HdfSerializer() {}
bool ready();
void writeHeader();
+2 -2
View File
@@ -596,8 +596,8 @@ protected:
subtract_before_project subtraction_settings_;
public:
SvgSerializer(const stream_or_filename& out_filename, const ifcopenshell::geometry::Settings& geometry_settings, const ifcopenshell::geometry::SerializerSettings& settings, Logger& logger = Logger::Root())
: WriteOnlyGeometrySerializer(geometry_settings, settings, logger)
SvgSerializer(const stream_or_filename& out_filename, const ifcopenshell::geometry::Settings& geometry_settings, const ifcopenshell::geometry::SerializerSettings& settings, Logger* logger = nullptr)
: WriteOnlyGeometrySerializer(geometry_settings, settings, logger_or_root(logger))
, svg_file(out_filename)
, xmin(+std::numeric_limits<double>::infinity())
, ymin(+std::numeric_limits<double>::infinity())
+2 -2
View File
@@ -233,8 +233,8 @@ namespace {
}
}
TtlWktSerializer::TtlWktSerializer(const stream_or_filename& filename, const ifcopenshell::geometry::Settings& geometry_settings, const ifcopenshell::geometry::SerializerSettings& settings, Logger& logger)
: WriteOnlyGeometrySerializer(geometry_settings, settings, logger)
TtlWktSerializer::TtlWktSerializer(const stream_or_filename& filename, const ifcopenshell::geometry::Settings& geometry_settings, const ifcopenshell::geometry::SerializerSettings& settings, Logger* logger)
: WriteOnlyGeometrySerializer(geometry_settings, settings, logger_or_root(logger))
, filename_(filename)
{
const auto& tri_setting = geometry_settings.get<ifcopenshell::geometry::settings::TriangulationType>().get();
+1 -1
View File
@@ -32,7 +32,7 @@ class SERIALIZERS_API TtlWktSerializer : public WriteOnlyGeometrySerializer {
private:
stream_or_filename filename_;
public:
TtlWktSerializer(const stream_or_filename& filename, const ifcopenshell::geometry::Settings& geometry_settings, const ifcopenshell::geometry::SerializerSettings& settings, Logger& logger = Logger::Root());
TtlWktSerializer(const stream_or_filename& filename, const ifcopenshell::geometry::Settings& geometry_settings, const ifcopenshell::geometry::SerializerSettings& settings, Logger* logger = nullptr);
virtual ~TtlWktSerializer() {}
bool ready();
void writeHeader();
+2 -2
View File
@@ -27,8 +27,8 @@
#include <boost/lexical_cast.hpp>
#include <iomanip>
WaveFrontOBJSerializer::WaveFrontOBJSerializer(const stream_or_filename& obj_filename, const stream_or_filename& mtl_filename, const ifcopenshell::geometry::Settings& geometry_settings, const ifcopenshell::geometry::SerializerSettings& settings, Logger& logger)
: WriteOnlyGeometrySerializer(geometry_settings, settings, logger)
WaveFrontOBJSerializer::WaveFrontOBJSerializer(const stream_or_filename& obj_filename, const stream_or_filename& mtl_filename, const ifcopenshell::geometry::Settings& geometry_settings, const ifcopenshell::geometry::SerializerSettings& settings, Logger* logger)
: WriteOnlyGeometrySerializer(geometry_settings, settings, logger_or_root(logger))
, obj_stream(obj_filename)
, mtl_stream(mtl_filename)
, vcount_total(1)
+1 -1
View File
@@ -35,7 +35,7 @@ private:
size_t vcount_total, ncount_total;
std::set<std::string> materials;
public:
WaveFrontOBJSerializer(const stream_or_filename& obj_filename, const stream_or_filename& mtl_filename, const ifcopenshell::geometry::Settings& geometry_settings, const ifcopenshell::geometry::SerializerSettings& settings, Logger& logger = Logger::Root());
WaveFrontOBJSerializer(const stream_or_filename& obj_filename, const stream_or_filename& mtl_filename, const ifcopenshell::geometry::Settings& geometry_settings, const ifcopenshell::geometry::SerializerSettings& settings, Logger* logger = nullptr);
virtual ~WaveFrontOBJSerializer() {}
bool ready();
void writeHeader();
+2 -2
View File
@@ -31,8 +31,8 @@ XmlSerializer* XmlSerializerFactory::Factory::construct(const std::string& schem
return it->second(file, xml_filename, logger);
}
XmlSerializer::XmlSerializer(IfcParse::IfcFile* file, const std::string& xml_filename, Logger& logger)
: Serializer(logger) {
XmlSerializer::XmlSerializer(IfcParse::IfcFile* file, const std::string& xml_filename, Logger* logger)
: Serializer(logger_or_root(logger)) {
if (file) {
implementation_ = XmlSerializerFactory::implementations().construct(file->schema()->name(), file, xml_filename, logger_);
}
+1 -1
View File
@@ -16,7 +16,7 @@ protected:
std::string xml_filename;
public:
XmlSerializer(IfcParse::IfcFile* file, const std::string& xml_filename, Logger& logger = Logger::Root());
XmlSerializer(IfcParse::IfcFile* file, const std::string& xml_filename, Logger* logger = nullptr);
virtual ~XmlSerializer() {}
@@ -40,7 +40,7 @@ private:
public:
POSTFIX_SCHEMA(XmlSerializer)(IfcParse::IfcFile* file, const std::string& xml_filename, Logger& logger = Logger::Root())
: XmlSerializer(0, "", logger)
: XmlSerializer(0, "", &logger)
, mapping_(ifcopenshell::geometry::impl::mapping_implementations().construct(file, settings_, logger))
{
this->file = file;