diff --git a/src/Mod/PartDesign/App/FeatureTransformed.cpp b/src/Mod/PartDesign/App/FeatureTransformed.cpp index d24a0199f205..9e638f32d7fd 100644 --- a/src/Mod/PartDesign/App/FeatureTransformed.cpp +++ b/src/Mod/PartDesign/App/FeatureTransformed.cpp @@ -36,6 +36,9 @@ # include #endif +#ifndef FC_DEBUG +#include +#endif #include "FeatureTransformed.h" #include "FeatureMultiTransform.h" @@ -198,7 +201,11 @@ short Transformed::mustExecute() const App::DocumentObjectExecReturn *Transformed::execute(void) { - rejected.clear(); + +#ifndef FC_DEBUG + std::clock_t start0; + start0 = std::clock(); +#endif std::vector originals = Originals.getValues(); if (originals.empty()) // typically InsideMultiTransform @@ -246,10 +253,6 @@ App::DocumentObjectExecReturn *Transformed::execute(void) supportShape.setTransform(Base::Matrix4D()); TopoDS_Shape support = supportShape.getShape(); - typedef std::set::const_iterator> trsf_it; - typedef std::map rej_it_map; - rej_it_map nointersect_trsfms; - // NOTE: It would be possible to build a compound from all original addShapes/subShapes and then // transform the compounds as a whole. But we choose to apply the transformations to each // Original separately. This way it is easier to discover what feature causes a fuse/cut @@ -276,132 +279,93 @@ App::DocumentObjectExecReturn *Transformed::execute(void) else { return new App::DocumentObjectExecReturn("Only additive and subtractive features can be transformed"); } + TopoDS_Shape origShape = fuseShape.isNull()?cutShape.getShape():fuseShape.getShape(); + + TopoDS_Shape current = support; - // Transform the add/subshape and collect the resulting shapes for overlap testing - /*typedef std::vector::const_iterator> trsf_it_vec; - trsf_it_vec v_transformations; - std::vector v_transformedShapes;*/ + BRep_Builder builder; + TopoDS_Compound compShape; + builder.MakeCompound(compShape); + std::vector shapes; + bool overlapping = false; std::vector::const_iterator t = transformations.begin(); ++t; // Skip first transformation, which is always the identity transformation for (; t != transformations.end(); ++t) { // Make an explicit copy of the shape because the "true" parameter to BRepBuilderAPI_Transform // seems to be pretty broken - BRepBuilderAPI_Copy copy(fuseShape.isNull()?cutShape.getShape():fuseShape.getShape()); + BRepBuilderAPI_Copy copy(origShape); + shape = copy.Shape(); - if (shape.IsNull()) - return new App::DocumentObjectExecReturn("Transformed: Linked shape object is empty"); - - BRepBuilderAPI_Transform mkTrf(shape, *t, false); // No need to copy, now - if (!mkTrf.IsDone()) - return new App::DocumentObjectExecReturn("Transformation failed", (*o)); - - shape = mkTrf.Shape(); - try { - // Intersection checking for additive shape is redundant. - // Because according to CheckIntersection() source code, it is - // implemented using fusion and counting of the resulting - // solid, which will be done in the following modeling step - // anyway. - // - // There is little reason for doing intersection checking on - // subtractive shape either, because it does not produce - // multiple solids. - // - // if (!Part::checkIntersection(support, mkTrf.Shape(), false, true)) { - - - TopoDS_Shape current = support; - - if (!fuseShape.isNull()) { - // We cannot wait to fuse a transformation with the support until all the transformations are done, - // because the "support" potentially changes with every transformation, basically when checking intersection - // above you need: - // 1. The original support - // 2. Any extra support gained by any previous transformation of any previous feature (multi-feature transform) - // 3. Any extra support gained by any previous transformation of this feature (feature multi-trasform) - // - // Therefore, if the transformation succeeded, then we fuse it with the support now, before checking the intersection - // of the next transformation. - - /*v_transformations.push_back(t); - v_transformedShapes.push_back(mkTrf.Shape());*/ - - // Note: Transformations that do not intersect the support are ignored in the overlap tests - - //insert scheme here. - /*TopoDS_Compound compoundTool; - std::vector individualTools; - divideTools(v_transformedShapes, individualTools, compoundTool);*/ - - // Fuse/Cut the compounded transformed shapes with the support - //TopoDS_Shape result; - - BRepAlgoAPI_Fuse mkFuse(current, shape); - if (!mkFuse.IsDone()) - return new App::DocumentObjectExecReturn("Fusion with support failed", *o); - - if(Part::TopoShape(current).countSubShapes(TopAbs_SOLID) - != Part::TopoShape(mkFuse.Shape()).countSubShapes(TopAbs_SOLID)) - { -#ifdef FC_DEBUG // do not write this in release mode because a message appears already in the task view - Base::Console().Warning("Transformed shape does not intersect support %s: Removed\n", (*o)->getNameInDocument()); + + shape.Move(*t); + shapes.emplace_back(shape); + builder.Add(compShape, shape); + + overlapping = overlapping || (countSolids(TopoShape(origShape).fuse(shape))==1); + + } + + TopoDS_Shape toolShape; + + if (overlapping) { + +#ifndef FC_DEBUG + Base::Console().Message("Transformed: Overlapping feature mode (fusing tool shapes)\n"); #endif - nointersect_trsfms[*o].insert(t); - continue; - } - // we have to get the solids (fuse sometimes creates compounds) - current = this->getSolid(mkFuse.Shape()); - // lets check if the result is a solid - if (current.IsNull()) - return new App::DocumentObjectExecReturn("Resulting shape is not a solid", *o); - - if (!cutShape.isNull()) { - BRepBuilderAPI_Copy copy(cutShape.getShape()); - shape = copy.Shape(); - if (shape.IsNull()) - return new App::DocumentObjectExecReturn("Transformed: Linked shape object is empty"); - - BRepBuilderAPI_Transform mkTrf(shape, *t, false); // No need to copy, now - if (!mkTrf.IsDone()) - return new App::DocumentObjectExecReturn("Transformation failed", (*o)); - shape = mkTrf.Shape(); - } - } - if (!cutShape.isNull()) { - BRepAlgoAPI_Cut mkCut(current, shape); - if (!mkCut.IsDone()) - return new App::DocumentObjectExecReturn("Cut out of support failed", *o); - current = mkCut.Shape(); - } - support = current; // Use result of this operation for fuse/cut of next original - } catch (Standard_Failure& e) { - // Note: Ignoring this failure is probably pointless because if the intersection check fails, the later - // fuse operation of the transformation result will also fail - - std::string msg("Transformation: Intersection check failed"); - if (e.GetMessageString() != NULL) - msg += std::string(": '") + e.GetMessageString() + "'"; - return new App::DocumentObjectExecReturn(msg.c_str()); + + toolShape = TopoShape(origShape).fuse(shapes, Precision::Confusion()); + } else { + +#ifndef FC_DEBUG + Base::Console().Message("Transformed: Non-Overlapping feature mode (compound of tool shapes)\n"); +#endif + + toolShape = compShape; + } + + if (!fuseShape.isNull()) { + std::unique_ptr mkBool(new BRepAlgoAPI_Fuse(current, toolShape)); + if (!mkBool->IsDone()) { + std::stringstream error; + error << "Boolean operation failed"; + return new App::DocumentObjectExecReturn(error.str()); + } + current = mkBool->Shape(); + } else { + std::unique_ptr mkBool(new BRepAlgoAPI_Cut(current, toolShape)); + if (!mkBool->IsDone()) { + std::stringstream error; + error << "Boolean operation failed"; + return new App::DocumentObjectExecReturn(error.str()); } + current = mkBool->Shape(); + } + try {} catch (Standard_Failure& e) { + // Note: Ignoring this failure is probably pointless because if the intersection check fails, the later + // fuse operation of the transformation result will also fail + + std::string msg("Transformation: Intersection check failed"); + if (e.GetMessageString() != NULL) + msg += std::string(": '") + e.GetMessageString() + "'"; + return new App::DocumentObjectExecReturn(msg.c_str()); } + + support = current; // Use result of this operation for fuse/cut of next original } - support = refineShapeIfActive(support); - for (rej_it_map::const_iterator it = nointersect_trsfms.begin(); it != nointersect_trsfms.end(); ++it) - for (trsf_it::const_iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2) - rejected[it->first].push_back(**it2); + support = refineShapeIfActive(support); int solidCount = countSolids(support); if (solidCount > 1) { - return new App::DocumentObjectExecReturn("Transformed: Result has multiple solids. This is not supported at this time."); + Base::Console().Warning("Transformed: Result has multiple solids. Only keeping the first.\n"); } - this->Shape.setValue(getSolid(support)); + this->Shape.setValue(getSolid(support)); // picking the first solid - if (rejected.size() > 0) { - return new App::DocumentObjectExecReturn("Transformation failed"); - } +#ifndef FC_DEBUG + Base::Console().Message("Transformed: Elapsed CPU time: %f s\n", (std::clock() - start0 ) / (double)(CLOCKS_PER_SEC)); +#endif return App::DocumentObject::StdReturn; } @@ -412,6 +376,9 @@ TopoDS_Shape Transformed::refineShapeIfActive(const TopoDS_Shape& oldShape) cons try { Part::BRepBuilderAPI_RefineModel mkRefine(oldShape); TopoDS_Shape resShape = mkRefine.Shape(); + if (!TopoShape(resShape).isClosed()) { + return oldShape; + } return resShape; } catch (Standard_Failure&) {