diff --git a/src/ifcopenshell-python/ifcopenshell/geom/main.py b/src/ifcopenshell-python/ifcopenshell/geom/main.py index af2b38e76f..6dbed7a69b 100644 --- a/src/ifcopenshell-python/ifcopenshell/geom/main.py +++ b/src/ifcopenshell-python/ifcopenshell/geom/main.py @@ -170,7 +170,12 @@ SETTING = Literal[ # It's possible to use any hybrid combination by the format below: # "hybrid-library1-library2". # List is updated from abstract_kernel.cpp. -GEOMETRY_LIBRARY = Literal["cgal", "cgal-simple", "opencascade", "hybrid-cgal-simple-opencascade"] +GEOMETRY_LIBRARY = Literal["cgal", "cgal-simple", "manifold", "opencascade", "hybrid-cgal-simple-opencascade"] + + +def has_geometry_library(geometry_library: str) -> bool: + """Return whether a geometry kernel library can be loaded.""" + return ifcopenshell_wrapper.has_geometry_library(geometry_library) class missing_setting: diff --git a/src/ifcopenshell-python/ifcopenshell/ifcopenshell_wrapper.pyi b/src/ifcopenshell-python/ifcopenshell/ifcopenshell_wrapper.pyi index a3a7c68a69..42457006f8 100644 --- a/src/ifcopenshell-python/ifcopenshell/ifcopenshell_wrapper.pyi +++ b/src/ifcopenshell-python/ifcopenshell/ifcopenshell_wrapper.pyi @@ -1623,6 +1623,7 @@ def arrange_polygons( def get_plugin_search_paths() -> tuple[str, ...]: ... def set_plugin_search_paths(paths: Sequence[str]) -> None: ... def clear_plugin_search_paths() -> None: ... +def has_geometry_library(geometry_library: str) -> bool: ... def clear_schemas(): ... def construct_iterator(geometry_library, settings, file, num_threads, logger=None): ... def construct_iterator_with_include_exclude( diff --git a/src/ifcopenshell-python/test/test_shape_stats.py b/src/ifcopenshell-python/test/test_shape_stats.py index 3ec65f609e..6cc67fbb7e 100644 --- a/src/ifcopenshell-python/test/test_shape_stats.py +++ b/src/ifcopenshell-python/test/test_shape_stats.py @@ -14,17 +14,27 @@ test_files = repo_root / "test" / "input" / "tests" settings = ifcopenshell.geom.settings() testdata = list(csv.reader((test_files / "data.csv").open(newline="")))[1:] +kernels = [ + pytest.param( + kernel, + marks=pytest.mark.skipif( + not ifcopenshell.geom.has_geometry_library(kernel), + reason=f"{kernel} geometry kernel is unavailable", + ), + ) + for kernel in ("manifold", "opencascade", "cgal") +] print(testdata) @pytest.mark.parametrize("fn,area,volume", testdata) -def test_conversion(fn, area, volume): +@pytest.mark.parametrize("kernel", kernels) +def test_conversion(fn, area, volume, kernel): f = ifcopenshell.open(test_files / fn) elem = next(inst for inst in f.by_type("IfcElement") if not inst.is_a("IfcOpeningElement")) - for kernel in ("manifold", "opencascade", "cgal"): - shp = ifcopenshell.geom.create_shape(settings, elem, geometry_library=kernel) - if area: - assert pytest.approx(float(area), rel=0.05) == ifcopenshell.util.shape.get_area(shp.geometry) - if volume: - assert pytest.approx(float(volume), rel=0.02) == ifcopenshell.util.shape.get_volume(shp.geometry) + shp = ifcopenshell.geom.create_shape(settings, elem, geometry_library=kernel) + if area: + assert pytest.approx(float(area), rel=0.05) == ifcopenshell.util.shape.get_area(shp.geometry) + if volume: + assert pytest.approx(float(volume), rel=0.02) == ifcopenshell.util.shape.get_volume(shp.geometry) diff --git a/src/ifcwrap/IfcPython.i b/src/ifcwrap/IfcPython.i index 804dbd89a7..d68524a09d 100644 --- a/src/ifcwrap/IfcPython.i +++ b/src/ifcwrap/IfcPython.i @@ -286,6 +286,7 @@ #include "../ifcgeom/conversion_result.h" #include "../ifcgeom/hybrid_kernel.h" #include "../ifcgeom/geometry_serializer.h" + #include "../ifcgeom/kernel_plugin.h" #include "../ifcparse/express.h" #include "../ifcparse/file.h" @@ -325,6 +326,16 @@ std::vector get_plugin_search_paths() { void clear_plugin_search_paths() { ifcopenshell::plugin::clear_search_paths(); } + +bool has_geometry_library(const std::string& geometry_library) { + auto& registry = ifcopenshell::geom::kernels::kernel_registry_instance(); + try { + return registry.has(geometry_library) || + ifcopenshell::geom::kernels::load_kernel_plugin(registry, geometry_library); + } catch (const std::exception&) { + return false; + } +} %} %include "IfcGeomWrapper.i"