xmsmesh  1.0
MePolyRedistributePtsCurvature.cpp
Go to the documentation of this file.
1 //------------------------------------------------------------------------------
6 //------------------------------------------------------------------------------
7 
8 //----- Included files ---------------------------------------------------------
9 
10 // 1. Precompiled header
11 
12 // 2. My own header
14 
15 // 3. Standard library headers
16 #include <map>
17 
18 // 4. External library headers
19 
20 // 5. Shared code headers
21 #include <xmscore/math/math.h>
22 #include <xmscore/misc/XmError.h>
23 #include <xmscore/points/pt.h>
24 #include <xmscore/stl/vector.h>
26 
27 // 6. Non-shared code headers
28 
29 //----- Forward declarations ---------------------------------------------------
30 
31 //----- External globals -------------------------------------------------------
32 
33 //----- Namespace declaration --------------------------------------------------
34 namespace xms
35 {
36 //----- Constants / Enumerations -----------------------------------------------
37 
38 //----- Classes / Structs ------------------------------------------------------
39 
40 //----- Internal functions -----------------------------------------------------
41 
42 //----- Class / Function definitions -------------------------------------------
43 
46 {
47 public:
49 
50  virtual VecPt3d Redistribute(const VecPt3d& a_points,
51  double a_featureSize,
52  double a_mean_spacing,
53  double a_minimumCurvature = 0.001,
54  bool a_smooth = false);
55  void Setup(const VecPt3d&);
56  VecPt3d PlacePoints(double a_featureSize,
57  int a_numPoints,
58  double a_minimumCurvature,
59  bool a_smooth);
60  void GetSignificantPoints(double a_featureSize);
61  void CalculateCurvature(double a_featureSize, double a_minimumCurvature);
62  double GetCurvatureFromParameter(double a_param, double a_interval);
63  Pt3d GetPointFromParameter(double a_param);
64  void GetParameterIFM(double a_param, double a_interval, double& a_ti, double& a_tm, double& a_tf);
65  void ShiftAndAggregateOpen();
67  void DoSmoothing(bool a_smooth);
68  VecPt3d NewPointsFromParamCurvs(int a_numPoints);
69 
75  double m_length = 0.0;
76  bool m_open = false;
77  double m_tol = 1e-6;
78  std::map<double, int> m_distMap;
79 };
80 //------------------------------------------------------------------------------
83 //------------------------------------------------------------------------------
84 BSHP<MePolyRedistributePtsCurvature> MePolyRedistributePtsCurvature::New()
85 {
86  BSHP<MePolyRedistributePtsCurvature> ret(new MePolyRedistributePtsCurvatureImpl());
87  return ret;
88 } // MePolyRedistributePtsCurvature::New
89 //------------------------------------------------------------------------------
91 //------------------------------------------------------------------------------
93 {
94 } // MePolyRedistributePts::~MePolyRedistributePts
95 //------------------------------------------------------------------------------
97 //------------------------------------------------------------------------------
99 {
100 } // MePolyRedistributePtsCurvature::MePolyRedistributePtsCurvature
101 
102 //------------------------------------------------------------------------------
114 //------------------------------------------------------------------------------
116  double a_featureSize,
117  double a_meanSpacing,
118  double a_minimumCurvature,
119  bool a_smooth)
120 {
121  XM_ENSURE_TRUE(!a_points.empty(), VecPt3d());
122 
123  Setup(a_points);
124  int numPoints = int(m_length / a_meanSpacing);
125  // if open, numPoints should be odd for an even number of segments; if clossed, it should be
126  // even.
127  return PlacePoints(a_featureSize, numPoints, a_minimumCurvature, a_smooth);
128 } // MePolyRedistributePtsCurvatureImpl::Redistribute
129 //------------------------------------------------------------------------------
132 //------------------------------------------------------------------------------
134 {
135  m_parametricDistance.clear();
136  m_curvature.clear();
137 
138  m_points = a_points;
139  Pt3d pMin, pMax;
140  gmExtents2D(m_points, pMin, pMax);
141  m_tol = Mdist(pMin.x, pMin.y, pMax.x, pMax.y) * 1e-9;
142  m_open = m_points.back() != m_points.front();
143  m_segmentLengths.assign(m_points.size(), 0.0);
144  m_accumulatedSegmentLengths.assign(m_points.size(), 0.0);
145 
146  double total(0.0);
147  for (size_t i = 0; i < m_points.size() - 1; ++i)
148  {
149  const Pt3d& p0 = a_points[i];
150  const Pt3d& p1 = a_points[i + 1];
151  double length = Mdist(p0.x, p0.y, p1.x, p1.y);
152  m_segmentLengths[i] = length;
153  m_accumulatedSegmentLengths[i] = total;
154  total += length;
155  }
156  m_length = total;
157 } // MePolyRedistributePtsCurvatureImpl::SetUp
158 //------------------------------------------------------------------------------
170 //------------------------------------------------------------------------------
172  int a_numPoints,
173  double a_minimumCurvature,
174  bool a_smooth)
175 {
176  GetSignificantPoints(a_featureSize);
177  CalculateCurvature(a_featureSize, a_minimumCurvature);
178 
179  if (m_open)
180  {
182  }
183  else
184  {
186  }
187  DoSmoothing(a_smooth);
188 
189  return NewPointsFromParamCurvs(a_numPoints);
190 } // MePolyRedistributePtsCurvatureImpl::PlacePoints
191 //------------------------------------------------------------------------------
198 //------------------------------------------------------------------------------
200  double a_minimumCurvature)
201 {
202  m_distMap.clear();
203  int i(0);
204  for (const auto& d : m_accumulatedSegmentLengths)
205  {
206  m_distMap.insert(std::make_pair(d, i));
207  i++;
208  }
209 
210  double interval = a_featureSize / m_length;
211  for (size_t i = 0; i < m_parametricDistance.size(); ++i)
212  {
213  double curv(0.0);
214  if (m_curvature[i] < 0) // Not calculated yet .isnan()
215  {
216  curv = GetCurvatureFromParameter(m_parametricDistance[i], interval);
217  // This puts a minimum limit to the curvature
218  curv = std::max(a_minimumCurvature, fabs(curv));
219  m_curvature[i] = curv;
220  }
221  }
222 } // MePolyRedistributePtsCurvatureImpl::CalculateCurvature
223 //------------------------------------------------------------------------------
227 //------------------------------------------------------------------------------
229 {
230  double sumDistance(0.0);
231  double total = m_length;
232  double halfFeatureSize = a_featureSize / 2.0;
233  for (size_t i = 0; i < m_points.size() - 1; ++i)
234  {
235  double delta_d = m_segmentLengths[i];
236  XM_ASSERT(sumDistance == m_accumulatedSegmentLengths[i]);
237  m_parametricDistance.push_back(sumDistance / total);
238  m_curvature.push_back(-1);
239  if (delta_d > a_featureSize) // Add two params half a featureSize from each segment end
240  {
241  m_parametricDistance.push_back((sumDistance + halfFeatureSize) / total);
242  m_curvature.push_back(0);
243 
244  m_parametricDistance.push_back((sumDistance + delta_d - halfFeatureSize) / total);
245  m_curvature.push_back(0);
246  }
247  else
248  {
249  m_parametricDistance.push_back((sumDistance + 0.5 * delta_d) / total);
250  m_curvature.push_back(0);
251  }
252  sumDistance += delta_d;
253  }
254  XM_ASSERT(sumDistance == total);
255 
256  m_parametricDistance.push_back(1);
257  m_curvature.push_back(-1);
258 } // MePolyRedistributePtsCurvatureImpl::GetSignificantPoints
259 //------------------------------------------------------------------------------
266 //------------------------------------------------------------------------------
268  double a_interval)
269 {
270  double ti, tm, tf;
271  GetParameterIFM(a_param, a_interval, ti, tm, tf);
272  Pt3d pi = GetPointFromParameter(ti);
273  Pt3d pm = GetPointFromParameter(tm);
274  Pt3d pf = GetPointFromParameter(tf);
275 
276  // get curvature
277  double xc, yc, r2;
278  gmCircumcircleWithTol(&pi, &pm, &pf, &xc, &yc, &r2, m_tol);
279  double r = sqrt(r2);
280  return 1 / r;
281 } // MePolyRedistributePtsCurvatureImpl::GetCurvatureFromParameter
282 //------------------------------------------------------------------------------
286 //------------------------------------------------------------------------------
288 {
289  int idx(-1);
290  double t = std::min(1.0, std::max(0.0, a_param));
291  double station = t * m_length;
292  auto it = m_distMap.lower_bound(station);
293  if (it != m_distMap.begin())
294  it--;
295  if (it != m_distMap.end())
296  {
297  idx = it->second;
298  }
299 
300  if (idx > -1)
301  {
302  double d0 = m_accumulatedSegmentLengths[idx];
303  double d1 = d0 + m_segmentLengths[idx];
304  if (d0 <= station && station <= d1)
305  {
306  double fraction = (station - d0) / (d1 - d0);
307  Pt3d& p0 = m_points[idx];
308  Pt3d& p1 = m_points[idx + 1];
309  Pt3d pt;
310  pt.x = p0.x + fraction * (p1.x - p0.x);
311  pt.y = p0.y + fraction * (p1.y - p0.y);
312  return pt;
313  }
314  }
315  return m_points.back();
316 } // MePolyRedistributePtsCurvatureImpl::GetPointFromParameter
317 //------------------------------------------------------------------------------
325 //------------------------------------------------------------------------------
327  double a_interval,
328  double& a_ti,
329  double& a_tm,
330  double& a_tf)
331 {
332  a_ti = a_param - a_interval;
333  a_tf = a_param + a_interval;
334  a_tm = a_param;
335  if (m_open)
336  {
337  if (a_ti < 0.0)
338  {
339  a_ti = 0.0;
340  a_tm = 0.5 * (a_ti + a_tf);
341  }
342  else if (a_tf > 1.0)
343  {
344  a_tf = 1.0;
345  a_tm = 0.5 * (a_ti + a_tf);
346  }
347  }
348  else
349  {
350  if (a_ti < 0.0)
351  {
352  a_ti += 1.0;
353  }
354  if (a_tf > 1.0)
355  {
356  a_tf -= 1.0;
357  }
358  }
359 } // MePolyRedistributePtsCurvatureImpl::GetParameterIFM
360 //------------------------------------------------------------------------------
362 //------------------------------------------------------------------------------
364 {
365  VecDbl shiftedParametricDistance(m_parametricDistance.size() + 1);
366  VecDbl shiftedCurvature(m_curvature.size() + 1);
367 
368  // shifted is 1 longer than a_paramCurv so this has to happend once:
369  double agg_curv = 0;
370  for (size_t i = 1; i < shiftedCurvature.size() - 1; ++i)
371  {
372  double pStart = m_parametricDistance[i - 1];
373  agg_curv += m_curvature[i - 1];
374  double p(0.5 * (pStart + m_parametricDistance[i]));
375  shiftedParametricDistance[i] = p;
376  shiftedCurvature[i] = agg_curv;
377  }
378  shiftedParametricDistance.back() = 1.0;
379  shiftedCurvature.back() = agg_curv;
380  m_parametricDistance.swap(shiftedParametricDistance);
381  m_curvature.swap(shiftedCurvature);
382 } // MePolyRedistributePtsCurvatureImpl::ShiftAndAggregateOpen
383 //------------------------------------------------------------------------------
385 //------------------------------------------------------------------------------
387 {
388  // a is the point before the end.
389  // b is the point at the end and the beginning.
390  // c is the point after the beginning.
391  XM_ASSERT(m_parametricDistance.size() >= 2);
392  // XM_ASSERT(a_paramCurvs.back() == a_paramCurvs.front());
394  VecDbl& curve(m_curvature);
395  int idxA(static_cast<int>(m_parametricDistance.size() - 2)), idxB(0), idxC(1);
396  double p_a(pdist[idxA]), p_c(pdist[idxC]);
397  double curv_a(curve[idxA]), curv_b(curve[idxB]);
398  double s_ab(1.0 - p_a);
399  double s_bc(p_c); // p_c - 0.0;
400  double proportion = s_ab / (s_ab + s_bc);
401  // Curvature at m_param = 0 and 1 after shifting.
402  double curv_0 = curv_a + proportion * (curv_b - curv_a);
403 
404  VecDbl shiftedPdist(pdist.size() + 1);
405  VecDbl shiftedCurve(curve.size() + 1);
406  shiftedCurve[0] = curv_0;
407  for (size_t i = 1; i < shiftedPdist.size() - 1; ++i)
408  {
409  double p(0.5 * (pdist[i - 1] + pdist[i]));
410  shiftedPdist[i] = p;
411  shiftedCurve[i] = curve[i - 1];
412  }
413  shiftedPdist.back() = 1.0;
414  shiftedCurve.back() = curv_0;
415 
416  // Now Aggregate
417  VecDbl aggregatedCurve(shiftedCurve.size());
418  // we start with zero curvature
419  aggregatedCurve[0] = 0.0;
420  // the second point we set the aggregated to be just the delta between 0 and 1
421  // because m_param=0 doesn't have a m_curv=0. After the second point we just
422  // aggregate by summing. We have not thought of a way to make this more clear.
423  double deltaCurvature = shiftedCurve[1] - shiftedCurve[0];
424  aggregatedCurve[1] = deltaCurvature;
425  double agg_curv(aggregatedCurve[1]);
426  for (size_t i = 2; i < shiftedCurve.size(); ++i)
427  {
428  agg_curv += shiftedCurve[i];
429  aggregatedCurve[i] = agg_curv;
430  }
431 
432  m_parametricDistance.swap(shiftedPdist);
433  m_curvature.swap(aggregatedCurve);
434 } // MePolyRedistributePtsCurvatureImpl::ShiftAndAggregateClosed
435 //------------------------------------------------------------------------------
438 //------------------------------------------------------------------------------
440 {
441  if (!a_smooth)
442  return;
443 
444  // size_t n = a_paramCurvs.size() - 1;
445  size_t n = m_curvature.size() - 1;
446  VecDbl smoothCurve(m_curvature.size());
447  smoothCurve[0] = m_curvature[0];
448  for (size_t i = 1; i < n; ++i)
449  {
450  double smooth_curv = 0.25 * (m_curvature[i - 1] + m_curvature[i + 1]) + 0.5 * m_curvature[i];
451  smoothCurve[i] = smooth_curv;
452  }
453  smoothCurve[n] = m_curvature[n];
454  m_curvature.swap(smoothCurve);
455 } // MePolyRedistributePtsCurvatureImpl::DoSmoothing
456 //------------------------------------------------------------------------------
460 //------------------------------------------------------------------------------
462 {
463  // The height of the accumulated curvature is divided by the desired number of points
464  double delta_threshold = m_curvature.back() / (a_numPoints - 1);
465  double tol(1e-9), sumDelta(0);
466  for (int i = 0; i < a_numPoints - 1; ++i)
467  {
468  sumDelta += delta_threshold;
469  }
470  tol = fabs(m_curvature.back() - sumDelta) * 10;
471  double threshold(0.0);
472 
473  VecPt3d result;
474  // size_t n = a_paramCurvs.size() - 1;
475  size_t n = m_parametricDistance.size() - 1;
476  for (size_t i = 0; i < n; ++i)
477  {
478  // ParamCurv& pc0 = a_paramCurvs[i];
479  // double p0 = pc0.m_param;
480  double p0 = m_parametricDistance[i];
481  // double c0 = pc0.m_curv;
482  double c0 = m_curvature[i];
483  // ParamCurv& pc1 = a_paramCurvs[i + 1];
484  // double p1 = pc1.m_param;
485  double p1 = m_parametricDistance[i + 1];
486  // double c1 = pc1.m_curv;
487  double c1 = m_curvature[i + 1];
488  while (LT_TOL(threshold, c1, tol))
489  {
490  double t = (threshold - c0) / (c1 - c0);
491  double p = p0 + t * (p1 - p0);
492  Pt3d pt = GetPointFromParameter(p);
493  result.push_back(pt);
494  threshold += delta_threshold;
495  }
496  }
497  result.push_back(m_points.back());
498  return result;
499 } // MePolyRedistributePtsCurvatureImpl::NewPointsFromParamCurvs
500 
501 } // namespace xms
502 
503 #ifdef CXX_TEST
504 
507 
508 #include <fstream>
509 
510 #include <xmscore/misc/StringUtil.h>
513 
514 using namespace xms;
515 
520 //------------------------------------------------------------------------------
522 //------------------------------------------------------------------------------
524 {
525  BSHP<MePolyRedistributePtsCurvature> b = MePolyRedistributePtsCurvature::New();
526  TS_ASSERT(b);
527 } // MePolyRedistributePtsUnitTests::testCreateClass#endif
528 //------------------------------------------------------------------------------
540 //------------------------------------------------------------------------------
542 {
544  VecPt3d pts = {{0, 0, 0}, {5, 5, 0}, {10, 10, 0}, {15, 5, 0}, {20, 10, 0}, {25, 0, 0}};
545  r.Setup(pts);
546 
547  {
548  TS_ASSERT(r.m_parametricDistance.empty());
549  TS_ASSERT(r.m_curvature.empty());
550  double tol(1e-3);
551  VecDbl expectedAccumulatedSegmentLengths = {0.0, 7.071, 14.142, 21.213, 28.284, 0};
552  TS_ASSERT_DELTA_VEC(expectedAccumulatedSegmentLengths, r.m_accumulatedSegmentLengths, tol);
553 
554  double expectedLength(39.464);
555  TS_ASSERT_DELTA(expectedLength, r.m_length, tol);
556 
557  TS_ASSERT(true == r.m_open);
558 
559  TS_ASSERT(pts == r.m_points);
560 
561  VecDbl expectedSegmentLengths = {7.071, 7.071, 7.071, 7.071, 11.180, 0};
562  TS_ASSERT_DELTA_VEC(expectedSegmentLengths, r.m_segmentLengths, tol);
563 
564  double expectTolerance(2.6925824035672520e-008);
565  TS_ASSERT_DELTA(expectTolerance, r.m_tol, 1e-7);
566  }
567 
568  pts.push_back(pts.front());
569  r.Setup(pts);
570  { // test with a closed loop
571  TS_ASSERT(r.m_parametricDistance.empty());
572  TS_ASSERT(r.m_curvature.empty());
573  double tol(1e-3);
574  VecDbl expectedAccumulatedSegmentLengths = {0.0, 7.071, 14.142, 21.213, 28.284, 39.464, 0};
575  TS_ASSERT_DELTA_VEC(expectedAccumulatedSegmentLengths, r.m_accumulatedSegmentLengths, tol);
576 
577  double expectedLength(64.464);
578  TS_ASSERT_DELTA(expectedLength, r.m_length, tol);
579 
580  TS_ASSERT(false == r.m_open);
581 
582  TS_ASSERT(pts == r.m_points);
583 
584  VecDbl expectedSegmentLengths = {7.071, 7.071, 7.071, 7.071, 11.180, 25.0, 0};
585  TS_ASSERT_DELTA_VEC(expectedSegmentLengths, r.m_segmentLengths, tol);
586 
587  double expectTolerance(2.6925824035672520e-008);
588  TS_ASSERT_DELTA(expectTolerance, r.m_tol, 1e-7);
589  }
590 
591 } // MePolyRedistributePtsCurvatureUnitTests::testSetup
592 //------------------------------------------------------------------------------
604 //------------------------------------------------------------------------------
606 {
608  VecPt3d pts = {{0, 0, 0}, {5, 5, 0}, {10, 10, 0}, {15, 5, 0}, {20, 10, 0}, {25, 0, 0}};
609  r.Setup(pts);
610  double featureSize(3.0);
611  r.GetSignificantPoints(featureSize);
612 
613  {
614  VecDbl expectedParam = {0, 0.038, 0.141, 0.179, 0.217, 0.320, 0.358, 0.396,
615  0.500, 0.538, 0.576, 0.679, 0.717, 0.755, 0.962, 1};
616  TS_ASSERT_DELTA_VEC(expectedParam, r.m_parametricDistance, 1e-3);
617  VecDbl expectedCurvature = {-1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1};
618  TS_ASSERT_DELTA_VEC(expectedCurvature, r.m_curvature, 1e-12);
619  }
620 } // MePolyRedistributePtsCurvatureUnitTests::testGetSignificantPoints
621 //------------------------------------------------------------------------------
633 //------------------------------------------------------------------------------
635 {
637  VecPt3d pts = {{0, 0, 0}, {5, 5, 0}, {10, 10, 0}, {15, 5, 0},
638  {20, 10, 0}, {21, 10, 0}, {25, 0, 0}};
639  r.Setup(pts);
640  double featureSize(3.0);
641  double minCurvature(0.001);
642  r.GetSignificantPoints(featureSize);
643  r.CalculateCurvature(featureSize, minCurvature);
644  {
645  VecDbl expectedParam = {0, 0.037, 0.139, 0.177, 0.214, 0.316, 0.353, 0.391, 0.492,
646  0.530, 0.567, 0.669, 0.706, 0.719, 0.731, 0.769, 0.963, 1};
647  TS_ASSERT_DELTA_VEC(expectedParam, r.m_parametricDistance, 1e-3);
648  VecDbl expectedCurvature = {0.001, 0.000, 0.000, 0.001, 0.000, 0.000, 0.471, 0.000, 0.000,
649  0.471, 0.000, 0.000, 0.516, 0.000, 0.522, 0.000, 0.000, 0.001};
650  TS_ASSERT_DELTA_VEC(expectedCurvature, r.m_curvature, 1e-3);
651  }
652  // now do a closed loop, make the last point the same as the first
653  pts.push_back(pts.front());
654  r.Setup(pts);
655  r.GetSignificantPoints(featureSize);
656  r.CalculateCurvature(featureSize, minCurvature);
657  {
658  VecDbl expectedParam = {0.000, 0.023, 0.086, 0.109, 0.132, 0.194, 0.217,
659  0.240, 0.303, 0.326, 0.349, 0.412, 0.435, 0.442,
660  0.450, 0.473, 0.593, 0.616, 0.639, 0.977, 1.000};
661  TS_ASSERT_DELTA_VEC(expectedParam, r.m_parametricDistance, 1e-3);
662  VecDbl expectedCurvature = {0.616, 0.000, 0.000, 0.001, 0.000, 0.000, 0.471,
663  0.000, 0.000, 0.471, 0.000, 0.000, 0.516, 0.000,
664  0.522, 0.000, 0.000, 0.552, 0.000, 0.000, 0.616};
665  TS_ASSERT_DELTA_VEC(expectedCurvature, r.m_curvature, 1e-3);
666  }
667 } // MePolyRedistributePtsCurvatureUnitTests::testCalculateCurvature
668 //------------------------------------------------------------------------------
680 //------------------------------------------------------------------------------
682 {
684  VecPt3d pts = {{0, 0, 0}, {5, 5, 0}, {10, 10, 0}, {15, 5, 0},
685  {20, 10, 0}, {21, 10, 0}, {25, 0, 0}};
686  r.Setup(pts);
687  double featureSize(3.0);
688  double minCurvature(0.001);
689  r.GetSignificantPoints(featureSize);
690  r.CalculateCurvature(featureSize, minCurvature);
692  {
693  VecDbl expectedParam = {0.000, 0.019, 0.088, 0.158, 0.195, 0.265, 0.334, 0.372, 0.441, 0.511,
694  0.548, 0.618, 0.687, 0.712, 0.725, 0.750, 0.866, 0.981, 1.000};
695  TS_ASSERT_DELTA_VEC(expectedParam, r.m_parametricDistance, 1e-3);
696  VecDbl expectedCurvature = {0.000, 0.001, 0.001, 0.001, 0.002, 0.002, 0.002,
697  0.473, 0.473, 0.473, 0.945, 0.945, 0.945, 1.461,
698  1.461, 1.983, 1.983, 1.983, 1.983};
699  TS_ASSERT_DELTA_VEC(expectedCurvature, r.m_curvature, 1e-3);
700  }
701 } // MePolyRedistributePtsCurvatureUnitTests::testShiftAndAggregateOpen
702 //------------------------------------------------------------------------------
714 //------------------------------------------------------------------------------
716 {
718  VecPt3d pts = {{0, 0, 0}, {5, 5, 0}, {10, 10, 0}, {15, 5, 0},
719  {20, 10, 0}, {21, 10, 0}, {25, 0, 0}, {0, 0, 0}};
720  r.Setup(pts);
721  double featureSize(3.0);
722  double minCurvature(0.001);
723  r.GetSignificantPoints(featureSize);
724  r.CalculateCurvature(featureSize, minCurvature);
726  {
727  VecDbl expectedParam = {0.000, 0.012, 0.054, 0.097, 0.120, 0.163, 0.206, 0.229,
728  0.272, 0.315, 0.338, 0.380, 0.423, 0.439, 0.446, 0.462,
729  0.533, 0.604, 0.627, 0.808, 0.988, 1.000};
730  TS_ASSERT_DELTA_VEC(expectedParam, r.m_parametricDistance, 1e-3);
731  VecDbl expectedCurvature = {0.000, 0.308, 0.308, 0.308, 0.309, 0.309, 0.309, 0.780,
732  0.780, 0.780, 1.252, 1.252, 1.252, 1.768, 1.768, 2.290,
733  2.290, 2.290, 2.842, 2.842, 2.842, 3.150};
734  TS_ASSERT_DELTA_VEC(expectedCurvature, r.m_curvature, 1e-3);
735  }
736 } // MePolyRedistributePtsCurvatureUnitTests::testShiftAndAggregateClosed
737 //------------------------------------------------------------------------------
749 //------------------------------------------------------------------------------
751 {
753  VecPt3d pts = {{0, 0, 0}, {5, 5, 0}, {10, 10, 0}, {15, 5, 0},
754  {20, 10, 0}, {21, 10, 0}, {25, 0, 0}};
755  r.Setup(pts);
756  double featureSize(3.0);
757  double minCurvature(0.001);
758  r.GetSignificantPoints(featureSize);
759  r.CalculateCurvature(featureSize, minCurvature);
761  bool smooth(true);
762  r.DoSmoothing(smooth);
763  {
764  VecDbl expectedParam = {0.000, 0.019, 0.088, 0.158, 0.195, 0.265, 0.334, 0.372, 0.441, 0.511,
765  0.548, 0.618, 0.687, 0.712, 0.725, 0.750, 0.866, 0.981, 1.000};
766  TS_ASSERT_DELTA_VEC(expectedParam, r.m_parametricDistance, 1e-3);
767  VecDbl expectedCurvature = {0.00000, 0.00075, 0.00100, 0.00125, 0.00175, 0.00200, 0.11985,
768  0.35555, 0.47340, 0.59126, 0.82696, 0.94481, 1.07384, 1.33190,
769  1.59154, 1.85277, 1.98338, 1.98338, 1.98338};
770  TS_ASSERT_DELTA_VEC(expectedCurvature, r.m_curvature, 1e-3);
771  }
772 } // MePolyRedistributePtsCurvatureUnitTests::testDoSmoothing
773 //------------------------------------------------------------------------------
785 //------------------------------------------------------------------------------
787 {
789  VecPt3d pts = {{0, 0, 0}, {5, 5, 0}, {10, 10, 0}, {15, 5, 0},
790  {20, 10, 0}, {21, 10, 0}, {25, 0, 0}};
791  r.Setup(pts);
792  double featureSize(3.0);
793  double minCurvature(0.001);
794  r.GetSignificantPoints(featureSize);
795  r.CalculateCurvature(featureSize, minCurvature);
797  int numPoints(10);
798  {
799  VecPt3d outPts = r.NewPointsFromParamCurvs(numPoints);
800  VecPt3d expectedPoints{{0, 0, 0}, {9.961, 9.961, 0}, {10.457, 9.543, 0},
801  {14.892, 5.108, 0}, {15.388, 5.388, 0}, {19.685, 9.685, 0},
802  {19.987, 9.987, 0}, {20.906, 10.000, 0}, {21.122, 9.695, 0},
803  {25.000, 0.000, 0}};
804  TS_ASSERT_DELTA_VECPT3D(expectedPoints, outPts, 1e-3);
805  }
806  // now with smoothing
807  {
808  bool smooth(true);
809  r.DoSmoothing(smooth);
810  VecPt3d outPts = r.NewPointsFromParamCurvs(numPoints);
811  VecPt3d expectedPoints{{0, 0, 0}, {9.922, 9.922, 0}, {11.954, 8.046, 0},
812  {14.784, 5.216, 0}, {16.442, 6.442, 0}, {19.546, 9.546, 0},
813  {20.213, 10.000, 0}, {20.656, 10.000, 0}, {21.151, 9.623, 0},
814  {25.000, 0.000, 0}};
815  TS_ASSERT_DELTA_VECPT3D(expectedPoints, outPts, 1e-3);
816  }
817 
818  // now do a closed loop
819  pts.push_back(pts.front());
820  r.Setup(pts);
821  r.GetSignificantPoints(featureSize);
822  r.CalculateCurvature(featureSize, minCurvature);
824  {
825  VecPt3d outPts = r.NewPointsFromParamCurvs(numPoints);
826  VecPt3d expectedPoints{{0, 0, 0}, {9.562, 9.562, 0}, {10.350, 9.650, 0},
827  {15.077, 5.077, 0}, {19.673, 9.673, 0}, {20.216, 10.000, 0},
828  {21.143, 9.641, 0}, {24.883, 0.293, 0}, {24.364, 0.000, 0},
829  {0, 0, 0}};
830  TS_ASSERT_DELTA_VECPT3D(expectedPoints, outPts, 1e-3);
831  }
832  // now with smoothing
833  {
834  bool smooth(true);
835  r.DoSmoothing(smooth);
836  VecPt3d outPts = r.NewPointsFromParamCurvs(numPoints);
837  VecPt3d expectedPoints{{0, 0, 0}, {8.187, 8.187, 0}, {11.158, 8.842, 0},
838  {15.153, 5.153, 0}, {19.523, 9.523, 0}, {20.464, 10.000, 0},
839  {21.194, 9.515, 0}, {24.766, 0.586, 0}, {16.082, 0.000, 0},
840  {0, 0, 0}};
841  TS_ASSERT_DELTA_VECPT3D(expectedPoints, outPts, 1e-3);
842  }
843 } // MePolyRedistributePtsCurvatureUnitTests::testNewPointsFromParamCurvs
844 //------------------------------------------------------------------------------
846 //------------------------------------------------------------------------------
848 {
849  std::string path(std::string(XMS_TEST_PATH) + "redistribution/");
850  std::string infile(path + "Coastline.txt"), outFile(path + "Coastline_out.txt"),
851  baseFile(path + "Coastline_base.txt");
852  std::fstream is;
853  is.open(infile, std::fstream::in);
854  VecPt3d pts;
855  while (is.good())
856  {
857  Pt3d p;
858  is >> p.x >> p.y >> p.z;
859  if (is.good())
860  pts.push_back(p);
861  }
862 
864  double featureSize(200.0), meanSpacing(50.0), minimumCurvature(.001);
865  bool smoothCurvature(true);
866  pts = r.Redistribute(pts, featureSize, meanSpacing, minimumCurvature, smoothCurvature);
867  {
868  int flag = STR_FULLWIDTH;
869  int width = 9;
870  std::fstream os(outFile, std::fstream::out);
871  for (auto& p : pts)
872  {
873  os << STRstd(p.x, -1, width, flag) << " " << STRstd(p.y, -1, width, flag) << "\n";
874  }
875  }
876  TS_ASSERT_TXT_FILES_EQUAL(baseFile, outFile);
877 } // MePolyRedistributePtsCurvatureIntermediateTests::testCoastline
878 //------------------------------------------------------------------------------
880 //------------------------------------------------------------------------------
882 {
883  std::string path(std::string(XMS_TEST_PATH) + "redistribution/");
884  std::string infile(path + "Island.txt"), outFile(path + "Island_out.txt"),
885  baseFile(path + "Island_base.txt");
886  std::fstream is;
887  is.open(infile, std::fstream::in);
888  VecPt3d pts;
889  while (is.good())
890  {
891  Pt3d p;
892  is >> p.x >> p.y >> p.z;
893  if (is.good())
894  pts.push_back(p);
895  }
896 
898  double featureSize(200.0), meanSpacing(50.0), minimumCurvature(.001);
899  bool smoothCurvature(true);
900  pts = r.Redistribute(pts, featureSize, meanSpacing, minimumCurvature, smoothCurvature);
901  {
902  int flag = STR_FULLWIDTH;
903  int width = 9;
904  std::fstream os(outFile, std::fstream::out);
905  for (auto& p : pts)
906  {
907  os << STRstd(p.x, -1, width, flag) << " " << STRstd(p.y, -1, width, flag) << "\n";
908  }
909  }
910  TS_ASSERT_TXT_FILES_EQUAL(baseFile, outFile);
911 } // MePolyRedistributePtsCurvatureIntermediateTests::testCoastline
912 #endif
Redistributes the point locations on a polyline or polygon based on curvature.
VecPt3d NewPointsFromParamCurvs(int a_numPoints)
Shifts half an interval and aggregates the curvature data in closed polygon.
void ShiftAndAggregateClosed()
Shifts half an interval and aggregates the curvature data in closed polygon.
VecPt3d PlacePoints(double a_featureSize, int a_numPoints, double a_minimumCurvature, bool a_smooth)
Redistribute points according to curvature.
bool gmCircumcircleWithTol(const Pt3d *pt1, const Pt3d *pt2, const Pt3d *pt3, double *xc, double *yc, double *r2, double tol)
void testSetup()
tests a simple case shown below
std::string STRstd(double a_value, int a_n, int width, int flags)
STR_FULLWIDTH
std::vector< double > VecDbl
void testNewPointsFromParamCurvs()
tests a simple case shown below
#define TS_ASSERT_DELTA_VECPT3D(a, b, delta)
virtual VecPt3d Redistribute(const VecPt3d &a_points, double a_featureSize, double a_mean_spacing, double a_minimumCurvature=0.001, bool a_smooth=false)
Redistribute points according to curvature.
void testShiftAndAggregateClosed()
tests a simple case shown below
void CalculateCurvature(double a_featureSize, double a_minimumCurvature)
Calculates the curvature at the points where it has not yet been calculated.
std::map< double, int > m_distMap
map of distances and indices
VecPt3d m_points
locations defining polyline/polygon
Pt3d GetPointFromParameter(double a_param)
Get location based on parametric value a_param.
Redistributes the point locations on a polyline or polygon based on curvature.
void testIsland()
Tests redistribution along an island.
void ShiftAndAggregateOpen()
Shifts half an interval and aggregates the curvature data in an open polyline.
void DoSmoothing(bool a_smooth)
Shifts half an interval and aggregates the curvature data in closed polygon.
double m_tol
tolerance used for geometric calculations
void testDoSmoothing()
tests a simple case shown below
void testCalculateCurvature()
tests a simple case shown below
#define TS_ASSERT_TXT_FILES_EQUAL(a, b)
#define XM_ENSURE_TRUE(...)
void testShiftAndAggregateOpen()
tests a simple case shown below
void GetSignificantPoints(double a_featureSize)
Redistribute points according to curvature.
bool m_open
false means polygon, true mean polyline
void GetParameterIFM(double a_param, double a_interval, double &a_ti, double &a_tm, double &a_tf)
Calculates the parameterized station values of the two point at each side of tc.
void testCoastline()
Tests redistribution along a coastline.
#define XM_ASSERT(x)
bool LT_TOL(_T A, _U B, _V tolerance)
static BSHP< MePolyRedistributePtsCurvature > New()
Creates an instance of this class.
void testGetSignificantPoints()
tests a simple case shown below
double Mdist(_T x1, _U y1, _V x2, _W y2)
void Setup(const VecPt3d &)
sets up the class to do a Redistribute operation
VecDbl m_accumulatedSegmentLengths
accumulated length of polyline
void gmExtents2D(const VecPt3d &a_points, Pt2d &a_min, Pt2d &a_max)
VecDbl m_parametricDistance
distance (0-1) from starting to ending point
double GetCurvatureFromParameter(double a_param, double a_interval)
Redistribute points according to curvature.
#define TS_ASSERT_DELTA_VEC(a, b, delta)
std::vector< Pt3d > VecPt3d