mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-09 13:52:23 +00:00
Penetration distances now are influenced by the normal for better results.
Add cache for points that have already been checked. Separate out new add_triangulated function with new kwarg add_element(should_triangulate=True) so that you can still have the old geom tree using aabb only if you want, also because add() is used in boolean_utils. Tolerance is now a kwarg. I misunderstood the OBB dimension and should *2 not /2 which now makes it much slower since there's more triangles to check.
This commit is contained in:
@@ -42,6 +42,7 @@
|
|||||||
|
|
||||||
#include <stack>
|
#include <stack>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
#include <BRepExtrema_TriangleSet.hxx>
|
#include <BRepExtrema_TriangleSet.hxx>
|
||||||
#include <BRepLProp_SLProps.hxx>
|
#include <BRepLProp_SLProps.hxx>
|
||||||
#include <BVH_BinaryTree.hxx>
|
#include <BVH_BinaryTree.hxx>
|
||||||
@@ -54,6 +55,7 @@
|
|||||||
#include <Geom_Plane.hxx>
|
#include <Geom_Plane.hxx>
|
||||||
#include <IntTools_FaceFace.hxx>
|
#include <IntTools_FaceFace.hxx>
|
||||||
#include "triangleintersects.hpp"
|
#include "triangleintersects.hpp"
|
||||||
|
#include <STEPConstruct_PointHasher.hxx>
|
||||||
#include <boost/stacktrace.hpp>
|
#include <boost/stacktrace.hpp>
|
||||||
|
|
||||||
|
|
||||||
@@ -128,6 +130,24 @@ namespace IfcGeom {
|
|||||||
float corners[2][3];
|
float corners[2][3];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PointHasher {
|
||||||
|
std::size_t operator()(const gp_Pnt& p) const {
|
||||||
|
// Assuming theUpperBound is somewhat arbitrary, but should be large enough
|
||||||
|
// and suitable for the size of the container.
|
||||||
|
// Note: std::unordered_set expects hash values starting from 0, but OpenCASCADE
|
||||||
|
// produces hash codes in the range [1, theUpperBound]. So, we adjust by subtracting 1.
|
||||||
|
return static_cast<std::size_t>(STEPConstruct_PointHasher::HashCode(p, std::numeric_limits<Standard_Integer>::max())) - 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Functor for comparing two gp_Pnt objects for equality
|
||||||
|
struct PointEqual {
|
||||||
|
bool operator()(const gp_Pnt& p1, const gp_Pnt& p2) const {
|
||||||
|
return STEPConstruct_PointHasher::IsEqual(p1, p2);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// Branchless slab method. Note that this can still be optimised further by batching boxes.
|
// Branchless slab method. Note that this can still be optimised further by batching boxes.
|
||||||
// https://tavianator.com/2022/ray_box_boundary.html
|
// https://tavianator.com/2022/ray_box_boundary.html
|
||||||
bool is_intersect_ray_box(const struct ray *ray, const struct box *box) const {
|
bool is_intersect_ray_box(const struct ray *ray, const struct box *box) const {
|
||||||
@@ -333,7 +353,7 @@ namespace IfcGeom {
|
|||||||
return std::array<gp_Pnt, 3> {moveTowards(v1), moveTowards(v2), moveTowards(v3)};
|
return std::array<gp_Pnt, 3> {moveTowards(v1), moveTowards(v2), moveTowards(v3)};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool test_intersection(const T& tA, const T& tB, const TopoDS_Shape& A, const TopoDS_Shape& B) const {
|
bool test_intersection(const T& tA, const T& tB, const TopoDS_Shape& A, const TopoDS_Shape& B, double tolerance) const {
|
||||||
// Attempt 3:
|
// Attempt 3:
|
||||||
// 1. For each vert of A that is inside shape B, find the shortest distance to the closest face
|
// 1. For each vert of A that is inside shape B, find the shortest distance to the closest face
|
||||||
// 2. Of those verts, find the innermost vert (i.e. the vert that has the longest distance)
|
// 2. Of those verts, find the innermost vert (i.e. the vert that has the longest distance)
|
||||||
@@ -410,6 +430,12 @@ namespace IfcGeom {
|
|||||||
|
|
||||||
BRepExtrema_TriangleSet triangle_set_a = triangle_sets_.find(tA)->second;
|
BRepExtrema_TriangleSet triangle_set_a = triangle_sets_.find(tA)->second;
|
||||||
BRepExtrema_TriangleSet triangle_set_b = triangle_sets_.find(tB)->second;
|
BRepExtrema_TriangleSet triangle_set_b = triangle_sets_.find(tB)->second;
|
||||||
|
std::unordered_map<int, TopoDS_Face> faces_a = faces_.find(tA)->second;
|
||||||
|
std::unordered_map<int, TopoDS_Face> faces_b = faces_.find(tB)->second;
|
||||||
|
|
||||||
|
// ~10% faster?
|
||||||
|
std::unordered_set<gp_Pnt, PointHasher, PointEqual> points_in_b_cache;
|
||||||
|
std::unordered_set<gp_Pnt, PointHasher, PointEqual> points_not_in_b_cache;
|
||||||
|
|
||||||
double protrusion = -std::numeric_limits<double>::infinity();
|
double protrusion = -std::numeric_limits<double>::infinity();
|
||||||
std::array<double, 3> protrusion_point;
|
std::array<double, 3> protrusion_point;
|
||||||
@@ -422,7 +448,12 @@ namespace IfcGeom {
|
|||||||
std::vector<gp_Vec> ray_vectors;
|
std::vector<gp_Vec> ray_vectors;
|
||||||
|
|
||||||
BVH_Vec3d v1, v2, v3;
|
BVH_Vec3d v1, v2, v3;
|
||||||
triangle_set_a.GetVertices(i, v1, v2, v3);
|
|
||||||
|
if (faces_a[triangle_set_a.GetFaceID(i)].Orientation() == TopAbs_REVERSED) {
|
||||||
|
triangle_set_a.GetVertices(i, v1, v3, v2);
|
||||||
|
} else {
|
||||||
|
triangle_set_a.GetVertices(i, v1, v2, v3);
|
||||||
|
}
|
||||||
|
|
||||||
gp_Pnt v1_a_pnt(v1[0], v1[1], v1[2]);
|
gp_Pnt v1_a_pnt(v1[0], v1[1], v1[2]);
|
||||||
gp_Pnt v2_a_pnt(v2[0], v2[1], v2[2]);
|
gp_Pnt v2_a_pnt(v2[0], v2[1], v2[2]);
|
||||||
@@ -438,10 +469,11 @@ namespace IfcGeom {
|
|||||||
std::array<double, 3> t1b = {v2_a_pnt.X(), v2_a_pnt.Y(), v2_a_pnt.Z()};
|
std::array<double, 3> t1b = {v2_a_pnt.X(), v2_a_pnt.Y(), v2_a_pnt.Z()};
|
||||||
std::array<double, 3> t1c = {v3_a_pnt.X(), v3_a_pnt.Y(), v3_a_pnt.Z()};
|
std::array<double, 3> t1c = {v3_a_pnt.X(), v3_a_pnt.Y(), v3_a_pnt.Z()};
|
||||||
|
|
||||||
|
gp_Vec normal_a;
|
||||||
try {
|
try {
|
||||||
gp_Vec dir1_a(v1_a_pnt, v2_a_pnt);
|
gp_Vec dir1_a(v1_a_pnt, v2_a_pnt);
|
||||||
gp_Vec dir2_a(v1_a_pnt, v3_a_pnt);
|
gp_Vec dir2_a(v1_a_pnt, v3_a_pnt);
|
||||||
gp_Vec normal_a = dir1_a.Crossed(dir2_a).Normalized();
|
normal_a = dir1_a.Crossed(dir2_a).Normalized();
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -450,9 +482,18 @@ namespace IfcGeom {
|
|||||||
std::vector<gp_Pnt> points_in_b;
|
std::vector<gp_Pnt> points_in_b;
|
||||||
|
|
||||||
for (const auto& v : points_a) {
|
for (const auto& v : points_a) {
|
||||||
if (is_point_in_shape(v, bvh_b, triangle_set_b)
|
if (points_not_in_b_cache.find(v) != points_not_in_b_cache.end()) {
|
||||||
&& is_point_in_shape(v, bvh_b, triangle_set_b, true)) {
|
continue;
|
||||||
|
} else if (points_in_b_cache.find(v) != points_in_b_cache.end()) {
|
||||||
points_in_b.push_back(v);
|
points_in_b.push_back(v);
|
||||||
|
} else {
|
||||||
|
if (is_point_in_shape(v, bvh_b, triangle_set_b)
|
||||||
|
&& is_point_in_shape(v, bvh_b, triangle_set_b, true)) {
|
||||||
|
points_in_b.push_back(v);
|
||||||
|
points_in_b_cache.insert(v);
|
||||||
|
} else {
|
||||||
|
points_not_in_b_cache.insert(v);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -466,15 +507,14 @@ namespace IfcGeom {
|
|||||||
for (const auto& bvh_b_i : bvh_b_is) {
|
for (const auto& bvh_b_i : bvh_b_is) {
|
||||||
for (int j=bvh_b->BegPrimitive(bvh_b_i); j<=bvh_b->EndPrimitive(bvh_b_i); ++j) {
|
for (int j=bvh_b->BegPrimitive(bvh_b_i); j<=bvh_b->EndPrimitive(bvh_b_i); ++j) {
|
||||||
BVH_Vec3d v1_b, v2_b, v3_b;
|
BVH_Vec3d v1_b, v2_b, v3_b;
|
||||||
triangle_set_b.GetVertices(j, v1_b, v2_b, v3_b);
|
|
||||||
tri_count_++;
|
|
||||||
|
|
||||||
/*
|
if (faces_b[triangle_set_b.GetFaceID(j)].Orientation() == TopAbs_REVERSED) {
|
||||||
std::cout << "This tri is a potential prim" << std::endl;
|
triangle_set_b.GetVertices(j, v1_b, v3_b, v2_b);
|
||||||
std::cout << "->tri " << v1_b[0] << " " << v1_b[1] << " " << v1_b[2] << std::endl;
|
} else {
|
||||||
std::cout << "->tri " << v2_b[0] << " " << v2_b[1] << " " << v2_b[2] << std::endl;
|
triangle_set_b.GetVertices(j, v1_b, v2_b, v3_b);
|
||||||
std::cout << "->tri " << v3_b[0] << " " << v3_b[1] << " " << v3_b[2] << std::endl;
|
}
|
||||||
*/
|
|
||||||
|
tri_count_++;
|
||||||
|
|
||||||
gp_Pnt v1_b_pnt(v1_b[0], v1_b[1], v1_b[2]);
|
gp_Pnt v1_b_pnt(v1_b[0], v1_b[1], v1_b[2]);
|
||||||
gp_Pnt v2_b_pnt(v2_b[0], v2_b[1], v2_b[2]);
|
gp_Pnt v2_b_pnt(v2_b[0], v2_b[1], v2_b[2]);
|
||||||
@@ -488,16 +528,21 @@ namespace IfcGeom {
|
|||||||
|
|
||||||
gp_Vec normal_b;
|
gp_Vec normal_b;
|
||||||
try {
|
try {
|
||||||
// It seems as though normal_b may be arbitrarily flipped.
|
|
||||||
// Maybe can use GetFaceID to check face orientation
|
|
||||||
gp_Vec dir1_b(v1_b_pnt, v2_b_pnt);
|
gp_Vec dir1_b(v1_b_pnt, v2_b_pnt);
|
||||||
gp_Vec dir2_b(v3_b_pnt, v1_b_pnt);
|
gp_Vec dir2_b(v1_b_pnt, v3_b_pnt);
|
||||||
normal_b = dir1_b.Crossed(dir2_b).Normalized();
|
normal_b = dir1_b.Crossed(dir2_b).Normalized();
|
||||||
ray_vectors.push_back(normal_b);
|
ray_vectors.push_back(normal_b);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We're penetrating _into_ a shape, so don't
|
||||||
|
// compare distances to faces with roughly the
|
||||||
|
// same normal as the penetration.
|
||||||
|
if (normal_a.Dot(normal_b) >= 0.9f) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
for (const auto& v : points_in_b) {
|
for (const auto& v : points_in_b) {
|
||||||
gp_Vec ray_origin(v.X(), v.Y(), v.Z());
|
gp_Vec ray_origin(v.X(), v.Y(), v.Z());
|
||||||
gp_Vec point_on_b;
|
gp_Vec point_on_b;
|
||||||
@@ -514,11 +559,18 @@ namespace IfcGeom {
|
|||||||
std::cout << "->tri " << v3_b[0] << " " << v3_b[1] << " " << v3_b[2] << std::endl;
|
std::cout << "->tri " << v3_b[0] << " " << v3_b[1] << " " << v3_b[2] << std::endl;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Do (cheaper) line check because normals may be flipped.
|
// Do (cheaper) line check.
|
||||||
if (is_intersect_ray_tri(ray_origin, normal_b, ta, tb, tc, point_on_b, true)) {
|
if (is_intersect_ray_tri(ray_origin, normal_b, ta, tb, tc, point_on_b, true)) {
|
||||||
gp_Pnt pnt_on_b(point_on_b.X(), point_on_b.Y(), point_on_b.Z());
|
gp_Pnt pnt_on_b(point_on_b.X(), point_on_b.Y(), point_on_b.Z());
|
||||||
double current_v_protrusion = v.Distance(pnt_on_b);
|
double current_v_protrusion = v.Distance(pnt_on_b);
|
||||||
|
|
||||||
|
/*
|
||||||
|
// What happens now?
|
||||||
|
if (current_v_protrusion > max_protrusion) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// std::cout << "We got a current protrusion " << current_v_protrusion << std::endl;
|
// std::cout << "We got a current protrusion " << current_v_protrusion << std::endl;
|
||||||
if (current_v_protrusion < v_protrusion) {
|
if (current_v_protrusion < v_protrusion) {
|
||||||
// std::cout << "New v_protrusion winner of " << current_v_protrusion << std::endl;
|
// std::cout << "New v_protrusion winner of " << current_v_protrusion << std::endl;
|
||||||
@@ -540,7 +592,7 @@ namespace IfcGeom {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (protrusion > 0.001) {
|
if (protrusion > tolerance) {
|
||||||
protrusion_distances_.push_back(protrusion);
|
protrusion_distances_.push_back(protrusion);
|
||||||
protrusion_points_.push_back(protrusion_point);
|
protrusion_points_.push_back(protrusion_point);
|
||||||
return true;
|
return true;
|
||||||
@@ -599,13 +651,20 @@ namespace IfcGeom {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void add(const T& t, const TopoDS_Shape& s) {
|
void add(const T& t, const TopoDS_Shape& s) {
|
||||||
// Note that this function is also used elsewhere (e.g. boolean_utils.cpp)
|
Bnd_Box b;
|
||||||
// We have to triangulate it to make clash detection faster
|
BRepBndLib::AddClose(s, b);
|
||||||
|
add(t, b);
|
||||||
|
shapes_[t] = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_triangulated(const T& t, const TopoDS_Shape& s) {
|
||||||
|
// Note that the original add function is also used elsewhere (e.g. boolean_utils.cpp)
|
||||||
|
// We don't want to randomly add triangulated voids in our
|
||||||
|
// tree, so for now this is a separate function.
|
||||||
BRepMesh_IncrementalMesh(s, 1.e-3, false, 0.5);
|
BRepMesh_IncrementalMesh(s, 1.e-3, false, 0.5);
|
||||||
|
|
||||||
Bnd_Box b;
|
Bnd_Box b;
|
||||||
BRepBndLib::AddClose(s, b);
|
BRepBndLib::AddClose(s, b);
|
||||||
//add(t, b);
|
|
||||||
tree_.Add(t, b);
|
tree_.Add(t, b);
|
||||||
shapes_[t] = s;
|
shapes_[t] = s;
|
||||||
|
|
||||||
@@ -613,7 +672,7 @@ namespace IfcGeom {
|
|||||||
BRepBndLib::AddOBB(s, obb);
|
BRepBndLib::AddOBB(s, obb);
|
||||||
obbs_[t] = obb;
|
obbs_[t] = obb;
|
||||||
|
|
||||||
max_protrusions_[t] = std::min(std::min(obb.XHSize(), obb.YHSize()), obb.ZHSize()) / 2;
|
max_protrusions_[t] = std::min(std::min(obb.XHSize(), obb.YHSize()), obb.ZHSize()) * 2;
|
||||||
|
|
||||||
BVH_BoxSet<double, 3>* boxset = new BVH_BoxSet<double, 3>();
|
BVH_BoxSet<double, 3>* boxset = new BVH_BoxSet<double, 3>();
|
||||||
BRepExtrema_ShapeList shape_list;
|
BRepExtrema_ShapeList shape_list;
|
||||||
@@ -659,10 +718,16 @@ namespace IfcGeom {
|
|||||||
for (int i=0; i<triangle_set.Size(); ++i) {
|
for (int i=0; i<triangle_set.Size(); ++i) {
|
||||||
BVH_Vec3d v1, v2, v3;
|
BVH_Vec3d v1, v2, v3;
|
||||||
triangle_set.GetVertices(i, v1, v2, v3);
|
triangle_set.GetVertices(i, v1, v2, v3);
|
||||||
|
int face_id = triangle_set.GetFaceID(i);
|
||||||
std::cout << "Triangle in triangle set:" << std::endl;
|
std::cout << "Triangle in triangle set:" << std::endl;
|
||||||
std::cout << v1[0] << " " << v1[1] << " " << v1[2] << std::endl;
|
std::cout << v1[0] << " " << v1[1] << " " << v1[2] << std::endl;
|
||||||
std::cout << v2[0] << " " << v2[1] << " " << v2[2] << std::endl;
|
if (faces[face_id].Orientation() == TopAbs_REVERSED) {
|
||||||
std::cout << v3[0] << " " << v3[1] << " " << v3[2] << std::endl;
|
std::cout << v3[0] << " " << v3[1] << " " << v3[2] << std::endl;
|
||||||
|
std::cout << v2[0] << " " << v2[1] << " " << v2[2] << std::endl;
|
||||||
|
} else {
|
||||||
|
std::cout << v2[0] << " " << v2[1] << " " << v2[2] << std::endl;
|
||||||
|
std::cout << v3[0] << " " << v3[1] << " " << v3[2] << std::endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -755,7 +820,7 @@ namespace IfcGeom {
|
|||||||
i++;
|
i++;
|
||||||
std::cout << "Currently doing" << i << std::endl;
|
std::cout << "Currently doing" << i << std::endl;
|
||||||
|
|
||||||
if (test_intersection(t, *it, A, B)) {
|
if (test_intersection(t, *it, A, B, tolerance)) {
|
||||||
ts_filtered.push_back(*it);
|
ts_filtered.push_back(*it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -954,13 +1019,17 @@ namespace IfcGeom {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_element(IfcGeom::BRepElement* elem) {
|
void add_element(IfcGeom::BRepElement* elem, bool should_triangulate=false) {
|
||||||
if (!elem) {
|
if (!elem) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto compound = elem->geometry().as_compound();
|
auto compound = elem->geometry().as_compound();
|
||||||
compound.Move(elem->transformation().data());
|
compound.Move(elem->transformation().data());
|
||||||
add(elem->product(), compound);
|
if (should_triangulate) {
|
||||||
|
add_triangulated(elem->product(), compound);
|
||||||
|
} else {
|
||||||
|
add(elem->product(), compound);
|
||||||
|
}
|
||||||
auto git = elem->geometry().begin();
|
auto git = elem->geometry().begin();
|
||||||
|
|
||||||
if (enable_face_styles_) {
|
if (enable_face_styles_) {
|
||||||
|
|||||||
Reference in New Issue
Block a user