diff --git a/src/Mod/Part/App/BSplineCurvePy.xml b/src/Mod/Part/App/BSplineCurvePy.xml index 77037c9dd7..2047f1e78c 100644 --- a/src/Mod/Part/App/BSplineCurvePy.xml +++ b/src/Mod/Part/App/BSplineCurvePy.xml @@ -293,12 +293,37 @@ from the knots table of this B-Spline curve. - + - approximate(list_of_points): - Replaces this B-Spline curve by approximating a set of points. + The function accepts keywords as arguments. + + approximate2(Points = list_of_points) + + Optional arguments : + + DegMin = integer (3) : Minimum degree of the curve. + DegMax = integer (8) : Maximum degree of the curve. + Tolerance = float (1e-3) : approximating tolerance. + Continuity = string ('C2') : Desired continuity of the curve. + Possible values : 'C0','G1','C1','G2','C2','C3','CN' + + LengthWeight = float, CurvatureWeight = float, TorsionWeight = float + If one of these arguments is not null, the functions approximates the + points using variational smoothing algorithm, which tries to minimize + additional criterium: + LengthWeight*CurveLength + CurvatureWeight*Curvature + TorsionWeight*Torsion + Continuity must be C0, C1 or C2, else defaults to C2. + + Parameters = list of floats : knot sequence of the approximated points. + This argument is only used if the weights above are all null. + + ParamType = string ('Uniform','Centripetal' or 'ChordLength') + Parameterization type. Only used if weights and Parameters above aren't specified. + + Note : Continuity of the spline defaults to C2. However, it may not be applied if + it conflicts with other parameters ( especially DegMax ). diff --git a/src/Mod/Part/App/BSplineCurvePyImp.cpp b/src/Mod/Part/App/BSplineCurvePyImp.cpp index 849f314bb5..e578594eab 100644 --- a/src/Mod/Part/App/BSplineCurvePyImp.cpp +++ b/src/Mod/Part/App/BSplineCurvePyImp.cpp @@ -27,6 +27,7 @@ # include # include # include +# include # include # include # include @@ -34,8 +35,9 @@ # include # include # include +# include # include -# include + # include #endif @@ -743,11 +745,29 @@ PyObject* BSplineCurvePy::toBiArcs(PyObject * args) } } -PyObject* BSplineCurvePy::approximate(PyObject *args) +PyObject* BSplineCurvePy::approximate(PyObject *args, PyObject *kwds) { PyObject* obj; - if (!PyArg_ParseTuple(args, "O", &obj)) + Standard_Integer degMin=3; + Standard_Integer degMax=8; + char* continuity = "C2"; + double tol3d = 1e-3; + char* parType = "ChordLength"; + PyObject* par = 0; + double weight1 = 0; + double weight2 = 0; + double weight3 = 0; + + static char* kwds_interp[] = {"Points", "DegMax", "Continuity", "Tolerance", "DegMin", "ParamType", "Parameters", + "LengthWeight", "CurvatureWeight", "TorsionWeight", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|isdisOddd",kwds_interp, + &obj, °Max, + &continuity, &tol3d, °Min, + &parType, &par, + &weight1, &weight2, &weight3)) return 0; + try { Py::Sequence list(obj); TColgp_Array1OfPnt pnts(1,list.size()); @@ -757,7 +777,77 @@ PyObject* BSplineCurvePy::approximate(PyObject *args) pnts(index++) = gp_Pnt(vec.x,vec.y,vec.z); } - GeomAPI_PointsToBSpline fit(pnts); + if (degMin > degMax) { + Standard_Failure::Raise("DegMin must be lower or equal to DegMax"); + } + + GeomAbs_Shape c; + std::string str = continuity; + if (str == "C0") + c = GeomAbs_C0; + else if (str == "G1") + c = GeomAbs_G1; + else if (str == "C1") + c = GeomAbs_C1; + else if (str == "G2") + c = GeomAbs_G2; + else if (str == "C2") + c = GeomAbs_C2; + else if (str == "C3") + c = GeomAbs_C3; + else if (str == "CN") + c = GeomAbs_CN; + else + c = GeomAbs_C2; + + if (weight1 || weight2 || weight3) { + // It seems that this function only works with Continuity = C0, C1 or C2 + if (!(c == GeomAbs_C0 || c == GeomAbs_C1 || c == GeomAbs_C2)) { + c = GeomAbs_C2; + } + GeomAPI_PointsToBSpline fit(pnts, weight1, weight2, weight3, degMax, c, tol3d); + Handle_Geom_BSplineCurve spline = fit.Curve(); + if (!spline.IsNull()) { + this->getGeomBSplineCurvePtr()->setHandle(spline); + Py_Return; + } + else { + Standard_Failure::Raise("Smoothing approximation failed"); + return 0; // goes to the catch block + } + } + + if (par) { + Py::Sequence plist(par); + TColStd_Array1OfReal parameters(1,plist.size()); + Standard_Integer index = 1; + for (Py::Sequence::iterator it = plist.begin(); it != plist.end(); ++it) { + Py::Float f(*it); + parameters(index++) = static_cast(f); + } + + GeomAPI_PointsToBSpline fit(pnts, parameters, degMin, degMax, c, tol3d); + Handle_Geom_BSplineCurve spline = fit.Curve(); + if (!spline.IsNull()) { + this->getGeomBSplineCurvePtr()->setHandle(spline); + Py_Return; + } + else { + Standard_Failure::Raise("Approximation with parameters failed"); + return 0; // goes to the catch block + } + } + + Approx_ParametrizationType pt; + std::string pstr = parType; + if (pstr == "Uniform") + pt = Approx_IsoParametric; + else if (pstr == "Centripetal") + pt = Approx_Centripetal; + else + pt = Approx_ChordLength; + + GeomAPI_PointsToBSpline fit(pnts, pt, degMin, degMax, c, tol3d); Handle_Geom_BSplineCurve spline = fit.Curve(); if (!spline.IsNull()) { this->getGeomBSplineCurvePtr()->setHandle(spline);