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

  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"

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

43 44
#include "vtkChartXYZ.h"

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

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

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

83 84 85 86 87 88 89 90 91 92 93 94 95 96
  // 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;
  };

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

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

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

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

175
  vtkNew<vtkTable> Histogram;
176
  bool VisibleColumnsModified;
177
  vtkWeakPointer<vtkChart> BigChart;
178
  vtkNew<vtkAnnotationLink> Link;
179

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

184 185 186
  // Axis ranges for the columns in the scatter plot matrix.
  std::map<std::string, ColumnSetting> ColumnSettings;

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

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

208 209 210 211
namespace
{

// This is just here for now - quick and dirty historgram calculations...
212 213
bool PopulateHistograms(vtkTable *input, vtkTable *output, vtkStringArray *s,
                        int NumberOfBins)
214 215 216
{
  // 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.
217
  for (vtkIdType i = 0; i < s->GetNumberOfTuples(); ++i)
218 219
    {
    double minmax[2] = { 0.0, 0.0 };
220 221 222
    vtkStdString name(s->GetValue(i));
    vtkDataArray *in =
        vtkDataArray::SafeDownCast(input->GetColumnByName(name.c_str()));
223 224 225 226 227 228 229 230
    if (in)
      {
      // The bin values are the centers, extending +/- half an inc either side
      in->GetRange(minmax);
      if (minmax[0] == minmax[1])
        {
        minmax[1] = minmax[0] + 1.0;
        }
231
      double inc = (minmax[1] - minmax[0]) / (NumberOfBins) * 1.001;
232
      double halfInc = inc / 2.0;
233 234 235 236 237 238 239 240
      vtkSmartPointer<vtkFloatArray> extents =
          vtkFloatArray::SafeDownCast(
            output->GetColumnByName(vtkStdString(name + "_extents").c_str()));
      if (!extents)
        {
        extents = vtkSmartPointer<vtkFloatArray>::New();
        extents->SetName(vtkStdString(name + "_extents").c_str());
        }
241 242
      extents->SetNumberOfTuples(NumberOfBins);
      float *centers = static_cast<float *>(extents->GetVoidPointer(0));
243
      double min = minmax[0] - 0.0005 * inc + halfInc;
244 245
      for (int j = 0; j < NumberOfBins; ++j)
        {
246 247 248 249 250 251 252 253 254
        extents->SetValue(j, min + j * inc);
        }
      vtkSmartPointer<vtkIntArray> populations =
          vtkIntArray::SafeDownCast(
            output->GetColumnByName(vtkStdString(name + "_pops").c_str()));
      if (!populations)
        {
        populations = vtkSmartPointer<vtkIntArray>::New();
        populations->SetName(vtkStdString(name + "_pops").c_str());
255 256 257 258 259 260 261 262 263 264 265 266 267
        }
      populations->SetNumberOfTuples(NumberOfBins);
      int *pops = static_cast<int *>(populations->GetVoidPointer(0));
      for (int k = 0; k < NumberOfBins; ++k)
        {
        pops[k] = 0;
        }
      for (vtkIdType j = 0; j < in->GetNumberOfTuples(); ++j)
        {
        double v(0.0);
        in->GetTuple(j, &v);
        for (int k = 0; k < NumberOfBins; ++k)
          {
268
          if (vtkMathUtilities::FuzzyCompare(v, double(centers[k]), halfInc))
269 270 271 272 273 274 275 276 277 278 279 280
            {
            ++pops[k];
            break;
            }
          }
        }
      output->AddColumn(extents.GetPointer());
      output->AddColumn(populations.GetPointer());
      }
    }
  return true;
}
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350

bool MoveColumn(vtkStringArray* visCols, int fromCol, int toCol)
{
  if(!visCols || visCols->GetNumberOfTuples() == 0
    || fromCol == toCol || fromCol == (toCol-1) || fromCol < 0 || toCol < 0)
    {
    return false;
    }
  int numCols = visCols->GetNumberOfTuples();
  if( fromCol >= numCols || toCol > numCols)
    {
    return false;
    }

  std::vector<vtkStdString> newVisCols;
  vtkIdType c;
  if(toCol == numCols)
    {
    for(c=0; c<numCols; c++)
      {
      if(c!=fromCol)
        {
        newVisCols.push_back(visCols->GetValue(c));
        }
      }
    // move the fromCol to the end
    newVisCols.push_back(visCols->GetValue(fromCol));
    }
  // insert the fromCol before toCol
  else if(fromCol < toCol)
    {
    // move Cols in the middle up
    for(c=0; c<fromCol; c++)
      {
      newVisCols.push_back(visCols->GetValue(c));
      }
    for(c=fromCol+1; c<numCols; c++)
      {
      if(c == toCol)
        {
        newVisCols.push_back(visCols->GetValue(fromCol));
        }
      newVisCols.push_back(visCols->GetValue(c));
      }
    }
  else
    {
    for(c=0; c<toCol; c++)
      {
      newVisCols.push_back(visCols->GetValue(c));
      }
    newVisCols.push_back(visCols->GetValue(fromCol));
    for(c=toCol; c<numCols; c++)
      {
      if(c != fromCol)
        {
        newVisCols.push_back(visCols->GetValue(c));
        }
      }
    }

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

353 354
vtkStandardNewMacro(vtkScatterPlotMatrix)

355 356
vtkScatterPlotMatrix::vtkScatterPlotMatrix()
  : NumberOfBins(10), NumberOfFrames(25)
357
{
358
  this->Private = new PIMPL;
359 360
  this->TitleProperties = vtkSmartPointer<vtkTextProperty>::New();
  this->TitleProperties->SetFontSize(12);
361
  this->SelectionMode = vtkContextScene::SELECTION_NONE;
362 363 364 365
}

vtkScatterPlotMatrix::~vtkScatterPlotMatrix()
{
366
  delete this->Private;
367 368 369 370
}

void vtkScatterPlotMatrix::Update()
{
371 372 373 374 375
  if (this->Private->VisibleColumnsModified)
    {
    // We need to handle layout changes due to modified visibility.
    // Build up our histograms data before updating the layout.
    PopulateHistograms(this->Input.GetPointer(),
376
                       this->Private->Histogram.GetPointer(),
377 378
                       this->VisibleColumns.GetPointer(),
                       this->NumberOfBins);
379 380 381
    this->UpdateLayout();
    this->Private->VisibleColumnsModified = false;
    }
382 383 384 385
}

bool vtkScatterPlotMatrix::Paint(vtkContext2D *painter)
{
386
  this->Update();
387 388 389
  return Superclass::Paint(painter);
}

390 391 392 393 394 395 396 397 398
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);
}

