From 8ce4bbdf6194649cbaca4dff2ec1f53342cf5d39 Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Thu, 16 Feb 2017 15:54:34 +0100 Subject: [PATCH] Adaptive tolerance boolean ops --- src/ifcgeom/IfcGeom.h | 9 +++ src/ifcgeom/IfcGeomFunctions.cpp | 108 +++++++++++++++++++++++++++++++ src/ifcgeom/IfcGeomShapes.cpp | 86 +++++------------------- 3 files changed, 133 insertions(+), 70 deletions(-) diff --git a/src/ifcgeom/IfcGeom.h b/src/ifcgeom/IfcGeom.h index c485d6e347..c14cb0bd00 100644 --- a/src/ifcgeom/IfcGeom.h +++ b/src/ifcgeom/IfcGeom.h @@ -44,6 +44,7 @@ inline static bool ALMOST_THE_SAME(const T& a, const T& b, double tolerance=ALMO #include #include #include +#include #include "../ifcparse/IfcParse.h" #include "../ifcparse/IfcUtil.h" @@ -181,6 +182,14 @@ public: bool split_solid_by_surface(const TopoDS_Shape&, const Handle_Geom_Surface&, TopoDS_Shape&, TopoDS_Shape&); bool split_solid_by_shell(const TopoDS_Shape&, const TopoDS_Shape& s, TopoDS_Shape&, TopoDS_Shape&); +#if OCC_VERSION_HEX < 0x60900 + bool boolean_operation(const TopoDS_Shape&, const TopTools_ListOfShape&, BOPAlgo_Operation, TopoDS_Shape&); + bool boolean_operation(const TopoDS_Shape&, const TopoDS_Shape&, BOPAlgo_Operation, TopoDS_Shape&); +#else + bool boolean_operation(const TopoDS_Shape&, const TopTools_ListOfShape&, BOPAlgo_Operation, TopoDS_Shape&, double fuzziness = -1.); + bool boolean_operation(const TopoDS_Shape&, const TopoDS_Shape&, BOPAlgo_Operation, TopoDS_Shape&, double fuzziness = -1.); +#endif + const Handle_Geom_Curve intersect(const Handle_Geom_Surface&, const Handle_Geom_Surface&); const Handle_Geom_Curve intersect(const Handle_Geom_Surface&, const TopoDS_Face&); const Handle_Geom_Curve intersect(const TopoDS_Face&, const Handle_Geom_Surface&); diff --git a/src/ifcgeom/IfcGeomFunctions.cpp b/src/ifcgeom/IfcGeomFunctions.cpp index aeb20536c0..0614fad66a 100644 --- a/src/ifcgeom/IfcGeomFunctions.cpp +++ b/src/ifcgeom/IfcGeomFunctions.cpp @@ -86,6 +86,7 @@ #include #include #include +#include #include @@ -95,6 +96,7 @@ #include #include +#include #include @@ -2413,3 +2415,109 @@ TopoDS_Shape IfcGeom::Kernel::apply_transformation(const TopoDS_Shape& s, const } } +#if OCC_VERSION_HEX < 0x60900 +bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a, const TopTools_ListOfShape& b, BOPAlgo_Operation op, TopoDS_Shape& result) { + result = a; + TopTools_ListIteratorOfListOfShape it(b); + for (; it.More(); it.Next()) { + TopoDS_Shape r; + if (!boolean_operation(result, it.Value(), op, r)) { + return false; + } + result = r; + } + return true; +} +bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a, const TopoDS_Shape& b, BOPAlgo_Operation op, TopoDS_Shape& result) { + bool succesful = true; + BRepAlgoAPI_BooleanOperation* builder; + if (op == BOPAlgo_CUT) { + builder = new BRepAlgoAPI_Cut(a, b); + } else if (op == BOPAlgo_COMMON) { + builder = new BRepAlgoAPI_Common(a, b); + } else if (op == BOPAlgo_FUSE) { + builder = new BRepAlgoAPI_Fuse(a, b); + } else { + return false; + } + if (builder->IsDone()) { + TopoDS_Shape r = *builder; + succesful = BRepCheck_Analyzer(r).IsValid() != 0; + if (succesful) { + result = r; + + ShapeFix_Shape fix(result); + try { + fix.Perform(); + result = fix.Shape(); + } catch (...) { + Logger::Message(Logger::LOG_WARNING, "Shape healing failed on boolean result"); + } + + } else { + // Increase tolerance max 3 times until succesful + TopoDS_Shape a2 = a; + TopoDS_Shape b2 = b; + ShapeAnalysis_ShapeTolerance tolerance; + const double t1 = tolerance.Tolerance(a, 1) * 10.; + const double t2 = tolerance.Tolerance(b, 1) * 10.; + if (((std::max)(t1, t2) + 1e-15) > getValue(GV_PRECISION) * 1000.) { + return false; + } + apply_tolerance(a2, t1); + apply_tolerance(b2, t2); + succesful = boolean_operation(a2, b2, op, result); + } + } + delete builder; + return succesful; +} +#else +bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a, const TopTools_ListOfShape& b, BOPAlgo_Operation op, TopoDS_Shape& result, double fuzziness) { + bool success = false; + BRepAlgoAPI_BooleanOperation* builder; + if (op == BOPAlgo_CUT) { + builder = new BRepAlgoAPI_Cut(); + } else if (op == BOPAlgo_COMMON) { + builder = new BRepAlgoAPI_Common(); + } else if (op == BOPAlgo_FUSE) { + builder = new BRepAlgoAPI_Fuse(); + } else { + return false; + } + if (fuzziness < 0.) { + fuzziness = getValue(GV_PRECISION); + } + TopTools_ListOfShape s1s; + s1s.Append(a); + builder->SetFuzzyValue(fuzziness); + builder->SetArguments(s1s); + builder->SetTools(b); + builder->Build(); + if (builder->IsDone()) { + TopoDS_Shape r = *builder; + success = BRepCheck_Analyzer(r).IsValid() != 0; + if (success) { + result = r; + ShapeFix_Shape fix(result); + try { + fix.Perform(); + result = fix.Shape(); + } catch (...) { + Logger::Message(Logger::LOG_WARNING, "Shape healing failed on boolean result"); + } + } + } + delete builder; + if (!success) { + return boolean_operation(a, b, op, result, fuzziness * 10.); + } + return success; +} + +bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a, const TopoDS_Shape& b, BOPAlgo_Operation op, TopoDS_Shape& result, double fuzziness) { + TopTools_ListOfShape bs; + bs.Append(b); + return boolean_operation(a, bs, op, result, fuzziness); +} +#endif \ No newline at end of file diff --git a/src/ifcgeom/IfcGeomShapes.cpp b/src/ifcgeom/IfcGeomShapes.cpp index 32ac0bd88a..ee2071b656 100644 --- a/src/ifcgeom/IfcGeomShapes.cpp +++ b/src/ifcgeom/IfcGeomShapes.cpp @@ -538,43 +538,22 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcBooleanResult* l, TopoDS_Shape return true; */ + BOPAlgo_Operation occ_op; if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_DIFFERENCE) { + occ_op = BOPAlgo_CUT; + } else if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_INTERSECTION) { + occ_op = BOPAlgo_COMMON; + } else if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_UNION) { + occ_op = BOPAlgo_FUSE; + } else { + return false; + } - bool valid_cut = false; + bool valid_result = boolean_operation(s1, s2, occ_op, shape); -#if OCC_VERSION_HEX < 0x60900 - BRepAlgoAPI_Cut brep_cut(s1, s2); -#else - BRepAlgoAPI_Cut brep_cut; - TopTools_ListOfShape s1s; - s1s.Append(s1); - TopTools_ListOfShape s2s; - s2s.Append(s2); - brep_cut.SetFuzzyValue(getValue(GV_PRECISION)); - brep_cut.SetArguments(s1s); - brep_cut.SetTools(s2s); - brep_cut.Build(); -#endif - - if ( brep_cut.IsDone() ) { - TopoDS_Shape result = brep_cut; - - ShapeFix_Shape fix(result); - try { - fix.Perform(); - result = fix.Shape(); - } catch (...) { - Logger::Message(Logger::LOG_WARNING, "Shape healing failed on boolean result", l->entity); - } - - bool is_valid = BRepCheck_Analyzer(result).IsValid() != 0; - if ( is_valid ) { - shape = result; - valid_cut = true; - } - } - - if ( valid_cut ) { + if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_DIFFERENCE) { + // In case of a subtraction, a check on volume is performed. + if (valid_result) { const double volume_after_subtraction = shape_volume(shape); if ( ALMOST_THE_SAME(first_operand_volume,volume_after_subtraction) ) Logger::Message(Logger::LOG_WARNING,"Subtraction yields unchanged volume:",l->entity); @@ -582,43 +561,10 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcBooleanResult* l, TopoDS_Shape Logger::Message(Logger::LOG_ERROR,"Failed to process subtraction:",l->entity); shape = s1; } - + // NB: After issuing error the first operand is returned! return true; - - } else if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_UNION) { - - BRepAlgoAPI_Fuse brep_fuse(s1,s2); - if ( brep_fuse.IsDone() ) { - TopoDS_Shape result = brep_fuse; - - ShapeFix_Shape fix(result); - fix.Perform(); - result = fix.Shape(); - - bool is_valid = BRepCheck_Analyzer(result).IsValid() != 0; - if ( is_valid ) { - shape = result; - return true; - } - } - - } else if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_INTERSECTION) { - - BRepAlgoAPI_Common brep_common(s1,s2); - if ( brep_common.IsDone() ) { - TopoDS_Shape result = brep_common; - - ShapeFix_Shape fix(result); - fix.Perform(); - result = fix.Shape(); - - bool is_valid = BRepCheck_Analyzer(result).IsValid() != 0; - if ( is_valid ) { - shape = result; - return true; - } - } - + } else { + return valid_result; } return false; }