1 
2 /*=========================================================================
3 
4   Program:   Visualization Toolkit
5   Module:    vtkSplitColumnComponents.cxx
6 
7   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
8   All rights reserved.
9   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
10 
11      This software is distributed WITHOUT ANY WARRANTY; without even
12      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
13      PURPOSE.  See the above copyright notice for more information.
14 
15 =========================================================================*/
16 #include "vtkSplitColumnComponents.h"
17 
18 #include "vtkAbstractArray.h"
19 #include "vtkDataArrayRange.h"
20 #include "vtkFieldData.h"
21 #include "vtkInformation.h"
22 #include "vtkInformationIntegerKey.h"
23 #include "vtkInformationStringKey.h"
24 #include "vtkInformationVector.h"
25 #include "vtkIntArray.h"
26 #include "vtkObjectFactory.h"
27 #include "vtkStringArray.h"
28 #include "vtkTable.h"
29 #include "vtkVariantArray.h"
30 
31 #include <cmath>
32 #include <sstream>
33 
34 vtkStandardNewMacro(vtkSplitColumnComponents);
35 vtkInformationKeyMacro(vtkSplitColumnComponents, ORIGINAL_ARRAY_NAME, String);
36 vtkInformationKeyMacro(vtkSplitColumnComponents, ORIGINAL_COMPONENT_NUMBER, Integer);
37 //------------------------------------------------------------------------------
vtkSplitColumnComponents()38 vtkSplitColumnComponents::vtkSplitColumnComponents()
39   : CalculateMagnitudes(true)
40   , NamingMode(vtkSplitColumnComponents::NUMBERS_WITH_PARENS)
41 {
42   this->SetNumberOfInputPorts(1);
43   this->SetNumberOfOutputPorts(1);
44 }
45 
46 //------------------------------------------------------------------------------
47 vtkSplitColumnComponents::~vtkSplitColumnComponents() = default;
48 
49 //------------------------------------------------------------------------------
RequestData(vtkInformation *,vtkInformationVector ** inputVector,vtkInformationVector * outputVector)50 int vtkSplitColumnComponents::RequestData(
51   vtkInformation*, vtkInformationVector** inputVector, vtkInformationVector* outputVector)
52 {
53   // Get input tables
54   vtkInformation* table1Info = inputVector[0]->GetInformationObject(0);
55   vtkTable* table = vtkTable::SafeDownCast(table1Info->Get(vtkDataObject::DATA_OBJECT()));
56 
57   // Get output table
58   vtkInformation* outInfo = outputVector->GetInformationObject(0);
59   vtkTable* output = vtkTable::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT()));
60 
61   // Add columns from table, split multiple component columns as necessary
62   for (int i = 0; i < table->GetNumberOfColumns(); ++i)
63   {
64     vtkAbstractArray* col = table->GetColumn(i);
65     if (col->GetName() == nullptr)
66     {
67       vtkWarningMacro("Skipping column with no name!");
68       continue;
69     }
70 
71     int components = col->GetNumberOfComponents();
72     if (components == 1)
73     {
74       output->AddColumn(col);
75     }
76     else if (components > 1)
77     {
78       // Split the multicomponent column up into individual columns
79       int colSize = col->GetNumberOfTuples();
80       for (int j = 0; j < components; ++j)
81       {
82         const std::string component_label = this->GetComponentLabel(col, j);
83         vtkAbstractArray* newCol = vtkAbstractArray::CreateArray(col->GetDataType());
84         newCol->SetName(component_label.c_str());
85         newCol->SetNumberOfTuples(colSize);
86         // pass component name overrides, if provided.
87         if (col->HasAComponentName())
88         {
89           newCol->SetComponentName(0, col->GetComponentName(j));
90         }
91         // Now copy the components into their new columns
92         if (col->IsA("vtkDataArray"))
93         {
94           // Handle numeric array types
95           vtkDataArray* srcDataArray = vtkDataArray::SafeDownCast(col);
96           vtkDataArray* dstDataArray = vtkDataArray::SafeDownCast(newCol);
97           dstDataArray->CopyComponent(0, srcDataArray, j);
98         }
99         else if (col->GetDataType() == VTK_STRING)
100         {
101           vtkStringArray* srcArray = vtkStringArray::SafeDownCast(col);
102           vtkStringArray* dstArray = vtkStringArray::SafeDownCast(newCol);
103           int numSrcComponents = srcArray->GetNumberOfComponents();
104           for (vtkIdType id = 0; id < srcArray->GetNumberOfTuples(); ++id)
105           {
106             dstArray->SetValue(id, srcArray->GetValue(id * numSrcComponents + j));
107           }
108         }
109         else if (col->GetDataType() == VTK_VARIANT)
110         {
111           vtkVariantArray* srcArray = vtkVariantArray::SafeDownCast(col);
112           vtkVariantArray* dstArray = vtkVariantArray::SafeDownCast(newCol);
113           int numSrcComponents = srcArray->GetNumberOfComponents();
114           for (vtkIdType id = 0; id < srcArray->GetNumberOfTuples(); ++id)
115           {
116             dstArray->SetValue(id, srcArray->GetValue(id * numSrcComponents + j));
117           }
118         }
119         else
120         {
121           vtkErrorMacro("Unsupported array type " << col->GetClassName());
122         }
123         if (auto info = newCol->GetInformation())
124         {
125           info->Set(ORIGINAL_ARRAY_NAME(), col->GetName());
126           info->Set(ORIGINAL_COMPONENT_NUMBER(), j);
127         }
128         output->AddColumn(newCol);
129         newCol->Delete();
130       }
131       // Add a magnitude column and calculate values if requested
132       if (this->CalculateMagnitudes && col->IsA("vtkDataArray"))
133       {
134         std::string component_label = this->GetComponentLabel(col, -1 /* for magnitude */);
135         vtkAbstractArray* newCol = vtkAbstractArray::CreateArray(col->GetDataType());
136         newCol->SetName(component_label.c_str());
137         newCol->SetNumberOfTuples(colSize);
138         // Now calculate the magnitude column
139         vtkDataArray* srcDataArray = vtkDataArray::SafeDownCast(col);
140         vtkDataArray* dstDataArray = vtkDataArray::SafeDownCast(newCol);
141 
142         const auto srcRange = vtk::DataArrayTupleRange(srcDataArray);
143         auto dstRange = vtk::DataArrayValueRange<1>(dstDataArray);
144         auto dstIter = dstRange.begin();
145 
146         for (const auto tuple : srcRange)
147         {
148           double mag = 0.0;
149           for (const auto component : tuple)
150           {
151             auto x = static_cast<double>(component);
152             mag += x * x;
153           }
154           (*dstIter) = std::sqrt(mag);
155 
156           ++dstIter;
157         }
158 
159         if (auto info = newCol->GetInformation())
160         {
161           info->Set(ORIGINAL_ARRAY_NAME(), col->GetName());
162           info->Set(ORIGINAL_COMPONENT_NUMBER(), -1); // for magnitude
163         }
164         output->AddColumn(newCol);
165         newCol->Delete();
166       }
167     }
168   }
169   return 1;
170 }
171 
172 namespace
173 {
174 //------------------------------------------------------------------------------
vtkDefaultComponentName(int componentNumber,int componentCount)175 std::string vtkDefaultComponentName(int componentNumber, int componentCount)
176 {
177   if (componentCount <= 1)
178   {
179     return "";
180   }
181   else if (componentNumber == -1)
182   {
183     return "Magnitude";
184   }
185   else if (componentCount <= 3 && componentNumber < 3)
186   {
187     const char* titles[] = { "X", "Y", "Z" };
188     return titles[componentNumber];
189   }
190   else if (componentCount == 6)
191   {
192     const char* titles[] = { "XX", "YY", "ZZ", "XY", "YZ", "XZ" };
193     // Assume this is a symmetric matrix.
194     return titles[componentNumber];
195   }
196   else
197   {
198     std::ostringstream buffer;
199     buffer << componentNumber;
200     return buffer.str();
201   }
202 }
vtkGetComponentName(vtkAbstractArray * array,int component_no)203 std::string vtkGetComponentName(vtkAbstractArray* array, int component_no)
204 {
205   const char* name = array->GetComponentName(component_no);
206   if (name)
207   {
208     return name;
209   }
210   return vtkDefaultComponentName(component_no, array->GetNumberOfComponents());
211 }
212 };
213 
214 //------------------------------------------------------------------------------
GetComponentLabel(vtkAbstractArray * array,int component_no)215 std::string vtkSplitColumnComponents::GetComponentLabel(vtkAbstractArray* array, int component_no)
216 {
217   std::ostringstream stream;
218   switch (this->NamingMode)
219   {
220     case NUMBERS_WITH_PARENS:
221       stream << array->GetName() << " (";
222       if (component_no == -1)
223       {
224         stream << "Magnitude)";
225       }
226       else
227       {
228         stream << component_no << ")";
229       }
230       break;
231 
232     case NUMBERS_WITH_UNDERSCORES:
233       stream << array->GetName() << "_";
234       if (component_no == -1)
235       {
236         stream << "Magnitude";
237       }
238       else
239       {
240         stream << component_no;
241       }
242       break;
243 
244     case NAMES_WITH_PARENS:
245       stream << array->GetName() << " (" << vtkGetComponentName(array, component_no).c_str() << ")";
246       break;
247 
248     case NAMES_WITH_UNDERSCORES:
249     default:
250       stream << array->GetName() << "_" << vtkGetComponentName(array, component_no).c_str();
251       break;
252   }
253   return stream.str();
254 }
255 
256 //------------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)257 void vtkSplitColumnComponents::PrintSelf(ostream& os, vtkIndent indent)
258 {
259   this->Superclass::PrintSelf(os, indent);
260   os << indent << "CalculateMagnitudes: " << this->CalculateMagnitudes << endl;
261   os << indent << "NamingMode: ";
262   switch (this->NamingMode)
263   {
264     case NAMES_WITH_UNDERSCORES:
265       os << "NAMES_WITH_UNDERSCORES" << endl;
266       break;
267     case NAMES_WITH_PARENS:
268       os << "NAMES_WITH_PARENS" << endl;
269       break;
270     case NUMBERS_WITH_UNDERSCORES:
271       os << "NUMBERS_WITH_UNDERSCORES" << endl;
272       break;
273     case NUMBERS_WITH_PARENS:
274       os << "NUMBERS_WITH_PARENS" << endl;
275       break;
276     default:
277       os << "INVALID" << endl;
278   }
279 }
280