diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index 382e7396b4..60294c2256 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -221,10 +221,11 @@ endif() if(GLTF_SUPPORT OR CITYJSON_SUPPORT) UNIFY_ENVVARS_AND_CACHE(JSON_INCLUDE_DIR) - find_file(json_hpp "json.hpp" ${JSON_INCLUDE_DIR}/nlohmann) + find_path(json_header_path "json.hpp" ${JSON_INCLUDE_DIR} PATH_SUFFIXES "nlohmann") + set(JSON_INCLUDE_DIR ${json_header_path}) - if(json_hpp) - message(STATUS "JSON for Modern C++ header file found") + if(json_header_path) + message(STATUS "JSON for Modern C++ header file found in ${JSON_INCLUDE_DIR}") else() message(FATAL_ERROR "Unable to find JSON for Modern C++ header file, aborting") endif() diff --git a/src/blenderbim/blenderbim/bim/import_ifc.py b/src/blenderbim/blenderbim/bim/import_ifc.py index 60d202194d..9018592086 100644 --- a/src/blenderbim/blenderbim/bim/import_ifc.py +++ b/src/blenderbim/blenderbim/bim/import_ifc.py @@ -551,7 +551,7 @@ class IfcImporter: for axis in axes: shape = tool.Loader.create_generic_shape(axis.AxisCurve) mesh = self.create_mesh(axis, shape) - obj = bpy.data.objects.new(f"IfcGridAxis/{axis.AxisTag}", mesh) + obj = bpy.data.objects.new(tool.Loader.get_name(axis), mesh) if tool.Blender.get_addon_preferences().lock_grids_on_import: obj.lock_location = (True, True, True) obj.lock_rotation = (True, True, True) @@ -781,7 +781,7 @@ class IfcImporter: mesh = bpy.data.meshes.new(mesh_name) mesh.from_pydata([mathutils.Vector(vertex) * self.unit_scale], [], []) - obj = bpy.data.objects.new("{}/{}".format(product.is_a(), product.Name), mesh) + obj = bpy.data.objects.new(tool.Loader.get_name(product), mesh) self.set_matrix_world(obj, tool.Loader.apply_blender_offset_to_matrix_world(obj, placement_matrix)) self.link_element(product, obj) @@ -847,7 +847,7 @@ class IfcImporter: mesh.from_pydata(vertex_list, [], []) tool.Ifc.link(representation, mesh) - obj = bpy.data.objects.new("{}/{}".format(product.is_a(), product.Name), mesh) + obj = bpy.data.objects.new(tool.Loader.get_name(product), mesh) self.set_matrix_world(obj, tool.Loader.apply_blender_offset_to_matrix_world(obj, placement_matrix)) self.link_element(product, obj) return product diff --git a/src/blenderbim/blenderbim/tool/loader.py b/src/blenderbim/blenderbim/tool/loader.py index a55339f4b0..3bfbc62cd0 100644 --- a/src/blenderbim/blenderbim/tool/loader.py +++ b/src/blenderbim/blenderbim/tool/loader.py @@ -91,6 +91,8 @@ class Loader(blenderbim.core.tool.Loader): @classmethod def get_name(cls, element: ifcopenshell.entity_instance) -> str: + if element.is_a("IfcGridAxis"): + return "{}/{}".format(element.is_a(), element.AxisTag) return "{}/{}".format(element.is_a(), getattr(element, "Name", "None")) @classmethod diff --git a/src/ifcgeom/mapping/IfcCurveSegment.cpp b/src/ifcgeom/mapping/IfcCurveSegment.cpp index 4e9cb9e1de..322b2f773b 100644 --- a/src/ifcgeom/mapping/IfcCurveSegment.cpp +++ b/src/ifcgeom/mapping/IfcCurveSegment.cpp @@ -888,7 +888,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcCurveSegment* inst) { auto length = cse.length(); - taxonomy::piecewise_function::spans spans; + taxonomy::piecewise_function::spans_t spans; spans.emplace_back(fabs(length), fn); auto pwf = taxonomy::make(0.0, spans,&settings_,inst); return pwf; diff --git a/src/ifcgeom/mapping/IfcGradientCurve.cpp b/src/ifcgeom/mapping/IfcGradientCurve.cpp index 144f68d877..3c5b95635a 100644 --- a/src/ifcgeom/mapping/IfcGradientCurve.cpp +++ b/src/ifcgeom/mapping/IfcGradientCurve.cpp @@ -84,7 +84,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcGradientCurve* inst) { return m; }; - taxonomy::piecewise_function::spans spans; + taxonomy::piecewise_function::spans_t spans; spans.emplace_back(length, composition); auto pwf = taxonomy::make(start, spans, &settings_, inst); return pwf; diff --git a/src/ifcgeom/mapping/IfcOffsetCurveByDistance.cpp b/src/ifcgeom/mapping/IfcOffsetCurveByDistance.cpp index ddc5621baa..7af8b42bd8 100644 --- a/src/ifcgeom/mapping/IfcOffsetCurveByDistance.cpp +++ b/src/ifcgeom/mapping/IfcOffsetCurveByDistance.cpp @@ -43,7 +43,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcOffsetCurveByDistances* inst double start = pw_curve->start(); double basis_curve_length = pw_curve->length(); - taxonomy::piecewise_function::spans offset_spans; + taxonomy::piecewise_function::spans_t offset_spans; #if defined SCHEMA_HAS_IfcDistanceExpression double first_distance = first_offset_value->DistanceAlong(); @@ -155,7 +155,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcOffsetCurveByDistances* inst // current implementation assumes that composition is equal to the full length of basis curve // this may change depending on decisions in the bSI-IF - taxonomy::piecewise_function::spans spans; + taxonomy::piecewise_function::spans_t spans; spans.emplace_back(basis_curve_length, composition); auto pwf = taxonomy::make(start,spans,&settings_,inst); return pwf; diff --git a/src/ifcgeom/mapping/IfcSegmentedReferenceCurve.cpp b/src/ifcgeom/mapping/IfcSegmentedReferenceCurve.cpp index 1ca21e3690..caf8104c65 100644 --- a/src/ifcgeom/mapping/IfcSegmentedReferenceCurve.cpp +++ b/src/ifcgeom/mapping/IfcSegmentedReferenceCurve.cpp @@ -83,7 +83,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSegmentedReferenceCurve* ins return m; }; - taxonomy::piecewise_function::spans spans; + taxonomy::piecewise_function::spans_t spans; spans.emplace_back(length, composition); auto pwf = taxonomy::make(start, spans, &settings_, inst); return pwf; diff --git a/src/ifcgeom/mapping/mapping.cpp b/src/ifcgeom/mapping/mapping.cpp index d9d140dc54..1fbd73d0c7 100644 --- a/src/ifcgeom/mapping/mapping.cpp +++ b/src/ifcgeom/mapping/mapping.cpp @@ -612,6 +612,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcStyledItem* inst) { taxonomy::ptr mapping::map(const IfcBaseInterface* inst) { auto iden = inst->as()->identity(); if (use_caching_) { + std::lock_guard guard(cache_guard_); auto it = cache_.find(iden); if (it != cache_.end()) { return it->second; @@ -629,7 +630,8 @@ taxonomy::ptr mapping::map(const IfcBaseInterface* inst) { if (item) { if (use_caching_) { - cache_.insert({ iden, item }); + std::lock_guard guard(cache_guard_); + cache_.insert({iden, item}); } } else if (!matched) { Logger::Message(Logger::LOG_ERROR, "No operation defined for:", inst); diff --git a/src/ifcgeom/mapping/mapping.h b/src/ifcgeom/mapping/mapping.h index 1111101bb6..ace92a2276 100644 --- a/src/ifcgeom/mapping/mapping.h +++ b/src/ifcgeom/mapping/mapping.h @@ -6,6 +6,8 @@ #include "../../ifcparse/IfcFile.h" #include "../../ifcparse/IfcLogger.h" +#include + #define INCLUDE_SCHEMA(x) STRINGIFY(../../ifcparse/x.h) #include INCLUDE_SCHEMA(IfcSchema) #undef INCLUDE_SCHEMA @@ -24,6 +26,7 @@ namespace geometry { std::string length_unit_name_; std::map cache_; + std::mutex cache_guard_; // provides mutually exclusive access to cache_ const IfcParse::declaration* placement_rel_to_type_; const IfcUtil::IfcBaseEntity* placement_rel_to_instance_; diff --git a/src/ifcgeom/piecewise_function_impl.cpp b/src/ifcgeom/piecewise_function_impl.cpp new file mode 100644 index 0000000000..ecd2762057 --- /dev/null +++ b/src/ifcgeom/piecewise_function_impl.cpp @@ -0,0 +1,106 @@ +#include "piecewise_function_impl.h" +#include "profile_helper.h" + +namespace ifcopenshell { + +namespace geometry { + +namespace taxonomy { + +std::vector ifcopenshell::geometry::taxonomy::piecewise_function_impl::evaluation_points() const { + if (!eval_points_.has_value()) { + double curve_length = length(); + + auto param_type = settings_ ? settings_->get().get() : ifcopenshell::geometry::settings::PiecewiseStepMethod::MAXSTEPSIZE; + auto param = settings_ ? settings_->get().get() : 0.5; + unsigned num_steps = 0; + if (param_type == ifcopenshell::geometry::settings::PiecewiseStepMethod::MAXSTEPSIZE) { + // parameter is max step size + num_steps = (unsigned)std::ceil(curve_length / param); + } else { + // parameter is minimum number of steps + num_steps = (unsigned)std::ceil(param); + } + + eval_points_ = evaluation_points(start_, start_ + curve_length, num_steps); + } + return *eval_points_; +} + +std::vector ifcopenshell::geometry::taxonomy::piecewise_function_impl::evaluation_points(double ustart, double uend, unsigned nsteps) const { + double curve_length = length(); + ustart = std::max(start_, ustart); + uend = std::min(uend, start_ + curve_length); + + nsteps = std::max(1u, nsteps); // never have fewer than 1 step + + auto resolution = (uend - ustart) / nsteps; + + std::vector u_values; + u_values.reserve(nsteps); + + for (unsigned i = 0; i <= nsteps; ++i) { + auto u = resolution * i + ustart; + u_values.push_back(u); + } + + return u_values; +} + +ifcopenshell::geometry::taxonomy::item::ptr ifcopenshell::geometry::taxonomy::piecewise_function_impl::evaluate() const { + return evaluate(evaluation_points()); +} + +item::ptr ifcopenshell::geometry::taxonomy::piecewise_function_impl::evaluate(double ustart, double uend, unsigned nsteps) const { + return evaluate(evaluation_points(ustart, uend, nsteps)); +} + +item::ptr ifcopenshell::geometry::taxonomy::piecewise_function_impl::evaluate(const std::vector& dist) const { + std::vector polygon; + polygon.reserve(dist.size()); + for (auto& u : dist) { + Eigen::Matrix4d m = evaluate(u); + polygon.push_back(taxonomy::make(m.col(3)(0), m.col(3)(1), m.col(3)(2))); + } + + return polygon_from_points(polygon); +} + +Eigen::Matrix4d ifcopenshell::geometry::taxonomy::piecewise_function_impl::evaluate(double u) const { + // assume monotonic evaluation and store last evaluated segment + if (current_span_fn_ == nullptr || (u < current_span_start_ || current_span_end_ < u)) { + // there isn't a current span or u is outside the range of the current span + // get a new "current span" + std::tie(current_span_start_, current_span_end_, current_span_fn_) = get_span(u); + } + + u -= current_span_start_; // make u relative to start of span + return (*current_span_fn_)(u); +} + +std::tuple*> ifcopenshell::geometry::taxonomy::piecewise_function_impl::get_span(double u) const { + // force u to be within bounds of the curve + double s = start(); + double e = end(); + u = std::max(s, u); + u = std::min(u, e); + + double span_start = s; + for (auto& [length, fn] : spans_) { + double span_end = span_start + length; + auto tolerance = settings_ ? settings_->get().get() : 0.001; + if (span_start <= u && u < span_end + tolerance) { + return {span_start, span_end, &fn}; + } + span_start += length; + } + + Logger::Error("piecewise_function_impl::get_span span not found."); + return {0, 0, nullptr}; +} + +} // namespace taxonomy + +} // namespace geometry + +} // namespace ifcopenshell diff --git a/src/ifcgeom/piecewise_function_impl.h b/src/ifcgeom/piecewise_function_impl.h new file mode 100644 index 0000000000..0c2f306ac5 --- /dev/null +++ b/src/ifcgeom/piecewise_function_impl.h @@ -0,0 +1,93 @@ +#ifndef PIECEWISE_FUNCTION_IMPL +#define PIECEWISE_FUNCTION_IMPL + +#include "taxonomy.h" + +namespace ifcopenshell { + +namespace geometry { + +namespace taxonomy { + +struct piecewise_function_impl { + using spans_t = std::vector>>; + + piecewise_function_impl(double start, const spans_t& s, ifcopenshell::geometry::Settings* settings = nullptr) : start_(start), + settings_(settings), + spans_(s){}; + piecewise_function_impl(double start, const std::vector& pwfs, ifcopenshell::geometry::Settings* settings = nullptr) : start_(start), + settings_(settings) { + for (auto& pwf : pwfs) { + spans_.insert(spans_.end(), pwf->spans().begin(), pwf->spans().end()); + } + }; + piecewise_function_impl(piecewise_function_impl&&) = default; + piecewise_function_impl(const piecewise_function_impl&) = default; + + const ifcopenshell::geometry::Settings* settings_ = nullptr; + + const spans_t& spans() const { return spans_; } + + bool is_empty() const { return spans_.empty(); } + + double start() const { + return start_; + } + + double end() const { + return start_ + length(); + } + + double length() const { + if (!length_.has_value()) { + length_ = std::accumulate(spans_.begin(), spans_.end(), 0.0, [](const auto& v, const auto& s) { return v + s.first; }); + } + return *length_; + } + + piecewise_function_impl* clone_() const { return new piecewise_function_impl(*this); } + + /// @brief returns a vector of "distance along" points where the evaluate function computes loop points + std::vector evaluation_points() const; + + /// @brief returns a vector of "distance along" points between ustart and uend + /// @param ustart starting location + /// @param uend ending location + /// @param nsteps number of steps to evaluate + std::vector evaluation_points(double ustart, double uend, unsigned nsteps) const; + + /// @brief evaluates the piecewise function between start and end + /// evaluation point step size is taken from the settings object + item::ptr evaluate() const; + + /// @brief evaluates the piecewise function between ustart and uend + /// if ustart and uend are out of range, the range of values evaluated + /// are constrained to start_ and start_+length_ + /// @param ustart starting location + /// @param uend ending location + /// @param nsteps number of steps to evaluate + /// @return taxonomy::loop::ptr + item::ptr evaluate(double ustart, double uend, unsigned nsteps) const; + + /// @brief evaluates the piecewise function at u + /// @param u u is constrained to be between start_ and start_+length + /// @return 4x4 placement matrix + Eigen::Matrix4d evaluate(double u) const; + + private: + item::ptr evaluate(const std::vector& dist) const; + std::tuple*> get_span(double u) const; + double start_ = 0.0; // starting value of the pwf + spans_t spans_; + + mutable double current_span_start_ = 0; + mutable double current_span_end_ = 0; + mutable const std::function* current_span_fn_ = nullptr; + mutable boost::optional length_; + mutable boost::optional> eval_points_; +}; + +} // namespace taxonomy +} // namespace geometry +} // namespace ifcopenshell +#endif PIECEWISE_FUNCTION_IMPL \ No newline at end of file diff --git a/src/ifcgeom/taxonomy.cpp b/src/ifcgeom/taxonomy.cpp index 9a80afe7d1..b9fdc78d27 100644 --- a/src/ifcgeom/taxonomy.cpp +++ b/src/ifcgeom/taxonomy.cpp @@ -1,6 +1,7 @@ #include "../ifcparse/IfcLogger.h" #include "taxonomy.h" #include "profile_helper.h" +#include "piecewise_function_impl.h" using namespace ifcopenshell::geometry::taxonomy; @@ -462,97 +463,34 @@ ifcopenshell::geometry::taxonomy::solid::ptr ifcopenshell::geometry::create_box( return solid; } -std::vector ifcopenshell::geometry::taxonomy::piecewise_function::evaluation_points() const { - if (!eval_points_.has_value()) { - double curve_length = length(); - - auto param_type = settings_ ? settings_->get().get() : ifcopenshell::geometry::settings::PiecewiseStepMethod::MAXSTEPSIZE; - auto param = settings_ ? settings_->get().get() : 0.5; - unsigned num_steps = 0; - if (param_type == ifcopenshell::geometry::settings::PiecewiseStepMethod::MAXSTEPSIZE) { - // parameter is max step size - num_steps = (unsigned)std::ceil(curve_length / param); - } else { - // parameter is minimum number of steps - num_steps = (unsigned)std::ceil(param); - } - - eval_points_ = evaluation_points(start_, start_ + curve_length, num_steps); - } - return *eval_points_; +/////////////////// +piecewise_function::piecewise_function(double start, const spans_t& s, ifcopenshell::geometry::Settings* settings, const IfcUtil::IfcBaseInterface* instance) : implicit_item(instance) { + impl_ = new piecewise_function_impl(start, s, settings); } -std::vector ifcopenshell::geometry::taxonomy::piecewise_function::evaluation_points(double ustart, double uend, unsigned nsteps) const { - double curve_length = length(); - ustart = std::max(start_, ustart); - uend = std::min(uend, start_ + curve_length); +piecewise_function::piecewise_function(double start, const std::vector& pwfs, ifcopenshell::geometry::Settings* settings, const IfcUtil::IfcBaseInterface* instance) : implicit_item(instance) { + impl_ = new piecewise_function_impl(start, pwfs, settings); +}; - nsteps = std::max(1u, nsteps); // never have fewer than 1 step - - auto resolution = (uend - ustart) / nsteps; - - std::vector u_values; - u_values.reserve(nsteps); - - for (unsigned i = 0; i <= nsteps; ++i) { - auto u = resolution * i + ustart; - u_values.push_back(u); - } - - return u_values; +piecewise_function::piecewise_function(const piecewise_function& other) : implicit_item(other) { + impl_ = other.impl_->clone_(); } -ifcopenshell::geometry::taxonomy::item::ptr ifcopenshell::geometry::taxonomy::piecewise_function::evaluate() const { - return evaluate(evaluation_points()); +piecewise_function::~piecewise_function() { + delete impl_; } -item::ptr ifcopenshell::geometry::taxonomy::piecewise_function::evaluate(double ustart, double uend,unsigned nsteps) const { - return evaluate(evaluation_points(ustart,uend,nsteps)); -} +const piecewise_function::spans_t& piecewise_function::spans() const { return impl_->spans(); } +bool piecewise_function::is_empty() const { return impl_->is_empty(); } +double piecewise_function::start() const { return impl_->start(); } +double piecewise_function::end() const { return impl_->end(); } +double piecewise_function::length() const { return impl_->length(); } -item::ptr ifcopenshell::geometry::taxonomy::piecewise_function::evaluate(const std::vector& dist) const { - std::vector polygon; - polygon.reserve(dist.size()); - for (auto& u : dist) { - Eigen::Matrix4d m = evaluate(u); - polygon.push_back(taxonomy::make(m.col(3)(0), m.col(3)(1), m.col(3)(2))); - } - - return polygon_from_points(polygon); -} - -Eigen::Matrix4d ifcopenshell::geometry::taxonomy::piecewise_function::evaluate(double u) const { - // assume monotonic evaluation and store last evaluated segment - if (current_span_fn_ == nullptr || (u < current_span_start_ || current_span_end_ < u)) { - // there isn't a current span or u is outside the range of the current span - // get a new "current span" - std::tie(current_span_start_,current_span_end_, current_span_fn_) = get_span(u); - } - - u -= current_span_start_; // make u relative to start of span - return (*current_span_fn_)(u); -} - -std::tuple*> ifcopenshell::geometry::taxonomy::piecewise_function::get_span(double u) const { - // force u to be within bounds of the curve - double s = start(); - double e = end(); - u = std::max(s, u); - u = std::min(u, e); - - double span_start = s; - for (auto& [length, fn] : spans_) { - double span_end = span_start + length; - auto tolerance = settings_ ? settings_->get().get() : 0.001; - if (span_start <= u && u < span_end + tolerance) { - return {span_start, span_end, &fn} ; - } - span_start += length; - } - - Logger::Error("taxonomy::piecewise_function::get_span span not found."); - return {0, 0, nullptr}; -} +std::vector piecewise_function::evaluation_points() const { return impl_->evaluation_points(); } +std::vector piecewise_function::evaluation_points(double ustart, double uend, unsigned nsteps) const { return impl_->evaluation_points(ustart, uend, nsteps); } +item::ptr piecewise_function::evaluate() const { return impl_->evaluate(); } +item::ptr piecewise_function::evaluate(double ustart, double uend, unsigned nsteps) const { return impl_->evaluate(ustart, uend, nsteps); } +Eigen::Matrix4d piecewise_function::evaluate(double u) const { return impl_->evaluate(u); } ifcopenshell::geometry::taxonomy::collection::ptr ifcopenshell::geometry::flatten(const taxonomy::collection::ptr& deep) { auto flat = make(); diff --git a/src/ifcgeom/taxonomy.h b/src/ifcgeom/taxonomy.h index 4fbbf636d2..3b77273240 100644 --- a/src/ifcgeom/taxonomy.h +++ b/src/ifcgeom/taxonomy.h @@ -351,46 +351,30 @@ typedef item const* ptr; virtual item::ptr evaluate() const = 0; }; + struct piecewise_function_impl; // forward declaration struct piecewise_function : public implicit_item { DECLARE_PTR(piecewise_function) - using spans = std::vector>>; + using spans_t = std::vector>>; - piecewise_function(double start,const spans& s, ifcopenshell::geometry::Settings* settings = nullptr, const IfcUtil::IfcBaseInterface* instance = nullptr) : - implicit_item(instance), start_(start), settings_(settings), spans_(s){}; - piecewise_function(double start,const std::vector& pwfs, ifcopenshell::geometry::Settings* settings = nullptr, const IfcUtil::IfcBaseInterface* instance = nullptr) : - implicit_item(instance), start_(start), settings_(settings) - { - for (auto& pwf : pwfs) { - spans_.insert(spans_.end(), pwf->spans_.begin(), pwf->spans_.end()); - } - }; + piecewise_function(double start, const spans_t& s, ifcopenshell::geometry::Settings* settings = nullptr, const IfcUtil::IfcBaseInterface* instance = nullptr); + piecewise_function(double start, const std::vector& pwfs, ifcopenshell::geometry::Settings* settings = nullptr, const IfcUtil::IfcBaseInterface* instance = nullptr); piecewise_function(piecewise_function&&) = default; - piecewise_function(const piecewise_function&) = default; + piecewise_function(const piecewise_function&); + virtual ~piecewise_function(); const ifcopenshell::geometry::Settings* settings_ = nullptr; - bool is_empty() const { return spans_.empty(); } - - double start() const { - return start_; - } - - double end() const { - return start_ + length(); - } - - double length() const { - if (!length_.has_value()) { - length_ = std::accumulate(spans_.begin(), spans_.end(), 0.0, [](const auto& v, const auto& s) { return v + s.first; }); - } - return *length_; - } + const spans_t& spans() const; + bool is_empty() const; + double start() const; + double end() const; + double length() const; virtual piecewise_function* clone_() const { return new piecewise_function(*this); } virtual kinds kind() const { return PIECEWISE_FUNCTION; } - virtual size_t calc_hash() const { + virtual size_t calc_hash() const { auto v = std::make_tuple(static_cast(PIECEWISE_FUNCTION), 0); return boost::hash{}(v); } @@ -423,15 +407,10 @@ typedef item const* ptr; Eigen::Matrix4d evaluate(double u) const; private: - item::ptr evaluate(const std::vector& dist) const; - std::tuple*> get_span(double u) const; - double start_ = 0.0; // starting value of the pwf - spans spans_; - mutable double current_span_start_ = 0; - mutable double current_span_end_ = 0; - mutable const std::function* current_span_fn_ = nullptr; - mutable boost::optional length_; - mutable boost::optional> eval_points_; + // note: it would be better if this were a std::unique_ptr, but that requires having the full definition + // of piecewise_function_impl in this header file, which defeats the purpose of the PIMPL idiom. + // if this is a std::unique_ptr, then the _ifcopenshell_wrapper library doesn't compile + piecewise_function_impl* impl_ = nullptr; }; #ifdef TAXONOMY_USE_SHARED_PTR @@ -1329,14 +1308,14 @@ typedef item const* ptr; boost::optional pwf_; public: - loop_to_piecewise_function_upgrade(taxonomy::ptr item) { + loop_to_piecewise_function_upgrade(taxonomy::ptr item) { if constexpr (std::is_same_v) { auto loop = taxonomy::dcast(item); if (loop) { if (loop->pwf.is_initialized()) { pwf_ = loop->pwf; } else { - taxonomy::piecewise_function::spans spans; + taxonomy::piecewise_function::spans_t spans; spans.reserve(loop->children.size()); for (auto& edge : loop->children) { // the edge could be an arc or trimmed circle in the case of IfcIndexPolyCurve - support for this isn't implemented yet diff --git a/src/ifcopenshell-python/docs/ifcopenshell/installation.rst b/src/ifcopenshell-python/docs/ifcopenshell/installation.rst index 99816bda1a..98fc19c955 100644 --- a/src/ifcopenshell-python/docs/ifcopenshell/installation.rst +++ b/src/ifcopenshell-python/docs/ifcopenshell/installation.rst @@ -62,6 +62,8 @@ operating systems. GCC (4.7 or newer) or Clang (any version) is required. sudo apt-get install git cmake gcc g++ libboost-all-dev libcgal-dev + The CGAL version that ships with Ubuntu 20.04 is too old. Users on Ubuntu 20.04 are advised to manually install CGAL 5.3. + 3. Install OpenCascade Technology (OCCT). .. code-block:: bash @@ -73,6 +75,8 @@ operating systems. GCC (4.7 or newer) or Clang (any version) is required. If OCCT is not available, an alternative is to `manually compile OCCT `__. + IfcOpenShell 0.8 depends on fairly recent OCCT additions such as the BVH Tree functionality. Users on Ubuntu 20.04 are advised to manually compile and install OCCT 7.7. + Another alternative is to use OpenCascade Community Edition (OCE), but it may lag behind OCCT and is no longer actively maintained so is not recommended. @@ -137,25 +141,27 @@ operating systems. GCC (4.7 or newer) or Clang (any version) is required. # Check all paths are valid for your environment cmake ../cmake \ -DOCC_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu/ \ - -DOCC_INCLUDE_DIR=/usr/include/ \ - + -DOCC_INCLUDE_DIR=/usr/include/opencascade \ + \ # Optional Collada support -DCOLLADA_SUPPORT=On \ -DOPENCOLLADA_INCLUDE_DIR="/usr/local/include/opencollada" \ -DOPENCOLLADA_LIBRARY_DIR="/usr/local/lib/opencollada" \ -DPCRE_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu/ \ - + \ # Optional HDF5 support -DHDF5_SUPPORT=On \ -DHDF5_LIBRARIES="/usr/local/hdf5/lib/libhdf5_cpp.so;/usr/local/hdf5/lib/libhdf5.so;/usr/lib64/libz.so;/usr/lib64/libsz.so;/usr/lib64/libaec.so" \ -DHDF5_INCLUDE_DIR="/usr/local/hdf5/include" \ - + \ -DCGAL_INCLUDE_DIR=/usr/include \ -DGMP_INCLUDE_DIR=/usr/include \ -DMPFR_INCLUDE_DIR=/usr/include \ -DGMP_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \ - -DMPFR_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu - # Replace X with number of CPU cores + 1 + -DMPFR_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \ + -DJSON_INCLUDE_DIR=/usr/include \ + -DEIGEN_DIR=/usr/include/eigen3 + # Replace X with number of CPU cores + 1. Reduce when running out of memory. Compiling the code generated from the schemas is resource intensive. make -j X # Optionally install to the system sudo make install diff --git a/src/ifcpatch/ifcpatch/recipes/ResetAbsoluteCoordinates.py b/src/ifcpatch/ifcpatch/recipes/ResetAbsoluteCoordinates.py index 283c8f71e0..7f58e39694 100644 --- a/src/ifcpatch/ifcpatch/recipes/ResetAbsoluteCoordinates.py +++ b/src/ifcpatch/ifcpatch/recipes/ResetAbsoluteCoordinates.py @@ -30,14 +30,15 @@ class Patcher: file: ifcopenshell.file, logger: logging.Logger, mode: Literal[ - "geometry", - "placement", - "both", - ] = "geometry", - a: Optional[float] = None, - b: Optional[float] = None, - c: Optional[float] = None, - d: Optional[float] = None, + "Geometry", + "Placement", + "Both", + ] = "Geometry", + automatic_offset_point: bool = True, + threshold: float = 1000000, # Arbitrary default threshold based on experience + x: Union[str, float] = "0", + y: Union[str, float] = "0", + z: Union[str, float] = "0", ): """Reset any large coordinates to smaller coordinates based on a threshold @@ -53,47 +54,34 @@ class Patcher: ordinate is larger than a threshold) and offsets it back down to a small number. - You may either manually specify the offset to apply to any large - coordinate, or an offset will be automatically determined arbitrarily - based on the first large number we encounter. + You may either let Blender BIM determine the offset to apply to any large + coordinate automatically, arbitrarily based on the first large number we + encounter, or manually specify this offset. Note that if your model inconsistently mixes coordinates between large and small (such as if your model mixes both local and map coordinates) then the results of this function may be poor. Provide a bug report back to your BIM application to get it fixed. - You may specify up to 4 arguments, a, b, c, and d. - - If you specify no arguments, then the threshold is set to 1000000. The - offset is auto detected. - - If you only specify 1 parameter (i.e. a), then this is treated as the - threshold beyond which an ordinate is considered to be large. The offset - is auto detected. - - If you specify 3 parameters, (i.e. a, b, c) then your three numbers are - treated as the X, Y, Z offset to apply. Typically your numbers will be - negative to bring the numbers smaller. The threshold is set to 1000000. - - If you specify 4 parameters (i.e. a, b, c, d), then the first three - numbers are treated as the X, Y, Z offset to apply (a, b, c). The fourth - (d) will be treated as the threshold. - - :param mode: Choose from "geometry", "placement", or "both". Choosing - "geometry" will only replace cartesian points used in shape - representations. Choosing "placement" will only replace cartesian - points used in object placements. Choosing "both" will replace all + :param mode: Choose from "Geometry", "Placement", or "Both". Choosing + "Geometry" will only replace cartesian points used in shape + representations. Choosing "Placement" will only replace cartesian + points used in object placements. Choosing "Both" will replace all cartesian points regardless of use (useful if the model has both large placement offsets and large geometry offsets). :type mode: str - :param a: The first parameter - :type a: float,optional - :param b: The second parameter - :type b: float,optional - :param c: The third parameter - :type c: float,optional - :param d: The fourth parameter - :type d: float,optional + :param automatic_offset_point: Choose, whether the offset should be + determined automatically or specified manually. + :type automatic_offset_point: bool + :param threshold: The threshold for deciding, whether a coordinate is + treated as a large coordinate. + :type threshold: float + :param x: The x-ordinate of the manually specified offset point. + :type x: Union[str, float] + :param y: The y-ordinate of the manually specified offset point. + :type y: Union[str, float] + :param z: The z-ordinate of the manually specified offset point. + :type z: Union[str, float] Example: @@ -103,39 +91,33 @@ class Patcher: ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "ResetAbsoluteCoordinates", "arguments": []}) # Reset all coordinates with an ordinate larger than 1000 arbitrarily - ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "ResetAbsoluteCoordinates", "arguments": [1000]}) + ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "ResetAbsoluteCoordinates", "arguments": [True, 1000]}) # Reset all coordinates with an ordinate larger than 1000000 by -50000,-20000,0 - ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "ResetAbsoluteCoordinates", "arguments": [-50000,-20000,0]}) + ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "ResetAbsoluteCoordinates", "arguments": [False, 1000000, -50000,-20000,0]}) # Reset all coordinates with an ordinate larger than 1000 by -500,-200,0 - ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "ResetAbsoluteCoordinates", "arguments": [-500,-200,0,1000]}) + ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "ResetAbsoluteCoordinates", "arguments": [False, 1000, -500,-200,0]}) """ self.src = src self.file = file self.logger = logger - self.mode = mode - self.args = [x for x in [a, b, c, d] if x is not None] + self.mode = mode.lower() + self.threshold = threshold + if automatic_offset_point: + self.offset_point = None + else: + self.offset_point = (float(x), float(y), float(z)) def patch(self): placement_coord_ids = set() for placement in self.file.by_type("IfcObjectPlacement"): [placement_coord_ids.add(e.id()) for e in self.file.traverse(placement) if e.is_a("IfcCartesianPoint")] - # Arbitrary threshold based on experience - self.threshold = 1000000 - if self.args and len(self.args) == 1: - self.threshold = float(self.args[0]) - elif self.args and len(self.args) == 4: - self.threshold = float(self.args[3]) - # This method will not work all the time, but will catch most issues. It # assumes that absolute coordinates are easily recognisable based on # having a large absolute value above a threshold. This is not always # the case, but is very fast to run, and works for most cases. - offset_point = None - if self.args and len(self.args) >= 3: - offset_point = (float(self.args[0]), float(self.args[1]), float(self.args[2])) try: point_lists = self.file.by_type("IfcCartesianPointList3D") except: @@ -147,10 +129,14 @@ class Patcher: if len(point) == 2 or not self.is_point_far_away(point): coord_list[i] = point continue - if not offset_point: - offset_point = (-point[0], -point[1], -point[2]) + if not self.offset_point: + self.offset_point = (-point[0], -point[1], -point[2]) self.logger.info(f"Resetting absolute coordinates by {point}") - point = (point[0] + offset_point[0], point[1] + offset_point[1], point[2] + offset_point[2]) + point = ( + point[0] + self.offset_point[0], + point[1] + self.offset_point[1], + point[2] + self.offset_point[2], + ) coord_list[i] = point point_list.CoordList = coord_list for point in self.file.by_type("IfcCartesianPoint"): @@ -162,13 +148,13 @@ class Patcher: elif self.mode == "placement": if point.id() not in placement_coord_ids: continue - if not offset_point: - offset_point = (-point.Coordinates[0], -point.Coordinates[1], -point.Coordinates[2]) + if not self.offset_point: + self.offset_point = (-point.Coordinates[0], -point.Coordinates[1], -point.Coordinates[2]) self.logger.info(f"Resetting absolute coordinates by {point}") point.Coordinates = ( - point.Coordinates[0] + offset_point[0], - point.Coordinates[1] + offset_point[1], - point.Coordinates[2] + offset_point[2], + point.Coordinates[0] + self.offset_point[0], + point.Coordinates[1] + self.offset_point[1], + point.Coordinates[2] + self.offset_point[2], ) def is_point_far_away(self, point: Union[ifcopenshell.entity_instance, npt.NDArray[np.float64]]) -> bool: diff --git a/src/serializers/SvgSerializer.cpp b/src/serializers/SvgSerializer.cpp index 86e5681ae3..61a6fb8591 100644 --- a/src/serializers/SvgSerializer.cpp +++ b/src/serializers/SvgSerializer.cpp @@ -461,7 +461,7 @@ namespace { TopExp_Explorer it(shell, TopAbs_FACE); for (; it.More(); it.Next()) { - const auto& face = TopoDS::Face(it.Value()); + const auto& face = TopoDS::Face(it.Current()); auto surf = BRep_Tool::Surface(face); if (surf->DynamicType() != STANDARD_TYPE(Geom_Plane)) { return boost::none;