diff --git a/src/Mod/PartDesign/App/FeatureHole.cpp b/src/Mod/PartDesign/App/FeatureHole.cpp index 440761fa4d..0fe227c413 100644 --- a/src/Mod/PartDesign/App/FeatureHole.cpp +++ b/src/Mod/PartDesign/App/FeatureHole.cpp @@ -1882,30 +1882,77 @@ App::DocumentObjectExecReturn* Hole::execute() // we reuse the name protoHole (only now it is threaded) protoHole = mkFuse.Shape(); } + std::vector holes; + auto compound = findHoles(holes, profileshape, protoHole); - TopoDS_Compound holes = findHoles(profileshape.getShape(), protoHole); - this->AddSubShape.setValue(holes); + TopoShape result(0,getDocument()->getStringHasher()); - // For some reason it is faster to do the cut through a BooleanOperation. - BRepAlgoAPI_Cut mkBool(base.getShape(), holes); - if (!mkBool.IsDone()) { - return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Boolean operation failed")); - } - TopoDS_Shape result = mkBool.Shape(); + // set the subtractive shape property for later usage in e.g. pattern + this->AddSubShape.setValue(compound); + if (isRecomputePaused()) + return App::DocumentObject::StdReturn; - - // We have to get the solids (fuse sometimes creates compounds) - base = getSolid(result); - if (base.isNull()) - return new App::DocumentObjectExecReturn(QT_TRANSLATE_NOOP("Exception", "Resulting shape is not a solid")); - base = refineShapeIfActive(base); - - if (!isSingleSolidRuleSatisfied(base.getShape())) { - return new App::DocumentObjectExecReturn( - QT_TRANSLATE_NOOP("Exception", "Result has multiple solids: that is not currently supported.")); + if (base.isNull()) { + Shape.setValue(compound); + return App::DocumentObject::StdReturn; } - this->Shape.setValue(base); + // First try cuting with compound which will be faster as it is done in + // parallel + bool retry = true; + const char *maker; + switch (getAddSubType()) { + case Additive: + maker = Part::OpCodes::Fuse; + break; + case Intersecting: + maker = Part::OpCodes::Common; + break; + default: + maker = Part::OpCodes::Cut; + } + try { + if (base.isNull()) + result = compound; + else + result.makEBoolean(maker, {base,compound}); + result = getSolid(result); + retry = false; + } catch (Standard_Failure & e) { + FC_WARN(getFullName() << ": boolean operation with compound failed (" + << e.GetMessageString() << "), retry..."); + } catch (Base::Exception & e) { + FC_WARN(getFullName() << ": boolean operation with compound failed (" + << e.what() << "), retry..."); + } + + if (retry) { + int i = 0; + for (auto & hole : holes) { + ++i; + try { + result.makEBoolean(maker, {base,hole}); + } catch (Standard_Failure &) { + std::string msg(QT_TRANSLATE_NOOP("Exception", "Boolean operation failed on profile Edge")); + msg += std::to_string(i); + return new App::DocumentObjectExecReturn(msg.c_str()); + } catch (Base::Exception &e) { + e.ReportException(); + std::string msg(QT_TRANSLATE_NOOP("Exception", "Boolean operataion failed on profile Edge")); + msg += std::to_string(i); + return new App::DocumentObjectExecReturn(msg.c_str()); + } + base = getSolid(result); + if (base.isNull()) { + std::string msg(QT_TRANSLATE_NOOP("Exception", "Boolean operataion produced non-solid on profile Edge")); + msg += std::to_string(i); + return new App::DocumentObjectExecReturn(msg.c_str()); + } + } + result = base; + } + + this->Shape.setValue(result); return App::DocumentObject::StdReturn; } @@ -1979,54 +2026,43 @@ gp_Vec Hole::computePerpendicular(const gp_Vec& zDir) const xDir.Normalize(); return xDir; } - -TopoDS_Compound Hole::findHoles(const TopoDS_Shape& profileshape, - const TopoDS_Shape& protohole) const +TopoShape Hole::findHoles(std::vector &holes, + const TopoShape& profileshape, + const TopoDS_Shape& protoHole) const { - BRep_Builder builder; - TopoDS_Compound holes; - builder.MakeCompound(holes); - TopTools_IndexedMapOfShape edgeMap; - TopExp::MapShapes(profileshape, TopAbs_EDGE, edgeMap); - std::vector holePointsList; - for (int i = 1; i <= edgeMap.Extent(); i++) { - bool dupCenter = false; + TopoShape result(0,getDocument()->getStringHasher()); + + int i = 0; + for(const auto &profileEdge : profileshape.getSubTopoShapes(TopAbs_EDGE)) { + ++i; Standard_Real c_start; Standard_Real c_end; - TopoDS_Edge edge = TopoDS::Edge(edgeMap(i)); + TopoDS_Edge edge = TopoDS::Edge(profileEdge.getShape()); Handle(Geom_Curve) c = BRep_Tool::Curve(edge, c_start, c_end); // Circle? - if (c.IsNull() || c->DynamicType() != STANDARD_TYPE(Geom_Circle)) { + if (c->DynamicType() != STANDARD_TYPE(Geom_Circle)) continue; - } Handle(Geom_Circle) circle = Handle(Geom_Circle)::DownCast(c); gp_Pnt loc = circle->Axis().Location(); - for (auto holePoint : holePointsList) { - if (holePoint.IsEqual(loc, Precision::Confusion())) { - Base::Console().Log( - "PartDesign_Hole - There is a duplicate circle/curve center at %.2f : %.2f " - ": %.2f therefore not passing parameter\n", - loc.X(), - loc.Y(), - loc.Z()); - dupCenter = true; - } - } - if (!dupCenter) { - holePointsList.push_back(loc); - gp_Trsf localSketchTransformation; - localSketchTransformation.SetTranslation(gp_Pnt(0, 0, 0), loc); - TopoDS_Shape copy = protohole; - copy.Move(localSketchTransformation); - builder.Add(holes, copy); - } + gp_Trsf localSketchTransformation; + localSketchTransformation.SetTranslation( gp_Pnt( 0, 0, 0 ), + gp_Pnt(loc.X(), loc.Y(), loc.Z()) ); + + Part::ShapeMapper mapper; + mapper.populate(true, profileEdge, TopoShape(protoHole).getSubTopoShapes(TopAbs_FACE)); + + TopoShape hole(-getID(), getDocument()->getStringHasher()); + hole.makESHAPE(protoHole, mapper, {profileEdge}); + + // transform and generate element map. + hole = hole.makETransform(localSketchTransformation); + holes.push_back(hole); } - - return holes; + return TopoShape().makECompound(holes); } TopoDS_Shape Hole::makeThread(const gp_Vec& xDir, const gp_Vec& zDir, double length) diff --git a/src/Mod/PartDesign/App/FeatureHole.h b/src/Mod/PartDesign/App/FeatureHole.h index 6460874bba..662dde2b6c 100644 --- a/src/Mod/PartDesign/App/FeatureHole.h +++ b/src/Mod/PartDesign/App/FeatureHole.h @@ -224,7 +224,7 @@ private: void rotateToNormal(const gp_Dir& helixAxis, const gp_Dir& normalAxis, TopoDS_Shape& helixShape) const; gp_Vec computePerpendicular(const gp_Vec&) const; TopoDS_Shape makeThread(const gp_Vec&, const gp_Vec&, double); - TopoDS_Compound findHoles(const TopoDS_Shape& profileshape, const TopoDS_Shape& protohole) const; + TopoShape findHoles(std::vector &holes, const TopoShape& profileshape, const TopoDS_Shape& protohole) const; // helpers for nlohmann json friend void from_json(const nlohmann::json &j, CounterBoreDimension &t);