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