This commit is contained in:
Chirag Singh
2024-08-02 11:23:09 +05:30
16 changed files with 320 additions and 204 deletions
+4 -3
View File
@@ -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()
+3 -3
View File
@@ -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
+2
View File
@@ -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
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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;
+3 -1
View File
@@ -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);
+3
View File
@@ -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_;
+106
View File
@@ -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
+93
View File
@@ -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
View File
@@ -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
View File
@@ -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:
+1 -1
View File
@@ -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;