vtkScatterPlotMatrix.cxx 61.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkScatterPlotMatrix.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/

#include "vtkScatterPlotMatrix.h"

18
#include "vtkAnnotationLink.h"
19
#include "vtkAxis.h"
20 21 22 23 24 25
#include "vtkBrush.h"
#include "vtkCallbackCommand.h"
#include "vtkChartXY.h"
#include "vtkChartXYZ.h"
#include "vtkCommand.h"
#include "vtkContext2D.h"
26
#include "vtkContextMouseEvent.h"
27 28 29
#include "vtkContextScene.h"
#include "vtkFloatArray.h"
#include "vtkIntArray.h"
30
#include "vtkMathUtilities.h"
31
#include "vtkNew.h"
32
#include "vtkObjectFactory.h"
33 34
#include "vtkPen.h"
#include "vtkPlot.h"
35
#include "vtkPlotPoints.h"
Zack Galbreath's avatar
Zack Galbreath committed
36
#include "vtkPlotPoints3D.h"
37
#include "vtkRenderWindowInteractor.h"
38 39 40 41
#include "vtkStdString.h"
#include "vtkStringArray.h"
#include "vtkTable.h"
#include "vtkTextProperty.h"
42
#include "vtkTooltipItem.h"
43
#include "vtkVectorOperators.h"
44

45
// STL includes
46
#include <algorithm>
47
#include <cassert>
48
#include <map>
49
#include <vector>
50 51 52 53

class vtkScatterPlotMatrix::PIMPL
{
public:
54
  PIMPL() : VisibleColumnsModified(true), BigChart(nullptr), BigChartPos(0, 0),
55
    ResizingBigChart(false), AnimationCallbackInitialized(false), TimerId(0),
56
    TimerCallbackInitialized(false)
57
  {
58
    pimplChartSetting* scatterplotSettings = new pimplChartSetting();
59
    scatterplotSettings->BackgroundBrush->SetColor(255, 255, 255, 255);
60 61
    this->ChartSettings[vtkScatterPlotMatrix::SCATTERPLOT] =
        scatterplotSettings;
62
    pimplChartSetting* histogramSettings = new pimplChartSetting();
63 64
    histogramSettings->BackgroundBrush->SetColor(127, 127, 127, 102);
    histogramSettings->PlotPen->SetColor(255, 255, 255, 255);
65
    histogramSettings->ShowAxisLabels = true;
66
    this->ChartSettings[vtkScatterPlotMatrix::HISTOGRAM] = histogramSettings;
67
    pimplChartSetting* activeplotSettings = new pimplChartSetting();
68
    activeplotSettings->BackgroundBrush->SetColor(255, 255, 255, 255);
69
    activeplotSettings->ShowAxisLabels = true;
70 71
    this->ChartSettings[vtkScatterPlotMatrix::ACTIVEPLOT] = activeplotSettings;
    activeplotSettings->MarkerSize = 8.0;
72 73
    this->SelectedChartBGBrush->SetColor(0, 204, 0, 102);
    this->SelectedRowColumnBGBrush->SetColor(204, 0, 0, 102);
74
    this->TooltipItem = vtkSmartPointer<vtkTooltipItem>::New();
75 76 77 78
  }

  ~PIMPL()
  {
79 80 81
    delete this->ChartSettings[vtkScatterPlotMatrix::SCATTERPLOT];
    delete this->ChartSettings[vtkScatterPlotMatrix::HISTOGRAM];
    delete this->ChartSettings[vtkScatterPlotMatrix::ACTIVEPLOT];
82
  }
83

84 85 86 87 88 89 90 91 92 93 94 95 96 97
  // Store columns settings such as axis range, title, number of tick marks.
  class ColumnSetting
  {
  public:
    ColumnSetting() : min(0), max(0), nTicks(0), title("?!?")
    {
    }

    double min;
    double max;
    int    nTicks;
    std::string title;
  };

98 99 100 101 102
  class pimplChartSetting
  {
  public:
    pimplChartSetting()
    {
103
      this->PlotPen->SetColor(0, 0, 0, 255);
104
      this->MarkerStyle = vtkPlotPoints::CIRCLE;
105
      this->MarkerSize = 3.0;
106
      this->AxisColor.Set(0, 0, 0, 255);
107
      this->GridColor.Set(242, 242, 242, 255);
108 109 110 111 112
      this->LabelNotation = vtkAxis::STANDARD_NOTATION;
      this->LabelPrecision = 2;
      this->TooltipNotation = vtkAxis::STANDARD_NOTATION;
      this->TooltipPrecision = 2;
      this->ShowGrid = true;
113
      this->ShowAxisLabels = false;
114
      this->LabelFont = vtkSmartPointer<vtkTextProperty>::New();
115 116 117 118 119
      this->LabelFont->SetFontFamilyToArial();
      this->LabelFont->SetFontSize(12);
      this->LabelFont->SetColor(0.0, 0.0, 0.0);
      this->LabelFont->SetOpacity(1.0);
    }
120
    ~pimplChartSetting() = default;
121

122 123
    int MarkerStyle;
    float MarkerSize;
124 125
    vtkColor4ub AxisColor;
    vtkColor4ub GridColor;
126 127 128 129 130 131
    int LabelNotation;
    int LabelPrecision;
    int TooltipNotation;
    int TooltipPrecision;
    bool ShowGrid;
    bool ShowAxisLabels;
132
    vtkSmartPointer<vtkTextProperty> LabelFont;
133 134 135
    vtkNew<vtkBrush> BackgroundBrush;
    vtkNew<vtkPen> PlotPen;
    vtkNew<vtkBrush> PlotBrush;
136 137 138
  };

