From 0edfb0e791e102376f4d0cf02078b992c5e8c7db Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Thu, 18 Apr 2024 21:04:02 +0200 Subject: [PATCH] Settings rework for plan/model/context and interfacing with python --- src/ifcconvert/IfcConvert.cpp | 14 ++++++ src/ifcconvert/cityjson | 2 +- src/ifcgeom/ConversionSettings.cpp | 38 ++++++++++++++-- src/ifcgeom/ConversionSettings.h | 44 ++++++++++++------- src/ifcgeom/kernels/opencascade/IfcGeomTree.h | 4 +- src/ifcwrap/IfcGeomWrapper.i | 35 +++++++++------ src/ifcwrap/utils/type_conversion.i | 37 +++++++++++++--- src/ifcwrap/utils/typemaps_in.i | 34 ++++++++++++++ 8 files changed, 164 insertions(+), 44 deletions(-) diff --git a/src/ifcconvert/IfcConvert.cpp b/src/ifcconvert/IfcConvert.cpp index f841238ca6..53bf1d8168 100644 --- a/src/ifcconvert/IfcConvert.cpp +++ b/src/ifcconvert/IfcConvert.cpp @@ -329,6 +329,10 @@ int main(int argc, char** argv) { ("exterior-only", po::value(&exterior_only_algo)->default_value("none")->implicit_value("minkowski-triangles"), "Export only the exterior shell of the building found by geometric analysis. convex-decomposition, minkowski-triangles or halfspace-snapping") + ("plan", "Specifies whether to include curves in the output result. Typically " + "these are representations of type Plan or Axis. Excluded by default.") + ("model", "Specifies whether to include surfaces and solids in the output result. " + "Typically these are representations of type Body or Facetation. ") ; geometry_settings.define_options(geom_options); @@ -975,6 +979,16 @@ int main(int argc, char** argv) { Logger::Notice(msg.str()); } */ + + // backwards compatibility + if (vmap.count("plan") && vmap.count("model")) { + geometry_settings.get().value = ifcopenshell::geometry::settings::CURVES_SURFACES_AND_SOLIDS; + } else if (vmap.count("model")) { + geometry_settings.get().value = ifcopenshell::geometry::settings::SURFACES_AND_SOLIDS; + } else if (vmap.count("plan")) { + geometry_settings.get().value = ifcopenshell::geometry::settings::CURVES; + } + std::unique_ptr context_iterator; if (!elems_from_adaptor) { context_iterator.reset(new IfcGeom::Iterator(geometry_kernel, geometry_settings, ifc_file, filter_funcs, num_threads)); diff --git a/src/ifcconvert/cityjson b/src/ifcconvert/cityjson index 581f3a29f8..ca39a6d81e 160000 --- a/src/ifcconvert/cityjson +++ b/src/ifcconvert/cityjson @@ -1 +1 @@ -Subproject commit 581f3a29f8291a4a258464a9e8c2f9463ec94008 +Subproject commit ca39a6d81e7da5f6473b2104bbc1df1b48b35d80 diff --git a/src/ifcgeom/ConversionSettings.cpp b/src/ifcgeom/ConversionSettings.cpp index 36669c7971..3841191f80 100644 --- a/src/ifcgeom/ConversionSettings.cpp +++ b/src/ifcgeom/ConversionSettings.cpp @@ -10,14 +10,28 @@ double ifcopenshell::geometry::ConversionSettings::getValue(GeomValue var) const } */ -std::istream& std::operator>>(istream& in, set& ints) { - string tokens; +template +void istream_helper(std::istream& in, std::set& ints) { + std::string tokens; in >> tokens; - vector strs; + std::vector strs; boost::split(strs, tokens, boost::is_any_of(",")); for (auto& s : strs) { - ints.insert(boost::lexical_cast(s)); + if constexpr (std::is_same_v) { + ints.insert(s); + } else { + ints.insert(boost::lexical_cast(s)); + } } +} + +std::istream& std::operator>>(istream& in, set& ints) { + istream_helper(in, ints); + return in; +} + +std::istream& std::operator>>(istream& in, set& strs) { + istream_helper(in, strs); return in; } @@ -51,3 +65,19 @@ std::istream& ifcopenshell::geometry::settings::operator>>(std::istream& in, Pie } return in; } + +std::istream& ifcopenshell::geometry::settings::operator>>(std::istream& in, OutputDimensionalityTypes& v) { + std::string token; + in >> token; + boost::to_upper(token); + if (token == "CURVES") { + v = CURVES; + } else if (token == "SURFACES_AND_SOLIDS") { + v = SURFACES_AND_SOLIDS; + } else if (token == "CURVES_SURFACES_AND_SOLIDS") { + v = CURVES_SURFACES_AND_SOLIDS; + } else { + in.setstate(std::ios_base::failbit); + } + return in; +} diff --git a/src/ifcgeom/ConversionSettings.h b/src/ifcgeom/ConversionSettings.h index b6572883d5..8da3966f5d 100644 --- a/src/ifcgeom/ConversionSettings.h +++ b/src/ifcgeom/ConversionSettings.h @@ -23,6 +23,7 @@ namespace po = boost::program_options; namespace std { istream& operator>>(istream& in, set& ints); + istream& operator>>(istream& in, set& ints); } #endif @@ -120,21 +121,6 @@ namespace ifcopenshell { static constexpr double defaultvalue = 0.00001; }; - struct IncludeCurves : public SettingBase { - static constexpr const char* const name = "plan"; - static constexpr const char* const description = "Specifies whether to include curves in the output result. Typically " - "these are representations of type Plan or Axis. Excluded by default."; - static constexpr bool defaultvalue = false; - }; - - struct IncludeSurfaces : public SettingBase { - static constexpr const char* const name = "model"; - static constexpr const char* const description = "Specifies whether to include surfaces and solids in the output result. " - "Typically these are representations of type Body or Facetation. " - "Included by default."; - static constexpr bool defaultvalue = true; - }; - struct LayersetFirst : public SettingBase { static constexpr const char* const name = "layerset-first"; static constexpr const char* const description = "Assigns the first layer material of the layerset " @@ -214,6 +200,30 @@ namespace ifcopenshell { static constexpr const char* const description = ""; }; + struct ContextTypes : public SettingBase> { + static constexpr const char* const name = "context-types"; + static constexpr const char* const description = ""; + }; + + struct ContextIdentifiers : public SettingBase> { + static constexpr const char* const name = "context-identifiers"; + static constexpr const char* const description = ""; + }; + + enum OutputDimensionalityTypes { + CURVES, + SURFACES_AND_SOLIDS, + CURVES_SURFACES_AND_SOLIDS + }; + + std::istream& operator>>(std::istream& in, OutputDimensionalityTypes& ioo); + + struct OutputDimensionality : public SettingBase { + static constexpr const char* const name = "dimensionality"; + static constexpr const char* const description = "Specifies whether to include curves and/or surfaces and solids in the output result. Defaults to only surfaces and solids."; + static constexpr OutputDimensionalityTypes defaultvalue = CURVES_SURFACES_AND_SOLIDS; + }; + enum IteratorOutputOptions { TRIANGULATED, NATIVE, @@ -336,7 +346,7 @@ namespace ifcopenshell { template class IFC_GEOM_API SettingsContainer { public: - typedef boost::variant, IteratorOutputOptions, PiecewiseStepMethod> value_variant_t; + typedef boost::variant, std::set, IteratorOutputOptions, PiecewiseStepMethod, OutputDimensionalityTypes> value_variant_t; private: settings_t settings; @@ -416,7 +426,7 @@ namespace ifcopenshell { }; class IFC_GEOM_API Settings : public SettingsContainer< - std::tuple + std::tuple > {}; } diff --git a/src/ifcgeom/kernels/opencascade/IfcGeomTree.h b/src/ifcgeom/kernels/opencascade/IfcGeomTree.h index 9e436ea24c..d2d41c4da2 100644 --- a/src/ifcgeom/kernels/opencascade/IfcGeomTree.h +++ b/src/ifcgeom/kernels/opencascade/IfcGeomTree.h @@ -80,8 +80,8 @@ namespace IfcGeom { struct clash { int clash_type; // 0 = protrusion, 1 = pierce, 2 = collision, 3 = clearance - IfcUtil::IfcBaseClass* a; - IfcUtil::IfcBaseClass* b; + const IfcUtil::IfcBaseClass* a; + const IfcUtil::IfcBaseClass* b; double distance; std::array p1; std::array p2; diff --git a/src/ifcwrap/IfcGeomWrapper.i b/src/ifcwrap/IfcGeomWrapper.i index 5100c475df..311f87d716 100644 --- a/src/ifcwrap/IfcGeomWrapper.i +++ b/src/ifcwrap/IfcGeomWrapper.i @@ -114,7 +114,13 @@ std::pair vector_to_buffer(const T& t) { } void set_(const std::string& name, ifcopenshell::geometry::settings::IteratorOutputOptions val) { return $self->set(name, val); - } + } + void set_(const std::string& name, ifcopenshell::geometry::settings::PiecewiseStepMethod val) { + return $self->set(name, val); + } + void set_(const std::string& name, ifcopenshell::geometry::settings::OutputDimensionalityTypes val) { + return $self->set(name, val); + } void set_(const std::string& name, double val) { return $self->set(name, val); } @@ -124,6 +130,9 @@ std::pair vector_to_buffer(const T& t) { void set_(const std::string& name, const std::set& val) { return $self->set(name, val); } + void set_(const std::string& name, const std::set& val) { + return $self->set(name, val); + } ifcopenshell::geometry::Settings::value_variant_t get_(const std::string& name) { return $self->get(name); } @@ -244,8 +253,8 @@ std::pair vector_to_buffer(const T& t) { } 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; + std::vector set_a_entities; + std::vector set_b_entities; for (auto* e : set_a) { if (!e->declaration().is("IfcProduct")) { throw IfcParse::IfcException("All instances should be of type IfcProduct"); @@ -262,8 +271,8 @@ std::pair vector_to_buffer(const T& t) { } 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; + std::vector set_a_entities; + std::vector set_b_entities; for (auto* e : set_a) { if (!e->declaration().is("IfcProduct")) { throw IfcParse::IfcException("All instances should be of type IfcProduct"); @@ -280,8 +289,8 @@ std::pair vector_to_buffer(const T& t) { } 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; + std::vector set_a_entities; + std::vector set_b_entities; for (auto* e : set_a) { if (!e->declaration().is("IfcProduct")) { throw IfcParse::IfcException("All instances should be of type IfcProduct"); @@ -597,13 +606,12 @@ struct ShapeRTTI : public boost::static_visitor if (!rep->RepresentationIdentifier()) { continue; } - if (settings.get().get()) { - if (*rep->RepresentationIdentifier() == "Body") { + if (settings.get().get() != ifcopenshell::geometry::settings::CURVES) { + if (*rep->RepresentationIdentifier() == "Body" || *rep->RepresentationIdentifier() == "Facetation") { ifc_representation = rep; break; } - } - if (settings.get().get()) { + } else { if (*rep->RepresentationIdentifier() == "Plan" || *rep->RepresentationIdentifier() == "Axis") { ifc_representation = rep; break; @@ -621,13 +629,12 @@ struct ShapeRTTI : public boost::static_visitor // TODO: Remove redundancy with IfcGeomIterator.h if (context->ContextType()) { std::set context_types; - if (settings.get().get()) { + if (settings.get().get() != ifcopenshell::geometry::settings::CURVES) { context_types.insert("model"); context_types.insert("design"); context_types.insert("model view"); context_types.insert("detail view"); - } - if (settings.get().get()) { + } else { context_types.insert("plan"); } diff --git a/src/ifcwrap/utils/type_conversion.i b/src/ifcwrap/utils/type_conversion.i index 67843cfcd2..6a1ef8370f 100644 --- a/src/ifcwrap/utils/type_conversion.i +++ b/src/ifcwrap/utils/type_conversion.i @@ -85,18 +85,40 @@ return static_cast(SWIG_IsOK(res) ? arg : 0); } - template - std::vector python_sequence_as_vector(PyObject* aggregate) { - std::vector result_vector; - result_vector.reserve(PySequence_Size(aggregate)); + template + void add_to_container(std::vector& container, const T& element) { + container.push_back(element); + } + + template + void add_to_container(std::set& container, const T& element) { + container.insert(element); + } + + template typename U> + U python_sequence_as_cpp_container(PyObject* aggregate) { + U result_vector; + if constexpr (std::is_same_v, std::vector>) { + result_vector.reserve(PySequence_Size(aggregate)); + } for(Py_ssize_t i = 0; i < PySequence_Size(aggregate); ++i) { PyObject* element = PySequence_GetItem(aggregate, i); T t = cast_pyobject(element); - result_vector.push_back(t); + add_to_container(result_vector, t); } return result_vector; } + template + std::vector python_sequence_as_vector(PyObject* aggregate) { + return python_sequence_as_cpp_container(aggregate); + } + + template + std::set python_sequence_as_set(PyObject* aggregate) { + return python_sequence_as_cpp_container(aggregate); + } + template std::vector< std::vector > python_sequence_as_vector_of_vector(PyObject* aggregate) { std::vector< std::vector > result_vector; @@ -196,7 +218,10 @@ if constexpr (std::is_same_v>, std::set>) { std::vector vs(t.begin(), t.end()); return pythonize_vector(vs); - } else { + } else if constexpr (std::is_same_v>, std::set>) { + std::vector vs(t.begin(), t.end()); + return pythonize_vector(vs); + } else { return pythonize(t); } } diff --git a/src/ifcwrap/utils/typemaps_in.i b/src/ifcwrap/utils/typemaps_in.i index 90a6a9fec2..86de15a28d 100644 --- a/src/ifcwrap/utils/typemaps_in.i +++ b/src/ifcwrap/utils/typemaps_in.i @@ -372,3 +372,37 @@ CREATE_OPTIONAL_TYPEMAP_IN(std::string, string, str) %typemap(freearg) const std::vector>& { delete $1; } + + + +%define CREATE_SET_TYPEMAP_IN(template_type) + + %typemap(in) std::set { + if (!check_aggregate_of_type($input, get_python_type())) { + SWIG_exception(SWIG_TypeError, "Invalid"); + } + $1 = python_sequence_as_set($input); + } + %typemap(typecheck,precedence=SWIG_TYPECHECK_INTEGER) std::set { + $1 = check_aggregate_of_type($input, get_python_type()) ? 1 : 0; + } + + %typemap(typecheck,precedence=SWIG_TYPECHECK_INTEGER) const std::set& { + $1 = check_aggregate_of_type($input, get_python_type()) ? 1 : 0; + } + %typemap(arginit) const std::set& { + $1 = new std::set(); + } + %typemap(in) const std::set& { + if (!check_aggregate_of_type($input, get_python_type())) { + SWIG_exception(SWIG_TypeError, "Invalid"); + } + *$1 = python_sequence_as_set($input); + } + %typemap(freearg) const std::set& { + delete $1; + } +%enddef + +CREATE_SET_TYPEMAP_IN(int) +CREATE_SET_TYPEMAP_IN(std::string)