399 400
bool vtkScatterPlotMatrix::SetActivePlot(const vtkVector2i &pos)
{
401 402
  if (pos.GetX() + pos.GetY() + 1 < this->Size.GetX() && pos.GetX() < this->Size.GetX() &&
      pos.GetY() < this->Size.GetY())
403 404 405
    {
    // The supplied index is valid (in the lower quadrant).
    this->ActivePlot = pos;
406

407 408 409
    // Invoke an interaction event, to let observers know something changed.
    this->InvokeEvent(vtkCommand::AnnotationChangedEvent);

410
    // set background colors for plots
411 412
    if (this->GetChart(this->ActivePlot)->GetPlot(0))
      {
413
      int plotCount = this->GetSize().GetX();
414
      for (int i = 0; i < plotCount; ++i)
415
        {
416
        for (int j = 0; j < plotCount; ++j)
417
          {
418
          if (this->GetPlotType(i, j) == SCATTERPLOT)
419
            {
420 421
            vtkChartXY *chart =
                vtkChartXY::SafeDownCast(this->GetChart(vtkVector2i(i, j)));
422

423
            if (pos[0] == i && pos[1] == j)
424 425
              {
              // set the new active chart background color to light green
426 427
              chart->SetBackgroundBrush(
                    this->Private->SelectedChartBGBrush.GetPointer());
428
              }
429
            else if (pos[0] == i || pos[1] == j)
430
              {
431 432
              // set background color for all other charts in the selected
              // chart's row and column to light red
433 434
              chart->SetBackgroundBrush(
                    this->Private->SelectedRowColumnBGBrush.GetPointer());
435 436 437 438
              }
            else
              {
              // set all else to white
439 440 441
              chart->SetBackgroundBrush(
                    this->Private->ChartSettings[SCATTERPLOT]
                    ->BackgroundBrush.GetPointer());
442 443 444 445
              }
            }
          }
        }
446
      }
447 448 449
    if (this->Private->BigChart)
      {
      vtkPlot *plot = this->Private->BigChart->GetPlot(0);
450 451
      vtkStdString column = this->GetColumnName(pos.GetX());
      vtkStdString row = this->GetRowName(pos.GetY());
452 453 454
      if (!plot)
        {
        plot = this->Private->BigChart->AddPlot(vtkChart::POINTS);
455
        vtkChart *active = this->GetChart(this->ActivePlot);
456 457 458
        vtkChartXY *xy = vtkChartXY::SafeDownCast(this->Private->BigChart);
        if (xy)
          {
459
          // Set plot corner, and axis visibility
460
          xy->SetPlotCorner(plot, 2);
461 462 463 464 465 466 467 468 469 470 471
          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);
472 473 474 475 476 477 478

          // set labels array
          if(this->Private->IndexedLabelsArray)
            {
              plot->SetIndexedLabels(this->Private->IndexedLabelsArray);
              plot->SetTooltipLabelFormat("%i");
            }
479
          }
480 481 482
        if (xy && active)
          {
          vtkAxis *a = active->GetAxis(vtkAxis::BOTTOM);
David Thompson's avatar
David Thompson committed
483 484
          xy->GetAxis(vtkAxis::TOP)->SetUnscaledRange(
            a->GetUnscaledMinimum(), a->GetUnscaledMaximum());
485
          a = active->GetAxis(vtkAxis::LEFT);
David Thompson's avatar
David Thompson committed
486 487
          xy->GetAxis(vtkAxis::RIGHT)->SetUnscaledRange(
            a->GetUnscaledMinimum(), a->GetUnscaledMaximum());
488
          }
489
        }
490 491 492 493
      else
        {
        this->Private->BigChart->ClearPlots();
        plot = this->Private->BigChart->AddPlot(vtkChart::POINTS);
494 495 496 497 498
        vtkChartXY *xy = vtkChartXY::SafeDownCast(this->Private->BigChart);
        if (xy)
          {
          xy->SetPlotCorner(plot, 2);
          }
499 500 501 502 503 504 505

        // set labels array
        if(this->Private->IndexedLabelsArray)
          {
          plot->SetIndexedLabels(this->Private->IndexedLabelsArray);
          plot->SetTooltipLabelFormat("%i");
          }
506
        }
507
      plot->SetInputData(this->Input.GetPointer(), column, row);
508 509
      plot->SetPen(this->Private->ChartSettings[ACTIVEPLOT]
                   ->PlotPen.GetPointer());
510
      this->ApplyAxisSetting(this->Private->BigChart.GetPointer(), column, row);
511

512
      // Set marker size and style.
513
      vtkPlotPoints *plotPoints = vtkPlotPoints::SafeDownCast(plot);
514 515 516 517
      plotPoints->SetMarkerSize(this->Private->ChartSettings[ACTIVEPLOT]
                                ->MarkerSize);
      plotPoints->SetMarkerStyle(this->Private->ChartSettings[ACTIVEPLOT]
                                 ->MarkerStyle);
518 519 520 521
      // Set background color.
      this->Private->BigChart->SetBackgroundBrush(
            this->Private->ChartSettings[ACTIVEPLOT]
            ->BackgroundBrush.GetPointer());
522
      this->Private->BigChart->GetAxis(vtkAxis::TOP)->SetTitle(
523
            this->VisibleColumns->GetValue(pos.GetX()));
524
      this->Private->BigChart->GetAxis(vtkAxis::RIGHT)->SetTitle(
525
            this->VisibleColumns->GetValue(this->GetSize().GetX() - pos.GetY() - 1));
526
      // Calculate the ideal range.
527
      //this->Private->BigChart->RecalculateBounds();
528 529 530 531 532 533 534 535 536 537 538 539 540 541
      }
    return true;
    }
  else
    {
    return false;
    }
}

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