  void UpdateAxis(vtkAxis* axis, pimplChartSetting* setting,
139
                  bool updateLabel = true)
140
  {
141
    if(axis && setting)
142
    {
143 144
      axis->GetPen()->SetColor(setting->AxisColor);
      axis->GetGridPen()->SetColor(setting->GridColor);
145
      axis->SetGridVisible(setting->ShowGrid);
146
      if (updateLabel)
147
      {
148
        vtkTextProperty *prop = setting->LabelFont;
149 150 151
        axis->SetNotation(setting->LabelNotation);
        axis->SetPrecision(setting->LabelPrecision);
        axis->SetLabelsVisible(setting->ShowAxisLabels);
152 153 154
        axis->GetLabelProperties()->SetFontSize(prop->GetFontSize());
        axis->GetLabelProperties()->SetColor(prop->GetColor());
        axis->GetLabelProperties()->SetOpacity(prop->GetOpacity());
155
        axis->GetLabelProperties()->SetFontFamilyAsString(
156
          prop->GetFontFamilyAsString());
157 158
        axis->GetLabelProperties()->SetBold(prop->GetBold());
        axis->GetLabelProperties()->SetItalic(prop->GetItalic());
159 160
      }
    }
161
  }
162

163
  void UpdateChart(vtkChart* chart, pimplChartSetting* setting)
164
  {
165
    if(chart && setting)
166
    {
167 168
      vtkPlot *plot = chart->GetPlot(0);
      if (plot)
169
      {
170 171 172 173
        plot->SetTooltipNotation(setting->TooltipNotation);
        plot->SetTooltipPrecision(setting->TooltipPrecision);
      }
    }
174
  }
175

176
  vtkNew<vtkTable> Histogram;
177
  bool VisibleColumnsModified;
178
  vtkWeakPointer<vtkChart> BigChart;
179 180
  vtkVector2i BigChartPos;
  bool ResizingBigChart;
181
  vtkNew<vtkAnnotationLink> Link;
182

183
  // Settings for the charts in the scatter plot matrix.
184 185 186
  std::map<int, pimplChartSetting*> ChartSettings;
  typedef std::map<int, pimplChartSetting*>::iterator chartIterator;

187 188 189
  // Axis ranges for the columns in the scatter plot matrix.
  std::map<std::string, ColumnSetting> ColumnSettings;

190 191
  vtkNew<vtkBrush> SelectedRowColumnBGBrush;
  vtkNew<vtkBrush> SelectedChartBGBrush;
192 193 194 195 196 197 198
  std::vector<vtkVector2i>           AnimationPath;
  std::vector<vtkVector2i>::iterator AnimationIter;
  vtkRenderWindowInteractor* Interactor;
  vtkNew<vtkCallbackCommand> AnimationCallback;
  bool                       AnimationCallbackInitialized;
  unsigned long int          TimerId;
  bool                       TimerCallbackInitialized;
199 200 201 202 203 204 205
  int                        AnimationPhase;
  float                      CurrentAngle;
  float                      IncAngle;
  float                      FinalAngle;
  vtkVector2i                NextActivePlot;

  vtkNew<vtkChartXYZ> BigChart3D;
206
  vtkNew<vtkAxis>     TestAxis;   // Used to get ranges/numer of ticks
207 208
  vtkSmartPointer<vtkTooltipItem> TooltipItem;
  vtkSmartPointer<vtkStringArray> IndexedLabelsArray;
209 210
};

