mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 09:48:32 +00:00
Adaptive tolerance boolean ops
This commit is contained in:
@@ -44,6 +44,7 @@ inline static bool ALMOST_THE_SAME(const T& a, const T& b, double tolerance=ALMO
|
||||
#include <gp_Pln.hxx>
|
||||
#include <TColgp_SequenceOfPnt.hxx>
|
||||
#include <TopTools_ListOfShape.hxx>
|
||||
#include <BOPAlgo_Operation.hxx>
|
||||
|
||||
#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&);
|
||||
|
||||
@@ -86,6 +86,7 @@
|
||||
#include <BRepAlgoAPI_Cut.hxx>
|
||||
#include <BRepAlgoAPI_Fuse.hxx>
|
||||
#include <BRepAlgoAPI_Common.hxx>
|
||||
#include <BRepAlgoAPI_BooleanOperation.hxx>
|
||||
|
||||
#include <BRepAlgo_NormalProjection.hxx>
|
||||
|
||||
@@ -95,6 +96,7 @@
|
||||
|
||||
#include <ShapeAnalysis_Curve.hxx>
|
||||
#include <ShapeAnalysis_Surface.hxx>
|
||||
#include <ShapeAnalysis_ShapeTolerance.hxx>
|
||||
|
||||
#include <BRepFilletAPI_MakeFillet2d.hxx>
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user