mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-13 19:07:57 +00:00
ifcgeom: don't silently accept implausible empty opening subtractions
When an opening subtraction collapses to an empty shape, the result passed every post-boolean sanity check (validity, manifoldness, interference) because those checks are vacuous on an empty compound, so the element silently disappeared from the output with no log message. Seen in issue 5779 where hollow pipe members voided by two non-manifold brep openings at their ends vanished from the converted glTF. An empty cut result is now only accepted when the subtraction operands can plausibly consume the whole first operand, meaning their combined bounding box encloses it and their total volume is at least as large. Otherwise the result is discarded, which engages the existing fuzziness retry and, ultimately, the unsubtracted fallback with the GEO192 error, matching how other subtraction failures already behave. Genuinely fully voided elements still resolve to empty output. Fixes #5779 Generated with the assistance of an AI coding tool.
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
#include <TopExp.hxx>
|
||||
#include <TopoDS.hxx>
|
||||
#include <Bnd_Box.hxx>
|
||||
#include <BRepBndLib.hxx>
|
||||
#include <Extrema_ExtPC.hxx>
|
||||
#include <Geom_Plane.hxx>
|
||||
#include <Geom_BSplineCurve.hxx>
|
||||
@@ -832,6 +833,34 @@ bool IfcGeom::util::points_on_planar_face_generator::operator()(gp_Pnt& p) {
|
||||
}
|
||||
|
||||
|
||||
namespace {
|
||||
// An empty subtraction result is only plausible when the second operands
|
||||
// can jointly cover the first operand, both in bounding box and in volume. #5779
|
||||
bool empty_cut_result_plausible(double tolerance, const TopoDS_Shape& a, const NCollection_List<TopoDS_Shape>& b) {
|
||||
Bnd_Box A, B;
|
||||
BRepBndLib::Add(a, A);
|
||||
double volume_b = 0.;
|
||||
for (NCollection_List<TopoDS_Shape>::Iterator it(b); it.More(); it.Next()) {
|
||||
BRepBndLib::Add(it.Value(), B);
|
||||
volume_b += std::abs(IfcGeom::util::shape_volume(it.Value()));
|
||||
}
|
||||
if (A.IsVoid() || B.IsVoid()) {
|
||||
return false;
|
||||
}
|
||||
B.Enlarge(tolerance);
|
||||
double axmin, aymin, azmin, axmax, aymax, azmax;
|
||||
double bxmin, bymin, bzmin, bxmax, bymax, bzmax;
|
||||
A.Get(axmin, aymin, azmin, axmax, aymax, azmax);
|
||||
B.Get(bxmin, bymin, bzmin, bxmax, bymax, bzmax);
|
||||
if (!(bxmin <= axmin && bymin <= aymin && bzmin <= azmin &&
|
||||
axmax <= bxmax && aymax <= bymax && azmax <= bzmax)) {
|
||||
return false;
|
||||
}
|
||||
const double volume_a = IfcGeom::util::shape_volume(a);
|
||||
return volume_b >= volume_a * (1. - 1.e-6);
|
||||
}
|
||||
}
|
||||
|
||||
bool IfcGeom::util::boolean_operation(const boolean_settings& settings, const TopoDS_Shape& a_input, const NCollection_List<TopoDS_Shape>& b_input, BOPAlgo_Operation op, TopoDS_Shape& result, double fuzziness) {
|
||||
using namespace std::string_literals;
|
||||
|
||||
@@ -1332,7 +1361,10 @@ bool IfcGeom::util::boolean_operation(const boolean_settings& settings, const To
|
||||
int result_n_faces = count(r, TopAbs_FACE);
|
||||
int first_op_n_faces = count(a, TopAbs_FACE);
|
||||
|
||||
if (op == BOPAlgo_CUT && has_open_shells && all_faces_included_in_result && result_n_faces > first_op_n_faces) {
|
||||
if (op == BOPAlgo_CUT && result_n_faces == 0 && first_op_n_faces > 0 && !empty_cut_result_plausible(settings.precision + fuzziness, a, b)) {
|
||||
success = false;
|
||||
settings.log().Notice("GEO", 405, "Empty subtraction result discarded because second operands do not enclose first operand");
|
||||
} else if (op == BOPAlgo_CUT && has_open_shells && all_faces_included_in_result && result_n_faces > first_op_n_faces) {
|
||||
success = false;
|
||||
settings.log().Notice("GEO", 149, "Boolean result discarded because subtractions results in only the addition of faces");
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user