542
void vtkScatterPlotMatrix::UpdateAnimationPath(const vtkVector2i& newActivePos)
543 544
{
  this->Private->AnimationPath.clear();
545 546
  if (newActivePos[0] != this->ActivePlot[0] ||
      newActivePos[1] != this->ActivePlot[1])
547
    {
548
    if (newActivePos[1] >= this->ActivePlot[1])
549 550
      {
      // x direction first
551
      if (this->ActivePlot[0]>newActivePos[0])
552
        {
553 554 555 556 557
        for(int r = this->ActivePlot[0] - 1; r >= newActivePos[0]; r--)
          {
          this->Private->AnimationPath.push_back(vtkVector2i(r,
                                                             this->ActivePlot[1]));
          }
558 559 560
        }
      else
        {
561 562 563 564 565
        for (int r = this->ActivePlot[0] + 1; r <= newActivePos[0]; r++)
          {
          this->Private->AnimationPath.push_back(vtkVector2i(r,
                                                             this->ActivePlot[1]));
          }
566 567
        }
      // then y direction
568 569 570 571
      for (int c = this->ActivePlot[1] + 1; c <= newActivePos[1]; c++)
        {
        this->Private->AnimationPath.push_back(vtkVector2i(newActivePos[0], c));
        }
572 573 574 575
      }
    else
      {
      // y direction first
576 577 578 579 580
      for (int c = this->ActivePlot[1] - 1; c >= newActivePos[1]; c--)
        {
        this->Private->AnimationPath.push_back(vtkVector2i(this->ActivePlot[0],
                                                           c));
        }
581
      // then x direction
582
      if (this->ActivePlot[0]>newActivePos[0])
583
        {
584 585 586 587 588
        for (int r = this->ActivePlot[0] - 1; r >= newActivePos[0]; r--)
          {
          this->Private->AnimationPath.push_back(vtkVector2i(r,
                                                             newActivePos[1]));
          }
589 590 591
        }
      else
        {
592 593 594 595
        for (int r = this->ActivePlot[0] + 1; r <= newActivePos[0]; r++)
          {
          this->Private->AnimationPath.push_back(vtkVector2i(r, newActivePos[1]));
          }
596 597 598 599
        }
      }
    }
}
600

