From 2eb2d6571016f8d15b9d9ecf70323e463c85a5a6 Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Sat, 9 May 2026 21:03:15 +0200 Subject: [PATCH] Allow passing buffer to serializers that support it --- src/ifcgeom/GeometrySerializer.h | 1 + .../ifcopenshell/geom/main.py | 20 +++++++- src/ifcwrap/IfcGeomWrapper.i | 49 +++++++++++++++++++ src/serializers/geometry_obj_plugin.cpp | 10 ++++ src/serializers/geometry_serializer_plugin.h | 2 + src/serializers/geometry_svg_plugin.cpp | 4 ++ src/serializers/geometry_ttl_plugin.cpp | 4 ++ 7 files changed, 88 insertions(+), 2 deletions(-) diff --git a/src/ifcgeom/GeometrySerializer.h b/src/ifcgeom/GeometrySerializer.h index 8fbb2834a4..bde7baaf06 100644 --- a/src/ifcgeom/GeometrySerializer.h +++ b/src/ifcgeom/GeometrySerializer.h @@ -282,6 +282,7 @@ public: stream_or_filename(const std::string& fn) : ofs_(new std::ofstream(ifcopenshell::path::from_utf8(fn).c_str())) + , filename_(fn) , stream(*ofs_) {} diff --git a/src/ifcopenshell-python/ifcopenshell/geom/main.py b/src/ifcopenshell-python/ifcopenshell/geom/main.py index 41a58eee0b..c82c8c28bb 100644 --- a/src/ifcopenshell-python/ifcopenshell/geom/main.py +++ b/src/ifcopenshell-python/ifcopenshell/geom/main.py @@ -616,6 +616,8 @@ def make_shape_function(fn): class _serializer_factory: + _stream_serializers = {"obj", "svg", "ttl"} + def __init__(self, name: str, extension: str): self.name = name self.extension = extension @@ -637,13 +639,27 @@ class _serializer_factory: + (obj_signature if self.name == "obj" else "") ) - output_filename = self._path(output_filename) - output_temp_filename = self._path(output_temp_filename) + if self._is_buffer(output_filename) or self._is_buffer(output_temp_filename): + if self.name not in self._stream_serializers: + raise TypeError(f"serializers.{self.name}() requires a filesystem path") + output_filename = self._buffer(output_filename) + output_temp_filename = self._buffer(output_temp_filename) + else: + output_filename = self._path(output_filename) + output_temp_filename = self._path(output_temp_filename) return ifcopenshell_wrapper.create_geometry_serializer( self.extension, output_filename, output_temp_filename, geometry_settings, serializer_settings ) + def _is_buffer(self, value: Any) -> bool: + return isinstance(value, ifcopenshell_wrapper.buffer) + + def _buffer(self, value: Union[str, PathLike[str], ifcopenshell_wrapper.buffer]) -> ifcopenshell_wrapper.buffer: + if self._is_buffer(value): + return value + return ifcopenshell_wrapper.buffer(self._path(value)) + def _path(self, value: Union[str, PathLike[str]]) -> str: try: path = fspath(value) diff --git a/src/ifcwrap/IfcGeomWrapper.i b/src/ifcwrap/IfcGeomWrapper.i index d645fa61fa..0badfec930 100644 --- a/src/ifcwrap/IfcGeomWrapper.i +++ b/src/ifcwrap/IfcGeomWrapper.i @@ -286,12 +286,45 @@ public: serializer_settings }; + initialize_serializer(extension, context); + } + + PythonPluginGeometrySerializer( + const std::string& extension, + const stream_or_filename& output_filename, + const stream_or_filename& output_temp_filename, + ifcopenshell::geometry::Settings& geometry_settings, + const ifcopenshell::geometry::SerializerSettings& serializer_settings + ) + : GeometrySerializer(geometry_settings, serializer_settings) + { + const auto output_filename_string = output_filename.filename().value_or(""); + const auto output_temp_filename_string = output_temp_filename.filename().value_or(output_filename_string); + ifcopenshell::serializers::geometry_serializer_context context{ + output_filename_string, + output_temp_filename_string, + geometry_settings, + serializer_settings, + &output_filename, + &output_temp_filename + }; + + initialize_serializer(extension, context); + } + +private: + void initialize_serializer( + const std::string& extension, + ifcopenshell::serializers::geometry_serializer_context& context + ) { auto& registry = ifcopenshell::serializers::geometry_serializer_registry_instance(); registry.configure(extension, context); geometry_settings_ = context.geometry_settings; serializer_ = registry.create(extension, context); } +public: + bool ready() override { return serializer_->ready(); } @@ -659,6 +692,22 @@ struct ShapeRTTI : public boost::static_visitor ); } + GeometrySerializer* create_geometry_serializer( + const std::string& extension, + const stream_or_filename& output_filename, + const stream_or_filename& output_temp_filename, + ifcopenshell::geometry::Settings& geometry_settings, + const ifcopenshell::geometry::SerializerSettings& serializer_settings + ) { + return new PythonPluginGeometrySerializer( + extension, + output_filename, + output_temp_filename, + geometry_settings, + serializer_settings + ); + } + IfcGeom::Iterator* construct_iterator(const std::string& geometry_library, ifcopenshell::geometry::Settings settings, ifcopenshell::file* file, int num_threads) { return new IfcGeom::Iterator(ifcopenshell::geometry::kernels::construct(file, geometry_library, settings), settings, file, num_threads); } diff --git a/src/serializers/geometry_obj_plugin.cpp b/src/serializers/geometry_obj_plugin.cpp index cac598bb6c..e8cd093404 100644 --- a/src/serializers/geometry_obj_plugin.cpp +++ b/src/serializers/geometry_obj_plugin.cpp @@ -50,6 +50,16 @@ plugin::metadata plugin_metadata() { } boost::shared_ptr create_serializer(const geometry_serializer_context& context) { + if (context.output_temp_stream || context.output_stream) { + stream_or_filename obj_filename = context.output_temp_stream + ? *context.output_temp_stream + : stream_or_filename(context.output_temp_filename); + stream_or_filename mtl_filename = context.output_stream + ? *context.output_stream + : stream_or_filename(obj_mtl_filename(context.output_filename)); + return boost::make_shared( + obj_filename, mtl_filename, context.geometry_settings, context.serializer_settings); + } return boost::make_shared(context.output_temp_filename, obj_mtl_filename(context.output_filename), context.geometry_settings, context.serializer_settings); } diff --git a/src/serializers/geometry_serializer_plugin.h b/src/serializers/geometry_serializer_plugin.h index eec15a88b5..610192c4a0 100644 --- a/src/serializers/geometry_serializer_plugin.h +++ b/src/serializers/geometry_serializer_plugin.h @@ -54,6 +54,8 @@ struct SERIALIZERS_API geometry_serializer_context { std::string output_temp_filename; ifcopenshell::geometry::Settings& geometry_settings; const ifcopenshell::geometry::SerializerSettings& serializer_settings; + const stream_or_filename* output_stream = nullptr; + const stream_or_filename* output_temp_stream = nullptr; }; class SERIALIZERS_API geometry_serializer_registry { diff --git a/src/serializers/geometry_svg_plugin.cpp b/src/serializers/geometry_svg_plugin.cpp index 29ebf39e23..c4399d2e06 100644 --- a/src/serializers/geometry_svg_plugin.cpp +++ b/src/serializers/geometry_svg_plugin.cpp @@ -38,6 +38,10 @@ plugin::metadata plugin_metadata() { } boost::shared_ptr create_serializer(const geometry_serializer_context& context) { + if (context.output_temp_stream) { + return boost::make_shared( + *context.output_temp_stream, context.geometry_settings, context.serializer_settings); + } return boost::make_shared(context.output_temp_filename, context.geometry_settings, context.serializer_settings); } diff --git a/src/serializers/geometry_ttl_plugin.cpp b/src/serializers/geometry_ttl_plugin.cpp index f5cdf1f260..27db1c1f91 100644 --- a/src/serializers/geometry_ttl_plugin.cpp +++ b/src/serializers/geometry_ttl_plugin.cpp @@ -36,6 +36,10 @@ plugin::metadata plugin_metadata() { } boost::shared_ptr create_serializer(const geometry_serializer_context& context) { + if (context.output_temp_stream) { + return boost::make_shared( + *context.output_temp_stream, context.geometry_settings, context.serializer_settings); + } return boost::make_shared(context.output_temp_filename, context.geometry_settings, context.serializer_settings); }