1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkMergeCells.cxx
5 
6   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7   All rights reserved.
8   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9 
10      This software is distributed WITHOUT ANY WARRANTY; without even
11      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12      PURPOSE.  See the above copyright notice for more information.
13 
14 =========================================================================*/
15 /*----------------------------------------------------------------------------
16  Copyright (c) Sandia Corporation
17  See Copyright.txt or http://www.paraview.org/HTML/Copyright.html for details.
18 ----------------------------------------------------------------------------*/
19 
20 #include "vtkMergeCells.h"
21 
22 #include "vtkArrayDispatch.h"
23 #include "vtkCell.h"
24 #include "vtkCellArray.h"
25 #include "vtkCellData.h"
26 #include "vtkDataArray.h"
27 #include "vtkDataArrayRange.h"
28 #include "vtkIdTypeArray.h"
29 #include "vtkKdTree.h"
30 #include "vtkMergePoints.h"
31 #include "vtkNew.h"
32 #include "vtkObjectFactory.h"
33 #include "vtkPointData.h"
34 #include "vtkPoints.h"
35 #include "vtkSmartPointer.h"
36 #include "vtkUnsignedCharArray.h"
37 #include "vtkUnstructuredGrid.h"
38 
39 #include <algorithm>
40 #include <cstdlib>
41 #include <map>
42 
43 namespace
44 {
45 
46 // use a fast path for 32/64 bit signed/unsigned ints as global ids:
47 using GIDFastTypes = vtkTypeList::Create<vtkTypeInt64, vtkTypeInt32, vtkTypeUInt64, vtkTypeUInt32>;
48 using IdDispatcher = vtkArrayDispatch::DispatchByValueType<GIDFastTypes>;
49 
50 } // end anon namespace
51 
52 vtkStandardNewMacro(vtkMergeCells);
53 
54 vtkCxxSetObjectMacro(vtkMergeCells, UnstructuredGrid, vtkUnstructuredGrid);
55 
56 class vtkMergeCellsSTLCloak
57 {
58 public:
59   std::map<vtkIdType, vtkIdType> IdTypeMap;
60 };
61 
62 //------------------------------------------------------------------------------
vtkMergeCells()63 vtkMergeCells::vtkMergeCells()
64 {
65   this->TotalNumberOfDataSets = 0;
66   this->TotalNumberOfCells = 0;
67   this->TotalNumberOfPoints = 0;
68 
69   this->NumberOfCells = 0;
70   this->NumberOfPoints = 0;
71 
72   this->PointMergeTolerance = 10e-4;
73   this->MergeDuplicatePoints = true;
74 
75   this->InputIsUGrid = false;
76   this->InputIsPointSet = false;
77 
78   this->PointList = nullptr;
79   this->CellList = nullptr;
80 
81   this->UnstructuredGrid = nullptr;
82 
83   this->GlobalIdMap = new vtkMergeCellsSTLCloak;
84   this->GlobalCellIdMap = new vtkMergeCellsSTLCloak;
85 
86   this->UseGlobalIds = 0;
87   this->UseGlobalCellIds = 0;
88 
89   this->NextGrid = 0;
90 }
91 
92 //------------------------------------------------------------------------------
~vtkMergeCells()93 vtkMergeCells::~vtkMergeCells()
94 {
95   this->FreeLists();
96 
97   delete this->GlobalIdMap;
98   delete this->GlobalCellIdMap;
99 
100   this->SetUnstructuredGrid(nullptr);
101 }
102 
103 //------------------------------------------------------------------------------
FreeLists()104 void vtkMergeCells::FreeLists()
105 {
106   delete this->PointList;
107   this->PointList = nullptr;
108 
109   delete this->CellList;
110   this->CellList = nullptr;
111 }
112 
113 //------------------------------------------------------------------------------
MergeDataSet(vtkDataSet * set)114 int vtkMergeCells::MergeDataSet(vtkDataSet* set)
115 {
116   vtkUnstructuredGrid* grid = this->UnstructuredGrid;
117 
118   if (!grid)
119   {
120     vtkErrorMacro(<< "SetUnstructuredGrid first");
121     return -1;
122   }
123 
124   if (this->TotalNumberOfDataSets <= 0)
125   {
126     // TotalNumberOfCells and TotalNumberOfPoints may both be zero
127     // if all data sets to be merged are empty
128 
129     vtkErrorMacro(<< "Must SetTotalNumberOfCells, SetTotalNumberOfPoints and "
130                      "SetTotalNumberOfDataSets (upper bounds at least)"
131                      " before starting to MergeDataSets");
132 
133     return -1;
134   }
135 
136   vtkPointData* pointArrays = set->GetPointData();
137   vtkCellData* cellArrays = set->GetCellData();
138 
139   // Since vtkMergeCells is to be used only on distributed vtkDataSets,
140   // each DataSet should have the same field arrays.  However the field arrays
141   // may get rearranged in the process of Marshalling/UnMarshalling.
142   // So we use a vtkDataSetAttributes::FieldList to ensure the field arrays are
143   // merged in the right order.
144   if (grid->GetNumberOfCells() == 0)
145   {
146     this->InputIsPointSet = vtkPointSet::SafeDownCast(set) ? 1 : 0;
147     this->InputIsUGrid = vtkUnstructuredGrid::SafeDownCast(set) ? 1 : 0;
148     this->StartUGrid(set);
149   }
150   else
151   {
152     this->PointList->IntersectFieldList(pointArrays);
153     this->CellList->IntersectFieldList(cellArrays);
154   }
155 
156   vtkIdType numPoints = set->GetNumberOfPoints();
157   vtkIdType numCells = set->GetNumberOfCells();
158 
159   if (numCells == 0)
160   {
161     return 0;
162   }
163 
164   vtkIdType* idMap = nullptr;
165   if (this->MergeDuplicatePoints)
166   {
167     if (this->UseGlobalIds) // faster by far
168     {
169       // Note:  It has been observed that an input dataset may
170       // have an invalid global ID array.  Using the array to
171       // merge points results in bad geometry.  It may be
172       // worthwhile to do a quick sanity check when merging
173       // points.  Downside is that will slow down this filter.
174 
175       idMap = this->MapPointsToIdsUsingGlobalIds(set);
176     }
177     else
178     {
179       idMap = this->MapPointsToIdsUsingLocator(set);
180     }
181   }
182 
183   vtkIdType nextPt = this->NumberOfPoints;
184   vtkPoints* pts = grid->GetPoints();
185 
186   for (vtkIdType oldPtId = 0; oldPtId < numPoints; oldPtId++)
187   {
188     vtkIdType newPtId = idMap ? idMap[oldPtId] : nextPt;
189 
190     if (newPtId == nextPt)
191     {
192       pts->SetPoint(nextPt, set->GetPoint(oldPtId));
193       grid->GetPointData()->CopyData(
194         *this->PointList, pointArrays, this->NextGrid, oldPtId, nextPt);
195       nextPt++;
196     }
197   }
198 
199   pts->Modified(); // so that subsequent GetBounds will be correct
200 
201   vtkIdType newCellId = this->InputIsUGrid ? this->AddNewCellsUnstructuredGrid(set, idMap)
202                                            : this->AddNewCellsDataSet(set, idMap);
203 
204   delete[] idMap;
205 
206   this->NumberOfPoints = nextPt;
207   this->NumberOfCells = newCellId;
208 
209   this->NextGrid++;
210 
211   return 0;
212 }
213 
214 namespace
215 {
216 
217 struct ProcessCellGIDsDataSet
218 {
219   // Pass in the gids to do duplicate checking, otherwise use other overload:
220   template <typename GIDArrayT>
operator ()__anonff1779950211::ProcessCellGIDsDataSet221   void operator()(GIDArrayT* gidArray, std::map<vtkIdType, vtkIdType>& gidMap)
222   {
223     vtkIdType nextCellId = static_cast<vtkIdType>(gidMap.size());
224 
225     const auto gids = vtk::DataArrayValueRange<1>(gidArray);
226     for (vtkIdType oldCellId = 0; oldCellId < gids.size(); oldCellId++)
227     {
228       vtkIdType globalId = static_cast<vtkIdType>(gids[oldCellId]);
229 
230       auto inserted = gidMap.insert(std::make_pair(globalId, nextCellId));
231 
232       if (inserted.second)
233       {
234         nextCellId++;
235       }
236     }
237   }
238 };
239 
240 } // end anon namespace
241 
242 //------------------------------------------------------------------------------
AddNewCellsDataSet(vtkDataSet * set,vtkIdType * idMap)243 vtkIdType vtkMergeCells::AddNewCellsDataSet(vtkDataSet* set, vtkIdType* idMap)
244 {
245   vtkUnstructuredGrid* grid = this->UnstructuredGrid;
246   const vtkIdType numCells = set->GetNumberOfCells();
247 
248   vtkDataArray* gidArray = this->UseGlobalIds ? set->GetCellData()->GetGlobalIds() : nullptr;
249 
250   if (gidArray)
251   { // Use duplicate matching:
252     ProcessCellGIDsDataSet worker;
253     if (!IdDispatcher::Execute(gidArray, worker, this->GlobalCellIdMap->IdTypeMap))
254     { // fallback for weird types:
255       worker(gidArray, this->GlobalCellIdMap->IdTypeMap);
256     }
257   }
258 
259   vtkCellData* gridCD = grid->GetCellData();
260   vtkCellData* setCD = set->GetCellData();
261 
262   vtkNew<vtkIdList> cellPoints;
263   cellPoints->Allocate(VTK_CELL_SIZE);
264 
265   for (vtkIdType oldCellId = 0; oldCellId < numCells; oldCellId++)
266   {
267     set->GetCellPoints(oldCellId, cellPoints);
268     for (vtkIdType pid = 0; pid < cellPoints->GetNumberOfIds(); ++pid)
269     {
270       const vtkIdType oldPtId = cellPoints->GetId(pid);
271       const vtkIdType newPtId = idMap ? idMap[oldPtId] : this->NumberOfPoints + oldPtId;
272       cellPoints->SetId(pid, newPtId);
273     }
274 
275     const vtkIdType newCellId = grid->InsertNextCell(set->GetCellType(oldCellId), cellPoints);
276 
277     gridCD->CopyData(*this->CellList, setCD, this->NextGrid, oldCellId, newCellId);
278   }
279 
280   return grid->GetNumberOfCells() - 1;
281 }
282 
283 namespace
284 {
285 
286 struct ProcessCellGIDsUG
287 {
288   template <typename GIDArrayT>
operator ()__anonff1779950311::ProcessCellGIDsUG289   void operator()(GIDArrayT* gidArray, vtkCellArray* newCells, vtkIdList*& duplicateCellIds,
290     vtkIdType& numDuplicateCells, vtkIdType& numDuplicateConnections,
291     std::map<vtkIdType, vtkIdType>& gidMap)
292   {
293     const auto gids = vtk::DataArrayValueRange<1>(gidArray);
294 
295     vtkIdType nextLocalId = static_cast<vtkIdType>(gidMap.size());
296 
297     duplicateCellIds = vtkIdList::New();
298 
299     for (vtkIdType cid = 0; cid < gids.size(); cid++)
300     {
301       vtkIdType globalId = static_cast<vtkIdType>(gids[cid]);
302 
303       auto inserted = gidMap.insert(std::make_pair(globalId, nextLocalId));
304       if (inserted.second)
305       {
306         nextLocalId++;
307       }
308       else
309       {
310         duplicateCellIds->InsertNextId(cid);
311         numDuplicateCells++;
312         numDuplicateConnections += newCells->GetCellSize(cid);
313       }
314     }
315 
316     if (numDuplicateCells == 0)
317     {
318       duplicateCellIds->Delete();
319       duplicateCellIds = nullptr;
320     }
321   }
322 };
323 
324 } // end anon namespace
325 
326 //------------------------------------------------------------------------------
AddNewCellsUnstructuredGrid(vtkDataSet * set,vtkIdType * idMap)327 vtkIdType vtkMergeCells::AddNewCellsUnstructuredGrid(vtkDataSet* set, vtkIdType* idMap)
328 {
329   bool firstSet = (this->NextGrid == 0);
330 
331   vtkUnstructuredGrid* newGrid = vtkUnstructuredGrid::SafeDownCast(set);
332   vtkUnstructuredGrid* grid = this->UnstructuredGrid;
333 
334   // Connectivity information for the new data set
335   vtkCellArray* newCells = newGrid->GetCells();
336   vtkIdType newNumCells = newCells->GetNumberOfCells();
337   vtkIdType newNumConnections = newCells->GetNumberOfConnectivityIds();
338 
339   // If we are checking for duplicate cells, create a list now of
340   // any cells in the new data set that we already have.
341   vtkIdList* duplicateCellIds = nullptr;
342   vtkIdType numDuplicateCells = 0;
343   vtkIdType numDuplicateConnections = 0;
344 
345   if (this->UseGlobalCellIds)
346   {
347     vtkDataArray* gidArray = set->GetCellData()->GetGlobalIds();
348     if (gidArray)
349     {
350       ProcessCellGIDsUG worker;
351       if (!IdDispatcher::Execute(gidArray, worker, newCells, duplicateCellIds, numDuplicateCells,
352             numDuplicateConnections, this->GlobalCellIdMap->IdTypeMap))
353       { // fallback for weird types:
354         worker(gidArray, newCells, duplicateCellIds, numDuplicateCells, numDuplicateConnections,
355           this->GlobalCellIdMap->IdTypeMap);
356       }
357     }
358   }
359 
360   // Connectivity for the merged grid so far
361 
362   vtkCellArray* cellArray = nullptr;
363   vtkIdType* flocs = nullptr;
364   vtkIdType* faces = nullptr;
365   unsigned char* types = nullptr;
366 
367   vtkIdType numCells = 0;
368   vtkIdType numConnections = 0;
369   vtkIdType numFacesConnections = 0;
370 
371   if (!firstSet)
372   {
373     cellArray = grid->GetCells();
374     types = grid->GetCellTypesArray()->GetPointer(0);
375     flocs = grid->GetFaceLocations() ? grid->GetFaceLocations()->GetPointer(0) : nullptr;
376     faces = grid->GetFaces() ? grid->GetFaces()->GetPointer(0) : nullptr;
377 
378     numCells = cellArray->GetNumberOfCells();
379     numConnections = cellArray->GetNumberOfConnectivityIds();
380     numFacesConnections = faces ? grid->GetFaces()->GetNumberOfValues() : 0;
381   }
382 
383   // New output grid: merging of existing and incoming grids
384 
385   // CELL ARRAY
386   vtkIdType totalNumCells = numCells + newNumCells - numDuplicateCells;
387   vtkIdType totalNumConnections = numConnections + newNumConnections - numDuplicateConnections;
388 
389   vtkNew<vtkCellArray> finalCellArray;
390   finalCellArray->AllocateExact(totalNumCells, totalNumConnections);
391 
392   if (!firstSet && cellArray)
393   {
394     finalCellArray->Append(cellArray, 0);
395   }
396 
397   // TYPE ARRAY
398   vtkNew<vtkUnsignedCharArray> typeArray;
399   typeArray->SetNumberOfValues(totalNumCells);
400   if (!firstSet && types)
401   {
402     unsigned char* cptr = typeArray->GetPointer(0);
403     memcpy(cptr, types, numCells * sizeof(unsigned char));
404   }
405 
406   // FACES LOCATION ARRAY
407   vtkNew<vtkIdTypeArray> facesLocationArray;
408   facesLocationArray->SetNumberOfValues(totalNumCells);
409   if (!firstSet && flocs)
410   {
411     vtkIdType* fiptr = facesLocationArray->GetPointer(0); // new output dataset
412     memcpy(fiptr, flocs, numCells * sizeof(vtkIdType));   // existing set
413   }
414   else if (!firstSet)
415   {
416     facesLocationArray->FillComponent(0, -1);
417   }
418 
419   bool havePolyhedron = false;
420 
421   // FACES ARRAY
422   vtkNew<vtkIdTypeArray> facesArray;
423   facesArray->SetNumberOfValues(numFacesConnections);
424   if (!firstSet && faces)
425   {
426     havePolyhedron = true;
427     vtkIdType* faptr = facesArray->GetPointer(0);                  // new output dataset
428     memcpy(faptr, faces, numFacesConnections * sizeof(vtkIdType)); // existing set
429   }
430 
431   // set up new cell data
432 
433   vtkIdType finalCellId = numCells;
434   vtkCellData* cellArrays = set->GetCellData();
435 
436   vtkIdType oldPtId, finalPtId, nextDuplicateCellId = 0;
437 
438   for (vtkIdType oldCellId = 0; oldCellId < newNumCells; oldCellId++)
439   {
440     if (duplicateCellIds && nextDuplicateCellId < duplicateCellIds->GetNumberOfIds())
441     {
442       vtkIdType skipId = duplicateCellIds->GetId(nextDuplicateCellId);
443       if (skipId == oldCellId)
444       {
445         nextDuplicateCellId++;
446         continue;
447       }
448     }
449 
450     vtkIdType npts;
451     const vtkIdType* pts;
452     newGrid->GetCellPoints(oldCellId, npts, pts);
453 
454     finalCellArray->InsertNextCell(static_cast<int>(npts));
455     unsigned char cellType = newGrid->GetCellType(oldCellId);
456     typeArray->SetValue(finalCellId, cellType);
457 
458     for (vtkIdType i = 0; i < npts; i++)
459     {
460       oldPtId = pts[i];
461       finalPtId = idMap ? idMap[oldPtId] : this->NumberOfPoints + oldPtId;
462       finalCellArray->InsertCellPoint(finalPtId);
463     }
464 
465     if (cellType == VTK_POLYHEDRON)
466     {
467       havePolyhedron = true;
468       vtkIdType nfaces;
469       const vtkIdType* ptIds;
470       newGrid->GetFaceStream(oldCellId, nfaces, ptIds);
471 
472       facesLocationArray->SetValue(finalCellId, facesArray->GetNumberOfValues());
473       facesArray->InsertNextValue(nfaces);
474 
475       for (vtkIdType i = 0; i < nfaces; i++)
476       {
477         vtkIdType nfpts = *ptIds++;
478         facesArray->InsertNextValue(nfpts);
479         for (vtkIdType j = 0; j < nfpts; j++)
480         {
481           oldPtId = *ptIds++;
482           finalPtId = idMap ? idMap[oldPtId] : this->NumberOfPoints + oldPtId;
483           facesArray->InsertNextValue(finalPtId);
484         }
485       }
486     }
487     else
488     {
489       facesLocationArray->SetValue(finalCellId, -1);
490     }
491 
492     grid->GetCellData()->CopyData(
493       *(this->CellList), cellArrays, this->NextGrid, oldCellId, finalCellId);
494 
495     finalCellId++;
496   }
497 
498   if (havePolyhedron)
499   {
500     grid->SetCells(typeArray, finalCellArray, facesLocationArray, facesArray);
501   }
502   else
503   {
504     grid->SetCells(typeArray, finalCellArray, nullptr, nullptr);
505   }
506 
507   if (duplicateCellIds)
508   {
509     duplicateCellIds->Delete();
510   }
511 
512   return finalCellId;
513 }
514 
515 //------------------------------------------------------------------------------
StartUGrid(vtkDataSet * set)516 void vtkMergeCells::StartUGrid(vtkDataSet* set)
517 {
518   vtkUnstructuredGrid* grid = this->UnstructuredGrid;
519 
520   if (!this->InputIsUGrid)
521   {
522     grid->Allocate(this->TotalNumberOfCells);
523   }
524 
525   vtkNew<vtkPoints> pts;
526   // If the input has a vtkPoints object, we'll make the merged output
527   // grid have a vtkPoints object of the same data type.  Otherwise,
528   // the merged output grid will have the default of points of type float.
529   if (this->InputIsPointSet)
530   {
531     vtkPointSet* ps = vtkPointSet::SafeDownCast(set);
532     pts->SetDataType(ps->GetPoints()->GetDataType());
533   }
534   pts->SetNumberOfPoints(this->TotalNumberOfPoints); // allocate for upper bound
535   grid->SetPoints(pts);
536 
537   // Order of field arrays may get changed when data sets are
538   // marshalled/sent/unmarshalled.  So we need to re-index the
539   // field arrays before copying them using a FieldList
540   this->PointList = new vtkDataSetAttributes::FieldList(this->TotalNumberOfDataSets);
541   this->CellList = new vtkDataSetAttributes::FieldList(this->TotalNumberOfDataSets);
542 
543   this->PointList->InitializeFieldList(set->GetPointData());
544   this->CellList->InitializeFieldList(set->GetCellData());
545 
546   if (this->UseGlobalIds)
547   {
548     grid->GetPointData()->CopyGlobalIdsOn();
549   }
550   grid->GetPointData()->CopyAllocate(*this->PointList, this->TotalNumberOfPoints);
551 
552   if (this->UseGlobalCellIds)
553   {
554     grid->GetCellData()->CopyGlobalIdsOn();
555   }
556   grid->GetCellData()->CopyAllocate(*this->CellList, this->TotalNumberOfCells);
557 }
558 
559 //------------------------------------------------------------------------------
Finish()560 void vtkMergeCells::Finish()
561 {
562   this->FreeLists();
563 
564   vtkUnstructuredGrid* grid = this->UnstructuredGrid;
565 
566   if (this->NumberOfPoints < this->TotalNumberOfPoints)
567   {
568     // if we don't do this, grid->GetNumberOfPoints() gives the wrong value
569     grid->GetPoints()->GetData()->Resize(this->NumberOfPoints);
570   }
571 
572   grid->Squeeze();
573 }
574 
575 namespace
576 {
577 
578 struct MapPointsUsingGIDsWorker
579 {
580   template <typename GIDArrayType>
operator ()__anonff1779950411::MapPointsUsingGIDsWorker581   void operator()(
582     GIDArrayType* gidArray, std::map<vtkIdType, vtkIdType>& globalIdMap, vtkIdType* idMap)
583   {
584     const auto gids = vtk::DataArrayValueRange<1>(gidArray);
585 
586     vtkIdType nextNewLocalId = static_cast<vtkIdType>(globalIdMap.size());
587     for (vtkIdType oldId = 0; oldId < gids.size(); ++oldId)
588     {
589       vtkIdType globalId = static_cast<vtkIdType>(gids[oldId]);
590 
591       auto inserted = globalIdMap.insert(std::make_pair(globalId, nextNewLocalId));
592 
593       if (inserted.second)
594       { // This is a new global node id
595         idMap[oldId] = nextNewLocalId;
596         nextNewLocalId++;
597       }
598       else
599       { // A repeat; it was not inserted
600         idMap[oldId] = inserted.first->second;
601       }
602     }
603   }
604 };
605 
606 } // end anon namespace
607 
608 //------------------------------------------------------------------------------
609 // Use an array of global node ids to map all points to
610 // their new ids in the merged grid.
MapPointsToIdsUsingGlobalIds(vtkDataSet * set)611 vtkIdType* vtkMergeCells::MapPointsToIdsUsingGlobalIds(vtkDataSet* set)
612 {
613   vtkDataArray* globalIdArray = set->GetPointData()->GetGlobalIds();
614   if (!this->UseGlobalIds || !globalIdArray)
615   {
616     vtkErrorMacro("global id array is not available");
617     return nullptr;
618   }
619 
620   vtkIdType npoints = set->GetNumberOfPoints();
621   vtkIdType* idMap = new vtkIdType[static_cast<std::size_t>(npoints)];
622   auto& gidMap = this->GlobalIdMap->IdTypeMap;
623 
624   MapPointsUsingGIDsWorker worker;
625 
626   if (!IdDispatcher::Execute(globalIdArray, worker, gidMap, idMap))
627   { // fallback to slow path for other value types:
628     worker(globalIdArray, gidMap, idMap);
629   }
630 
631   return idMap;
632 }
633 
634 //------------------------------------------------------------------------------
635 // Use a spatial locator to filter out duplicate points and map
636 // the new ids to their ids in the merged grid.
MapPointsToIdsUsingLocator(vtkDataSet * set)637 vtkIdType* vtkMergeCells::MapPointsToIdsUsingLocator(vtkDataSet* set)
638 {
639   vtkUnstructuredGrid* grid = this->UnstructuredGrid;
640   vtkPoints* points0 = grid->GetPoints();
641   vtkIdType npoints0 = this->NumberOfPoints;
642 
643   vtkPointSet* ps = vtkPointSet::SafeDownCast(set);
644   vtkIdType npoints1 = set->GetNumberOfPoints();
645   vtkSmartPointer<vtkPoints> points1;
646 
647   if (ps)
648   {
649     points1 = ps->GetPoints();
650   }
651   else
652   {
653     points1 = vtkSmartPointer<vtkPoints>::New();
654     points1->SetNumberOfPoints(npoints1);
655 
656     for (vtkIdType ptId = 0; ptId < npoints1; ptId++)
657     {
658       points1->SetPoint(ptId, set->GetPoint(ptId));
659     }
660   }
661 
662   vtkIdType* idMap = new vtkIdType[npoints1];
663 
664   double bounds[6];
665   set->GetBounds(bounds);
666   if (npoints0 > 0)
667   {
668     double tmpBounds[6];
669 
670     // Prior to MapPointsToIdsUsingLocator(), points0->SetNumberOfPoints()
671     // has been called to set the number of points to the upper bound on the
672     // points TO BE merged and now points0->GetNumberOfPoints() does not
673     // refer to the number of the points merged so far. Thus we need to
674     // temporarily set the number to the latter such that grid->GetBounds()
675     // is able to return the correct bounding information. This is a fix to
676     // bug #0009626.
677     points0->GetData()->SetNumberOfTuples(npoints0);
678     grid->GetBounds(tmpBounds); // safe to call GetBounds() for real info
679     points0->GetData()->SetNumberOfTuples(this->TotalNumberOfPoints);
680 
681     bounds[0] = ((tmpBounds[0] < bounds[0]) ? tmpBounds[0] : bounds[0]);
682     bounds[2] = ((tmpBounds[2] < bounds[2]) ? tmpBounds[2] : bounds[2]);
683     bounds[4] = ((tmpBounds[4] < bounds[4]) ? tmpBounds[4] : bounds[4]);
684 
685     bounds[1] = ((tmpBounds[1] > bounds[1]) ? tmpBounds[1] : bounds[1]);
686     bounds[3] = ((tmpBounds[3] > bounds[3]) ? tmpBounds[3] : bounds[3]);
687     bounds[5] = ((tmpBounds[5] > bounds[5]) ? tmpBounds[5] : bounds[5]);
688   }
689   if (!this->Locator)
690   {
691     vtkNew<vtkPoints> ptarray;
692     if (this->PointMergeTolerance == 0.0)
693     {
694       // testing shows vtkMergePoints is fastest when tolerance is 0
695       this->Locator = vtkSmartPointer<vtkMergePoints>::New();
696     }
697     else
698     {
699       // vtkPointLocator allows to merge duplicated points within a given tolerance
700       this->Locator = vtkSmartPointer<vtkPointLocator>::New();
701       this->Locator->SetTolerance(this->PointMergeTolerance);
702     }
703     // Set the desired precision for the points in the output.
704     if (this->OutputPointsPrecision == vtkAlgorithm::DEFAULT_PRECISION)
705     {
706       // The logical behaviour would be to use the data type from the input.
707       // However, input is a vtkDataSet, which has no point data type; only the
708       // derived class vtkPointSet has a vtkPoints attribute, so only for that
709       // the logical practice can be applied, while for others (currently
710       // vtkImageData and vtkRectilinearGrid) the data type is the default
711       // for vtkPoints - which is VTK_FLOAT.
712       if (ps)
713       {
714         ptarray->SetDataType(ps->GetPoints()->GetDataType());
715       }
716     }
717     else if (this->OutputPointsPrecision == vtkAlgorithm::SINGLE_PRECISION)
718     {
719       ptarray->SetDataType(VTK_FLOAT);
720     }
721     else if (this->OutputPointsPrecision == vtkAlgorithm::DOUBLE_PRECISION)
722     {
723       ptarray->SetDataType(VTK_DOUBLE);
724     }
725     // Init the vtkPointLocator object
726     this->Locator->InitPointInsertion(ptarray, bounds);
727   }
728 
729   vtkIdType newId;
730   double x[3];
731 
732   for (vtkIdType ptId = 0; ptId < npoints1; ptId++)
733   {
734     points1->GetPoint(ptId, x);
735     this->Locator->InsertUniquePoint(x, newId);
736     idMap[ptId] = newId;
737   }
738   return idMap;
739 }
740 
741 //------------------------------------------------------------------------------
InvalidateCachedLocator()742 void vtkMergeCells::InvalidateCachedLocator()
743 {
744   this->Locator = nullptr;
745 }
746 
747 //------------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)748 void vtkMergeCells::PrintSelf(ostream& os, vtkIndent indent)
749 {
750   this->Superclass::PrintSelf(os, indent);
751 
752   os << indent << "TotalNumberOfDataSets: " << this->TotalNumberOfDataSets << endl;
753   os << indent << "TotalNumberOfCells: " << this->TotalNumberOfCells << endl;
754   os << indent << "TotalNumberOfPoints: " << this->TotalNumberOfPoints << endl;
755 
756   os << indent << "NumberOfCells: " << this->NumberOfCells << endl;
757   os << indent << "NumberOfPoints: " << this->NumberOfPoints << endl;
758 
759   os << indent << "GlobalIdMap: " << this->GlobalIdMap->IdTypeMap.size() << endl;
760   os << indent << "GlobalCellIdMap: " << this->GlobalCellIdMap->IdTypeMap.size() << endl;
761 
762   os << indent << "OutputPointsPrecision" << this->OutputPointsPrecision << endl;
763 
764   os << indent << "PointMergeTolerance: " << this->PointMergeTolerance << endl;
765   os << indent << "MergeDuplicatePoints: " << this->MergeDuplicatePoints << endl;
766   os << indent << "InputIsUGrid: " << this->InputIsUGrid << endl;
767   os << indent << "InputIsPointSet: " << this->InputIsPointSet << endl;
768   os << indent << "UnstructuredGrid: " << this->UnstructuredGrid << endl;
769   os << indent << "PointList: " << this->PointList << endl;
770   os << indent << "CellList: " << this->CellList << endl;
771   os << indent << "UseGlobalIds: " << this->UseGlobalIds << endl;
772   os << indent << "UseGlobalCellIds: " << this->UseGlobalCellIds << endl;
773   os << indent << "Locator:";
774   if (this->Locator)
775   {
776     os << "\n";
777     this->Locator->PrintSelf(os, indent.GetNextIndent());
778   }
779   else
780   {
781     os << "(None)" << endl;
782   }
783 }
784