From d2cc66fdf05e25f6a1dd7b234c69e71b86c2e365 Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Fri, 17 Apr 2026 11:24:09 +0200 Subject: [PATCH] tree and document plug-ins --- cmake/CMakeLists.txt | 1 - src/ifcconvert/CMakeLists.txt | 3 + src/ifcgeom/CMakeLists.txt | 7 +- src/ifcgeom/ConversionResult.h | 40 +- src/ifcgeom/Serialization/CMakeLists.txt | 17 +- src/ifcgeom/Serialization/Serialization.cpp | 50 +- .../Serialization/ifc_geomserialization_api.h | 4 +- ...opencascade_geometry_ifc_writer_plugin.cpp | 57 ++ .../opencascade_geometry_ifc_writer_plugin.h | 38 + .../Serialization/schema/CMakeLists.txt | 13 +- src/ifcgeom/Serialization/schema/plugin.cpp | 69 ++ src/ifcgeom/abstract_mapping.cpp | 38 +- src/ifcgeom/abstract_mapping.h | 4 +- src/ifcgeom/ifc_geom_api.h | 4 +- src/ifcgeom/kernel_plugin.cpp | 31 +- src/ifcgeom/kernel_plugin.h | 4 +- src/ifcgeom/kernel_registry.h | 4 +- src/ifcgeom/kernels/CMakeLists.txt | 24 + src/ifcgeom/kernels/ifc_geomlibrary_api.h | 4 +- src/ifcgeom/kernels/opencascade/IfcGeomTree.h | 31 +- .../kernels/opencascade/tree_backends.h | 182 +++++ .../kernels/opencascade/tree_brep_plugin.cpp | 55 ++ .../opencascade/tree_trianglebvh_plugin.cpp | 55 ++ src/ifcgeom/mapping/CMakeLists.txt | 13 +- src/ifcgeom/mapping/plugin.cpp | 58 ++ src/ifcgeom/mapping_plugin.cpp | 57 ++ src/ifcgeom/mapping_plugin.h | 42 ++ src/ifcgeom/profile_helper.h | 13 +- src/ifcgeom/tree.cpp | 266 +++++++ src/ifcgeom/tree.h | 116 +++ src/ifcgeom/tree_plugin.cpp | 54 ++ src/ifcgeom/tree_plugin.h | 42 ++ src/ifcgeom/tree_registry.cpp | 144 ++++ src/ifcgeom/tree_registry.h | 103 +++ src/ifcgeomserver/CMakeLists.txt | 3 + .../ifcopenshell/geom/main.py | 10 +- src/ifcparse/ifc_parse_api.h | 4 +- src/ifcparse/schema.h | 4 +- src/ifcwrap/CMakeLists.txt | 3 + src/ifcwrap/IfcGeomWrapper.i | 668 +++++++----------- src/ifcwrap/IfcPython.i | 20 +- src/ifcwrap/utils/type_conversion.i | 86 ++- src/ifcwrap/utils/typemaps_in.i | 64 +- src/plugin/plugin.cpp | 33 + src/plugin/plugin.h | 2 + src/plugin/plugin_api.h | 4 +- src/serializers/CMakeLists.txt | 10 +- src/serializers/JsonSerializer.cpp | 23 +- src/serializers/JsonSerializer.h | 15 +- src/serializers/XmlSerializer.cpp | 24 +- src/serializers/XmlSerializer.h | 20 +- .../document_serializer_plugin.cpp | 78 ++ src/serializers/document_serializer_plugin.h | 51 ++ .../schema_dependent/CMakeLists.txt | 36 +- .../schema_dependent/JsonSerializer.cpp | 18 +- .../schema_dependent/XmlSerializer.cpp | 18 - .../schema_dependent/json_plugin.cpp | 66 ++ .../schema_dependent/xml_plugin.cpp | 62 ++ src/serializers/serializers_api.h | 4 +- src/svgfill/src/svgfill.h | 4 +- 60 files changed, 2207 insertions(+), 766 deletions(-) create mode 100644 src/ifcgeom/Serialization/opencascade_geometry_ifc_writer_plugin.cpp create mode 100644 src/ifcgeom/Serialization/opencascade_geometry_ifc_writer_plugin.h create mode 100644 src/ifcgeom/Serialization/schema/plugin.cpp create mode 100644 src/ifcgeom/kernels/opencascade/tree_backends.h create mode 100644 src/ifcgeom/kernels/opencascade/tree_brep_plugin.cpp create mode 100644 src/ifcgeom/kernels/opencascade/tree_trianglebvh_plugin.cpp create mode 100644 src/ifcgeom/mapping/plugin.cpp create mode 100644 src/ifcgeom/mapping_plugin.cpp create mode 100644 src/ifcgeom/mapping_plugin.h create mode 100644 src/ifcgeom/tree.cpp create mode 100644 src/ifcgeom/tree.h create mode 100644 src/ifcgeom/tree_plugin.cpp create mode 100644 src/ifcgeom/tree_plugin.h create mode 100644 src/ifcgeom/tree_registry.cpp create mode 100644 src/ifcgeom/tree_registry.h create mode 100644 src/serializers/document_serializer_plugin.cpp create mode 100644 src/serializers/document_serializer_plugin.h create mode 100644 src/serializers/schema_dependent/json_plugin.cpp create mode 100644 src/serializers/schema_dependent/xml_plugin.cpp diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index 14e2f2e3ae..915f24649a 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -595,7 +595,6 @@ endif(BUILD_IFCGEOM) if(BUILD_CONVERT OR BUILD_IFCPYTHON) add_subdirectory(../src/serializers serializers) - set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} ${SERIALIZER_SCHEMA_LIBRARIES}) endif(BUILD_CONVERT OR BUILD_IFCPYTHON) if(BUILD_CONVERT) diff --git a/src/ifcconvert/CMakeLists.txt b/src/ifcconvert/CMakeLists.txt index 24c525f638..262ff1b3bf 100644 --- a/src/ifcconvert/CMakeLists.txt +++ b/src/ifcconvert/CMakeLists.txt @@ -2,5 +2,8 @@ add_executable(IfcConvert IfcConvert.cpp) target_link_libraries(IfcConvert IfcGeom IfcParse Serializers ${OpenCASCADE_LIBRARIES} ${Boost_LIBRARIES} ${HDF5_LIBRARIES} ${USD_LIBRARIES}) +if(kernel_libraries OR mapping_libraries OR document_serializer_libraries) + add_dependencies(IfcConvert ${kernel_libraries} ${mapping_libraries} ${document_serializer_libraries}) +endif() install(TARGETS IfcConvert) diff --git a/src/ifcgeom/CMakeLists.txt b/src/ifcgeom/CMakeLists.txt index 1c35ba53bb..993e480149 100644 --- a/src/ifcgeom/CMakeLists.txt +++ b/src/ifcgeom/CMakeLists.txt @@ -1,4 +1,5 @@ add_subdirectory(mapping) +set(mapping_libraries ${mapping_libraries} PARENT_SCOPE) # IfcGeom (schema agnostic) file(GLOB SCHEMA_AGNOSTIC_H_FILES *.h) @@ -18,16 +19,18 @@ find_package(Eigen3 REQUIRED) add_subdirectory(kernels) set(kernel_libraries ${kernel_libraries} PARENT_SCOPE) +set(tree_libraries ${tree_libraries} PARENT_SCOPE) if(WASM_BUILD) - target_link_libraries(IfcGeom plugin ${Boost_LIBRARIES} ${mapping_libraries} ${CMAKE_THREAD_LIBS_INIT} "Eigen3::Eigen") + target_link_libraries(IfcGeom plugin ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} "Eigen3::Eigen") else() - target_link_libraries(IfcGeom plugin IfcParse ${Boost_LIBRARIES} ${mapping_libraries} ${CMAKE_THREAD_LIBS_INIT} "Eigen3::Eigen") + target_link_libraries(IfcGeom plugin IfcParse ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} "Eigen3::Eigen") endif() if((BUILD_CONVERT OR BUILD_IFCPYTHON) AND WITH_OPENCASCADE) add_subdirectory(Serialization) set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} PARENT_SCOPE) + set(geometry_serializer_libraries ${geometry_serializer_libraries} PARENT_SCOPE) endif() install(FILES ${SCHEMA_AGNOSTIC_H_FILES} diff --git a/src/ifcgeom/ConversionResult.h b/src/ifcgeom/ConversionResult.h index 98b66afc3e..52a5b0c4d3 100644 --- a/src/ifcgeom/ConversionResult.h +++ b/src/ifcgeom/ConversionResult.h @@ -58,6 +58,7 @@ struct EdgeKey { } }; +#ifndef SWIG namespace std { template <> struct hash { @@ -66,6 +67,7 @@ namespace std { } }; } +#endif namespace IfcGeom { @@ -73,6 +75,7 @@ namespace IfcGeom { class IFC_GEOM_API Triangulation; } +#ifndef SWIG template constexpr T add_(T a, T b) { return a + b; @@ -107,6 +110,7 @@ namespace IfcGeom { constexpr T negate_(T a) { return -a; } +#endif class IFC_GEOM_API OpaqueNumber { public: @@ -126,6 +130,7 @@ namespace IfcGeom { }; // @todo this can simply be a template class, to remove the need for the NumberEpeck in CGAL kernel. +#ifndef SWIG class IFC_GEOM_API NumberNativeDouble : public OpaqueNumber { private: double value_; @@ -189,7 +194,24 @@ namespace IfcGeom { return new NumberNativeDouble(value_); } }; +#else + class IFC_GEOM_API NumberNativeDouble : public OpaqueNumber { + public: + NumberNativeDouble(double v); + virtual double to_double() const; + virtual std::string to_string() const; + virtual OpaqueNumber* operator+(OpaqueNumber* other) const; + virtual OpaqueNumber* operator-(OpaqueNumber* other) const; + virtual OpaqueNumber* operator*(OpaqueNumber* other) const; + virtual OpaqueNumber* operator/(OpaqueNumber* other) const; + virtual bool operator==(OpaqueNumber* other) const; + virtual bool operator<(OpaqueNumber* other) const; + virtual OpaqueNumber* operator-() const; + virtual OpaqueNumber* clone() const; + }; +#endif +#ifndef SWIG template struct IFC_GEOM_API OpaqueCoordinate { private: @@ -251,10 +273,25 @@ namespace IfcGeom { } } }; +#else + template + struct IFC_GEOM_API OpaqueCoordinate { + OpaqueCoordinate(); + OpaqueCoordinate(const OpaqueCoordinate& other); + OpaqueCoordinate& operator=(const OpaqueCoordinate& other); + ~OpaqueCoordinate(); + OpaqueNumber* get(size_t i) const; + void set(size_t i, OpaqueNumber* n); + }; +#endif class IFC_GEOM_API ConversionResultShape { public: +#ifdef SWIG + virtual std::string backend_id() const = 0; +#else virtual std::string_view backend_id() const = 0; +#endif virtual void Triangulate(ifcopenshell::geometry::Settings settings, const ifcopenshell::geometry::taxonomy::matrix4& place, Representation::Triangulation* t, int item_id, int surface_style_id) const = 0; IfcGeom::Representation::Triangulation* Triangulate(const ifcopenshell::geometry::Settings& settings) const; virtual void Serialize(const ifcopenshell::geometry::taxonomy::matrix4& place, std::string&) const = 0; @@ -346,7 +383,7 @@ namespace IfcGeom { typedef std::vector ConversionResults; - +#ifndef SWIG namespace util { // @todo this is now moved to occt kernel, do we need something similar in cgal? // bool flatten_shape_list(const IfcGeom::ConversionResults& shapes, TopoDS_Shape& result, bool fuse, double tol); @@ -424,6 +461,7 @@ namespace IfcGeom { return loops; } } +#endif } #endif diff --git a/src/ifcgeom/Serialization/CMakeLists.txt b/src/ifcgeom/Serialization/CMakeLists.txt index 7aff8993f1..67930f2535 100644 --- a/src/ifcgeom/Serialization/CMakeLists.txt +++ b/src/ifcgeom/Serialization/CMakeLists.txt @@ -1,15 +1,20 @@ +set(geometry_serialization_plugin_runtime_dir "${CMAKE_CURRENT_BINARY_DIR}/$") + add_subdirectory(schema) -add_library(geometry_serializer Serialization.cpp) +add_library(geometry_serializer Serialization.cpp opencascade_geometry_ifc_writer_plugin.cpp) add_library(opencascade_geometry_ifc_writer ALIAS geometry_serializer) -set_target_properties(geometry_serializer PROPERTIES COMPILE_FLAGS "-DIFC_GEOMSERIALIZATION_EXPORTS") -target_link_libraries(geometry_serializer plugin ${geometry_serializer_libraries} IfcGeom IfcParse) +set_target_properties(geometry_serializer PROPERTIES + COMPILE_FLAGS "-DIFC_GEOMSERIALIZATION_EXPORTS" + RUNTIME_OUTPUT_DIRECTORY "${geometry_serialization_plugin_runtime_dir}" + LIBRARY_OUTPUT_DIRECTORY "${geometry_serialization_plugin_runtime_dir}" +) +target_link_libraries(geometry_serializer plugin IfcGeom IfcParse ${OpenCASCADE_LIBRARIES}) target_link_libraries(geometry_serializer geometry_kernel_opencascade) -set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} "geometry_serializer" ${geometry_serializer_libraries}) +set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} "geometry_serializer") set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} PARENT_SCOPE) -file(GLOB IFCGEOM_SERIALIZATION_H_FILE *.h) -install(FILES ${IFCGEOM_SERIALIZATION_H_FILE} +install(FILES Serialization.h DESTINATION ${INCLUDEDIR}/ifcgeom/Serialization ) diff --git a/src/ifcgeom/Serialization/Serialization.cpp b/src/ifcgeom/Serialization/Serialization.cpp index 7c1c4d0022..87eb60084b 100644 --- a/src/ifcgeom/Serialization/Serialization.cpp +++ b/src/ifcgeom/Serialization/Serialization.cpp @@ -1,11 +1,10 @@ #include "Serialization.h" +#include "opencascade_geometry_ifc_writer_plugin.h" #include "../../ifcgeom/kernels/opencascade/OpenCascadeConversionResult.h" #include "../../ifcparse/file.h" #include -#include -#include #include @@ -13,55 +12,12 @@ namespace { std::string writer_schema_key(const std::string& schema_name) { return boost::to_upper_copy(schema_name); } - - ifcopenshell::plugin::module builtin_writer_module(const std::string& schema_name) { - ifcopenshell::plugin::metadata metadata; - metadata.kind_ = ifcopenshell::plugin::kind::opencascade_geometry_ifc_writer; - metadata.id = "geometry.ifc_writer." + boost::to_lower_copy(schema_name); - metadata.schema = schema_name; - return ifcopenshell::plugin::module::builtin(metadata); - } - - const ifcopenshell::geometry::OpenCascadeShape& require_opencascade_shape(const IfcGeom::ConversionResultShape& shape) { - const auto* oc_shape = dynamic_cast(&shape); - if (!oc_shape) { - throw ifcopenshell::exception("OpenCascade geometry IFC writer requires an opencascade conversion result shape"); - } - return *oc_shape; - } - - template - express::Base serialise_adapter(ifcopenshell::file& f, const IfcGeom::ConversionResultShape& shape, bool advanced) { - return Fn(f, require_opencascade_shape(shape).shape(), advanced); - } - - template - express::Base tesselate_adapter(ifcopenshell::file& f, const IfcGeom::ConversionResultShape& shape, double deflection) { - return Fn(f, require_opencascade_shape(shape).shape(), deflection); - } } -#define EXTERNAL_TESSELATE(r, data, elem) \ - express::Base BOOST_PP_CAT(tesselate_Ifc, elem)(ifcopenshell::file&, const TopoDS_Shape& shape, double deflection); - -#define EXTERNAL_SERIALISE(r, data, elem) \ - express::Base BOOST_PP_CAT(serialise_Ifc, elem)(ifcopenshell::file&, const TopoDS_Shape& shape, bool advanced); - -#define BIND_WRITER(r, data, elem) \ - registry.bind(BOOST_PP_STRINGIZE(BOOST_PP_CAT(Ifc, elem)), \ - &serialise_adapter, \ - &tesselate_adapter, \ - builtin_writer_module(BOOST_PP_STRINGIZE(BOOST_PP_CAT(Ifc, elem)))); - -BOOST_PP_SEQ_FOR_EACH(EXTERNAL_TESSELATE, , SCHEMA_SEQ) -BOOST_PP_SEQ_FOR_EACH(EXTERNAL_SERIALISE, , SCHEMA_SEQ) - IfcGeom::opencascade_geometry_ifc_writer_registry& IfcGeom::opencascade_geometry_ifc_writer_registry_instance() { static opencascade_geometry_ifc_writer_registry registry; static std::once_flag once; - std::call_once(once, []() { - BOOST_PP_SEQ_FOR_EACH(BIND_WRITER, , SCHEMA_SEQ) - }); + std::call_once(once, load_opencascade_geometry_ifc_writer_plugins, std::ref(registry)); return registry; } @@ -69,7 +25,7 @@ void IfcGeom::opencascade_geometry_ifc_writer_registry::bind(const std::string& entry entry; entry.serialise_ = serialise; entry.tesselate_ = tesselate; - entry.module_ = module; + entry.module_ = module.meta().id.empty() ? ifcopenshell::plugin::module(opencascade_geometry_ifc_writer_plugin_metadata(schema_name)) : module; entries_[writer_schema_key(schema_name)] = entry; } diff --git a/src/ifcgeom/Serialization/ifc_geomserialization_api.h b/src/ifcgeom/Serialization/ifc_geomserialization_api.h index 939d5feff0..54e891ed3f 100644 --- a/src/ifcgeom/Serialization/ifc_geomserialization_api.h +++ b/src/ifcgeom/Serialization/ifc_geomserialization_api.h @@ -20,7 +20,9 @@ #ifndef IFC_GEOMSERIALIZATION_API_H #define IFC_GEOMSERIALIZATION_API_H -#ifdef _WIN32 +#ifdef SWIG +#define IFC_GEOMSERIALIZATION_API +#elif defined(_WIN32) #ifdef IFC_GEOMSERIALIZATION_EXPORTS #define IFC_GEOMSERIALIZATION_API __declspec(dllexport) #else diff --git a/src/ifcgeom/Serialization/opencascade_geometry_ifc_writer_plugin.cpp b/src/ifcgeom/Serialization/opencascade_geometry_ifc_writer_plugin.cpp new file mode 100644 index 0000000000..3a76ffae9a --- /dev/null +++ b/src/ifcgeom/Serialization/opencascade_geometry_ifc_writer_plugin.cpp @@ -0,0 +1,57 @@ +/******************************************************************************** + * * + * This file is part of IfcOpenShell. * + * * + * IfcOpenShell is free software: you can redistribute it and/or modify * + * it under the terms of the Lesser GNU General Public License as published by * + * the Free Software Foundation, either version 3.0 of the License, or * + * (at your option) any later version. * + * * + * IfcOpenShell is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * Lesser GNU General Public License for more details. * + * * + * You should have received a copy of the Lesser GNU General Public License * + * along with this program. If not, see . * + * * + ********************************************************************************/ + +#include "opencascade_geometry_ifc_writer_plugin.h" + +#include + +namespace { + constexpr const char* opencascade_geometry_ifc_writer_plugin_prefix = "geometry.serialization."; +} + +const char* IfcGeom::opencascade_geometry_ifc_writer_plugin_registration_symbol() { + return "ifcopenshell_register_opencascade_geometry_ifc_writer_plugin_v1"; +} + +ifcopenshell::plugin::metadata IfcGeom::opencascade_geometry_ifc_writer_plugin_metadata(const std::string& schema_name) { + ifcopenshell::plugin::metadata metadata; + metadata.kind_ = ifcopenshell::plugin::kind::opencascade_geometry_ifc_writer; + metadata.id = opencascade_geometry_ifc_writer_plugin_prefix + boost::to_lower_copy(schema_name); + metadata.schema = boost::to_upper_copy(schema_name); + return metadata; +} + +std::filesystem::path IfcGeom::opencascade_geometry_ifc_writer_plugin_directory() { + return ifcopenshell::plugin::module_directory(reinterpret_cast(&IfcGeom::load_opencascade_geometry_ifc_writer_plugins)); +} + +void IfcGeom::load_opencascade_geometry_ifc_writer_plugins(opencascade_geometry_ifc_writer_registry& registry) { + ifcopenshell::plugin::manager manager; + manager.add_search_path(opencascade_geometry_ifc_writer_plugin_directory()); + + for (const auto& path : manager.discover(opencascade_geometry_ifc_writer_plugin_prefix)) { + auto module = manager.load(path); + if (module.meta().kind_ != ifcopenshell::plugin::kind::opencascade_geometry_ifc_writer) { + continue; + } + + auto register_plugin = module.get_alias(opencascade_geometry_ifc_writer_plugin_registration_symbol()); + register_plugin(registry, module); + } +} diff --git a/src/ifcgeom/Serialization/opencascade_geometry_ifc_writer_plugin.h b/src/ifcgeom/Serialization/opencascade_geometry_ifc_writer_plugin.h new file mode 100644 index 0000000000..7d35784ad6 --- /dev/null +++ b/src/ifcgeom/Serialization/opencascade_geometry_ifc_writer_plugin.h @@ -0,0 +1,38 @@ +/******************************************************************************** + * * + * This file is part of IfcOpenShell. * + * * + * IfcOpenShell is free software: you can redistribute it and/or modify * + * it under the terms of the Lesser GNU General Public License as published by * + * the Free Software Foundation, either version 3.0 of the License, or * + * (at your option) any later version. * + * * + * IfcOpenShell is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * Lesser GNU General Public License for more details. * + * * + * You should have received a copy of the Lesser GNU General Public License * + * along with this program. If not, see . * + * * + ********************************************************************************/ + +#ifndef IFCOPENSHELL_OPENCASCADE_GEOMETRY_IFC_WRITER_PLUGIN_H +#define IFCOPENSHELL_OPENCASCADE_GEOMETRY_IFC_WRITER_PLUGIN_H + +#include "Serialization.h" + +#include + +namespace IfcGeom { + +typedef void register_opencascade_geometry_ifc_writer_plugin_fn(opencascade_geometry_ifc_writer_registry&, const ifcopenshell::plugin::module&); + +IFC_GEOMSERIALIZATION_API const char* opencascade_geometry_ifc_writer_plugin_registration_symbol(); +IFC_GEOMSERIALIZATION_API ifcopenshell::plugin::metadata opencascade_geometry_ifc_writer_plugin_metadata(const std::string& schema_name); +IFC_GEOMSERIALIZATION_API std::filesystem::path opencascade_geometry_ifc_writer_plugin_directory(); +IFC_GEOMSERIALIZATION_API void load_opencascade_geometry_ifc_writer_plugins(opencascade_geometry_ifc_writer_registry& registry); + +} + +#endif diff --git a/src/ifcgeom/Serialization/schema/CMakeLists.txt b/src/ifcgeom/Serialization/schema/CMakeLists.txt index 0ff0938674..5d56ebf383 100644 --- a/src/ifcgeom/Serialization/schema/CMakeLists.txt +++ b/src/ifcgeom/Serialization/schema/CMakeLists.txt @@ -1,7 +1,12 @@ foreach(schema ${SCHEMA_VERSIONS}) - add_library(geometry_serializer_ifc${schema} STATIC Serialization.cpp) - target_link_libraries(geometry_serializer_ifc${schema} ${OpenCASCADE_LIBRARIES}) - set_target_properties(geometry_serializer_ifc${schema} PROPERTIES COMPILE_FLAGS "-DIFC_GEOMSERIALIZATION_EXPORTS -DIfcSchema=Ifc${schema}") + add_library(geometry_serializer_ifc${schema} SHARED Serialization.cpp plugin.cpp) + target_link_libraries(geometry_serializer_ifc${schema} PRIVATE plugin geometry_serializer IfcGeom IfcParse ${OpenCASCADE_LIBRARIES}) + set_target_properties(geometry_serializer_ifc${schema} PROPERTIES + COMPILE_FLAGS "-DIfcSchema=Ifc${schema}" + OUTPUT_NAME "geometry.serialization.ifc${schema}" + RUNTIME_OUTPUT_DIRECTORY "${geometry_serialization_plugin_runtime_dir}" + LIBRARY_OUTPUT_DIRECTORY "${geometry_serialization_plugin_runtime_dir}" + ) list(APPEND geometry_serializer_libraries geometry_serializer_ifc${schema}) endforeach() -set(geometry_serializer_libraries ${geometry_serializer_libraries} PARENT_SCOPE) \ No newline at end of file +set(geometry_serializer_libraries ${geometry_serializer_libraries} PARENT_SCOPE) diff --git a/src/ifcgeom/Serialization/schema/plugin.cpp b/src/ifcgeom/Serialization/schema/plugin.cpp new file mode 100644 index 0000000000..3c62991f00 --- /dev/null +++ b/src/ifcgeom/Serialization/schema/plugin.cpp @@ -0,0 +1,69 @@ +/******************************************************************************** + * * + * This file is part of IfcOpenShell. * + * * + * IfcOpenShell is free software: you can redistribute it and/or modify * + * it under the terms of the Lesser GNU General Public License as published by * + * the Free Software Foundation, either version 3.0 of the License, or * + * (at your option) any later version. * + * * + * IfcOpenShell is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * Lesser GNU General Public License for more details. * + * * + * You should have received a copy of the Lesser GNU General Public License * + * along with this program. If not, see . * + * * + ********************************************************************************/ + +#include "../opencascade_geometry_ifc_writer_plugin.h" + +#include "../../../ifcgeom/kernels/opencascade/OpenCascadeConversionResult.h" +#include "../../../ifcparse/exception.h" +#include "../../../ifcparse/macros.h" + +#include + +express::Base POSTFIX_SCHEMA(serialise)(ifcopenshell::file& f, const TopoDS_Shape& shape, bool advanced); +express::Base POSTFIX_SCHEMA(tesselate)(ifcopenshell::file& f, const TopoDS_Shape& shape, double deflection); + +namespace { + const ifcopenshell::geometry::OpenCascadeShape& require_opencascade_shape(const IfcGeom::ConversionResultShape& shape) { + const auto* oc_shape = dynamic_cast(&shape); + if (!oc_shape) { + throw ifcopenshell::exception("OpenCascade geometry IFC writer requires an opencascade conversion result shape"); + } + return *oc_shape; + } + + express::Base serialise_adapter(ifcopenshell::file& f, const IfcGeom::ConversionResultShape& shape, bool advanced) { + return POSTFIX_SCHEMA(serialise)(f, require_opencascade_shape(shape).shape(), advanced); + } + + express::Base tesselate_adapter(ifcopenshell::file& f, const IfcGeom::ConversionResultShape& shape, double deflection) { + return POSTFIX_SCHEMA(tesselate)(f, require_opencascade_shape(shape).shape(), deflection); + } +} + +namespace IfcGeom { + namespace opencascade_geometry_ifc_writer_plugin { + + ifcopenshell::plugin::abi_info plugin_abi() { + return ifcopenshell::plugin::host_abi(); + } + + ifcopenshell::plugin::metadata plugin_metadata() { + return opencascade_geometry_ifc_writer_plugin_metadata(STRINGIFY(IfcSchema)); + } + + void register_plugin(opencascade_geometry_ifc_writer_registry& registry, const ifcopenshell::plugin::module& module) { + registry.bind(STRINGIFY(IfcSchema), &serialise_adapter, &tesselate_adapter, module); + } + + } +} + +BOOST_DLL_ALIAS(IfcGeom::opencascade_geometry_ifc_writer_plugin::plugin_abi, ifcopenshell_plugin_abi_v1) +BOOST_DLL_ALIAS(IfcGeom::opencascade_geometry_ifc_writer_plugin::plugin_metadata, ifcopenshell_plugin_metadata_v1) +BOOST_DLL_ALIAS(IfcGeom::opencascade_geometry_ifc_writer_plugin::register_plugin, ifcopenshell_register_opencascade_geometry_ifc_writer_plugin_v1) diff --git a/src/ifcgeom/abstract_mapping.cpp b/src/ifcgeom/abstract_mapping.cpp index de26611c55..18c074e926 100644 --- a/src/ifcgeom/abstract_mapping.cpp +++ b/src/ifcgeom/abstract_mapping.cpp @@ -1,57 +1,29 @@ #include "abstract_mapping.h" #include "../ifcparse/file.h" +#include "../ifcgeom/mapping_plugin.h" -#include -#include +#include #include -#ifndef SCHEMA_SEQ -static_assert(false, "A boost preprocessor sequence of schema identifiers is needed for this file to compile."); -#endif - -// Declares the schema-based external kernel initialization routines: -// - extern void init_MappingImplementation_Ifc2x3(IfcGeom::impl::mapping_registry*); -// - ... -#define EXTERNAL_DEFS(r, data, elem) \ - extern void BOOST_PP_CAT(init_MappingImplementation_Ifc, elem)(ifcopenshell::geometry::impl::mapping_registry*); - -// Declares the schema-based external iterator initialization routines: -// - init_MappingImplementation_Ifc2x3(this); -// - ... -#define CALL_DEFS(r, data, elem) \ - BOOST_PP_CAT(init_MappingImplementation_Ifc, elem)(®istry); - -BOOST_PP_SEQ_FOR_EACH(EXTERNAL_DEFS, , SCHEMA_SEQ) - namespace { std::string mapping_key(const std::string& schema_name) { return boost::to_lower_copy(schema_name); } - ifcopenshell::plugin::module builtin_mapping_module(const std::string& schema_name) { - ifcopenshell::plugin::metadata metadata; - metadata.kind_ = ifcopenshell::plugin::kind::mapping; - metadata.id = "geometry.mapping." + boost::to_lower_copy(schema_name); - metadata.schema = schema_name; - return ifcopenshell::plugin::module::builtin(metadata); - } - } ifcopenshell::geometry::impl::mapping_registry& ifcopenshell::geometry::impl::mapping_registry_instance() { static mapping_registry registry; static std::once_flag once; - std::call_once(once, []() { - BOOST_PP_SEQ_FOR_EACH(CALL_DEFS, , SCHEMA_SEQ) - }); + std::call_once(once, load_mapping_plugins, std::ref(registry)); return registry; } void ifcopenshell::geometry::impl::mapping_registry::bind(const std::string& schema_name, ifcopenshell::geometry::impl::mapping_fn fn, const plugin::module& module) { auto& entry = entries_[mapping_key(schema_name)]; entry.fn_ = fn; - entry.module_ = module.meta().id.empty() ? builtin_mapping_module(schema_name) : module; + entry.module_ = module.meta().id.empty() ? plugin::module(mapping_plugin_metadata(schema_name)) : module; } ifcopenshell::geometry::abstract_mapping* ifcopenshell::geometry::impl::mapping_registry::construct(ifcopenshell::file* file, Settings& s) { @@ -75,7 +47,7 @@ ifcopenshell::geometry::impl::MappingFactoryImplementation::MappingFactoryImplem } void ifcopenshell::geometry::impl::MappingFactoryImplementation::bind(const std::string& schema_name, ifcopenshell::geometry::impl::mapping_fn fn) { - mapping_registry_instance().bind(schema_name, fn, builtin_mapping_module(schema_name)); + mapping_registry_instance().bind(schema_name, fn, plugin::module(mapping_plugin_metadata(schema_name))); } ifcopenshell::geometry::abstract_mapping* ifcopenshell::geometry::impl::MappingFactoryImplementation::construct(ifcopenshell::file* file, Settings& s) { diff --git a/src/ifcgeom/abstract_mapping.h b/src/ifcgeom/abstract_mapping.h index e49f4851c2..a3c794a5be 100644 --- a/src/ifcgeom/abstract_mapping.h +++ b/src/ifcgeom/abstract_mapping.h @@ -82,13 +82,13 @@ namespace geometry { class IFC_GEOM_API mapping_registry { public: - void bind(const std::string& schema_name, mapping_fn fn, const plugin::module& module = plugin::module()); + void bind(const std::string& schema_name, mapping_fn fn, const ifcopenshell::plugin::module& module = ifcopenshell::plugin::module()); abstract_mapping* construct(ifcopenshell::file* file, Settings& settings); private: struct entry { mapping_fn fn_; - plugin::module module_; + ifcopenshell::plugin::module module_; }; std::map entries_; diff --git a/src/ifcgeom/ifc_geom_api.h b/src/ifcgeom/ifc_geom_api.h index 18b4a9f87b..380a202166 100644 --- a/src/ifcgeom/ifc_geom_api.h +++ b/src/ifcgeom/ifc_geom_api.h @@ -20,7 +20,9 @@ #ifndef IFC_GEOM_API_H #define IFC_GEOM_API_H -#ifdef _WIN32 +#ifdef SWIG + #define IFC_GEOM_API +#elif defined(_WIN32) #ifdef IFC_GEOM_EXPORTS #define IFC_GEOM_API __declspec(dllexport) #else diff --git a/src/ifcgeom/kernel_plugin.cpp b/src/ifcgeom/kernel_plugin.cpp index 6df0e14836..9267fee5e6 100644 --- a/src/ifcgeom/kernel_plugin.cpp +++ b/src/ifcgeom/kernel_plugin.cpp @@ -19,12 +19,6 @@ #include "kernel_plugin.h" -#ifndef _WIN32 -#include -#else -#include -#endif - #include namespace { @@ -43,30 +37,7 @@ ifcopenshell::plugin::metadata ifcopenshell::geometry::kernels::kernel_plugin_me } std::filesystem::path ifcopenshell::geometry::kernels::kernel_plugin_directory() { -#ifdef _WIN32 - HMODULE module_handle = nullptr; - if (!GetModuleHandleExW( - GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, - reinterpret_cast(&ifcopenshell::geometry::kernels::load_kernel_plugins), - &module_handle)) { - throw std::runtime_error("Unable to resolve IfcGeom module path"); - } - - wchar_t buffer[MAX_PATH]; - const DWORD length = GetModuleFileNameW(module_handle, buffer, MAX_PATH); - if (length == 0) { - throw std::runtime_error("Unable to read IfcGeom module filename"); - } - - return std::filesystem::path(std::wstring(buffer, length)).parent_path(); -#else - Dl_info info; - if (dladdr(reinterpret_cast(&ifcopenshell::geometry::kernels::load_kernel_plugins), &info) == 0 || !info.dli_fname) { - throw std::runtime_error("Unable to resolve IfcGeom module path"); - } - - return std::filesystem::path(info.dli_fname).parent_path(); -#endif + return plugin::module_directory(reinterpret_cast(&ifcopenshell::geometry::kernels::load_kernel_plugins)); } void ifcopenshell::geometry::kernels::load_kernel_plugins(kernel_registry& registry) { diff --git a/src/ifcgeom/kernel_plugin.h b/src/ifcgeom/kernel_plugin.h index 5a72beb433..5183be4a75 100644 --- a/src/ifcgeom/kernel_plugin.h +++ b/src/ifcgeom/kernel_plugin.h @@ -28,10 +28,10 @@ namespace ifcopenshell { namespace geometry { namespace kernels { - typedef void register_kernel_plugin_fn(kernel_registry&, const plugin::module&); + typedef void register_kernel_plugin_fn(kernel_registry&, const ifcopenshell::plugin::module&); IFC_GEOM_API const char* kernel_plugin_registration_symbol(); - IFC_GEOM_API plugin::metadata kernel_plugin_metadata(const std::string& plugin_name); + IFC_GEOM_API ifcopenshell::plugin::metadata kernel_plugin_metadata(const std::string& plugin_name); IFC_GEOM_API std::filesystem::path kernel_plugin_directory(); IFC_GEOM_API void load_kernel_plugins(kernel_registry& registry); diff --git a/src/ifcgeom/kernel_registry.h b/src/ifcgeom/kernel_registry.h index cab91a0ec6..ec36969019 100644 --- a/src/ifcgeom/kernel_registry.h +++ b/src/ifcgeom/kernel_registry.h @@ -45,7 +45,7 @@ namespace ifcopenshell { public: typedef boost::function2 create_fn; - void bind(const kernel_info& info, create_fn create, const plugin::module& module = plugin::module()); + void bind(const kernel_info& info, create_fn create, const ifcopenshell::plugin::module& module = ifcopenshell::plugin::module()); bool has(const std::string& backend_id) const; std::unique_ptr create(const std::string& backend_id, ifcopenshell::file* file, Settings& settings) const; std::vector kernels() const; @@ -54,7 +54,7 @@ namespace ifcopenshell { struct entry { kernel_info info_; create_fn create_; - plugin::module module_; + ifcopenshell::plugin::module module_; }; std::map entries_; diff --git a/src/ifcgeom/kernels/CMakeLists.txt b/src/ifcgeom/kernels/CMakeLists.txt index 4973758444..af5b415ef9 100644 --- a/src/ifcgeom/kernels/CMakeLists.txt +++ b/src/ifcgeom/kernels/CMakeLists.txt @@ -6,6 +6,12 @@ foreach(kernel ${GEOMETRY_KERNELS}) string(TOUPPER ${kernel} KERNEL_UPPER) file(GLOB IFCGEOM_H_FILES ${kernel}/*.h) file(GLOB IFCGEOM_CPP_FILES ${kernel}/*.cpp) + if(${kernel} STREQUAL "opencascade") + list(REMOVE_ITEM IFCGEOM_CPP_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/${kernel}/tree_brep_plugin.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/${kernel}/tree_trianglebvh_plugin.cpp + ) + endif() set(IFCGEOM_FILES ${IFCGEOM_CPP_FILES} ${IFCGEOM_H_FILES}) set(KERNEL_TARGET "geometry_kernel_${kernel}") @@ -37,6 +43,23 @@ foreach(kernel ${GEOMETRY_KERNELS}) install(TARGETS ${KERNEL_TARGET_SIMPLE}) elseif(${kernel} STREQUAL "opencascade") target_link_libraries(${KERNEL_TARGET} PRIVATE ${OpenCASCADE_LIBRARIES}) + + foreach(tree_backend brep trianglebvh) + set(TREE_TARGET "geometry_tree_opencascade_${tree_backend}") + add_library(${TREE_TARGET} SHARED + ${kernel}/tree_${tree_backend}_plugin.cpp + ${kernel}/tree_backends.h + ) + set_target_properties(${TREE_TARGET} PROPERTIES + COMPILE_FLAGS "-DIFC_GEOMLIBRARY_EXPORTS" + OUTPUT_NAME "geometry.tree.opencascade.${tree_backend}" + RUNTIME_OUTPUT_DIRECTORY "${kernel_plugin_runtime_dir}" + LIBRARY_OUTPUT_DIRECTORY "${kernel_plugin_runtime_dir}" + ) + list(APPEND tree_libraries ${TREE_TARGET}) + target_link_libraries(${TREE_TARGET} PRIVATE plugin IfcGeom geometry_kernel_opencascade ${OpenCASCADE_LIBRARIES} Eigen3::Eigen) + install(TARGETS ${TREE_TARGET}) + endforeach() endif() install(FILES ${IFCGEOM_H_FILES} @@ -45,3 +68,4 @@ foreach(kernel ${GEOMETRY_KERNELS}) endforeach() set(kernel_libraries ${kernel_libraries} PARENT_SCOPE) +set(tree_libraries ${tree_libraries} PARENT_SCOPE) diff --git a/src/ifcgeom/kernels/ifc_geomlibrary_api.h b/src/ifcgeom/kernels/ifc_geomlibrary_api.h index 5f44d31169..bac5f5799b 100644 --- a/src/ifcgeom/kernels/ifc_geomlibrary_api.h +++ b/src/ifcgeom/kernels/ifc_geomlibrary_api.h @@ -20,7 +20,9 @@ #ifndef IFC_GEOMLIBRARY_API_H #define IFC_GEOMLIBRARY_API_H -#ifdef _WIN32 +#ifdef SWIG +#define IFC_GEOMLIBRARY_API +#elif defined(_WIN32) #ifdef IFC_GEOMLIBRARY_EXPORTS #define IFC_GEOMLIBRARY_API __declspec(dllexport) #else diff --git a/src/ifcgeom/kernels/opencascade/IfcGeomTree.h b/src/ifcgeom/kernels/opencascade/IfcGeomTree.h index f680e9d645..0a760fe1b9 100644 --- a/src/ifcgeom/kernels/opencascade/IfcGeomTree.h +++ b/src/ifcgeom/kernels/opencascade/IfcGeomTree.h @@ -24,6 +24,7 @@ #include "../../../ifcgeom/IfcGeomElement.h" #include "../../../ifcgeom/Iterator.h" +#include "../../../ifcgeom/tree.h" #include "OpenCascadeConversionResult.h" #include "OpenCascadeKernel.h" #include "base_utils.h" @@ -69,26 +70,6 @@ namespace IfcGeom { - - struct ray_intersection_result { - double distance; - int style_index; - express::Entity instance; - std::array position; - std::array normal; - double ray_distance; - double dot_product; - }; - - struct clash { - int clash_type; // 0 = protrusion, 1 = pierce, 2 = collision, 3 = clearance - express::Base a; - express::Base b; - double distance; - std::array p1; - std::array p2; - }; - namespace { // Approximates the distance `other` protrudes into `volume` by finding the @@ -1481,20 +1462,20 @@ namespace IfcGeom { }; } - class tree : public impl::tree { + class opencascade_tree : public impl::tree { public: - tree() {}; + opencascade_tree() {}; - tree(ifcopenshell::file& f) { + opencascade_tree(ifcopenshell::file& f) { add_file(f, ifcopenshell::geometry::Settings{}); } - tree(ifcopenshell::file& f, ifcopenshell::geometry::Settings settings) { + opencascade_tree(ifcopenshell::file& f, ifcopenshell::geometry::Settings settings) { add_file(f, settings); } - tree(IfcGeom::Iterator& it) { + opencascade_tree(IfcGeom::Iterator& it) { add_file(it); } diff --git a/src/ifcgeom/kernels/opencascade/tree_backends.h b/src/ifcgeom/kernels/opencascade/tree_backends.h new file mode 100644 index 0000000000..f3805f73d7 --- /dev/null +++ b/src/ifcgeom/kernels/opencascade/tree_backends.h @@ -0,0 +1,182 @@ +/******************************************************************************** + * * + * This file is part of IfcOpenShell. * + * * + * IfcOpenShell is free software: you can redistribute it and/or modify * + * it under the terms of the Lesser GNU General Public License as published by * + * the Free Software Foundation, either version 3.0 of the License, or * + * (at your option) any later version. * + * * + * IfcOpenShell is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * Lesser GNU General Public License for more details. * + * * + * You should have received a copy of the Lesser GNU General Public License * + * along with this program. If not, see . * + * * + ********************************************************************************/ + +#ifndef IFCOPENSHELL_OPENCASCADE_TREE_BACKENDS_H +#define IFCOPENSHELL_OPENCASCADE_TREE_BACKENDS_H + +#include "../../kernel_registry.h" +#include "../../tree_registry.h" +#include "../../../ifcparse/exception.h" +#include "IfcGeomTree.h" + +namespace ifcopenshell { + namespace geometry { + namespace trees { + namespace opencascade_tree_backends { + + inline gp_Pnt make_point(const IfcGeom::tree_point& point) { + return gp_Pnt(point[0], point[1], point[2]); + } + + inline Bnd_Box make_box(const IfcGeom::tree_box& bounds) { + Bnd_Box box; + box.Add(make_point(bounds[0])); + box.Add(make_point(bounds[1])); + return box; + } + + class brep_tree : public abstract_tree { + public: + std::string_view backend_id() const override { + return "opencascade.brep"; + } + + void add_file(ifcopenshell::file& file, const ifcopenshell::geometry::Settings& settings) override { + auto settings_ = settings; + settings_.get().value = ifcopenshell::geometry::settings::NATIVE; + settings_.get().value = true; + settings_.get().value = true; + + IfcGeom::Iterator iterator(ifcopenshell::geometry::kernels::construct(&file, "opencascade", settings_), settings_, &file, {}, 1); + if (iterator.initialize()) { + do { + add_element(iterator.get()); + } while (iterator.next()); + } + } + + void add_element(IfcGeom::Element* element) override { + auto* brep = dynamic_cast(element); + if (!brep) { + throw ifcopenshell::exception("Tree backend 'opencascade.brep' requires native BRep elements"); + } + tree_.add_element(brep); + } + + std::vector select_box(const express::Entity& entity, bool completely_within, double extend) const override { + return tree_.select_box(entity, completely_within, extend); + } + + std::vector select_box(const IfcGeom::tree_point& point) const override { + return tree_.select_box(make_point(point)); + } + + std::vector select_box(const IfcGeom::tree_box& bounds, bool completely_within) const override { + return tree_.select_box(make_box(bounds), completely_within); + } + + std::vector select(const express::Entity& entity, bool completely_within, double extend) const override { + return tree_.select(entity, completely_within, extend); + } + + std::vector select(const IfcGeom::Element* element, bool completely_within, double extend) const override { + auto* brep = dynamic_cast(element); + if (!brep) { + throw ifcopenshell::exception("Tree backend 'opencascade.brep' requires BRep elements for select()"); + } + return tree_.select(brep, completely_within, extend); + } + + std::vector select(const IfcGeom::tree_point& point, double extend) const override { + return tree_.select(make_point(point), extend); + } + + std::vector select_ray(const IfcGeom::tree_point& origin, const IfcGeom::tree_point& direction, double length) const override { + return tree_.select_ray(make_point(origin), gp_Dir(direction[0], direction[1], direction[2]), length); + } + + const std::vector& distances() const override { + return tree_.distances(); + } + + const std::vector& protrusion_distances() const override { + return tree_.protrusion_distances(); + } + + bool enable_face_styles() const override { + return tree_.enable_face_styles(); + } + + void enable_face_styles(bool enable) override { + tree_.enable_face_styles(enable); + } + + const std::vector& styles() const override { + return tree_.styles(); + } + + private: + IfcGeom::opencascade_tree tree_; + }; + + class trianglebvh_tree : public abstract_tree { + public: + std::string_view backend_id() const override { + return "opencascade.trianglebvh"; + } + + void add_file(ifcopenshell::file& file, const ifcopenshell::geometry::Settings& settings) override { + auto settings_ = settings; + settings_.get().value = ifcopenshell::geometry::settings::TRIANGULATED; + settings_.get().value = true; + + IfcGeom::Iterator iterator(ifcopenshell::geometry::kernels::construct(&file, "opencascade", settings_), settings_, &file, {}, 1); + if (iterator.initialize()) { + do { + add_element(iterator.get()); + } while (iterator.next()); + } + } + + void add_element(IfcGeom::Element* element) override { + auto* triangulation = dynamic_cast(element); + if (!triangulation) { + throw ifcopenshell::exception("Tree backend 'opencascade.trianglebvh' requires triangulation elements"); + } + tree_.add_element(triangulation); + } + + std::vector clash_intersection_many(const std::vector& set_a, const std::vector& set_b, double tolerance, bool check_all) const override { + return tree_.clash_intersection_many(set_a, set_b, tolerance, check_all); + } + + std::vector clash_collision_many(const std::vector& set_a, const std::vector& set_b, bool allow_touching) const override { + return tree_.clash_collision_many(set_a, set_b, allow_touching); + } + + std::vector clash_clearance_many(const std::vector& set_a, const std::vector& set_b, double clearance, bool check_all) const override { + return tree_.clash_clearance_many(set_a, set_b, clearance, check_all); + } + +#ifdef WITH_HDF5 + void write_h5() override { + tree_.write_h5(); + } +#endif + + private: + IfcGeom::opencascade_tree tree_; + }; + + } + } + } +} + +#endif diff --git a/src/ifcgeom/kernels/opencascade/tree_brep_plugin.cpp b/src/ifcgeom/kernels/opencascade/tree_brep_plugin.cpp new file mode 100644 index 0000000000..2dea2bed1b --- /dev/null +++ b/src/ifcgeom/kernels/opencascade/tree_brep_plugin.cpp @@ -0,0 +1,55 @@ +/******************************************************************************** + * * + * This file is part of IfcOpenShell. * + * * + * IfcOpenShell is free software: you can redistribute it and/or modify * + * it under the terms of the Lesser GNU General Public License as published by * + * the Free Software Foundation, either version 3.0 of the License, or * + * (at your option) any later version. * + * * + * IfcOpenShell is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * Lesser GNU General Public License for more details. * + * * + * You should have received a copy of the Lesser GNU General Public License * + * along with this program. If not, see . * + * * + ********************************************************************************/ + +#include "../../tree_plugin.h" +#include "tree_backends.h" + +#include + +namespace ifcopenshell { + namespace geometry { + namespace trees { + namespace opencascade_brep_tree_plugin { + + plugin::abi_info plugin_abi() { + return plugin::host_abi(); + } + + plugin::metadata plugin_metadata() { + return tree_plugin_metadata("opencascade.brep"); + } + + abstract_tree* create_tree() { + return new opencascade_tree_backends::brep_tree(); + } + + void register_plugin(tree_registry& registry, const plugin::module& module) { + tree_info info; + info.backend_id = "opencascade.brep"; + registry.bind(info, create_tree, module); + } + + } + } + } +} + +BOOST_DLL_ALIAS(ifcopenshell::geometry::trees::opencascade_brep_tree_plugin::plugin_abi, ifcopenshell_plugin_abi_v1) +BOOST_DLL_ALIAS(ifcopenshell::geometry::trees::opencascade_brep_tree_plugin::plugin_metadata, ifcopenshell_plugin_metadata_v1) +BOOST_DLL_ALIAS(ifcopenshell::geometry::trees::opencascade_brep_tree_plugin::register_plugin, ifcopenshell_register_tree_plugin_v1) diff --git a/src/ifcgeom/kernels/opencascade/tree_trianglebvh_plugin.cpp b/src/ifcgeom/kernels/opencascade/tree_trianglebvh_plugin.cpp new file mode 100644 index 0000000000..71d262cc99 --- /dev/null +++ b/src/ifcgeom/kernels/opencascade/tree_trianglebvh_plugin.cpp @@ -0,0 +1,55 @@ +/******************************************************************************** + * * + * This file is part of IfcOpenShell. * + * * + * IfcOpenShell is free software: you can redistribute it and/or modify * + * it under the terms of the Lesser GNU General Public License as published by * + * the Free Software Foundation, either version 3.0 of the License, or * + * (at your option) any later version. * + * * + * IfcOpenShell is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * Lesser GNU General Public License for more details. * + * * + * You should have received a copy of the Lesser GNU General Public License * + * along with this program. If not, see . * + * * + ********************************************************************************/ + +#include "../../tree_plugin.h" +#include "tree_backends.h" + +#include + +namespace ifcopenshell { + namespace geometry { + namespace trees { + namespace opencascade_trianglebvh_tree_plugin { + + plugin::abi_info plugin_abi() { + return plugin::host_abi(); + } + + plugin::metadata plugin_metadata() { + return tree_plugin_metadata("opencascade.trianglebvh"); + } + + abstract_tree* create_tree() { + return new opencascade_tree_backends::trianglebvh_tree(); + } + + void register_plugin(tree_registry& registry, const plugin::module& module) { + tree_info info; + info.backend_id = "opencascade.trianglebvh"; + registry.bind(info, create_tree, module); + } + + } + } + } +} + +BOOST_DLL_ALIAS(ifcopenshell::geometry::trees::opencascade_trianglebvh_tree_plugin::plugin_abi, ifcopenshell_plugin_abi_v1) +BOOST_DLL_ALIAS(ifcopenshell::geometry::trees::opencascade_trianglebvh_tree_plugin::plugin_metadata, ifcopenshell_plugin_metadata_v1) +BOOST_DLL_ALIAS(ifcopenshell::geometry::trees::opencascade_trianglebvh_tree_plugin::register_plugin, ifcopenshell_register_tree_plugin_v1) diff --git a/src/ifcgeom/mapping/CMakeLists.txt b/src/ifcgeom/mapping/CMakeLists.txt index ef5a658c06..77778df425 100644 --- a/src/ifcgeom/mapping/CMakeLists.txt +++ b/src/ifcgeom/mapping/CMakeLists.txt @@ -1,14 +1,21 @@ find_package(Eigen3 REQUIRED) +set(mapping_plugin_runtime_dir "${CMAKE_BINARY_DIR}/ifcgeom/$") + foreach(schema ${SCHEMA_VERSIONS}) file(GLOB IFCGEOM_I_FILES *.i) file(GLOB IFCGEOM_H_FILES *.h) file(GLOB IFCGEOM_CPP_FILES *.cpp) set(IFCGEOM_FILES ${IFCGEOM_CPP_FILES} ${IFCGEOM_H_FILES} ${IFCGEOM_I_FILES}) - add_library(geometry_mapping_ifc${schema} OBJECT ${IFCGEOM_FILES}) - set_target_properties(geometry_mapping_ifc${schema} PROPERTIES COMPILE_FLAGS "-DIFC_GEOM_EXPORTS -DIfcSchema=Ifc${schema}") - target_link_libraries(geometry_mapping_ifc${schema} IfcParse Eigen3::Eigen) + add_library(geometry_mapping_ifc${schema} SHARED ${IFCGEOM_FILES}) + set_target_properties(geometry_mapping_ifc${schema} PROPERTIES + COMPILE_FLAGS "-DIfcSchema=Ifc${schema}" + OUTPUT_NAME "geometry.mapping.ifc${schema}" + RUNTIME_OUTPUT_DIRECTORY "${mapping_plugin_runtime_dir}" + LIBRARY_OUTPUT_DIRECTORY "${mapping_plugin_runtime_dir}" + ) + target_link_libraries(geometry_mapping_ifc${schema} PRIVATE plugin IfcGeom IfcParse Eigen3::Eigen ${OpenCASCADE_LIBRARIES}) list(APPEND mapping_libraries geometry_mapping_ifc${schema}) diff --git a/src/ifcgeom/mapping/plugin.cpp b/src/ifcgeom/mapping/plugin.cpp new file mode 100644 index 0000000000..ebddd6387b --- /dev/null +++ b/src/ifcgeom/mapping/plugin.cpp @@ -0,0 +1,58 @@ +/******************************************************************************** + * * + * This file is part of IfcOpenShell. * + * * + * IfcOpenShell is free software: you can redistribute it and/or modify * + * it under the terms of the Lesser GNU General Public License as published by * + * the Free Software Foundation, either version 3.0 of the License, or * + * (at your option) any later version. * + * * + * IfcOpenShell is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * Lesser GNU General Public License for more details. * + * * + * You should have received a copy of the Lesser GNU General Public License * + * along with this program. If not, see . * + * * + ********************************************************************************/ + +#include "../mapping_plugin.h" +#include "mapping.h" + +#include + +namespace ifcopenshell { + namespace geometry { + namespace impl { + namespace mapping_plugin { + + namespace { + struct POSTFIX_SCHEMA(factory_t) { + abstract_mapping* operator()(ifcopenshell::file* file, Settings& settings) const { + return new POSTFIX_SCHEMA(mapping)(file, settings); + } + }; + } + + plugin::abi_info plugin_abi() { + return plugin::host_abi(); + } + + plugin::metadata plugin_metadata() { + return mapping_plugin_metadata(STRINGIFY(IfcSchema)); + } + + void register_plugin(mapping_registry& registry, const plugin::module& module) { + POSTFIX_SCHEMA(factory_t) factory; + registry.bind(STRINGIFY(IfcSchema), factory, module); + } + + } + } + } +} + +BOOST_DLL_ALIAS(ifcopenshell::geometry::impl::mapping_plugin::plugin_abi, ifcopenshell_plugin_abi_v1) +BOOST_DLL_ALIAS(ifcopenshell::geometry::impl::mapping_plugin::plugin_metadata, ifcopenshell_plugin_metadata_v1) +BOOST_DLL_ALIAS(ifcopenshell::geometry::impl::mapping_plugin::register_plugin, ifcopenshell_register_mapping_plugin_v1) diff --git a/src/ifcgeom/mapping_plugin.cpp b/src/ifcgeom/mapping_plugin.cpp new file mode 100644 index 0000000000..3136ebde5f --- /dev/null +++ b/src/ifcgeom/mapping_plugin.cpp @@ -0,0 +1,57 @@ +/******************************************************************************** + * * + * This file is part of IfcOpenShell. * + * * + * IfcOpenShell is free software: you can redistribute it and/or modify * + * it under the terms of the Lesser GNU General Public License as published by * + * the Free Software Foundation, either version 3.0 of the License, or * + * (at your option) any later version. * + * * + * IfcOpenShell is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * Lesser GNU General Public License for more details. * + * * + * You should have received a copy of the Lesser GNU General Public License * + * along with this program. If not, see . * + * * + ********************************************************************************/ + +#include "mapping_plugin.h" + +#include + +namespace { + constexpr const char* mapping_plugin_prefix = "geometry.mapping."; +} + +const char* ifcopenshell::geometry::impl::mapping_plugin_registration_symbol() { + return "ifcopenshell_register_mapping_plugin_v1"; +} + +ifcopenshell::plugin::metadata ifcopenshell::geometry::impl::mapping_plugin_metadata(const std::string& schema_name) { + plugin::metadata metadata; + metadata.kind_ = plugin::kind::mapping; + metadata.id = mapping_plugin_prefix + boost::to_lower_copy(schema_name); + metadata.schema = boost::to_upper_copy(schema_name); + return metadata; +} + +std::filesystem::path ifcopenshell::geometry::impl::mapping_plugin_directory() { + return plugin::module_directory(reinterpret_cast(&ifcopenshell::geometry::impl::load_mapping_plugins)); +} + +void ifcopenshell::geometry::impl::load_mapping_plugins(mapping_registry& registry) { + plugin::manager manager; + manager.add_search_path(mapping_plugin_directory()); + + for (const auto& path : manager.discover(mapping_plugin_prefix)) { + auto module = manager.load(path); + if (module.meta().kind_ != plugin::kind::mapping) { + continue; + } + + auto register_plugin = module.get_alias(mapping_plugin_registration_symbol()); + register_plugin(registry, module); + } +} diff --git a/src/ifcgeom/mapping_plugin.h b/src/ifcgeom/mapping_plugin.h new file mode 100644 index 0000000000..14e25b923d --- /dev/null +++ b/src/ifcgeom/mapping_plugin.h @@ -0,0 +1,42 @@ +/******************************************************************************** + * * + * This file is part of IfcOpenShell. * + * * + * IfcOpenShell is free software: you can redistribute it and/or modify * + * it under the terms of the Lesser GNU General Public License as published by * + * the Free Software Foundation, either version 3.0 of the License, or * + * (at your option) any later version. * + * * + * IfcOpenShell is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * Lesser GNU General Public License for more details. * + * * + * You should have received a copy of the Lesser GNU General Public License * + * along with this program. If not, see . * + * * + ********************************************************************************/ + +#ifndef IFCOPENSHELL_MAPPING_PLUGIN_H +#define IFCOPENSHELL_MAPPING_PLUGIN_H + +#include "../ifcgeom/abstract_mapping.h" + +#include + +namespace ifcopenshell { + namespace geometry { + namespace impl { + + typedef void register_mapping_plugin_fn(mapping_registry&, const ifcopenshell::plugin::module&); + + IFC_GEOM_API const char* mapping_plugin_registration_symbol(); + IFC_GEOM_API ifcopenshell::plugin::metadata mapping_plugin_metadata(const std::string& schema_name); + IFC_GEOM_API std::filesystem::path mapping_plugin_directory(); + IFC_GEOM_API void load_mapping_plugins(mapping_registry& registry); + + } + } +} + +#endif diff --git a/src/ifcgeom/profile_helper.h b/src/ifcgeom/profile_helper.h index 56f7c9b474..7825f93332 100644 --- a/src/ifcgeom/profile_helper.h +++ b/src/ifcgeom/profile_helper.h @@ -1,6 +1,7 @@ #ifndef PROFILE_HELPER_H #define PROFILE_HELPER_H +#include "ifc_geom_api.h" #include "taxonomy.h" namespace ifcopenshell { @@ -27,17 +28,17 @@ namespace ifcopenshell { taxonomy::edge::ptr previous, next; }; - taxonomy::loop::ptr polygon_from_points(const std::vector& ps, bool external = true); + IFC_GEOM_API taxonomy::loop::ptr polygon_from_points(const std::vector& ps, bool external = true); - taxonomy::loop::ptr profile_helper(const taxonomy::matrix4::ptr& m4, const std::vector& points); + IFC_GEOM_API taxonomy::loop::ptr profile_helper(const taxonomy::matrix4::ptr& m4, const std::vector& points); - taxonomy::loop::ptr fillet_loop(taxonomy::loop::ptr lp, double radius); + IFC_GEOM_API taxonomy::loop::ptr fillet_loop(taxonomy::loop::ptr lp, double radius); - void remove_duplicate_points_from_loop(std::vector& polygon, bool closed, double tol); + IFC_GEOM_API void remove_duplicate_points_from_loop(std::vector& polygon, bool closed, double tol); - std::pair, std::vector>> remove_duplicate_points_from_loop(const std::vector& polygon, const std::vector& tags); + IFC_GEOM_API std::pair, std::vector>> remove_duplicate_points_from_loop(const std::vector& polygon, const std::vector& tags); } } -#endif \ No newline at end of file +#endif diff --git a/src/ifcgeom/tree.cpp b/src/ifcgeom/tree.cpp new file mode 100644 index 0000000000..ad99adc576 --- /dev/null +++ b/src/ifcgeom/tree.cpp @@ -0,0 +1,266 @@ +/******************************************************************************** + * * + * This file is part of IfcOpenShell. * + * * + * IfcOpenShell is free software: you can redistribute it and/or modify * + * it under the terms of the Lesser GNU General Public License as published by * + * the Free Software Foundation, either version 3.0 of the License, or * + * (at your option) any later version. * + * * + * IfcOpenShell is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * Lesser GNU General Public License for more details. * + * * + * You should have received a copy of the Lesser GNU General Public License * + * along with this program. If not, see . * + * * + ********************************************************************************/ + +#include "tree.h" + +#include "Iterator.h" +#include "tree_registry.h" +#include "../ifcparse/exception.h" + +#include + +#include +#include +#include + +namespace { + constexpr const char* default_selection_backend_id = "opencascade.brep"; + constexpr const char* default_clash_backend_id = "opencascade.trianglebvh"; + + const std::vector& empty_double_vector() { + static const std::vector values; + return values; + } + + const std::vector& empty_style_vector() { + static const std::vector values; + return values; + } + + std::vector to_product_entities(const std::vector& instances) { + std::vector entities; + entities.reserve(instances.size()); + + for (const auto& instance : instances) { + if (!instance) { + throw ifcopenshell::exception("All instances should be of type IfcProduct"); + } + + auto entity = instance.as(); + if (!entity || !instance.declaration().is("IfcProduct")) { + throw ifcopenshell::exception("All instances should be of type IfcProduct"); + } + entities.push_back(entity); + } + + return entities; + } +} + +class IfcGeom::tree::impl { +public: + impl() = default; + + explicit impl(const std::string& backend_id) + : backend_id_(backend_id) + {} + + ifcopenshell::geometry::trees::abstract_tree& backend() const { + if (!backend_) { + if (backend_id_.empty()) { + throw ifcopenshell::exception("No geometry tree backend configured"); + } + backend_ = ifcopenshell::geometry::trees::construct(backend_id_); + } + return *backend_; + } + + ifcopenshell::geometry::trees::abstract_tree& backend(const std::string& default_backend_id) const { + if (backend_id_.empty()) { + backend_id_ = default_backend_id; + } + return backend(); + } + + ifcopenshell::geometry::trees::abstract_tree& backend_for_element(IfcGeom::Element* element) const { + if (dynamic_cast(element)) { + return backend(default_selection_backend_id); + } + + if (dynamic_cast(element)) { + return backend(default_clash_backend_id); + } + + throw ifcopenshell::exception("Unsupported tree element type"); + } + + const ifcopenshell::geometry::trees::abstract_tree* backend_or_null() const { + return backend_.get(); + } + +private: + mutable std::string backend_id_; + mutable std::unique_ptr backend_; +}; + +IfcGeom::tree::tree() + : impl_(new impl()) +{} + +IfcGeom::tree::tree(const std::string& backend_id) + : impl_(new impl(backend_id)) +{} + +IfcGeom::tree::tree(ifcopenshell::file& file) + : tree() +{ + add_file(file, ifcopenshell::geometry::Settings{}); +} + +IfcGeom::tree::tree(ifcopenshell::file& file, const ifcopenshell::geometry::Settings& settings) + : tree() +{ + add_file(file, settings); +} + +IfcGeom::tree::tree(IfcGeom::Iterator& iterator) + : tree() +{ + add_file(iterator); +} + +IfcGeom::tree::~tree() = default; + +IfcGeom::tree::tree(tree&& other) noexcept = default; + +IfcGeom::tree& IfcGeom::tree::operator=(tree&& other) noexcept = default; + +void IfcGeom::tree::add_file(ifcopenshell::file& file, const ifcopenshell::geometry::Settings& settings) { + impl_->backend(default_selection_backend_id).add_file(file, settings); +} + +void IfcGeom::tree::add_file(IfcGeom::Iterator& iterator) { + if (!iterator.initialize()) { + return; + } + + do { + add_element(iterator.get()); + } while (iterator.next()); +} + +void IfcGeom::tree::add_element(IfcGeom::Element* element) { + if (!element) { + return; + } + + impl_->backend_for_element(element).add_element(element); +} + +std::vector IfcGeom::tree::select_box(const express::Entity& entity, bool completely_within, double extend) const { + return impl_->backend(default_selection_backend_id).select_box(entity, completely_within, extend); +} + +std::vector IfcGeom::tree::select_box(const tree_point& point) const { + return impl_->backend(default_selection_backend_id).select_box(point); +} + +std::vector IfcGeom::tree::select_box(const tree_box& bounds, bool completely_within) const { + return impl_->backend(default_selection_backend_id).select_box(bounds, completely_within); +} + +std::vector IfcGeom::tree::select(const express::Entity& entity, bool completely_within, double extend) const { + return impl_->backend(default_selection_backend_id).select(entity, completely_within, extend); +} + +std::vector IfcGeom::tree::select(const IfcGeom::Element* element, bool completely_within, double extend) const { + return impl_->backend(default_selection_backend_id).select(element, completely_within, extend); +} + +std::vector IfcGeom::tree::select(const tree_point& point, double extend) const { + return impl_->backend(default_selection_backend_id).select(point, extend); +} + +std::vector IfcGeom::tree::select_ray(const tree_point& origin, const tree_point& direction, double length) const { + return impl_->backend(default_selection_backend_id).select_ray(origin, direction, length); +} + +std::vector IfcGeom::tree::clash_intersection_many(const std::vector& set_a, const std::vector& set_b, double tolerance, bool check_all) const { + return impl_->backend(default_clash_backend_id).clash_intersection_many(to_product_entities(set_a), to_product_entities(set_b), tolerance, check_all); +} + +std::vector IfcGeom::tree::clash_collision_many(const std::vector& set_a, const std::vector& set_b, bool allow_touching) const { + return impl_->backend(default_clash_backend_id).clash_collision_many(to_product_entities(set_a), to_product_entities(set_b), allow_touching); +} + +std::vector IfcGeom::tree::clash_clearance_many(const std::vector& set_a, const std::vector& set_b, double clearance, bool check_all) const { + return impl_->backend(default_clash_backend_id).clash_clearance_many(to_product_entities(set_a), to_product_entities(set_b), clearance, check_all); +} + +const std::vector& IfcGeom::tree::distances() const { + const auto* backend = impl_->backend_or_null(); + return backend ? backend->distances() : empty_double_vector(); +} + +const std::vector& IfcGeom::tree::protrusion_distances() const { + const auto* backend = impl_->backend_or_null(); + return backend ? backend->protrusion_distances() : empty_double_vector(); +} + +bool IfcGeom::tree::enable_face_styles() const { + const auto* backend = impl_->backend_or_null(); + return backend ? backend->enable_face_styles() : false; +} + +void IfcGeom::tree::enable_face_styles(bool enable) { + impl_->backend(default_selection_backend_id).enable_face_styles(enable); +} + +const std::vector& IfcGeom::tree::styles() const { + const auto* backend = impl_->backend_or_null(); + return backend ? backend->styles() : empty_style_vector(); +} + +#ifdef WITH_HDF5 +void IfcGeom::tree::write_h5() { + impl_->backend(default_clash_backend_id).write_h5(); +} +#endif + +std::string IfcGeom::tree::uint8_to_b64(const std::vector& uuids_array) const { + std::string hex_str; + hex_str.reserve(uuids_array.size() * 2); + + for (auto byte : uuids_array) { + char hex[3]; + std::snprintf(hex, sizeof(hex), "%02x", byte); + hex_str.append(hex); + } + + return hex_str; +} + +bool IfcGeom::tree::is_manifold(const std::vector& faces) { + std::unordered_set, boost::hash>> directed_edges; + + for (size_t i = 0; i < faces.size(); i += 3) { + for (size_t j = 0; j < 3; ++j) { + const auto k = (j + 1) % 3; + const std::pair edge(faces[i + j], faces[i + k]); + const auto it = directed_edges.find(edge); + if (it != directed_edges.end()) { + directed_edges.erase(it); + } else { + directed_edges.insert({ faces[i + k], faces[i + j] }); + } + } + } + + return directed_edges.empty(); +} diff --git a/src/ifcgeom/tree.h b/src/ifcgeom/tree.h new file mode 100644 index 0000000000..c113ea4a2d --- /dev/null +++ b/src/ifcgeom/tree.h @@ -0,0 +1,116 @@ +/******************************************************************************** + * * + * This file is part of IfcOpenShell. * + * * + * IfcOpenShell is free software: you can redistribute it and/or modify * + * it under the terms of the Lesser GNU General Public License as published by * + * the Free Software Foundation, either version 3.0 of the License, or * + * (at your option) any later version. * + * * + * IfcOpenShell is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * Lesser GNU General Public License for more details. * + * * + * You should have received a copy of the Lesser GNU General Public License * + * along with this program. If not, see . * + * * + ********************************************************************************/ + +#ifndef IFCOPENSHELL_TREE_H +#define IFCOPENSHELL_TREE_H + +#include "IfcGeomElement.h" + +#include +#include +#include +#include +#include + +namespace ifcopenshell { + class file; + + namespace geometry { + class Settings; + } +} + +namespace IfcGeom { + class Iterator; + + using tree_point = std::array; + using tree_box = std::array; + + struct IFC_GEOM_API ray_intersection_result { + double distance; + int style_index; + express::Entity instance; + tree_point position; + tree_point normal; + double ray_distance; + double dot_product; + }; + + struct IFC_GEOM_API clash { + int clash_type; + express::Base a; + express::Base b; + double distance; + tree_point p1; + tree_point p2; + }; + + class IFC_GEOM_API tree { + public: + tree(); + explicit tree(const std::string& backend_id); + explicit tree(ifcopenshell::file& file); + tree(ifcopenshell::file& file, const ifcopenshell::geometry::Settings& settings); + explicit tree(IfcGeom::Iterator& iterator); + ~tree(); + + tree(tree&& other) noexcept; + tree& operator=(tree&& other) noexcept; + + tree(const tree& other) = delete; + tree& operator=(const tree& other) = delete; + + void add_file(ifcopenshell::file& file, const ifcopenshell::geometry::Settings& settings); + void add_file(IfcGeom::Iterator& iterator); + void add_element(IfcGeom::Element* element); + + std::vector select_box(const express::Entity& entity, bool completely_within = false, double extend = -1.e-5) const; + std::vector select_box(const tree_point& point) const; + std::vector select_box(const tree_box& bounds, bool completely_within = false) const; + + std::vector select(const express::Entity& entity, bool completely_within = false, double extend = 0.0) const; + std::vector select(const IfcGeom::Element* element, bool completely_within = false, double extend = -1.e-5) const; + std::vector select(const tree_point& point, double extend = 0.0) const; + std::vector select_ray(const tree_point& origin, const tree_point& direction, double length = 1000.) const; + + std::vector clash_intersection_many(const std::vector& set_a, const std::vector& set_b, double tolerance = 0.002, bool check_all = true) const; + std::vector clash_collision_many(const std::vector& set_a, const std::vector& set_b, bool allow_touching = false) const; + std::vector clash_clearance_many(const std::vector& set_a, const std::vector& set_b, double clearance = 0.05, bool check_all = false) const; + + const std::vector& distances() const; + const std::vector& protrusion_distances() const; + + bool enable_face_styles() const; + void enable_face_styles(bool enable); + const std::vector& styles() const; + +#ifdef WITH_HDF5 + void write_h5(); +#endif + + std::string uint8_to_b64(const std::vector& uuids_array) const; + static bool is_manifold(const std::vector& faces); + + private: + class impl; + std::unique_ptr impl_; + }; +} + +#endif diff --git a/src/ifcgeom/tree_plugin.cpp b/src/ifcgeom/tree_plugin.cpp new file mode 100644 index 0000000000..06dcf30c3f --- /dev/null +++ b/src/ifcgeom/tree_plugin.cpp @@ -0,0 +1,54 @@ +/******************************************************************************** + * * + * This file is part of IfcOpenShell. * + * * + * IfcOpenShell is free software: you can redistribute it and/or modify * + * it under the terms of the Lesser GNU General Public License as published by * + * the Free Software Foundation, either version 3.0 of the License, or * + * (at your option) any later version. * + * * + * IfcOpenShell is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * Lesser GNU General Public License for more details. * + * * + * You should have received a copy of the Lesser GNU General Public License * + * along with this program. If not, see . * + * * + ********************************************************************************/ + +#include "tree_plugin.h" + +namespace { + constexpr const char* tree_plugin_prefix = "geometry.tree."; +} + +const char* ifcopenshell::geometry::trees::tree_plugin_registration_symbol() { + return "ifcopenshell_register_tree_plugin_v1"; +} + +ifcopenshell::plugin::metadata ifcopenshell::geometry::trees::tree_plugin_metadata(const std::string& plugin_name) { + plugin::metadata metadata; + metadata.kind_ = plugin::kind::tree; + metadata.id = std::string(tree_plugin_prefix) + plugin_name; + return metadata; +} + +std::filesystem::path ifcopenshell::geometry::trees::tree_plugin_directory() { + return plugin::module_directory(reinterpret_cast(&ifcopenshell::geometry::trees::load_tree_plugins)); +} + +void ifcopenshell::geometry::trees::load_tree_plugins(tree_registry& registry) { + plugin::manager manager; + manager.add_search_path(tree_plugin_directory()); + + for (const auto& path : manager.discover(tree_plugin_prefix)) { + auto module = manager.load(path); + if (module.meta().kind_ != plugin::kind::tree) { + continue; + } + + auto register_plugin = module.get_alias(tree_plugin_registration_symbol()); + register_plugin(registry, module); + } +} diff --git a/src/ifcgeom/tree_plugin.h b/src/ifcgeom/tree_plugin.h new file mode 100644 index 0000000000..210515e0b9 --- /dev/null +++ b/src/ifcgeom/tree_plugin.h @@ -0,0 +1,42 @@ +/******************************************************************************** + * * + * This file is part of IfcOpenShell. * + * * + * IfcOpenShell is free software: you can redistribute it and/or modify * + * it under the terms of the Lesser GNU General Public License as published by * + * the Free Software Foundation, either version 3.0 of the License, or * + * (at your option) any later version. * + * * + * IfcOpenShell is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * Lesser GNU General Public License for more details. * + * * + * You should have received a copy of the Lesser GNU General Public License * + * along with this program. If not, see . * + * * + ********************************************************************************/ + +#ifndef IFCOPENSHELL_TREE_PLUGIN_H +#define IFCOPENSHELL_TREE_PLUGIN_H + +#include "tree_registry.h" + +#include + +namespace ifcopenshell { + namespace geometry { + namespace trees { + + typedef void register_tree_plugin_fn(tree_registry&, const ifcopenshell::plugin::module&); + + IFC_GEOM_API const char* tree_plugin_registration_symbol(); + IFC_GEOM_API ifcopenshell::plugin::metadata tree_plugin_metadata(const std::string& plugin_name); + IFC_GEOM_API std::filesystem::path tree_plugin_directory(); + IFC_GEOM_API void load_tree_plugins(tree_registry& registry); + + } + } +} + +#endif diff --git a/src/ifcgeom/tree_registry.cpp b/src/ifcgeom/tree_registry.cpp new file mode 100644 index 0000000000..6e0ad7a7bc --- /dev/null +++ b/src/ifcgeom/tree_registry.cpp @@ -0,0 +1,144 @@ +/******************************************************************************** + * * + * This file is part of IfcOpenShell. * + * * + * IfcOpenShell is free software: you can redistribute it and/or modify * + * it under the terms of the Lesser GNU General Public License as published by * + * the Free Software Foundation, either version 3.0 of the License, or * + * (at your option) any later version. * + * * + * IfcOpenShell is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * Lesser GNU General Public License for more details. * + * * + * You should have received a copy of the Lesser GNU General Public License * + * along with this program. If not, see . * + * * + ********************************************************************************/ + +#include "tree_registry.h" + +#include "tree_plugin.h" +#include "../ifcparse/exception.h" + +#include + +#include + +namespace { + std::string tree_key(const std::string& backend_id) { + return boost::to_lower_copy(backend_id); + } + + [[noreturn]] void unsupported_tree_operation(const std::string& backend_id, const std::string& operation) { + throw ifcopenshell::exception("Tree backend '" + backend_id + "' does not support " + operation); + } +} + +ifcopenshell::geometry::trees::abstract_tree::~abstract_tree() = default; + +void ifcopenshell::geometry::trees::abstract_tree::add_file(ifcopenshell::file&, const ifcopenshell::geometry::Settings&) { + unsupported_tree_operation(std::string(backend_id()), "add_file()"); +} + +void ifcopenshell::geometry::trees::abstract_tree::add_element(IfcGeom::Element*) { + unsupported_tree_operation(std::string(backend_id()), "add_element()"); +} + +std::vector ifcopenshell::geometry::trees::abstract_tree::select_box(const express::Entity&, bool, double) const { + unsupported_tree_operation(std::string(backend_id()), "select_box(entity)"); +} + +std::vector ifcopenshell::geometry::trees::abstract_tree::select_box(const IfcGeom::tree_point&) const { + unsupported_tree_operation(std::string(backend_id()), "select_box(point)"); +} + +std::vector ifcopenshell::geometry::trees::abstract_tree::select_box(const IfcGeom::tree_box&, bool) const { + unsupported_tree_operation(std::string(backend_id()), "select_box(bounds)"); +} + +std::vector ifcopenshell::geometry::trees::abstract_tree::select(const express::Entity&, bool, double) const { + unsupported_tree_operation(std::string(backend_id()), "select(entity)"); +} + +std::vector ifcopenshell::geometry::trees::abstract_tree::select(const IfcGeom::Element*, bool, double) const { + unsupported_tree_operation(std::string(backend_id()), "select(element)"); +} + +std::vector ifcopenshell::geometry::trees::abstract_tree::select(const IfcGeom::tree_point&, double) const { + unsupported_tree_operation(std::string(backend_id()), "select(point)"); +} + +std::vector ifcopenshell::geometry::trees::abstract_tree::select_ray(const IfcGeom::tree_point&, const IfcGeom::tree_point&, double) const { + unsupported_tree_operation(std::string(backend_id()), "select_ray()"); +} + +std::vector ifcopenshell::geometry::trees::abstract_tree::clash_intersection_many(const std::vector&, const std::vector&, double, bool) const { + unsupported_tree_operation(std::string(backend_id()), "clash_intersection_many()"); +} + +std::vector ifcopenshell::geometry::trees::abstract_tree::clash_collision_many(const std::vector&, const std::vector&, bool) const { + unsupported_tree_operation(std::string(backend_id()), "clash_collision_many()"); +} + +std::vector ifcopenshell::geometry::trees::abstract_tree::clash_clearance_many(const std::vector&, const std::vector&, double, bool) const { + unsupported_tree_operation(std::string(backend_id()), "clash_clearance_many()"); +} + +const std::vector& ifcopenshell::geometry::trees::abstract_tree::distances() const { + unsupported_tree_operation(std::string(backend_id()), "distances()"); +} + +const std::vector& ifcopenshell::geometry::trees::abstract_tree::protrusion_distances() const { + unsupported_tree_operation(std::string(backend_id()), "protrusion_distances()"); +} + +bool ifcopenshell::geometry::trees::abstract_tree::enable_face_styles() const { + unsupported_tree_operation(std::string(backend_id()), "enable_face_styles()"); +} + +void ifcopenshell::geometry::trees::abstract_tree::enable_face_styles(bool) { + unsupported_tree_operation(std::string(backend_id()), "enable_face_styles(bool)"); +} + +const std::vector& ifcopenshell::geometry::trees::abstract_tree::styles() const { + unsupported_tree_operation(std::string(backend_id()), "styles()"); +} + +#ifdef WITH_HDF5 +void ifcopenshell::geometry::trees::abstract_tree::write_h5() { + unsupported_tree_operation(std::string(backend_id()), "write_h5()"); +} +#endif + +void ifcopenshell::geometry::trees::tree_registry::bind(const tree_info& info, create_fn create, const plugin::module& module) { + entry entry; + entry.info_ = info; + entry.create_ = create; + entry.module_ = module; + entries_[tree_key(info.backend_id)] = entry; +} + +bool ifcopenshell::geometry::trees::tree_registry::has(const std::string& backend_id) const { + return entries_.find(tree_key(backend_id)) != entries_.end(); +} + +std::unique_ptr ifcopenshell::geometry::trees::tree_registry::create(const std::string& backend_id) const { + const auto iter = entries_.find(tree_key(backend_id)); + if (iter == entries_.end()) { + throw ifcopenshell::exception("No geometry tree registered for " + backend_id); + } + return std::unique_ptr(iter->second.create_()); +} + +ifcopenshell::geometry::trees::tree_registry& ifcopenshell::geometry::trees::tree_registry_instance() { + static tree_registry registry; + static std::once_flag once; + std::call_once(once, load_tree_plugins, std::ref(registry)); + return registry; +} + +std::unique_ptr ifcopenshell::geometry::trees::construct(const std::string& backend_id) { + return tree_registry_instance().create(backend_id); +} diff --git a/src/ifcgeom/tree_registry.h b/src/ifcgeom/tree_registry.h new file mode 100644 index 0000000000..d38d5e5949 --- /dev/null +++ b/src/ifcgeom/tree_registry.h @@ -0,0 +1,103 @@ +/******************************************************************************** + * * + * This file is part of IfcOpenShell. * + * * + * IfcOpenShell is free software: you can redistribute it and/or modify * + * it under the terms of the Lesser GNU General Public License as published by * + * the Free Software Foundation, either version 3.0 of the License, or * + * (at your option) any later version. * + * * + * IfcOpenShell is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * Lesser GNU General Public License for more details. * + * * + * You should have received a copy of the Lesser GNU General Public License * + * along with this program. If not, see . * + * * + ********************************************************************************/ + +#ifndef IFCOPENSHELL_TREE_REGISTRY_H +#define IFCOPENSHELL_TREE_REGISTRY_H + +#include "tree.h" +#include "../plugin/plugin.h" + +#include + +#include +#include +#include +#include + +namespace ifcopenshell { + class file; + + namespace geometry { + class Settings; + + namespace trees { + + struct IFC_GEOM_API tree_info { + std::string backend_id; + }; + + class IFC_GEOM_API abstract_tree { + public: + virtual ~abstract_tree(); + + virtual std::string_view backend_id() const = 0; + + virtual void add_file(ifcopenshell::file& file, const ifcopenshell::geometry::Settings& settings); + virtual void add_element(IfcGeom::Element* element); + + virtual std::vector select_box(const express::Entity& entity, bool completely_within, double extend) const; + virtual std::vector select_box(const IfcGeom::tree_point& point) const; + virtual std::vector select_box(const IfcGeom::tree_box& bounds, bool completely_within) const; + + virtual std::vector select(const express::Entity& entity, bool completely_within, double extend) const; + virtual std::vector select(const IfcGeom::Element* element, bool completely_within, double extend) const; + virtual std::vector select(const IfcGeom::tree_point& point, double extend) const; + virtual std::vector select_ray(const IfcGeom::tree_point& origin, const IfcGeom::tree_point& direction, double length) const; + + virtual std::vector clash_intersection_many(const std::vector& set_a, const std::vector& set_b, double tolerance, bool check_all) const; + virtual std::vector clash_collision_many(const std::vector& set_a, const std::vector& set_b, bool allow_touching) const; + virtual std::vector clash_clearance_many(const std::vector& set_a, const std::vector& set_b, double clearance, bool check_all) const; + + virtual const std::vector& distances() const; + virtual const std::vector& protrusion_distances() const; + virtual bool enable_face_styles() const; + virtual void enable_face_styles(bool enable); + virtual const std::vector& styles() const; + +#ifdef WITH_HDF5 + virtual void write_h5(); +#endif + }; + + class IFC_GEOM_API tree_registry { + public: + typedef boost::function0 create_fn; + + void bind(const tree_info& info, create_fn create, const ifcopenshell::plugin::module& module = ifcopenshell::plugin::module()); + bool has(const std::string& backend_id) const; + std::unique_ptr create(const std::string& backend_id) const; + + private: + struct entry { + tree_info info_; + create_fn create_; + ifcopenshell::plugin::module module_; + }; + + std::map entries_; + }; + + IFC_GEOM_API tree_registry& tree_registry_instance(); + IFC_GEOM_API std::unique_ptr construct(const std::string& backend_id); + + } + } +} + +#endif diff --git a/src/ifcgeomserver/CMakeLists.txt b/src/ifcgeomserver/CMakeLists.txt index ddd9358532..2462ce966f 100644 --- a/src/ifcgeomserver/CMakeLists.txt +++ b/src/ifcgeomserver/CMakeLists.txt @@ -7,5 +7,8 @@ target_link_libraries(IfcGeomServer IfcGeom ${OpenCASCADE_LIBRARIES}) if(WITH_OPENCASCADE) target_link_libraries(IfcGeomServer geometry_kernel_opencascade) endif() +if(kernel_libraries OR mapping_libraries) + add_dependencies(IfcGeomServer ${kernel_libraries} ${mapping_libraries}) +endif() install(TARGETS IfcGeomServer) diff --git a/src/ifcopenshell-python/ifcopenshell/geom/main.py b/src/ifcopenshell-python/ifcopenshell/geom/main.py index 84866892ce..e32d927ef1 100644 --- a/src/ifcopenshell-python/ifcopenshell/geom/main.py +++ b/src/ifcopenshell-python/ifcopenshell/geom/main.py @@ -379,9 +379,7 @@ class tree(ifcopenshell_wrapper.tree): def select( self, - value: Union[ - entity_instance, ifcopenshell_wrapper.BRepElement, tuple[float, float, float], TopoDS.TopoDS_Shape - ], + value: Union[entity_instance, ifcopenshell_wrapper.BRepElement, tuple[float, float, float]], **kwargs, ) -> list[entity_instance]: def unwrap(value): @@ -399,12 +397,6 @@ class tree(ifcopenshell_wrapper.tree): elif isinstance(value, (list, tuple)) and len(value) == 3 and set(map(type, value)) == {float}: if "extend" in kwargs: args.append(kwargs["extend"]) - elif has_occ: - if isinstance(value, TopoDS.TopoDS_Shape): - args[1] = utils.serialize_shape(value) - args.append(kwargs.get("completely_within", False)) - if "extend" in kwargs: - args.append(kwargs["extend"]) return ifcopenshell_wrapper.tree.select(*args) def select_box(self, value, **kwargs) -> list[entity_instance]: diff --git a/src/ifcparse/ifc_parse_api.h b/src/ifcparse/ifc_parse_api.h index 70d600b733..614c58e4b7 100644 --- a/src/ifcparse/ifc_parse_api.h +++ b/src/ifcparse/ifc_parse_api.h @@ -20,7 +20,9 @@ #ifndef IFC_PARSE_API_H #define IFC_PARSE_API_H -#ifdef _WIN32 +#ifdef SWIG +#define IFC_PARSE_API +#elif defined(_WIN32) #ifdef IFC_PARSE_EXPORTS #define IFC_PARSE_API __declspec(dllexport) #else diff --git a/src/ifcparse/schema.h b/src/ifcparse/schema.h index 3b9209e729..1104bf8d3e 100644 --- a/src/ifcparse/schema.h +++ b/src/ifcparse/schema.h @@ -504,7 +504,7 @@ class IFC_PARSE_API schema_registry { typedef const schema_definition& (*get_schema_fn)(); typedef void (*clear_schema_fn)(); - void bind(const std::string& schema_name, get_schema_fn get, clear_schema_fn clear, const plugin::module& module = plugin::module()); + void bind(const std::string& schema_name, get_schema_fn get, clear_schema_fn clear, const ifcopenshell::plugin::module& module = ifcopenshell::plugin::module()); void bind(schema_definition* schema); const schema_definition* get(const std::string& schema_name); std::vector names() const; @@ -515,7 +515,7 @@ class IFC_PARSE_API schema_registry { const schema_definition* schema_ = nullptr; get_schema_fn get_ = nullptr; clear_schema_fn clear_ = nullptr; - plugin::module module_; + ifcopenshell::plugin::module module_; }; std::map entries_; diff --git a/src/ifcwrap/CMakeLists.txt b/src/ifcwrap/CMakeLists.txt index 32f472af16..b336a57a4c 100644 --- a/src/ifcwrap/CMakeLists.txt +++ b/src/ifcwrap/CMakeLists.txt @@ -147,6 +147,9 @@ target_link_libraries(ifcopenshell_wrapper PRIVATE ${IFCOPENSHELL_LIBRARIES} ${O else() target_link_libraries(ifcopenshell_wrapper PRIVATE ${IFCOPENSHELL_LIBRARIES} ${LIBSVGFILL}) endif() +if(kernel_libraries OR tree_libraries OR mapping_libraries OR geometry_serializer_libraries OR document_serializer_libraries) + add_dependencies(ifcopenshell_wrapper ${kernel_libraries} ${tree_libraries} ${mapping_libraries} ${geometry_serializer_libraries} ${document_serializer_libraries}) +endif() if(NOT WIN32) SET_INSTALL_RPATHS(ifcopenshell_wrapper "${IFCDIRS};${OCC_LIBRARY_DIR}") endif() diff --git a/src/ifcwrap/IfcGeomWrapper.i b/src/ifcwrap/IfcGeomWrapper.i index d6faa6e549..17e76e9cdf 100644 --- a/src/ifcwrap/IfcGeomWrapper.i +++ b/src/ifcwrap/IfcGeomWrapper.i @@ -50,9 +50,7 @@ $result = PyBool_FromLong(static_cast(*$1)); } -%ignore IfcGeom::impl::tree::selector; - -// Using RTTI return a more specialized type of Element +// Using RTTI return a more specialized type of Element // Note that these elements are not to be owned by SWIG/Python as they will be freed automatically upon the next iteration // except for the IfcGeom::Element instances which are returned by Iterator::getObject() calls %typemap(out) IfcGeom::Element* { @@ -255,12 +253,11 @@ namespace { %shared_ptr(ifcopenshell::geometry::taxonomy::extrusion); %shared_ptr(ifcopenshell::geometry::taxonomy::revolve); %shared_ptr(ifcopenshell::geometry::taxonomy::sweep_along_curve); -%shared_ptr(ifcopenshell::geometry::taxonomy::node); - -%include "../ifcgeom/ifc_geom_api.h" -%include "../ifcgeom/Converter.h" -%include "../ifcgeom/ConversionResult.h" -%include "../ifcgeom/ConversionSettings.h" +%shared_ptr(ifcopenshell::geometry::taxonomy::node); + +%include "../ifcgeom/ifc_geom_api.h" +%include "../ifcgeom/ConversionResult.h" +%include "../ifcgeom/ConversionSettings.h" %include "../ifcgeom/IfcGeomElement.h" %include "../ifcgeom/IfcGeomRepresentation.h" %include "../ifcgeom/Iterator.h" @@ -435,139 +432,72 @@ assign_matrix_access(revolve); } } -#ifdef IFOPSH_WITH_OPENCASCADE - -%template(ray_intersection_results) std::vector; - -%template(clashes) std::vector; - -// A Template instantantation should be defined before it is used as a base class. -// But frankly I don't care as most methods are subtlely different anyway. -%include "../ifcgeom/kernels/opencascade/IfcGeomTree.h" - -%extend IfcGeom::tree { - - std::vector select_box(const express::Base& e, bool completely_within = false, double extend=-1.e-5) const { +%template(ray_intersection_results) std::vector; + +%template(clashes) std::vector; + +%include "../ifcgeom/tree.h" + +%extend IfcGeom::tree { + + std::vector select_box(const express::Base& e, bool completely_within = false, double extend=-1.e-5) const { if (!e.declaration().is("IfcProduct")) { throw ifcopenshell::exception("Instance should be an IfcProduct"); - } - return cast_vector($self->select_box(e.as(), completely_within, extend)); - } - - std::vector select_box(const gp_Pnt& p) const { - return cast_vector($self->select_box(p)); - } - - std::vector select_box(const Bnd_Box& b, bool completely_within = false) const { - return cast_vector($self->select_box(b, completely_within)); - } - - std::vector select(const express::Base& e, bool completely_within = false, double extend = 0.0) const { - if (!e.declaration().is("IfcProduct")) { - throw ifcopenshell::exception("Instance should be an IfcProduct"); - } - return cast_vector($self->select(e.as(), completely_within, extend)); - } - - std::vector select(const gp_Pnt& p, double extend=0.0) const { - return cast_vector($self->select(p, extend)); - } - - std::vector select(const std::string& shape_serialization, bool completely_within = false, double extend = -1.e-5) const { - std::stringstream stream(shape_serialization); - BRepTools_ShapeSet shapes; - shapes.Read(stream); - const TopoDS_Shape& shp = shapes.Shape(shapes.NbShapes()); - - return cast_vector($self->select(shp, completely_within, extend)); - } - - std::vector select(const IfcGeom::BRepElement* elem, bool completely_within = false, double extend = -1.e-5) const { - return cast_vector($self->select(elem, completely_within, extend)); - } - - /* - %typemap(in) const std::vector& (std::vector temp) { - if (!PyList_Check($input)) { - PyErr_SetString(PyExc_TypeError, "Expected a list."); - return NULL; - } - $1 = &temp; // Set $1 to the address of temp, which SWIG will use as the argument in the wrapped function - temp.reserve(PyList_Size($input)); // Pre-allocate memory for efficiency - for (Py_ssize_t i = 0; i < PyList_Size($input); ++i) { - PyObject* pyObj = PyList_GetItem($input, i); - void* ptr = 0; - int res = SWIG_ConvertPtr(pyObj, &ptr, SWIGTYPE_p_express__Base, 0); - if (!SWIG_IsOK(res)) { - PyErr_SetString(PyExc_TypeError, "List item is not of type IfcBaseClass."); - return NULL; - } - temp.push_back(reinterpret_cast(ptr)); - } - } - */ - - std::vector clash_intersection_many(const std::vector& set_a, const std::vector& set_b, double tolerance, bool check_all) const { - std::vector set_a_entities; - std::vector set_b_entities; - for (auto& e : set_a) { - if (!e.declaration().is("IfcProduct")) { - throw ifcopenshell::exception("All instances should be of type IfcProduct"); - } - set_a_entities.push_back(e.as()); - } - for (auto& e : set_b) { - if (!e.declaration().is("IfcProduct")) { - throw ifcopenshell::exception("All instances should be of type IfcProduct"); - } - set_b_entities.push_back(e.as()); - } - return $self->clash_intersection_many(set_a_entities, set_b_entities, tolerance, check_all); - } - - std::vector clash_collision_many(const std::vector& set_a, const std::vector& set_b, bool allow_touching) const { - std::vector set_a_entities; - std::vector set_b_entities; - for (auto& e : set_a) { - if (!e.declaration().is("IfcProduct")) { - throw ifcopenshell::exception("All instances should be of type IfcProduct"); - } - set_a_entities.push_back(e.as()); - } - for (auto& e : set_b) { - if (!e.declaration().is("IfcProduct")) { - throw ifcopenshell::exception("All instances should be of type IfcProduct"); - } - set_b_entities.push_back(e.as()); - } - return $self->clash_collision_many(set_a_entities, set_b_entities, allow_touching); - } - - std::vector clash_clearance_many(const std::vector& set_a, const std::vector& set_b, double clearance, bool check_all) const { - std::vector set_a_entities; - std::vector set_b_entities; - for (auto& e : set_a) { - if (!e.declaration().is("IfcProduct")) { - throw ifcopenshell::exception("All instances should be of type IfcProduct"); - } - set_a_entities.push_back(e.as()); - } - for (auto& e : set_b) { - if (!e.declaration().is("IfcProduct")) { - throw ifcopenshell::exception("All instances should be of type IfcProduct"); - } - set_b_entities.push_back(e.as()); - } - return $self->clash_clearance_many(set_a_entities, set_b_entities, clearance, check_all); - } - - -} - -#endif - -// A visitor -%{ + } + return cast_vector($self->select_box(e.as(), completely_within, extend)); + } + + std::vector select_box(const std::vector& p) const { + if (p.size() != 3) { + throw ifcopenshell::exception("Point should be a sequence of 3 floats"); + } + IfcGeom::tree_point point = {{ p[0], p[1], p[2] }}; + return cast_vector($self->select_box(point)); + } + + std::vector select_box(const std::vector>& b, bool completely_within = false) const { + if (b.size() != 2 || b[0].size() != 3 || b[1].size() != 3) { + throw ifcopenshell::exception("Bounding box should be a sequence of 2 x 3 floats"); + } + IfcGeom::tree_box box = {{ + { b[0][0], b[0][1], b[0][2] }, + { b[1][0], b[1][1], b[1][2] } + }}; + return cast_vector($self->select_box(box, completely_within)); + } + + std::vector select(const express::Base& e, bool completely_within = false, double extend = 0.0) const { + if (!e.declaration().is("IfcProduct")) { + throw ifcopenshell::exception("Instance should be an IfcProduct"); + } + return cast_vector($self->select(e.as(), completely_within, extend)); + } + + std::vector select(const std::vector& p, double extend=0.0) const { + if (p.size() != 3) { + throw ifcopenshell::exception("Point should be a sequence of 3 floats"); + } + IfcGeom::tree_point point = {{ p[0], p[1], p[2] }}; + return cast_vector($self->select(point, extend)); + } + + std::vector select(const IfcGeom::Element* elem, bool completely_within = false, double extend = -1.e-5) const { + return cast_vector($self->select(elem, completely_within, extend)); + } + + std::vector select_ray(const std::vector& p0, const std::vector& d, double length = 1000.) const { + if (p0.size() != 3 || d.size() != 3) { + throw ifcopenshell::exception("Origin and direction should be sequences of 3 floats"); + } + IfcGeom::tree_point origin = {{ p0[0], p0[1], p0[2] }}; + IfcGeom::tree_point direction = {{ d[0], d[1], d[2] }}; + return $self->select_ray(origin, direction, length); + } + +} + +// A visitor +%{ struct ShapeRTTI : public boost::static_visitor { PyObject* operator()(IfcGeom::Element* elem) const { @@ -853,134 +783,70 @@ struct ShapeRTTI : public boost::static_visitor %} }; -%{ - template - std::string to_locale_invariant_string(const T& t) { - std::ostringstream oss; - oss.imbue(std::locale::classic()); - oss << t; - return oss.str(); - } - - template - static std::variant helper_fn_create_shape(const std::string& geometry_library, ifcopenshell::geometry::Settings& st, const express::Base& instance, const express::Base& representation = express::Base()) { - ifcopenshell::file* file = instance.file(); - - ifcopenshell::geometry::Converter kernel(ifcopenshell::geometry::kernels::construct(file, geometry_library, st), file, st); - - if (auto product = instance.as()) { - if (representation) { - if (!representation.declaration().is(Schema::IfcRepresentation::Class())) { - throw ifcopenshell::exception("Supplied representation not of type IfcRepresentation"); - } - } - - if (!representation && !product.Representation()) { - throw ifcopenshell::exception("Representation is NULL"); - } - - auto prodrep = product.Representation(); - auto reps = prodrep.Representations(); - auto ifc_representation = representation ? representation.as() : typename Schema::IfcRepresentation(); - - if (!ifc_representation) { - // First, try to find a representation based on the settings - for (auto& rep : reps) { - if (!rep.RepresentationIdentifier()) { - continue; - } - if (st.get().get() != ifcopenshell::geometry::settings::CURVES) { - if (*rep.RepresentationIdentifier() == "Body" || *rep.RepresentationIdentifier() == "Facetation") { - ifc_representation = rep; - break; - } - } else { - if (*rep.RepresentationIdentifier() == "Plan" || *rep.RepresentationIdentifier() == "Axis") { - ifc_representation = rep; - break; - } - } - } - } - - // Otherwise, find a representation within the 'Model' or 'Plan' context - if (!ifc_representation) { - for (auto& rep : reps) { - auto context = rep.ContextOfItems(); - - // TODO: Remove redundancy with IfcGeomIterator.h - if (context.ContextType()) { - std::set context_types; - if (st.get().get() != ifcopenshell::geometry::settings::CURVES) { - context_types.insert("model"); - context_types.insert("design"); - context_types.insert("model view"); - context_types.insert("detail view"); - } else { - context_types.insert("plan"); - } - - std::string context_type_lc = *context.ContextType(); - for (std::string::iterator c = context_type_lc.begin(); c != context_type_lc.end(); ++c) { - *c = tolower(*c); - } - if (context_types.find(context_type_lc) != context_types.end()) { - ifc_representation = rep; - } - } - } - } - - if (!ifc_representation) { - if (reps.size()) { - // Return a random representation - ifc_representation = reps.front(); - } else { - throw ifcopenshell::exception("No suitable IfcRepresentation found"); - } - } - - IfcGeom::BRepElement* brep = kernel.create_brep_for_representation_and_product(ifc_representation, product); - if (!brep) { - std::ostringstream oss_repr, oss_product; - ifc_representation.to_string(oss_repr); - product.to_string(oss_product); - throw ifcopenshell::exception("Failed to process shape. Product: " + oss_product.str() + ", representation: " + oss_repr.str()); - } - if (st.get().get() == ifcopenshell::geometry::settings::SERIALIZED) { - IfcGeom::SerializedElement* serialization = new IfcGeom::SerializedElement(*brep); - delete brep; +%{ + template + std::string to_locale_invariant_string(const T& t) { + std::ostringstream oss; + oss.imbue(std::locale::classic()); + oss << t; + return oss.str(); + } + + static std::variant helper_fn_create_shape(const std::string& geometry_library, ifcopenshell::geometry::Settings& st, const express::Base& instance, const express::Base& representation = express::Base()) { + ifcopenshell::file* file = instance.file(); + + ifcopenshell::geometry::Converter kernel(ifcopenshell::geometry::kernels::construct(file, geometry_library, st), file, st); + if (instance.declaration().is("IfcProduct")) { + if (representation && !representation.declaration().is("IfcRepresentation")) { + throw ifcopenshell::exception("Supplied representation not of type IfcRepresentation"); + } + + auto selected_representation = representation ? representation : kernel.mapping()->representation_of(instance); + if (!selected_representation) { + throw ifcopenshell::exception("No suitable IfcRepresentation found"); + } + + IfcGeom::BRepElement* brep = kernel.create_brep_for_representation_and_product(selected_representation, instance); + if (!brep) { + std::ostringstream oss_repr, oss_product; + selected_representation.to_string(oss_repr); + instance.to_string(oss_product); + throw ifcopenshell::exception("Failed to process shape. Product: " + oss_product.str() + ", representation: " + oss_repr.str()); + } + if (st.get().get() == ifcopenshell::geometry::settings::SERIALIZED) { + IfcGeom::SerializedElement* serialization = new IfcGeom::SerializedElement(*brep); + delete brep; return serialization; } else if (st.get().get() == ifcopenshell::geometry::settings::TRIANGULATED) { IfcGeom::TriangulationElement* triangulation = new IfcGeom::TriangulationElement(*brep); delete brep; return triangulation; - } else { - return brep; - } - } else if (instance.as() || instance.as()) { - auto item = ifcopenshell::geometry::taxonomy::cast(kernel.mapping()->map(instance)); - if (item == nullptr) { - throw ifcopenshell::exception("Failed to convert placement"); - } - if (st.get().get()) { + } else { + return brep; + } + } else if (instance.declaration().is("IfcPlacement") || instance.declaration().is("IfcObjectPlacement")) { + auto item = ifcopenshell::geometry::taxonomy::cast(kernel.mapping()->map(instance)); + if (item == nullptr) { + throw ifcopenshell::exception("Failed to convert placement"); + } + if (st.get().get()) { // we pass the settings to the Transformation object, but access the data just offloads to the // generic cartesian_base so there's no time to apply the settings to the translation part. item = ifcopenshell::geometry::taxonomy::matrix4::ptr(item->clone_()); item->components().col(3).head<3>() /= kernel.settings().get().get(); - } - return new IfcGeom::Transformation(kernel.settings(), item); - } else { - if (!representation) { - if (instance.declaration().is(Schema::IfcRepresentationItem::Class()) || - instance.declaration().is(Schema::IfcRepresentation::Class()) || - // https://github.com/IfcOpenShell/IfcOpenShell/issues/1649 - instance.declaration().is(Schema::IfcProfileDef::Class()) - ) { - IfcGeom::ConversionResults shapes; - try { - shapes = kernel.convert(instance); - } catch (...) { + } + return new IfcGeom::Transformation(kernel.settings(), item); + } else { + if (!representation) { + if (instance.declaration().is("IfcRepresentationItem") || + instance.declaration().is("IfcRepresentation") || + // https://github.com/IfcOpenShell/IfcOpenShell/issues/1649 + instance.declaration().is("IfcProfileDef") + ) { + IfcGeom::ConversionResults shapes; + try { + shapes = kernel.convert(instance); + } catch (...) { std::ostringstream oss; instance.to_string(oss); throw ifcopenshell::exception("Failed to process shape. Instance: " + oss.str()); @@ -1037,75 +903,12 @@ ifcopenshell::geometry::taxonomy::item::ptr try_upcast(PyObject* obj0, swig_type std::unique_ptr mapping(ifcopenshell::geometry::impl::mapping_implementations().construct(instance.file(), settings)); return mapping->map(instance); } -%} - -%inline %{ - static std::variant create_shape(ifcopenshell::geometry::Settings& settings, const express::Base& instance, const express::Base& representation, const char* const geometry_library="opencascade") { - const std::string& schema_name = instance.declaration().schema()->name(); - - #ifdef HAS_SCHEMA_2x3 - if (schema_name == "IFC2X3") { - return helper_fn_create_shape(geometry_library, settings, instance, representation); - } - #endif - #ifdef HAS_SCHEMA_4 - if (schema_name == "IFC4") { - return helper_fn_create_shape(geometry_library, settings, instance, representation); - } - #endif - #ifdef HAS_SCHEMA_4x1 - if (schema_name == "IFC4X1") { - return helper_fn_create_shape(geometry_library, settings, instance, representation); - } - #endif - #ifdef HAS_SCHEMA_4x2 - if (schema_name == "IFC4X2") { - return helper_fn_create_shape(geometry_library, settings, instance, representation); - } - #endif - #ifdef HAS_SCHEMA_4x3_rc1 - if (schema_name == "IFC4X3_RC1") { - return helper_fn_create_shape(geometry_library, settings, instance, representation); - } - #endif - #ifdef HAS_SCHEMA_4x3_rc2 - if (schema_name == "IFC4X3_RC2") { - return helper_fn_create_shape(geometry_library, settings, instance, representation); - } - #endif - #ifdef HAS_SCHEMA_4x3_rc3 - if (schema_name == "IFC4X3_RC3") { - return helper_fn_create_shape(geometry_library, settings, instance, representation); - } - #endif - #ifdef HAS_SCHEMA_4x3_rc4 - if (schema_name == "IFC4X3_RC4") { - return helper_fn_create_shape(geometry_library, settings, instance, representation); - } - #endif - #ifdef HAS_SCHEMA_4x3 - if (schema_name == "IFC4X3") { - return helper_fn_create_shape(geometry_library, settings, instance, representation); - } - #endif - #ifdef HAS_SCHEMA_4x3_tc1 - if (schema_name == "IFC4X3_TC1") { - return helper_fn_create_shape(geometry_library, settings, instance, representation); - } - #endif - #ifdef HAS_SCHEMA_4x3_add1 - if (schema_name == "IFC4X3_ADD1") { - return helper_fn_create_shape(geometry_library, settings, instance, representation); - } - #endif - #ifdef HAS_SCHEMA_4x3_add2 - if (schema_name == "IFC4X3_ADD2") { - return helper_fn_create_shape(geometry_library, settings, instance, representation); - } - #endif - - throw ifcopenshell::exception("No geometry support for " + schema_name); - } +%} + +%inline %{ + static std::variant create_shape(ifcopenshell::geometry::Settings& settings, const express::Base& instance, const express::Base& representation, const char* const geometry_library="opencascade") { + return helper_fn_create_shape(geometry_library, settings, instance, representation); + } // Manual definition of overload without representation argument static std::variant create_shape(ifcopenshell::geometry::Settings& settings, const express::Base& instance, const char* const geometry_library="opencascade") { @@ -1113,13 +916,15 @@ ifcopenshell::geometry::taxonomy::item::ptr try_upcast(PyObject* obj0, swig_type } %} -#ifdef IFOPSH_WITH_OPENCASCADE - -%inline %{ - express::Base serialise(ifcopenshell::file& f, const std::string& shape_str, bool advanced=true) { - std::stringstream stream(shape_str); - BRepTools_ShapeSet shapes; - shapes.Read(stream); +#ifdef IFOPSH_WITH_OPENCASCADE + +%inline %{ + #include + + express::Base serialise(ifcopenshell::file& f, const std::string& shape_str, bool advanced=true) { + std::stringstream stream(shape_str); + BRepTools_ShapeSet shapes; + shapes.Read(stream); const TopoDS_Shape& shp = shapes.Shape(shapes.NbShapes()); return IfcGeom::serialise(f, shp, advanced); @@ -1137,86 +942,95 @@ ifcopenshell::geometry::taxonomy::item::ptr try_upcast(PyObject* obj0, swig_type #endif -#ifdef IFOPSH_WITH_CGAL - -%ignore hlr_writer; -%ignore hlr_calc; -%ignore occt_join; -%ignore prefiltered_hlr; -%ignore svgfill::svg_to_line_segments; -%ignore svgfill::line_segments_to_polygons; -%ignore svgfill::svg_to_polygons; -%ignore svgfill::arrange_polygons; - -%template(svg_line_segments) std::vector>; -%template(svg_groups_of_line_segments) std::vector>>; -%template(svg_point) std::array; -%template(line_segment) std::array; -%template(svg_polygons) std::vector; -%template(svg_groups_of_polygons) std::vector>; -%template(svg_loop) std::vector>; -%template(svg_loops) std::vector>>; - -%template(OpaqueCoordinate_3) IfcGeom::OpaqueCoordinate<3>; -%template(OpaqueCoordinate_4) IfcGeom::OpaqueCoordinate<4>; - -%newobject create_epeck; - -%inline %{ - IfcGeom::OpaqueNumber* create_epeck(int i) { - return new ifcopenshell::geometry::NumberEpeck(i); - } - IfcGeom::OpaqueNumber* create_epeck(double d) { - return new ifcopenshell::geometry::NumberEpeck(d); - } - IfcGeom::OpaqueNumber* create_epeck(const std::string& s) { - return new ifcopenshell::geometry::NumberEpeck(typename CGAL::Epeck::FT::ET(s)); - } -%} - -%inline %{ - IfcGeom::ConversionResultShape* nary_union(PyObject* sequence) { - std::vector*> nefs; - for(Py_ssize_t i = 0; i < PySequence_Size(sequence); ++i) { - PyObject* element = PySequence_GetItem(sequence, i); - void* argp1 = nullptr; - auto res1 = SWIG_ConvertPtr(element, &argp1, SWIGTYPE_p_IfcGeom__ConversionResultShape, 0); - if (SWIG_IsOK(res1)) { - auto arg1 = reinterpret_cast(argp1); - auto cgs = dynamic_cast(arg1); - if (cgs) { - nefs.push_back(&cgs->nef()); - } - } - } - ifcopenshell::geometry::CgalShape* shp; - Py_BEGIN_ALLOW_THREADS; - CGAL::Nef_nary_union_3< CGAL::Nef_polyhedron_3 > accum; - for (auto& n : nefs) { - accum.add_polyhedron(*n); - } - shp = new ifcopenshell::geometry::CgalShape(accum.get_union()); - Py_END_ALLOW_THREADS; - return shp; - } -%} - -%extend IfcGeom::ConversionResultShape { - std::string serialize_obj() { - std::ostringstream result; - auto cgs = dynamic_cast($self); - if (cgs) { - write_to_obj(cgs->nef(), result, std::numeric_limits::max()); - } - return result.str(); - } - - void convex_tag(bool b) { - auto cgs = dynamic_cast($self); - if (cgs) { - cgs->convex_tag() = b; - } - } +%template(OpaqueCoordinate_3) IfcGeom::OpaqueCoordinate<3>; +%template(OpaqueCoordinate_4) IfcGeom::OpaqueCoordinate<4>; + +%newobject create_epeck; + +%inline %{ + IfcGeom::OpaqueNumber* create_epeck(int i) { + return new IfcGeom::NumberNativeDouble(i); + } + IfcGeom::OpaqueNumber* create_epeck(double d) { + return new IfcGeom::NumberNativeDouble(d); + } + IfcGeom::OpaqueNumber* create_epeck(const std::string& s) { + return new IfcGeom::NumberNativeDouble(std::stod(s)); + } +%} + +%inline %{ + IfcGeom::ConversionResultShape* nary_union(PyObject* sequence) { + IfcGeom::ConversionResultShape* result = nullptr; + std::string backend_id; + auto identity = ifcopenshell::geometry::taxonomy::make(); + for(Py_ssize_t i = 0; i < PySequence_Size(sequence); ++i) { + PyObject* element = PySequence_GetItem(sequence, i); + void* argp1 = nullptr; + auto res1 = SWIG_ConvertPtr(element, &argp1, SWIGTYPE_p_IfcGeom__ConversionResultShape, 0); + if (SWIG_IsOK(res1)) { + auto arg1 = reinterpret_cast(argp1); + auto element_backend_id = std::string(arg1->backend_id()); + if (backend_id.empty()) { + backend_id = element_backend_id; + result = arg1->moved(identity); + } else { + if (element_backend_id != backend_id) { + delete result; + throw ifcopenshell::exception("nary_union requires shapes from the same geometry backend"); + } + auto next = result->add(arg1); + if (!next) { + delete result; + throw ifcopenshell::exception("nary_union failed for backend " + backend_id); + } + delete result; + result = next; + } + } + } + if (!result) { + throw ifcopenshell::exception("nary_union requires at least one shape"); + } + return result; + } +%} + +%extend IfcGeom::ConversionResultShape { + std::string serialize_obj() { + ifcopenshell::geometry::Settings settings; + std::unique_ptr triangulation($self->Triangulate(settings)); + std::ostringstream result; + + for (auto it = triangulation->verts().begin(); it != triangulation->verts().end();) { + result << "v " << *(it++) << " " << *(it++) << " " << *(it++) << "\n"; + } + for (auto it = triangulation->normals().begin(); it != triangulation->normals().end();) { + result << "vn " << *(it++) << " " << *(it++) << " " << *(it++) << "\n"; + } + + const bool has_normals = !triangulation->normals().empty(); + for (auto it = triangulation->faces().begin(); it != triangulation->faces().end();) { + const auto v1 = *(it++) + 1; + const auto v2 = *(it++) + 1; + const auto v3 = *(it++) + 1; + if (has_normals) { + result << "f " + << v1 << "//" << v1 << " " + << v2 << "//" << v2 << " " + << v3 << "//" << v3 << "\n"; + } else { + result << "f " << v1 << " " << v2 << " " << v3 << "\n"; + } + } + + return result.str(); + } + + void convex_tag(bool b) { + (void)b; + throw ifcopenshell::exception("convex_tag is not available through the generic conversion result interface"); + } std::string serialize() { std::string result; @@ -1231,8 +1045,28 @@ ifcopenshell::geometry::taxonomy::item::ptr try_upcast(PyObject* obj0, swig_type r = $self->solid(); Py_END_ALLOW_THREADS; return r; - } -} + } +} + +#ifdef IFOPSH_WITH_CGAL + +%ignore hlr_writer; +%ignore hlr_calc; +%ignore occt_join; +%ignore prefiltered_hlr; +%ignore svgfill::svg_to_line_segments; +%ignore svgfill::line_segments_to_polygons; +%ignore svgfill::svg_to_polygons; +%ignore svgfill::arrange_polygons; + +%template(svg_line_segments) std::vector>; +%template(svg_groups_of_line_segments) std::vector>>; +%template(svg_point) std::array; +%template(line_segment) std::array; +%template(svg_polygons) std::vector; +%template(svg_groups_of_polygons) std::vector>; +%template(svg_loop) std::vector>; +%template(svg_loops) std::vector>>; %naturalvar svgfill::polygon_2::boundary; %naturalvar svgfill::polygon_2::inner_boundaries; diff --git a/src/ifcwrap/IfcPython.i b/src/ifcwrap/IfcPython.i index 720631b511..02c18f7b2b 100644 --- a/src/ifcwrap/IfcPython.i +++ b/src/ifcwrap/IfcPython.i @@ -195,14 +195,10 @@ // of the module definition below. %{ #include "../ifcgeom/Iterator.h" + #include "../ifcgeom/tree.h" + #include "../ifcgeom/Serialization/Serialization.h" #include "../ifcgeom/taxonomy.h" #include "../ifcgeom/function_item_evaluator.h" -#ifdef IFOPSH_WITH_OPENCASCADE - #include "../ifcgeom/Serialization/Serialization.h" - #include "../ifcgeom/kernels/opencascade/IfcGeomTree.h" - - #include -#endif #include "../serializers/SvgSerializer.h" #include "../serializers/WavefrontObjSerializer.h" @@ -256,10 +252,6 @@ #include "../ifcgeom/ConversionResult.h" #include "../svgfill/src/svgfill.h" - -#ifdef IFOPSH_WITH_CGAL - #include "../ifcgeom/kernels/cgal/CgalConversionResult.h" -#endif %} // Create docstrings for generated python code. @@ -273,14 +265,10 @@ %module ifcopenshell_wrapper %{ #include "../ifcgeom/Converter.h" + #include "../ifcgeom/tree.h" + #include "../ifcgeom/Serialization/Serialization.h" #include "../ifcgeom/taxonomy.h" #include "../ifcgeom/function_item_evaluator.h" -#ifdef IFOPSH_WITH_OPENCASCADE - #include "../ifcgeom/Serialization/Serialization.h" - #include "../ifcgeom/kernels/opencascade/IfcGeomTree.h" - - #include -#endif #include "../ifcgeom/Iterator.h" #include "../ifcgeom/ConversionResult.h" #include "../ifcgeom/hybrid_kernel.h" diff --git a/src/ifcwrap/utils/type_conversion.i b/src/ifcwrap/utils/type_conversion.i index 743b9a6653..117accb6fe 100644 --- a/src/ifcwrap/utils/type_conversion.i +++ b/src/ifcwrap/utils/type_conversion.i @@ -29,6 +29,29 @@ template <> void* get_python_type() { return &PyString_Type; } #endif + template + bool check_python_type(PyObject* element) { + return element->ob_type == get_python_type(); + } + + inline bool convert_pyobject_to_base(PyObject* element, express::Base& value) { + void* arg = nullptr; + int result = SWIG_ConvertPtr(element, &arg, SWIGTYPE_p_express__Base, 0); + if (SWIG_IsOK(result) && arg) { + value = *static_cast(arg); + return true; + } + + value = express::Base{}; + return false; + } + + template <> + bool check_python_type(PyObject* element) { + express::Base value; + return convert_pyobject_to_base(element, value); + } + bool check_aggregate_of_type(PyObject* aggregate, void* type_obj) { if (!PySequence_Check(aggregate)) return false; for(Py_ssize_t i = 0; i < PySequence_Size(aggregate); ++i) { @@ -57,6 +80,63 @@ return true; } + template + bool check_aggregate_of_type(PyObject* aggregate) { + if (!PySequence_Check(aggregate)) return false; + for(Py_ssize_t i = 0; i < PySequence_Size(aggregate); ++i) { + PyObject* element = PySequence_GetItem(aggregate, i); + bool valid = check_python_type(element); + Py_DECREF(element); + if (!valid) { + return false; + } + } + return true; + } + + template <> + bool check_aggregate_of_type(PyObject* aggregate) { + if (!PySequence_Check(aggregate)) return false; + for(Py_ssize_t i = 0; i < PySequence_Size(aggregate); ++i) { + PyObject* element = PySequence_GetItem(aggregate, i); + express::Base value; + bool valid = convert_pyobject_to_base(element, value); + Py_DECREF(element); + if (!valid) { + return false; + } + } + return true; + } + + template + bool check_aggregate_of_aggregate_of_type(PyObject* aggregate) { + if (!PySequence_Check(aggregate)) return false; + for(Py_ssize_t i = 0; i < PySequence_Size(aggregate); ++i) { + PyObject* element = PySequence_GetItem(aggregate, i); + bool valid = check_aggregate_of_type(element); + Py_DECREF(element); + if (!valid) { + return false; + } + } + return true; + } + + template <> + bool check_aggregate_of_aggregate_of_type(PyObject* aggregate) { + if (!PySequence_Check(aggregate)) return false; + for(Py_ssize_t i = 0; i < PySequence_Size(aggregate); ++i) { + PyObject* element = PySequence_GetItem(aggregate, i); + bool valid = check_aggregate_of_type(element); + Py_DECREF(element); + if (!valid) { + return false; + } + } + return true; + } + template T cast_pyobject(PyObject* element); @@ -87,9 +167,8 @@ template <> express::Base cast_pyobject(PyObject* element) { - void *arg = 0; - int res = SWIG_ConvertPtr(element, &arg, SWIGTYPE_p_express__Base, 0); - return SWIG_IsOK(res) ? *reinterpret_cast(arg) : express::Base{}; + express::Base value; + return convert_pyobject_to_base(element, value) ? value : express::Base{}; } template @@ -112,6 +191,7 @@ PyObject* element = PySequence_GetItem(aggregate, i); T t = cast_pyobject(element); add_to_container(result_vector, t); + Py_DECREF(element); } return result_vector; } diff --git a/src/ifcwrap/utils/typemaps_in.i b/src/ifcwrap/utils/typemaps_in.i index 78b6fbaa63..5bf17722f5 100644 --- a/src/ifcwrap/utils/typemaps_in.i +++ b/src/ifcwrap/utils/typemaps_in.i @@ -20,23 +20,23 @@ %define CREATE_VECTOR_TYPEMAP_IN(template_type, express_name, python_name) %typemap(in) std::vector { - if (!check_aggregate_of_type($input, get_python_type())) { + if (!check_aggregate_of_type($input)) { SWIG_exception(SWIG_TypeError, "Attribute of type AGGREGATE OF " #express_name " needs a python sequence of " #python_name "s"); } $1 = python_sequence_as_vector($input); } %typemap(typecheck,precedence=SWIG_TYPECHECK_INTEGER) std::vector { - $1 = check_aggregate_of_type($input, get_python_type()) ? 1 : 0; + $1 = check_aggregate_of_type($input) ? 1 : 0; } %typemap(typecheck,precedence=SWIG_TYPECHECK_INTEGER) const std::vector& { - $1 = check_aggregate_of_type($input, get_python_type()) ? 1 : 0; + $1 = check_aggregate_of_type($input) ? 1 : 0; } %typemap(arginit) const std::vector& { $1 = new std::vector(); } %typemap(in) const std::vector& { - if (!check_aggregate_of_type($input, get_python_type())) { + if (!check_aggregate_of_type($input)) { SWIG_exception(SWIG_TypeError, "Attribute of type AGGREGATE OF " #express_name " needs a python sequence of " #python_name "s"); } *$1 = python_sequence_as_vector($input); @@ -46,7 +46,7 @@ } %typemap(in) std::vector< std::vector > { - if (!check_aggregate_of_aggregate_of_type($input, get_python_type())) { + if (!check_aggregate_of_aggregate_of_type($input)) { SWIG_exception(SWIG_TypeError, "Attribute of type AGGREGATE OF AGGREGATE OF " #express_name " needs a python sequence of sequence of " #python_name "s"); } $1 = python_sequence_as_vector_of_vector($input); @@ -56,7 +56,7 @@ $1 = new std::vector< std::vector >(); } %typemap(in) const std::vector< std::vector >& { - if (!check_aggregate_of_aggregate_of_type($input, get_python_type())) { + if (!check_aggregate_of_aggregate_of_type($input)) { SWIG_exception(SWIG_TypeError, "Attribute of type AGGREGATE OF AGGREGATE OF " #express_name " needs a python sequence of sequence of " #python_name "s"); } *$1 = python_sequence_as_vector_of_vector($input); @@ -70,6 +70,7 @@ CREATE_VECTOR_TYPEMAP_IN(int, INTEGER, int) CREATE_VECTOR_TYPEMAP_IN(double, REAL, float) CREATE_VECTOR_TYPEMAP_IN(std::string, STRING, str) +CREATE_VECTOR_TYPEMAP_IN(express::Base, ENTITY INSTANCE, entity instance) // @todo use macros. @@ -166,57 +167,6 @@ CREATE_VECTOR_TYPEMAP_IN(std::string, STRING, str) } } -%typemap(in) aggregate_of_instance::ptr { - if (PySequence_Check($input)) { - $1 = aggregate_of_instance::ptr(new aggregate_of_instance()); - for(Py_ssize_t i = 0; i < PySequence_Size($input); ++i) { - PyObject* element = PySequence_GetItem($input, i); - express::Base inst = cast_pyobject(element); - Py_DECREF(element); - if (inst) { - $1->push(inst); - } else { - SWIG_exception(SWIG_TypeError, "Attribute of type AGGREGATE OF ENTITY INSTANCE needs a python sequence of entity instances"); - } - } - } else { - SWIG_exception(SWIG_TypeError, "Attribute of type AGGREGATE OF ENTITY INSTANCE needs a python sequence of entity instances"); - } -} - -%typemap(in) aggregate_of_aggregate_of_instance::ptr { - if (PySequence_Check($input)) { - $1 = aggregate_of_aggregate_of_instance::ptr(new aggregate_of_aggregate_of_instance()); - for(Py_ssize_t i = 0; i < PySequence_Size($input); ++i) { - PyObject* element = PySequence_GetItem($input, i); - bool b = false; - if (PySequence_Check(element)) { - b = true; - std::vector vector; - vector.reserve(PySequence_Size(element)); - for(Py_ssize_t j = 0; j < PySequence_Size(element); ++j) { - PyObject* element_element = PySequence_GetItem(element, j); - express::Base inst = cast_pyobject(element_element); - Py_DECREF(element_element); - if (inst) { - vector.push_back(inst); - } else { - SWIG_exception(SWIG_TypeError, "Attribute of type AGGREGATE OF AGGREGATE OF ENTITY INSTANCE needs a python sequence of sequence of entity instances"); - } - } - $1->push(vector); - } - Py_DECREF(element); - if (!b) { - SWIG_exception(SWIG_TypeError, "Attribute of type AGGREGATE OF AGGREGATE OF ENTITY INSTANCE needs a python sequence of sequence of entity instances"); - break; - } - } - } else { - SWIG_exception(SWIG_TypeError, "Attribute of type AGGREGATE OF AGGREGATE OF ENTITY INSTANCE needs a python sequence of sequence of entity instances"); - } -} - %typemap(in) const gp_Pnt& { if (!check_aggregate_of_type($input, get_python_type())) { SWIG_exception(SWIG_TypeError, " type needs a python sequence of 3 floats"); diff --git a/src/plugin/plugin.cpp b/src/plugin/plugin.cpp index 7f8c290dba..b0cccc3402 100644 --- a/src/plugin/plugin.cpp +++ b/src/plugin/plugin.cpp @@ -19,6 +19,12 @@ #include "plugin.h" +#ifndef _WIN32 +#include +#else +#include +#endif + #include #include @@ -195,3 +201,30 @@ void ifcopenshell::plugin::validate_abi(const abi_info& abi) { stream << " (plugin debug " << abi.debug_build << ", host debug " << host.debug_build << ")"; throw std::runtime_error(stream.str()); } + +std::filesystem::path ifcopenshell::plugin::module_directory(const void* symbol) { +#ifdef _WIN32 + HMODULE module_handle = nullptr; + if (!GetModuleHandleExW( + GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + reinterpret_cast(symbol), + &module_handle)) { + throw std::runtime_error("Unable to resolve module path"); + } + + wchar_t buffer[MAX_PATH]; + const DWORD length = GetModuleFileNameW(module_handle, buffer, MAX_PATH); + if (length == 0) { + throw std::runtime_error("Unable to read module filename"); + } + + return std::filesystem::path(std::wstring(buffer, length)).parent_path(); +#else + Dl_info info; + if (dladdr(symbol, &info) == 0 || !info.dli_fname) { + throw std::runtime_error("Unable to resolve module path"); + } + + return std::filesystem::path(info.dli_fname).parent_path(); +#endif +} diff --git a/src/plugin/plugin.h b/src/plugin/plugin.h index 68d2110498..62eecf7fb4 100644 --- a/src/plugin/plugin.h +++ b/src/plugin/plugin.h @@ -37,6 +37,7 @@ enum class kind { parse_schema, mapping, kernel, + tree, document_serializer, geometry_serializer, opencascade_geometry_ifc_writer @@ -101,6 +102,7 @@ private: PLUGIN_API abi_info host_abi(); PLUGIN_API void validate_abi(const abi_info& abi); +PLUGIN_API std::filesystem::path module_directory(const void* symbol); } } diff --git a/src/plugin/plugin_api.h b/src/plugin/plugin_api.h index 17eb1f0198..ae76d173ba 100644 --- a/src/plugin/plugin_api.h +++ b/src/plugin/plugin_api.h @@ -20,7 +20,9 @@ #ifndef PLUGIN_API_H #define PLUGIN_API_H -#ifdef _WIN32 +#ifdef SWIG +#define PLUGIN_API +#elif defined(_WIN32) #ifdef PLUGIN_EXPORTS #define PLUGIN_API __declspec(dllexport) #else diff --git a/src/serializers/CMakeLists.txt b/src/serializers/CMakeLists.txt index 0ed46dcfb1..9d0b0fab46 100644 --- a/src/serializers/CMakeLists.txt +++ b/src/serializers/CMakeLists.txt @@ -2,12 +2,12 @@ add_subdirectory(schema_dependent) file(GLOB SERIALIZERS_H_FILES *.h) file(GLOB SERIALIZERS_CPP_FILES *.cpp) +file(GLOB SERIALIZERS_S_H_FILES schema_dependent/*.h) set(SERIALIZERS_FILES ${SERIALIZERS_H_FILES} ${SERIALIZERS_CPP_FILES}) add_library(Serializers ${SERIALIZERS_FILES}) add_library(serialization ALIAS Serializers) set_target_properties(Serializers PROPERTIES COMPILE_FLAGS "-DSERIALIZERS_EXPORTS" VERSION "${PROJECT_VERSION}" SOVERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}") -set(SERIALIZER_SCHEMA_LIBRARIES Serializers ${SERIALIZER_SCHEMA_LIBRARIES} PARENT_SCOPE) if(WITH_PROJ) find_package(proj REQUIRED) @@ -20,7 +20,7 @@ endif() target_link_libraries(Serializers PRIVATE - ${SERIALIZER_SCHEMA_LIBRARIES} ${OPENCOLLADA_LIBRARIES} ${USD_LIBRARIES} + plugin ${OPENCOLLADA_LIBRARIES} ${USD_LIBRARIES} IfcGeom ${OpenCASCADE_LIBRARIES} IfcParse ) @@ -28,7 +28,7 @@ if(WITH_OPENCASCADE) target_link_libraries(Serializers PRIVATE geometry_kernel_opencascade) endif() -install(TARGETS Serializers) +install(TARGETS Serializers ${document_serializer_libraries}) install(FILES ${SERIALIZERS_H_FILES} DESTINATION ${INCLUDEDIR}/serializers/ @@ -37,3 +37,7 @@ install(FILES ${SERIALIZERS_H_FILES} install(FILES ${SERIALIZERS_S_H_FILES} DESTINATION ${INCLUDEDIR}/serializers/schema_dependent ) + +set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} Serializers) +set(IFCOPENSHELL_LIBRARIES ${IFCOPENSHELL_LIBRARIES} PARENT_SCOPE) +set(document_serializer_libraries ${document_serializer_libraries} PARENT_SCOPE) diff --git a/src/serializers/JsonSerializer.cpp b/src/serializers/JsonSerializer.cpp index 68c8b4aa48..d29e4e8101 100644 --- a/src/serializers/JsonSerializer.cpp +++ b/src/serializers/JsonSerializer.cpp @@ -20,35 +20,26 @@ #ifdef WITH_GLTF #include "JsonSerializer.h" +#include "document_serializer_plugin.h" #include -#include -#include - -#define EXTERNAL_DEFS(r, data, elem) \ - extern void BOOST_PP_CAT(init_JsonSerializer_Ifc, elem)(JsonSerializerFactory::Factory*); - -#define CALL_DEFS(r, data, elem) \ - BOOST_PP_CAT(init_JsonSerializer_Ifc, elem)(this); - -BOOST_PP_SEQ_FOR_EACH(EXTERNAL_DEFS, , SCHEMA_SEQ) JsonSerializerFactory::Factory::Factory() { - BOOST_PP_SEQ_FOR_EACH(CALL_DEFS, , SCHEMA_SEQ) + ifcopenshell::serializers::load_document_serializer_plugins(*this, "json"); } -void JsonSerializerFactory::Factory::bind(const std::string& schema_name, fn f) { +void JsonSerializerFactory::Factory::bind(const std::string& schema_name, fn f, const ifcopenshell::plugin::module& module) { const std::string schema_name_lower = boost::to_lower_copy(schema_name); - this->insert(std::make_pair(schema_name_lower, f)); + entries_[schema_name_lower] = { f, module }; } JsonSerializer* JsonSerializerFactory::Factory::construct(const std::string& schema_name, ifcopenshell::file* file, std::string json_filename, JsonSerializer::Dialect dialect) { const std::string schema_name_lower = boost::to_lower_copy(schema_name); - auto it = this->find(schema_name_lower); - if (it == this->end()) { + auto it = entries_.find(schema_name_lower); + if (it == entries_.end()) { throw ifcopenshell::exception("No Json serializer registered for " + schema_name); } - return it->second(file, json_filename, dialect); + return it->second.fn_(file, json_filename, dialect); } JsonSerializer::JsonSerializer(ifcopenshell::file* file, const std::string& json_filename, JsonSerializer::Dialect dialect) { diff --git a/src/serializers/JsonSerializer.h b/src/serializers/JsonSerializer.h index 1e73d21549..3137214739 100644 --- a/src/serializers/JsonSerializer.h +++ b/src/serializers/JsonSerializer.h @@ -5,6 +5,7 @@ #include "../ifcgeom/Serializer.h" #include "../ifcparse/file.h" +#include "../plugin/plugin.h" #include "../serializers/serializers_api.h" #include @@ -16,7 +17,7 @@ class SERIALIZERS_API JsonSerializer : public Serializer { JSON_DIALECT_CREOOX }; private: - JsonSerializer* implementation_; + JsonSerializer* implementation_ = nullptr; protected: std::string json_filename; @@ -37,11 +38,19 @@ class SERIALIZERS_API JsonSerializer : public Serializer { struct SERIALIZERS_API JsonSerializerFactory { typedef boost::function3 fn; - class Factory : public std::map { + class SERIALIZERS_API Factory { public: Factory(); - void bind(const std::string& schema_name, fn); + void bind(const std::string& schema_name, fn, const ifcopenshell::plugin::module& module = ifcopenshell::plugin::module()); JsonSerializer* construct(const std::string& schema_name, ifcopenshell::file*, std::string, JsonSerializer::Dialect); + + private: + struct entry { + fn fn_; + ifcopenshell::plugin::module module_; + }; + + std::map entries_; }; static Factory& implementations(); diff --git a/src/serializers/XmlSerializer.cpp b/src/serializers/XmlSerializer.cpp index 6fa8fb4afc..da303059ab 100644 --- a/src/serializers/XmlSerializer.cpp +++ b/src/serializers/XmlSerializer.cpp @@ -1,34 +1,24 @@ #include "XmlSerializer.h" +#include "document_serializer_plugin.h" -#include -#include #include -#define EXTERNAL_DEFS(r, data, elem) \ - extern void BOOST_PP_CAT(init_XmlSerializer_Ifc, elem)(XmlSerializerFactory::Factory*); - -#define CALL_DEFS(r, data, elem) \ - BOOST_PP_CAT(init_XmlSerializer_Ifc, elem)(this); - -BOOST_PP_SEQ_FOR_EACH(EXTERNAL_DEFS, , SCHEMA_SEQ) - XmlSerializerFactory::Factory::Factory() { - BOOST_PP_SEQ_FOR_EACH(CALL_DEFS, , SCHEMA_SEQ) + ifcopenshell::serializers::load_document_serializer_plugins(*this, "xml"); } -void XmlSerializerFactory::Factory::bind(const std::string& schema_name, fn f) { +void XmlSerializerFactory::Factory::bind(const std::string& schema_name, fn f, const ifcopenshell::plugin::module& module) { const std::string schema_name_lower = boost::to_lower_copy(schema_name); - this->insert(std::make_pair(schema_name_lower, f)); + entries_[schema_name_lower] = { f, module }; } XmlSerializer* XmlSerializerFactory::Factory::construct(const std::string& schema_name, ifcopenshell::file* file, std::string xml_filename) { const std::string schema_name_lower = boost::to_lower_copy(schema_name); - typename std::map::const_iterator it; - it = this->find(schema_name_lower); - if (it == this->end()) { + auto it = entries_.find(schema_name_lower); + if (it == entries_.end()) { throw ifcopenshell::exception("No XML serializer registered for " + schema_name); } - return it->second(file, xml_filename); + return it->second.fn_(file, xml_filename); } XmlSerializer::XmlSerializer(ifcopenshell::file* file, const std::string& xml_filename) { diff --git a/src/serializers/XmlSerializer.h b/src/serializers/XmlSerializer.h index a2d7df70bb..e5da03b8be 100644 --- a/src/serializers/XmlSerializer.h +++ b/src/serializers/XmlSerializer.h @@ -1,8 +1,12 @@ +#ifndef XMLSERIALIZER_H +#define XMLSERIALIZER_H + #define SCHEMA_METHOD #include "../serializers/serializers_api.h" #include "../ifcgeom/Serializer.h" #include "../ifcparse/file.h" +#include "../plugin/plugin.h" #include @@ -10,7 +14,7 @@ class SERIALIZERS_API XmlSerializer : public Serializer { private: - XmlSerializer* implementation_; + XmlSerializer* implementation_ = nullptr; protected: std::string xml_filename; @@ -30,12 +34,22 @@ public: struct SERIALIZERS_API XmlSerializerFactory { typedef boost::function2 fn; - class Factory : public std::map { + class SERIALIZERS_API Factory { public: Factory(); - void bind(const std::string& schema_name, fn); + void bind(const std::string& schema_name, fn, const ifcopenshell::plugin::module& module = ifcopenshell::plugin::module()); XmlSerializer* construct(const std::string& schema_name, ifcopenshell::file*, std::string); + + private: + struct entry { + fn fn_; + ifcopenshell::plugin::module module_; + }; + + std::map entries_; }; static Factory& implementations(); }; + +#endif diff --git a/src/serializers/document_serializer_plugin.cpp b/src/serializers/document_serializer_plugin.cpp new file mode 100644 index 0000000000..7f4d466fb5 --- /dev/null +++ b/src/serializers/document_serializer_plugin.cpp @@ -0,0 +1,78 @@ +/******************************************************************************** + * * + * This file is part of IfcOpenShell. * + * * + * IfcOpenShell is free software: you can redistribute it and/or modify * + * it under the terms of the Lesser GNU General Public License as published by * + * the Free Software Foundation, either version 3.0 of the License, or * + * (at your option) any later version. * + * * + * IfcOpenShell is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * Lesser GNU General Public License for more details. * + * * + * You should have received a copy of the Lesser GNU General Public License * + * along with this program. If not, see . * + * * + ********************************************************************************/ + +#include "document_serializer_plugin.h" + +#include + +namespace { + +std::string document_serializer_plugin_prefix(const std::string& format) { + return "document." + boost::to_lower_copy(format) + "."; +} + +template +void load_document_serializer_plugins_impl(registry_t& registry, const std::string& format) { + ifcopenshell::plugin::manager manager; + manager.add_search_path(ifcopenshell::serializers::document_serializer_plugin_directory()); + + const auto format_lower = boost::to_lower_copy(format); + const auto prefix = document_serializer_plugin_prefix(format_lower); + for (const auto& path : manager.discover(prefix)) { + auto module = manager.load(path); + if (module.meta().kind_ != ifcopenshell::plugin::kind::document_serializer) { + continue; + } + if (boost::to_lower_copy(module.meta().format) != format_lower) { + continue; + } + + auto register_plugin = module.get_alias(ifcopenshell::serializers::document_serializer_plugin_registration_symbol()); + register_plugin(registry, module); + } +} + +} + +const char* ifcopenshell::serializers::document_serializer_plugin_registration_symbol() { + return "ifcopenshell_register_document_serializer_plugin_v1"; +} + +ifcopenshell::plugin::metadata ifcopenshell::serializers::document_serializer_plugin_metadata(const std::string& format, const std::string& schema_name) { + plugin::metadata metadata; + metadata.kind_ = plugin::kind::document_serializer; + metadata.id = document_serializer_plugin_prefix(format) + boost::to_lower_copy(schema_name); + metadata.schema = boost::to_upper_copy(schema_name); + metadata.format = boost::to_lower_copy(format); + return metadata; +} + +std::filesystem::path ifcopenshell::serializers::document_serializer_plugin_directory() { + return plugin::module_directory(reinterpret_cast(&ifcopenshell::serializers::document_serializer_plugin_directory)); +} + +void ifcopenshell::serializers::load_document_serializer_plugins(XmlSerializerFactory::Factory& registry, const std::string& format) { + load_document_serializer_plugins_impl(registry, format); +} + +#ifdef WITH_GLTF +void ifcopenshell::serializers::load_document_serializer_plugins(JsonSerializerFactory::Factory& registry, const std::string& format) { + load_document_serializer_plugins_impl(registry, format); +} +#endif diff --git a/src/serializers/document_serializer_plugin.h b/src/serializers/document_serializer_plugin.h new file mode 100644 index 0000000000..ce68247089 --- /dev/null +++ b/src/serializers/document_serializer_plugin.h @@ -0,0 +1,51 @@ +/******************************************************************************** + * * + * This file is part of IfcOpenShell. * + * * + * IfcOpenShell is free software: you can redistribute it and/or modify * + * it under the terms of the Lesser GNU General Public License as published by * + * the Free Software Foundation, either version 3.0 of the License, or * + * (at your option) any later version. * + * * + * IfcOpenShell is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * Lesser GNU General Public License for more details. * + * * + * You should have received a copy of the Lesser GNU General Public License * + * along with this program. If not, see . * + * * + ********************************************************************************/ + +#ifndef IFCOPENSHELL_DOCUMENT_SERIALIZER_PLUGIN_H +#define IFCOPENSHELL_DOCUMENT_SERIALIZER_PLUGIN_H + +#include "XmlSerializer.h" + +#ifdef WITH_GLTF +#include "JsonSerializer.h" +#endif + +#include +#include + +namespace ifcopenshell { +namespace serializers { + +typedef void register_xml_document_serializer_plugin_fn(XmlSerializerFactory::Factory&, const ifcopenshell::plugin::module&); + +SERIALIZERS_API const char* document_serializer_plugin_registration_symbol(); +SERIALIZERS_API ifcopenshell::plugin::metadata document_serializer_plugin_metadata(const std::string& format, const std::string& schema_name); +SERIALIZERS_API std::filesystem::path document_serializer_plugin_directory(); +SERIALIZERS_API void load_document_serializer_plugins(XmlSerializerFactory::Factory& registry, const std::string& format); + +#ifdef WITH_GLTF +typedef void register_json_document_serializer_plugin_fn(JsonSerializerFactory::Factory&, const ifcopenshell::plugin::module&); + +SERIALIZERS_API void load_document_serializer_plugins(JsonSerializerFactory::Factory& registry, const std::string& format); +#endif + +} +} + +#endif diff --git a/src/serializers/schema_dependent/CMakeLists.txt b/src/serializers/schema_dependent/CMakeLists.txt index 25ec31c9b3..ef7010333b 100644 --- a/src/serializers/schema_dependent/CMakeLists.txt +++ b/src/serializers/schema_dependent/CMakeLists.txt @@ -1,19 +1,27 @@ -file(GLOB SERIALIZERS_S_H_FILES *.h) -file(GLOB SERIALIZERS_S_CPP_FILES *.cpp) -set(SERIALIZERS_S_FILES ${SERIALIZERS_S_H_FILES} ${SERIALIZERS_S_CPP_FILES}) +set(document_serializer_plugin_runtime_dir "${CMAKE_BINARY_DIR}/serializers/$") foreach(schema ${SCHEMA_VERSIONS}) - add_library(Serializers_ifc${schema} OBJECT ${SERIALIZERS_S_FILES}) - set(SERIALIZER_SCHEMA_LIBRARIES ${SERIALIZER_SCHEMA_LIBRARIES} Serializers_ifc${schema}) - set_target_properties(Serializers_ifc${schema} PROPERTIES COMPILE_FLAGS "-DSERIALIZERS_EXPORTS -DIfcSchema=Ifc${schema}") - if(WASM_BUILD) - target_link_libraries(Serializers_ifc${schema} ${HDF5_LIBRARIES}) - else() - target_link_libraries(Serializers_ifc${schema} IfcGeom ${OpenCASCADE_LIBRARIES} ${HDF5_LIBRARIES}) - endif() + add_library(document_serializer_xml_ifc${schema} SHARED XmlSerializer.cpp xml_plugin.cpp) + target_link_libraries(document_serializer_xml_ifc${schema} PRIVATE plugin Serializers IfcGeom IfcParse) + set_target_properties(document_serializer_xml_ifc${schema} PROPERTIES + COMPILE_FLAGS "-DIfcSchema=Ifc${schema}" + OUTPUT_NAME "document.xml.ifc${schema}" + RUNTIME_OUTPUT_DIRECTORY "${document_serializer_plugin_runtime_dir}" + LIBRARY_OUTPUT_DIRECTORY "${document_serializer_plugin_runtime_dir}" + ) + list(APPEND document_serializer_libraries document_serializer_xml_ifc${schema}) - install(TARGETS Serializers_ifc${schema}) + if(WITH_GLTF) + add_library(document_serializer_json_ifc${schema} SHARED JsonSerializer.cpp json_plugin.cpp) + target_link_libraries(document_serializer_json_ifc${schema} PRIVATE plugin Serializers IfcGeom IfcParse) + set_target_properties(document_serializer_json_ifc${schema} PROPERTIES + COMPILE_FLAGS "-DIfcSchema=Ifc${schema}" + OUTPUT_NAME "document.json.ifc${schema}" + RUNTIME_OUTPUT_DIRECTORY "${document_serializer_plugin_runtime_dir}" + LIBRARY_OUTPUT_DIRECTORY "${document_serializer_plugin_runtime_dir}" + ) + list(APPEND document_serializer_libraries document_serializer_json_ifc${schema}) + endif() endforeach() - -set(SERIALIZER_SCHEMA_LIBRARIES ${SERIALIZER_SCHEMA_LIBRARIES} PARENT_SCOPE) +set(document_serializer_libraries ${document_serializer_libraries} PARENT_SCOPE) diff --git a/src/serializers/schema_dependent/JsonSerializer.cpp b/src/serializers/schema_dependent/JsonSerializer.cpp index 11f5dc1548..48b2087338 100644 --- a/src/serializers/schema_dependent/JsonSerializer.cpp +++ b/src/serializers/schema_dependent/JsonSerializer.cpp @@ -32,22 +32,6 @@ using json = nlohmann::json; -namespace { - struct POSTFIX_SCHEMA(factory_t) { - JsonSerializer* operator()(ifcopenshell::file* file, const std::string& json_filename, JsonSerializer::Dialect dialect) const { - POSTFIX_SCHEMA(JsonSerializer)* s = new POSTFIX_SCHEMA(JsonSerializer)(file, json_filename, dialect); - s->setFile(file); - return s; - } - }; -} - -void MAKE_INIT_FN(JsonSerializer)(JsonSerializerFactory::Factory* mapping) { - static const std::string schema_name = STRINGIFY(IfcSchema); - POSTFIX_SCHEMA(factory_t) factory; - mapping->bind(schema_name, factory); -} - namespace { class format_value_visitor : public boost::static_visitor { @@ -598,4 +582,4 @@ void POSTFIX_SCHEMA(JsonSerializer)::finalize() { f << output.dump(4); } -#endif \ No newline at end of file +#endif diff --git a/src/serializers/schema_dependent/XmlSerializer.cpp b/src/serializers/schema_dependent/XmlSerializer.cpp index 11c923238e..1439771c10 100644 --- a/src/serializers/schema_dependent/XmlSerializer.cpp +++ b/src/serializers/schema_dependent/XmlSerializer.cpp @@ -32,24 +32,6 @@ using boost::property_tree::ptree; -#include "XmlSerializer.h" - -namespace { - struct POSTFIX_SCHEMA(factory_t) { - XmlSerializer* operator()(ifcopenshell::file* file, const std::string& xml_filename) const { - POSTFIX_SCHEMA(XmlSerializer)* s = new POSTFIX_SCHEMA(XmlSerializer)(file, xml_filename); - s->setFile(file); - return s; - } - }; -} - -void MAKE_INIT_FN(XmlSerializer)(XmlSerializerFactory::Factory* mapping) { - static const std::string schema_name = STRINGIFY(IfcSchema); - POSTFIX_SCHEMA(factory_t) factory; - mapping->bind(schema_name, factory); -} - namespace { // TODO: Make this a member of XmlSerializer? diff --git a/src/serializers/schema_dependent/json_plugin.cpp b/src/serializers/schema_dependent/json_plugin.cpp new file mode 100644 index 0000000000..ee3f9b4e4b --- /dev/null +++ b/src/serializers/schema_dependent/json_plugin.cpp @@ -0,0 +1,66 @@ +/******************************************************************************** + * * + * This file is part of IfcOpenShell. * + * * + * IfcOpenShell is free software: you can redistribute it and/or modify * + * it under the terms of the Lesser GNU General Public License as published by * + * the Free Software Foundation, either version 3.0 of the License, or * + * (at your option) any later version. * + * * + * IfcOpenShell is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * Lesser GNU General Public License for more details. * + * * + * You should have received a copy of the Lesser GNU General Public License * + * along with this program. If not, see . * + * * + ********************************************************************************/ + +#ifdef WITH_GLTF + +#include "../document_serializer_plugin.h" +#include "JsonSerializer.h" + +#include "../../ifcparse/macros.h" + +#include + +namespace { + +struct factory_t { + JsonSerializer* operator()(ifcopenshell::file* file, const std::string& json_filename, JsonSerializer::Dialect dialect) const { + auto* serializer = new POSTFIX_SCHEMA(JsonSerializer)(file, json_filename, dialect); + serializer->setFile(file); + return serializer; + } +}; + +} + +namespace ifcopenshell { +namespace serializers { +namespace json_document_serializer_plugin { + +plugin::abi_info plugin_abi() { + return plugin::host_abi(); +} + +plugin::metadata plugin_metadata() { + return document_serializer_plugin_metadata("json", STRINGIFY(IfcSchema)); +} + +void register_plugin(JsonSerializerFactory::Factory& registry, const plugin::module& module) { + factory_t factory; + registry.bind(STRINGIFY(IfcSchema), factory, module); +} + +} +} +} + +BOOST_DLL_ALIAS(ifcopenshell::serializers::json_document_serializer_plugin::plugin_abi, ifcopenshell_plugin_abi_v1) +BOOST_DLL_ALIAS(ifcopenshell::serializers::json_document_serializer_plugin::plugin_metadata, ifcopenshell_plugin_metadata_v1) +BOOST_DLL_ALIAS(ifcopenshell::serializers::json_document_serializer_plugin::register_plugin, ifcopenshell_register_document_serializer_plugin_v1) + +#endif diff --git a/src/serializers/schema_dependent/xml_plugin.cpp b/src/serializers/schema_dependent/xml_plugin.cpp new file mode 100644 index 0000000000..43f6289bac --- /dev/null +++ b/src/serializers/schema_dependent/xml_plugin.cpp @@ -0,0 +1,62 @@ +/******************************************************************************** + * * + * This file is part of IfcOpenShell. * + * * + * IfcOpenShell is free software: you can redistribute it and/or modify * + * it under the terms of the Lesser GNU General Public License as published by * + * the Free Software Foundation, either version 3.0 of the License, or * + * (at your option) any later version. * + * * + * IfcOpenShell is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * Lesser GNU General Public License for more details. * + * * + * You should have received a copy of the Lesser GNU General Public License * + * along with this program. If not, see . * + * * + ********************************************************************************/ + +#include "../document_serializer_plugin.h" +#include "XmlSerializer.h" + +#include "../../ifcparse/macros.h" + +#include + +namespace { + +struct factory_t { + XmlSerializer* operator()(ifcopenshell::file* file, const std::string& xml_filename) const { + auto* serializer = new POSTFIX_SCHEMA(XmlSerializer)(file, xml_filename); + serializer->setFile(file); + return serializer; + } +}; + +} + +namespace ifcopenshell { +namespace serializers { +namespace xml_document_serializer_plugin { + +plugin::abi_info plugin_abi() { + return plugin::host_abi(); +} + +plugin::metadata plugin_metadata() { + return document_serializer_plugin_metadata("xml", STRINGIFY(IfcSchema)); +} + +void register_plugin(XmlSerializerFactory::Factory& registry, const plugin::module& module) { + factory_t factory; + registry.bind(STRINGIFY(IfcSchema), factory, module); +} + +} +} +} + +BOOST_DLL_ALIAS(ifcopenshell::serializers::xml_document_serializer_plugin::plugin_abi, ifcopenshell_plugin_abi_v1) +BOOST_DLL_ALIAS(ifcopenshell::serializers::xml_document_serializer_plugin::plugin_metadata, ifcopenshell_plugin_metadata_v1) +BOOST_DLL_ALIAS(ifcopenshell::serializers::xml_document_serializer_plugin::register_plugin, ifcopenshell_register_document_serializer_plugin_v1) diff --git a/src/serializers/serializers_api.h b/src/serializers/serializers_api.h index c11b099419..6188d4f82a 100644 --- a/src/serializers/serializers_api.h +++ b/src/serializers/serializers_api.h @@ -20,7 +20,9 @@ #ifndef IFC_SERIALIZERS_API_H #define IFC_SERIALIZERS_API_H -#ifdef _WIN32 +#ifdef SWIG + #define SERIALIZERS_API +#elif defined(_WIN32) #ifdef SERIALIZERS_EXPORTS #define SERIALIZERS_API __declspec(dllexport) #else diff --git a/src/svgfill/src/svgfill.h b/src/svgfill/src/svgfill.h index 903b7f1c3b..db58389dd8 100644 --- a/src/svgfill/src/svgfill.h +++ b/src/svgfill/src/svgfill.h @@ -21,7 +21,9 @@ #ifndef SVGFILL_H #define SVGFILL_H -#ifdef _WIN32 +#ifdef SWIG +#define SVGFILL_API +#elif defined(_WIN32) #ifdef svgfill_EXPORTS #define SVGFILL_API __declspec(dllexport) #else