601
void vtkScatterPlotMatrix::StartAnimation(vtkRenderWindowInteractor* interactor)
602
{
603 604 605 606 607 608 609 610 611 612 613 614 615 616 617
  // Start a simple repeating timer to advance along the path until completion.
  if (!this->Private->TimerCallbackInitialized && interactor)
    {
    if (!this->Private->AnimationCallbackInitialized)
      {
      this->Private->AnimationCallback->SetClientData(this);
      this->Private->AnimationCallback->SetCallback(
            vtkScatterPlotMatrix::ProcessEvents);
      interactor->AddObserver(vtkCommand::TimerEvent,
                              this->Private->AnimationCallback.GetPointer(),
                              0);
      this->Private->Interactor = interactor;
      this->Private->AnimationCallbackInitialized = true;
      }
    this->Private->TimerCallbackInitialized = true;
618 619
    // This defines the interval at which the animation will proceed. 25Hz?
    this->Private->TimerId = interactor->CreateRepeatingTimer(1000 / 50);
620
    this->Private->AnimationIter = this->Private->AnimationPath.begin();
621
    this->Private->AnimationPhase = 0;
622 623 624 625 626
    }
}

void vtkScatterPlotMatrix::AdvanceAnimation()
{
627 628 629 630 631 632 633 634 635 636
  // 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.
637
  this->InvokeEvent(vtkCommand::AnimationCueTickEvent);
638 639 640
  switch (this->Private->AnimationPhase)
    {
  case 0: // Remove decoration from the big chart, load up the 3D chart
641
    {
642
    this->Private->NextActivePlot = *this->Private->AnimationIter;
643 644
    vtkChartXYZ *chart = this->Private->BigChart3D.GetPointer();
    chart->SetVisible(false);
Zack Galbreath's avatar
Zack Galbreath committed
645 646 647
    chart->SetAutoRotate(true);
    chart->SetDecorateAxes(false);
    chart->SetFitToScene(false);
648

649
    int yColumn = this->GetSize().GetY() - this->ActivePlot.GetY() - 1;
650 651
    bool isX = false;
    int zColumn = 0;
652

Zack Galbreath's avatar
Zack Galbreath committed
653
    vtkRectf size = this->Private->BigChart->GetSize();
654
    float zSize;
655
    this->Private->FinalAngle = 90.0;
656
    this->Private->IncAngle = this->Private->FinalAngle / this->NumberOfFrames;
657

658
    if (this->Private->NextActivePlot.GetY() == this->ActivePlot.GetY())
659
      {
660
      // Horizontal move.
661
      zColumn = this->Private->NextActivePlot.GetX();
662
      isX = false;
663
      if (this->ActivePlot.GetX() < zColumn)
664
        {
665
        this->Private->IncAngle *= 1.0;
666
        zSize = size.GetWidth();
667 668 669
        }
      else
        {
670
        this->Private->IncAngle *= -1.0;
671
        zSize = -size.GetWidth();
672 673 674 675
        }
      }
    else
      {
676
      // Vertical move.
677
      zColumn = this->GetSize().GetY() - this->Private->NextActivePlot.GetY() - 1;
678
      isX = true;
679
      if (this->GetSize().GetY() - this->ActivePlot.GetY() - 1 < zColumn)
680
        {
681
        this->Private->IncAngle *= -1.0;
682
        zSize = size.GetHeight();
683 684 685
        }
      else
        {
686
        this->Private->IncAngle *= 1.0;
687
        zSize = -size.GetHeight();
688 689
        }
      }
690
    chart->SetAroundX(isX);
Zack Galbreath's avatar
Zack Galbreath committed
691 692
    chart->SetGeometry(size);

693
    vtkStdString names[3];
694
    names[0] = this->VisibleColumns->GetValue(this->ActivePlot.GetX());
695 696
    names[1] = this->VisibleColumns->GetValue(yColumn);
    names[2] = this->VisibleColumns->GetValue(zColumn);
Zack Galbreath's avatar
Zack Galbreath committed
697 698 699 700 701 702 703 704

    // Setup the 3D chart
    this->Private->BigChart3D->ClearPlots();
    vtkNew<vtkPlotPoints3D> scatterPlot3D;
    scatterPlot3D->SetInputData(
      this->Input.GetPointer(), names[0], names[1], names[2]);
    this->Private->BigChart3D->AddPlot(scatterPlot3D.GetPointer());

705 706 707 708 709 710
    // 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)
      {
      PIMPL::ColumnSetting &settings = this->Private->ColumnSettings[names[i]];
David Thompson's avatar
David Thompson committed
711
      chart->GetAxis(i)->SetUnscaledRange(settings.min, settings.max);
712 713
      }
    chart->RecalculateTransform();
714 715 716
    this->GetScene()->SetDirty(true);
    ++this->Private->AnimationPhase;
    return;
717
    }
Zack Galbreath's avatar
Zack Galbreath committed
718
  case 1: // Make BigChart invisible, and BigChart3D visible.
719 720 721 722 723 724 725 726
    this->Private->BigChart->SetVisible(false);
    this->AddItem(this->Private->BigChart3D.GetPointer());
    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.
727
    if (fabs(this->Private->CurrentAngle) < (this->Private->FinalAngle - 0.001))
728 729 730 731 732 733 734 735 736 737 738 739
      {
      this->Private->CurrentAngle += this->Private->IncAngle;
      this->Private->BigChart3D->SetAngle(this->Private->CurrentAngle);
      }
    else
      {
      ++this->Private->AnimationPhase;
      }
    this->GetScene()->SetDirty(true);
    return;
  case 3: // Transition to new dimensionality, update the big chart.
    this->SetActivePlot(this->Private->NextActivePlot);
740
    this->Private->BigChart->Update();
741
    this->GetScene()->SetDirty(true);
742 743 744
    ++this->Private->AnimationPhase;
    break;
  case 4:
745
    this->GetScene()->SetDirty(true);
746
    ++this->Private->AnimationIter;
747 748 749 750
    // Clean up - we are done.
    this->Private->AnimationPhase = 0;
    if (this->Private->AnimationIter == this->Private->AnimationPath.end())
      {
751 752 753
      this->Private->BigChart->SetVisible(true);
      this->RemoveItem(this->Private->BigChart3D.GetPointer());
      this->Private->BigChart3D->SetVisible(false);
754 755 756 757
      this->Private->Interactor->DestroyTimer(this->Private->TimerId);
      this->Private->TimerId = 0;
      this->Private->TimerCallbackInitialized = false;
      }
758 759 760
    }
}

