diff --git a/src/Mod/Part/App/ExtrusionHelper.cpp b/src/Mod/Part/App/ExtrusionHelper.cpp index c6128cce1b..2b40a7a637 100644 --- a/src/Mod/Part/App/ExtrusionHelper.cpp +++ b/src/Mod/Part/App/ExtrusionHelper.cpp @@ -477,3 +477,234 @@ void ExtrusionHelper::createTaperedPrismOffset(TopoDS_Wire sourceWire, } } + +static TopoShape makEDraftUsingPipe(const std::vector &_wires, + App::StringHasherRef hasher) +{ + std::vector shells; + std::vector frontwires, backwires; + + if (_wires.size() < 2) + throw Base::CADKernelError("Not enough wire section"); + + std::vector wires; + wires.reserve(_wires.size()); + for (auto &wire : _wires) { + // Make a copy to work around OCCT bug on offset circular shapes + wires.push_back(wire.makECopy()); + } + GeomLineSegment line; + Base::Vector3d pstart, pend; + wires.front().getCenterOfGravity(pstart); + gp_Pln pln; + if (wires.back().findPlane(pln)) { + auto dir = pln.Position().Direction(); + auto base = pln.Location(); + pend = pstart; + pend.ProjectToPlane(Base::Vector3d(base.X(), base.Y(), base.Z()), + Base::Vector3d(dir.X(), dir.Y(), dir.Z())); + } else + wires.back().getCenterOfGravity(pend); + line.setPoints(pstart, pend); + + BRepBuilderAPI_MakeWire mkWire(TopoDS::Edge(line.toShape())); + BRepOffsetAPI_MakePipeShell mkPS(mkWire.Wire()); + mkPS.SetTolerance(Precision::Confusion()); + mkPS.SetTransitionMode(BRepBuilderAPI_Transformed); + mkPS.SetMode(false); + + for (auto &wire : wires) + mkPS.Add(TopoDS::Wire(wire.getShape())); + + if (!mkPS.IsReady()) + throw Base::CADKernelError("Shape could not be built"); + + TopoShape result(0,hasher); + result.makEShape(mkPS,wires); + + if (!mkPS.Shape().Closed()) { + // shell is not closed - use simulate to get the end wires + TopTools_ListOfShape sim; + mkPS.Simulate(2, sim); + + TopoShape front(sim.First()); + if(front.countSubShapes(TopAbs_EDGE)==wires.front().countSubShapes(TopAbs_EDGE)) { + front = wires.front(); + front.setShape(sim.First(),false); + }else + front.Tag = -wires.front().Tag; + TopoShape back(sim.Last()); + if(back.countSubShapes(TopAbs_EDGE)==wires.back().countSubShapes(TopAbs_EDGE)) { + back = wires.back(); + back.setShape(sim.Last(),false); + }else + back.Tag = -wires.back().Tag; + + // build the end faces, sew the shell and build the final solid + front = front.makEFace(); + back = back.makEFace(); + + BRepBuilderAPI_Sewing sewer; + sewer.SetTolerance(Precision::Confusion()); + sewer.Add(front.getShape()); + sewer.Add(back.getShape()); + sewer.Add(result.getShape()); + + sewer.Perform(); + result = result.makEShape(sewer); + } + + result = result.makESolid(); + + BRepClass3d_SolidClassifier SC(result.getShape()); + SC.PerformInfinitePoint(Precision::Confusion()); + if (SC.State() == TopAbs_IN) { + result.setShape(result.getShape().Reversed(),false); + } + return result; +} + +void ExtrusionHelper::makEDraft(const ExtrusionParameters& params, + const TopoShape& _shape, + std::vector& drafts, + App::StringHasherRef hasher) +{ + double distanceFwd = tan(params.taperAngleFwd)*params.lengthFwd; + double distanceRev = tan(params.taperAngleRev)*params.lengthRev; + + gp_Vec vecFwd = gp_Vec(params.dir)*params.lengthFwd; + gp_Vec vecRev = gp_Vec(params.dir.Reversed())*params.lengthRev; + + bool bFwd = fabs(params.lengthFwd) > Precision::Confusion(); + bool bRev = fabs(params.lengthRev) > Precision::Confusion(); + bool bMid = !bFwd || !bRev || params.lengthFwd*params.lengthRev > 0.0; //include the source shape as loft section? + + TopoShape shape = _shape; + TopoShape sourceWire; + if (shape.isNull()) + Standard_Failure::Raise("Not a valid shape"); + + if (params.solid && !shape.hasSubShape(TopAbs_FACE)) + shape = shape.makEFace(nullptr, params.faceMakerClass.c_str()); + + if (shape.shapeType() == TopAbs_FACE) { + std::vector wires; + TopoShape outerWire = shape.splitWires(&wires, TopoShape::ReorientForward); + if (outerWire.isNull()) + Standard_Failure::Raise("Missing outer wire"); + if (wires.empty()) + shape = outerWire; + else { + unsigned pos = drafts.size(); + makEDraft(params, outerWire, drafts, hasher); + if (drafts.size() != pos+1) + Standard_Failure::Raise("Failed to make drafted extrusion"); + std::vector inner; + TopoShape innerWires(0, hasher); + innerWires.makECompound(wires,"",false); + ExtrusionParameters copy = params; + copy.taperAngleFwd = params.innerTaperAngleFwd; + copy.taperAngleRev = params.innerTaperAngleRev; + makEDraft(copy, innerWires, inner, hasher); + if (inner.empty()) + Standard_Failure::Raise("Failed to make drafted extrusion with inner hole"); + inner.insert(inner.begin(), drafts.back()); + drafts.back().makECut(inner); + return; + } + } + + if (shape.shapeType() == TopAbs_WIRE) { + ShapeFix_Wire aFix; + aFix.Load(TopoDS::Wire(shape.getShape())); + aFix.FixReorder(); + aFix.FixConnected(); + aFix.FixClosed(); + sourceWire.setShape(aFix.Wire()); + sourceWire.Tag = shape.Tag; + sourceWire.mapSubElement(shape); + } + else if (shape.shapeType() == TopAbs_COMPOUND) { + for(auto &s : shape.getSubTopoShapes()) + makEDraft(params, s, drafts, hasher); + } + else { + Standard_Failure::Raise("Only a wire or a face is supported"); + } + + if (!sourceWire.isNull()) { + std::vector list_of_sections; + auto makeOffset = [&sourceWire](const gp_Vec& translation, double offset) -> TopoShape { + gp_Trsf mat; + mat.SetTranslation(translation); + TopoShape offsetShape(sourceWire.makETransform(mat,"RV")); + if (fabs(offset)>Precision::Confusion()) + offsetShape = offsetShape.makEOffset2D(offset, TopoShape::JoinType::Intersection); + return offsetShape; + }; + + //first. add wire for reversed part of extrusion + if (bRev){ + auto offsetShape = makeOffset(vecRev, distanceRev); + if (offsetShape.isNull()) + Standard_Failure::Raise("Tapered shape is empty"); + TopAbs_ShapeEnum type = offsetShape.getShape().ShapeType(); + if (type == TopAbs_WIRE) { + list_of_sections.push_back(offsetShape); + } + else if (type == TopAbs_EDGE) { + list_of_sections.push_back(offsetShape.makEWires()); + } + else { + Standard_Failure::Raise("Tapered shape type is not supported"); + } + } + + //next. Add source wire as middle section. Order is important. + if (bMid){ + list_of_sections.push_back(sourceWire); + } + + //finally. Forward extrusion offset wire. + if (bFwd){ + auto offsetShape = makeOffset(vecFwd, distanceFwd); + if (offsetShape.isNull()) + Standard_Failure::Raise("Tapered shape is empty"); + TopAbs_ShapeEnum type = offsetShape.getShape().ShapeType(); + if (type == TopAbs_WIRE) { + list_of_sections.push_back(offsetShape); + } + else if (type == TopAbs_EDGE) { + list_of_sections.push_back(offsetShape.makEWires()); + } + else { + Standard_Failure::Raise("Tapered shape type is not supported"); + } + } + + try { +#if defined(__GNUC__) && defined (FC_OS_LINUX) + Base::SignalException se; +#endif + if (params.usepipe) { + drafts.push_back(makEDraftUsingPipe(list_of_sections, hasher)); + return; + } + + //make loft + BRepOffsetAPI_ThruSections mkGenerator( + params.solid ? Standard_True : Standard_False, /*ruled=*/Standard_True); + for(auto &s : list_of_sections) + mkGenerator.AddWire(TopoDS::Wire(s.getShape())); + + mkGenerator.Build(); + drafts.push_back(TopoShape(0,hasher).makEShape(mkGenerator,list_of_sections)); + } + catch (Standard_Failure &){ + throw; + } + catch (...) { + throw Base::CADKernelError("Unknown exception from BRepOffsetAPI_ThruSections"); + } + } +} diff --git a/src/Mod/Part/App/ExtrusionHelper.h b/src/Mod/Part/App/ExtrusionHelper.h index 90c24eab59..f87474502e 100644 --- a/src/Mod/Part/App/ExtrusionHelper.h +++ b/src/Mod/Part/App/ExtrusionHelper.h @@ -68,6 +68,11 @@ public: double offset, bool isSecond, TopoDS_Wire& result); + /** Same as makeDraft() with support of element mapping + */ + static void makEDraft(const ExtrusionParameters& params, const TopoShape&, + std::vector&, App::StringHasherRef hasher); + }; } //namespace Part diff --git a/src/Mod/Part/App/FeatureExtrusion.cpp b/src/Mod/Part/App/FeatureExtrusion.cpp index 53b72a5bf6..ef38272aec 100644 --- a/src/Mod/Part/App/FeatureExtrusion.cpp +++ b/src/Mod/Part/App/FeatureExtrusion.cpp @@ -231,9 +231,9 @@ Base::Vector3d Extrusion::calculateShapeNormal(const App::PropertyLink& shapeLin TopoShape Extrusion::extrudeShape(const TopoShape& source, const Extrusion::ExtrusionParameters& params) { - TopoDS_Shape result; gp_Vec vec = gp_Vec(params.dir).Multiplied(params.lengthFwd + params.lengthRev);//total vector of extrusion - +#ifndef FC_USE_TNP_FIX + TopoDS_Shape result; if (std::fabs(params.taperAngleFwd) >= Precision::Angular() || std::fabs(params.taperAngleRev) >= Precision::Angular()) { //Tapered extrusion! @@ -309,6 +309,50 @@ TopoShape Extrusion::extrudeShape(const TopoShape& source, const Extrusion::Extr if (result.IsNull()) throw NullShapeException("Result of extrusion is null shape."); return TopoShape(result); +#else // FC_NO_ELEMENT_MAP + + // #0000910: Circles Extrude Only Surfaces, thus use BRepBuilderAPI_Copy + TopoShape myShape(source.makECopy()); + + if (std::fabs(params.taperAngleFwd) >= Precision::Angular() || + std::fabs(params.taperAngleRev) >= Precision::Angular()) { + //Tapered extrusion! +# if defined(__GNUC__) && defined (FC_OS_LINUX) + Base::SignalException se; +# endif + std::vector drafts; + ExtrusionHelper::makEDraft(params, myShape, drafts, result.Hasher); + if (drafts.empty()) { + Standard_Failure::Raise("Drafting shape failed"); + }else + result.makECompound(drafts,0,false); + } + else { + //Regular (non-tapered) extrusion! + if (source.isNull()) + Standard_Failure::Raise("Cannot extrude empty shape"); + + //apply reverse part of extrusion by shifting the source shape + if (fabs(params.lengthRev) > Precision::Confusion()) { + gp_Trsf mov; + mov.SetTranslation(gp_Vec(params.dir) * (-params.lengthRev)); + myShape = myShape.makETransform(mov); + } + + //make faces from wires + if (params.solid) { + //test if we need to make faces from wires. If there are faces - we don't. + if(!myShape.hasSubShape(TopAbs_FACE)) { + if(!myShape.Hasher) + myShape.Hasher = result.Hasher; + myShape = myShape.makEFace(0,params.faceMakerClass.c_str()); + } + } + + //extrude! + result.makEPrism(myShape,vec); + } +#endif } App::DocumentObjectExecReturn* Extrusion::execute()