diff --git a/src/Mod/ReverseEngineering/App/AppReverseEngineering.cpp b/src/Mod/ReverseEngineering/App/AppReverseEngineering.cpp index 59625d463..e20d21478 100644 --- a/src/Mod/ReverseEngineering/App/AppReverseEngineering.cpp +++ b/src/Mod/ReverseEngineering/App/AppReverseEngineering.cpp @@ -41,6 +41,7 @@ #include #include "ApproxSurface.h" +#include "BSplineFitting.h" #include "SurfaceTriangulation.h" using namespace Reen; @@ -60,6 +61,9 @@ public: add_varargs_method("triangulate",&Module::triangulate, "triangulate(PointKernel,searchRadius[,mu=2.5])." ); + add_keyword_method("fitBSpline",&Module::fitBSpline, + "fitBSpline(PointKernel)." + ); #endif initialize("This module is the ReverseEngineering module."); // register with Python } @@ -176,6 +180,45 @@ private: return Py::asObject(new Mesh::MeshPy(mesh)); } + Py::Object fitBSpline(const Py::Tuple& args, const Py::Dict& kwds) + { + PyObject *pcObj; + int degree = 2; + int refinement = 4; + int iterations = 10; + double interiorSmoothness = 0.2; + double interiorWeight = 1.0; + double boundarySmoothness = 0.2; + double boundaryWeight = 0.0; + + static char* kwds_approx[] = {"Points", "Degree", "Refinement", "Iterations", + "InteriorSmoothness", "InteriorWeight", "BoundarySmoothness", "BoundaryWeight", NULL}; + if (!PyArg_ParseTupleAndKeywords(args.ptr(), kwds.ptr(), "O!|iiidddd", kwds_approx, + &(Points::PointsPy::Type), &pcObj, + °ree, &refinement, &iterations, + &interiorSmoothness, &interiorWeight, + &boundarySmoothness, &boundaryWeight)) + throw Py::Exception(); + + Points::PointsPy* pPoints = static_cast(pcObj); + Points::PointKernel* points = pPoints->getPointKernelPtr(); + + BSplineFitting fit(points->getBasicPoints()); + fit.setOrder(degree+1); + fit.setRefinement(refinement); + fit.setIterations(iterations); + fit.setInteriorSmoothness(interiorSmoothness); + fit.setInteriorWeight(interiorWeight); + fit.setBoundarySmoothness(boundarySmoothness); + fit.setBoundaryWeight(boundaryWeight); + Handle(Geom_BSplineSurface) hSurf = fit.perform(); + + if (!hSurf.IsNull()) { + return Py::asObject(new Part::BSplineSurfacePy(new Part::GeomBSplineSurface(hSurf))); + } + + throw Py::RuntimeError("Computation of B-Spline surface failed"); + } #endif }; } // namespace Reen diff --git a/src/Mod/ReverseEngineering/App/BSplineFitting.cpp b/src/Mod/ReverseEngineering/App/BSplineFitting.cpp new file mode 100644 index 000000000..bc569f1e1 --- /dev/null +++ b/src/Mod/ReverseEngineering/App/BSplineFitting.cpp @@ -0,0 +1,239 @@ +/*************************************************************************** + * Copyright (c) 2015 Werner Mayer * + * * + * 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 "PreCompiled.h" +#ifndef _PreComp_ +# include +# include +# include +# include +# include +# include +#endif + +#include + +#include "BSplineFitting.h" + +#if defined(HAVE_PCL_SURFACE) +#include +#if PCL_VERSION_COMPARE(>=,1,7,0) +# include +# include +# include +# include +# include +#endif +#endif + +using namespace Reen; + + +BSplineFitting::BSplineFitting(const std::vector& pts) + : myPoints(pts) + , myIterations(10) + , myOrder(3) + , myRefinement(4) + , myInteriorSmoothness(0.2) + , myInteriorWeight(1.0) + , myBoundarySmoothness(0.2) + , myBoundaryWeight(0.0) +{ +} + +void BSplineFitting::setIterations(unsigned value) +{ + myIterations = value; +} + +void BSplineFitting::setOrder(unsigned value) +{ + myOrder = value; +} + +void BSplineFitting::setRefinement(unsigned value) +{ + myRefinement = value; +} + +void BSplineFitting::setInteriorSmoothness(double value) +{ + myInteriorSmoothness = value; +} + +void BSplineFitting::setInteriorWeight(double value) +{ + myInteriorWeight = value; +} + +void BSplineFitting::setBoundarySmoothness(double value) +{ + myBoundarySmoothness = value; +} + +void BSplineFitting::setBoundaryWeight(double value) +{ + myBoundaryWeight = value; +} + +Handle(Geom_BSplineSurface) BSplineFitting::perform() +{ +#if PCL_VERSION_COMPARE(>=,1,7,0) + pcl::on_nurbs::NurbsDataSurface data; + for (std::vector::const_iterator it = myPoints.begin(); it != myPoints.end(); ++it) { + if (!pcl_isnan (it->x) && !pcl_isnan (it->y) && !pcl_isnan (it->z)) + data.interior.push_back (Eigen::Vector3d (it->x, it->y, it->z)); + } + + + // fit B-spline surface + // + + pcl::on_nurbs::FittingSurface::Parameter params; + params.interior_smoothness = myInteriorSmoothness; + params.interior_weight = myInteriorWeight; + params.boundary_smoothness = myBoundarySmoothness; + params.boundary_weight = myBoundaryWeight; + + // initialize + ON_NurbsSurface nurbs = pcl::on_nurbs::FittingSurface::initNurbsPCABoundingBox (myOrder, &data); + pcl::on_nurbs::FittingSurface fit (&data, nurbs); + // fit.setQuiet (false); // enable/disable debug output + + // surface refinement + for (unsigned i = 0; i < myRefinement; i++) { + fit.refine(0); + fit.refine(1); + fit.assemble(params); + fit.solve(); + } + + // surface fitting with final refinement level + for (unsigned i = 0; i < myIterations; i++) { + fit.assemble(params); + fit.solve(); + } + + // fit B-spline curve +#if 0 + // parameters + pcl::on_nurbs::FittingCurve2dAPDM::FitParameter curve_params; + curve_params.addCPsAccuracy = 5e-2; + curve_params.addCPsIteration = 3; + curve_params.maxCPs = 200; + curve_params.accuracy = 1e-3; + curve_params.iterations = 100; + + curve_params.param.closest_point_resolution = 0; + curve_params.param.closest_point_weight = 1.0; + curve_params.param.closest_point_sigma2 = 0.1; + curve_params.param.interior_sigma2 = 0.00001; + curve_params.param.smooth_concavity = 1.0; + curve_params.param.smoothness = 1.0; + + // initialisation (circular) + pcl::on_nurbs::NurbsDataCurve2d curve_data; + curve_data.interior = data.interior_param; + curve_data.interior_weight_function.push_back(true); + ON_NurbsCurve curve_nurbs = pcl::on_nurbs::FittingCurve2dAPDM::initNurbsCurve2D(order, curve_data.interior); + + // curve fitting + pcl::on_nurbs::FittingCurve2dASDM curve_fit (&curve_data, curve_nurbs); + // curve_fit.setQuiet (false); // enable/disable debug output + curve_fit.fitting (curve_params); +#endif + + // u parameters + int numUKnots = fit.m_nurbs.KnotCount(0); + int numUPoles = fit.m_nurbs.CVCount(0); + int uDegree = fit.m_nurbs.Degree(0); + bool uPeriodic = fit.m_nurbs.IsPeriodic(0) ? true : false; + std::map uKnots; + + // v parameters + int numVKnots = fit.m_nurbs.KnotCount(1); + int numVPoles = fit.m_nurbs.CVCount(1); + int vDegree = fit.m_nurbs.Degree(1); + bool vPeriodic = fit.m_nurbs.IsPeriodic(1) ? true : false; + std::map vKnots; + + TColgp_Array2OfPnt poles(1, numUPoles, 1, numVPoles); + TColStd_Array2OfReal weights(1, numUPoles, 1, numVPoles); + + for (int i=0; i::iterator it = uKnots.find(value); + if (it == uKnots.end()) + uKnots[value] = 1; + else + it->second++; + } + + vKnots[fit.m_nurbs.SuperfluousKnot(1,0)] = 1; + vKnots[fit.m_nurbs.SuperfluousKnot(1,1)] = 1; + for (int i=0; i::iterator it = vKnots.find(value); + if (it == vKnots.end()) + vKnots[value] = 1; + else + it->second++; + } + + TColStd_Array1OfReal uKnotArray(1,uKnots.size()); + TColStd_Array1OfInteger uMultArray(1,uKnots.size()); + int index = 1; + for (std::map::iterator it = uKnots.begin(); it != uKnots.end(); ++it, index++) { + uKnotArray.SetValue(index, it->first); + uMultArray.SetValue(index, it->second); + } + + TColStd_Array1OfReal vKnotArray(1,vKnots.size()); + TColStd_Array1OfInteger vMultArray(1,vKnots.size()); + index = 1; + for (std::map::iterator it = vKnots.begin(); it != vKnots.end(); ++it, index++) { + vKnotArray.SetValue(index, it->first); + vMultArray.SetValue(index, it->second); + } + + Handle_Geom_BSplineSurface spline = new Geom_BSplineSurface(poles,weights, + uKnotArray, vKnotArray, uMultArray, vMultArray, uDegree, vDegree, + uPeriodic, vPeriodic); + return spline; +#else + return Handle_Geom_BSplineSurface(); +#endif +} diff --git a/src/Mod/ReverseEngineering/App/BSplineFitting.h b/src/Mod/ReverseEngineering/App/BSplineFitting.h new file mode 100644 index 000000000..b7ed8ae31 --- /dev/null +++ b/src/Mod/ReverseEngineering/App/BSplineFitting.h @@ -0,0 +1,60 @@ +/*************************************************************************** + * Copyright (c) 2015 Werner Mayer * + * * + * 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 * + * * + ***************************************************************************/ + + +#ifndef REEN_BSPLINEFITTING_H +#define REEN_BSPLINEFITTING_H + +#include +#include +#include + +namespace Reen { + +class BSplineFitting +{ +public: + BSplineFitting(const std::vector&); + Handle(Geom_BSplineSurface) perform(); + + void setIterations(unsigned); + void setOrder(unsigned); + void setRefinement(unsigned); + void setInteriorSmoothness(double); + void setInteriorWeight(double); + void setBoundarySmoothness(double); + void setBoundaryWeight(double); + +private: + std::vector myPoints; + unsigned myIterations; + unsigned myOrder; + unsigned myRefinement; + double myInteriorSmoothness; + double myInteriorWeight; + double myBoundarySmoothness; + double myBoundaryWeight; +}; + +} + +#endif // REEN_BSPLINEFITTING_H diff --git a/src/Mod/ReverseEngineering/App/CMakeLists.txt b/src/Mod/ReverseEngineering/App/CMakeLists.txt index 049ce841c..60d54a75a 100644 --- a/src/Mod/ReverseEngineering/App/CMakeLists.txt +++ b/src/Mod/ReverseEngineering/App/CMakeLists.txt @@ -38,6 +38,8 @@ SET(Reen_SRCS AppReverseEngineering.cpp ApproxSurface.cpp ApproxSurface.h + BSplineFitting.cpp + BSplineFitting.h SurfaceTriangulation.cpp SurfaceTriangulation.h PreCompiled.cpp