1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkSortDataArray.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 /*
17  * Copyright 2003 Sandia Corporation.
18  * Under the terms of Contract DE-AC04-94AL85000, there is a non-exclusive
19  * license for use of this work by or on behalf of the
20  * U.S. Government. Redistribution and use in source and binary forms, with
21  * or without modification, are permitted provided that this Notice and any
22  * statement of authorship are reproduced on all copies.
23  */
24 
25 #include "vtkSortDataArray.h"
26 
27 #include "vtkAbstractArray.h"
28 #include "vtkIdList.h"
29 #include "vtkMath.h"
30 #include "vtkObjectFactory.h"
31 #include "vtkSMPTools.h"
32 #include "vtkStdString.h"
33 #include "vtkStringArray.h"
34 #include "vtkVariant.h"
35 #include "vtkVariantArray.h"
36 #include <functional> //std::greater
37 
38 //------------------------------------------------------------------------------
39 
40 vtkStandardNewMacro(vtkSortDataArray);
41 
42 //------------------------------------------------------------------------------
43 vtkSortDataArray::vtkSortDataArray() = default;
44 
45 //------------------------------------------------------------------------------
46 vtkSortDataArray::~vtkSortDataArray() = default;
47 
48 //------------------------------------------------------------------------------
Sort(vtkIdList * keys,int dir)49 void vtkSortDataArray::Sort(vtkIdList* keys, int dir)
50 {
51   if (keys == nullptr)
52   {
53     return;
54   }
55   vtkIdType* data = keys->GetPointer(0);
56   vtkIdType numKeys = keys->GetNumberOfIds();
57   if (dir == 0)
58   {
59     vtkSMPTools::Sort(data, data + numKeys);
60   }
61   else
62   {
63     vtkSMPTools::Sort(data, data + numKeys, std::greater<vtkIdType>());
64   }
65 }
66 
67 //------------------------------------------------------------------------------
Sort(vtkAbstractArray * keys,int dir)68 void vtkSortDataArray::Sort(vtkAbstractArray* keys, int dir)
69 {
70   if (keys == nullptr)
71   {
72     return;
73   }
74 
75   if (keys->GetNumberOfComponents() != 1)
76   {
77     vtkGenericWarningMacro("Can only sort keys that are 1-tuples.");
78     return;
79   }
80 
81   void* data = keys->GetVoidPointer(0);
82   vtkIdType numKeys = keys->GetNumberOfTuples();
83 
84   if (dir == 0)
85   {
86     switch (keys->GetDataType())
87     {
88       vtkExtendedTemplateMacro(
89         vtkSMPTools::Sort(static_cast<VTK_TT*>(data), static_cast<VTK_TT*>(data) + numKeys));
90     }
91   }
92   else
93   {
94     switch (keys->GetDataType())
95     {
96       vtkExtendedTemplateMacro(vtkSMPTools::Sort(
97         static_cast<VTK_TT*>(data), static_cast<VTK_TT*>(data) + numKeys, std::greater<VTK_TT>()));
98     }
99   }
100 }
101 
102 //------------------------------------------------------------------------------
103 // Hide some stuff; mostly things plugged into templated functions
104 namespace
105 {
106 
107 //------------------------------------------------------------------------------
108 // We sort the indices based on a key value in another array. Produces sort
109 // in ascending direction. Note that sort comparison operator is for single
110 // component arrays.
111 template <typename T>
112 struct KeyComp
113 {
114   const T* Array;
KeyComp__anon7d4c914e0111::KeyComp115   KeyComp(T* array)
116     : Array(array)
117   {
118   }
operator ()__anon7d4c914e0111::KeyComp119   bool operator()(vtkIdType idx0, vtkIdType idx1) const { return (Array[idx0] < Array[idx1]); }
120 };
121 
122 //------------------------------------------------------------------------------
123 // Special comparison functor using tuple component as a key. Note that this
124 // comparison function is for general arrays of n components.
125 template <typename T>
126 struct TupleComp
127 {
128   const T* Array;
129   int NumComp;
130   int K;
TupleComp__anon7d4c914e0111::TupleComp131   TupleComp(T* array, int n, int k)
132     : Array(array)
133     , NumComp(n)
134     , K(k)
135   {
136   }
operator ()__anon7d4c914e0111::TupleComp137   bool operator()(vtkIdType idx0, vtkIdType idx1) const
138   {
139     return Array[idx0 * NumComp + K] < Array[idx1 * NumComp + K];
140   }
141 };
142 
143 //------------------------------------------------------------------------------
144 // Given a set of indices (after sorting), copy the data from a pre-sorted
145 // array to a final, post-sorted array, Implementation note: the direction of
146 // sort (dir) is treated here rather than in the std::sort() function to
147 // reduce object file .obj size; e.g., running std::sort with a different
148 // comporator function causes inline expansion to produce very large object
149 // files.
150 template <typename T>
Shuffle1Tuples(vtkIdType * idx,vtkIdType sze,vtkAbstractArray * arrayIn,T * preSort,int dir)151 void Shuffle1Tuples(vtkIdType* idx, vtkIdType sze, vtkAbstractArray* arrayIn, T* preSort, int dir)
152 {
153   T* postSort = new T[sze];
154 
155   if (dir == 0) // ascending
156   {
157     for (vtkIdType i = 0; i < sze; ++i)
158     {
159       postSort[i] = preSort[idx[i]];
160     }
161   }
162   else
163   {
164     vtkIdType end = sze - 1;
165     for (vtkIdType i = 0; i < sze; ++i)
166     {
167       postSort[i] = preSort[idx[end - i]];
168     }
169   }
170 
171   arrayIn->SetVoidArray(postSort, sze, 0, vtkAbstractArray::VTK_DATA_ARRAY_DELETE);
172 }
173 
174 //------------------------------------------------------------------------------
175 // Given a set of indices (after sorting), copy the data from a pre-sorted
176 // data array to a final, post-sorted array. Note that the data array is
177 // assumed to have arbitrary sized components.
178 template <typename T>
ShuffleTuples(vtkIdType * idx,vtkIdType sze,int numComp,vtkAbstractArray * arrayIn,T * preSort,int dir)179 void ShuffleTuples(
180   vtkIdType* idx, vtkIdType sze, int numComp, vtkAbstractArray* arrayIn, T* preSort, int dir)
181 {
182   T* postSort = new T[sze * numComp];
183 
184   int k;
185   vtkIdType i;
186   if (dir == 0) // ascending
187   {
188     for (i = 0; i < sze; ++i)
189     {
190       for (k = 0; k < numComp; ++k)
191       {
192         postSort[i * numComp + k] = preSort[idx[i] * numComp + k];
193       }
194     }
195   }
196   else
197   {
198     vtkIdType end = sze - 1;
199     for (i = 0; i < sze; ++i)
200     {
201       for (k = 0; k < numComp; ++k)
202       {
203         postSort[i * numComp + k] = preSort[idx[end - i] * numComp + k];
204       }
205     }
206   }
207 
208   arrayIn->SetVoidArray(postSort, sze * numComp, 0, vtkAbstractArray::VTK_DATA_ARRAY_DELETE);
209 }
210 
211 } // anonymous namespace
212 
213 //------------------------------------------------------------------------------
214 // Allocate and initialize sort indices
InitializeSortIndices(vtkIdType num)215 vtkIdType* vtkSortDataArray::InitializeSortIndices(vtkIdType num)
216 {
217   vtkIdType* idx = new vtkIdType[num];
218   for (vtkIdType i = 0; i < num; ++i)
219   {
220     idx[i] = i;
221   }
222   return idx;
223 }
224 
225 //------------------------------------------------------------------------------
226 // Efficient function for generating sort ordering specialized to single
227 // component arrays.
GenerateSort1Indices(int dataType,void * dataIn,vtkIdType numKeys,vtkIdType * idx)228 void vtkSortDataArray::GenerateSort1Indices(
229   int dataType, void* dataIn, vtkIdType numKeys, vtkIdType* idx)
230 {
231   if (dataType == VTK_VARIANT)
232   {
233     vtkSMPTools::Sort(idx, idx + numKeys, KeyComp<vtkVariant>(static_cast<vtkVariant*>(dataIn)));
234   }
235   else
236   {
237     switch (dataType)
238     {
239       vtkExtendedTemplateMacro(
240         vtkSMPTools::Sort(idx, idx + numKeys, KeyComp<VTK_TT>(static_cast<VTK_TT*>(dataIn))));
241     }
242   }
243 }
244 
245 //------------------------------------------------------------------------------
246 // Function for generating sort ordering for general arrays.
GenerateSortIndices(int dataType,void * dataIn,vtkIdType numKeys,int numComp,int k,vtkIdType * idx)247 void vtkSortDataArray::GenerateSortIndices(
248   int dataType, void* dataIn, vtkIdType numKeys, int numComp, int k, vtkIdType* idx)
249 {
250   // Specialized and faster for single component arrays
251   if (numComp == 1)
252   {
253     return vtkSortDataArray::GenerateSort1Indices(dataType, dataIn, numKeys, idx);
254   }
255 
256   if (dataType == VTK_VARIANT)
257   {
258     vtkSMPTools::Sort(
259       idx, idx + numKeys, TupleComp<vtkVariant>(static_cast<vtkVariant*>(dataIn), numComp, k));
260   }
261   else
262   {
263     switch (dataType)
264     {
265       vtkExtendedTemplateMacro(vtkSMPTools::Sort(
266         idx, idx + numKeys, TupleComp<VTK_TT>(static_cast<VTK_TT*>(dataIn), numComp, k)));
267     }
268   }
269 }
270 
271 //------------------------------------------------------------------------------
272 // Set up the actual templated shuffling operation. This method is for
273 // VTK arrays that are precsisely one component.
Shuffle1Array(vtkIdType * idx,int dataType,vtkIdType numKeys,vtkAbstractArray * arr,void * dataIn,int dir)274 void vtkSortDataArray::Shuffle1Array(
275   vtkIdType* idx, int dataType, vtkIdType numKeys, vtkAbstractArray* arr, void* dataIn, int dir)
276 {
277   if (dataType == VTK_VARIANT)
278   {
279     Shuffle1Tuples(idx, numKeys, arr, static_cast<vtkVariant*>(dataIn), dir);
280   }
281   else
282   {
283     switch (arr->GetDataType())
284     {
285       vtkExtendedTemplateMacro(
286         Shuffle1Tuples(idx, numKeys, arr, static_cast<VTK_TT*>(dataIn), dir));
287     }
288   }
289 }
290 
291 //------------------------------------------------------------------------------
292 // Set up the actual templated shuffling operation
ShuffleArray(vtkIdType * idx,int dataType,vtkIdType numKeys,int numComp,vtkAbstractArray * arr,void * dataIn,int dir)293 void vtkSortDataArray::ShuffleArray(vtkIdType* idx, int dataType, vtkIdType numKeys, int numComp,
294   vtkAbstractArray* arr, void* dataIn, int dir)
295 {
296   // Specialized for single component arrays
297   if (numComp == 1)
298   {
299     return vtkSortDataArray::Shuffle1Array(idx, dataType, numKeys, arr, dataIn, dir);
300   }
301 
302   if (dataType == VTK_VARIANT)
303   {
304     ShuffleTuples(idx, numKeys, numComp, arr, static_cast<vtkVariant*>(dataIn), dir);
305   }
306   else
307   {
308     switch (arr->GetDataType())
309     {
310       vtkExtendedTemplateMacro(
311         ShuffleTuples(idx, numKeys, numComp, arr, static_cast<VTK_TT*>(dataIn), dir));
312     }
313   }
314 }
315 
316 //------------------------------------------------------------------------------
317 // Given a set of indices (after sorting), copy the ids from a pre-sorted
318 // id array to a final, post-sorted array.
ShuffleIdList(vtkIdType * idx,vtkIdType sze,vtkIdList * arrayIn,vtkIdType * preSort,int dir)319 void vtkSortDataArray::ShuffleIdList(
320   vtkIdType* idx, vtkIdType sze, vtkIdList* arrayIn, vtkIdType* preSort, int dir)
321 {
322   vtkIdType* postSort = new vtkIdType[sze];
323 
324   if (dir == 0) // ascending
325   {
326     for (vtkIdType i = 0; i < sze; ++i)
327     {
328       postSort[i] = preSort[idx[i]];
329     }
330   }
331   else
332   {
333     vtkIdType end = sze - 1;
334     for (vtkIdType i = 0; i < sze; ++i)
335     {
336       postSort[i] = preSort[idx[end - i]];
337     }
338   }
339 
340   arrayIn->SetArray(postSort, sze);
341 }
342 
343 //------------------------------------------------------------------------------
344 // Sort a position index based on the values in the abstract array. Once
345 // sorted, then shuffle the keys and values around into new arrays.
Sort(vtkAbstractArray * keys,vtkAbstractArray * values,int dir)346 void vtkSortDataArray::Sort(vtkAbstractArray* keys, vtkAbstractArray* values, int dir)
347 {
348   // Check input
349   if (keys == nullptr || values == nullptr)
350   {
351     return;
352   }
353   if (keys->GetNumberOfComponents() != 1)
354   {
355     vtkGenericWarningMacro("Can only sort keys that are 1-tuples.");
356     return;
357   }
358   vtkIdType numKeys = keys->GetNumberOfTuples();
359   vtkIdType numValues = values->GetNumberOfTuples();
360   if (numKeys != numValues)
361   {
362     vtkGenericWarningMacro("Could not sort arrays.  Key and value arrays have different sizes.");
363     return;
364   }
365 
366   // Sort the index array
367   vtkIdType* idx = vtkSortDataArray::InitializeSortIndices(numKeys);
368 
369   // Generate the sorting index array
370   void* dataIn = keys->GetVoidPointer(0);
371   int numComp = 1;
372   int dataType = keys->GetDataType();
373   vtkSortDataArray::GenerateSortIndices(dataType, dataIn, numKeys, numComp, 0, idx);
374 
375   // Now shuffle data around based on sorted indices
376   vtkSortDataArray::ShuffleArray(idx, dataType, numKeys, numComp, keys, dataIn, dir);
377 
378   dataIn = values->GetVoidPointer(0);
379   numComp = values->GetNumberOfComponents();
380   dataType = values->GetDataType();
381   vtkSortDataArray::ShuffleArray(idx, dataType, numKeys, numComp, values, dataIn, dir);
382 
383   // Clean up
384   delete[] idx;
385 }
386 
387 //------------------------------------------------------------------------------
Sort(vtkAbstractArray * keys,vtkIdList * values,int dir)388 void vtkSortDataArray::Sort(vtkAbstractArray* keys, vtkIdList* values, int dir)
389 {
390   // Check input
391   if (keys == nullptr || values == nullptr)
392   {
393     return;
394   }
395   if (keys->GetNumberOfComponents() != 1)
396   {
397     vtkGenericWarningMacro("Can only sort keys that are 1-tuples.");
398     return;
399   }
400   vtkIdType numKeys = keys->GetNumberOfTuples();
401   vtkIdType numIds = values->GetNumberOfIds();
402   if (numKeys != numIds)
403   {
404     vtkGenericWarningMacro("Could not sort arrays.  Key and id arrays have different sizes.");
405     return;
406   }
407 
408   // Sort the index array
409   vtkIdType* idx = vtkSortDataArray::InitializeSortIndices(numKeys);
410 
411   // Generate the sorting index array
412   void* dataIn = keys->GetVoidPointer(0);
413   int numComp = 1;
414   int dataType = keys->GetDataType();
415   vtkSortDataArray::GenerateSortIndices(dataType, dataIn, numKeys, numComp, 0, idx);
416 
417   // Shuffle the keys
418   vtkSortDataArray::ShuffleArray(idx, dataType, numKeys, numComp, keys, dataIn, dir);
419 
420   // Now shuffle the ids to match the sort
421   vtkIdType* ids = values->GetPointer(0);
422   ShuffleIdList(idx, numKeys, values, ids, dir);
423 
424   // Clean up
425   delete[] idx;
426 }
427 
428 //------------------------------------------------------------------------------
SortArrayByComponent(vtkAbstractArray * arr,int k,int dir)429 void vtkSortDataArray::SortArrayByComponent(vtkAbstractArray* arr, int k, int dir)
430 {
431   // Check input
432   if (arr == nullptr)
433   {
434     return;
435   }
436   vtkIdType numKeys = arr->GetNumberOfTuples();
437   int nc = arr->GetNumberOfComponents();
438 
439   if (k < 0 || k >= nc)
440   {
441     vtkGenericWarningMacro(
442       "Cannot sort by column " << k << " since the array only has columns 0 through " << (nc - 1));
443     return;
444   }
445 
446   // Perform the sort
447   vtkIdType* idx = vtkSortDataArray::InitializeSortIndices(numKeys);
448 
449   void* dataIn = arr->GetVoidPointer(0);
450   int dataType = arr->GetDataType();
451   vtkSortDataArray::GenerateSortIndices(dataType, dataIn, numKeys, nc, k, idx);
452 
453   vtkSortDataArray::ShuffleArray(idx, dataType, numKeys, nc, arr, dataIn, dir);
454 
455   // Clean up
456   delete[] idx;
457 }
458 
459 //------------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)460 void vtkSortDataArray::PrintSelf(ostream& os, vtkIndent indent)
461 {
462   this->Superclass::PrintSelf(os, indent);
463 }
464 
465 // vtkSortDataArray methods -------------------------------------------------------
466