211 212 213 214
namespace
{

// This is just here for now - quick and dirty historgram calculations...
215 216
bool PopulateHistograms(vtkTable *input, vtkTable *output, vtkStringArray *s,
                        int NumberOfBins)
217 218 219
{
  // The output table will have the twice the number of columns, they will be
  // the x and y for input column. This is the bin centers, and the population.
220
  for (vtkIdType i = 0; i < s->GetNumberOfTuples(); ++i)
221
  {
222
    double minmax[2] = { 0.0, 0.0 };
223 224
    vtkStdString name(s->GetValue(i));
    vtkDataArray *in =
225
        vtkArrayDownCast<vtkDataArray>(input->GetColumnByName(name.c_str()));
226
    if (in)
227
    {
228 229 230
      // The bin values are the centers, extending +/- half an inc either side
      in->GetRange(minmax);
      if (minmax[0] == minmax[1])
231
      {
232
        minmax[1] = minmax[0] + 1.0;
233
      }
234
      double inc = (minmax[1] - minmax[0]) / (NumberOfBins) * 1.001;
235
      double halfInc = inc / 2.0;
236
      vtkSmartPointer<vtkFloatArray> extents =
237
          vtkArrayDownCast<vtkFloatArray>(
238 239
            output->GetColumnByName(vtkStdString(name + "_extents").c_str()));
      if (!extents)
240
      {
241 242
        extents = vtkSmartPointer<vtkFloatArray>::New();
        extents->SetName(vtkStdString(name + "_extents").c_str());
243
      }
244 245
      extents->SetNumberOfTuples(NumberOfBins);
      float *centers = static_cast<float *>(extents->GetVoidPointer(0));
246
      double min = minmax[0] - 0.0005 * inc + halfInc;
247
      for (int j = 0; j < NumberOfBins; ++j)
248
      {
249
        extents->SetValue(j, min + j * inc);
250
      }
251
      vtkSmartPointer<vtkIntArray> populations =
252
          vtkArrayDownCast<vtkIntArray>(
253 254
            output->GetColumnByName(vtkStdString(name + "_pops").c_str()));
      if (!populations)
255
      {
256 257
        populations = vtkSmartPointer<vtkIntArray>::New();
        populations->SetName(vtkStdString(name + "_pops").c_str());
258
      }
259 260 261
      populations->SetNumberOfTuples(NumberOfBins);
      int *pops = static_cast<int *>(populations->GetVoidPointer(0));
      for (int k = 0; k < NumberOfBins; ++k)
262
      {
263
        pops[k] = 0;
264
      }
265
      for (vtkIdType j = 0; j < in->GetNumberOfTuples(); ++j)
266
      {
267 268 269
        double v(0.0);
        in->GetTuple(j, &v);
        for (int k = 0; k < NumberOfBins; ++k)
270
        {
271
          if (vtkMathUtilities::FuzzyCompare(v, double(centers[k]), halfInc))
272
          {
273 274 275 276
            ++pops[k];
            break;
          }
        }
277
      }
278 279
      output->AddColumn(extents);
      output->AddColumn(populations);
280
    }
281
  }
282 283
  return true;
}
284 285 286 287 288

bool MoveColumn(vtkStringArray* visCols, int fromCol, int toCol)
{
  if(!visCols || visCols->GetNumberOfTuples() == 0
    || fromCol == toCol || fromCol == (toCol-1) || fromCol < 0 || toCol < 0)
289
  {
290
    return false;
291
  }
292 293
  int numCols = visCols->GetNumberOfTuples();
  if( fromCol >= numCols || toCol > numCols)
294
  {
295
    return false;
296
  }
297 298 299 300

  std::vector<vtkStdString> newVisCols;
  vtkIdType c;
  if(toCol == numCols)
301
  {
302
    for(c=0; c<numCols; c++)
303
    {
304
      if(c!=fromCol)
305
      {
306 307
        newVisCols.push_back(visCols->GetValue(c));
      }
308
    }
309 310
    // move the fromCol to the end
    newVisCols.push_back(visCols->GetValue(fromCol));
311
  }
312 313
  // insert the fromCol before toCol
  else if(fromCol < toCol)
314
  {
315 316
    // move Cols in the middle up
    for(c=0; c<fromCol; c++)
317
    {
318
      newVisCols.push_back(visCols->GetValue(c));
319
    }
320
    for(c=fromCol+1; c<numCols; c++)
321
    {
322
      if(c == toCol)
323
      {
324 325
        newVisCols.push_back(visCols->GetValue(fromCol));
      }
326
      newVisCols.push_back(visCols->GetValue(c));
327
    }
328
  }
329
  else
330
  {
331
    for(c=0; c<toCol; c++)
332
    {
333
      newVisCols.push_back(visCols->GetValue(c));
334
    }
335 336
    newVisCols.push_back(visCols->GetValue(fromCol));
    for(c=toCol; c<numCols; c++)
337
    {
338
      if(c != fromCol)
339
      {
340 341 342
        newVisCols.push_back(visCols->GetValue(c));
      }
    }
343
  }
344 345 346 347 348

  // repopulate the visCols
  vtkIdType visId=0;
  std::vector<vtkStdString>::iterator arrayIt;
  for(arrayIt=newVisCols.begin(); arrayIt!=newVisCols.end(); ++arrayIt)
349
  {
350
    visCols->SetValue(visId++, *arrayIt);
351
  }
352 353
  return true;
}
354
} // End of anonymous namespace
355