761
void vtkScatterPlotMatrix::ProcessEvents(vtkObject *, unsigned long event,
762 763 764 765 766 767 768 769 770 771 772 773
                                         void *clientData, void *callerData)
{
  vtkScatterPlotMatrix *self =
      reinterpret_cast<vtkScatterPlotMatrix *>(clientData);
  switch (event)
    {
    case vtkCommand::TimerEvent:
      {
      // 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 &&
774
          timerId == static_cast<int>(self->Private->TimerId))
775 776 777 778 779 780 781
        {
        self->AdvanceAnimation();
        }
      break;
      }
    default:
      break;
782 783 784
    }
}

785
vtkAnnotationLink* vtkScatterPlotMatrix::GetAnnotationLink()
786
{
787
  return this->Private->Link.GetPointer();
788 789
}

790 791
void vtkScatterPlotMatrix::SetInput(vtkTable *table)
{
792 793 794 795 796 797
  if(table && table->GetNumberOfRows() == 0)
    {
    // do nothing if the table is emtpy
    return;
    }

798 799
  if (this->Input != table)
    {
800 801
    // Set the input, then update the size of the scatter plot matrix, set
    // their inputs and all the other stuff needed.
802
    this->Input = table;
803
    this->SetSize(vtkVector2i(0, 0));
804 805 806 807
    this->Modified();

    if (table == NULL)
      {
808
      this->SetColumnVisibilityAll(true);
809 810 811
      return;
      }
    int n = static_cast<int>(this->Input->GetNumberOfColumns());
812
    this->SetColumnVisibilityAll(true);
813
    this->SetSize(vtkVector2i(n, n));
814 815 816 817 818 819 820 821 822
    }
}

