#ifndef CONVERSIONSETTINGS_H #define CONVERSIONSETTINGS_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ifc_geom_api.h" #ifndef SWIG namespace po = boost::program_options; namespace std { IFC_GEOM_API istream& operator>>(istream& in, set& ints); IFC_GEOM_API istream& operator>>(istream& in, set& ints); IFC_GEOM_API istream& operator>>(istream& in, vector& vs); } #endif namespace ifcopenshell { namespace geometry { inline namespace settings { #ifndef SWIG template struct HasDefault : std::false_type { }; template struct HasDefault : std::true_type { }; #endif template struct SettingBase { typedef T base_type; // boost program options does not seem to handle optional types, so in case // of vector settings we need to strip away the optional and detect argument presence // with !vector::empty() // tfk: we no longer do this because negative values can not be passed like this as boost confuses them with options // std::conditional_t>, T, boost::optional> value; boost::optional value; SettingBase() {} void defineOption(po::options_description& desc) { auto apply_default = [](auto x) { if constexpr (HasDefault()) { return x->default_value(Derived::defaultvalue); } else { return x; } }; if constexpr (Internal) { // do nothing, this is an internal setting and not supposed to be set from the command line } else if constexpr (std::is_same_v) { // @todo bool_switch doesn't work with optional unfortunately... value.emplace(); desc.add_options()(Derived::name, apply_default(po::bool_switch(&*value)), Derived::description); } else if constexpr (std::is_same_v>) { // these options have to be supplied manually in IfcConvert.cpp // desc.add_options()(Derived::name, apply_default(po::value(&value)->multitoken()), Derived::description); } else { desc.add_options()(Derived::name, apply_default(po::value(&value)), Derived::description); } } T get() const { if constexpr (false && std::is_same_v>) { return value; } else { if (value) { return value.get(); } if constexpr (HasDefault()) { return Derived::defaultvalue; } else { throw std::runtime_error("Setting not set"); } } } bool has() const { if constexpr (false && std::is_same_v>) { return !value.empty(); } else { // @todo this is not reliable, better use vmap[...].defaulted() return !!value; } } }; // These are the old geometry settings values from the kernel struct MesherLinearDeflection : public SettingBase { static constexpr const char* const name = "mesher-linear-deflection"; static constexpr const char* const description = "Specifies the linear deflection of the mesher. Controls the detail of curved surfaces in triangulated output formats."; static constexpr double defaultvalue = 0.001; }; struct MesherAngularDeflection : public SettingBase { static constexpr const char* const name = "mesher-angular-deflection"; static constexpr const char* const description = "Sets the angular tolerance of the mesher in radians 0.5 by default if not specified."; static constexpr double defaultvalue = 0.5; }; struct ReorientShells : public SettingBase { static constexpr const char* const name = "reorient-shells"; static constexpr const char* const description = "Specifies whether to orient the faces of IfcConnectedFaceSets. " "This is a potentially time consuming operation, but guarantees a " "consistent orientation of surface normals, even if the faces are not " "properly oriented in the IFC file."; static constexpr bool defaultvalue = false; }; struct LengthUnit : public SettingBase { static constexpr const char* const name = "length-unit"; static constexpr const char* const description = ""; static constexpr double defaultvalue = 1.0; }; struct PlaneUnit : public SettingBase { static constexpr const char* const name = "angle-unit"; static constexpr const char* const description = ""; static constexpr double defaultvalue = 1.0; }; struct Precision : public SettingBase { static constexpr const char* const name = "precision"; static constexpr const char* const description = ""; static constexpr double defaultvalue = 0.00001; }; 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 " "to the complete product."; static constexpr bool defaultvalue = false; }; struct DisableBooleanResult : public SettingBase { static constexpr const char* const name = "disable-boolean-result"; static constexpr const char* const description = "Specifies whether to disable the boolean operation within representations " "such as clippings by means of IfcBooleanResult and subtypes"; static constexpr bool defaultvalue = false; }; struct NoWireIntersectionCheck : public SettingBase { static constexpr const char* const name = "no-wire-intersection-check"; static constexpr const char* const description = "Skip wire intersection check."; static constexpr bool defaultvalue = false; }; struct NoWireIntersectionTolerance : public SettingBase { static constexpr const char* const name = "no-wire-intersection-tolerance"; static constexpr const char* const description = "Set wire intersection tolerance to 0."; static constexpr bool defaultvalue = false; }; struct PrecisionFactor : public SettingBase { static constexpr const char* const name = "precision-factor"; static constexpr const char* const description = "Option to increase linear tolerance for more permissive edge curves and fewer artifacts after " "boolean operations at the expense of geometric detail " "due to vertex collapsing and wire intersection fuzziness."; static constexpr double defaultvalue = 1.0; }; struct DebugBooleanOperations : public SettingBase { static constexpr const char* const name = "debug"; static constexpr const char* const description = "write boolean operands to file in current directory for debugging purposes"; static constexpr bool defaultvalue = false; }; struct BooleanAttempt2d : public SettingBase { static constexpr const char* const name = "boolean-attempt-2d"; static constexpr const char* const description = "Do not attempt to process boolean subtractions in 2D."; static constexpr bool defaultvalue = true; }; // These are the old IteratorSettings struct WeldVertices : public SettingBase { static constexpr const char* const name = "weld-vertices"; static constexpr const char* const description = "Specifies whether vertices are welded, meaning that the coordinates " "vector will only contain unique xyz-triplets. This results in a " "manifold mesh which is useful for modelling applications, but might " "result in unwanted shading artefacts in rendering applications."; static constexpr bool defaultvalue = true; }; struct UseWorldCoords : public SettingBase { static constexpr const char* const name = "use-world-coords"; static constexpr const char* const description = "Specifies whether to apply the local placements of building elements " "directly to the coordinates of the representation mesh rather than " "to represent the local placement in the 4x3 matrix, which will in that " "case be the identity matrix."; static constexpr bool defaultvalue = false; }; struct UnifyShapes : public SettingBase { static constexpr const char* const name = "unify-shapes"; static constexpr const char* const description = "Unify adjacent co-planar and co-linear subshapes (topological entities " "sharing the same geometric domain) before triangulation or further processing"; static constexpr bool defaultvalue = false; }; struct UseMaterialNames : public SettingBase { static constexpr const char* const name = "use-material-names"; static constexpr const char* const description = "Use material names instead of unique IDs for naming materials upon serialization. " "Applicable for OBJ and DAE output."; static constexpr bool defaultvalue = false; }; struct ConvertBackUnits : public SettingBase { static constexpr const char* const name = "convert-back-units"; static constexpr const char* const description = "Specifies whether to convert back geometrical output back to the " "unit of measure in which it is defined in the IFC file. Default is " "to use meters."; static constexpr bool defaultvalue = false; }; struct ContextIds : public SettingBase> { static constexpr const char* const name = "context-ids"; static constexpr const char* const description = "List of comma separated context ids to process - e.g. '15,29' (no quotes needed)."; }; struct ContextTypes : public SettingBase> { static constexpr const char* const name = "context-types"; static constexpr const char* const description = "Currently option has no effect."; }; struct ContextIdentifiers : public SettingBase> { static constexpr const char* const name = "context-identifiers"; static constexpr const char* const description = "Currently option has no effect."; }; enum OutputDimensionalityTypes { CURVES, SURFACES_AND_SOLIDS, CURVES_SURFACES_AND_SOLIDS }; IFC_GEOM_API 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 (SURFACES_AND_SOLIDS). " "Other possible values are CURVES, CURVES_SURFACES_AND_SOLIDS."; static constexpr OutputDimensionalityTypes defaultvalue = SURFACES_AND_SOLIDS; }; enum IteratorOutputOptions { TRIANGULATED, NATIVE, SERIALIZED }; IFC_GEOM_API std::istream& operator>>(std::istream& in, IteratorOutputOptions& ioo); struct IteratorOutput : public SettingBase { static constexpr const char* const name = "iterator-output"; static constexpr const char* const description = ""; static constexpr IteratorOutputOptions defaultvalue = TRIANGULATED; }; struct DisableOpeningSubtractions : public SettingBase { static constexpr const char* const name = "disable-opening-subtractions"; static constexpr const char* const description = "Specifies whether to disable the boolean subtraction of " "IfcOpeningElement Representations from their RelatingElements."; static constexpr bool defaultvalue = false; }; struct ApplyDefaultMaterials : public SettingBase { static constexpr const char* const name = "apply-default-materials"; static constexpr const char* const description = ""; static constexpr bool defaultvalue = true; }; struct DontEmitNormals : public SettingBase { static constexpr const char* const name = "no-normals"; static constexpr const char* const description = "Disables computation of normals.Saves time and file size and is useful " "in instances where you're going to recompute normals for the exported " "model in other modelling application in any case."; static constexpr bool defaultvalue = false; }; struct GenerateUvs : public SettingBase { static constexpr const char* const name = "generate-uvs"; static constexpr const char* const description = "Generates UVs (texture coordinates) by using simple box projection. Requires normals. " "Not guaranteed to work properly if used with --weld-vertices."; static constexpr bool defaultvalue = false; }; struct ApplyLayerSets : public SettingBase { static constexpr const char* const name = "enable-layerset-slicing"; static constexpr const char* const description = "Specifies whether to enable the slicing of products according " "to their associated IfcMaterialLayerSet."; static constexpr bool defaultvalue = false; }; struct UseElementHierarchy : public SettingBase { static constexpr const char* const name = "element-hierarchy"; static constexpr const char* const description = "Assign the elements using their e.g IfcBuildingStorey parent." "Applicable to DAE output."; static constexpr bool defaultvalue = false; }; struct ValidateQuantities : public SettingBase { static constexpr const char* const name = "validate"; static constexpr const char* const description = "Checks whether geometrical output conforms to the included explicit quantities."; static constexpr bool defaultvalue = false; }; struct EdgeArrows : public SettingBase { static constexpr const char* const name = "edge-arrows"; static constexpr const char* const description = "Adds arrow heads to edge segments to signify edge direction"; static constexpr bool defaultvalue = false; }; struct SiteLocalPlacement : public SettingBase { static constexpr const char* const name = "site-local-placement"; static constexpr const char* const description = "Place elements locally in the IfcSite coordinate system, instead of placing " "them in the IFC global coords. Applicable for OBJ, DAE, and STP output."; static constexpr bool defaultvalue = false; }; struct BuildingLocalPlacement : public SettingBase { static constexpr const char* const name = "building-local-placement"; static constexpr const char* const description = "Similar to --site-local-placement, but placing elements in locally in the parent IfcBuilding coord system"; static constexpr bool defaultvalue = false; }; struct NoParallelMapping : public SettingBase { static constexpr const char* const name = "no-parallel-mapping"; static constexpr const char* const description = "Perform mapping upfront (single-threaded) as opposed to in parallel. May decrease performance, but also decrease output size (in the future)"; static constexpr bool defaultvalue = false; }; struct PermissiveShapeReuse : public SettingBase { static constexpr const char* const name = "permissive-shape-reuse"; static constexpr const char* const description = "Traverse geometry-level transformations and apply to product-level placement in order to increase reuse of geometries"; static constexpr bool defaultvalue = false; }; struct ForceSpaceTransparency : public SettingBase { static constexpr const char* const name = "force-space-transparency"; static constexpr const char* const description = "Overrides transparency of spaces in geometry output."; }; struct CircleSegments : public SettingBase { static constexpr const char* const name = "circle-segments"; static constexpr const char* const description = "Number of segments to approximate full circles in the CGAL kernel. When 0 (the default) the segment count is derived from mesher-linear-deflection instead, so curves stay within the deflection tolerance regardless of radius."; static constexpr int defaultvalue = 0; }; struct CgalSmoothAngleDegrees : public SettingBase { static constexpr const char* const name = "cgal-smooth-angle-degrees"; static constexpr const char* const description = "Angle in degrees under which adjacent facets will have averaged vertex normals in CGAL output. NB irrespective of original IFC geometry types. Defaults to -1 to disable smoothing."; static constexpr double defaultvalue = -1.; }; struct SvgRidgeAngleMinDegrees : public SettingBase { static constexpr const char* const name = "svg-ridge-angle-min-degrees"; static constexpr const char* const description = "SVG edge classification (issue #3668): minimum convex dihedral deviation from flat, in degrees, for a projection edge to be classified as 'sharp' rather than 'flush'."; static constexpr double defaultvalue = 45.; }; struct SvgValleyAngleMinDegrees : public SettingBase { static constexpr const char* const name = "svg-valley-angle-min-degrees"; static constexpr const char* const description = "SVG edge classification (issue #3668): minimum concave dihedral deviation from flat, in degrees, for a projection edge to be classified as 'crease' rather than 'flush'."; static constexpr double defaultvalue = 12.; }; struct SvgEmitFlushEdges : public SettingBase { static constexpr const char* const name = "svg-emit-flush-edges"; static constexpr const char* const description = "SVG edge classification (issue #3668): whether to emit 'flush' projection edges (dihedral deviation below both ridge/valley thresholds). Defaults to false, i.e. flush edges are omitted from the output."; static constexpr bool defaultvalue = false; }; struct SvgUseEdgeClassification : public SettingBase { static constexpr const char* const name = "svg-use-edge-classification"; static constexpr const char* const description = "SVG edge classification (issue #3668): enable the 5-class boundary/outline/sharp/crease/flush scheme. When false (the default), falls back to the original unclassified linework."; static constexpr bool defaultvalue = false; }; struct SvgRenderCreaseEdges : public SettingBase { static constexpr const char* const name = "svg-render-crease-edges"; static constexpr const char* const description = "SVG edge classification (issue #3668): whether to emit 'crease' (concave) projection edges. Only relevant when svg-use-edge-classification is enabled."; static constexpr bool defaultvalue = true; }; struct SvgRenderSharpEdges : public SettingBase { static constexpr const char* const name = "svg-render-sharp-edges"; static constexpr const char* const description = "SVG edge classification (issue #3668): whether to emit 'sharp' (convex) projection edges. Only relevant when svg-use-edge-classification is enabled."; static constexpr bool defaultvalue = true; }; struct KeepBoundingBoxes : public SettingBase { static constexpr const char* const name = "keep-bounding-boxes"; static constexpr const char* const description = "Default is to removes IfcBoundingBox from model prior to converting geometry." "Setting this option disables that behaviour"; static constexpr bool defaultvalue = false; }; struct SurfaceColour : public SettingBase { static constexpr const char* const name = "surface-colour"; static constexpr const char* const description = "Prioritizes the surface color instead of using diffuse."; static constexpr bool defaultvalue = false; }; struct ComputeCurvature : public SettingBase { static constexpr const char* const name = "compute-curvature"; static constexpr const char* const description = "Specifies whether function_item_evaluator.evaluate() computes curvature."; static constexpr bool defaultvalue = false; }; enum FunctionStepMethod { MAXSTEPSIZE, MINSTEPS }; IFC_GEOM_API std::istream& operator>>(std::istream& in, FunctionStepMethod& ioo); struct FunctionStepType : public SettingBase { static constexpr const char* const name = "function-step-type"; static constexpr const char* const description = "Indicates the method used for defining step size when evaluating function-based curves. Provides interpretation of function-step-param"; static constexpr FunctionStepMethod defaultvalue = MAXSTEPSIZE; }; struct FunctionStepParam : public SettingBase { static constexpr const char* const name = "function-step-param"; static constexpr const char* const description = "Indicates the parameter value for defining step size when evaluating function-based curves."; static constexpr double defaultvalue = 0.5; // ceiling of this value is used when FunctionStepMethod is MinSteps }; struct ModelOffset : public SettingBase> { static constexpr const char* const name = "model-offset"; static constexpr const char* const description = "Applies an arbitrary offset of form x,y,z to all placements."; }; struct ModelRotation : public SettingBase> { static constexpr const char* const name = "model-rotation"; static constexpr const char* const description = "Applies an arbitrary quaternion rotation of form x,y,z,w to all placements."; }; enum TriangulationMethod { TRIANGLE_MESH, POLYHEDRON_WITHOUT_HOLES, POLYHEDRON_WITH_HOLES }; IFC_GEOM_API std::istream& operator>>(std::istream& in, TriangulationMethod& ioo); struct TriangulationType : public SettingBase { static constexpr const char* const name = "triangulation-type"; static constexpr const char* const description = "Type of planar facet to be emitted"; static constexpr TriangulationMethod defaultvalue = TRIANGLE_MESH; }; struct CgalEmitOriginalEdges : public SettingBase { static constexpr const char* const name = "cgal-original-edges"; static constexpr const char* const description = "Try to emit original edge face boundary edges instead of recomputed ones based on face normal. Falls back to triangulated data in case of boolean operands and faces with holes."; static constexpr bool defaultvalue = false; }; struct OcctNoCleanTriangulation : public SettingBase { static constexpr const char* const name = "no-clean-triangulation"; static constexpr const char* const description = "Don't clean triangulations, might cause memory leaks"; static constexpr bool defaultvalue = false; }; struct CacheShapes : public SettingBase { static constexpr const char* const name = "cache-shapes"; static constexpr const char* const description = "Experimental as not all topology hash functions fully implemented"; static constexpr bool defaultvalue = false; }; struct MakeVolume : public SettingBase { static constexpr const char* const name = "make-volume"; static constexpr const char* const description = "Try to isolate and fix a valid volume from non-manifold elements prior to opening subtraction"; static constexpr bool defaultvalue = false; }; struct DeferProcessingFirstElement : public SettingBase { static constexpr const char* const name = "defer-processing-first-element"; static constexpr const char* const description = "Don't process first element in Iterator::initialize call()"; static constexpr bool defaultvalue = false; }; struct MaxOffset : public SettingBase { static constexpr const char* const name = "max-offset"; static constexpr const char* const description = "Maximum translation offset to be observed after which median offset in model gets removed and logged. Requires --no-parallel-mapping."; }; struct MaxOffsetDeviation : public SettingBase { static constexpr const char* const name = "max-offset-deviation"; static constexpr const char* const description = "To retain field of view, completely remove elements outside of the median offset. Requires --no-parallel-mapping."; }; struct ApplyOffset : public SettingBase> { static constexpr const char* const name = "apply-offset"; static constexpr const char* const description = "Slight variation of --model-offset where large offsets are applied by negating existing large offsets to retain maximum precision. Requires --no-parallel-mapping."; }; } namespace impl { template struct readable_name { static constexpr const char* name = "Unknown Type"; }; template <> struct readable_name { static constexpr const char* name = "bool"; }; template <> struct readable_name { static constexpr const char* name = "int"; }; template <> struct readable_name { static constexpr const char* name = "double"; }; template <> struct readable_name { static constexpr const char* name = "std::string"; }; template <> struct readable_name> { static constexpr const char* name = "std::set"; }; template <> struct readable_name> { static constexpr const char* name = "std::set"; }; template <> struct readable_name> { static constexpr const char* name = "std::vector"; }; template <> struct readable_name { static constexpr const char* name = "IteratorOutputOptions"; }; template <> struct readable_name { static constexpr const char* name = "FunctionStepMethod"; }; template <> struct readable_name { static constexpr const char* name = "OutputDimensionalityTypes"; }; template <> struct readable_name { static constexpr const char* name = "TriangulationMethod"; }; } template class SettingsContainer { public: typedef boost::variant, std::set, std::vector, IteratorOutputOptions, FunctionStepMethod, OutputDimensionalityTypes, TriangulationMethod> value_variant_t; private: settings_t settings; template void define_options_(po::options_description& desc) { std::get(settings).defineOption(desc); if constexpr (Index + 1 < std::tuple_size_v) { define_options_(desc); } } template value_variant_t get_option_(const std::string& name) const { if (std::tuple_element_t::name == name) { return std::get(settings).get(); } if constexpr (Index + 1 < std::tuple_size_v) { return get_option_(name); } else { throw std::runtime_error("Setting not available"); } } template std::string get_type_(const std::string& name) const { if (std::tuple_element_t::name == name) { return impl::readable_name::base_type>::name; } if constexpr (Index + 1 < std::tuple_size_v) { return get_type_(name); } else { throw std::runtime_error("Setting not available"); } } template void set_option_(const std::string& name, const value_variant_t& val) { if (std::tuple_element_t::name == name) { if constexpr (std::is_enum_v::base_type>) { if (auto* val_ptr = boost::get(&val)) { auto val_as_enum = (typename std::tuple_element_t::base_type) *val_ptr; std::get(settings).value = val_as_enum; return; } } try { std::get(settings).value = boost::get::base_type>(val); } catch (const boost::bad_get&) { std::string ty = impl::readable_name::base_type>::name; throw std::runtime_error("Expected a value of type <" + ty + "> for setting '" + name + "'"); } } else if constexpr (Index + 1 < std::tuple_size_v) { set_option_(name, val); } else { throw std::runtime_error("Setting not available"); } } template void get_setting_names_(std::vector& vec) const { vec.push_back(std::tuple_element_t::name); if constexpr (Index + 1 < std::tuple_size_v) { return get_setting_names_(vec); } } public: typedef settings_t settings_tuple; void define_options(po::options_description& desc) { define_options_<0>(desc); } template const T& get() const { return std::get(settings); } template T& get() { return std::get(settings); } template void set(T& v) { std::get(settings) = v; } value_variant_t get(const std::string& name) const { return get_option_<0>(name); } void set(const std::string& name, value_variant_t val) { set_option_<0>(name, val); } std::string get_type(const std::string& name) { return get_type_<0>(name); } std::vector setting_names() const { std::vector r; get_setting_names_<0>(r); return r; } }; class Settings : public SettingsContainer< std::tuple > {}; } } // @todo find a place namespace IfcGeom { #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable: 4275) #endif class IFC_GEOM_API geometry_exception : public std::runtime_error { protected: std::string message; public: geometry_exception(const std::string& m) : std::runtime_error(m) {} ~geometry_exception() override; }; class IFC_GEOM_API too_many_faces_exception : public geometry_exception { public: too_many_faces_exception() : geometry_exception("Too many faces for operation") {} ~too_many_faces_exception() override; }; } #if defined(_MSC_VER) #pragma warning(pop) #endif #endif