Mathieu Westphal's avatar
Mathieu Westphal committed
356
vtkObjectFactoryNewMacro(vtkScatterPlotMatrix)
357

358
vtkScatterPlotMatrix::vtkScatterPlotMatrix()
359 360
  : NumberOfBins(10), NumberOfFrames(25),
  LayoutUpdatedTime(0)
361
{
362
  this->Private = new PIMPL;
363 364
  this->TitleProperties = vtkSmartPointer<vtkTextProperty>::New();
  this->TitleProperties->SetFontSize(12);
365
  this->SelectionMode = vtkContextScene::SELECTION_NONE;
Mathieu Westphal's avatar
Mathieu Westphal committed
366
  this->ActivePlot = vtkVector2i(0, -2);
367
  this->ActivePlotValid = false;
Mathieu Westphal's avatar
Mathieu Westphal committed
368
  this->Animating = false;
369 370 371 372
}

vtkScatterPlotMatrix::~vtkScatterPlotMatrix()
{
373
  delete this->Private;
374 375 376 377
}

void vtkScatterPlotMatrix::Update()
{
378
  if (this->Private->VisibleColumnsModified)
379
  {
380 381
    // We need to handle layout changes due to modified visibility.
    // Build up our histograms data before updating the layout.
382 383 384
    PopulateHistograms(this->Input,
                       this->Private->Histogram,
                       this->VisibleColumns,
385
                       this->NumberOfBins);
386 387
    this->UpdateLayout();
    this->Private->VisibleColumnsModified = false;
388
  }
389
  else if (this->GetMTime() > this->LayoutUpdatedTime)
390
  {
391
    this->UpdateLayout();
392
  }
393 394 395 396
}

bool vtkScatterPlotMatrix::Paint(vtkContext2D *painter)
{
397
  this->CurrentPainter = painter;
398
  this->Update();
399 400 401
  bool ret = this->Superclass::Paint(painter);
  this->ResizeBigChart();
  return ret;
402 403
}

404 405 406 407 408 409 410 411 412
void vtkScatterPlotMatrix::SetScene(vtkContextScene *scene)
{
  // The internal axis shouldn't be a child as it isn't rendered with the
  // chart, but it does need access to the scene.
  this->Private->TestAxis->SetScene(scene);

  this->Superclass::SetScene(scene);
}

