From edc3857bdba2542cc50fd9dfc0dc664a0d93d490 Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Fri, 9 Feb 2024 21:19:07 -0500 Subject: [PATCH] TopoNaming/Part: transfer in MakERuledSurface --- src/Mod/Part/App/PartFeatures.cpp | 21 ++++ src/Mod/Part/App/TopoShape.h | 22 ++++ src/Mod/Part/App/TopoShapeExpansion.cpp | 139 ++++++++++++++++++++++++ 3 files changed, 182 insertions(+) diff --git a/src/Mod/Part/App/PartFeatures.cpp b/src/Mod/Part/App/PartFeatures.cpp index facce022f3..cdba29b292 100644 --- a/src/Mod/Part/App/PartFeatures.cpp +++ b/src/Mod/Part/App/PartFeatures.cpp @@ -114,6 +114,7 @@ App::DocumentObjectExecReturn* RuledSurface::getShape(const App::PropertyLinkSub App::DocumentObjectExecReturn *RuledSurface::execute() { try { +#ifdef FC_USE_TNP_FIX App::DocumentObjectExecReturn* ret; // get the first input shape @@ -231,6 +232,26 @@ App::DocumentObjectExecReturn *RuledSurface::execute() this->Shape.setValue(ruledShape); return App::DocumentObject::StdReturn; +#else + std::vector shapes; + std::array links = {&Curve1,&Curve2}; + for(auto link : links) { + const auto &subs = link->getSubValues(); + if(subs.empty()) + shapes.push_back(getTopoShape(link->getValue())); + else if(subs.size()!=1) + return new App::DocumentObjectExecReturn("Not exactly one sub-shape linked."); + else + shapes.push_back(getTopoShape(link->getValue(), + subs.front().c_str(),true)); + if(shapes.back().isNull()) + return new App::DocumentObjectExecReturn("Invalid link."); + } + TopoShape res(0, getDocument()->getStringHasher()); + res.makERuledSurface(shapes, Orientation.getValue()); + this->Shape.setValue(res); + return Part::Feature::execute(); +#endif } catch (Standard_Failure& e) { diff --git a/src/Mod/Part/App/TopoShape.h b/src/Mod/Part/App/TopoShape.h index 09e1e72723..52d1374660 100644 --- a/src/Mod/Part/App/TopoShape.h +++ b/src/Mod/Part/App/TopoShape.h @@ -786,6 +786,28 @@ public: } }; + /** Make a ruled surface + * + * @param sources: the source shapes, each of which must contain either a + * single edge or a single wire. + * @param orientation: + * @param isSolid: whether to make a solid + * @param isRuled: If true, then the faces generated between the edges of + * two consecutive section wires are ruled surfaces. If + * false, then they are smoothed out by approximation + * @param isClosed: If true, then the first section is duplicated to close + * the loft as the last section + * @param maxDegree: define the maximal U degree of the result surface + * @param op: optional string to be encoded into topo naming for indicating + * the operation + * + * @return The original content of this TopoShape is discarded and replaced + * with the new shape. The function returns the TopoShape itself as + * a self reference so that multiple operations can be carried out + * for the same shape in the same line of code. + */ + TopoShape &makERuledSurface(const std::vector &source, int orientation=0, const char *op=nullptr); + /** Core function to generate mapped element names from shape history * * @param shape: the new shape diff --git a/src/Mod/Part/App/TopoShapeExpansion.cpp b/src/Mod/Part/App/TopoShapeExpansion.cpp index f77357c39f..76ebe54daf 100644 --- a/src/Mod/Part/App/TopoShapeExpansion.cpp +++ b/src/Mod/Part/App/TopoShapeExpansion.cpp @@ -1653,6 +1653,145 @@ TopoShape TopoShape::getSubTopoShape(TopAbs_ShapeEnum type, int idx, bool silent return shapeMap.getTopoShape(*this, idx); } +TopoShape &TopoShape::makERuledSurface(const std::vector &shapes, + int orientation, const char *op) +{ + if(!op) + op = Part::OpCodes::RuledSurface; + + if(shapes.size()!=2) + FC_THROWM(Base::CADKernelError,"Wrong number of input shape"); + + std::vector curves(2); + int i=0; + for(auto &s : shapes) { + if(s.isNull()) + HANDLE_NULL_INPUT; + auto type = s.shapeType(); + if(type == TopAbs_WIRE || type == TopAbs_EDGE) { + curves[i++] = s; + continue; + } + auto count = s.countSubShapes(TopAbs_WIRE); + if(count>1) + FC_THROWM(Base::CADKernelError,"Input shape has more than one wire"); + if(count==1) { + curves[i++] = s.getSubTopoShape(TopAbs_WIRE,1); + continue; + } + count = s.countSubShapes(TopAbs_EDGE); + if(count==0) + FC_THROWM(Base::CADKernelError,"Input shape has no edge"); + if(count == 1) { + curves[i++] = s.getSubTopoShape(TopAbs_EDGE,1); + continue; + } + curves[i] = s.makEWires(); + if(curves[i].isNull()) + HANDLE_NULL_INPUT; + if(curves[i].shapeType()!=TopAbs_WIRE) + FC_THROWM(Base::CADKernelError,"Input shape forms more than one wire"); + ++i; + } + + if(curves[0].shapeType()!=curves[1].shapeType()) { + for(auto &curve : curves) { + if(curve.shapeType() == TopAbs_EDGE) + curve = curve.makEWires(); + } + } + + auto &S1 = curves[0]; + auto &S2 = curves[1]; + bool isWire = S1.shapeType()==TopAbs_WIRE; + + // https://forum.freecadweb.org/viewtopic.php?f=8&t=24052 + // + // if both shapes are sub-elements of one common shape then the fill + // algorithm leads to problems if the shape has set a placement. The + // workaround is to copy the sub-shape + S1 = S1.makECopy(); + S2 = S2.makECopy(); + + if (orientation == 0) { + // Automatic + Handle(Adaptor3d_HCurve) a1; + Handle(Adaptor3d_HCurve) a2; + if (!isWire) { + BRepAdaptor_Curve adapt1(TopoDS::Edge(S1.getShape())); + BRepAdaptor_Curve adapt2(TopoDS::Edge(S2.getShape())); + a1 = new BRepAdaptor_HCurve(adapt1); + a2 = new BRepAdaptor_HCurve(adapt2); + } + else { + BRepAdaptor_CompCurve adapt1(TopoDS::Wire(S1.getShape())); + BRepAdaptor_CompCurve adapt2(TopoDS::Wire(S2.getShape())); + a1 = new BRepAdaptor_HCompCurve(adapt1); + a2 = new BRepAdaptor_HCompCurve(adapt2); + } + + if (!a1.IsNull() && !a2.IsNull()) { + // get end points of 1st curve + gp_Pnt p1 = a1->Value(a1->FirstParameter()); + gp_Pnt p2 = a1->Value(a1->LastParameter()); + if (S1.getShape().Orientation() == TopAbs_REVERSED) { + std::swap(p1, p2); + } + + // get end points of 2nd curve + gp_Pnt p3 = a2->Value(a2->FirstParameter()); + gp_Pnt p4 = a2->Value(a2->LastParameter()); + if (S2.getShape().Orientation() == TopAbs_REVERSED) { + std::swap(p3, p4); + } + + // Form two triangles (P1,P2,P3) and (P4,P3,P2) and check their normals. + // If the dot product is negative then it's assumed that the resulting face + // is twisted, hence the 2nd edge is reversed. + gp_Vec v1(p1, p2); + gp_Vec v2(p1, p3); + gp_Vec n1 = v1.Crossed(v2); + + gp_Vec v3(p4, p3); + gp_Vec v4(p4, p2); + gp_Vec n2 = v3.Crossed(v4); + + if (n1.Dot(n2) < 0) { + S2.setShape(S2.getShape().Reversed(), false); + } + } + } + else if (orientation == 2) { + // Reverse + S2.setShape(S2.getShape().Reversed(), false); + } + + TopoDS_Shape ruledShape; + if (!isWire) { + ruledShape = BRepFill::Face(TopoDS::Edge(S1.getShape()), TopoDS::Edge(S2.getShape())); + } + else { + ruledShape = BRepFill::Shell(TopoDS::Wire(S1.getShape()), TopoDS::Wire(S2.getShape())); + } + + // Both BRepFill::Face() and Shell() modifies the original input edges + // without any API to provide relationship to the output edges. So we have + // to use searchSubShape() to build the relationship by ourselves. + + TopoShape res(ruledShape.Located(TopLoc_Location())); + std::vector edges; + for (const auto &c : curves) { + for (const auto &e : c.getSubTopoShapes(TopAbs_EDGE)) { + auto found = res.searchSubShape(e); + if (found.size() > 0) { + found.front().resetElementMap(e.elementMap()); + edges.push_back(found.front()); + } + } + } + // Use empty mapper and let makEShape name the created surface with lower elements. + return makESHAPE(res.getShape(),Mapper(),edges,op); +} TopoShape& TopoShape::makeElementCompound(const std::vector& shapes, const char* op,