1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkParallelCoordinatesActor.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 "vtkParallelCoordinatesActor.h"
16 
17 #include "vtkAxisActor2D.h"
18 #include "vtkCellArray.h"
19 #include "vtkFieldData.h"
20 #include "vtkMath.h"
21 #include "vtkObjectFactory.h"
22 #include "vtkPolyData.h"
23 #include "vtkPolyDataMapper2D.h"
24 #include "vtkTextMapper.h"
25 #include "vtkTextProperty.h"
26 #include "vtkTrivialProducer.h"
27 #include "vtkViewport.h"
28 #include "vtkWindow.h"
29 
30 vtkStandardNewMacro(vtkParallelCoordinatesActor);
31 
32 vtkCxxSetObjectMacro(vtkParallelCoordinatesActor, LabelTextProperty, vtkTextProperty);
33 vtkCxxSetObjectMacro(vtkParallelCoordinatesActor, TitleTextProperty, vtkTextProperty);
34 
35 class vtkParallelCoordinatesActorConnection : public vtkAlgorithm
36 {
37 public:
38   static vtkParallelCoordinatesActorConnection* New();
39   vtkTypeMacro(vtkParallelCoordinatesActorConnection, vtkAlgorithm);
40 
vtkParallelCoordinatesActorConnection()41   vtkParallelCoordinatesActorConnection() { this->SetNumberOfInputPorts(1); }
42 };
43 
44 vtkStandardNewMacro(vtkParallelCoordinatesActorConnection);
45 
46 //------------------------------------------------------------------------------
47 // Instantiate object
vtkParallelCoordinatesActor()48 vtkParallelCoordinatesActor::vtkParallelCoordinatesActor()
49 {
50   this->PositionCoordinate->SetCoordinateSystemToNormalizedViewport();
51   this->PositionCoordinate->SetValue(0.1, 0.1);
52 
53   this->Position2Coordinate->SetValue(0.9, 0.8);
54 
55   this->IndependentVariables = VTK_IV_COLUMN;
56   this->N = 0;
57 
58   this->ConnectionHolder = vtkParallelCoordinatesActorConnection::New();
59 
60   this->Axes = nullptr;
61   this->Mins = nullptr;
62   this->Maxs = nullptr;
63   this->Xs = nullptr;
64 
65   this->Title = nullptr;
66 
67   this->TitleMapper = vtkTextMapper::New();
68 
69   this->TitleActor = vtkActor2D::New();
70   this->TitleActor->SetMapper(this->TitleMapper);
71   this->TitleActor->GetPositionCoordinate()->SetCoordinateSystemToViewport();
72 
73   this->PlotData = vtkPolyData::New();
74 
75   this->PlotMapper = vtkPolyDataMapper2D::New();
76   this->PlotMapper->SetInputData(this->PlotData);
77 
78   this->PlotActor = vtkActor2D::New();
79   this->PlotActor->SetMapper(this->PlotMapper);
80 
81   this->NumberOfLabels = 2;
82 
83   this->LabelTextProperty = vtkTextProperty::New();
84   this->LabelTextProperty->SetBold(1);
85   this->LabelTextProperty->SetItalic(1);
86   this->LabelTextProperty->SetShadow(1);
87   this->LabelTextProperty->SetFontFamilyToArial();
88 
89   this->TitleTextProperty = vtkTextProperty::New();
90   this->TitleTextProperty->ShallowCopy(this->LabelTextProperty);
91 
92   this->LabelFormat = new char[8];
93   snprintf(this->LabelFormat, 8, "%s", "%-#6.3g");
94 
95   this->LastPosition[0] = this->LastPosition[1] = this->LastPosition2[0] = this->LastPosition2[1] =
96     0;
97 }
98 
99 //------------------------------------------------------------------------------
~vtkParallelCoordinatesActor()100 vtkParallelCoordinatesActor::~vtkParallelCoordinatesActor()
101 {
102   this->TitleMapper->Delete();
103   this->TitleMapper = nullptr;
104   this->TitleActor->Delete();
105   this->TitleActor = nullptr;
106 
107   this->ConnectionHolder->Delete();
108   this->ConnectionHolder = nullptr;
109 
110   this->Initialize();
111 
112   this->PlotData->Delete();
113   this->PlotMapper->Delete();
114   this->PlotActor->Delete();
115 
116   delete[] this->Title;
117   this->Title = nullptr;
118 
119   delete[] this->LabelFormat;
120   this->LabelFormat = nullptr;
121 
122   this->SetLabelTextProperty(nullptr);
123   this->SetTitleTextProperty(nullptr);
124 }
125 
126 //------------------------------------------------------------------------------
127 // Free-up axes and related stuff
Initialize()128 void vtkParallelCoordinatesActor::Initialize()
129 {
130   if (this->Axes)
131   {
132     for (int i = 0; i < this->N; i++)
133     {
134       this->Axes[i]->Delete();
135     }
136     delete[] this->Axes;
137     this->Axes = nullptr;
138     delete[] this->Mins;
139     this->Mins = nullptr;
140     delete[] this->Maxs;
141     this->Maxs = nullptr;
142     delete[] this->Xs;
143     this->Xs = nullptr;
144   }
145   this->N = 0;
146 }
147 
148 //------------------------------------------------------------------------------
SetInputConnection(vtkAlgorithmOutput * ao)149 void vtkParallelCoordinatesActor::SetInputConnection(vtkAlgorithmOutput* ao)
150 {
151   this->ConnectionHolder->SetInputConnection(ao);
152 }
153 
154 //------------------------------------------------------------------------------
SetInputData(vtkDataObject * dobj)155 void vtkParallelCoordinatesActor::SetInputData(vtkDataObject* dobj)
156 {
157   vtkTrivialProducer* tp = vtkTrivialProducer::New();
158   tp->SetOutput(dobj);
159   this->SetInputConnection(tp->GetOutputPort());
160   tp->Delete();
161 }
162 
163 //------------------------------------------------------------------------------
GetInput()164 vtkDataObject* vtkParallelCoordinatesActor::GetInput()
165 {
166   return this->ConnectionHolder->GetInputDataObject(0, 0);
167 }
168 
169 //------------------------------------------------------------------------------
170 // Plot scalar data for each input dataset.
RenderOverlay(vtkViewport * viewport)171 int vtkParallelCoordinatesActor::RenderOverlay(vtkViewport* viewport)
172 {
173   int renderedSomething = 0;
174 
175   // Make sure input is up to date.
176   if (this->GetInput() == nullptr || this->N <= 0)
177   {
178     vtkErrorMacro(<< "Nothing to plot!");
179     return 0;
180   }
181 
182   if (this->Title != nullptr)
183   {
184     renderedSomething += this->TitleActor->RenderOverlay(viewport);
185   }
186 
187   this->PlotActor->SetProperty(this->GetProperty());
188   renderedSomething += this->PlotActor->RenderOverlay(viewport);
189 
190   for (int i = 0; i < this->N; i++)
191   {
192     renderedSomething += this->Axes[i]->RenderOverlay(viewport);
193   }
194 
195   return renderedSomething;
196 }
197 
198 //------------------------------------------------------------------------------
RenderOpaqueGeometry(vtkViewport * viewport)199 int vtkParallelCoordinatesActor::RenderOpaqueGeometry(vtkViewport* viewport)
200 {
201   int renderedSomething = 0;
202 
203   // Initialize
204 
205   vtkDebugMacro(<< "Plotting parallel coordinates");
206 
207   // Make sure input is up to date, and that the data is the correct shape to
208   // plot.
209 
210   if (!this->GetInput())
211   {
212     vtkErrorMacro(<< "Nothing to plot!");
213     return renderedSomething;
214   }
215 
216   if (!this->TitleTextProperty)
217   {
218     vtkErrorMacro(<< "Need title text property to render plot");
219     return renderedSomething;
220   }
221 
222   if (!this->LabelTextProperty)
223   {
224     vtkErrorMacro(<< "Need label text property to render plot");
225     return renderedSomething;
226   }
227 
228   // Viewport change may not require rebuild
229 
230   int positionsHaveChanged = 0;
231   if (viewport->GetMTime() > this->BuildTime ||
232     (viewport->GetVTKWindow() && viewport->GetVTKWindow()->GetMTime() > this->BuildTime))
233   {
234     int* lastPosition = this->PositionCoordinate->GetComputedViewportValue(viewport);
235     int* lastPosition2 = this->Position2Coordinate->GetComputedViewportValue(viewport);
236     if (lastPosition[0] != this->LastPosition[0] || lastPosition[1] != this->LastPosition[1] ||
237       lastPosition2[0] != this->LastPosition2[0] || lastPosition2[1] != this->LastPosition2[1])
238     {
239       this->LastPosition[0] = lastPosition[0];
240       this->LastPosition[1] = lastPosition[1];
241       this->LastPosition2[0] = lastPosition2[0];
242       this->LastPosition2[1] = lastPosition2[1];
243       positionsHaveChanged = 1;
244     }
245   }
246 
247   // Check modified time to see whether we have to rebuild.
248 
249   this->ConnectionHolder->GetInputAlgorithm()->Update();
250 
251   if (positionsHaveChanged || this->GetMTime() > this->BuildTime ||
252     this->GetInput()->GetMTime() > this->BuildTime ||
253     this->LabelTextProperty->GetMTime() > this->BuildTime ||
254     this->TitleTextProperty->GetMTime() > this->BuildTime)
255   {
256     const int* size = viewport->GetSize();
257     int stringSize[2];
258 
259     vtkDebugMacro(<< "Rebuilding plot");
260 
261     // Build axes
262 
263     if (!this->PlaceAxes(viewport, size))
264     {
265       return renderedSomething;
266     }
267 
268     // Build title
269 
270     this->TitleMapper->SetInput(this->Title);
271 
272     if (this->TitleTextProperty->GetMTime() > this->BuildTime)
273     {
274       // Shallow copy here since the justification is changed but we still
275       // want to allow actors to share the same text property, and in that case
276       // specifically allow the title and label text prop to be the same.
277       this->TitleMapper->GetTextProperty()->ShallowCopy(this->TitleTextProperty);
278       this->TitleMapper->GetTextProperty()->SetJustificationToCentered();
279     }
280 
281     // We could do some caching here, but hey, that's just the title
282     vtkTextMapper::SetRelativeFontSize(this->TitleMapper, viewport, size, stringSize, 0.015);
283 
284     this->TitleActor->GetPositionCoordinate()->SetValue(
285       (this->Xs[0] + this->Xs[this->N - 1]) / 2.0, this->YMax + stringSize[1] / 2.0);
286     this->TitleActor->SetProperty(this->GetProperty());
287 
288     this->BuildTime.Modified();
289 
290   } // If need to rebuild the plot
291 
292   if (this->Title != nullptr)
293   {
294     renderedSomething += this->TitleActor->RenderOpaqueGeometry(viewport);
295   }
296 
297   this->PlotActor->SetProperty(this->GetProperty());
298   renderedSomething += this->PlotActor->RenderOpaqueGeometry(viewport);
299 
300   for (int i = 0; i < this->N; i++)
301   {
302     renderedSomething += this->Axes[i]->RenderOpaqueGeometry(viewport);
303   }
304 
305   return renderedSomething;
306 }
307 
308 //------------------------------------------------------------------------------
309 // Description:
310 // Does this prop have some translucent polygonal geometry?
HasTranslucentPolygonalGeometry()311 vtkTypeBool vtkParallelCoordinatesActor::HasTranslucentPolygonalGeometry()
312 {
313   return 0;
314 }
315 
316 //------------------------------------------------------------------------------
vtkParallelCoordinatesActorGetComponent(vtkFieldData * field,vtkIdType tuple,int component,double * val)317 static inline int vtkParallelCoordinatesActorGetComponent(
318   vtkFieldData* field, vtkIdType tuple, int component, double* val)
319 {
320   int array_comp;
321   int array_index = field->GetArrayContainingComponent(component, array_comp);
322   if (array_index < 0)
323   {
324     return 0;
325   }
326   vtkDataArray* da = field->GetArray(array_index);
327   if (!da)
328   {
329     // non-numeric array.
330     return 0;
331   }
332   *val = da->GetComponent(tuple, array_comp);
333   return 1;
334 }
335 
336 //------------------------------------------------------------------------------
PlaceAxes(vtkViewport * viewport,const int * vtkNotUsed (size))337 int vtkParallelCoordinatesActor::PlaceAxes(vtkViewport* viewport, const int* vtkNotUsed(size))
338 {
339   vtkIdType i, j, k, ptId;
340   vtkDataObject* input = this->GetInput();
341   vtkFieldData* field = input->GetFieldData();
342   double v = 0.0;
343 
344   this->Initialize();
345 
346   if (!field)
347   {
348     return 0;
349   }
350 
351   // Determine the shape of the field
352   int numComponents =
353     field->GetNumberOfComponents(); // number of components
354                                     // Note: numComponents also includes the non-numeric arrays.
355 
356   int numColumns = 0;             // number of "columns" -- includes only numeric arrays.
357   vtkIdType numRows = VTK_ID_MAX; // figure out number of rows
358   vtkIdType numTuples;
359   vtkDataArray* array;
360   for (i = 0; i < field->GetNumberOfArrays(); i++)
361   {
362     array = field->GetArray(i);
363     if (!array)
364     {
365       // skip over non-numeric arrays.
366       continue;
367     }
368     numColumns += array->GetNumberOfComponents();
369     numTuples = array->GetNumberOfTuples();
370     if (numTuples < numRows)
371     {
372       numRows = numTuples;
373     }
374   }
375 
376   // Determine the number of independent variables
377   if (this->IndependentVariables == VTK_IV_COLUMN)
378   {
379     this->N = numColumns;
380   }
381   else // row
382   {
383     this->N = numRows;
384   }
385 
386   if (this->N <= 0 || this->N >= VTK_ID_MAX)
387   {
388     this->N = 0;
389     vtkErrorMacro(<< "No field data to plot");
390     return 0;
391   }
392 
393   // We need to loop over the field to determine the range of
394   // each independent variable.
395   this->Mins = new double[this->N];
396   this->Maxs = new double[this->N];
397   for (i = 0; i < this->N; i++)
398   {
399     this->Mins[i] = VTK_DOUBLE_MAX;
400     this->Maxs[i] = -VTK_DOUBLE_MAX;
401   }
402 
403   if (this->IndependentVariables == VTK_IV_COLUMN)
404   {
405     k = 0;
406     for (j = 0; j < numComponents; j++)
407     {
408       int array_comp, array_index;
409       array_index = field->GetArrayContainingComponent(j, array_comp);
410       if (array_index < 0 || !field->GetArray(array_index))
411       {
412         // non-numeric component, simply skip it.
413         continue;
414       }
415       for (i = 0; i < numRows; i++)
416       {
417         // v = field->GetComponent(i,j);
418         ::vtkParallelCoordinatesActorGetComponent(field, i, j, &v);
419         if (v < this->Mins[k])
420         {
421           this->Mins[k] = v;
422         }
423         if (v > this->Maxs[k])
424         {
425           this->Maxs[k] = v;
426         }
427       }
428       k++;
429     }
430   }
431   else // row
432   {
433     for (j = 0; j < numRows; j++)
434     {
435       for (i = 0; i < numComponents; i++)
436       {
437         // v = field->GetComponent(j,i);
438         if (::vtkParallelCoordinatesActorGetComponent(field, j, i, &v) == 0)
439         {
440           // non-numeric component, simply skip.
441           continue;
442         }
443         if (v < this->Mins[j])
444         {
445           this->Mins[j] = v;
446         }
447         if (v > this->Maxs[j])
448         {
449           this->Maxs[j] = v;
450         }
451       }
452     }
453   }
454 
455   // Allocate space and create axes
456 
457   // TODO: this should be optimized, maybe by keeping a list of allocated
458   // objects, in order to avoid creation/destruction of axis actors
459   // and their underlying text properties (i.e. each time an axis is
460   // created, text properties are created and shallow-assigned a
461   // font size which value might be "far" from the target font size).
462 
463   this->Axes = new vtkAxisActor2D*[this->N];
464   for (i = 0; i < this->N; i++)
465   {
466     this->Axes[i] = vtkAxisActor2D::New();
467     this->Axes[i]->GetPositionCoordinate()->SetCoordinateSystemToViewport();
468     this->Axes[i]->GetPosition2Coordinate()->SetCoordinateSystemToViewport();
469     this->Axes[i]->SetRange(this->Mins[i], this->Maxs[i]);
470     this->Axes[i]->AdjustLabelsOff();
471     this->Axes[i]->SetNumberOfLabels(this->NumberOfLabels);
472     this->Axes[i]->SetLabelFormat(this->LabelFormat);
473     this->Axes[i]->SetProperty(this->GetProperty());
474     // We do not need shallow copy here since we do not modify any attributes
475     // in that class and we know that vtkAxisActor2D use ShallowCopy internally
476     // so that the size of the text prop is not affected by the automatic
477     // adjustment of its text mapper's size.
478     this->Axes[i]->SetLabelTextProperty(this->LabelTextProperty);
479   }
480   this->Xs = new int[this->N];
481 
482   // Get the location of the corners of the box
483   int* p1 = this->PositionCoordinate->GetComputedViewportValue(viewport);
484   int* p2 = this->Position2Coordinate->GetComputedViewportValue(viewport);
485 
486   // Specify the positions for the axes
487   this->YMin = p1[1];
488   this->YMax = p2[1];
489   for (i = 0; i < this->N; i++)
490   {
491     this->Xs[i] = static_cast<int>(p1[0] + i / static_cast<double>(this->N) * (p2[0] - p1[0]));
492     this->Axes[i]->GetPositionCoordinate()->SetValue(this->Xs[i], this->YMin);
493     this->Axes[i]->GetPosition2Coordinate()->SetValue(this->Xs[i], this->YMax);
494   }
495 
496   // Now generate the lines to plot
497   this->PlotData->Initialize(); // remove old polydata, if any
498   vtkPoints* pts = vtkPoints::New();
499   pts->Allocate(numRows * numColumns);
500   vtkCellArray* lines = vtkCellArray::New();
501   this->PlotData->SetPoints(pts);
502   this->PlotData->SetLines(lines);
503 
504   double x[3];
505   x[2] = 0.0;
506   if (this->IndependentVariables == VTK_IV_COLUMN)
507   {
508     lines->AllocateEstimate(numRows, numColumns);
509     for (j = 0; j < numRows; j++)
510     {
511       lines->InsertNextCell(numColumns);
512       for (i = 0, k = 0; i < numColumns && k < numComponents; k++)
513       {
514         // v = field->GetComponent(j,i);
515         if (::vtkParallelCoordinatesActorGetComponent(field, j, k, &v) == 0)
516         {
517           // skip non-numeric components.
518           continue;
519         }
520         x[0] = this->Xs[i];
521         if ((this->Maxs[i] - this->Mins[i]) == 0.0)
522         {
523           x[1] = 0.5 * (this->YMax - this->YMin);
524         }
525         else
526         {
527           x[1] = this->YMin +
528             ((v - this->Mins[i]) / (this->Maxs[i] - this->Mins[i])) * (this->YMax - this->YMin);
529         }
530         ptId = pts->InsertNextPoint(x);
531         lines->InsertCellPoint(ptId);
532         i++;
533       }
534     }
535   }
536   else // row
537   {
538     lines->AllocateEstimate(numColumns, numRows);
539     for (j = 0; j < numComponents; j++)
540     {
541       int array_comp;
542       int array_index = field->GetArrayContainingComponent(j, array_comp);
543       if (!field->GetArray(array_index))
544       {
545         // non-numeric component, skip it.
546         continue;
547       }
548       lines->InsertNextCell(numColumns);
549       for (i = 0; i < numRows; i++)
550       {
551         x[0] = this->Xs[i];
552         // v = field->GetComponent(i,j);
553         vtkParallelCoordinatesActorGetComponent(field, i, j, &v);
554         if ((this->Maxs[i] - this->Mins[i]) == 0.0)
555         {
556           x[1] = 0.5 * (this->YMax - this->YMin);
557         }
558         else
559         {
560           x[1] = this->YMin +
561             ((v - this->Mins[i]) / (this->Maxs[i] - this->Mins[i])) * (this->YMax - this->YMin);
562         }
563         ptId = pts->InsertNextPoint(x);
564         lines->InsertCellPoint(ptId);
565       }
566     }
567   }
568 
569   pts->Delete();
570   lines->Delete();
571 
572   return 1;
573 }
574 
575 //------------------------------------------------------------------------------
576 // Release any graphics resources that are being consumed by this actor.
577 // The parameter window could be used to determine which graphic
578 // resources to release.
ReleaseGraphicsResources(vtkWindow * win)579 void vtkParallelCoordinatesActor::ReleaseGraphicsResources(vtkWindow* win)
580 {
581   this->TitleActor->ReleaseGraphicsResources(win);
582   for (int i = 0; this->Axes && i < this->N; i++)
583   {
584     this->Axes[i]->ReleaseGraphicsResources(win);
585   }
586 }
587 
588 //------------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)589 void vtkParallelCoordinatesActor::PrintSelf(ostream& os, vtkIndent indent)
590 {
591   this->Superclass::PrintSelf(os, indent);
592 
593   if (this->TitleTextProperty)
594   {
595     os << indent << "Title Text Property:\n";
596     this->TitleTextProperty->PrintSelf(os, indent.GetNextIndent());
597   }
598   else
599   {
600     os << indent << "Title Text Property: (none)\n";
601   }
602 
603   if (this->LabelTextProperty)
604   {
605     os << indent << "Label Text Property:\n";
606     this->LabelTextProperty->PrintSelf(os, indent.GetNextIndent());
607   }
608   else
609   {
610     os << indent << "Label Text Property: (none)\n";
611   }
612 
613   os << indent << "Position2 Coordinate: " << this->Position2Coordinate << "\n";
614   this->Position2Coordinate->PrintSelf(os, indent.GetNextIndent());
615 
616   os << indent << "Title: " << (this->Title ? this->Title : "(none)") << "\n";
617   os << indent << "Number Of Independent Variables: " << this->N << "\n";
618   os << indent << "Independent Variables: ";
619   if (this->IndependentVariables == VTK_IV_COLUMN)
620   {
621     os << "Columns\n";
622   }
623   else
624   {
625     os << "Rows\n";
626   }
627 
628   os << indent << "Number Of Labels: " << this->NumberOfLabels << "\n";
629 
630   os << indent << "Label Format: " << this->LabelFormat << "\n";
631 }
632