413 414
bool vtkScatterPlotMatrix::SetActivePlot(const vtkVector2i &pos)
{
415 416
  if (pos.GetX() + pos.GetY() + 1 < this->Size.GetX() && pos.GetX() < this->Size.GetX() &&
      pos.GetY() < this->Size.GetY())
417
  {
418 419
    // The supplied index is valid (in the lower quadrant).
    this->ActivePlot = pos;
420
    this->ActivePlotValid = true;
421

422 423 424
    // Invoke an interaction event, to let observers know something changed.
    this->InvokeEvent(vtkCommand::AnnotationChangedEvent);

425
    // set background colors for plots
426
    if (this->GetChart(this->ActivePlot)->GetPlot(0))
427
    {
428
      int plotCount = this->GetSize().GetX();
429
      for (int i = 0; i < plotCount; ++i)
430
      {
431
        for (int j = 0; j < plotCount; ++j)
432
        {
433
          if (this->GetPlotType(i, j) == SCATTERPLOT)
434
          {
435 436
            vtkChartXY *chart =
                vtkChartXY::SafeDownCast(this->GetChart(vtkVector2i(i, j)));
437

438
            if (pos[0] == i && pos[1] == j)
439
            {
440
              // set the new active chart background color to light green
441
              chart->SetBackgroundBrush(
442
                    this->Private->SelectedChartBGBrush);
443
            }
444
            else if (pos[0] == i || pos[1] == j)
445
            {
446 447
              // set background color for all other charts in the selected
              // chart's row and column to light red
448
              chart->SetBackgroundBrush(
449
                    this->Private->SelectedRowColumnBGBrush);
450
            }
451
            else
452
            {
453
              // set all else to white
454 455
              chart->SetBackgroundBrush(
                    this->Private->ChartSettings[SCATTERPLOT]
456
                    ->BackgroundBrush);
457 458 459
            }
          }
        }
460
      }
461
    }
462
    if (this->Private->BigChart)
463
    {
464
      vtkPlot *plot = this->Private->BigChart->GetPlot(0);
465 466
      vtkStdString column = this->GetColumnName(pos.GetX());
      vtkStdString row = this->GetRowName(pos.GetY());
467
      if (!plot)
468
      {
469
        plot = this->Private->BigChart->AddPlot(vtkChart::POINTS);
470
        vtkChart *active = this->GetChart(this->ActivePlot);
471 472
        vtkChartXY *xy = vtkChartXY::SafeDownCast(this->Private->BigChart);
        if (xy)
473
        {
474
          // Set plot corner, and axis visibility
475
          xy->SetPlotCorner(plot, 2);
476 477 478 479 480 481 482 483 484 485 486
          xy->SetAutoAxes(false);
          xy->GetAxis(vtkAxis::TOP)->SetVisible(true);
          xy->GetAxis(vtkAxis::RIGHT)->SetVisible(true);
          xy->GetAxis(vtkAxis::BOTTOM)->SetLabelsVisible(false);
          xy->GetAxis(vtkAxis::BOTTOM)->SetGridVisible(false);
          xy->GetAxis(vtkAxis::BOTTOM)->SetTicksVisible(false);
          xy->GetAxis(vtkAxis::BOTTOM)->SetVisible(true);
          xy->GetAxis(vtkAxis::LEFT)->SetLabelsVisible(false);
          xy->GetAxis(vtkAxis::LEFT)->SetGridVisible(false);
          xy->GetAxis(vtkAxis::LEFT)->SetTicksVisible(false);
          xy->GetAxis(vtkAxis::LEFT)->SetVisible(true);
487 488 489

          // set labels array
          if(this->Private->IndexedLabelsArray)
490
          {
491 492
              plot->SetIndexedLabels(this->Private->IndexedLabelsArray);
              plot->SetTooltipLabelFormat("%i");
493
          }
494
        }
495
        if (xy && active)
496
        {
497
          vtkAxis *a = active->GetAxis(vtkAxis::BOTTOM);
498 499
          xy->GetAxis(vtkAxis::TOP)->SetUnscaledRange(
            a->GetUnscaledMinimum(), a->GetUnscaledMaximum());
500
          a = active->GetAxis(vtkAxis::LEFT);
501 502
          xy->GetAxis(vtkAxis::RIGHT)->SetUnscaledRange(
            a->GetUnscaledMinimum(), a->GetUnscaledMaximum());
503
        }
504
      }
505
      else
506
      {
507 508
        this->Private->BigChart->ClearPlots();
        plot = this->Private->BigChart->AddPlot(vtkChart::POINTS);
509 510
        vtkChartXY *xy = vtkChartXY::SafeDownCast(this->Private->BigChart);
        if (xy)
511
        {
512
          xy->SetPlotCorner(plot, 2);
513
        }
514 515 516

        // set labels array
        if(this->Private->IndexedLabelsArray)
517
        {
518 519
          plot->SetIndexedLabels(this->Private->IndexedLabelsArray);
          plot->SetTooltipLabelFormat("%i");
520
        }
521
      }
522
      plot->SetInputData(this->Input, column, row);
523
      plot->SetPen(this->Private->ChartSettings[ACTIVEPLOT]
524 525
                   ->PlotPen);
      this->ApplyAxisSetting(this->Private->BigChart, column, row);
526

527
      // Set marker size and style.
528
      vtkPlotPoints *plotPoints = vtkPlotPoints::SafeDownCast(plot);
529 530 531 532
      plotPoints->SetMarkerSize(this->Private->ChartSettings[ACTIVEPLOT]
                                ->MarkerSize);
      plotPoints->SetMarkerStyle(this->Private->ChartSettings[ACTIVEPLOT]
                                 ->MarkerStyle);
Mathieu Westphal's avatar
Mathieu Westphal committed
533 534 535 536

      // Add supplementary plot if any
      this->AddSupplementaryPlot(this->Private->BigChart, ACTIVEPLOT, row, column, 2);

537 538 539
      // Set background color.
      this->Private->BigChart->SetBackgroundBrush(
            this->Private->ChartSettings[ACTIVEPLOT]
540
            ->BackgroundBrush);
541
      this->Private->BigChart->GetAxis(vtkAxis::TOP)->SetTitle(
542
            this->VisibleColumns->GetValue(pos.GetX()));
543
      this->Private->BigChart->GetAxis(vtkAxis::RIGHT)->SetTitle(
544
            this->VisibleColumns->GetValue(this->GetSize().GetX() - pos.GetY() - 1));
545
      // Calculate the ideal range.
546
      //this->Private->BigChart->RecalculateBounds();
547
    }
548 549
    return true;
  }
550
  else
551
  {
552
    return false;
553
  }
554 555 556 557 558 559 560
}

vtkVector2i vtkScatterPlotMatrix::GetActivePlot()
{
  return this->ActivePlot;
}

