Commit cee00a0e authored by Joachim Pouderoux's avatar Joachim Pouderoux

Fix and enhance the box plot and chart

Fix the problem with the position of Y axis: long labels were trunked.
This position was fixed, it is not flexible depending the size of the labels.

Add new functions to the chart API to allow manipulation of series
by id (vs by name).
Add support for manual plot labels setting.
Enhance the boxplot displacement, it now correctly follow the mouse
without ugly jumps.

Change-Id: I400cd064616f7b93415e17db9749d709ea2f7345
parent 05265ad8
......@@ -13,18 +13,20 @@
=========================================================================*/
#include "vtkRenderer.h"
#include "vtkRenderWindow.h"
#include "vtkAxis.h"
#include "vtkChartBox.h"
#include "vtkPlotBox.h"
#include "vtkTable.h"
#include "vtkLookupTable.h"
#include "vtkIntArray.h"
#include "vtkFloatArray.h"
#include "vtkContextView.h"
#include "vtkContextScene.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkContextView.h"
#include "vtkFloatArray.h"
#include "vtkIntArray.h"
#include "vtkLookupTable.h"
#include "vtkNew.h"
#include "vtkPlotBox.h"
#include "vtkRenderer.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkStringArray.h"
#include "vtkTable.h"
//----------------------------------------------------------------------------
int TestBoxPlot(int , char* [])
......@@ -39,31 +41,29 @@ int TestBoxPlot(int , char* [])
// Creates a vtkPlotBox input table
// The vtkPlotBox object will display 4 (arbitrary) box plots
int numParam = 4;
int numParam = 5;
vtkNew<vtkTable> inputBoxPlotTable;
for (int i = 0; i < numParam; i++)
{
char num[3];
sprintf(num, "%d", i);
char name[10];
strcpy(name,"Param ");
strcat(name,num);
char num[10];
sprintf(num, "P%d", i);
vtkNew<vtkIntArray> arrIndex;
arrIndex->SetName(name);
arrIndex->SetName(num);
inputBoxPlotTable->AddColumn(arrIndex.GetPointer());
}
inputBoxPlotTable->SetNumberOfRows(5);
// This scaling parameter can be used to test Y axis positioning
const double scale = 1e02;
for (int i = 0; i < numParam; i++)
{
inputBoxPlotTable->SetValue(0, i, i/2); //Q0
inputBoxPlotTable->SetValue(1, i, 2*i + 2 - i); //Q1
inputBoxPlotTable->SetValue(2, i, 2*i + 4); //Q2
inputBoxPlotTable->SetValue(3, i, 2*i + 7); //Q3
inputBoxPlotTable->SetValue(4, i, 2*i + 8); //Q4
inputBoxPlotTable->SetValue(0, i, (i/2) * scale); //Q0
inputBoxPlotTable->SetValue(1, i, (2*i + 2 - i) * scale); //Q1
inputBoxPlotTable->SetValue(2, i, (2*i + 4) * scale); //Q2
inputBoxPlotTable->SetValue(3, i, (2*i + 7) * scale); //Q3
inputBoxPlotTable->SetValue(4, i, (2*i + 8) * scale); //Q4
}
vtkNew<vtkLookupTable> lookup;
......@@ -74,8 +74,23 @@ int TestBoxPlot(int , char* [])
chart->GetPlot(0)->SetInputData(inputBoxPlotTable.GetPointer());
chart->SetColumnVisibilityAll(true);
chart->SetShowLegend(true);
double rgb[3] = { 1., 1., 0. };
vtkPlotBox::SafeDownCast(chart->GetPlot(0))->SetColumnColor("Param 1", rgb);
// Hide one box plot
chart->SetColumnVisibility(3, false);
// Set the labels
vtkNew<vtkStringArray> labels;
labels->SetNumberOfValues(5);
labels->SetValue(0, "Param 0");
labels->SetValue(1, "Param 1");
labels->SetValue(2, "Param 2");
labels->SetValue(3, "Param 3");
labels->SetValue(4, "Param 4");
chart->GetPlot(0)->SetLabels(labels.GetPointer());
// Manually change the color of one serie
double rgb[3] = { 0.5, 0.5, 0.5 };
vtkPlotBox::SafeDownCast(chart->GetPlot(0))->SetColumnColor("P1", rgb);
// Render the scene
view->GetRenderWindow()->SetMultiSamples(0);
......
bb6a441740a4bafd1b25160626bfe04a
94400c10e2afe09ce3a6265ab624f225
\ No newline at end of file
......@@ -62,6 +62,7 @@ public:
vtkNew<vtkTransform2D> Transform;
vtkNew<vtkAxis> YAxis;
vtkNew<vtkPlotGrid> Grid;
float SelectedColumnDelta;
};
//-----------------------------------------------------------------------------
......@@ -157,8 +158,9 @@ bool vtkChartBox::Paint(vtkContext2D *painter)
return false;
}
//this->UpdateGeometry(painter);
this->Update();
this->UpdateGeometry();
this->UpdateGeometry(painter);
// Handle selections
vtkIdTypeArray *idArray = 0;
......@@ -246,6 +248,21 @@ void vtkChartBox::SetColumnVisibility(const vtkStdString& name,
}
}
//-----------------------------------------------------------------------------
void vtkChartBox::SetColumnVisibility(vtkIdType column, bool visible)
{
vtkPlot *plot = this->GetPlot(0);
if (!plot || !plot->GetInput())
{
return;
}
vtkTable *table = plot->GetInput();
if (table)
{
this->SetColumnVisibility(table->GetColumnName(column), visible);
}
}
//-----------------------------------------------------------------------------
void vtkChartBox::SetColumnVisibilityAll(bool visible)
{
......@@ -254,7 +271,12 @@ void vtkChartBox::SetColumnVisibilityAll(bool visible)
this->SelectedColumn = -1;
if (visible)
{
vtkTable *table = this->GetPlot(0)->GetInput();
vtkPlot *plot = this->GetPlot(0);
if (!plot || !plot->GetInput())
{
return;
}
vtkTable *table = plot->GetInput();
for (vtkIdType i = 0; i < table->GetNumberOfColumns(); ++i)
{
this->SetColumnVisibility(table->GetColumnName(i), visible);
......@@ -275,12 +297,44 @@ bool vtkChartBox::GetColumnVisibility(const vtkStdString& name)
return false;
}
//-----------------------------------------------------------------------------
bool vtkChartBox::GetColumnVisibility(vtkIdType column)
{
vtkPlot *plot = this->GetPlot(0);
if (!plot || !plot->GetInput())
{
return false;
}
vtkTable *table = plot->GetInput();
return this->GetColumnVisibility(table->GetColumnName(column));
}
//-----------------------------------------------------------------------------
vtkIdType vtkChartBox::GetNumberOfVisibleColumns()
{
return this->VisibleColumns->GetNumberOfTuples();
}
//-----------------------------------------------------------------------------
vtkIdType vtkChartBox::GetColumnId(const vtkStdString& name)
{
vtkPlot *plot = this->GetPlot(0);
if (!plot || !plot->GetInput())
{
return -1;
}
vtkTable *table = plot->GetInput();
vtkIdType nbColumn = table->GetNumberOfColumns();
for (vtkIdType i = 0; i < nbColumn; i++)
{
if (!strcmp(table->GetColumnName(i), name.c_str()))
{
return i;
}
}
return -1;
}
//-----------------------------------------------------------------------------
vtkAxis* vtkChartBox::GetYAxis()
{
......@@ -292,6 +346,7 @@ void vtkChartBox::SetPlot(vtkPlotBox *plot)
{
this->Storage->Plot = plot;
this->Storage->Plot->SetParent(this);
this->Modified();
}
//-----------------------------------------------------------------------------
......@@ -314,7 +369,7 @@ float vtkChartBox::GetXPosition(int index)
}
//-----------------------------------------------------------------------------
void vtkChartBox::UpdateGeometry()
void vtkChartBox::UpdateGeometry(vtkContext2D* painter)
{
vtkVector2i geometry(this->GetScene()->GetViewWidth(),
this->GetScene()->GetViewHeight());
......@@ -322,32 +377,42 @@ void vtkChartBox::UpdateGeometry()
if (geometry.GetX() != this->Geometry[0] ||
geometry.GetY() != this->Geometry[1] || !this->GeometryValid)
{
vtkAxis* axis = this->Storage->YAxis.GetPointer();
axis->SetPoint1(0, this->Point1[1]);
axis->SetPoint2(0, this->Point2[1]);
if (axis->GetBehavior() == 0)
{
axis->AutoScale();
}
axis->Update();
int leftBorder = 0;
if (axis->GetVisible())
{
vtkRectf bounds = axis->GetBoundingRect(painter);
leftBorder = int(bounds.GetWidth());;
}
axis->SetPoint1(leftBorder, this->Point1[1]);
axis->SetPoint2(leftBorder, this->Point2[1]);
// Take up the entire window right now, this could be made configurable
this->SetGeometry(geometry.GetData());
this->SetBorders(40, 30, 0, 20);
this->SetBorders(leftBorder, 30, 0, 20);
int nbPlots = static_cast<int>(this->Storage->XPosition.size());
// Iterate through the axes and set them up to span the chart area.
int xStep = (this->Point2[0] - this->Point1[0]) /
(static_cast<int>(this->Storage->XPosition.size()));
int xStep = (this->Point2[0] - this->Point1[0]) / nbPlots;
int x = this->Point1[0] + (xStep / 2);
for (size_t i = 0; i < this->Storage->XPosition.size(); ++i)
for (int i = 0; i < nbPlots; ++i)
{
this->Storage->XPosition[i] = x;
x += xStep;
}
vtkAxis* axis = this->Storage->YAxis.GetPointer();
axis->SetPoint1(40, this->Point1[1]);
axis->SetPoint2(40, this->Point2[1]);
if (axis->GetBehavior() == 0)
{
axis->AutoScale();
}
axis->Update();
this->GeometryValid = true;
// Cause the plot transform to be recalculated if necessary
this->CalculatePlotTransform();
......@@ -400,11 +465,9 @@ bool vtkChartBox::MouseMoveEvent(const vtkContextMouseEvent &mouse)
this->Tooltip->SetVisible(false);
}
// Move the axis in x
float deltaX = mouse.GetScenePos().GetX() - mouse.GetLastScenePos().GetX();
this->Storage->XPosition[this->SelectedColumn] += deltaX;
float selX = this->Storage->XPosition[this->SelectedColumn];
// Move the plot in x
float posX = mouse.GetScenePos().GetX() + this->SelectedColumnDelta;
this->Storage->XPosition[this->SelectedColumn] = posX;
int nbCols = static_cast<int>(this->Storage->XPosition.size());
int left = this->SelectedColumn - 1;
......@@ -412,17 +475,18 @@ bool vtkChartBox::MouseMoveEvent(const vtkContextMouseEvent &mouse)
float width = this->Storage->Plot->GetBoxWidth() * 0.5f;
if (left >= 0 && (selX - width) < this->Storage->XPosition[left])
if (left >= 0 && (posX - width) < this->Storage->XPosition[left])
{
this->SwapAxes(this->SelectedColumn, this->SelectedColumn - 1);
this->SelectedColumn--;
}
else if (right < nbCols && (selX + width) > this->Storage->XPosition[right])
else if (right < nbCols && (posX + width) > this->Storage->XPosition[right])
{
this->SwapAxes(this->SelectedColumn, this->SelectedColumn + 1);
this->SelectedColumn++;
}
this->Scene->SetDirty(true);
this->Storage->XPosition[this->SelectedColumn] = posX;
}
if (mouse.GetButton() == vtkContextMouseEvent::NO_BUTTON)
......@@ -442,7 +506,7 @@ bool vtkChartBox::MouseButtonPressEvent(const vtkContextMouseEvent& mouse)
{
if (mouse.GetButton() == this->Actions.Pan())
{
// Select an axis if we are within range
// Select a plot if we are within range
if (mouse.GetScenePos()[1] > this->Point1[1] &&
mouse.GetScenePos()[1] < this->Point2[1])
{
......@@ -455,6 +519,8 @@ bool vtkChartBox::MouseButtonPressEvent(const vtkContextMouseEvent& mouse)
selX + width > mouse.GetScenePos()[0])
{
this->SelectedColumn = static_cast<int>(i);
this->SelectedColumnDelta =
this->GetXPosition(this->SelectedColumn) - mouse.GetScenePos().GetX();
this->Scene->SetDirty(true);
return true;
}
......@@ -497,7 +563,6 @@ bool vtkChartBox::MouseButtonReleaseEvent(const vtkContextMouseEvent& mouse)
else if (mouse.GetButton() == this->Actions.Pan())
{
this->GeometryValid = false;
this->UpdateGeometry();
this->SelectedColumn = -1;
return true;
}
......@@ -638,22 +703,16 @@ void vtkChartBox::PrintSelf(ostream &os, vtkIndent indent)
//-----------------------------------------------------------------------------
void vtkChartBox::SwapAxes(int a1, int a2)
{
// only neighboring axes
if (abs(a1-a2) != 1)
{
return;
}
float xTmp = this->Storage->XPosition[a1];
this->Storage->XPosition[a1] = this->Storage->XPosition[a2];
this->Storage->XPosition[a2] = xTmp;
vtkStdString colTmp = this->VisibleColumns->GetValue(a1);
this->VisibleColumns->SetValue(a1, this->VisibleColumns->GetValue(a2));
this->VisibleColumns->SetValue(a2, colTmp);
this->GeometryValid = false;
this->UpdateGeometry();
int xStep = (this->Point2[0] - this->Point1[0]) /
(static_cast<int>(this->Storage->XPosition.size()));
int xPos = (this->Point1[0] + (xStep / 2)) + xStep * a1;
this->Storage->XPosition[a1] = xPos;
this->GeometryValid = true;
this->Storage->Plot->Update();
}
......@@ -53,6 +53,7 @@ public:
// Description:
// Set the visibility of the specified column.
void SetColumnVisibility(const vtkStdString& name, bool visible);
void SetColumnVisibility(vtkIdType column, bool visible);
// Description:
// Set the visibility of all columns (true will make them all visible, false
......@@ -62,6 +63,11 @@ public:
// Description:
// Get the visibility of the specified column.
bool GetColumnVisibility(const vtkStdString& name);
bool GetColumnVisibility(vtkIdType column);
// Description:
// Get the input table column id of a column by its name.
vtkIdType GetColumnId(const vtkStdString& name);
// Description:
// Get a list of the columns, and the order in which they are displayed.
......@@ -152,6 +158,7 @@ protected:
// Description:
// Index of the selected column in the visible columns list.
int SelectedColumn;
float SelectedColumnDelta;
// Description:
// The point cache is marked dirty until it has been initialized.
......@@ -162,7 +169,7 @@ protected:
vtkSmartPointer<vtkTooltipItem> Tooltip;
void ResetSelection();
void UpdateGeometry();
void UpdateGeometry(vtkContext2D*);
void CalculatePlotTransform();
void SwapAxes(int a1, int a2);
......
......@@ -199,6 +199,16 @@ void vtkPlotBox::DrawBoxPlot(int i, unsigned char *rgba, double x,
painter->DrawLine(xneg, q[2], xpos, q[2]);
}
//-----------------------------------------------------------------------------
vtkStringArray* vtkPlotBox::GetLabels()
{
if (this->Labels)
{
return this->Labels;
}
return 0;
}
//-----------------------------------------------------------------------------
bool vtkPlotBox::PaintLegend(vtkContext2D* painter, const vtkRectf& rec, int)
{
......@@ -215,6 +225,10 @@ bool vtkPlotBox::PaintLegend(vtkContext2D* painter, const vtkRectf& rec, int)
for (int i = 0; i < nbCols; i++)
{
vtkStdString colName = parent->GetVisibleColumns()->GetValue(i);
if (this->GetLabels() && this->GetLabels()->GetNumberOfValues() > i)
{
colName = this->GetLabels()->GetValue(parent->GetColumnId(colName));
}
painter->DrawString(parent->GetXPosition(i), rec.GetY(), colName);
}
return true;
......
......@@ -69,6 +69,11 @@ public:
this->SetInputData(table);
}
// Description:
// Get the plot labels. If this array has a length greater than 1 the index
// refers to the stacked objects in the plot.
virtual vtkStringArray *GetLabels();
// Description:
// Function to query a plot for the nearest point to the specified coordinate.
// Returns the index of the data series with which the point is associated
......
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