Consistent policy on normalization in halfspace eq map()

This commit is contained in:
Thomas Krijnen
2026-06-20 13:30:17 +02:00
parent c0f2fb1860
commit 669e04664d
6 changed files with 78 additions and 61 deletions
+3 -2
View File
@@ -24,6 +24,7 @@
#include "../ifcgeom/ConversionSettings.h"
#include "../ifcgeom/taxonomy.h"
#include <cstddef>
#include <memory>
#include <vector>
#include <unordered_map>
@@ -293,8 +294,8 @@ namespace IfcGeom {
virtual ConversionResultShape* intersect(ConversionResultShape*) = 0;
virtual ConversionResultShape* concat(ConversionResultShape*) = 0;
virtual void map(OpaqueCoordinate<4>& from, OpaqueCoordinate<4>& to) = 0;
virtual void map(const std::vector<OpaqueCoordinate<4>>& from, const std::vector<OpaqueCoordinate<4>>& to) = 0;
virtual std::size_t map(OpaqueCoordinate<4>& from, OpaqueCoordinate<4>& to) = 0;
virtual std::size_t map(const std::vector<OpaqueCoordinate<4>>& from, const std::vector<OpaqueCoordinate<4>>& to) = 0;
virtual ConversionResultShape* moved(ifcopenshell::geometry::taxonomy::matrix4::ptr) const = 0;
virtual bool surface_area_along_direction(double tol, const ifcopenshell::geometry::taxonomy::matrix4::ptr&, double& along_x, double& along_y, double& along_z) const = 0;
@@ -78,6 +78,31 @@ namespace {
);
}
cgal_plane_t plane_from_opaque(const OpaqueCoordinate<4>& p) {
#ifdef IFOPSH_SIMPLE_KERNEL
return cgal_plane_t(
p.get(0)->to_double(),
p.get(1)->to_double(),
p.get(2)->to_double(),
p.get(3)->to_double()
);
#else
return cgal_plane_t(
static_cast<NumberEpeck*>(p.get(0))->value(),
static_cast<NumberEpeck*>(p.get(1))->value(),
static_cast<NumberEpeck*>(p.get(2))->value(),
static_cast<NumberEpeck*>(p.get(3))->value()
);
#endif
}
void insert_normalized_plane_map(plane_map<Kernel_>& mp, const OpaqueCoordinate<4>& from, const OpaqueCoordinate<4>& to) {
mp.insert({
normalized_plane_for_map<Kernel_>(plane_from_opaque(from)),
normalized_plane_for_map<Kernel_>(plane_from_opaque(to))
});
}
cgal_vector_t wire_normal(const cgal_wire_t& wire) {
typename Kernel_::FT a(0), b(0), c(0);
if (wire.size() < 3) {
@@ -984,11 +1009,11 @@ ConversionResultShape* ifcopenshell::geometry::CgalShape::moved(ifcopenshell::ge
return new CgalShape(s, convex_tag_);
}
void ifcopenshell::geometry::CgalShape::map(OpaqueCoordinate<4>&, OpaqueCoordinate<4>&) {
std::size_t ifcopenshell::geometry::CgalShape::map(OpaqueCoordinate<4>&, OpaqueCoordinate<4>&) {
throw std::runtime_error("Not implemented");
}
void ifcopenshell::geometry::CgalShape::map(const std::vector<OpaqueCoordinate<4>>&, const std::vector<OpaqueCoordinate<4>>&) {
std::size_t ifcopenshell::geometry::CgalShape::map(const std::vector<OpaqueCoordinate<4>>&, const std::vector<OpaqueCoordinate<4>>&) {
throw std::runtime_error("Not implemented");
}
@@ -1165,27 +1190,16 @@ ConversionResultShape* ifcopenshell::geometry::CgalShapeHalfSpaceDecomposition::
throw std::runtime_error("Not implemented");
}
void ifcopenshell::geometry::CgalShapeHalfSpaceDecomposition::map(OpaqueCoordinate<4>& from, OpaqueCoordinate<4>& to) {
std::size_t ifcopenshell::geometry::CgalShapeHalfSpaceDecomposition::map(OpaqueCoordinate<4>& from, OpaqueCoordinate<4>& to) {
plane_map<Kernel_> mp;
mp.insert({
CGAL::Plane_3<Kernel_>(
static_cast<NumberEpeck*>(from.get(0))->value(),
static_cast<NumberEpeck*>(from.get(1))->value(),
static_cast<NumberEpeck*>(from.get(2))->value(),
static_cast<NumberEpeck*>(from.get(3))->value()
),
CGAL::Plane_3<Kernel_>(
static_cast<NumberEpeck*>(to.get(0))->value(),
static_cast<NumberEpeck*>(to.get(1))->value(),
static_cast<NumberEpeck*>(to.get(2))->value(),
static_cast<NumberEpeck*>(to.get(3))->value()
)
});
auto nw = shape_->map(mp);
insert_normalized_plane_map(mp, from, to);
std::size_t mutated = 0;
auto nw = shape_->map(mp, mutated);
shape_ = std::move(nw);
return mutated;
}
void ifcopenshell::geometry::CgalShapeHalfSpaceDecomposition::map(const std::vector<OpaqueCoordinate<4>>& froms, const std::vector<OpaqueCoordinate<4>>& tos) {
std::size_t ifcopenshell::geometry::CgalShapeHalfSpaceDecomposition::map(const std::vector<OpaqueCoordinate<4>>& froms, const std::vector<OpaqueCoordinate<4>>& tos) {
plane_map<Kernel_> mp;
if (froms.size() != tos.size()) {
throw std::runtime_error("Expected equal size");
@@ -1195,23 +1209,12 @@ void ifcopenshell::geometry::CgalShapeHalfSpaceDecomposition::map(const std::vec
for (; it < froms.end(); ++it, ++jt) {
auto& from = *it;
auto& to = *jt;
mp.insert({
CGAL::Plane_3<Kernel_>(
static_cast<NumberEpeck*>(from.get(0))->value(),
static_cast<NumberEpeck*>(from.get(1))->value(),
static_cast<NumberEpeck*>(from.get(2))->value(),
static_cast<NumberEpeck*>(from.get(3))->value()
),
CGAL::Plane_3<Kernel_>(
static_cast<NumberEpeck*>(to.get(0))->value(),
static_cast<NumberEpeck*>(to.get(1))->value(),
static_cast<NumberEpeck*>(to.get(2))->value(),
static_cast<NumberEpeck*>(to.get(3))->value()
)
});
insert_normalized_plane_map(mp, from, to);
}
auto nw = shape_->map(mp);
std::size_t mutated = 0;
auto nw = shape_->map(mp, mutated);
shape_ = std::move(nw);
return mutated;
}
@@ -276,8 +276,8 @@ namespace ifcopenshell { namespace geometry {
virtual ConversionResultShape* intersect(ConversionResultShape*);
virtual ConversionResultShape* concat(ConversionResultShape*);
virtual void map(OpaqueCoordinate<4>& from, OpaqueCoordinate<4>& to);
virtual void map(const std::vector<OpaqueCoordinate<4>>& from, const std::vector<OpaqueCoordinate<4>>& to);
virtual std::size_t map(OpaqueCoordinate<4>& from, OpaqueCoordinate<4>& to);
virtual std::size_t map(const std::vector<OpaqueCoordinate<4>>& from, const std::vector<OpaqueCoordinate<4>>& to);
virtual ConversionResultShape* moved(ifcopenshell::geometry::taxonomy::matrix4::ptr) const;
virtual bool surface_area_along_direction(double tol, const ifcopenshell::geometry::taxonomy::matrix4::ptr&, double& along_x, double& along_y, double& along_z) const;
@@ -347,8 +347,8 @@ namespace ifcopenshell { namespace geometry {
return nullptr;
}
virtual void map(OpaqueCoordinate<4>& from, OpaqueCoordinate<4>& to);
virtual void map(const std::vector<OpaqueCoordinate<4>>& from, const std::vector<OpaqueCoordinate<4>>& to);
virtual std::size_t map(OpaqueCoordinate<4>& from, OpaqueCoordinate<4>& to);
virtual std::size_t map(const std::vector<OpaqueCoordinate<4>>& from, const std::vector<OpaqueCoordinate<4>>& to);
virtual ConversionResultShape* moved(ifcopenshell::geometry::taxonomy::matrix4::ptr) const;
virtual bool surface_area_along_direction(double tol, const ifcopenshell::geometry::taxonomy::matrix4::ptr&, double& along_x, double& along_y, double& along_z) const {
@@ -46,6 +46,7 @@
#include <boost/iterator/transform_iterator.hpp>
#include <boost/graph/copy.hpp>
#include <cstddef>
#include <list>
#include <queue>
#include <memory>
@@ -116,6 +117,23 @@ template <typename Kernel>
using plane_map = std::map<typename Kernel::Plane_3, typename Kernel::Plane_3, PlaneLess<Kernel>>;
// using plane_map = std::unordered_map<typename Kernel::Plane_3, typename Kernel::Plane_3, PlaneHash<Kernel>>;
template <typename Kernel>
typename Kernel::Plane_3 normalized_plane_for_map(const typename Kernel::Plane_3& plane) {
std::array<typename Kernel::FT, 3> abc{ plane.a(), plane.b(), plane.c() };
auto minel = std::min_element(abc.begin(), abc.end());
auto maxel = std::max_element(abc.begin(), abc.end());
auto maxval = ((-*minel) > *maxel) ? (-*minel) : *maxel;
if (maxval == 0) {
return plane;
}
return typename Kernel::Plane_3(
plane.a() / maxval,
plane.b() / maxval,
plane.c() / maxval,
plane.d() / maxval
);
}
// Lexicographic comparator for CGAL Point_d (operator< is deleted in CGAL 6.x)
struct Point_d_4d_Less {
using Point_d = CGAL::Epick_d<CGAL::Dimension_tag<4>>::Point_d;
@@ -264,7 +282,11 @@ class halfspace_tree {
public:
virtual CGAL::Nef_polyhedron_3<Kernel> evaluate() const = 0;
virtual void accumulate(std::list<typename Kernel::Plane_3>&) const = 0;
virtual std::unique_ptr<halfspace_tree> map(const plane_map<Kernel>&) const = 0;
std::unique_ptr<halfspace_tree> map(const plane_map<Kernel>& m) const {
std::size_t ignored = 0;
return map(m, ignored);
}
virtual std::unique_ptr<halfspace_tree> map(const plane_map<Kernel>&, std::size_t& mutated) const = 0;
virtual std::string dump(int level = 0) const = 0;
virtual tree_type kind() const = 0;
virtual void merge(CGAL::Nef_polyhedron_3<Kernel>&) const = 0;
@@ -366,10 +388,10 @@ public:
op->accumulate(points);
}
}
virtual std::unique_ptr<halfspace_tree<Kernel>> map(const plane_map<Kernel>& m) const {
virtual std::unique_ptr<halfspace_tree<Kernel>> map(const plane_map<Kernel>& m, std::size_t& mutated) const {
decltype(operands_) mapped;
for (auto& op : operands_) {
mapped.emplace_back(op->map(m));
mapped.emplace_back(op->map(m, mutated));
}
return std::unique_ptr<halfspace_tree<Kernel>>(new halfspace_tree_nary_branch(operation_, std::move(mapped)));
}
@@ -485,21 +507,12 @@ public:
virtual void accumulate(std::list<typename Kernel::Plane_3>& points) const {
points.push_back(plane_);
}
virtual std::unique_ptr<halfspace_tree<Kernel>> map(const plane_map<Kernel>& m) const {
std::array<typename Kernel::FT, 3> abc{ plane_.a(), plane_.b(), plane_.c() };
auto minel = std::min_element(abc.begin(), abc.end());
auto maxel = std::max_element(abc.begin(), abc.end());
auto maxval = ((-*minel) > *maxel) ? (-*minel) : *maxel;
CGAL::Plane_3<Kernel> pp(
plane_.a() / maxval,
plane_.b() / maxval,
plane_.c() / maxval,
plane_.d() / maxval
);
virtual std::unique_ptr<halfspace_tree<Kernel>> map(const plane_map<Kernel>& m, std::size_t& mutated) const {
CGAL::Plane_3<Kernel> pp = normalized_plane_for_map<Kernel>(plane_);
auto it = m.find(pp);
if (it != m.end()) {
++mutated;
return std::unique_ptr<halfspace_tree<Kernel>>(new halfspace_tree_plane(it->second));
} else {
return std::unique_ptr<halfspace_tree<Kernel>>(new halfspace_tree_plane(plane_));
@@ -1358,4 +1371,4 @@ bool write_to_obj(const CGAL::Nef_polyhedron_3<Kernel>& a, std::ostream& ofs, si
return volume_index == std::numeric_limits<size_t>::max();
}
#endif
#endif
@@ -696,10 +696,10 @@ bool ifcopenshell::geometry::OpenCascadeShape::surface_area_along_direction(doub
return true;
}
void ifcopenshell::geometry::OpenCascadeShape::map(OpaqueCoordinate<4>&, OpaqueCoordinate<4>&) {
std::size_t ifcopenshell::geometry::OpenCascadeShape::map(OpaqueCoordinate<4>&, OpaqueCoordinate<4>&) {
throw std::runtime_error("Not implemented");
}
void ifcopenshell::geometry::OpenCascadeShape::map(const std::vector<OpaqueCoordinate<4>>&, const std::vector<OpaqueCoordinate<4>>&) {
std::size_t ifcopenshell::geometry::OpenCascadeShape::map(const std::vector<OpaqueCoordinate<4>>&, const std::vector<OpaqueCoordinate<4>>&) {
throw std::runtime_error("Not implemented");
}
@@ -101,8 +101,8 @@ namespace ifcopenshell {
virtual ConversionResultShape* intersect(ConversionResultShape*);
virtual ConversionResultShape* concat(ConversionResultShape*);
virtual void map(OpaqueCoordinate<4>& from, OpaqueCoordinate<4>& to);
virtual void map(const std::vector<OpaqueCoordinate<4>>& from, const std::vector<OpaqueCoordinate<4>>& to);
virtual std::size_t map(OpaqueCoordinate<4>& from, OpaqueCoordinate<4>& to);
virtual std::size_t map(const std::vector<OpaqueCoordinate<4>>& from, const std::vector<OpaqueCoordinate<4>>& to);
virtual ConversionResultShape* moved(ifcopenshell::geometry::taxonomy::matrix4::ptr) const;
virtual bool surface_area_along_direction(double tol, const ifcopenshell::geometry::taxonomy::matrix4::ptr&, double& along_x, double& along_y, double& along_z) const;
@@ -113,4 +113,4 @@ namespace ifcopenshell {
}
}
#endif
#endif