mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-12 10:33:20 +00:00
Merge branch 'v0.8.0' of https://github.com/IfcOpenShell/IfcOpenShell into v0.8.0
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<taxonomy::piecewise_function>(0.0, spans,&settings_,inst);
|
||||
return pwf;
|
||||
|
||||
@@ -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<taxonomy::piecewise_function>(start, spans, &settings_, inst);
|
||||
return pwf;
|
||||
|
||||
@@ -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<taxonomy::piecewise_function>(start,spans,&settings_,inst);
|
||||
return pwf;
|
||||
|
||||
@@ -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<taxonomy::piecewise_function>(start, spans, &settings_, inst);
|
||||
return pwf;
|
||||
|
||||
@@ -612,6 +612,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcStyledItem* inst) {
|
||||
taxonomy::ptr mapping::map(const IfcBaseInterface* inst) {
|
||||
auto iden = inst->as<IfcUtil::IfcBaseClass>()->identity();
|
||||
if (use_caching_) {
|
||||
std::lock_guard<std::mutex> 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<std::mutex> guard(cache_guard_);
|
||||
cache_.insert({iden, item});
|
||||
}
|
||||
} else if (!matched) {
|
||||
Logger::Message(Logger::LOG_ERROR, "No operation defined for:", inst);
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include "../../ifcparse/IfcFile.h"
|
||||
#include "../../ifcparse/IfcLogger.h"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#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<uint32_t, ifcopenshell::geometry::taxonomy::ptr> 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_;
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
#include "piecewise_function_impl.h"
|
||||
#include "profile_helper.h"
|
||||
|
||||
namespace ifcopenshell {
|
||||
|
||||
namespace geometry {
|
||||
|
||||
namespace taxonomy {
|
||||
|
||||
std::vector<double> ifcopenshell::geometry::taxonomy::piecewise_function_impl::evaluation_points() const {
|
||||
if (!eval_points_.has_value()) {
|
||||
double curve_length = length();
|
||||
|
||||
auto param_type = settings_ ? settings_->get<ifcopenshell::geometry::settings::PiecewiseStepType>().get() : ifcopenshell::geometry::settings::PiecewiseStepMethod::MAXSTEPSIZE;
|
||||
auto param = settings_ ? settings_->get<ifcopenshell::geometry::settings::PiecewiseStepParam>().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<double> 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<double> 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<double>& dist) const {
|
||||
std::vector<taxonomy::point3::ptr> polygon;
|
||||
polygon.reserve(dist.size());
|
||||
for (auto& u : dist) {
|
||||
Eigen::Matrix4d m = evaluate(u);
|
||||
polygon.push_back(taxonomy::make<taxonomy::point3>(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<double, double, const std::function<Eigen::Matrix4d(double u)>*> 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<ifcopenshell::geometry::settings::Precision>().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
|
||||
@@ -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<std::pair<double, std::function<Eigen::Matrix4d(double u)>>>;
|
||||
|
||||
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<piecewise_function::ptr>& 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<double> 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<double> 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<double>& dist) const;
|
||||
std::tuple<double, double, const std::function<Eigen::Matrix4d(double u)>*> 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<Eigen::Matrix4d(double u)>* current_span_fn_ = nullptr;
|
||||
mutable boost::optional<double> length_;
|
||||
mutable boost::optional<std::vector<double>> eval_points_;
|
||||
};
|
||||
|
||||
} // namespace taxonomy
|
||||
} // namespace geometry
|
||||
} // namespace ifcopenshell
|
||||
#endif PIECEWISE_FUNCTION_IMPL
|
||||
+21
-83
@@ -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<double> ifcopenshell::geometry::taxonomy::piecewise_function::evaluation_points() const {
|
||||
if (!eval_points_.has_value()) {
|
||||
double curve_length = length();
|
||||
|
||||
auto param_type = settings_ ? settings_->get<ifcopenshell::geometry::settings::PiecewiseStepType>().get() : ifcopenshell::geometry::settings::PiecewiseStepMethod::MAXSTEPSIZE;
|
||||
auto param = settings_ ? settings_->get<ifcopenshell::geometry::settings::PiecewiseStepParam>().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<double> 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<piecewise_function::ptr>& 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<double> 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<double>& dist) const {
|
||||
std::vector<taxonomy::point3::ptr> polygon;
|
||||
polygon.reserve(dist.size());
|
||||
for (auto& u : dist) {
|
||||
Eigen::Matrix4d m = evaluate(u);
|
||||
polygon.push_back(taxonomy::make<taxonomy::point3>(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<double, double, const std::function<Eigen::Matrix4d(double u)>*> 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<ifcopenshell::geometry::settings::Precision>().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<double> piecewise_function::evaluation_points() const { return impl_->evaluation_points(); }
|
||||
std::vector<double> 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<taxonomy::collection>();
|
||||
|
||||
+18
-39
@@ -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<std::pair<double, std::function<Eigen::Matrix4d(double u)>>>;
|
||||
using spans_t = std::vector<std::pair<double, std::function<Eigen::Matrix4d(double u)>>>;
|
||||
|
||||
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<piecewise_function::ptr>& 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<piecewise_function::ptr>& 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<size_t>(PIECEWISE_FUNCTION), 0);
|
||||
return boost::hash<decltype(v)>{}(v);
|
||||
}
|
||||
@@ -423,15 +407,10 @@ typedef item const* ptr;
|
||||
Eigen::Matrix4d evaluate(double u) const;
|
||||
|
||||
private:
|
||||
item::ptr evaluate(const std::vector<double>& dist) const;
|
||||
std::tuple<double, double, const std::function<Eigen::Matrix4d(double u)>*> 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<Eigen::Matrix4d(double u)>* current_span_fn_ = nullptr;
|
||||
mutable boost::optional<double> length_;
|
||||
mutable boost::optional<std::vector<double>> 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<taxonomy::piecewise_function::ptr> pwf_;
|
||||
|
||||
public:
|
||||
loop_to_piecewise_function_upgrade(taxonomy::ptr item) {
|
||||
loop_to_piecewise_function_upgrade(taxonomy::ptr item) {
|
||||
if constexpr (std::is_same_v<T, piecewise_function>) {
|
||||
auto loop = taxonomy::dcast<taxonomy::loop>(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
|
||||
|
||||
@@ -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
|
||||
<https://dev.opencascade.org/release>`__.
|
||||
|
||||
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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user