void vtkScatterPlotMatrix::SetColumnVisibility(const vtkStdString &name,
                                               bool visible)
{
  if (visible)
    {
    for (vtkIdType i = 0; i < this->VisibleColumns->GetNumberOfTuples(); ++i)
823
      {
824
      if (this->VisibleColumns->GetValue(i) == name)
825
        {
826 827 828 829
        // Already there, nothing more needs to be done
        return;
        }
      }
830 831 832 833 834 835 836 837 838 839 840
    // Add the column to the end of the list if it is a numeric column
    if (this->Input && this->Input->GetColumnByName(name.c_str()) &&
        vtkDataArray::SafeDownCast(this->Input->GetColumnByName(name.c_str())))
      {
      this->VisibleColumns->InsertNextValue(name);
      this->Private->VisibleColumnsModified = true;
      this->SetSize(vtkVector2i(0, 0));
      this->SetSize(vtkVector2i(this->VisibleColumns->GetNumberOfTuples(),
                                this->VisibleColumns->GetNumberOfTuples()));
      this->Modified();
      }
841 842 843 844 845 846 847 848 849
    }
  else
    {
    // Remove the value if present
    for (vtkIdType i = 0; i < this->VisibleColumns->GetNumberOfTuples(); ++i)
      {
      if (this->VisibleColumns->GetValue(i) == name)
        {
        // Move all the later elements down by one, and reduce the size
850
        while (i < this->VisibleColumns->GetNumberOfTuples() - 1)
851
          {
852
          this->VisibleColumns->SetValue(i,
853
                                         this->VisibleColumns->GetValue(i + 1));
854
          ++i;
855
          }
856
        this->VisibleColumns->SetNumberOfTuples(
857
            this->VisibleColumns->GetNumberOfTuples() - 1);
858
        this->SetSize(vtkVector2i(0, 0));
859 860
        this->SetSize(vtkVector2i(this->VisibleColumns->GetNumberOfTuples(),
                                  this->VisibleColumns->GetNumberOfTuples()));
861
        if (this->ActivePlot.GetX() + this->ActivePlot.GetY() + 1 >=
862 863 864 865
            this->VisibleColumns->GetNumberOfTuples())
          {
          this->ActivePlot.Set(0, this->VisibleColumns->GetNumberOfTuples() - 1);
          }
866 867 868 869 870 871 872
        this->Private->VisibleColumnsModified = true;
        this->Modified();
        }
      }
    }
}

