vtkXYChartRepresentationInternals.h
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: Copyright (c) Kitware Inc.
2 // SPDX-License-Identifier: BSD-3-Clause
9 #ifndef vtkXYChartRepresentationInternals_h
10 #define vtkXYChartRepresentationInternals_h
11 
12 #include "vtkCSVExporter.h" // for vtkCSVExporter
13 #include "vtkChartXY.h" // for vtkChartXY
14 #include "vtkDataArray.h" // for vtkDataArray
15 #include "vtkIdTypeArray.h" // for vtkIdTypeArray
16 #include "vtkNew.h" // for vtkNew
17 #include "vtkPen.h" // for vtkPen enums
18 #include "vtkPlotBar.h" // for vtkPlotBar
19 #include "vtkPlotFunctionalBag.h" // for vtkPlotFunctionalBag
20 #include "vtkPlotPoints.h" // for vtkPlotPoints
21 #include "vtkSmartPointer.h" // for vtkSmartPointer
22 #include "vtkTable.h" // for vtkTable
23 #include "vtkXYChartRepresentation.h" // for vtkXYChartRepresentation
24 
25 #include <map> // for std::map
26 #include <string> // for std::string
27 
29 {
30 protected:
31  struct TableInfo
32  {
33  std::string TableName;
34  std::string ColumnName;
35  std::string Role;
36  unsigned int Block;
37  };
38 
39  struct PlotInfo
40  {
42  std::string TableName;
43  std::string ColumnName;
44  };
45 
46  typedef std::map<std::string, PlotInfo> PlotsMapItem;
47  class PlotsMap : public std::map<std::string, PlotsMapItem>
48  {
49  bool Contains(const std::string& key, const std::string& role) const
50  {
51  PlotsMap::const_iterator iter1 = this->find(key);
52  if (iter1 != this->end())
53  {
54  PlotsMapItem::const_iterator iter2 = iter1->second.find(role);
55  return (iter2 != iter1->second.end());
56  }
57  return false;
58  }
59 
60  public:
61  vtkSmartPointer<vtkPlot> GetPlot(vtkChartRepresentation* self, const std::string& tableName,
62  const std::string& columnName, const std::string& role = std::string()) const
63  {
64  const std::string key = self->GetDefaultSeriesLabel(tableName, columnName);
65  PlotsMap::const_iterator iter1 = this->find(key);
66  if (iter1 != this->end())
67  {
68  PlotsMapItem::const_iterator iter2 = iter1->second.find(role);
69  if (iter2 != iter1->second.end())
70  {
71  return iter2->second.Plot;
72  }
73  }
74  return vtkSmartPointer<vtkPlot>();
75  }
76 
77  bool RemovePlot(vtkChartRepresentation* self, const std::string& tableName,
78  const std::string& columnName, const std::string& role = std::string())
79  {
80  const std::string key = self->GetDefaultSeriesLabel(tableName, columnName);
81  PlotsMap::iterator iter1 = this->find(key);
82  if (iter1 != this->end())
83  {
84  PlotsMapItem::iterator iter2 = iter1->second.find(role);
85  if (iter2 != iter1->second.end())
86  {
87  iter1->second.erase(iter2);
88  return true;
89  }
90  }
91  return false;
92  }
93 
94  void AddPlot(vtkChartRepresentation* self, const std::string& tableName,
95  const std::string& columnName, const std::string& role, vtkPlot* plot)
96  {
97  const std::string key = self->GetDefaultSeriesLabel(tableName, columnName);
98  PlotInfo& info = (*this)[key][role];
99  info.TableName = tableName;
100  info.ColumnName = columnName;
101  info.Plot = plot;
102  }
103 
104  void SetPlotVisibility(bool val) const
105  {
106  for (PlotsMap::const_iterator iter1 = this->begin(); iter1 != this->end(); ++iter1)
107  {
108  for (PlotsMapItem::const_iterator iter2 = iter1->second.begin();
109  iter2 != iter1->second.end(); ++iter2)
110  {
111  iter2->second.Plot->SetVisible(val);
112  }
113  }
114  }
115 
116  void RemoveAllPlots(vtkChartXY* chartXY)
117  {
118  for (PlotsMap::const_iterator iter1 = this->begin(); iter1 != this->end(); ++iter1)
119  {
120  for (PlotsMapItem::const_iterator iter2 = iter1->second.begin();
121  iter2 != iter1->second.end(); ++iter2)
122  {
123  chartXY->RemovePlotInstance(iter2->second.Plot.GetPointer());
124  }
125  }
126  this->clear();
127  }
128 
129  void Intersect(const PlotsMap& other, vtkChartXY* chartXY)
130  {
131  for (PlotsMap::iterator iter1 = this->begin(); iter1 != this->end(); ++iter1)
132  {
133  for (PlotsMapItem::iterator iter2 = iter1->second.begin(); iter2 != iter1->second.end();)
134  {
135  if (other.Contains(iter1->first, iter2->first) == false)
136  {
137  chartXY->RemovePlotInstance(iter2->second.Plot.GetPointer());
138  PlotsMapItem::iterator iter2old = iter2;
139  ++iter2;
140  iter1->second.erase(iter2old);
141  }
142  else
143  {
144  ++iter2;
145  }
146  }
147  }
148  }
149  };
150 
152 
153  //---------------------------------------------------------------------------
162  template <class T>
163  T GetSeriesParameter(vtkXYChartRepresentation* self, const std::string& tableName,
164  const std::string& columnName, const std::string& vtkNotUsed(role),
165  const std::map<std::string, T>& parameter_map, const T default_value = T()) const
166  {
167  typename std::map<std::string, T>::const_iterator iter;
168 
169  // when setting properties for a series, I want to support two mechanisms:
170  // simply specifying the array name or suffixing it with the block-name.
171  // This logic makes that possible.
172 
173  // first try most specific form of identifying the series.
174  std::string key = self->GetDefaultSeriesLabel(tableName, columnName);
175  iter = parameter_map.find(key);
176  if (iter != parameter_map.end())
177  {
178  return iter->second;
179  }
180 
181  // now try the cheap form for identifying it.
182  key = self->GetDefaultSeriesLabel(std::string(), columnName);
183  iter = parameter_map.find(key);
184  if (iter != parameter_map.end())
185  {
186  return iter->second;
187  }
188  return default_value;
189  }
190 
191 public:
193 
194  // we have to keep these separate since they are set by different properties
195  // and hence may not always match up.
196  std::map<std::string, bool> SeriesVisibilities;
197  std::map<std::string, int> SeriesOrder;
198  std::map<std::string, int> LineThicknesses;
199  std::map<std::string, int> LineStyles;
200  std::map<std::string, vtkColor3d> Colors;
201  std::map<std::string, double> Opacities;
202  std::map<std::string, int> AxisCorners;
203  std::map<std::string, int> MarkerStyles;
204  std::map<std::string, double> MarkerSizes;
205  std::map<std::string, std::string> Labels;
206  std::map<std::string, bool> UseColorMapping;
207  std::map<std::string, vtkScalarsToColors*> Lut;
208 
209  // These are used to determine when to recalculate chart bounds. If user
210  // changes the X axis, we force recalculation of the chart bounds
211  // automatically.
214 
216  : PreviousUseIndexForXAxis(false)
217  {
218  }
219 
220  virtual ~vtkInternals() = default;
221 
222  //---------------------------------------------------------------------------
226  void HideAllPlots() { this->SeriesPlots.SetPlotVisibility(false); }
227 
228  //---------------------------------------------------------------------------
232  void RemoveAllPlots(vtkChartXY* chartXY) { this->SeriesPlots.RemoveAllPlots(chartXY); }
233 
234  //---------------------------------------------------------------------------
240  virtual std::vector<std::string> GetSeriesRoles(
241  const std::string& vtkNotUsed(tableName), const std::string& vtkNotUsed(columnName))
242  {
243  return { std::string() };
244  }
245 
246  //---------------------------------------------------------------------------
250  virtual vtkPlot* NewPlot(vtkXYChartRepresentation* self, const std::string& tableName,
251  const std::string& columnName, const std::string& role, unsigned int block = 0)
252  {
253  (void)tableName;
254  (void)columnName;
255  (void)role;
256 
257  assert(self);
258  vtkChartXY* chartXY = self->GetChart();
259 
260  assert(chartXY);
261  return chartXY->AddPlot(self->GetChartType(), block);
262  }
263 
264  //---------------------------------------------------------------------------
265  virtual int GetInputArrayIndex(const std::string& vtkNotUsed(tableName),
266  const std::string& vtkNotUsed(columnName), const std::string& vtkNotUsed(role))
267  {
268  return 1;
269  }
270 
271  //---------------------------------------------------------------------------
275  virtual void UpdatePlots(vtkXYChartRepresentation* self, const MapOfTables& tables)
276  {
277  PlotsMap newPlots;
278  assert(self != nullptr);
279  vtkChartXY* chartXY = self->GetChart();
280  this->RemoveAllPlots(chartXY);
281  std::multimap<int, std::pair<vtkTable*, TableInfo>> orderMap;
282  for (MapOfTables::const_iterator tablesIter = tables.begin(); tablesIter != tables.end();
283  ++tablesIter)
284  {
285  const std::string& tableName = tablesIter->first;
286  vtkTable* table = tablesIter->second.first.GetPointer();
287  vtkIdType numCols = table->GetNumberOfColumns();
288  for (vtkIdType cc = 0; cc < numCols; ++cc)
289  {
290  std::string columnName = table->GetColumnName(cc);
291  auto roles = this->GetSeriesRoles(tableName, columnName);
292 
293  for (const auto& role : roles)
294  {
295  // recover the order of the current table column
296  const int order =
297  this->GetSeriesParameter(self, tableName, columnName, role, this->SeriesOrder, -1);
298 
299  // store all info in a (sorted) map
300  TableInfo info;
301  info.TableName = tableName;
302  info.ColumnName = columnName;
303  info.Role = role;
304  info.Block = tablesIter->second.second;
305  orderMap.insert(std::make_pair(order, std::make_pair(tablesIter->second.first, info)));
306  }
307  }
308  }
309 
310  // tables with an index column, used when the index is plotted along the
311  // vertical axis.
312  std::map<vtkTable*, vtkSmartPointer<vtkTable>> indexedTables;
313 
314  // for each ordered column
315  for (auto it = orderMap.begin(); it != orderMap.end(); ++it)
316  {
317  vtkTable* table = it->second.first;
318  const TableInfo& info = it->second.second;
319 
321  this->SeriesPlots.GetPlot(self, info.TableName, info.ColumnName, info.Role);
322  if (!plot)
323  {
324  // Create the plot in the right order
325  plot = this->NewPlot(self, info.TableName, info.ColumnName, info.Role, info.Block);
326  if (!plot)
327  {
328  continue;
329  }
330  }
331  const int arrayIndex = this->GetInputArrayIndex(info.TableName, info.ColumnName, info.Role);
332  if (self->GetSwapXYAxes() && arrayIndex == 1 && vtkPlotPoints::SafeDownCast(plot))
333  {
334  // Plot the series along the horizontal axis and the X array (or the
335  // index) along the vertical one. Since plots only support using the
336  // index for the X series, add the index as a column instead.
337  const bool useIndex = self->GetUseIndexForXAxis();
338  if (useIndex)
339  {
340  auto& indexedTable = indexedTables[table];
341  if (!indexedTable)
342  {
343  indexedTable = AddIndexColumn(table);
344  }
345  table = indexedTable;
346  }
347  plot->SetInputData(table);
348  plot->SetUseIndexForXSeries(false);
349  plot->SetInputArray(0, info.ColumnName);
350  plot->SetInputArray(1, useIndex ? IndexColumnName() : self->GetXAxisSeriesName());
351  }
352  else
353  {
354  plot->SetInputData(table);
355  plot->SetUseIndexForXSeries(self->GetUseIndexForXAxis());
356  plot->SetInputArray(0, self->GetXAxisSeriesName());
357  plot->SetInputArray(arrayIndex, info.ColumnName);
358  }
359  this->SeriesPlots.AddPlot(self, info.TableName, info.ColumnName, info.Role, plot);
360  newPlots.AddPlot(self, info.TableName, info.ColumnName, info.Role, plot);
361  }
362 
363  // Remove any plots in this->SeriesPlots that are not in newPlots.
364  this->SeriesPlots.Intersect(newPlots, chartXY);
365  }
366 
367  //---------------------------------------------------------------------------
372  {
373  vtkChartXY* chartXY = self->GetChart();
374  vtkPlot* lastFunctionalBagPlot = nullptr;
375  for (PlotsMap::iterator iter1 = this->SeriesPlots.begin(); iter1 != this->SeriesPlots.end();
376  ++iter1)
377  {
378  for (PlotsMapItem::const_iterator iter2 = iter1->second.begin(); iter2 != iter1->second.end();
379  ++iter2)
380  {
381  const PlotInfo& plotInfo = iter2->second;
382  const std::string& tableName = plotInfo.TableName;
383  const std::string& columnName = plotInfo.ColumnName;
384  vtkPlot* plot = plotInfo.Plot;
385  const std::string& role = iter2->first;
386 
387  if (this->UpdateSinglePlotProperties(self, tableName, columnName, role, plot))
388  {
389  // Functional bag plots shall be stacked under the other plots.
391  if (plotBag)
392  {
393  // We can't select the median line as it may not exist in other dataset.
394  if (columnName == "QMedianLine")
395  {
396  plotBag->SelectableOff();
397  }
398  if (plotBag->IsBag())
399  {
400  if (!lastFunctionalBagPlot)
401  {
402  chartXY->LowerPlot(plotBag);
403  }
404  else
405  {
406  chartXY->StackPlotAbove(plotBag, lastFunctionalBagPlot);
407  }
408  lastFunctionalBagPlot = plotBag;
409  }
410  }
411  }
412  }
413  }
414  }
415 
416  //---------------------------------------------------------------------------
421  {
422  for (PlotsMap::iterator iter1 = this->SeriesPlots.begin(); iter1 != this->SeriesPlots.end();
423  ++iter1)
424  {
425  for (PlotsMapItem::const_iterator iter2 = iter1->second.begin(); iter2 != iter1->second.end();
426  ++iter2)
427  {
428  const PlotInfo& plotInfo = iter2->second;
429  vtkPlot* plot = plotInfo.Plot;
430  if (!plot->GetVisible())
431  {
432  continue;
433  }
434  const std::string& columnName = plotInfo.ColumnName;
435  vtkTable* table = plot->GetInput();
436  vtkDataArray* xarray = self->GetUseIndexForXAxis()
437  ? nullptr
438  : vtkDataArray::SafeDownCast(table->GetColumnByName(self->GetXAxisSeriesName()));
439  vtkAbstractArray* yarray = table->GetColumnByName(columnName.c_str());
440  if (yarray != nullptr)
441  {
442  const std::string plotName = plot->GetLabel();
443  exporter->AddColumn(yarray, plotName.c_str(), xarray);
444  exporter->AddStyle(plot, plotName.c_str());
445  }
446  }
447  }
448 
449  vtkChartXY* chartXY = self->GetChart();
450  exporter->SetGlobalStyle(chartXY);
451  return true;
452  }
453 
454 protected:
455  //---------------------------------------------------------------------------
459  static const char* IndexColumnName() { return "vtkXYChartRepresentationIndex"; }
460 
461  //---------------------------------------------------------------------------
467  {
468  auto result = vtkSmartPointer<vtkTable>::New();
469  result->ShallowCopy(table);
470 
471  const vtkIdType numRows = table->GetNumberOfRows();
472  vtkNew<vtkIdTypeArray> indices;
473  indices->SetName(IndexColumnName());
474  indices->SetNumberOfTuples(numRows);
475  for (vtkIdType cc = 0; cc < numRows; ++cc)
476  {
477  indices->SetTypedComponent(cc, 0, cc);
478  }
479  result->AddColumn(indices);
480  return result;
481  }
482 
483  //---------------------------------------------------------------------------
488  const std::string& tableName, const std::string& columnName, const std::string& role,
489  vtkPlot* plot)
490  {
491  vtkChartXY* chartXY = self->GetChart();
492  const bool visible =
493  this->GetSeriesParameter(self, tableName, columnName, role, this->SeriesVisibilities, false);
494  plot->SetVisible(visible);
495  if (!visible)
496  {
497  return false;
498  }
499 
500  std::string label =
501  this->GetSeriesParameter(self, tableName, columnName, role, this->Labels, columnName);
502  if (self->GetSeriesLabelPrefix())
503  {
504  label = std::string(self->GetSeriesLabelPrefix()) + label;
505  }
506  if (self->GetArraySelectionMode() == ArraySelectionModes::INDIVIDUAL_BLOCKS &&
507  self->GetShowBlockName())
508  {
509  label = label + " (" + tableName + ")";
510  }
511  plot->SetLabel(label);
512 
514  self, tableName, columnName, role, this->Colors, vtkColor3d(0, 0, 0));
515  double opacity =
516  this->GetSeriesParameter(self, tableName, columnName, role, this->Opacities, 1.0);
517  plot->SetColorF(color.GetRed(), color.GetGreen(), color.GetBlue(), opacity);
518  plot->GetSelectionPen()->SetColorF(self->SelectionColor);
519 
520  plot->SetWidth(
521  this->GetSeriesParameter(self, tableName, columnName, role, this->LineThicknesses, 2));
522  plot->GetPen()->SetLineType(this->GetSeriesParameter(
523  self, tableName, columnName, role, this->LineStyles, static_cast<int>(vtkPen::SOLID_LINE)));
524 
525  if (vtkPlotPoints* plotPoints = vtkPlotPoints::SafeDownCast(plot))
526  {
527  plotPoints->SetMarkerStyle(this->GetSeriesParameter(self, tableName, columnName, role,
528  this->MarkerStyles, static_cast<int>(vtkPlotPoints::NONE)));
529  plotPoints->SetMarkerSize(
530  this->GetSeriesParameter(self, tableName, columnName, role, this->MarkerSizes, 1.0));
531  // the vtkValidPointMask array is used by some filters (like plot
532  // over line) to indicate invalid points. this instructs the line
533  // plot to not render those points
534  plotPoints->SetValidPointMaskName("vtkValidPointMask");
535  }
536 
537  chartXY->SetPlotCorner(
538  plot, this->GetSeriesParameter(self, tableName, columnName, role, this->AxisCorners, 0));
539 
540  // for now only vtkPlotBar has color mapping
541  vtkPlotBar* plotBar = vtkPlotBar::SafeDownCast(plot);
542  if (plotBar && columnName == "bin_values")
543  {
544  bool colorMapping =
545  this->GetSeriesParameter(self, tableName, columnName, role, this->UseColorMapping, false);
546  plotBar->SetScalarVisibility(colorMapping);
547  plotBar->SelectColorArray("bin_extents");
549  self, tableName, columnName, role, this->Lut, static_cast<vtkScalarsToColors*>(nullptr));
550  if (lut)
551  {
552  plotBar->SetLookupTable(lut);
553  }
554  }
555  return true;
556  }
557 };
558 
559 #endif
560 // VTK-HeaderTest-Exclude: vtkXYChartRepresentationInternals.h
color
virtual void SetGlobalStyle(vtkChart *chart)=0
Set Global style elements like graph and axis titles.
std::map< std::string, vtkScalarsToColors * > Lut
order
virtual bool Export(vtkXYChartRepresentation *self, vtkAbstractChartExporter *exporter)
Export visible plots to a CSV file.
bool RemovePlot(vtkChartRepresentation *self, const std::string &tableName, const std::string &columnName, const std::string &role=std::string())
static vtkDataArray * SafeDownCast(vtkObjectBase *o)
T GetSeriesParameter(vtkXYChartRepresentation *self, const std::string &tableName, const std::string &columnName, const std::string &vtkNotUsed(role), const std::map< std::string, T > &parameter_map, const T default_value=T()) const
Makes is easy to obtain a value for a series parameter, is set, else the default. ...
virtual void SetWidth(float width)
const double & GetBlue() const
static vtkSmartPointer< vtkTable > AddIndexColumn(vtkTable *table)
Returns a shallow copy of the table with an additional column, named IndexColumnName(), holding the row indices.
vtkXYChartRepresentation is representation that is used to add vtkPlot subclasses to a vtkChartXY ins...
void AddPlot(vtkChartRepresentation *self, const std::string &tableName, const std::string &columnName, const std::string &role, vtkPlot *plot)
info
vtkXYChartRepresentation::MapOfTables MapOfTables
static vtkPlotFunctionalBag * SafeDownCast(vtkObjectBase *o)
int vtkIdType
virtual std::vector< std::string > GetSeriesRoles(const std::string &vtkNotUsed(tableName), const std::string &vtkNotUsed(columnName))
Subclasses can override this method to assign a role for a specific data array in the input dataset...
void SetPlotCorner(vtkPlot *plot, int corner)
static vtkSmartPointer< T > New()
virtual int GetInputArrayIndex(const std::string &vtkNotUsed(tableName), const std::string &vtkNotUsed(columnName), const std::string &vtkNotUsed(role))
const double & GetRed() const
virtual bool UpdateSinglePlotProperties(vtkXYChartRepresentation *self, const std::string &tableName, const std::string &columnName, const std::string &role, vtkPlot *plot)
Returns false for in-visible plots.
vtkPen * GetPen()
virtual void SetName(const char *)
vtkSmartPointer< vtkPlot > GetPlot(vtkChartRepresentation *self, const std::string &tableName, const std::string &columnName, const std::string &role=std::string()) const
virtual void SetLookupTable(vtkScalarsToColors *lut)
static const char * IndexColumnName()
Name of the index column added by AddIndexColumn().
std::map< std::string, std::pair< vtkSmartPointer< vtkTable >, unsigned int > > MapOfTables
vtkAbstractArray * GetColumnByName(const char *name)
virtual void AddStyle(vtkPlot *plot, const char *plotName)=0
Attach information about the style of the plot (color,line type, marker type etc. ...
virtual vtkPlot * AddPlot(int type)
void RemoveAllPlots(vtkChartXY *chartXY)
Destroy all vtkPlot instances.
static vtkPlotPoints * SafeDownCast(vtkObjectBase *o)
const char * GetColumnName(vtkIdType col)
virtual bool RemovePlotInstance(vtkPlot *plot)
virtual void UpdatePlotProperties(vtkXYChartRepresentation *self)
Update properties for plots in the chart.
virtual bool IsBag()
virtual vtkTable * GetInput()
virtual void SetInputArray(int index, const vtkStdString &name)
vtkIdType LowerPlot(vtkPlot *plot)
virtual void SetScalarVisibility(bool)
virtual void SetInputData(vtkTable *table)
virtual vtkIdType StackPlotAbove(vtkPlot *plot, vtkPlot *under)
virtual vtkStdString GetLabel()
virtual void UpdatePlots(vtkXYChartRepresentation *self, const MapOfTables &tables)
Update i.e.
void SelectColorArray(vtkIdType arrayNum)
vtkChartRepresentation is the base representation for charting representations.
void SetColorF(double color[3])
virtual void SetUseIndexForXSeries(bool)
static vtkPlotBar * SafeDownCast(vtkObjectBase *o)
vtkPen * GetSelectionPen()
virtual void SetNumberOfTuples(vtkIdType numTuples)=0
vtkIdType GetNumberOfColumns()
virtual void SetVisible(bool)
virtual void SetLabel(const vtkStdString &label)
virtual vtkPlot * NewPlot(vtkXYChartRepresentation *self, const std::string &tableName, const std::string &columnName, const std::string &role, unsigned int block=0)
Add new plot.
void SetLineType(int type)
exporter used by certain views to export data into a file or stream.
virtual bool GetVisible()
vtkIdType GetNumberOfRows()
void Intersect(const PlotsMap &other, vtkChartXY *chartXY)
const double & GetGreen() const
virtual void AddColumn(vtkAbstractArray *yarray, const char *yarrayname=nullptr, vtkDataArray *xarray=nullptr)=0
In STREAM_COLUMNS mode, use this method to add a column (yarray).
virtual void SelectableOff()
key