Commit a99a42c6 authored by Marcus D. Hanwell's avatar Marcus D. Hanwell Committed by Kitware Robot
Browse files

Merge topic 'scatter-plot-matrix'

87b3781b ENH: Use more points, reduced number of bins.
f1833451 BUG: Add LayoutIsDirty flag to vtkChartMatrix
39f446e9 ENH: Made number of bins configurable.
398212ff ENH: Added picking for charts in bottom-left triangle.
4ac08d90 ENH: Allow the action button to be set to -1.
parents f2d7982e 87b3781b
......@@ -51,8 +51,8 @@ int TestScatterPlotMatrix(int, char * [])
tangent->SetName("tan(x)");
table->AddColumn(tangent.GetPointer());
// Test the chart scatter plot matrix
int numPoints = 42;
float inc = 7.5 / (numPoints-1);
int numPoints = 100;
float inc = 4.0 * 3.14 / (numPoints-1);
table->SetNumberOfRows(numPoints);
for (int i = 0; i < numPoints; ++i)
{
......@@ -66,6 +66,8 @@ int TestScatterPlotMatrix(int, char * [])
// Set the scatter plot matrix up to analyze all columns in the table.
matrix->SetInput(table.GetPointer());
matrix->SetNumberOfBins(7);
//Finally render the scene and compare the image to a reference image
view->GetRenderWindow()->SetMultiSamples(0);
view->GetInteractor()->Initialize();
......
......@@ -274,7 +274,7 @@ vtkRectf vtkChart::GetSize()
void vtkChart::SetActionToButton(int action, int button)
{
if (action < 0 || action > 2)
if (action < -1 || action > 2)
{
vtkErrorMacro("Error, invalid action value supplied: " << action)
return;
......
......@@ -47,6 +47,7 @@ vtkChartMatrix::vtkChartMatrix() : Gutter(15.0, 15.0)
this->Borders[vtkAxis::BOTTOM] = 40;
this->Borders[vtkAxis::RIGHT] = 50;
this->Borders[vtkAxis::TOP] = 40;
this->LayoutIsDirty = true;
}
vtkChartMatrix::~vtkChartMatrix()
......@@ -61,7 +62,8 @@ void vtkChartMatrix::Update()
bool vtkChartMatrix::Paint(vtkContext2D *painter)
{
if (this->GetScene()->GetSceneWidth() != this->Private->Geometry.X() ||
if (this->LayoutIsDirty ||
this->GetScene()->GetSceneWidth() != this->Private->Geometry.X() ||
this->GetScene()->GetSceneHeight() != this->Private->Geometry.Y())
{
// Update the chart element positions
......@@ -112,6 +114,7 @@ bool vtkChartMatrix::Paint(vtkContext2D *painter)
}
}
}
this->LayoutIsDirty = false;
}
return Superclass::Paint(painter);
}
......@@ -131,12 +134,14 @@ void vtkChartMatrix::SetSize(const vtkVector2i &size)
}
this->Private->Charts.resize(size.X() * size.Y());
this->Private->Spans.resize(size.X() * size.Y(), vtkVector2i(1, 1));
this->LayoutIsDirty = true;
}
}
void vtkChartMatrix::SetGutter(const vtkVector2f &gutter)
{
this->Gutter = gutter;
this->LayoutIsDirty = true;
}
void vtkChartMatrix::Allocate()
......@@ -195,6 +200,7 @@ bool vtkChartMatrix::SetChartSpan(const vtkVector2i& position,
else
{
this->Private->Spans[position.Y() * this->Size.X() + position.X()] = span;
this->LayoutIsDirty = true;
return true;
}
}
......
......@@ -105,6 +105,7 @@ protected:
// The gutter between each chart.
vtkVector2f Gutter;
int Borders[4];
bool LayoutIsDirty;
private:
vtkChartMatrix(const vtkChartMatrix &); // Not implemented.
......
......@@ -21,10 +21,13 @@
#include "vtkChartXY.h"
#include "vtkPlot.h"
#include "vtkAxis.h"
#include "vtkContextMouseEvent.h"
#include "vtkStdString.h"
#include "vtkStringArray.h"
#include "vtkNew.h"
#include "vtkPen.h"
#include "vtkMathUtilities.h"
#include "vtkAnnotationLink.h"
#include "vtkObjectFactory.h"
class vtkScatterPlotMatrix::PIMPL
......@@ -41,22 +44,18 @@ public:
vtkNew<vtkTable> Histogram;
bool VisibleColumnsModified;
vtkChart* BigChart;
vtkNew<vtkAnnotationLink> Link;
};
namespace
{
int NumberOfBins = 10;
// This is just here for now - quick and dirty historgram calculations...
bool PopulateHistograms(vtkTable *input, vtkTable *output, vtkStringArray *s)
bool PopulateHistograms(vtkTable *input, vtkTable *output, vtkStringArray *s,
int NumberOfBins)
{
// 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.
for (vtkIdType i = output->GetNumberOfColumns() - 1; i >= 0; --i)
{
output->RemoveColumn(i);
}
for (vtkIdType i = 0; i < s->GetNumberOfTuples(); ++i)
{
double minmax[2] = { 0.0, 0.0 };
......@@ -71,18 +70,31 @@ bool PopulateHistograms(vtkTable *input, vtkTable *output, vtkStringArray *s)
{
minmax[1] = minmax[0] + 1.0;
}
double inc = (minmax[1] - minmax[0]) / NumberOfBins;
double inc = (minmax[1] - minmax[0]) / (NumberOfBins) * 1.001;
double halfInc = inc / 2.0;
vtkNew<vtkFloatArray> extents;
extents->SetName(vtkStdString(name + "_extents").c_str());
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());
}
extents->SetNumberOfTuples(NumberOfBins);
float *centers = static_cast<float *>(extents->GetVoidPointer(0));
double min = minmax[0] - 0.0005 * inc + halfInc;
for (int j = 0; j < NumberOfBins; ++j)
{
extents->SetValue(j, minmax[0] + j * inc);
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());
}
vtkNew<vtkIntArray> populations;
populations->SetName(vtkStdString(name + "_pops").c_str());
populations->SetNumberOfTuples(NumberOfBins);
int *pops = static_cast<int *>(populations->GetVoidPointer(0));
for (int k = 0; k < NumberOfBins; ++k)
......@@ -112,7 +124,7 @@ bool PopulateHistograms(vtkTable *input, vtkTable *output, vtkStringArray *s)
vtkStandardNewMacro(vtkScatterPlotMatrix)
vtkScatterPlotMatrix::vtkScatterPlotMatrix()
vtkScatterPlotMatrix::vtkScatterPlotMatrix() : NumberOfBins(10)
{
this->Private = new PIMPL;
}
......@@ -130,7 +142,8 @@ void vtkScatterPlotMatrix::Update()
// Build up our histograms data before updating the layout.
PopulateHistograms(this->Input.GetPointer(),
this->Private->Histogram.GetPointer(),
this->VisibleColumns.GetPointer());
this->VisibleColumns.GetPointer(),
this->NumberOfBins);
this->UpdateLayout();
this->Private->VisibleColumnsModified = false;
}
......@@ -148,23 +161,40 @@ bool vtkScatterPlotMatrix::SetActivePlot(const vtkVector2i &pos)
pos.Y() < this->Size.Y())
{
// The supplied index is valid (in the lower quadrant).
if (this->GetChart(this->ActivePlot)->GetPlot(0))
{
this->GetChart(this->ActivePlot)->GetPlot(0)->SetColor(0.0, 0.0, 0.0);
}
this->ActivePlot = pos;
if (this->GetChart(this->ActivePlot)->GetPlot(0))
{
this->GetChart(this->ActivePlot)->GetPlot(0)->SetColor(0.0, 0.0, 1.0);
}
if (this->Private->BigChart)
{
vtkPlot *plot = this->Private->BigChart->GetPlot(0);
if (!plot)
{
plot = this->Private->BigChart->AddPlot(vtkChart::POINTS);
vtkChart *active = this->GetChart(this->ActivePlot);
vtkChartXY *xy = vtkChartXY::SafeDownCast(this->Private->BigChart);
if (xy)
{
xy->SetPlotCorner(plot, 2);
}
if (xy && active)
{
vtkAxis *a = active->GetAxis(vtkAxis::BOTTOM);
xy->GetAxis(vtkAxis::TOP)->SetRange(a->GetMinimum(), a->GetMaximum());
a = active->GetAxis(vtkAxis::LEFT);
xy->GetAxis(vtkAxis::RIGHT)->SetRange(a->GetMinimum(), a->GetMaximum());
}
}
plot->SetInput(this->Input.GetPointer(),
this->VisibleColumns->GetValue(pos.X()),
this->VisibleColumns->GetValue(this->Size.X() -
pos.Y() - 1));
this->Private->BigChart->RecalculateBounds();
}
return true;
}
......@@ -247,7 +277,13 @@ void vtkScatterPlotMatrix::SetColumnVisibility(const vtkStdString &name,
this->SetSize(vtkVector2i(0, 0));
this->SetSize(vtkVector2i(this->VisibleColumns->GetNumberOfTuples(),
this->VisibleColumns->GetNumberOfTuples()));
if (this->ActivePlot.X() + this->ActivePlot.Y() + 1 >=
this->VisibleColumns->GetNumberOfTuples())
{
this->ActivePlot.Set(0, this->VisibleColumns->GetNumberOfTuples() - 1);
}
this->Private->VisibleColumnsModified = true;
this->Modified();
return;
}
......@@ -292,6 +328,58 @@ vtkStringArray* vtkScatterPlotMatrix::GetVisibleColumns()
return this->VisibleColumns.GetPointer();
}
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();
}
}
bool vtkScatterPlotMatrix::Hit(const vtkContextMouseEvent &)
{
return true;
}
bool vtkScatterPlotMatrix::MouseMoveEvent(const vtkContextMouseEvent &)
{
// Eat the event, don't do anything for now...
return true;
}
bool vtkScatterPlotMatrix::MouseButtonPressEvent(
const vtkContextMouseEvent &)
{
return true;
}
bool vtkScatterPlotMatrix::MouseButtonReleaseEvent(
const vtkContextMouseEvent &mouse)
{
// Work out which scatter plot was clicked - make that one the active plot.
int n = this->GetSize().X();
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < n; ++j)
{
if (i + j + 1 < n && this->GetChart(vtkVector2i(i, j))->Hit(mouse))
{
this->SetActivePlot(vtkVector2i(i, j));
return true;
}
}
}
return false;
}
void vtkScatterPlotMatrix::UpdateLayout()
{
// We want scatter plots on the lower-left triangle, then histograms along
......@@ -313,14 +401,21 @@ void vtkScatterPlotMatrix::UpdateLayout()
vtkVector2i pos(i, j);
if (i + j + 1 < n)
{
this->GetChart(pos)->SetAnnotationLink(this->Private->Link.GetPointer());
// Lower-left triangle - scatter plots.
this->GetChart(pos)->SetActionToButton(vtkChart::PAN, -1);
this->GetChart(pos)->SetActionToButton(vtkChart::ZOOM, -1);
this->GetChart(pos)->SetActionToButton(vtkChart::SELECT, -1);
vtkPlot *plot = this->GetChart(pos)->AddPlot(vtkChart::POINTS);
plot->SetInput(this->Input.GetPointer(), i, n - j - 1);
plot->SetInput(this->Input.GetPointer(),
this->VisibleColumns->GetValue(i),
this->VisibleColumns->GetValue(n - j - 1));
}
else if (i == n - j - 1)
{
// We are on the diagonal - need a histogram plot.
vtkPlot *plot = this->GetChart(pos)->AddPlot(vtkChart::BAR);
plot->GetPen()->SetColor(255, 255, 255);
vtkStdString name(this->VisibleColumns->GetValue(i));
plot->SetInput(this->Private->Histogram.GetPointer(),
name + "_extents", name + "_pops");
......@@ -334,6 +429,7 @@ void vtkScatterPlotMatrix::UpdateLayout()
vtkChartXY *xy = vtkChartXY::SafeDownCast(this->GetChart(pos));
if (xy)
{
xy->SetBarWidthFraction(1.0);
xy->SetPlotCorner(plot, 2);
}
}
......@@ -341,6 +437,8 @@ void vtkScatterPlotMatrix::UpdateLayout()
{
// This big plot in the top-right
this->Private->BigChart = this->GetChart(pos);
this->Private->BigChart->SetAnnotationLink(
this->Private->Link.GetPointer());
this->SetChartSpan(pos, vtkVector2i(n - i, n - j));
this->SetActivePlot(vtkVector2i(0, n - 2));
}
......
......@@ -81,6 +81,32 @@ public:
// Get a list of the columns, and the order in which they are displayed.
virtual vtkStringArray* GetVisibleColumns();
// Description:
// Set the number of bins in the histograms along the central diagonal of the
// scatter plot matrix.
virtual void SetNumberOfBins(int numberOfBins);
// Description:
// Get the number of bins the histograms along the central diagonal scatter
// plot matrix. The default value is 10.
virtual int GetNumberOfBins() const { return this->NumberOfBins; }
// Description:
// Return true if the supplied x, y coordinate is inside the item.
bool Hit(const vtkContextMouseEvent &mouse);
// Description:
// Mouse move event.
bool MouseMoveEvent(const vtkContextMouseEvent &mouse);
// Description:
// Mouse button down event
bool MouseButtonPressEvent(const vtkContextMouseEvent &mouse);
// Description:
// Mouse button release event.
bool MouseButtonReleaseEvent(const vtkContextMouseEvent &mouse);
protected:
vtkScatterPlotMatrix();
~vtkScatterPlotMatrix();
......@@ -102,9 +128,13 @@ protected:
// Weakly owned input data for the scatter plot matrix.
vtkSmartPointer<vtkTable> Input;
// Strongly owned internal data for the column visibility.
vtkNew<vtkStringArray> VisibleColumns;
// The number of bins in the histograms.
int NumberOfBins;
private:
vtkScatterPlotMatrix(const vtkScatterPlotMatrix &); // Not implemented.
void operator=(const vtkScatterPlotMatrix &); // Not implemented.
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment