diff --git a/doc/tutorials/content/bspline_fitting.rst b/doc/tutorials/content/bspline_fitting.rst
new file mode 100644
index 00000000000..6d7d76e2bd5
--- /dev/null
+++ b/doc/tutorials/content/bspline_fitting.rst
@@ -0,0 +1,276 @@
+.. _bspline_fitting:
+
+Fitting trimmed B-splines to unordered point clouds
+---------------------------------------------------
+
+This tutorial explains how to run a B-spline fitting algorithm on a
+point-cloud, to obtain a smooth, parametric surface representation.
+The algorithm consists of the following steps:
+
+* Initialization of the B-spline surface by using the Principal Component Analysis (PCA). This
+ assumes that the point-cloud has two main orientations, i.e. that it is roughly planar.
+
+* Refinement and fitting of the B-spline surface.
+
+* Circular initialization of the B-spline curve. Here we assume that the point-cloud is
+ compact, i.e. no separated clusters.
+
+* Fitting of the B-spline curve.
+
+* Triangulation of the trimmed B-spline surface.
+
+In this video, the algorithm is applied to the frontal scan of the stanford bunny (204800 points):
+
+.. raw:: html
+
+
+
+
+Theoretical background
+----------------------
+
+Theoretical information on the algorithm can be found in this `report
+`_ and in my `PhD thesis
+`_.
+
+
+PCL installation settings
+-------------------------
+
+Please note that the modules for NURBS and B-splines are not enabled by default.
+Make sure you enable "BUILD_surface_on_nurbs" in your ccmake configuration, by setting it to ON.
+
+If your license permits, also enable "USE_UMFPACK" for sparse linear solving.
+This requires SuiteSparse (libsuitesparse-dev in Ubuntu) which is faster,
+allows more degrees of freedom (i.e. control points) and more data points.
+
+The program created during this tutorial is available in
+*pcl/examples/surface/example_nurbs_fitting_surface.cpp* and is built when
+"BUILD_examples" is set to ON. This will create the binary called *pcl_example_nurbs_fitting_surface*
+in your *bin* folder.
+
+
+The code
+--------
+
+The cpp file used in this tutorial can be found in *pcl/doc/tutorials/content/sources/bspline_fitting/bspline_fitting.cpp*.
+You can find the input file at *pcl/test/bunny.pcd*.
+
+.. literalinclude:: sources/bspline_fitting/bspline_fitting.cpp
+ :language: cpp
+ :linenos:
+ :lines: 1-169
+
+
+The explanation
+---------------
+Now, let's break down the code piece by piece.
+Lets start with the choice of the parameters for B-spline surface fitting:
+
+.. literalinclude:: sources/bspline_fitting/bspline_fitting.cpp
+ :language: cpp
+ :linenos:
+ :lines: 56-66
+
+* *order* is the polynomial order of the B-spline surface.
+
+* *refinement* is the number of refinement iterations, where for each iteration control-points
+ are inserted, approximately doubling the control points in each parametric direction
+ of the B-spline surface.
+
+* *iterations* is the number of iterations that are performed after refinement is completed.
+
+* *mesh_resolution* the number of vertices in each parametric direction,
+ used for triangulation of the B-spline surface.
+
+Fitting:
+
+* *interior_smoothness* is the smoothness of the surface interior.
+
+* *interior_weight* is the weight for optimization for the surface interior.
+
+* *boundary_smoothness* is the smoothness of the surface boundary.
+
+* *boundary_weight* is the weight for optimization for the surface boundary.
+
+Note, that the boundary in this case is not the trimming curve used later on.
+The boundary can be used when a point-set exists that defines the boundary. Those points
+can be declared in *pcl::on_nurbs::NurbsDataSurface::boundary*. In that case, when the
+*boundary_weight* is greater than 0.0, the algorithm tries to align the domain boundaries
+to these points. In our example we are trimming the surface anyway, so there is no need
+for aligning the boundary.
+
+Initialization of the B-spline surface
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. literalinclude:: sources/bspline_fitting/bspline_fitting.cpp
+ :language: cpp
+ :lines: 68-72
+
+The command *initNurbsPCABoundingBox* uses PCA to create a coordinate systems, where the principal
+eigenvectors point into the direction of the maximum, middle and minimum extension of the point-cloud.
+The center of the coordinate system is located at the mean of the points.
+To estimate the extension of the B-spline surface domain, a bounding box is computed in the plane formed
+by the maximum and middle eigenvectors. That bounding box is used to initialize the B-spline surface with
+its minimum number of control points, according to the polynomial degree chosen.
+
+The surface fitting class *pcl::on_nurbs::FittingSurface* is initialized with the point data and the initial
+B-spline.
+
+.. literalinclude:: sources/bspline_fitting/bspline_fitting.cpp
+ :language: cpp
+ :lines: 74-80
+
+The *on_nurbs::Triangulation* class allows easy conversion between the *ON_NurbsSurface* and the *PolygonMesh* class,
+for visualization of the B-spline surfaces. Note that NURBS are a generalization of B-splines,
+and are therefore a valid container for B-splines, with all control-point weights = 1.
+
+.. literalinclude:: sources/bspline_fitting/bspline_fitting.cpp
+ :language: cpp
+ :lines: 82-92
+
+Refinement and fitting of the B-spline surface
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+At this point of the code we have a B-spline surface with minimal number of control points.
+Typically they are not enough to represent finer details of the underlying geometry
+of the point-cloud. However, if we increase the control-points to our desired level of detail and
+subsequently fit the refined B-spline, we run into problems. For robust fitting B-spline surfaces
+the rule is:
+"The higher the degree of freedom of the B-spline surface, the closer we have to be to the points to be approximated".
+
+This is the reason why we iteratively increase the degree of freedom by refinement in both directions (line 85-86),
+and fit the B-spline surface to the point-cloud, getting closer to the final solution.
+
+.. literalinclude:: sources/bspline_fitting/bspline_fitting.cpp
+ :language: cpp
+ :lines: 94-102
+
+After we reached the final level of refinement, the surface is further fitted to the point-cloud
+for a pleasing end result.
+
+Initialization of the B-spline curve
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Now that we have the surface fitted to the point-cloud, we want to cut off the overlapping regions of the surface.
+To achieve this we project the point-cloud into the parametric domain using the closest points to the B-spline surface.
+In this domain of R^2 we perform the weighted B-spline curve fitting, that creates a closed trimming curve that approximately
+contains all the points.
+
+.. literalinclude:: sources/bspline_fitting/bspline_fitting.cpp
+ :language: cpp
+ :lines: 107-120
+
+The topic of curve fitting goes a bit deeper into the thematics of B-splines. Here we assume that you are
+familiar with the concept of B-splines, knot vectors, control-points, and so forth.
+Please consider the curve being split into supporting regions which is bound by consecutive knots.
+Also note that points that are inside and outside the curve are distinguished.
+
+* *addCPsAccuracy* the distance of the supporting region of the curve to the closest data points has to be below
+ this value, otherwise a control point is inserted.
+
+* *addCPsIteration* inner iterations without inserting control points.
+
+* *maxCPs* the maximum total number of control-points.
+
+* *accuracy* the average fitting accuracy of the curve, w.r.t. the supporting regions.
+
+* *iterations* maximum number of iterations performed.
+
+* *closest_point_resolution* number of control points that must lie within each supporting region. (0 turns this constraint off)
+
+* *closest_point_weight* weight for fitting the curve to its closest points.
+
+* *closest_point_sigma2* threshold for closest points (disregard points that are further away from the curve).
+
+* *interior_sigma2* threshold for interior points (disregard points that are further away from and lie within the curve).
+
+* *smooth_concavity* value that leads to inward bending of the curve (0 = no bending; <0 inward bending; >0 outward bending).
+
+* *smoothness* weight of smoothness term.
+
+
+.. literalinclude:: sources/bspline_fitting/bspline_fitting.cpp
+ :language: cpp
+ :lines: 122-127
+
+The curve is initialized using a minimum number of control points to represent a circle, with the center located
+at the mean of the point-cloud and the radius of the maximum distance of a point to the center.
+Please note that interior weighting is enabled for all points with the command *curve_data.interior_weight_function.push_back (true)*.
+
+Fitting of the B-spline curve
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. literalinclude:: sources/bspline_fitting/bspline_fitting.cpp
+ :language: cpp
+ :lines: 129-133
+
+Similar to the surface fitting approach, the curve is iteratively fitted and refined, as shown in the video.
+Note how the curve tends to bend inwards at regions where it is not supported by any points.
+
+Triangulation of the trimmed B-spline surface
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. literalinclude:: sources/bspline_fitting/bspline_fitting.cpp
+ :language: cpp
+ :lines: 136-142
+
+After the curve fitting terminated, our geometric representation consists of a B-spline surface and a closed
+B-spline curved, defined within the parametric domain of the B-spline surface. This is called trimmed B-spline surface.
+In line 140 we can use the trimmed B-spline to create a triangular mesh. The triangulation algorithm first triangulates
+the whole domain and afterwards removes triangles that lie outside of the trimming curve. Vertices of triangles
+that intersect the trimming curve are clamped to the curve.
+
+When running this example and switch to wire-frame mode (w), you will notice that the triangles are ordered in
+a rectangular way, which is a result of the rectangular domain of the surface.
+
+Some hints
+----------
+Please bear in mind that the robustness of this algorithm heavily depends on the underlying data.
+The parameters for B-spline fitting are designed to model the characteristics of this data.
+
+* If you have holes or steps in your data, you might want to work with lower refinement levels and lower accuracy to
+ prevent the B-spline from folding and twisting. Moderately increasing of the smoothness might also work.
+
+* Try to introduce as much pre-conditioning and constraints to the parameters. E.g. if you know, that
+ the trimming curve is rather simple, then limit the number of maximum control points.
+
+* Start simple! Before giving up on gaining control over twisting and bending B-splines, I highly recommend
+ to start your fitting trials with a small number of control points (low refinement),
+ low accuracy but also low smoothness (B-splines have implicit smoothing property).
+
+Compiling and running the program
+---------------------------------
+
+Add the following lines to your CMakeLists.txt file:
+
+.. literalinclude:: sources/bspline_fitting/CMakeLists.txt
+ :language: cmake
+ :linenos:
+
+After you have made the executable, you can run it. Simply do:
+
+ $ ./bspline_fitting ${PCL_ROOT}/test/bunny.pcd
+
+
+Saving and viewing the result
+-----------------------------
+
+* Saving as OpenNURBS (3dm) file
+You can save the B-spline surface by using the commands provided by OpenNurbs:
+
+.. literalinclude:: sources/bspline_fitting/bspline_fitting.cpp
+ :language: cpp
+ :lines: 145-163
+
+The files generated can be viewed with the pcl/examples/surface/example_nurbs_viewer_surface.cpp.
+
+* Saving as triangle mesh into a vtk file
+You can save the triangle mesh for example by saving into a VTK file by:
+
+ #include
+ ...
+ pcl::io::saveVTKFile ("mesh.vtk", mesh);
+
+PCL also provides vtk conversion into other formats (PLY, OBJ).
+
diff --git a/doc/tutorials/content/images/bspline_bunny.png b/doc/tutorials/content/images/bspline_bunny.png
new file mode 100644
index 00000000000..90fbd1b9cc6
Binary files /dev/null and b/doc/tutorials/content/images/bspline_bunny.png differ
diff --git a/doc/tutorials/content/index.rst b/doc/tutorials/content/index.rst
index 9ddf91a544e..d894e224d69 100644
--- a/doc/tutorials/content/index.rst
+++ b/doc/tutorials/content/index.rst
@@ -1025,6 +1025,22 @@ Surface
.. |su_3| image:: images/greedy_triangulation.png
:height: 100px
+ * :ref:`bspline_fitting`
+
+ ====== ======
+ |su_4| Title: **Fitting trimmed B-splines to unordered point clouds**
+
+ Author: *Thomas Mörwald*
+
+ Compatibility: > PCL 1.7
+
+ In this tutorial we will learn how to reconstruct a smooth surface from an unordered point-cloud by fitting trimmed B-splines.
+ ====== ======
+
+ .. |su_4| image:: images/bspline_bunny.png
+ :height: 100px
+
+
.. _visualization_tutorial:
Visualization
diff --git a/doc/tutorials/content/sources/bspline_fitting/CMakeLists.txt b/doc/tutorials/content/sources/bspline_fitting/CMakeLists.txt
new file mode 100644
index 00000000000..332515885ea
--- /dev/null
+++ b/doc/tutorials/content/sources/bspline_fitting/CMakeLists.txt
@@ -0,0 +1,13 @@
+cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
+
+project(bspline_fitting)
+
+find_package(PCL 1.7 REQUIRED)
+
+include_directories(${PCL_INCLUDE_DIRS})
+link_directories(${PCL_LIBRARY_DIRS})
+add_definitions(${PCL_DEFINITIONS})
+
+add_executable (bspline_fitting bspline_fitting.cpp)
+target_link_libraries (bspline_fitting ${PCL_LIBRARIES})
+
diff --git a/doc/tutorials/content/sources/bspline_fitting/bspline_fitting.cpp b/doc/tutorials/content/sources/bspline_fitting/bspline_fitting.cpp
new file mode 100644
index 00000000000..b5628ee64fc
--- /dev/null
+++ b/doc/tutorials/content/sources/bspline_fitting/bspline_fitting.cpp
@@ -0,0 +1,219 @@
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+typedef pcl::PointXYZ Point;
+
+void
+PointCloud2Vector3d (pcl::PointCloud::Ptr cloud, pcl::on_nurbs::vector_vec3d &data);
+
+void
+visualizeCurve (ON_NurbsCurve &curve,
+ ON_NurbsSurface &surface,
+ pcl::visualization::PCLVisualizer &viewer);
+
+int
+main (int argc, char *argv[])
+{
+ std::string pcd_file, file_3dm;
+
+ if (argc < 3)
+ {
+ printf ("\nUsage: pcl_example_nurbs_fitting_surface pcd-in-file 3dm-out-file\n\n");
+ exit (0);
+ }
+ pcd_file = argv[1];
+ file_3dm = argv[2];
+
+ pcl::visualization::PCLVisualizer viewer ("B-spline surface fitting");
+ viewer.setSize (800, 600);
+
+ // ############################################################################
+ // load point cloud
+
+ printf (" loading %s\n", pcd_file.c_str ());
+ pcl::PointCloud::Ptr cloud (new pcl::PointCloud);
+ pcl::PCLPointCloud2 cloud2;
+ pcl::on_nurbs::NurbsDataSurface data;
+
+ if (pcl::io::loadPCDFile (pcd_file, cloud2) == -1)
+ throw std::runtime_error (" PCD file not found.");
+
+ fromPCLPointCloud2 (cloud2, *cloud);
+ PointCloud2Vector3d (cloud, data.interior);
+ pcl::visualization::PointCloudColorHandlerCustom handler (cloud, 0, 255, 0);
+ viewer.addPointCloud (cloud, handler, "cloud_cylinder");
+ printf (" %zu points in data set\n", cloud->size ());
+
+ // ############################################################################
+ // fit B-spline surface
+
+ // parameters
+ unsigned order (3);
+ unsigned refinement (5);
+ unsigned iterations (10);
+ unsigned mesh_resolution (256);
+
+ pcl::on_nurbs::FittingSurface::Parameter params;
+ params.interior_smoothness = 0.2;
+ params.interior_weight = 1.0;
+ params.boundary_smoothness = 0.2;
+ params.boundary_weight = 0.0;
+
+ // initialize
+ printf (" surface fitting ...\n");
+ ON_NurbsSurface nurbs = pcl::on_nurbs::FittingSurface::initNurbsPCABoundingBox (order, &data);
+ pcl::on_nurbs::FittingSurface fit (&data, nurbs);
+ // fit.setQuiet (false); // enable/disable debug output
+
+ // mesh for visualization
+ pcl::PolygonMesh mesh;
+ pcl::PointCloud::Ptr mesh_cloud (new pcl::PointCloud);
+ std::vector mesh_vertices;
+ std::string mesh_id = "mesh_nurbs";
+ pcl::on_nurbs::Triangulation::convertSurface2PolygonMesh (fit.m_nurbs, mesh, mesh_resolution);
+ viewer.addPolygonMesh (mesh, mesh_id);
+
+ // surface refinement
+ for (unsigned i = 0; i < refinement; i++)
+ {
+ fit.refine (0);
+ fit.refine (1);
+ fit.assemble (params);
+ fit.solve ();
+ pcl::on_nurbs::Triangulation::convertSurface2Vertices (fit.m_nurbs, mesh_cloud, mesh_vertices, mesh_resolution);
+ viewer.updatePolygonMesh (mesh_cloud, mesh_vertices, mesh_id);
+ viewer.spinOnce ();
+ }
+
+ // surface fitting with final refinement level
+ for (unsigned i = 0; i < iterations; i++)
+ {
+ fit.assemble (params);
+ fit.solve ();
+ pcl::on_nurbs::Triangulation::convertSurface2Vertices (fit.m_nurbs, mesh_cloud, mesh_vertices, mesh_resolution);
+ viewer.updatePolygonMesh (mesh_cloud, mesh_vertices, mesh_id);
+ viewer.spinOnce ();
+ }
+
+ // ############################################################################
+ // fit B-spline curve
+
+ // 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)
+ printf (" curve fitting ...\n");
+ 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);
+ visualizeCurve (curve_fit.m_nurbs, fit.m_nurbs, viewer);
+
+ // ############################################################################
+ // triangulation of trimmed surface
+
+ printf (" triangulate trimmed surface ...\n");
+ viewer.removePolygonMesh (mesh_id);
+ pcl::on_nurbs::Triangulation::convertTrimmedSurface2PolygonMesh (fit.m_nurbs, curve_fit.m_nurbs, mesh,
+ mesh_resolution);
+ viewer.addPolygonMesh (mesh, mesh_id);
+
+
+ // save trimmed B-spline surface
+ if ( fit.m_nurbs.IsValid() )
+ {
+ ONX_Model model;
+ ONX_Model_Object& surf = model.m_object_table.AppendNew();
+ surf.m_object = new ON_NurbsSurface(fit.m_nurbs);
+ surf.m_bDeleteObject = true;
+ surf.m_attributes.m_layer_index = 1;
+ surf.m_attributes.m_name = "surface";
+
+ ONX_Model_Object& curv = model.m_object_table.AppendNew();
+ curv.m_object = new ON_NurbsCurve(curve_fit.m_nurbs);
+ curv.m_bDeleteObject = true;
+ curv.m_attributes.m_layer_index = 2;
+ curv.m_attributes.m_name = "trimming curve";
+
+ model.Write(file_3dm.c_str());
+ printf(" model saved: %s\n", file_3dm.c_str());
+ }
+
+ printf (" ... done.\n");
+
+ viewer.spin ();
+ return 0;
+}
+
+void
+PointCloud2Vector3d (pcl::PointCloud::Ptr cloud, pcl::on_nurbs::vector_vec3d &data)
+{
+ for (unsigned i = 0; i < cloud->size (); i++)
+ {
+ Point &p = cloud->at (i);
+ if (!pcl_isnan (p.x) && !pcl_isnan (p.y) && !pcl_isnan (p.z))
+ data.push_back (Eigen::Vector3d (p.x, p.y, p.z));
+ }
+}
+
+void
+visualizeCurve (ON_NurbsCurve &curve, ON_NurbsSurface &surface, pcl::visualization::PCLVisualizer &viewer)
+{
+ pcl::PointCloud::Ptr curve_cloud (new pcl::PointCloud);
+
+ pcl::on_nurbs::Triangulation::convertCurve2PointCloud (curve, surface, curve_cloud, 4);
+ for (std::size_t i = 0; i < curve_cloud->size () - 1; i++)
+ {
+ pcl::PointXYZRGB &p1 = curve_cloud->at (i);
+ pcl::PointXYZRGB &p2 = curve_cloud->at (i + 1);
+ std::ostringstream os;
+ os << "line" << i;
+ viewer.removeShape (os.str ());
+ viewer.addLine (p1, p2, 1.0, 0.0, 0.0, os.str ());
+ }
+
+ pcl::PointCloud::Ptr curve_cps (new pcl::PointCloud);
+ for (int i = 0; i < curve.CVCount (); i++)
+ {
+ ON_3dPoint p1;
+ curve.GetCV (i, p1);
+
+ double pnt[3];
+ surface.Evaluate (p1.x, p1.y, 0, 3, pnt);
+ pcl::PointXYZRGB p2;
+ p2.x = float (pnt[0]);
+ p2.y = float (pnt[1]);
+ p2.z = float (pnt[2]);
+
+ p2.r = 255;
+ p2.g = 0;
+ p2.b = 0;
+
+ curve_cps->push_back (p2);
+ }
+ viewer.removePointCloud ("cloud_cps");
+ viewer.addPointCloud (curve_cps, "cloud_cps");
+}
diff --git a/examples/surface/CMakeLists.txt b/examples/surface/CMakeLists.txt
index 1507f7871a8..9d105618d00 100644
--- a/examples/surface/CMakeLists.txt
+++ b/examples/surface/CMakeLists.txt
@@ -19,6 +19,10 @@ if(BUILD_surface_on_nurbs)
PCL_ADD_EXAMPLE(pcl_example_nurbs_fitting_surface
FILES example_nurbs_fitting_surface.cpp
LINK_WITH pcl_common pcl_io pcl_surface pcl_visualization)
+
+ PCL_ADD_EXAMPLE(pcl_example_nurbs_viewer_surface
+ FILES example_nurbs_viewer_surface.cpp
+ LINK_WITH pcl_common pcl_io pcl_surface pcl_visualization)
PCL_ADD_EXAMPLE(pcl_example_nurbs_fitting_closed_curve
FILES example_nurbs_fitting_closed_curve.cpp
diff --git a/examples/surface/example_nurbs_fitting_surface.cpp b/examples/surface/example_nurbs_fitting_surface.cpp
index 91e61112003..b5628ee64fc 100644
--- a/examples/surface/example_nurbs_fitting_surface.cpp
+++ b/examples/surface/example_nurbs_fitting_surface.cpp
@@ -10,78 +10,32 @@
typedef pcl::PointXYZ Point;
void
-PointCloud2Vector3d (pcl::PointCloud::Ptr cloud, pcl::on_nurbs::vector_vec3d &data)
-{
- for (unsigned i = 0; i < cloud->size (); i++)
- {
- Point &p = cloud->at (i);
- if (!pcl_isnan (p.x) && !pcl_isnan (p.y) && !pcl_isnan (p.z))
- data.push_back (Eigen::Vector3d (p.x, p.y, p.z));
- }
-}
+PointCloud2Vector3d (pcl::PointCloud::Ptr cloud, pcl::on_nurbs::vector_vec3d &data);
void
-visualizeCurve (ON_NurbsCurve &curve, ON_NurbsSurface &surface, pcl::visualization::PCLVisualizer &viewer)
-{
- pcl::PointCloud::Ptr curve_cloud (new pcl::PointCloud);
-
- pcl::on_nurbs::Triangulation::convertCurve2PointCloud (curve, surface, curve_cloud, 4);
- for (std::size_t i = 0; i < curve_cloud->size () - 1; i++)
- {
- pcl::PointXYZRGB &p1 = curve_cloud->at (i);
- pcl::PointXYZRGB &p2 = curve_cloud->at (i + 1);
- std::ostringstream os;
- os << "line" << i;
- viewer.removeShape (os.str ());
- viewer.addLine (p1, p2, 1.0, 0.0, 0.0, os.str ());
- }
-
- pcl::PointCloud::Ptr curve_cps (new pcl::PointCloud);
- for (int i = 0; i < curve.CVCount (); i++)
- {
- ON_3dPoint p1;
- curve.GetCV (i, p1);
-
- double pnt[3];
- surface.Evaluate (p1.x, p1.y, 0, 3, pnt);
- pcl::PointXYZRGB p2;
- p2.x = float (pnt[0]);
- p2.y = float (pnt[1]);
- p2.z = float (pnt[2]);
-
- p2.r = 255;
- p2.g = 0;
- p2.b = 0;
-
- curve_cps->push_back (p2);
- }
- viewer.removePointCloud ("cloud_cps");
- viewer.addPointCloud (curve_cps, "cloud_cps");
-}
+visualizeCurve (ON_NurbsCurve &curve,
+ ON_NurbsSurface &surface,
+ pcl::visualization::PCLVisualizer &viewer);
int
main (int argc, char *argv[])
{
- std::string pcd_file;
+ std::string pcd_file, file_3dm;
- if (argc < 2)
+ if (argc < 3)
{
- printf ("\nUsage: pcl_example_nurbs_fitting_surface pcd-file\n\n");
+ printf ("\nUsage: pcl_example_nurbs_fitting_surface pcd-in-file 3dm-out-file\n\n");
exit (0);
}
-
pcd_file = argv[1];
+ file_3dm = argv[2];
- unsigned order (3);
- unsigned refinement (6);
- unsigned iterations (10);
- unsigned mesh_resolution (256);
-
- pcl::visualization::PCLVisualizer viewer ("Test: NURBS surface fitting");
+ pcl::visualization::PCLVisualizer viewer ("B-spline surface fitting");
viewer.setSize (800, 600);
// ############################################################################
// load point cloud
+
printf (" loading %s\n", pcd_file.c_str ());
pcl::PointCloud::Ptr cloud (new pcl::PointCloud);
pcl::PCLPointCloud2 cloud2;
@@ -97,27 +51,35 @@ main (int argc, char *argv[])
printf (" %zu points in data set\n", cloud->size ());
// ############################################################################
- // fit NURBS surface
+ // fit B-spline surface
+
+ // parameters
+ unsigned order (3);
+ unsigned refinement (5);
+ unsigned iterations (10);
+ unsigned mesh_resolution (256);
+
+ pcl::on_nurbs::FittingSurface::Parameter params;
+ params.interior_smoothness = 0.2;
+ params.interior_weight = 1.0;
+ params.boundary_smoothness = 0.2;
+ params.boundary_weight = 0.0;
+
+ // initialize
printf (" surface fitting ...\n");
ON_NurbsSurface nurbs = pcl::on_nurbs::FittingSurface::initNurbsPCABoundingBox (order, &data);
pcl::on_nurbs::FittingSurface fit (&data, nurbs);
- // fit.setQuiet (false);
+ // fit.setQuiet (false); // enable/disable debug output
+ // mesh for visualization
pcl::PolygonMesh mesh;
pcl::PointCloud::Ptr mesh_cloud (new pcl::PointCloud);
std::vector mesh_vertices;
-
std::string mesh_id = "mesh_nurbs";
pcl::on_nurbs::Triangulation::convertSurface2PolygonMesh (fit.m_nurbs, mesh, mesh_resolution);
viewer.addPolygonMesh (mesh, mesh_id);
- pcl::on_nurbs::FittingSurface::Parameter params;
- params.interior_smoothness = 0.15;
- params.interior_weight = 1.0;
- params.boundary_smoothness = 0.15;
- params.boundary_weight = 0.0;
-
- // NURBS refinement
+ // surface refinement
for (unsigned i = 0; i < refinement; i++)
{
fit.refine (0);
@@ -129,7 +91,7 @@ main (int argc, char *argv[])
viewer.spinOnce ();
}
- // fitting iterations
+ // surface fitting with final refinement level
for (unsigned i = 0; i < iterations; i++)
{
fit.assemble (params);
@@ -140,44 +102,118 @@ main (int argc, char *argv[])
}
// ############################################################################
- // fit NURBS curve
+ // fit B-spline curve
+
+ // parameters
pcl::on_nurbs::FittingCurve2dAPDM::FitParameter curve_params;
- curve_params.addCPsAccuracy = 3e-2;
+ curve_params.addCPsAccuracy = 5e-2;
curve_params.addCPsIteration = 3;
curve_params.maxCPs = 200;
curve_params.accuracy = 1e-3;
- curve_params.iterations = 10000;
+ 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.0001;
+ curve_params.param.interior_sigma2 = 0.00001;
curve_params.param.smooth_concavity = 1.0;
curve_params.param.smoothness = 1.0;
+ // initialisation (circular)
+ printf (" curve fitting ...\n");
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);
-
- // ############################### FITTING ###############################
+ // curve_fit.setQuiet (false); // enable/disable debug output
curve_fit.fitting (curve_params);
visualizeCurve (curve_fit.m_nurbs, fit.m_nurbs, viewer);
// ############################################################################
// triangulation of trimmed surface
+
printf (" triangulate trimmed surface ...\n");
viewer.removePolygonMesh (mesh_id);
pcl::on_nurbs::Triangulation::convertTrimmedSurface2PolygonMesh (fit.m_nurbs, curve_fit.m_nurbs, mesh,
mesh_resolution);
viewer.addPolygonMesh (mesh, mesh_id);
+
+ // save trimmed B-spline surface
+ if ( fit.m_nurbs.IsValid() )
+ {
+ ONX_Model model;
+ ONX_Model_Object& surf = model.m_object_table.AppendNew();
+ surf.m_object = new ON_NurbsSurface(fit.m_nurbs);
+ surf.m_bDeleteObject = true;
+ surf.m_attributes.m_layer_index = 1;
+ surf.m_attributes.m_name = "surface";
+
+ ONX_Model_Object& curv = model.m_object_table.AppendNew();
+ curv.m_object = new ON_NurbsCurve(curve_fit.m_nurbs);
+ curv.m_bDeleteObject = true;
+ curv.m_attributes.m_layer_index = 2;
+ curv.m_attributes.m_name = "trimming curve";
+
+ model.Write(file_3dm.c_str());
+ printf(" model saved: %s\n", file_3dm.c_str());
+ }
+
printf (" ... done.\n");
viewer.spin ();
return 0;
}
+
+void
+PointCloud2Vector3d (pcl::PointCloud::Ptr cloud, pcl::on_nurbs::vector_vec3d &data)
+{
+ for (unsigned i = 0; i < cloud->size (); i++)
+ {
+ Point &p = cloud->at (i);
+ if (!pcl_isnan (p.x) && !pcl_isnan (p.y) && !pcl_isnan (p.z))
+ data.push_back (Eigen::Vector3d (p.x, p.y, p.z));
+ }
+}
+
+void
+visualizeCurve (ON_NurbsCurve &curve, ON_NurbsSurface &surface, pcl::visualization::PCLVisualizer &viewer)
+{
+ pcl::PointCloud::Ptr curve_cloud (new pcl::PointCloud);
+
+ pcl::on_nurbs::Triangulation::convertCurve2PointCloud (curve, surface, curve_cloud, 4);
+ for (std::size_t i = 0; i < curve_cloud->size () - 1; i++)
+ {
+ pcl::PointXYZRGB &p1 = curve_cloud->at (i);
+ pcl::PointXYZRGB &p2 = curve_cloud->at (i + 1);
+ std::ostringstream os;
+ os << "line" << i;
+ viewer.removeShape (os.str ());
+ viewer.addLine (p1, p2, 1.0, 0.0, 0.0, os.str ());
+ }
+
+ pcl::PointCloud::Ptr curve_cps (new pcl::PointCloud);
+ for (int i = 0; i < curve.CVCount (); i++)
+ {
+ ON_3dPoint p1;
+ curve.GetCV (i, p1);
+
+ double pnt[3];
+ surface.Evaluate (p1.x, p1.y, 0, 3, pnt);
+ pcl::PointXYZRGB p2;
+ p2.x = float (pnt[0]);
+ p2.y = float (pnt[1]);
+ p2.z = float (pnt[2]);
+
+ p2.r = 255;
+ p2.g = 0;
+ p2.b = 0;
+
+ curve_cps->push_back (p2);
+ }
+ viewer.removePointCloud ("cloud_cps");
+ viewer.addPointCloud (curve_cps, "cloud_cps");
+}
diff --git a/examples/surface/example_nurbs_viewer_surface.cpp b/examples/surface/example_nurbs_viewer_surface.cpp
new file mode 100644
index 00000000000..710cfac9ddc
--- /dev/null
+++ b/examples/surface/example_nurbs_viewer_surface.cpp
@@ -0,0 +1,88 @@
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+typedef pcl::PointXYZ Point;
+
+int
+main (int argc, char *argv[])
+{
+ std::string file_3dm;
+
+ if (argc < 2)
+ {
+ printf ("\nUsage: pcl_example_nurbs_viewer_surface 3dm-out-file\n\n");
+ exit (0);
+ }
+ file_3dm = argv[1];
+
+ pcl::visualization::PCLVisualizer viewer ("B-spline surface viewer");
+ viewer.setSize (800, 600);
+
+ int mesh_resolution = 128;
+
+ ON::Begin();
+
+ // load surface
+ ONX_Model on_model;
+ bool rc = on_model.Read(file_3dm.c_str());
+
+ // print diagnostic
+ if ( rc )
+ std::cout << "Successfully read: " << file_3dm << std::endl;
+ else
+ std::cout << "Errors during reading: " << file_3dm << std::endl;
+
+// ON_TextLog out;
+// on_model.Dump(out);
+
+ if(on_model.m_object_table.Count()==0)
+ {
+ std::cout << "3dm file does not contain any objects: " << file_3dm << std::endl;
+ return -1;
+ }
+
+ const ON_Object* on_object = on_model.m_object_table[0].m_object;
+ if(on_object==NULL)
+ {
+ std::cout << "object[0] not valid." << std::endl;
+ return -1;
+ }
+
+ const ON_NurbsSurface& on_surf = *(ON_NurbsSurface*)on_object;
+
+ pcl::PolygonMesh mesh;
+ std::string mesh_id = "mesh_nurbs";
+ if(on_model.m_object_table.Count()==1)
+ {
+ std::cout << "3dm file does not contain a trimming curve: " << file_3dm << std::endl;
+
+
+ pcl::on_nurbs::Triangulation::convertSurface2PolygonMesh (on_surf, mesh, mesh_resolution);
+ }
+ else
+ {
+ on_object = on_model.m_object_table[1].m_object;
+ if(on_object==NULL)
+ {
+ std::cout << "object[1] not valid." << std::endl;
+ return -1;
+ }
+
+ const ON_NurbsCurve& on_curv = *(ON_NurbsCurve*)on_object;
+
+ pcl::on_nurbs::Triangulation::convertTrimmedSurface2PolygonMesh (on_surf, on_curv, mesh,
+ mesh_resolution);
+ }
+
+ viewer.addPolygonMesh (mesh, mesh_id);
+
+ viewer.spin ();
+ return 0;
+}
+