mergePoints.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration |
5  \\ / A nd | www.openfoam.com
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8  Copyright (C) 2011-2016 OpenFOAM Foundation
9  Copyright (C) 2017-2022 OpenCFD Ltd.
10 -------------------------------------------------------------------------------
11 License
12  This file is part of OpenFOAM.
13 
14  OpenFOAM is free software: you can redistribute it and/or modify it
15  under the terms of the GNU General Public License as published by
16  the Free Software Foundation, either version 3 of the License, or
17  (at your option) any later version.
18 
19  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22  for more details.
23 
24  You should have received a copy of the GNU General Public License
25  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26 
27 \*---------------------------------------------------------------------------*/
28 
29 #include "ListOps.H" // sortedOrder, ListOps::identity
30 
31 // * * * * * * * * * * * * * * * Implementation * * * * * * * * * * * * * * //
32 
33 template<class PointList, class IndexerOp>
34 Foam::label Foam::Detail::mergePoints
35 (
36  const PointList& points,
37  const IndexerOp& indexer,
38  const label nSubPoints,
39  labelList& pointToUnique,
40  labelList& uniquePoints,
41  const scalar mergeTol,
42  const bool verbose
43 )
44 {
45  const label nTotPoints = points.size();
46 
47  if (!nTotPoints || !nSubPoints)
48  {
49  // Nothing to do
50  pointToUnique = identity(nTotPoints);
51  uniquePoints = pointToUnique;
52  return 0; // No points removed
53  }
54 
55  // Properly size for old to new mapping array
56  pointToUnique.resize_nocopy(nTotPoints);
57 
58 
59  // Use the boundBox minimum as the reference point. This
60  // stretches distances with fewer collisions than a mid-point
61  // reference would.
62 
63  auto comparePoint(points[indexer(0)]);
64  for (label pointi = 1; pointi < nSubPoints; ++pointi)
65  {
66  comparePoint = min(comparePoint, points[indexer(pointi)]);
67  }
68 
69  // We're comparing distance squared to reference point first.
70  // Say if starting from two close points:
71  // x, y, z
72  // x+mergeTol, y+mergeTol, z+mergeTol
73  // Then the magSqr of both will be
74  // x^2+y^2+z^2
75  // x^2+y^2+z^2 + 2*mergeTol*(x+z+y) + mergeTol^2*...
76  // so the difference will be 2*mergeTol*(x+y+z)
77 
78  const scalar mergeTolSqr(magSqr(mergeTol));
79 
80  // Use magSqr distance for the points sort order
81  List<scalar> sqrDist(nSubPoints);
82  for (label pointi = 0; pointi < nSubPoints; ++pointi)
83  {
84  const auto& p = points[indexer(pointi)];
85 
86  sqrDist[pointi] =
87  (
88  // Use scalar precision
89  magSqr(scalar(p.x() - comparePoint.x()))
90  + magSqr(scalar(p.y() - comparePoint.y()))
91  + magSqr(scalar(p.z() - comparePoint.z()))
92  );
93  }
94  labelList order(Foam::sortedOrder(sqrDist));
95 
96  List<scalar> sortedTol(nSubPoints);
97  forAll(order, sorti)
98  {
99  const auto& p = points[indexer(order[sorti])];
100 
101  sortedTol[sorti] =
102  (
103  2*mergeTol*
104  (
105  // Use scalar precision
106  mag(scalar(p.x() - comparePoint.x()))
107  + mag(scalar(p.y() - comparePoint.y()))
108  + mag(scalar(p.z() - comparePoint.z()))
109  )
110  );
111  }
112 
113 
114  // Bookkeeping parameters
115  // ~~~~~~~~~~~~~~~~~~~~~~
116 
117  // Will only be working on a subset of the points
118  // Can use a slice of pointToUnique (full length not needed until later).
119  SubList<label> subPointMap(pointToUnique, nSubPoints);
120 
121  // Track number of unique points - this will form an offsets table
122  labelList newPointCounts(nSubPoints, Zero);
123 
124  label nNewPoints = 0;
125  for (label sorti = 0; sorti < order.size(); ++sorti)
126  {
127  // The (sub)point index
128  const label pointi = order[sorti];
129  const scalar currDist = sqrDist[order[sorti]];
130  const auto& currPoint = points[indexer(pointi)];
131 
132  // Compare to previous points to find equal one
133  // - automatically a no-op for sorti == 0 (the first point)
134 
135  bool matched = false;
136 
137  for
138  (
139  label prevSorti = sorti - 1;
140  (
141  prevSorti >= 0
142  && (mag(sqrDist[order[prevSorti]] - currDist) <= sortedTol[sorti])
143  );
144  --prevSorti
145  )
146  {
147  const label prevPointi = order[prevSorti];
148  const auto& prevPoint = points[indexer(prevPointi)];
149 
150  // Matched within tolerance?
151  matched =
152  (
153  (
154  // Use scalar precision
155  magSqr(scalar(currPoint.x() - prevPoint.x()))
156  + magSqr(scalar(currPoint.y() - prevPoint.y()))
157  + magSqr(scalar(currPoint.z() - prevPoint.z()))
158  ) <= mergeTolSqr
159  );
160 
161  if (matched)
162  {
163  // Both pointi and prevPointi have similar coordinates.
164  // Map to the same new point.
165  subPointMap[pointi] = subPointMap[prevPointi];
166 
167  if (verbose)
168  {
169  Pout<< "Foam::mergePoints : [" << subPointMap[pointi]
170  << "] Point " << pointi << " duplicate of "
171  << prevPointi << " : coordinates:" << currPoint
172  << " and " << prevPoint << endl;
173  }
174  break;
175  }
176  }
177 
178  if (!matched)
179  {
180  // Differs. Store new point.
181  subPointMap[pointi] = nNewPoints++;
182 
189  }
190  ++newPointCounts[subPointMap[pointi]];
191  }
192 
193  const label nDupPoints(nSubPoints - nNewPoints);
194  const label nUniqPoints(nTotPoints - nDupPoints);
195 
196  if (verbose)
197  {
198  Pout<< "Foam::mergePoints : "
199  << "Merging removed " << nDupPoints << '/'
200  << nTotPoints << " points" << endl;
201  }
202 
203  if (!nDupPoints)
204  {
205  // Nothing to do
206  pointToUnique = identity(nTotPoints);
207  uniquePoints = pointToUnique;
208  return 0; // No points removed
209  }
210 
211 
212  // The subPointMap now contains a mapping of the sub-selection
213  // to the list of (sorted) merged points.
214  // Get its sort order to bundle according to the merged point target.
215  // This is in effect an adjacent list of graph edges to mapping back
216  // to the merged points, but in compact form.
217  // Use the previously obtained newPointCounts for the offsets list.
218 
219  labelList lookupMerged(std::move(order));
220  Foam::sortedOrder(subPointMap, lookupMerged);
221 
222  // Remap inplace to the original points ids
223  for (label& idx : lookupMerged)
224  {
225  idx = indexer(idx);
226  }
227  // The subPointMap slice is not needed beyond here
228 
229 
230  // Setup initial identity +1 mapping for pointToUnique
231  // The +1 allows negatives to mark duplicates
232 
233  ListOps::identity(pointToUnique, 1);
234 
235  // The newPointCounts is an offsets table that we use to walk
236  // across the adjacency list (lookupMerged), picking the original
237  // point with the lowest id as the one to retain (master).
238  {
239  label beg = 0;
240  for (const label len : newPointCounts)
241  {
242  if (!len) continue; // Can be empty
243 
244  const label end = (beg + len);
245 
246  // Pass 1:
247  // Find the 'master' (lowest point id)
248 
249  label masterPointi = lookupMerged[beg];
250 
251  for (label iter = beg + 1; iter < end; ++iter)
252  {
253  const label origPointi = lookupMerged[iter];
254 
255  if (masterPointi > origPointi)
256  {
257  masterPointi = origPointi;
258  }
259  }
260 
261  // Pass 2:
262  // Markup duplicate points, encoding information about master
263  for (label iter = beg; iter < end; ++iter)
264  {
265  const label origPointi = lookupMerged[iter];
266 
267  if (masterPointi != origPointi)
268  {
269  // Encode the originating 'master' point
270  pointToUnique[origPointi] = (-masterPointi-1);
271  }
272  }
273 
274  beg = end;
275  }
276  }
277 
278  // Now have all the information needed
279 
280  uniquePoints.resize_nocopy(nUniqPoints);
281  {
282  label uniquei = 0;
283 
284  forAll(pointToUnique, pointi)
285  {
286  const label origPointi = pointToUnique[pointi];
287 
288  if (origPointi > 0)
289  {
290  // Subtract one to align addressing
291  uniquePoints[uniquei] = (origPointi - 1);
292  pointToUnique[pointi] = uniquei;
293  ++uniquei;
294  }
295  else
296  {
297  // A duplicate point. Also guaranteed that the 'master' point
298  // has a lower index and thus already been seen.
299  const label masterPointi = mag(origPointi) - 1;
300  pointToUnique[pointi] = pointToUnique[masterPointi];
301  }
302  }
303  }
304 
305  return nDupPoints;
306 }
307 
308 
309 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
310 
311 template<class PointList>
312 Foam::label Foam::mergePoints
313 (
314  const PointList& points,
315  labelList& pointToUnique,
316  labelList& uniquePoints,
317  const scalar mergeTol,
318  const bool verbose
319 )
320 {
321  const label nTotPoints = points.size();
322 
323  if (!nTotPoints)
324  {
325  // Nothing to do
326  pointToUnique.clear();
327  uniquePoints.clear();
328  return 0; // No points removed
329  }
330 
332  (
333  points,
334  identityOp(), // identity indexer
335  nTotPoints, // == nSubPoints
336  pointToUnique,
337  uniquePoints,
338  mergeTol,
339  verbose
340  );
341 }
342 
343 
344 template<class PointList>
345 Foam::label Foam::mergePoints
346 (
347  const PointList& points,
348  const labelUList& selection,
349  labelList& pointToUnique,
350  labelList& uniquePoints,
351  const scalar mergeTol,
352  const bool verbose
353 )
354 {
355  const label nTotPoints = points.size();
356  const label nSubPoints = selection.size();
357 
358  if (!nTotPoints || !nSubPoints)
359  {
360  // Nothing to do
361  pointToUnique.clear();
362  uniquePoints.clear();
363  return 0; // No points removed
364  }
365 
366  const auto indexer = [&](const label i) -> label { return selection[i]; };
367 
369  (
370  points,
371  indexer,
372  nSubPoints,
373  pointToUnique,
374  uniquePoints,
375  mergeTol,
376  verbose
377  );
378 }
379 
380 
381 template<class PointList>
382 Foam::label Foam::mergePoints
383 (
384  const PointList& points,
385  const scalar mergeTol,
386  const bool verbose,
387  labelList& pointToUnique
388 )
389 {
390  labelList uniquePoints;
391  const label nChanged = Foam::mergePoints
392  (
393  points,
394  pointToUnique,
395  uniquePoints,
396  mergeTol,
397  verbose
398  );
399 
400  // Number of unique points
401  return (points.size() - nChanged);
402 }
403 
404 
405 template<class PointList>
406 Foam::label Foam::inplaceMergePoints
407 (
408  PointList& points,
409  const scalar mergeTol,
410  const bool verbose,
411  labelList& pointToUnique
412 )
413 {
414  labelList uniquePoints;
415  const label nChanged = Foam::mergePoints
416  (
417  points,
418  pointToUnique,
419  uniquePoints,
420  mergeTol,
421  verbose
422  );
423 
424  if (nChanged)
425  {
426  // Overwrite
427  points = List<typename PointList::value_type>(points, uniquePoints);
428  }
429  else
430  {
431  // TDB:
432  // pointToUnique.clear();
433  }
434 
435  return nChanged;
436 }
437 
438 
439 template<class PointList>
440 Foam::label Foam::inplaceMergePoints
441 (
442  PointList& points,
443  const labelUList& selection,
444  const scalar mergeTol,
445  const bool verbose,
446  labelList& pointToUnique
447 )
448 {
449  labelList uniquePoints;
450  const label nChanged = Foam::mergePoints
451  (
452  points,
453  selection,
454  pointToUnique,
455  uniquePoints,
456  mergeTol,
457  verbose
458  );
459 
460  if (nChanged)
461  {
462  // Overwrite
463  points = List<typename PointList::value_type>(points, uniquePoints);
464  }
465  else
466  {
467  // TDB:
468  // pointToUnique.clear();
469  }
470 
471  return nChanged;
472 }
473 
474 
475 template<class PointList>
476 Foam::label Foam::mergePoints
477 (
478  const PointList& points,
479  const scalar mergeTol,
480  const bool verbose,
481  labelList& pointToUnique,
482  List<typename PointList::value_type>& newPoints
483 )
484 {
485  const label nTotPoints = points.size();
486 
487  if (!nTotPoints)
488  {
489  // Nothing to do
490  pointToUnique.clear();
491  newPoints.clear();
492  return 0; // No points removed
493  }
494 
495  labelList uniquePoints;
496  const label nChanged = Foam::mergePoints
497  (
498  points,
499  pointToUnique,
500  uniquePoints,
501  mergeTol,
502  verbose
503  );
504 
505  if (nChanged)
506  {
507  newPoints = List<typename PointList::value_type>(points, uniquePoints);
508  }
509  else
510  {
511  // TDB:
512  // pointToUnique.clear();
513  newPoints = points;
514  }
515 
516  return nChanged;
517 }
518 
519 
520 // ************************************************************************* //
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
labelList sortedOrder(const UList< T > &input)
Return the (stable) sort order for the list.
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:487
UList< label > labelUList
A UList of labels.
Definition: UList.H:78
Various functions to operate on Lists.
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:414
label mergePoints(const PointList &points, labelList &pointToUnique, labelList &uniquePoints, const scalar mergeTol=SMALL, const bool verbose=false)
Calculate merge mapping, preserving the original point order. All points closer/equal mergeTol are to...
const pointField & points
labelList identity(const label len, label start=0)
Return an identity map of the given length with (map[i] == i)
Definition: labelList.C:31
label inplaceMergePoints(PointList &points, const scalar mergeTol, const bool verbose, labelList &pointToUnique)
Inplace merge points, preserving the original point order. All points closer/equal mergeTol are to be...
label min(const labelHashSet &set, label minValue=labelMax)
Find the min value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:26
constexpr auto end(C &c) -> decltype(c.end())
Return iterator to the end of the container c.
Definition: stdFoam.H:194
label mergePoints(const PointList &points, const IndexerOp &indexer, const label nSubPoints, labelList &pointToUnique, labelList &uniquePoints, const scalar mergeTol, const bool verbose)
Implementation detail for Foam::mergePoints.
List< label > labelList
A List of labels.
Definition: List.H:62
volScalarField & p
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
dimensioned< typename typeOfMag< Type >::type > magSqr(const dimensioned< Type > &dt)
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:133