873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931
void vtkScatterPlotMatrix::InsertVisibleColumn(const vtkStdString &name,
                                               int index)
{
  if(!this->Input || !this->Input->GetColumnByName(name.c_str()))
    {
    return;
    }

  // 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)
    {
    if (this->VisibleColumns->GetValue(i) == name)
      {
      currIdx = i;
      break;
      }
    }

  if(currIdx > 0 && currIdx == index)
    {
    //This column is already there.
    return;
    }

  if(currIdx < 0)
    {
    this->VisibleColumns->SetNumberOfTuples(numCols+1);
    if(index >= numCols)
      {
      this->VisibleColumns->SetValue(numCols, name);
      }
    else // move all the values after index down 1
      {
      vtkIdType startidx = numCols;
      vtkIdType idx = (index < 0) ? 0 : index;
      while (startidx > idx)
        {
        this->VisibleColumns->SetValue(startidx,
          this->VisibleColumns->GetValue(startidx-1));
        startidx--;
        }
      this->VisibleColumns->SetValue(idx, name);
      }
    this->Private->VisibleColumnsModified = true;
    }
  else // need to rearrange table columns
    {
    vtkIdType toIdx = (index < 0) ? 0 : index;
    toIdx = toIdx>numCols ? numCols : toIdx;
    this->Private->VisibleColumnsModified =
      MoveColumn(this->VisibleColumns.GetPointer(), currIdx, toIdx);
    }

  this->Update();
}

932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959
bool vtkScatterPlotMatrix::GetColumnVisibility(const vtkStdString &name)
{
  for (vtkIdType i = 0; i < this->VisibleColumns->GetNumberOfTuples(); ++i)
    {
    if (this->VisibleColumns->GetValue(i) == name)
      {
      return true;
      }
    }
  return false;
}

