From e95e16c102fda1bd1e6daec2b6241aa38c9907b6 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Thu, 1 Feb 2024 18:30:20 +1100 Subject: [PATCH] First attempt at implementation of intersection clash checks. This considers only protruding clashes. It does not consider touching nor encroaching clashes and does not consider protrusion direction in the protrusion distance. --- src/ifcgeom_schema_agnostic/IfcGeomTree.h | 570 +++++++++++++++++- .../ifcopenshell/geom/main.py | 9 + src/ifcwrap/IfcGeomWrapper.i | 8 + src/ifcwrap/IfcPython.i | 1 + 4 files changed, 587 insertions(+), 1 deletion(-) diff --git a/src/ifcgeom_schema_agnostic/IfcGeomTree.h b/src/ifcgeom_schema_agnostic/IfcGeomTree.h index 8c7e99fde5..3d2533a0db 100644 --- a/src/ifcgeom_schema_agnostic/IfcGeomTree.h +++ b/src/ifcgeom_schema_agnostic/IfcGeomTree.h @@ -40,6 +40,23 @@ #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "triangleintersects.hpp" +#include + + namespace IfcGeom { struct ray_intersection_result { @@ -101,6 +118,435 @@ namespace IfcGeom { namespace impl { template class tree { + struct ray { + float origin[3]; + float dir[3]; + float dir_inv[3]; + }; + + struct box { + float corners[2][3]; + }; + + // Branchless slab method. Note that this can still be optimised further by batching boxes. + // https://tavianator.com/2022/ray_box_boundary.html + bool is_intersect_ray_box(const struct ray *ray, const struct box *box) const { + float tmin = 0.0, tmax = INFINITY; + + for (int d = 0; d < 3; ++d) { + bool sign = std::signbit(ray->dir_inv[d]); + float bmin = box->corners[sign][d]; + float bmax = box->corners[!sign][d]; + + float dmin = (bmin - ray->origin[d]) * ray->dir_inv[d]; + float dmax = (bmax - ray->origin[d]) * ray->dir_inv[d]; + + tmin = std::max(dmin, tmin); + tmax = std::min(dmax, tmax); + } + + return tmin < tmax; + } + + // Modified slightly to use gp_Vec and allow line-tri intersection + // https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm + bool is_intersect_ray_tri( + const gp_Vec& ray_origin, + const gp_Vec& ray_vector, + const gp_Vec& ta, + const gp_Vec& tb, + const gp_Vec& tc, + gp_Vec& out_intersection_point, + const bool is_line = false + ) const { + constexpr float epsilon = std::numeric_limits::epsilon(); + + gp_Vec edge1 = tb - ta; + gp_Vec edge2 = tc - ta; + gp_Vec ray_cross_e2 = ray_vector.Crossed(edge2); + float det = edge1.Dot(ray_cross_e2); + + if (det > -epsilon && det < epsilon) + return false; // This ray is parallel to this triangle. + + float inv_det = 1.0 / det; + gp_Vec s = ray_origin - ta; + float u = inv_det * s.Dot(ray_cross_e2); + + if (u < 0 || u > 1) + return false; + + gp_Vec s_cross_e1 = s.Crossed(edge1); + float v = inv_det * ray_vector.Dot(s_cross_e1); + + if (v < 0 || u + v > 1) + return false; + + // At this stage we can compute t to find out where the intersection point is on the line. + float t = inv_det * edge2.Dot(s_cross_e1); + + if (is_line) { + out_intersection_point = ray_origin + ray_vector * t; + return true; + } else { + if (t > epsilon) // ray intersection + { + out_intersection_point = ray_origin + ray_vector * t; + return true; + } + else // This means that there is a line intersection but not a ray intersection. + return false; + } + } + + bool is_point_in_shape( + gp_Pnt v, + opencascade::handle> bvh, + BRepExtrema_TriangleSet triangle_set, + // In the case of "touching" rays, let's check again! + bool should_check_again = false + ) const { + ray v_ray; + v_ray.origin[0] = v.X(); + v_ray.origin[1] = v.Y(); + v_ray.origin[2] = v.Z(); + + if (should_check_again) { + // The first check may be incorrect if it intersects + // exactly between triangles or on edges of triangles. + // A second check is used to "double check" the results. + // The second check is perpendicular because AEC objects + // are typically symmetrical along an axis, and goes down + // because there's typically less stuff down there. + v_ray.dir[0] = 0.0f; + v_ray.dir[1] = 0.0f; + v_ray.dir[2] = -1.0f; + v_ray.dir_inv[0] = INFINITY; // 1.0f/dir[0] + v_ray.dir_inv[1] = INFINITY; // 1.0f/dir[1] + v_ray.dir_inv[2] = -1.0f; // 1.0f/dir[2] + } else { + v_ray.dir[0] = 1.0f; + v_ray.dir[1] = 0.0f; + v_ray.dir[2] = 0.0f; + v_ray.dir_inv[0] = 1.0f; // 1.0f/dir[0] + v_ray.dir_inv[1] = INFINITY; // 1.0f/dir[1] + v_ray.dir_inv[2] = INFINITY; // 1.0f/dir[2] + } + + gp_Vec ray_origin(v.X(), v.Y(), v.Z()); + gp_Vec ray_vector(v_ray.dir[0], v_ray.dir[1], v_ray.dir[2]); + + int total_intersections = 0; + + std::stack stack; + stack.push(0); + + while ( ! stack.empty()) { + int i = stack.top(); + stack.pop(); + + BVH_TreeBase::BVH_VecNt min_point = bvh->MinPoint(i); + BVH_TreeBase::BVH_VecNt max_point = bvh->MaxPoint(i); + + box box; + // + 1e-5 for tolerance + box.corners[0][0] = min_point[0] - 1e-5; + box.corners[0][1] = min_point[1] - 1e-5; + box.corners[0][2] = min_point[2] - 1e-5; + box.corners[1][0] = max_point[0] + 1e-5; + box.corners[1][1] = max_point[1] + 1e-5; + box.corners[1][2] = max_point[2] + 1e-5; + /* + std::cout << "Ray " + << v_ray.origin[0] << " " + << v_ray.origin[1] << " " + << v_ray.origin[2] << " " + << std::endl; + std::cout << "Box " + << min_point[0] << " " + << min_point[1] << " " + << min_point[2] << " " + << max_point[0] << " " + << max_point[1] << " " + << max_point[2] << " " + << std::endl; + */ + + if ( ! is_intersect_ray_box(&v_ray, &box)) { + continue; + } + //std::cout << "Ray hits box" << std::endl; + if (bvh->IsOuter(i)) { + //std::cout << "Ray hits leaf" << std::endl; + // Do ray triangle check. + for (int j=bvh->BegPrimitive(i); j<=bvh->EndPrimitive(i); ++j) { + BVH_Vec3d v1, v2, v3; + triangle_set.GetVertices(j, v1, v2, v3); + + gp_Vec ta(v1[0], v1[1], v1[2]); + gp_Vec tb(v2[0], v2[1], v2[2]); + gp_Vec tc(v3[0], v3[1], v3[2]); + gp_Vec intersection_point; + + /* + std::cout << "ray origin " << ray_origin.X() << " " << ray_origin.Y() << " " << ray_origin.Z() << std::endl; + std::cout << "inside-tri " << ta.X() << " " << ta.Y() << " " << ta.Z() << std::endl; + std::cout << "inside-tri " << tb.X() << " " << tb.Y() << " " << tb.Z() << std::endl; + std::cout << "inside-tri " << tc.X() << " " << tc.Y() << " " << tc.Z() << std::endl; + */ + if (is_intersect_ray_tri(ray_origin, ray_vector, ta, tb, tc, intersection_point)) { + // std::cout << " intersected " << intersection_point.X() << " " << intersection_point.Y() << " " << intersection_point.Z() << std::endl; + total_intersections++; + } + } + } else { + stack.push(bvh->Child<0>(i)); + stack.push(bvh->Child<1>(i)); + } + } + + return total_intersections % 2 != 0; + } + + std::array shrink_triangle( + const gp_Pnt& v1, + const gp_Pnt& v2, + const gp_Pnt& v3, + double insetDistance + ) const { + gp_Pnt centroid((v1.X() + v2.X() + v3.X()) / 3.0, + (v1.Y() + v2.Y() + v3.Y()) / 3.0, + (v1.Z() + v2.Z() + v3.Z()) / 3.0); + + if (v1.Distance(centroid) < insetDistance + || v2.Distance(centroid) < insetDistance + || v3.Distance(centroid) < insetDistance) { + return std::array {v1, v2, v3}; + } + + auto moveTowards = [&](const gp_Pnt& vertex) -> gp_Pnt { + gp_Vec direction(vertex, centroid); + direction.Normalize(); + return gp_Pnt(vertex.XYZ() + direction.XYZ() * insetDistance); + }; + + return std::array {moveTowards(v1), moveTowards(v2), moveTowards(v3)}; + } + + bool test_intersection(const T& tA, const T& tB, const TopoDS_Shape& A, const TopoDS_Shape& B) const { + // Attempt 3: + // 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) + + // OBB check + auto a_larger = obbs_.find(tA)->second; + auto b_larger = obbs_.find(tB)->second; + // Maybe for clashing we should shrink in order to prevent touches. + // a_larger.Enlarge(extend); + if (a_larger.IsOut(b_larger)) { + return false; + } + + double max_protrusion = max_protrusions_.find(tB)->second; + + // Collide BVH trees of shape A vs B + opencascade::handle> bvh_a = bvhs_.find(tA)->second; + opencascade::handle> bvh_b = bvhs_.find(tB)->second; + std::unordered_map> bvh_clashes; + + for (int i=0; iLength(); ++i) { + if ( ! bvh_a->IsOuter(i)) { + continue; + } + + BVH_TreeBase::BVH_VecNt bvh_a_min = bvh_a->MinPoint(i); + BVH_TreeBase::BVH_VecNt bvh_a_max = bvh_a->MaxPoint(i); + bvh_a_min[0] -= 1e-3; + bvh_a_min[1] -= 1e-3; + bvh_a_min[2] -= 1e-3; + bvh_a_max[0] += 1e-3; + bvh_a_max[1] += 1e-3; + bvh_a_max[2] += 1e-3; + + BVH_Box box_a(bvh_a_min, bvh_a_max); + //BVH_Box box_a(bvh_a->MinPoint(i), bvh_a->MaxPoint(i)); + + std::stack stack; + stack.push(0); + + while ( ! stack.empty()) { + int j = stack.top(); + stack.pop(); + + BVH_TreeBase::BVH_VecNt bvh_b_min = bvh_b->MinPoint(j); + BVH_TreeBase::BVH_VecNt bvh_b_max = bvh_b->MaxPoint(j); + bvh_b_min[0] -= max_protrusion + 1e-3; + bvh_b_min[1] -= max_protrusion + 1e-3; + bvh_b_min[2] -= max_protrusion + 1e-3; + bvh_b_max[0] += max_protrusion + 1e-3; + bvh_b_max[1] += max_protrusion + 1e-3; + bvh_b_max[2] += max_protrusion + 1e-3; + + //if (box_a.IsOut(bvh_b->MinPoint(j), bvh_b->MaxPoint(j))) { + if (box_a.IsOut(bvh_b_min, bvh_b_max)) { + continue; + } + if (bvh_b->IsOuter(j)) { + if (bvh_clashes.find(i) != bvh_clashes.end()) { + bvh_clashes[i].push_back(j); + } else { + bvh_clashes[i] = {j}; + } + } else { + stack.push(bvh_b->Child<0>(j)); + stack.push(bvh_b->Child<1>(j)); + } + } + } + + if (bvh_clashes.empty()) { + return false; + } + + BRepExtrema_TriangleSet triangle_set_a = triangle_sets_.find(tA)->second; + BRepExtrema_TriangleSet triangle_set_b = triangle_sets_.find(tB)->second; + + double protrusion = -std::numeric_limits::infinity(); + std::array protrusion_point; + + for (const auto& pair : bvh_clashes) { + int bvh_a_i = pair.first; + std::vector bvh_b_is = pair.second; + + for (int i=bvh_a->BegPrimitive(bvh_a_i); i<=bvh_a->EndPrimitive(bvh_a_i); ++i) { + std::vector ray_vectors; + + BVH_Vec3d v1, v2, v3; + triangle_set_a.GetVertices(i, v1, v2, v3); + + gp_Pnt v1_a_pnt(v1[0], v1[1], v1[2]); + gp_Pnt v2_a_pnt(v2[0], v2[1], v2[2]); + gp_Pnt v3_a_pnt(v3[0], v3[1], v3[2]); + + // Shrink triangle slightly to prevent getting "touching" clashes + std::array shrunk_verts = shrink_triangle(v1_a_pnt, v2_a_pnt, v3_a_pnt, 0.002); + v1_a_pnt = shrunk_verts[0]; + v2_a_pnt = shrunk_verts[1]; + v3_a_pnt = shrunk_verts[2]; + + std::array t1a = {v1_a_pnt.X(), v1_a_pnt.Y(), v1_a_pnt.Z()}; + std::array t1b = {v2_a_pnt.X(), v2_a_pnt.Y(), v2_a_pnt.Z()}; + std::array t1c = {v3_a_pnt.X(), v3_a_pnt.Y(), v3_a_pnt.Z()}; + + try { + gp_Vec dir1_a(v1_a_pnt, v2_a_pnt); + gp_Vec dir2_a(v1_a_pnt, v3_a_pnt); + gp_Vec normal_a = dir1_a.Crossed(dir2_a).Normalized(); + } catch (...) { + continue; + } + + std::array points_a = {v1_a_pnt, v2_a_pnt, v3_a_pnt}; + std::vector points_in_b; + + for (const auto& v : points_a) { + 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); + } + } + + if (points_in_b.empty()) { + continue; + } + + double v_protrusion = std::numeric_limits::infinity(); + std::array v_protrusion_point; + + 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) { + BVH_Vec3d v1_b, v2_b, v3_b; + triangle_set_b.GetVertices(j, v1_b, v2_b, v3_b); + tri_count_++; + + /* + std::cout << "This tri is a potential prim" << std::endl; + std::cout << "->tri " << v1_b[0] << " " << v1_b[1] << " " << v1_b[2] << std::endl; + std::cout << "->tri " << v2_b[0] << " " << v2_b[1] << " " << v2_b[2] << std::endl; + std::cout << "->tri " << v3_b[0] << " " << v3_b[1] << " " << v3_b[2] << std::endl; + */ + + 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 v3_b_pnt(v3_b[0], v3_b[1], v3_b[2]); + + /* + std::cout << "->cont " << v1_b[0] << " " << v1_b[1] << " " << v1_b[2] << std::endl; + std::cout << "->cont " << v2_b[0] << " " << v2_b[1] << " " << v2_b[2] << std::endl; + std::cout << "->cont " << v3_b[0] << " " << v3_b[1] << " " << v3_b[2] << std::endl; + */ + + gp_Vec normal_b; + 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 dir2_b(v3_b_pnt, v1_b_pnt); + normal_b = dir1_b.Crossed(dir2_b).Normalized(); + ray_vectors.push_back(normal_b); + } catch (...) { + continue; + } + + for (const auto& v : points_in_b) { + gp_Vec ray_origin(v.X(), v.Y(), v.Z()); + gp_Vec point_on_b; + + gp_Vec ta(v1_b[0], v1_b[1], v1_b[2]); + gp_Vec tb(v2_b[0], v2_b[1], v2_b[2]); + gp_Vec tc(v3_b[0], v3_b[1], v3_b[2]); + + /* + std::cout << "POINT IN B " << v.X() << " " << v.Y() << " " << v.Z() << std::endl; + std::cout << "dir-> " << normal_b.X() << " " << normal_b.Y() << " " << normal_b.Z() << std::endl; + std::cout << "->tri " << v1_b[0] << " " << v1_b[1] << " " << v1_b[2] << std::endl; + std::cout << "->tri " << v2_b[0] << " " << v2_b[1] << " " << v2_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. + 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()); + double current_v_protrusion = v.Distance(pnt_on_b); + + // std::cout << "We got a current protrusion " << current_v_protrusion << std::endl; + if (current_v_protrusion < v_protrusion) { + // std::cout << "New v_protrusion winner of " << current_v_protrusion << std::endl; + v_protrusion = current_v_protrusion; + v_protrusion_point = {v.X(), v.Y(), v.Z()}; + } + } + } + } + } + + if (v_protrusion != std::numeric_limits::infinity()) { + if (v_protrusion > protrusion) { + std::cout << "New actual protrusion winner of " << v_protrusion << std::endl; + protrusion = v_protrusion; + protrusion_point = v_protrusion_point; + } + } + } + } + + if (protrusion > 0.001) { + protrusion_distances_.push_back(protrusion); + protrusion_points_.push_back(protrusion_point); + return true; + } + return false; + } bool test(const TopoDS_Shape& A, const TopoDS_Shape& B, bool completely_within, double extend) const { if (extend > 0.) { @@ -143,6 +589,8 @@ namespace IfcGeom { // @todo this is ugly, embed this in the return type mutable std::vector distances_; mutable std::vector protrusion_distances_; + mutable std::vector> protrusion_points_; + mutable long long tri_count_ = 0; public: @@ -151,10 +599,77 @@ namespace IfcGeom { } void add(const T& t, const TopoDS_Shape& s) { + // Note that this function is also used elsewhere (e.g. boolean_utils.cpp) + // We have to triangulate it to make clash detection faster + BRepMesh_IncrementalMesh(s, 1.e-3, false, 0.5); + Bnd_Box b; BRepBndLib::AddClose(s, b); - add(t, b); + //add(t, b); + tree_.Add(t, b); shapes_[t] = s; + + Bnd_OBB obb; + BRepBndLib::AddOBB(s, obb); + obbs_[t] = obb; + + max_protrusions_[t] = std::min(std::min(obb.XHSize(), obb.YHSize()), obb.ZHSize()) / 2; + + BVH_BoxSet* boxset = new BVH_BoxSet(); + BRepExtrema_ShapeList shape_list; + + std::unordered_map faces; + + TopExp_Explorer exp_f; + int i = 0; + for (exp_f.Init(s, TopAbs_FACE); exp_f.More(); exp_f.Next()) { + shape_list.Append(exp_f.Current()); + + Bnd_Box aabb; + BRepBndLib::Add(exp_f.Current(), aabb); + double x, y, z, X, Y, Z; + aabb.Get(x, y, z, X, Y, Z); + const BVH_Box::BVH_VecNt min(x, y, z); + const BVH_Box::BVH_VecNt max(X, Y, Z); + BVH_Box bvhBox(min, max); + boxset->Add(i, bvhBox); + + faces[i] = TopoDS::Face(exp_f.Current()); + i++; + } + + /* Option 1: Builder? + BVH_Tree* bvh = new BVH_Tree(); + BVH_Box bvhBox2; // What's the point of this? + BVH_LinearBuilder builder; + builder.Build(boxset, bvh, bvhBox2); + */ + + /* Option 2: Box set works, but ends up still comparing over 17 billion tri pairs + const opencascade::handle>& bvh = boxset->BVH(); + */ + + // Option 3: Triangle set - down to 96 million pairs + BRepExtrema_TriangleSet triangle_set(shape_list); + const opencascade::handle>& bvh = triangle_set.BVH(); + + // Debug + /* + std::cout << "DEBUGG:" << std::endl; + for (int i=0; i select_box(const T& t, bool completely_within = false, double extend=-1.e-5) const { @@ -171,6 +686,7 @@ namespace IfcGeom { // Gap should work as well. b.SetGap(b.GetGap() + extend); + // Should this filter itself? (i.e. t) return select_box(b, completely_within); } @@ -211,6 +727,43 @@ namespace IfcGeom { } } + std::vector clash_intersection(const T& t, double tolerance = 0.002) const { + protrusion_distances_.clear(); + protrusion_points_.clear(); + + std::vector ts = select_box(t, true, 1e-5); + if (ts.empty()) { + return ts; + } + std::cout << "Passes box check" << std::endl; + + const TopoDS_Shape& A = shapes_.find(t)->second; + + std::vector ts_filtered; + ts_filtered.reserve(ts.size()); + std::cout << "We have to check X box results " << ts.size() << std::endl; + + int i = 0; + + typename std::vector::const_iterator it = ts.begin(); + for (it = ts.begin(); it != ts.end(); ++it) { + const TopoDS_Shape& B = shapes_.find(*it)->second; + // Don't clash against itself. + if (t == *it) { + continue; + } + i++; + std::cout << "Currently doing" << i << std::endl; + + if (test_intersection(t, *it, A, B)) { + ts_filtered.push_back(*it); + } + } + std::cout << "Tri count " << tri_count_ << std::endl; + + return ts_filtered; + } + std::vector select(const T& t, bool completely_within = false, double extend = 0.0) const { distances_.clear(); protrusion_distances_.clear(); @@ -240,6 +793,7 @@ namespace IfcGeom { std::vector select(const TopoDS_Shape& s, bool completely_within = false, double extend = -1.e-5) const { distances_.clear(); protrusion_distances_.clear(); + protrusion_points_.clear(); Bnd_Box bb; BRepBndLib::AddClose(s, bb); @@ -258,9 +812,11 @@ namespace IfcGeom { for (it = ts.begin(); it != ts.end(); ++it) { const TopoDS_Shape& B = shapes_.find(*it)->second; + /* if (test(s, B, completely_within, extend)) { ts_filtered.push_back(*it); } + */ } return ts_filtered; @@ -275,6 +831,7 @@ namespace IfcGeom { std::vector select(const gp_Pnt& p, double extend=0.0) const { distances_.clear(); protrusion_distances_.clear(); + protrusion_points_.clear(); std::vector ts = select_box(p, extend); if (ts.empty()) { @@ -322,6 +879,13 @@ namespace IfcGeom { tree_t tree_; map_t shapes_; + std::map obbs_; + std::map max_protrusions_; + //std::map*> bvhs_; + std::map>> bvhs_; + std::map*> boxsets_; + std::map triangle_sets_; + std::unordered_map> faces_; bool enable_face_styles_ = false; @@ -435,6 +999,10 @@ namespace IfcGeom { return protrusion_distances_; } + const std::vector>& protrusion_points() const { + return protrusion_points_; + } + std::vector select_ray(const gp_Pnt& p0, const gp_Dir& d, double length = 1000.) const { gp_Pnt p1 = p0.XYZ() + d.XYZ() * length; auto E = BRepBuilderAPI_MakeEdge(p0, p1).Edge(); diff --git a/src/ifcopenshell-python/ifcopenshell/geom/main.py b/src/ifcopenshell-python/ifcopenshell/geom/main.py index ad1a705e83..ca7a8ea5f9 100644 --- a/src/ifcopenshell-python/ifcopenshell/geom/main.py +++ b/src/ifcopenshell-python/ifcopenshell/geom/main.py @@ -181,6 +181,15 @@ class tree(ifcopenshell_wrapper.tree): args.append(kwargs.get("extend", -1.0e-5)) return [entity_instance(e) for e in ifcopenshell_wrapper.tree.select_box(*args)] + def clash_intersection(self, value, tolerance=0.002): + def unwrap(value): + if isinstance(value, entity_instance): + return value.wrapped_data + return value + + args = [self, unwrap(value), tolerance] + return [entity_instance(e) for e in ifcopenshell_wrapper.tree.clash_intersection(*args)] + def create_shape(settings, inst, repr=None): """ diff --git a/src/ifcwrap/IfcGeomWrapper.i b/src/ifcwrap/IfcGeomWrapper.i index 1e37fb9819..6289332802 100644 --- a/src/ifcwrap/IfcGeomWrapper.i +++ b/src/ifcwrap/IfcGeomWrapper.i @@ -106,6 +106,14 @@ return IfcGeom_tree_vector_to_list(ps); } + aggregate_of_instance::ptr clash_intersection(IfcUtil::IfcBaseClass* e, double tolerance = 0.002) const { + if (!e->declaration().is("IfcProduct")) { + throw IfcParse::IfcException("Instance should be an IfcProduct"); + } + std::vector ps = $self->clash_intersection((IfcUtil::IfcBaseEntity*)e, tolerance); + return IfcGeom_tree_vector_to_list(ps); + } + aggregate_of_instance::ptr select(IfcUtil::IfcBaseClass* e, bool completely_within = false, double extend = 0.0) const { if (!e->declaration().is("IfcProduct")) { throw IfcParse::IfcException("Instance should be an IfcProduct"); diff --git a/src/ifcwrap/IfcPython.i b/src/ifcwrap/IfcPython.i index 4c259c68a4..6d4c801de0 100644 --- a/src/ifcwrap/IfcPython.i +++ b/src/ifcwrap/IfcPython.i @@ -206,4 +206,5 @@ namespace std { %template(float_array_3) array; + %template(vector_float_array_3) vector>; }