561
void vtkScatterPlotMatrix::UpdateAnimationPath(const vtkVector2i& newActivePos)
562 563
{
  this->Private->AnimationPath.clear();
564 565
  if (newActivePos[0] != this->ActivePlot[0] ||
      newActivePos[1] != this->ActivePlot[1])
566
  {
567
    if (newActivePos[1] >= this->ActivePlot[1])
568
    {
569
      // x direction first
570
      if (this->ActivePlot[0]>newActivePos[0])
571
      {
572
        for(int r = this->ActivePlot[0] - 1; r >= newActivePos[0]; r--)
573
        {
574 575
          this->Private->AnimationPath.push_back(vtkVector2i(r,
                                                             this->ActivePlot[1]));
576
        }
577
      }
578
      else
579
      {
580
        for (int r = this->ActivePlot[0] + 1; r <= newActivePos[0]; r++)
581
        {
582 583
          this->Private->AnimationPath.push_back(vtkVector2i(r,
                                                             this->ActivePlot[1]));
584
        }
585
      }
586
      // then y direction
587
      for (int c = this->ActivePlot[1] + 1; c <= newActivePos[1]; c++)
588
      {
589
        this->Private->AnimationPath.push_back(vtkVector2i(newActivePos[0], c));
590
      }
591
    }
592
    else
593
    {
594
      // y direction first
595
      for (int c = this->ActivePlot[1] - 1; c >= newActivePos[1]; c--)
596
      {
597 598
        this->Private->AnimationPath.push_back(vtkVector2i(this->ActivePlot[0],
                                                           c));
599
      }
600
      // then x direction
601
      if (this->ActivePlot[0]>newActivePos[0])
602
      {
603
        for (int r = this->ActivePlot[0] - 1; r >= newActivePos[0]; r--)
604
        {
605 606
          this->Private->AnimationPath.push_back(vtkVector2i(r,
                                                             newActivePos[1]));
607
        }
608
      }
609
      else
610
      {
611
        for (int r = this->ActivePlot[0] + 1; r <= newActivePos[0]; r++)
612
        {
613
          this->Private->AnimationPath.push_back(vtkVector2i(r, newActivePos[1]));
614 615 616
        }
      }
    }
617
  }
618
}
619

620
void vtkScatterPlotMatrix::StartAnimation(vtkRenderWindowInteractor* interactor)
621
{
622 623
  // Start a simple repeating timer to advance along the path until completion.
  if (!this->Private->TimerCallbackInitialized && interactor)
624
  {
Mathieu Westphal's avatar
Mathieu Westphal committed
625
    this->Animating = true;
626
    if (!this->Private->AnimationCallbackInitialized)
627
    {
628 629 630 631
      this->Private->AnimationCallback->SetClientData(this);
      this->Private->AnimationCallback->SetCallback(
            vtkScatterPlotMatrix::ProcessEvents);
      interactor->AddObserver(vtkCommand::TimerEvent,
632
                              this->Private->AnimationCallback,
633 634 635
                              0);
      this->Private->Interactor = interactor;
      this->Private->AnimationCallbackInitialized = true;
636
    }
637
    this->Private->TimerCallbackInitialized = true;
638 639
    // This defines the interval at which the animation will proceed. 25Hz?
    this->Private->TimerId = interactor->CreateRepeatingTimer(1000 / 50);
640
    this->Private->AnimationIter = this->Private->AnimationPath.begin();
641
    this->Private->AnimationPhase = 0;
642
  }
643 644 645 646
}