void vtkScatterPlotMatrix::SetColumnVisibilityAll(bool visible)
{
  if (visible && this->Input)
    {
    vtkIdType n = this->Input->GetNumberOfColumns();
    this->VisibleColumns->SetNumberOfTuples(n);
    for (vtkIdType i = 0; i < n; ++i)
      {
      this->VisibleColumns->SetValue(i, this->Input->GetColumnName(i));
      }
    }
  else
    {
    this->SetSize(vtkVector2i(0, 0));
    this->VisibleColumns->SetNumberOfTuples(0);
    }
960 961

  this->Private->VisibleColumnsModified = true;
962 963 964 965 966 967 968
}

vtkStringArray* vtkScatterPlotMatrix::GetVisibleColumns()
{
  return this->VisibleColumns.GetPointer();
}

969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985
void vtkScatterPlotMatrix::SetVisibleColumns(vtkStringArray* visColumns)
{
  if(!visColumns || visColumns->GetNumberOfTuples() == 0)
    {
    this->SetSize(vtkVector2i(0, 0));
    this->VisibleColumns->SetNumberOfTuples(0);
    }
  else
    {
    this->VisibleColumns->SetNumberOfTuples(
      visColumns->GetNumberOfTuples());
    this->VisibleColumns->DeepCopy(visColumns);
    }
  this->Private->VisibleColumnsModified = true;
  this->Update();
}

986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001
void vtkScatterPlotMatrix::SetNumberOfBins(int numberOfBins)
{
  if (this->NumberOfBins != numberOfBins)
    {
    this->NumberOfBins = numberOfBins;
    if (this->Input)
      {
      PopulateHistograms(this->Input.GetPointer(),
                         this->Private->Histogram.GetPointer(),
                         this->VisibleColumns.GetPointer(),
                         this->NumberOfBins);
      }
    this->Modified();
    }
}

1002
void vtkScatterPlotMatrix::SetPlotColor(int plotType, const vtkColor4ub& color)
1003
{
1004
  if(plotType >= 0 && plotType < NOPLOT)
1005
    {
1006
    if (plotType == ACTIVEPLOT || plotType == SCATTERPLOT)
1007
      {
1008
      this->Private->ChartSettings[plotType]->PlotPen->SetColor(color);
1009
      }
1010 1011 1012 1013 1014
    else
      {
      this->Private->ChartSettings[HISTOGRAM]->PlotBrush->SetColor(color);
      }
    this->Modified();
1015 1016 1017
    }
}

1018
void vtkScatterPlotMatrix::SetPlotMarkerStyle(int plotType, int style)
1019
{
1020 1021
  if(plotType >= 0 && plotType < NOPLOT &&
     style != this->Private->ChartSettings[plotType]->MarkerStyle)
1022
    {
1023
    this->Private->ChartSettings[plotType]->MarkerStyle = style;
1024

1025
    if (plotType == ACTIVEPLOT)
1026
      {
1027 1028
      vtkChart *chart = this->Private->BigChart;
      if (chart)
1029
        {
1030 1031
        vtkPlotPoints *plot = vtkPlotPoints::SafeDownCast(chart->GetPlot(0));
        if (plot)
1032 1033 1034 1035
          {
          plot->SetMarkerStyle(style);
          }
        }
1036
      this->Modified();
1037
      }
1038
    else if (plotType == SCATTERPLOT)
1039
      {
1040
      int plotCount = this->GetSize().GetX();
1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057
      for (int i = 0; i < plotCount - 1; ++i)
        {
        for(int j = 0; j < plotCount - 1; ++j)
          {
          if(this->GetPlotType(i, j) == SCATTERPLOT &&
             this->GetChart(vtkVector2i(i, j)))
            {
            vtkChart *chart = this->GetChart(vtkVector2i(i, j));
            vtkPlotPoints *plot = vtkPlotPoints::SafeDownCast(chart->GetPlot(0));
            if (plot)
              {
              plot->SetMarkerStyle(style);
              }
            }
          }
        }
      this->Modified();
1058 1059