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