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