00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 #include <pcl/surface/on_nurbs/fitting_curve_pdm.h>
00039 #include <stdexcept>
00040
00041 using namespace pcl;
00042 using namespace on_nurbs;
00043
00044 FittingCurve::FittingCurve (int order, NurbsDataCurve *data)
00045 {
00046 if (order < 2)
00047 throw std::runtime_error ("[NurbsFittingCylinder::NurbsFittingCylinder] Error order to low (order<2).");
00048
00049 ON::Begin ();
00050
00051 m_data = data;
00052 m_nurbs = initNurbsCurvePCA (order, m_data->interior);
00053
00054 in_max_steps = 100;
00055 in_accuracy = 1e-4;
00056 m_quiet = true;
00057 }
00058
00059 FittingCurve::FittingCurve (NurbsDataCurve *data, const ON_NurbsCurve &ns)
00060 {
00061 ON::Begin ();
00062
00063 m_nurbs = ON_NurbsCurve (ns);
00064 m_data = data;
00065
00066 in_max_steps = 100;
00067 in_accuracy = 1e-4;
00068 m_quiet = true;
00069 }
00070
00071 int
00072 FittingCurve::findElement (double xi, const std::vector<double> &elements)
00073 {
00074 if (xi >= elements.back ())
00075 return (int (elements.size ()) - 2);
00076
00077 for (unsigned i = 0; i < elements.size () - 1; i++)
00078 {
00079 if (xi >= elements[i] && xi < elements[i + 1])
00080 {
00081 return i;
00082 }
00083 }
00084
00085
00086 return 0;
00087
00088 }
00089
00090 void
00091 FittingCurve::refine ()
00092 {
00093 std::vector<double> xi;
00094
00095 std::vector<double> elements = this->getElementVector (m_nurbs);
00096
00097 for (unsigned i = 0; i < elements.size () - 1; i++)
00098 xi.push_back (elements[i] + 0.5 * (elements[i + 1] - elements[i]));
00099
00100 for (unsigned i = 0; i < xi.size (); i++)
00101 m_nurbs.InsertKnot (xi[i], 1);
00102 }
00103
00104 void
00105 FittingCurve::assemble (const Parameter ¶meter)
00106 {
00107 int cp_red = m_nurbs.m_order - 2;
00108 int ncp = m_nurbs.m_cv_count - 2 * cp_red;
00109 int nCageReg = m_nurbs.m_cv_count - 2 * cp_red;
00110 int nInt = int (m_data->interior.size ());
00111
00112 int nrows = nInt + nCageReg;
00113
00114 double wInt = 1.0;
00115 double wCageReg = parameter.smoothness;
00116
00117 m_solver.assign (nrows, ncp, 3);
00118
00119 unsigned row (0);
00120
00121 assembleInterior (wInt, row);
00122
00123 addCageRegularisation (wCageReg, row);
00124 }
00125
00126 void
00127 FittingCurve::solve (double damp)
00128 {
00129 if (m_solver.solve ())
00130 updateCurve (damp);
00131 }
00132
00133 void
00134 FittingCurve::updateCurve (double)
00135 {
00136 int cp_red = m_nurbs.m_order - 2;
00137 int ncp = m_nurbs.m_cv_count - 2 * cp_red;
00138
00139 for (int j = 0; j < ncp; j++)
00140 {
00141
00142 ON_3dPoint cp;
00143 cp.x = m_solver.x (j, 0);
00144 cp.y = m_solver.x (j, 1);
00145 cp.z = m_solver.x (j, 2);
00146
00147 m_nurbs.SetCV (j + cp_red, cp);
00148 }
00149
00150 for (int j = 0; j < cp_red; j++)
00151 {
00152
00153 ON_3dPoint cp;
00154 m_nurbs.GetCV (m_nurbs.m_cv_count - 1 - cp_red + j, cp);
00155 m_nurbs.SetCV (j, cp);
00156
00157 m_nurbs.GetCV (cp_red - j, cp);
00158 m_nurbs.SetCV (m_nurbs.m_cv_count - 1 - j, cp);
00159 }
00160 }
00161
00162 void
00163 FittingCurve::addPointConstraint (const double ¶m, const Eigen::Vector3d &point, double weight, unsigned &row)
00164 {
00165 int cp_red = m_nurbs.m_order - 2;
00166 int ncp = m_nurbs.m_cv_count - 2 * cp_red;
00167 double *N = new double[m_nurbs.m_order * m_nurbs.m_order];
00168
00169 int E = ON_NurbsSpanIndex (m_nurbs.m_order, m_nurbs.m_cv_count, m_nurbs.m_knot, param, 0, 0);
00170
00171 ON_EvaluateNurbsBasis (m_nurbs.m_order, m_nurbs.m_knot + E, param, N);
00172
00173 m_solver.f (row, 0, point (0) * weight);
00174 m_solver.f (row, 1, point (1) * weight);
00175 m_solver.f (row, 2, point (2) * weight);
00176
00177 for (int i = 0; i < m_nurbs.m_order; i++)
00178 m_solver.K (row, (E + i) % ncp, weight * N[i]);
00179
00180 row++;
00181
00182 delete [] N;
00183 }
00184
00185 void
00186 FittingCurve::addCageRegularisation (double weight, unsigned &row)
00187 {
00188 int cp_red = (m_nurbs.m_order - 2);
00189 int ncpj = (m_nurbs.m_cv_count - 2 * cp_red);
00190
00191 m_solver.f (row, 0, 0.0);
00192 m_solver.f (row, 1, 0.0);
00193 m_solver.f (row, 2, 0.0);
00194
00195 for (int j = 1; j < ncpj + 1; j++)
00196 {
00197
00198 m_solver.K (row, (j + 0) % ncpj, -2.0 * weight);
00199 m_solver.K (row, (j - 1) % ncpj, 1.0 * weight);
00200 m_solver.K (row, (j + 1) % ncpj, 1.0 * weight);
00201
00202 row++;
00203 }
00204 }
00205
00206 ON_NurbsCurve
00207 FittingCurve::initNurbsCurve2D (int order, const vector_vec2d &data)
00208 {
00209 if (data.empty ())
00210 printf ("[FittingCurve::initNurbsCurve2D] Warning, no boundary parameters available\n");
00211
00212 Eigen::Vector2d mean = NurbsTools::computeMean (data);
00213
00214 unsigned s = unsigned (data.size ());
00215
00216 double r (0.0);
00217 for (unsigned i = 0; i < s; i++)
00218 {
00219 Eigen::Vector2d d = data[i] - mean;
00220 double sn = d.squaredNorm ();
00221 if (sn > r)
00222 r = sn;
00223 }
00224 r = sqrt (r);
00225
00226 int ncpsV (2 * order);
00227 ON_NurbsCurve nurbs = ON_NurbsCurve (3, false, order, ncpsV);
00228 nurbs.MakePeriodicUniformKnotVector (1.0 / (ncpsV - order + 1));
00229
00230 double dcv = (2.0 * M_PI) / (ncpsV - order + 1);
00231 Eigen::Vector2d cv;
00232 for (int j = 0; j < ncpsV; j++)
00233 {
00234 cv (0) = r * sin (dcv * j);
00235 cv (1) = r * cos (dcv * j);
00236 cv = cv + mean;
00237 nurbs.SetCV (j, ON_3dPoint (cv (0), cv (1), 0.0));
00238 }
00239
00240 return nurbs;
00241 }
00242
00243 ON_NurbsCurve
00244 FittingCurve::initNurbsCurvePCA (int order, const vector_vec3d &data, int ncps, double rf)
00245 {
00246 if (data.empty ())
00247 printf ("[FittingCurve::initNurbsCurvePCA] Warning, no boundary parameters available\n");
00248
00249 Eigen::Vector3d mean;
00250 Eigen::Matrix3d eigenvectors;
00251 Eigen::Vector3d eigenvalues;
00252
00253 unsigned s = unsigned (data.size ());
00254
00255 NurbsTools::pca (data, mean, eigenvectors, eigenvalues);
00256
00257 eigenvalues = eigenvalues / s;
00258
00259 double r = rf * sqrt (eigenvalues (0));
00260
00261 if (ncps < 2 * order)
00262 ncps = 2 * order;
00263
00264 ON_NurbsCurve nurbs = ON_NurbsCurve (3, false, order, ncps);
00265 nurbs.MakePeriodicUniformKnotVector (1.0 / (ncps - order + 1));
00266
00267 double dcv = (2.0 * M_PI) / (ncps - order + 1);
00268 Eigen::Vector3d cv, cv_t;
00269 for (int j = 0; j < ncps; j++)
00270 {
00271 cv (0) = r * sin (dcv * j);
00272 cv (1) = r * cos (dcv * j);
00273 cv (2) = 0.0;
00274 cv_t = eigenvectors * cv + mean;
00275 nurbs.SetCV (j, ON_3dPoint (cv_t (0), cv_t (1), cv_t (2)));
00276 }
00277
00278 return nurbs;
00279 }
00280
00281 std::vector<double>
00282 FittingCurve::getElementVector (const ON_NurbsCurve &nurbs)
00283 {
00284 std::vector<double> result;
00285
00286 int idx_min = 0;
00287 int idx_max = nurbs.m_knot_capacity - 1;
00288 if (nurbs.IsClosed ())
00289 {
00290 idx_min = nurbs.m_order - 2;
00291 idx_max = nurbs.m_knot_capacity - nurbs.m_order + 1;
00292 }
00293
00294 const double* knotsU = nurbs.Knot ();
00295
00296 result.push_back (knotsU[idx_min]);
00297
00298
00299 for (int E = idx_min + 1; E <= idx_max; E++)
00300 {
00301
00302 if (knotsU[E] != knotsU[E - 1])
00303 result.push_back (knotsU[E]);
00304
00305 }
00306
00307 return result;
00308 }
00309
00310 void
00311 FittingCurve::assembleInterior (double wInt, unsigned &row)
00312 {
00313 int nInt = int (m_data->interior.size ());
00314 m_data->interior_line_start.clear ();
00315 m_data->interior_line_end.clear ();
00316 m_data->interior_error.clear ();
00317 m_data->interior_normals.clear ();
00318 for (int p = 0; p < nInt; p++)
00319 {
00320 Eigen::Vector3d pcp (m_data->interior[p] (0), m_data->interior[p] (1), m_data->interior[p] (2));
00321
00322
00323 double param;
00324 Eigen::Vector3d pt, t;
00325 double error;
00326 if (p < int (m_data->interior_param.size ()))
00327 {
00328 param = inverseMapping (m_nurbs, pcp, m_data->interior_param[p], error, pt, t, in_max_steps, in_accuracy);
00329 m_data->interior_param[p] = param;
00330 }
00331 else
00332 {
00333 param = findClosestElementMidPoint (m_nurbs, pcp);
00334 param = inverseMapping (m_nurbs, pcp, param, error, pt, t, in_max_steps, in_accuracy);
00335 m_data->interior_param.push_back (param);
00336 }
00337
00338 m_data->interior_error.push_back (error);
00339 m_data->interior_line_start.push_back (pcp);
00340 m_data->interior_line_end.push_back (pt);
00341
00342 addPointConstraint (m_data->interior_param[p], m_data->interior[p], wInt, row);
00343 }
00344 }
00345
00346 double
00347 FittingCurve::inverseMapping (const ON_NurbsCurve &nurbs, const Eigen::Vector3d &pt, const double &hint, double &error,
00348 Eigen::Vector3d &p, Eigen::Vector3d &t, int maxSteps, double accuracy, bool quiet)
00349 {
00350
00351
00352 double pointAndTangents[6];
00353
00354 double current, delta;
00355 Eigen::Vector3d r;
00356 std::vector<double> elements = getElementVector (nurbs);
00357 double minU = elements[0];
00358 double maxU = elements[elements.size () - 1];
00359
00360 current = hint;
00361
00362 for (int k = 0; k < maxSteps; k++)
00363 {
00364
00365 nurbs.Evaluate (current, 1, 3, pointAndTangents);
00366
00367 p (0) = pointAndTangents[0];
00368 p (1) = pointAndTangents[1];
00369 p (2) = pointAndTangents[2];
00370
00371 t (0) = pointAndTangents[3];
00372 t (1) = pointAndTangents[4];
00373 t (2) = pointAndTangents[5];
00374
00375 r = p - pt;
00376
00377
00378 int E = findElement (current, elements);
00379 double e = elements[E + 1] - elements[E];
00380
00381 delta = -(0.5 * e) * r.dot (t) / t.norm ();
00382
00383 if (std::fabs (delta) < accuracy)
00384 {
00385
00386 error = r.norm ();
00387 return current;
00388
00389 }
00390 else
00391 {
00392 current = current + delta;
00393
00394 if (current < minU)
00395 current = maxU - (minU - current);
00396 else if (current > maxU)
00397 current = minU + (current - maxU);
00398
00399 }
00400
00401 }
00402
00403 error = r.norm ();
00404
00405 if (!quiet)
00406 {
00407 printf ("[FittingCurve::inverseMapping] Warning: Method did not converge (%e %d).\n", accuracy, maxSteps);
00408 printf ("[FittingCurve::inverseMapping] hint: %f current: %f delta: %f error: %f\n", hint, current, delta, error);
00409 }
00410
00411 return current;
00412 }
00413
00414 double
00415 FittingCurve::findClosestElementMidPoint (const ON_NurbsCurve &nurbs, const Eigen::Vector3d &pt)
00416 {
00417 double hint (0.0);
00418 Eigen::Vector3d p, r;
00419 std::vector<double> elements = getElementVector (nurbs);
00420 double points[3];
00421
00422 double d_shortest (DBL_MAX);
00423
00424 for (unsigned i = 0; i < elements.size () - 1; i++)
00425 {
00426 double xi = elements[i] + 0.5 * (elements[i + 1] - elements[i]);
00427
00428 nurbs.Evaluate (xi, 0, 3, points);
00429 p (0) = points[0];
00430 p (1) = points[1];
00431 p (2) = points[2];
00432
00433 r = p - pt;
00434
00435 double d = r.squaredNorm ();
00436
00437 if (d < d_shortest)
00438 {
00439 d_shortest = d;
00440 hint = xi;
00441 }
00442 }
00443
00444 return hint;
00445 }
00446