void vtkScatterPlotMatrix::AdvanceAnimation()
{
647 648 649 650 651 652 653 654 655 656
  // The animation has several phases, and we must track where we are.

  // 1: Remove decoration from the big chart.
  // 2: Set three dimensions to plot in the BigChart3D.
  // 3: Make BigChart inivisible, and BigChart3D visible.
  // 4: Rotate between the two dimensions we are transitioning between.
  //    -> Loop from start to end angle to complete the effect.
  // 5: Make the new dimensionality active, update BigChart.
  // 5: Make BigChart3D invisible and BigChart visible.
  // 6: Stop the timer.
657
  this->InvokeEvent(vtkCommand::AnimationCueTickEvent);
658
  switch (this->Private->AnimationPhase)
659
  {
660
  case 0: // Remove decoration from the big chart, load up the 3D chart
661
  {
662
    this->Private->NextActivePlot = *this->Private->AnimationIter;
663
    vtkChartXYZ *chart = this->Private->BigChart3D;
664
    chart->SetVisible(false);
Zack Galbreath's avatar
Zack Galbreath committed
665 666 667
    chart->SetAutoRotate(true);
    chart->SetDecorateAxes(false);
    chart->SetFitToScene(false);
668

669
    int yColumn = this->GetSize().GetY() - this->ActivePlot.GetY() - 1;
670 671
    bool isX = false;
    int zColumn = 0;
672

Zack Galbreath's avatar
Zack Galbreath committed
673
    vtkRectf size = this->Private->BigChart->GetSize();
674
    float zSize;
675
    this->Private->FinalAngle = 90.0;
676
    this->Private->IncAngle = this->Private->FinalAngle / this->NumberOfFrames;
677

678
    if (this->Private->NextActivePlot.GetY() == this->ActivePlot.GetY())
679
    {
680
      // Horizontal move.
681
      zColumn = this->Private->NextActivePlot.GetX();
682
      isX = false;
683
      if (this->ActivePlot.GetX() < zColumn)
684
      {
685
        this->Private->IncAngle *= 1.0;
686
        zSize = size.GetWidth();
687
      }
688
      else
689
      {
690
        this->Private->IncAngle *= -1.0;
691
        zSize = -size.GetWidth();
692
      }
693
    }
694
    else
695
    {
696
      // Vertical move.
697
      zColumn = this->GetSize().GetY() - this->Private->NextActivePlot.GetY() - 1;
698
      isX = true;
699
      if (this->GetSize().GetY() - this->ActivePlot.GetY() - 1 < zColumn)
700
      {
701
        this->Private->IncAngle *= -1.0;
702
        zSize = size.GetHeight();
703
      }
704
      else
705
      {
706
        this->Private->IncAngle *= 1.0;
707
        zSize = -size.GetHeight();
708
      }
709
    }
710
    chart->SetAroundX(isX);
Zack Galbreath's avatar
Zack Galbreath committed
711 712
    chart->SetGeometry(size);

713
    vtkStdString names[3];
714
    names[0] = this->VisibleColumns->GetValue(this->ActivePlot.GetX());
715 716
    names[1] = this->VisibleColumns->GetValue(yColumn);
    names[2] = this->VisibleColumns->GetValue(zColumn);
Zack Galbreath's avatar
Zack Galbreath committed
717 718 719 720 721

    // Setup the 3D chart
    this->Private->BigChart3D->ClearPlots();
    vtkNew<vtkPlotPoints3D> scatterPlot3D;
    scatterPlot3D->SetInputData(
722 723
      this->Input, names[0], names[1], names[2]);
    this->Private->BigChart3D->AddPlot(scatterPlot3D);
Zack Galbreath's avatar
Zack Galbreath committed
724

725 726 727 728
    // Set the z axis up so that it ends in the right orientation.
    chart->GetAxis(2)->SetPoint2(0, zSize);
    // Now set the ranges for the three axes.
    for (int i = 0; i < 3; ++i)
729
    {
730
      PIMPL::ColumnSetting &settings = this->Private->ColumnSettings[names[i]];
731
      chart->GetAxis(i)->SetUnscaledRange(settings.min, settings.max);
732
    }
733
    chart->RecalculateTransform();
734 735 736
    this->GetScene()->SetDirty(true);
    ++this->Private->AnimationPhase;
    return;
737
  }
Zack Galbreath's avatar
Zack Galbreath committed
738
  case 1: // Make BigChart invisible, and BigChart3D visible.
739
    this->Private->BigChart->SetVisible(false);
740
    this->AddItem(this->Private->BigChart3D);
741 742 743 744 745 746
    this->Private->BigChart3D->SetVisible(true);
    this->GetScene()->SetDirty(true);
    ++this->Private->AnimationPhase;
    this->Private->CurrentAngle = 0.0;
    return;
  case 2: // Rotation of the 3D chart from start to end angle.
747
    if (fabs(this->Private->CurrentAngle) < (this->Private->FinalAngle - 0.001))
748
    {
749 750
      this->Private->CurrentAngle += this->Private->IncAngle;
      this->Private->BigChart3D->SetAngle(this->Private->CurrentAngle);
751
    }
752
    else
753
    {
754
      ++this->Private->AnimationPhase;
755
    }
756 757 758 759
    this->GetScene()->SetDirty(true);
    return;
  case 3: // Transition to new dimensionality, update the big chart.
    this->SetActivePlot(this->Private->NextActivePlot);
760
    this->Private->BigChart->Update();
761
    this->GetScene()->SetDirty(true);
762 763 764
    ++this->Private->AnimationPhase;
    break;
  case 4:
765
    this->GetScene()->SetDirty(true);
766
    ++this->Private->AnimationIter;
767 768 769
    // Clean up - we are done.
    this->Private->AnimationPhase = 0;
    if (this->Private->AnimationIter == this->Private->AnimationPath.end())
770
    {
771
      this->Private->BigChart->SetVisible(true);
772
      this->RemoveItem(this->Private->BigChart3D);
773
      this->Private->BigChart3D->SetVisible(false);
774 775 776
      this->Private->Interactor->DestroyTimer(this->Private->TimerId);
      this->Private->TimerId = 0;
      this->Private->TimerCallbackInitialized = false;
777
    }
Mathieu Westphal's avatar
Mathieu Westphal committed
778
    this->Animating = false;
779
  }
780 781
}

782
void vtkScatterPlotMatrix::ProcessEvents(vtkObject *, unsigned long event,
783 784 785 786 787
                                         void *clientData, void *callerData)
{
  vtkScatterPlotMatrix *self =
      reinterpret_cast<vtkScatterPlotMatrix *>(clientData);
  switch (event)
788
  {
789
    case vtkCommand::TimerEvent:
790
    {
791 792 793 794
      // We must filter the events to ensure we actually get the timer event we
      // created. I would love signals and slots...
      int timerId = *reinterpret_cast<int *>(callerData);   // Seems to work.
      if (self->Private->TimerCallbackInitialized &&
795
          timerId == static_cast<int>(self->Private->TimerId))
796
      {
797 798 799
        self->AdvanceAnimation();
      }
      break;
800
    }
801 802 803
    default:
      break;
  }
804 805
}

