// SPDX-License-Identifier: LGPL-2.1-or-later /*************************************************************************** * Copyright (c) 2014-2015 Nathan Miller * * Balázs Bámer * * * * This file is part of the FreeCAD CAx development system. * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Library General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public * * License along with this library; see the file COPYING.LIB. If not, * * write to the Free Software Foundation, Inc., 59 Temple Place, * * Suite 330, Boston, MA 02111-1307, USA * * * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "FeatureGeomFillSurface.h" using namespace Surface; ShapeValidator::ShapeValidator() { initValidator(); } void ShapeValidator::initValidator() { willBezier = true; edgeCount = 0; } // shows error message if the shape is not an edge void ShapeValidator::checkEdge(const TopoDS_Shape& shape) { if (shape.IsNull() || shape.ShapeType() != TopAbs_EDGE) { Standard_Failure::Raise("Shape is not an edge.\n"); } TopoDS_Edge etmp = TopoDS::Edge(shape); // Curve TopoDS_Edge TopLoc_Location heloc; // this will be output Standard_Real u0; // contains output Standard_Real u1; // contains output Handle(Geom_Curve) c_geom = BRep_Tool::Curve(etmp, heloc, u0, u1); // The geometric curve Handle(Geom_BezierCurve) bez_geom = Handle(Geom_BezierCurve)::DownCast(c_geom); // Try to get // Bezier curve // if not a Bezier then try to create a B-spline surface from the edges if (bez_geom.IsNull()) { willBezier = false; } edgeCount++; } void ShapeValidator::checkAndAdd(const TopoDS_Shape& shape, Handle(ShapeExtend_WireData) * aWD) { checkEdge(shape); if (aWD) { BRepBuilderAPI_Copy copier(shape); // make a copy of the shape and the underlying geometry to avoid to affect the input shapes (*aWD)->Add(TopoDS::Edge(copier.Shape())); } } void ShapeValidator::checkAndAdd( const Part::TopoShape& ts, const char* subName, Handle(ShapeExtend_WireData) * aWD ) { try { if (subName && *subName != '\0') { // we want only the subshape which is linked checkAndAdd(ts.getSubShape(subName), aWD); } else if (!ts.getShape().IsNull() && ts.getShape().ShapeType() == TopAbs_WIRE) { TopoDS_Wire wire = TopoDS::Wire(ts.getShape()); for (TopExp_Explorer xp(wire, TopAbs_EDGE); xp.More(); xp.Next()) { checkAndAdd(xp.Current(), aWD); } } else { checkAndAdd(ts.getShape(), aWD); } } catch (Standard_Failure&) { // any OCC exception means an inappropriate shape in the selection Standard_Failure::Raise("Wrong shape type.\n"); } } PROPERTY_SOURCE(Surface::GeomFillSurface, Part::Spline) const char* GeomFillSurface::FillTypeEnums[] = {"Stretched", "Coons", "Curved", nullptr}; GeomFillSurface::GeomFillSurface() : Spline() { ADD_PROPERTY(FillType, ((long)0)); ADD_PROPERTY(BoundaryList, (nullptr, "Dummy")); ADD_PROPERTY(ReversedList, (false)); FillType.setEnums(FillTypeEnums); BoundaryList.setScope(App::LinkScope::Global); } // Check if any components of the surface have been modified short GeomFillSurface::mustExecute() const { if (BoundaryList.isTouched() || ReversedList.isTouched() || FillType.isTouched()) { return 1; } return Spline::mustExecute(); } void GeomFillSurface::onChanged(const App::Property* prop) { if (isRestoring()) { if (prop == &BoundaryList) { // auto-adjusting size of this list if (BoundaryList.getSize() != ReversedList.getSize()) { ReversedList.setSize(BoundaryList.getSize()); } } } Part::Spline::onChanged(prop); } App::DocumentObjectExecReturn* GeomFillSurface::execute() { try { TopoDS_Wire aWire; // Gets the healed wire if (getWire(aWire)) { createBezierSurface(aWire); } else { createBSplineSurface(aWire); } return App::DocumentObject::StdReturn; } catch (Standard_ConstructionError&) { // message is in a Latin language, show a normal one return new App::DocumentObjectExecReturn("Curves are disjoint."); } catch (StdFail_NotDone&) { return new App::DocumentObjectExecReturn( "A curve was not a B-spline and could not be converted into one." ); } catch (Standard_Failure& e) { return new App::DocumentObjectExecReturn(e.GetMessageString()); } } GeomFill_FillingStyle GeomFillSurface::getFillingStyle() { // Identify filling style switch (FillType.getValue()) { case GeomFill_StretchStyle: case GeomFill_CoonsStyle: case GeomFill_CurvedStyle: return static_cast(FillType.getValue()); default: Standard_Failure::Raise("Filling style must be 0 (Stretch), 1 (Coons), or 2 (Curved).\n"); return GeomFill_StretchStyle; // this is to shut up the compiler } } bool GeomFillSurface::getWire(TopoDS_Wire& aWire) { Handle(ShapeFix_Wire) aShFW = new ShapeFix_Wire; Handle(ShapeExtend_WireData) aWD = new ShapeExtend_WireData; std::vector boundary = BoundaryList.getSubListValues(); if (boundary.size() > 4) { // if too many not even try Standard_Failure::Raise("Only 2-4 curves are allowed\n"); } ShapeValidator validator; for (const auto& set : boundary) { if (set.first->isDerivedFrom()) { for (const auto& jt : set.second) { const Part::TopoShape& ts = static_cast(set.first)->Shape.getShape(); validator.checkAndAdd(ts, jt.c_str(), &aWD); } } else { Standard_Failure::Raise("Curve not from Part::Feature\n"); } } if (validator.numEdges() < 2 || validator.numEdges() > 4) { Standard_Failure::Raise("Only 2-4 curves are allowed\n"); } // Reorder the curves and fix the wire if required aShFW->Load(aWD); // Load in the wire aShFW->FixReorder(); // Fix the order of the edges if required aShFW->ClosedWireMode() = Standard_True; // Enables closed wire mode aShFW->FixConnected(); // Fix connection between wires aShFW->FixSelfIntersection(); // Fix Self Intersection aShFW->Perform(); // Perform the fixes aWire = aShFW->Wire(); // Healed Wire if (aWire.IsNull()) { Standard_Failure::Raise("Wire unable to be constructed\n"); } return validator.isBezier(); } void GeomFillSurface::createFace(const Handle(Geom_BoundedSurface) & aSurface) { BRepBuilderAPI_MakeFace aFaceBuilder; Standard_Real u1, u2, v1, v2; // transfer surface bounds to face aSurface->Bounds(u1, u2, v1, v2); aFaceBuilder.Init(aSurface, u1, u2, v1, v2, Precision::Confusion()); TopoDS_Face aFace = aFaceBuilder.Face(); if (!aFaceBuilder.IsDone()) { Standard_Failure::Raise("Face unable to be constructed\n"); } if (aFace.IsNull()) { Standard_Failure::Raise("Resulting Face is null\n"); } this->Shape.setValue(aFace); } void GeomFillSurface::createBezierSurface(TopoDS_Wire& aWire) { std::vector curves; curves.reserve(4); Standard_Real u1, u2; // contains output TopExp_Explorer anExp(aWire, TopAbs_EDGE); for (; anExp.More(); anExp.Next()) { const TopoDS_Edge hedge = TopoDS::Edge(anExp.Current()); TopLoc_Location heloc; // this will be output Handle(Geom_Curve) c_geom = BRep_Tool::Curve(hedge, heloc, u1, u2); // The geometric curve Handle(Geom_BezierCurve) bezier = Handle(Geom_BezierCurve)::DownCast(c_geom); // Try to get // Bezier curve if (!bezier.IsNull()) { bezier->Segment(u1, u2); // DownCast(c_geom) will not trim bezier, so DIY gp_Trsf transf = heloc.Transformation(); bezier->Transform(transf); // apply original transformation to control points // Store Underlying Geometry curves.push_back(bezier); } else { Standard_Failure::Raise("Curve not a Bezier Curve"); } } GeomFill_FillingStyle fstyle = getFillingStyle(); GeomFill_BezierCurves aSurfBuilder; // Create Surface Builder std::size_t edgeCount = curves.size(); const boost::dynamic_bitset<>& booleans = ReversedList.getValues(); if (edgeCount == booleans.size()) { for (std::size_t i = 0; i < edgeCount; i++) { if (booleans[i]) { curves[i]->Reverse(); } } } if (edgeCount == 2) { aSurfBuilder.Init(curves[0], curves[1], fstyle); } else if (edgeCount == 3) { aSurfBuilder.Init(curves[0], curves[1], curves[2], fstyle); } else if (edgeCount == 4) { aSurfBuilder.Init(curves[0], curves[1], curves[2], curves[3], fstyle); } createFace(aSurfBuilder.Surface()); } void GeomFillSurface::createBSplineSurface(TopoDS_Wire& aWire) { std::vector curves; curves.reserve(4); Standard_Real u1, u2; // contains output TopExp_Explorer anExp(aWire, TopAbs_EDGE); for (; anExp.More(); anExp.Next()) { const TopoDS_Edge& edge = TopoDS::Edge(anExp.Current()); TopLoc_Location heloc; // this will be output Handle(Geom_Curve) c_geom = BRep_Tool::Curve(edge, heloc, u1, u2); // The geometric curve Handle(Geom_BSplineCurve) bspline = Handle(Geom_BSplineCurve)::DownCast( c_geom ); // Try to get BSpline curve gp_Trsf transf = heloc.Transformation(); if (!bspline.IsNull()) { bspline->Segment(u1, u2); // DownCast(c_geom) will not trim spline, so DIY bspline->Transform(transf); // apply original transformation to control points // Store Underlying Geometry curves.push_back(bspline); } else { // try to convert it into a B-spline Handle(Geom_TrimmedCurve) trim = new Geom_TrimmedCurve(c_geom, u1, u2); // Approximate the curve to non-rational polynomial BSpline // to avoid C0 continuity in output surface GeomConvert conv; Convert_ParameterisationType paratype = Convert_Polynomial; Handle(Geom_BSplineCurve) bspline2 = conv.CurveToBSplineCurve(trim, paratype); if (!bspline2.IsNull()) { bspline2->Transform(transf); // apply original transformation to control points curves.push_back(bspline2); } else { // GeomConvert failed, try ShapeConstruct_Curve now ShapeConstruct_Curve scc; Handle(Geom_BSplineCurve) spline = scc.ConvertToBSpline(c_geom, u1, u2, Precision::Confusion()); if (spline.IsNull()) { Standard_Failure::Raise( "A curve was not a B-spline and could not be converted into one." ); } spline->Transform(transf); // apply original transformation to control points curves.push_back(spline); } } } GeomFill_FillingStyle fstyle = getFillingStyle(); GeomFill_BSplineCurves aSurfBuilder; // Create Surface Builder std::size_t edgeCount = curves.size(); const boost::dynamic_bitset<>& booleans = ReversedList.getValues(); if (edgeCount == booleans.size()) { for (std::size_t i = 0; i < edgeCount; i++) { if (booleans[i]) { curves[i]->Reverse(); } } } if (edgeCount == 2) { aSurfBuilder.Init(curves[0], curves[1], fstyle); } else if (edgeCount == 3) { aSurfBuilder.Init(curves[0], curves[1], curves[2], fstyle); } else if (edgeCount == 4) { aSurfBuilder.Init(curves[0], curves[1], curves[2], curves[3], fstyle); } createFace(aSurfBuilder.Surface()); }