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