806
vtkAnnotationLink* vtkScatterPlotMatrix::GetAnnotationLink()
807
{
808
  return this->Private->Link;
809 810
}

811 812
void vtkScatterPlotMatrix::SetInput(vtkTable *table)
{
813
  if(table && table->GetNumberOfRows() == 0)
814
  {
luz.paz's avatar
luz.paz committed
815
    // do nothing if the table is empty
816
    return;
817
  }
818

819
  if (this->Input != table)
820
  {
821 822
    // Set the input, then update the size of the scatter plot matrix, set
    // their inputs and all the other stuff needed.
823
    this->Input = table;
824
    this->SetSize(vtkVector2i(0, 0));
825 826
    this->Modified();

827
    if (table == nullptr)
828
    {
829
      this->SetColumnVisibilityAll(true);
830
      return;
831
    }
832
    int n = static_cast<int>(this->Input->GetNumberOfColumns());
833
    this->SetColumnVisibilityAll(true);
834
    this->SetSize(vtkVector2i(n, n));
835
  }
836 837 838 839 840 841
}

void vtkScatterPlotMatrix::SetColumnVisibility(const vtkStdString &name,
                                               bool visible)
{
  if (visible)
842
  {
843
    for (vtkIdType i = 0; i < this->VisibleColumns->GetNumberOfTuples(); ++i)
844
    {
845
      if (this->VisibleColumns->GetValue(i) == name)
846
      {
847 848 849
        // Already there, nothing more needs to be done
        return;
      }
850
    }
851 852
    // Add the column to the end of the list if it is a numeric column
    if (this->Input && this->Input->GetColumnByName(name.c_str()) &&
853
        vtkArrayDownCast<vtkDataArray>(this->Input->GetColumnByName(name.c_str())))
854
    {
855 856 857 858 859 860
      this->VisibleColumns->InsertNextValue(name);
      this->Private->VisibleColumnsModified = true;
      this->SetSize(vtkVector2i(0, 0));
      this->SetSize(vtkVector2i(this->VisibleColumns->GetNumberOfTuples(),
                                this->VisibleColumns->GetNumberOfTuples()));
      this->Modified();
861
    }
862
  }
863
  else
864
  {
865 866
    // Remove the value if present
    for (vtkIdType i = 0; i < this->VisibleColumns->GetNumberOfTuples(); ++i)
867
    {
868
      if (this->VisibleColumns->GetValue(i) == name)
869
      {
870
        // Move all the later elements down by one, and reduce the size
871
        while (i < this->VisibleColumns->GetNumberOfTuples() - 1)
872
        {
873
          this->VisibleColumns->SetValue(i,
874
                                         this->VisibleColumns->GetValue(i + 1));
875
          ++i;
876
        }
877
        this->VisibleColumns->SetNumberOfTuples(
878
            this->VisibleColumns->GetNumberOfTuples() - 1);
879
        this->SetSize(vtkVector2i(0, 0));
880 881
        this->SetSize(vtkVector2i(this->VisibleColumns->GetNumberOfTuples(),
                                  this->VisibleColumns->GetNumberOfTuples()));
882
        if (this->ActivePlot.GetX() + this->ActivePlot.GetY() + 1 >=
883
            this->VisibleColumns->GetNumberOfTuples())
884
        {
885
          this->ActivePlot.Set(0, this->VisibleColumns->GetNumberOfTuples() - 1);
886
        }
887 888 889 890
        this->Private->VisibleColumnsModified = true;
        this->Modified();
      }
    }
891
  }
892 893
}

894 895 896 897
void vtkScatterPlotMatrix::InsertVisibleColumn(const vtkStdString &name,
                                               int index)
{
  if(!this->Input || !this->Input->GetColumnByName(name.c_str()))
898
  {
899
    return;
900
  }
901 902 903 904 905 906

  // Check if the column is already in the list. If yes,
  // we may need to rearrange the order of the columns.
  vtkIdType currIdx = -1;
  vtkIdType numCols = this->VisibleColumns->GetNumberOfTuples();
  for (vtkIdType i = 0; i < numCols; ++i)
907
  {
908
    if (this->VisibleColumns->GetValue(i) == name)
909
    {
910 911 912
      currIdx = i;
      break;
    }
913
  }
914 915

  if(currIdx > 0 && currIdx == index)
916
  {
917 918
    //This column is already there.
    return;
919
  }
920 921

  if(currIdx < 0)
922
  {
923 924
    this->VisibleColumns->SetNumberOfTuples(numCols+1);
    if(index >= numCols)
925
    {
926
      this->VisibleColumns->SetValue(numCols