mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-20 23:36: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)
|
if(GLTF_SUPPORT OR CITYJSON_SUPPORT)
|
||||||
UNIFY_ENVVARS_AND_CACHE(JSON_INCLUDE_DIR)
|
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)
|
if(json_header_path)
|
||||||
message(STATUS "JSON for Modern C++ header file found")
|
message(STATUS "JSON for Modern C++ header file found in ${JSON_INCLUDE_DIR}")
|
||||||
else()
|
else()
|
||||||
message(FATAL_ERROR "Unable to find JSON for Modern C++ header file, aborting")
|
message(FATAL_ERROR "Unable to find JSON for Modern C++ header file, aborting")
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -551,7 +551,7 @@ class IfcImporter:
|
|||||||
for axis in axes:
|
for axis in axes:
|
||||||
shape = tool.Loader.create_generic_shape(axis.AxisCurve)
|
shape = tool.Loader.create_generic_shape(axis.AxisCurve)
|
||||||
mesh = self.create_mesh(axis, shape)
|
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:
|
if tool.Blender.get_addon_preferences().lock_grids_on_import:
|
||||||
obj.lock_location = (True, True, True)
|
obj.lock_location = (True, True, True)
|
||||||
obj.lock_rotation = (True, True, True)
|
obj.lock_rotation = (True, True, True)
|
||||||
@@ -781,7 +781,7 @@ class IfcImporter:
|
|||||||
mesh = bpy.data.meshes.new(mesh_name)
|
mesh = bpy.data.meshes.new(mesh_name)
|
||||||
mesh.from_pydata([mathutils.Vector(vertex) * self.unit_scale], [], [])
|
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.set_matrix_world(obj, tool.Loader.apply_blender_offset_to_matrix_world(obj, placement_matrix))
|
||||||
self.link_element(product, obj)
|
self.link_element(product, obj)
|
||||||
|
|
||||||
@@ -847,7 +847,7 @@ class IfcImporter:
|
|||||||
mesh.from_pydata(vertex_list, [], [])
|
mesh.from_pydata(vertex_list, [], [])
|
||||||
tool.Ifc.link(representation, mesh)
|
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.set_matrix_world(obj, tool.Loader.apply_blender_offset_to_matrix_world(obj, placement_matrix))
|
||||||
self.link_element(product, obj)
|
self.link_element(product, obj)
|
||||||
return product
|
return product
|
||||||
|
|||||||
@@ -91,6 +91,8 @@ class Loader(blenderbim.core.tool.Loader):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_name(cls, element: ifcopenshell.entity_instance) -> str:
|
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"))
|
return "{}/{}".format(element.is_a(), getattr(element, "Name", "None"))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@@ -888,7 +888,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcCurveSegment* inst) {
|
|||||||
|
|
||||||
auto length = cse.length();
|
auto length = cse.length();
|
||||||
|
|
||||||
taxonomy::piecewise_function::spans spans;
|
taxonomy::piecewise_function::spans_t spans;
|
||||||
spans.emplace_back(fabs(length), fn);
|
spans.emplace_back(fabs(length), fn);
|
||||||
auto pwf = taxonomy::make<taxonomy::piecewise_function>(0.0, spans,&settings_,inst);
|
auto pwf = taxonomy::make<taxonomy::piecewise_function>(0.0, spans,&settings_,inst);
|
||||||
return pwf;
|
return pwf;
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcGradientCurve* inst) {
|
|||||||
return m;
|
return m;
|
||||||
};
|
};
|
||||||
|
|
||||||
taxonomy::piecewise_function::spans spans;
|
taxonomy::piecewise_function::spans_t spans;
|
||||||
spans.emplace_back(length, composition);
|
spans.emplace_back(length, composition);
|
||||||
auto pwf = taxonomy::make<taxonomy::piecewise_function>(start, spans, &settings_, inst);
|
auto pwf = taxonomy::make<taxonomy::piecewise_function>(start, spans, &settings_, inst);
|
||||||
return pwf;
|
return pwf;
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcOffsetCurveByDistances* inst
|
|||||||
double start = pw_curve->start();
|
double start = pw_curve->start();
|
||||||
double basis_curve_length = pw_curve->length();
|
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
|
#if defined SCHEMA_HAS_IfcDistanceExpression
|
||||||
double first_distance = first_offset_value->DistanceAlong();
|
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
|
// current implementation assumes that composition is equal to the full length of basis curve
|
||||||
// this may change depending on decisions in the bSI-IF
|
// 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);
|
spans.emplace_back(basis_curve_length, composition);
|
||||||
auto pwf = taxonomy::make<taxonomy::piecewise_function>(start,spans,&settings_,inst);
|
auto pwf = taxonomy::make<taxonomy::piecewise_function>(start,spans,&settings_,inst);
|
||||||
return pwf;
|
return pwf;
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSegmentedReferenceCurve* ins
|
|||||||
return m;
|
return m;
|
||||||
};
|
};
|
||||||
|
|
||||||
taxonomy::piecewise_function::spans spans;
|
taxonomy::piecewise_function::spans_t spans;
|
||||||
spans.emplace_back(length, composition);
|
spans.emplace_back(length, composition);
|
||||||
auto pwf = taxonomy::make<taxonomy::piecewise_function>(start, spans, &settings_, inst);
|
auto pwf = taxonomy::make<taxonomy::piecewise_function>(start, spans, &settings_, inst);
|
||||||
return pwf;
|
return pwf;
|
||||||
|
|||||||
@@ -612,6 +612,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcStyledItem* inst) {
|
|||||||
taxonomy::ptr mapping::map(const IfcBaseInterface* inst) {
|
taxonomy::ptr mapping::map(const IfcBaseInterface* inst) {
|
||||||
auto iden = inst->as<IfcUtil::IfcBaseClass>()->identity();
|
auto iden = inst->as<IfcUtil::IfcBaseClass>()->identity();
|
||||||
if (use_caching_) {
|
if (use_caching_) {
|
||||||
|
std::lock_guard<std::mutex> guard(cache_guard_);
|
||||||
auto it = cache_.find(iden);
|
auto it = cache_.find(iden);
|
||||||
if (it != cache_.end()) {
|
if (it != cache_.end()) {
|
||||||
return it->second;
|
return it->second;
|
||||||
@@ -629,7 +630,8 @@ taxonomy::ptr mapping::map(const IfcBaseInterface* inst) {
|
|||||||
|
|
||||||
if (item) {
|
if (item) {
|
||||||
if (use_caching_) {
|
if (use_caching_) {
|
||||||
cache_.insert({ iden, item });
|
std::lock_guard<std::mutex> guard(cache_guard_);
|
||||||
|
cache_.insert({iden, item});
|
||||||
}
|
}
|
||||||
} else if (!matched) {
|
} else if (!matched) {
|
||||||
Logger::Message(Logger::LOG_ERROR, "No operation defined for:", inst);
|
Logger::Message(Logger::LOG_ERROR, "No operation defined for:", inst);
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
#include "../../ifcparse/IfcFile.h"
|
#include "../../ifcparse/IfcFile.h"
|
||||||
#include "../../ifcparse/IfcLogger.h"
|
#include "../../ifcparse/IfcLogger.h"
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
#define INCLUDE_SCHEMA(x) STRINGIFY(../../ifcparse/x.h)
|
#define INCLUDE_SCHEMA(x) STRINGIFY(../../ifcparse/x.h)
|
||||||
#include INCLUDE_SCHEMA(IfcSchema)
|
#include INCLUDE_SCHEMA(IfcSchema)
|
||||||
#undef INCLUDE_SCHEMA
|
#undef INCLUDE_SCHEMA
|
||||||
@@ -24,6 +26,7 @@ namespace geometry {
|
|||||||
std::string length_unit_name_;
|
std::string length_unit_name_;
|
||||||
|
|
||||||
std::map<uint32_t, ifcopenshell::geometry::taxonomy::ptr> cache_;
|
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 IfcParse::declaration* placement_rel_to_type_;
|
||||||
const IfcUtil::IfcBaseEntity* placement_rel_to_instance_;
|
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 "../ifcparse/IfcLogger.h"
|
||||||
#include "taxonomy.h"
|
#include "taxonomy.h"
|
||||||
#include "profile_helper.h"
|
#include "profile_helper.h"
|
||||||
|
#include "piecewise_function_impl.h"
|
||||||
|
|
||||||
using namespace ifcopenshell::geometry::taxonomy;
|
using namespace ifcopenshell::geometry::taxonomy;
|
||||||
|
|
||||||
@@ -462,97 +463,34 @@ ifcopenshell::geometry::taxonomy::solid::ptr ifcopenshell::geometry::create_box(
|
|||||||
return solid;
|
return solid;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<double> ifcopenshell::geometry::taxonomy::piecewise_function::evaluation_points() const {
|
///////////////////
|
||||||
if (!eval_points_.has_value()) {
|
piecewise_function::piecewise_function(double start, const spans_t& s, ifcopenshell::geometry::Settings* settings, const IfcUtil::IfcBaseInterface* instance) : implicit_item(instance) {
|
||||||
double curve_length = length();
|
impl_ = new piecewise_function_impl(start, s, settings);
|
||||||
|
|
||||||
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::evaluation_points(double ustart, double uend, unsigned nsteps) const {
|
piecewise_function::piecewise_function(double start, const std::vector<piecewise_function::ptr>& pwfs, ifcopenshell::geometry::Settings* settings, const IfcUtil::IfcBaseInterface* instance) : implicit_item(instance) {
|
||||||
double curve_length = length();
|
impl_ = new piecewise_function_impl(start, pwfs, settings);
|
||||||
ustart = std::max(start_, ustart);
|
};
|
||||||
uend = std::min(uend, start_ + curve_length);
|
|
||||||
|
|
||||||
nsteps = std::max(1u, nsteps); // never have fewer than 1 step
|
piecewise_function::piecewise_function(const piecewise_function& other) : implicit_item(other) {
|
||||||
|
impl_ = other.impl_->clone_();
|
||||||
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::evaluate() const {
|
piecewise_function::~piecewise_function() {
|
||||||
return evaluate(evaluation_points());
|
delete impl_;
|
||||||
}
|
}
|
||||||
|
|
||||||
item::ptr ifcopenshell::geometry::taxonomy::piecewise_function::evaluate(double ustart, double uend,unsigned nsteps) const {
|
const piecewise_function::spans_t& piecewise_function::spans() const { return impl_->spans(); }
|
||||||
return evaluate(evaluation_points(ustart,uend,nsteps));
|
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<double> piecewise_function::evaluation_points() const { return impl_->evaluation_points(); }
|
||||||
std::vector<taxonomy::point3::ptr> polygon;
|
std::vector<double> piecewise_function::evaluation_points(double ustart, double uend, unsigned nsteps) const { return impl_->evaluation_points(ustart, uend, nsteps); }
|
||||||
polygon.reserve(dist.size());
|
item::ptr piecewise_function::evaluate() const { return impl_->evaluate(); }
|
||||||
for (auto& u : dist) {
|
item::ptr piecewise_function::evaluate(double ustart, double uend, unsigned nsteps) const { return impl_->evaluate(ustart, uend, nsteps); }
|
||||||
Eigen::Matrix4d m = evaluate(u);
|
Eigen::Matrix4d piecewise_function::evaluate(double u) const { return impl_->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};
|
|
||||||
}
|
|
||||||
|
|
||||||
ifcopenshell::geometry::taxonomy::collection::ptr ifcopenshell::geometry::flatten(const taxonomy::collection::ptr& deep) {
|
ifcopenshell::geometry::taxonomy::collection::ptr ifcopenshell::geometry::flatten(const taxonomy::collection::ptr& deep) {
|
||||||
auto flat = make<taxonomy::collection>();
|
auto flat = make<taxonomy::collection>();
|
||||||
|
|||||||
+18
-39
@@ -351,46 +351,30 @@ typedef item const* ptr;
|
|||||||
virtual item::ptr evaluate() const = 0;
|
virtual item::ptr evaluate() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct piecewise_function_impl; // forward declaration
|
||||||
struct piecewise_function : public implicit_item {
|
struct piecewise_function : public implicit_item {
|
||||||
DECLARE_PTR(piecewise_function)
|
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) :
|
piecewise_function(double start, const spans_t& 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);
|
||||||
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(piecewise_function&&) = default;
|
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;
|
const ifcopenshell::geometry::Settings* settings_ = nullptr;
|
||||||
|
|
||||||
bool is_empty() const { return spans_.empty(); }
|
const spans_t& spans() const;
|
||||||
|
bool is_empty() const;
|
||||||
double start() const {
|
double start() const;
|
||||||
return start_;
|
double end() const;
|
||||||
}
|
double length() const;
|
||||||
|
|
||||||
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_;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual piecewise_function* clone_() const { return new piecewise_function(*this); }
|
virtual piecewise_function* clone_() const { return new piecewise_function(*this); }
|
||||||
virtual kinds kind() const { return PIECEWISE_FUNCTION; }
|
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);
|
auto v = std::make_tuple(static_cast<size_t>(PIECEWISE_FUNCTION), 0);
|
||||||
return boost::hash<decltype(v)>{}(v);
|
return boost::hash<decltype(v)>{}(v);
|
||||||
}
|
}
|
||||||
@@ -423,15 +407,10 @@ typedef item const* ptr;
|
|||||||
Eigen::Matrix4d evaluate(double u) const;
|
Eigen::Matrix4d evaluate(double u) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
item::ptr evaluate(const std::vector<double>& dist) const;
|
// note: it would be better if this were a std::unique_ptr, but that requires having the full definition
|
||||||
std::tuple<double, double, const std::function<Eigen::Matrix4d(double u)>*> get_span(double u) const;
|
// of piecewise_function_impl in this header file, which defeats the purpose of the PIMPL idiom.
|
||||||
double start_ = 0.0; // starting value of the pwf
|
// if this is a std::unique_ptr, then the _ifcopenshell_wrapper library doesn't compile
|
||||||
spans spans_;
|
piecewise_function_impl* impl_ = nullptr;
|
||||||
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_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef TAXONOMY_USE_SHARED_PTR
|
#ifdef TAXONOMY_USE_SHARED_PTR
|
||||||
@@ -1329,14 +1308,14 @@ typedef item const* ptr;
|
|||||||
boost::optional<taxonomy::piecewise_function::ptr> pwf_;
|
boost::optional<taxonomy::piecewise_function::ptr> pwf_;
|
||||||
|
|
||||||
public:
|
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>) {
|
if constexpr (std::is_same_v<T, piecewise_function>) {
|
||||||
auto loop = taxonomy::dcast<taxonomy::loop>(item);
|
auto loop = taxonomy::dcast<taxonomy::loop>(item);
|
||||||
if (loop) {
|
if (loop) {
|
||||||
if (loop->pwf.is_initialized()) {
|
if (loop->pwf.is_initialized()) {
|
||||||
pwf_ = loop->pwf;
|
pwf_ = loop->pwf;
|
||||||
} else {
|
} else {
|
||||||
taxonomy::piecewise_function::spans spans;
|
taxonomy::piecewise_function::spans_t spans;
|
||||||
spans.reserve(loop->children.size());
|
spans.reserve(loop->children.size());
|
||||||
for (auto& edge : loop->children) {
|
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
|
// 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
|
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).
|
3. Install OpenCascade Technology (OCCT).
|
||||||
|
|
||||||
.. code-block:: bash
|
.. 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
|
If OCCT is not available, an alternative is to `manually compile OCCT
|
||||||
<https://dev.opencascade.org/release>`__.
|
<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
|
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.
|
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
|
# Check all paths are valid for your environment
|
||||||
cmake ../cmake \
|
cmake ../cmake \
|
||||||
-DOCC_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu/ \
|
-DOCC_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu/ \
|
||||||
-DOCC_INCLUDE_DIR=/usr/include/ \
|
-DOCC_INCLUDE_DIR=/usr/include/opencascade \
|
||||||
|
\
|
||||||
# Optional Collada support
|
# Optional Collada support
|
||||||
-DCOLLADA_SUPPORT=On \
|
-DCOLLADA_SUPPORT=On \
|
||||||
-DOPENCOLLADA_INCLUDE_DIR="/usr/local/include/opencollada" \
|
-DOPENCOLLADA_INCLUDE_DIR="/usr/local/include/opencollada" \
|
||||||
-DOPENCOLLADA_LIBRARY_DIR="/usr/local/lib/opencollada" \
|
-DOPENCOLLADA_LIBRARY_DIR="/usr/local/lib/opencollada" \
|
||||||
-DPCRE_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu/ \
|
-DPCRE_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu/ \
|
||||||
|
\
|
||||||
# Optional HDF5 support
|
# Optional HDF5 support
|
||||||
-DHDF5_SUPPORT=On \
|
-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_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" \
|
-DHDF5_INCLUDE_DIR="/usr/local/hdf5/include" \
|
||||||
|
\
|
||||||
-DCGAL_INCLUDE_DIR=/usr/include \
|
-DCGAL_INCLUDE_DIR=/usr/include \
|
||||||
-DGMP_INCLUDE_DIR=/usr/include \
|
-DGMP_INCLUDE_DIR=/usr/include \
|
||||||
-DMPFR_INCLUDE_DIR=/usr/include \
|
-DMPFR_INCLUDE_DIR=/usr/include \
|
||||||
-DGMP_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \
|
-DGMP_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu \
|
||||||
-DMPFR_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
|
-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
|
make -j X
|
||||||
# Optionally install to the system
|
# Optionally install to the system
|
||||||
sudo make install
|
sudo make install
|
||||||
|
|||||||
@@ -30,14 +30,15 @@ class Patcher:
|
|||||||
file: ifcopenshell.file,
|
file: ifcopenshell.file,
|
||||||
logger: logging.Logger,
|
logger: logging.Logger,
|
||||||
mode: Literal[
|
mode: Literal[
|
||||||
"geometry",
|
"Geometry",
|
||||||
"placement",
|
"Placement",
|
||||||
"both",
|
"Both",
|
||||||
] = "geometry",
|
] = "Geometry",
|
||||||
a: Optional[float] = None,
|
automatic_offset_point: bool = True,
|
||||||
b: Optional[float] = None,
|
threshold: float = 1000000, # Arbitrary default threshold based on experience
|
||||||
c: Optional[float] = None,
|
x: Union[str, float] = "0",
|
||||||
d: Optional[float] = None,
|
y: Union[str, float] = "0",
|
||||||
|
z: Union[str, float] = "0",
|
||||||
):
|
):
|
||||||
"""Reset any large coordinates to smaller coordinates based on a threshold
|
"""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
|
ordinate is larger than a threshold) and offsets it back down to a small
|
||||||
number.
|
number.
|
||||||
|
|
||||||
You may either manually specify the offset to apply to any large
|
You may either let Blender BIM determine the offset to apply to any large
|
||||||
coordinate, or an offset will be automatically determined arbitrarily
|
coordinate automatically, arbitrarily based on the first large number we
|
||||||
based on the first large number we encounter.
|
encounter, or manually specify this offset.
|
||||||
|
|
||||||
Note that if your model inconsistently mixes coordinates between large
|
Note that if your model inconsistently mixes coordinates between large
|
||||||
and small (such as if your model mixes both local and map coordinates)
|
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
|
then the results of this function may be poor. Provide a bug report back
|
||||||
to your BIM application to get it fixed.
|
to your BIM application to get it fixed.
|
||||||
|
|
||||||
You may specify up to 4 arguments, a, b, c, and d.
|
:param mode: Choose from "Geometry", "Placement", or "Both". Choosing
|
||||||
|
"Geometry" will only replace cartesian points used in shape
|
||||||
If you specify no arguments, then the threshold is set to 1000000. The
|
representations. Choosing "Placement" will only replace cartesian
|
||||||
offset is auto detected.
|
points used in object placements. Choosing "Both" will replace all
|
||||||
|
|
||||||
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
|
|
||||||
cartesian points regardless of use (useful if the model has both
|
cartesian points regardless of use (useful if the model has both
|
||||||
large placement offsets and large geometry offsets).
|
large placement offsets and large geometry offsets).
|
||||||
:type mode: str
|
:type mode: str
|
||||||
:param a: The first parameter
|
:param automatic_offset_point: Choose, whether the offset should be
|
||||||
:type a: float,optional
|
determined automatically or specified manually.
|
||||||
:param b: The second parameter
|
:type automatic_offset_point: bool
|
||||||
:type b: float,optional
|
:param threshold: The threshold for deciding, whether a coordinate is
|
||||||
:param c: The third parameter
|
treated as a large coordinate.
|
||||||
:type c: float,optional
|
:type threshold: float
|
||||||
:param d: The fourth parameter
|
:param x: The x-ordinate of the manually specified offset point.
|
||||||
:type d: float,optional
|
: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:
|
Example:
|
||||||
|
|
||||||
@@ -103,39 +91,33 @@ class Patcher:
|
|||||||
ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "ResetAbsoluteCoordinates", "arguments": []})
|
ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "ResetAbsoluteCoordinates", "arguments": []})
|
||||||
|
|
||||||
# Reset all coordinates with an ordinate larger than 1000 arbitrarily
|
# 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
|
# 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
|
# 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.src = src
|
||||||
self.file = file
|
self.file = file
|
||||||
self.logger = logger
|
self.logger = logger
|
||||||
self.mode = mode
|
self.mode = mode.lower()
|
||||||
self.args = [x for x in [a, b, c, d] if x is not None]
|
self.threshold = threshold
|
||||||
|
if automatic_offset_point:
|
||||||
|
self.offset_point = None
|
||||||
|
else:
|
||||||
|
self.offset_point = (float(x), float(y), float(z))
|
||||||
|
|
||||||
def patch(self):
|
def patch(self):
|
||||||
placement_coord_ids = set()
|
placement_coord_ids = set()
|
||||||
for placement in self.file.by_type("IfcObjectPlacement"):
|
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")]
|
[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
|
# This method will not work all the time, but will catch most issues. It
|
||||||
# assumes that absolute coordinates are easily recognisable based on
|
# assumes that absolute coordinates are easily recognisable based on
|
||||||
# having a large absolute value above a threshold. This is not always
|
# 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.
|
# 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:
|
try:
|
||||||
point_lists = self.file.by_type("IfcCartesianPointList3D")
|
point_lists = self.file.by_type("IfcCartesianPointList3D")
|
||||||
except:
|
except:
|
||||||
@@ -147,10 +129,14 @@ class Patcher:
|
|||||||
if len(point) == 2 or not self.is_point_far_away(point):
|
if len(point) == 2 or not self.is_point_far_away(point):
|
||||||
coord_list[i] = point
|
coord_list[i] = point
|
||||||
continue
|
continue
|
||||||
if not offset_point:
|
if not self.offset_point:
|
||||||
offset_point = (-point[0], -point[1], -point[2])
|
self.offset_point = (-point[0], -point[1], -point[2])
|
||||||
self.logger.info(f"Resetting absolute coordinates by {point}")
|
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
|
coord_list[i] = point
|
||||||
point_list.CoordList = coord_list
|
point_list.CoordList = coord_list
|
||||||
for point in self.file.by_type("IfcCartesianPoint"):
|
for point in self.file.by_type("IfcCartesianPoint"):
|
||||||
@@ -162,13 +148,13 @@ class Patcher:
|
|||||||
elif self.mode == "placement":
|
elif self.mode == "placement":
|
||||||
if point.id() not in placement_coord_ids:
|
if point.id() not in placement_coord_ids:
|
||||||
continue
|
continue
|
||||||
if not offset_point:
|
if not self.offset_point:
|
||||||
offset_point = (-point.Coordinates[0], -point.Coordinates[1], -point.Coordinates[2])
|
self.offset_point = (-point.Coordinates[0], -point.Coordinates[1], -point.Coordinates[2])
|
||||||
self.logger.info(f"Resetting absolute coordinates by {point}")
|
self.logger.info(f"Resetting absolute coordinates by {point}")
|
||||||
point.Coordinates = (
|
point.Coordinates = (
|
||||||
point.Coordinates[0] + offset_point[0],
|
point.Coordinates[0] + self.offset_point[0],
|
||||||
point.Coordinates[1] + offset_point[1],
|
point.Coordinates[1] + self.offset_point[1],
|
||||||
point.Coordinates[2] + offset_point[2],
|
point.Coordinates[2] + self.offset_point[2],
|
||||||
)
|
)
|
||||||
|
|
||||||
def is_point_far_away(self, point: Union[ifcopenshell.entity_instance, npt.NDArray[np.float64]]) -> bool:
|
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);
|
TopExp_Explorer it(shell, TopAbs_FACE);
|
||||||
for (; it.More(); it.Next()) {
|
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);
|
auto surf = BRep_Tool::Surface(face);
|
||||||
if (surf->DynamicType() != STANDARD_TYPE(Geom_Plane)) {
|
if (surf->DynamicType() != STANDARD_TYPE(Geom_Plane)) {
|
||||||
return boost::none;
|
return boost::none;
|
||||||
|
|||||||
Reference in New Issue
Block a user