1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkContourGrid.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 #include "vtkSMPMergePolyDataHelper.h"
16 
17 #include "vtkCellArray.h"
18 #include "vtkCellData.h"
19 #include "vtkDataArray.h"
20 #include "vtkDataArrayRange.h"
21 #include "vtkNew.h"
22 #include "vtkPointData.h"
23 #include "vtkPolyData.h"
24 #include "vtkSMPMergePoints.h"
25 #include "vtkSMPTools.h"
26 #include "vtkSmartPointer.h"
27 
28 #include <algorithm>
29 
30 namespace
31 {
32 
33 struct vtkMergePointsData
34 {
35   vtkPolyData* Output;
36   vtkSMPMergePoints* Locator;
37 
vtkMergePointsData__anon97b0d2e20111::vtkMergePointsData38   vtkMergePointsData(vtkPolyData* output, vtkSMPMergePoints* locator)
39     : Output(output)
40     , Locator(locator)
41   {
42   }
43 };
44 
45 class vtkParallelMergePoints
46 {
47 public:
48   vtkIdType* BucketIds;
49   std::vector<vtkMergePointsData>::iterator Begin;
50   std::vector<vtkMergePointsData>::iterator End;
51   vtkSMPMergePoints* Merger;
52   vtkIdList** IdMaps;
53   vtkPointData* OutputPointData;
54   vtkPointData** InputPointDatas;
55 
operator ()(vtkIdType begin,vtkIdType end)56   void operator()(vtkIdType begin, vtkIdType end)
57   {
58     // All actual work is done by vtkSMPMergePoints::Merge
59     std::vector<vtkMergePointsData>::iterator itr;
60     vtkPointData* outPD = this->OutputPointData;
61 
62     vtkIdType counter = 0;
63     for (itr = Begin; itr != End; ++itr)
64     {
65       vtkIdList* idMap = this->IdMaps[counter];
66       vtkPointData* inPD = this->InputPointDatas[counter++];
67       for (vtkIdType i = begin; i < end; i++)
68       {
69         vtkIdType bucketId = BucketIds[i];
70         if ((*itr).Locator->GetNumberOfIdsInBucket(bucketId) > 0)
71         {
72           Merger->Merge((*itr).Locator, bucketId, outPD, inPD, idMap);
73         }
74       }
75     }
76   }
77 };
78 
MergePoints(std::vector<vtkMergePointsData> & data,std::vector<vtkIdList * > & idMaps,vtkPolyData * outPolyData)79 void MergePoints(
80   std::vector<vtkMergePointsData>& data, std::vector<vtkIdList*>& idMaps, vtkPolyData* outPolyData)
81 {
82   // This merges points in parallel/
83 
84   std::vector<vtkMergePointsData>::iterator itr = data.begin();
85   std::vector<vtkMergePointsData>::iterator begin = itr;
86   std::vector<vtkMergePointsData>::iterator end = data.end();
87   vtkPoints* outPts = (*itr).Output->GetPoints();
88 
89   // Prepare output points
90   vtkIdType numPts = 0;
91   while (itr != end)
92   {
93     numPts += (*itr).Output->GetNumberOfPoints();
94     ++itr;
95   }
96   outPts->Resize(numPts);
97 
98   // Find non-empty buckets for best load balancing. We don't
99   // want to visit bunch of empty buckets.
100   vtkIdType numBuckets = (*begin).Locator->GetNumberOfBuckets();
101   std::vector<vtkIdType> nonEmptyBuckets;
102   std::vector<bool> bucketVisited(numBuckets, false);
103   nonEmptyBuckets.reserve(numBuckets);
104   for (itr = begin; itr != end; ++itr)
105   {
106     vtkSMPMergePoints* mp = (*itr).Locator;
107     for (vtkIdType i = 0; i < numBuckets; i++)
108     {
109       if (mp->GetNumberOfIdsInBucket(i) > 0 && !bucketVisited[i])
110       {
111         nonEmptyBuckets.push_back(i);
112         bucketVisited[i] = true;
113       }
114     }
115   }
116 
117   // These id maps will later be used when merging cells.
118   std::vector<vtkPointData*> pds;
119   itr = begin;
120   ++itr;
121   while (itr != end)
122   {
123     pds.push_back((*itr).Output->GetPointData());
124     vtkIdList* idMap = vtkIdList::New();
125     idMap->Allocate((*itr).Output->GetNumberOfPoints());
126     idMaps.push_back(idMap);
127     ++itr;
128   }
129 
130   vtkParallelMergePoints mergePoints;
131   mergePoints.BucketIds = &nonEmptyBuckets[0];
132   mergePoints.Merger = (*begin).Locator;
133   mergePoints.OutputPointData = (*begin).Output->GetPointData();
134   if (!idMaps.empty())
135   {
136     mergePoints.Merger->InitializeMerge();
137     mergePoints.IdMaps = &idMaps[0];
138     // Prepare output point data
139     int numArrays = mergePoints.OutputPointData->GetNumberOfArrays();
140     for (int i = 0; i < numArrays; i++)
141     {
142       mergePoints.OutputPointData->GetArray(i)->Resize(numPts);
143     }
144     mergePoints.InputPointDatas = &pds[0];
145 
146     // The first locator is what we will use to accumulate all others
147     // So all iteration starts from second dataset.
148     std::vector<vtkMergePointsData>::iterator second = begin;
149     ++second;
150     mergePoints.Begin = second;
151     mergePoints.End = end;
152     // Actual work
153     vtkSMPTools::For(0, static_cast<vtkIdType>(nonEmptyBuckets.size()), mergePoints);
154     // mergePoints.operator()(0, nonEmptyBuckets.size());
155 
156     // Fixup output sizes.
157     mergePoints.Merger->FixSizeOfPointArray();
158     for (int i = 0; i < numArrays; i++)
159     {
160       mergePoints.OutputPointData->GetArray(i)->SetNumberOfTuples(
161         mergePoints.Merger->GetMaxId() + 1);
162     }
163   }
164   outPolyData->SetPoints(mergePoints.Merger->GetPoints());
165   outPolyData->GetPointData()->ShallowCopy(mergePoints.OutputPointData);
166 }
167 
168 class vtkParallelMergeCells
169 {
170 public:
171   vtkIdList* CellOffsets;
172   vtkIdList* ConnOffsets;
173   vtkCellArray* InCellArray;
174   vtkCellArray* OutCellArray;
175   vtkIdType OutputCellOffset;
176   vtkIdType OutputConnOffset;
177   vtkIdList* IdMap;
178 
179   struct MapCellsImpl
180   {
181     // Call this signature:
182     template <typename InCellStateT>
operator ()__anon97b0d2e20111::vtkParallelMergeCells::MapCellsImpl183     void operator()(InCellStateT& inState, vtkCellArray* outCells, vtkIdType inCellOffset,
184       vtkIdType inCellOffsetEnd, vtkIdType inConnOffset, vtkIdType inConnOffsetEnd,
185       vtkIdType outCellOffset, vtkIdType outConnOffset, vtkIdList* map)
186     {
187       outCells->Visit(*this, inState, inCellOffset, inCellOffsetEnd, inConnOffset, inConnOffsetEnd,
188         outCellOffset, outConnOffset, map);
189     }
190 
191     // Internal signature:
192     template <typename InCellStateT, typename OutCellStateT>
operator ()__anon97b0d2e20111::vtkParallelMergeCells::MapCellsImpl193     void operator()(OutCellStateT& outState, InCellStateT& inState, vtkIdType inCellOffset,
194       vtkIdType inCellOffsetEnd, vtkIdType inConnOffset, vtkIdType inConnOffsetEnd,
195       vtkIdType outCellOffset, vtkIdType outConnOffset, vtkIdList* map)
196     {
197       using InIndexType = typename InCellStateT::ValueType;
198       using OutIndexType = typename OutCellStateT::ValueType;
199 
200       const auto inCell =
201         vtk::DataArrayValueRange<1>(inState.GetOffsets(), inCellOffset, inCellOffsetEnd + 1);
202       const auto inConn =
203         vtk::DataArrayValueRange<1>(inState.GetConnectivity(), inConnOffset, inConnOffsetEnd);
204       auto outCell =
205         vtk::DataArrayValueRange<1>(outState.GetOffsets(), outCellOffset + inCellOffset);
206       auto outConn =
207         vtk::DataArrayValueRange<1>(outState.GetConnectivity(), outConnOffset + inConnOffset);
208 
209       // Copy the offsets, adding outConnOffset to adjust for existing
210       // connectivity entries:
211       std::transform(
212         inCell.cbegin(), inCell.cend(), outCell.begin(), [&](InIndexType i) -> OutIndexType {
213           return static_cast<OutIndexType>(i + outConnOffset);
214         });
215 
216       // Copy the connectivities, passing them through the map:
217       std::transform(
218         inConn.cbegin(), inConn.cend(), outConn.begin(), [&](InIndexType i) -> OutIndexType {
219           return static_cast<OutIndexType>(map->GetId(static_cast<vtkIdType>(i)));
220         });
221     }
222   };
223 
operator ()(vtkIdType begin,vtkIdType end)224   void operator()(vtkIdType begin, vtkIdType end)
225   {
226     vtkIdType noffsets = this->CellOffsets->GetNumberOfIds();
227     vtkIdList* cellOffsets = this->CellOffsets;
228     vtkIdList* connOffsets = this->ConnOffsets;
229     vtkCellArray* outCellArray = this->OutCellArray;
230     vtkCellArray* inCellArray = this->InCellArray;
231     vtkIdType outputCellOffset = this->OutputCellOffset;
232     vtkIdType outputConnOffset = this->OutputConnOffset;
233     vtkIdList* map = this->IdMap;
234 
235     for (vtkIdType i = begin; i < end; i++)
236     {
237       // Note that there may be multiple cells starting at
238       // this offset. So we find the next offset and insert
239       // all cells between here and there.
240       vtkIdType nextCellOffset;
241       vtkIdType nextConnOffset;
242       if (i ==
243         noffsets - 1) // This needs to be the end of the array always, not the loop counter's end
244       {
245         nextCellOffset = this->InCellArray->GetNumberOfCells();
246         nextConnOffset = this->InCellArray->GetNumberOfConnectivityIds();
247       }
248       else
249       {
250         nextCellOffset = cellOffsets->GetId(i + 1);
251         nextConnOffset = connOffsets->GetId(i + 1);
252       }
253       // Process all cells between the given offset and the next.
254       vtkIdType cellOffset = cellOffsets->GetId(i);
255       vtkIdType connOffset = connOffsets->GetId(i);
256 
257       inCellArray->Visit(MapCellsImpl{}, outCellArray, cellOffset, nextCellOffset, connOffset,
258         nextConnOffset, outputCellOffset, outputConnOffset, map);
259     }
260   }
261 };
262 
263 class vtkParallelCellDataCopier
264 {
265 public:
266   vtkDataSetAttributes* InputCellData;
267   vtkDataSetAttributes* OutputCellData;
268   vtkIdType Offset;
269 
operator ()(vtkIdType begin,vtkIdType end)270   void operator()(vtkIdType begin, vtkIdType end)
271   {
272     vtkDataSetAttributes* inputCellData = this->InputCellData;
273     vtkDataSetAttributes* outputCellData = this->OutputCellData;
274     vtkIdType offset = this->Offset;
275 
276     for (vtkIdType i = begin; i < end; i++)
277     {
278       outputCellData->SetTuple(offset + i, i, inputCellData);
279     }
280   }
281 };
282 
283 struct vtkMergeCellsData
284 {
285   vtkPolyData* Output;
286   vtkIdList* CellOffsets;
287   vtkIdList* ConnOffsets;
288   vtkCellArray* OutCellArray;
289 
vtkMergeCellsData__anon97b0d2e20111::vtkMergeCellsData290   vtkMergeCellsData(
291     vtkPolyData* output, vtkIdList* cellOffsets, vtkIdList* connOffsets, vtkCellArray* cellArray)
292     : Output(output)
293     , CellOffsets(cellOffsets)
294     , ConnOffsets(connOffsets)
295     , OutCellArray(cellArray)
296   {
297   }
298 };
299 
300 struct CopyCellArraysToFront
301 {
302   // call this signature:
303   template <typename OutCellArraysT>
operator ()__anon97b0d2e20111::CopyCellArraysToFront304   void operator()(OutCellArraysT& out, vtkCellArray* in)
305   {
306     in->Visit(*this, out);
307   }
308 
309   // Internal signature:
310   template <typename InCellArraysT, typename OutCellArraysT>
operator ()__anon97b0d2e20111::CopyCellArraysToFront311   void operator()(InCellArraysT& in, OutCellArraysT& out)
312   {
313     using InIndexType = typename InCellArraysT::ValueType;
314     using OutIndexType = typename OutCellArraysT::ValueType;
315 
316     const auto inCell = vtk::DataArrayValueRange<1>(in.GetOffsets());
317     const auto inConn = vtk::DataArrayValueRange<1>(in.GetConnectivity());
318     auto outCell = vtk::DataArrayValueRange<1>(out.GetOffsets());
319     auto outConn = vtk::DataArrayValueRange<1>(out.GetConnectivity());
320 
321     auto cast = [](InIndexType i) -> OutIndexType { return static_cast<OutIndexType>(i); };
322 
323     std::transform(inCell.cbegin(), inCell.cend(), outCell.begin(), cast);
324     std::transform(inConn.cbegin(), inConn.cend(), outConn.begin(), cast);
325   }
326 };
327 
MergeCells(std::vector<vtkMergeCellsData> & data,const std::vector<vtkIdList * > & idMaps,vtkIdType cellDataOffset,vtkCellArray * outCells)328 void MergeCells(std::vector<vtkMergeCellsData>& data, const std::vector<vtkIdList*>& idMaps,
329   vtkIdType cellDataOffset, vtkCellArray* outCells)
330 {
331   std::vector<vtkMergeCellsData>::iterator begin = data.begin();
332   std::vector<vtkMergeCellsData>::iterator itr;
333   std::vector<vtkMergeCellsData>::iterator second = begin;
334   std::vector<vtkMergeCellsData>::iterator end = data.end();
335   ++second;
336 
337   std::vector<vtkIdList*>::const_iterator mapIter = idMaps.begin();
338 
339   vtkCellArray* firstCells = (*begin).OutCellArray;
340 
341   vtkIdType outCellOffset = 0;
342   vtkIdType outConnOffset = 0;
343   outCellOffset += firstCells->GetNumberOfCells();
344   outConnOffset += firstCells->GetNumberOfConnectivityIds();
345 
346   // Prepare output. Since there's no mapping here, do a simple copy in
347   // serial:
348   outCells->Visit(CopyCellArraysToFront{}, firstCells);
349 
350   vtkParallelMergeCells mergeCells;
351   mergeCells.OutCellArray = outCells;
352 
353   // The first locator is what we will use to accumulate all others
354   // So all iteration starts from second dataset.
355   for (itr = second; itr != end; ++itr, ++mapIter)
356   {
357     mergeCells.CellOffsets = (*itr).CellOffsets;
358     mergeCells.ConnOffsets = (*itr).ConnOffsets;
359     mergeCells.InCellArray = (*itr).OutCellArray;
360     mergeCells.OutputCellOffset = outCellOffset;
361     mergeCells.OutputConnOffset = outConnOffset;
362     mergeCells.IdMap = *mapIter;
363 
364     // First, we merge the cell arrays. This also adjust point ids.
365     vtkSMPTools::For(0, mergeCells.CellOffsets->GetNumberOfIds(), mergeCells);
366 
367     outCellOffset += (*itr).OutCellArray->GetNumberOfCells();
368     outConnOffset += (*itr).OutCellArray->GetNumberOfConnectivityIds();
369   }
370 
371   vtkIdType outCellsOffset = cellDataOffset + (*begin).OutCellArray->GetNumberOfCells();
372 
373   // Now copy cell data in parallel
374   vtkParallelCellDataCopier cellCopier;
375   cellCopier.OutputCellData = (*begin).Output->GetCellData();
376   int numCellArrays = cellCopier.OutputCellData->GetNumberOfArrays();
377   if (numCellArrays > 0)
378   {
379     for (itr = second; itr != end; ++itr)
380     {
381       cellCopier.InputCellData = (*itr).Output->GetCellData();
382       cellCopier.Offset = outCellsOffset;
383       vtkCellArray* cells = (*itr).OutCellArray;
384 
385       vtkSMPTools::For(0, cells->GetNumberOfCells(), cellCopier);
386       // cellCopier.operator()(0, polys->GetNumberOfCells());
387 
388       outCellsOffset += (*itr).Output->GetPolys()->GetNumberOfCells();
389     }
390   }
391 }
392 }
393 
MergePolyData(std::vector<InputData> & inputs)394 vtkPolyData* vtkSMPMergePolyDataHelper::MergePolyData(std::vector<InputData>& inputs)
395 {
396   // First merge points
397 
398   std::vector<InputData>::iterator itr = inputs.begin();
399   std::vector<InputData>::iterator begin = itr;
400   std::vector<InputData>::iterator end = inputs.end();
401 
402   std::vector<vtkMergePointsData> mpData;
403   while (itr != end)
404   {
405     mpData.emplace_back((*itr).Input, (*itr).Locator);
406     ++itr;
407   }
408 
409   std::vector<vtkIdList*> idMaps;
410   vtkPolyData* outPolyData = vtkPolyData::New();
411 
412   MergePoints(mpData, idMaps, outPolyData);
413 
414   itr = begin;
415   vtkIdType vertSize = 0;
416   vtkIdType lineSize = 0;
417   vtkIdType polySize = 0;
418   vtkIdType numVerts = 0;
419   vtkIdType numLines = 0;
420   vtkIdType numPolys = 0;
421   std::vector<vtkMergeCellsData> mcData;
422   while (itr != end)
423   {
424     vertSize += (*itr).Input->GetVerts()->GetNumberOfConnectivityIds();
425     lineSize += (*itr).Input->GetLines()->GetNumberOfConnectivityIds();
426     polySize += (*itr).Input->GetPolys()->GetNumberOfConnectivityIds();
427     numVerts += (*itr).Input->GetVerts()->GetNumberOfCells();
428     numLines += (*itr).Input->GetLines()->GetNumberOfCells();
429     numPolys += (*itr).Input->GetPolys()->GetNumberOfCells();
430     ++itr;
431   }
432 
433   vtkIdType numOutCells = numVerts + numLines + numPolys;
434 
435   vtkCellData* outCellData = (*begin).Input->GetCellData();
436   int numCellArrays = outCellData->GetNumberOfArrays();
437   for (int i = 0; i < numCellArrays; i++)
438   {
439     outCellData->GetArray(i)->Resize(numOutCells);
440     outCellData->GetArray(i)->SetNumberOfTuples(numOutCells);
441   }
442 
443   // Now merge each cell type. Because vtkPolyData stores each
444   // cell type separately, we need to merge them separately.
445 
446   if (vertSize > 0)
447   {
448     vtkNew<vtkCellArray> outVerts;
449     outVerts->ResizeExact(numVerts, vertSize);
450 
451     itr = begin;
452     while (itr != end)
453     {
454       mcData.emplace_back(
455         (*itr).Input, (*itr).VertCellOffsets, (*itr).VertConnOffsets, (*itr).Input->GetVerts());
456       ++itr;
457     }
458     MergeCells(mcData, idMaps, 0, outVerts);
459 
460     outPolyData->SetVerts(outVerts);
461 
462     mcData.clear();
463   }
464 
465   if (lineSize > 0)
466   {
467     vtkNew<vtkCellArray> outLines;
468     outLines->ResizeExact(numLines, lineSize);
469 
470     itr = begin;
471     while (itr != end)
472     {
473       mcData.emplace_back(
474         (*itr).Input, (*itr).LineCellOffsets, (*itr).LineConnOffsets, (*itr).Input->GetLines());
475       ++itr;
476     }
477     MergeCells(mcData, idMaps, vertSize, outLines);
478 
479     outPolyData->SetLines(outLines);
480 
481     mcData.clear();
482   }
483 
484   if (polySize > 0)
485   {
486     vtkNew<vtkCellArray> outPolys;
487     outPolys->ResizeExact(numPolys, polySize);
488 
489     itr = begin;
490     while (itr != end)
491     {
492       mcData.emplace_back(
493         (*itr).Input, (*itr).PolyCellOffsets, (*itr).PolyConnOffsets, (*itr).Input->GetPolys());
494       ++itr;
495     }
496     MergeCells(mcData, idMaps, vertSize + lineSize, outPolys);
497 
498     outPolyData->SetPolys(outPolys);
499   }
500 
501   outPolyData->GetCellData()->ShallowCopy(outCellData);
502 
503   std::vector<vtkIdList*>::iterator mapIter = idMaps.begin();
504   while (mapIter != idMaps.end())
505   {
506     (*mapIter)->Delete();
507     ++mapIter;
508   }
509 
510   return outPolyData;
511 }
512