Commit cb356937 authored by Keith Fieldhouse's avatar Keith Fieldhouse
Browse files

Add Stacked Plot

A series of StackedPlots may be added to a ChartXY and they will
be drawn stacked (and filled) on top of each other.  Only the actual
values need be supplied, the Plot and Chart deal with offsetting
the plot for stacking.
parent 918aca16
......@@ -47,6 +47,7 @@ SET(Kit_SRCS
vtkPlotBar.cxx
vtkPlotGrid.cxx
vtkPlotLine.cxx
vtkPlotStacked.cxx
vtkPlotParallelCoordinates.cxx
vtkPlotPoints.cxx
vtkTooltipItem.cxx
......
......@@ -6,6 +6,7 @@ IF (VTK_USE_RENDERING AND VTK_USE_VIEWS)
TestPCPlot.cxx
TestDiagram.cxx
TestLinePlot.cxx
TestStackedPlot.cxx
TestBarGraph.cxx
TestContext.cxx
TestGLSL.cxx
......
/*=========================================================================
Program: Visualization Toolkit
Module: TestLinePlot.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 "vtkRenderer.h"
#include "vtkRenderWindow.h"
#include "vtkSmartPointer.h"
#include "vtkChartXY.h"
#include "vtkPlot.h"
#include "vtkPlotStacked.h"
#include "vtkTable.h"
#include "vtkIntArray.h"
#include "vtkContextView.h"
#include "vtkContextScene.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRegressionTestImage.h"
#define VTK_CREATE(type, name) \
vtkSmartPointer<type> name = vtkSmartPointer<type>::New()
// Monthly checkout data
static int book[] = {5675, 5902, 6388, 5990, 5575, 7393, 9878, 8082, 6417, 5946, 5526, 5166};
static int new_popular[] = {701, 687, 736, 696, 750, 814, 923, 860, 786, 735, 680, 741};
static int periodical[] = {184, 176, 166, 131, 171, 191, 231, 166, 197, 162, 152, 143};
static int audiobook[] = {903, 1038, 987, 1073, 1144, 1203, 1173, 1196, 1213, 1076, 926, 874};
static int video[] = {1524, 1565, 1627, 1445, 1179, 1816, 2293, 1811, 1588, 1561, 1542, 1563};
//----------------------------------------------------------------------------
int TestStackedPlot( int argc, char * argv [] )
{
// Set up a 2D scene, add an XY chart to it
VTK_CREATE(vtkContextView, view);
view->GetRenderer()->SetBackground(1.0, 1.0, 1.0);
view->GetRenderWindow()->SetSize(400, 300);
VTK_CREATE(vtkChartXY, chart);
view->GetScene()->AddItem(chart);
// Create a table with some points in it...
VTK_CREATE(vtkTable, table);
VTK_CREATE(vtkIntArray, arrMonth);
arrMonth->SetName("Month");
table->AddColumn(arrMonth);
VTK_CREATE(vtkIntArray,arrBase);
arrBase->SetName("Base");
table->AddColumn(arrBase);
VTK_CREATE(vtkIntArray, arrBook);
arrBook->SetName("Books");
table->AddColumn(arrBook);
VTK_CREATE(vtkIntArray, arrNewPopularBook);
arrNewPopularBook->SetName("New / Popular");
table->AddColumn(arrNewPopularBook);
VTK_CREATE(vtkIntArray, arrPeriodical);
arrPeriodical->SetName("Periodical");
table->AddColumn(arrPeriodical);
VTK_CREATE(vtkIntArray, arrAudiobook);
arrAudiobook->SetName("Audiobook");
table->AddColumn(arrAudiobook);
VTK_CREATE(vtkIntArray, arrVideo);
arrVideo->SetName("Video");
table->AddColumn(arrVideo);
table->SetNumberOfRows(12);
for (int i = 0; i < 12; i++)
{
table->SetValue(i,0,i+1);
table->SetValue(i,1,book[i]);
table->SetValue(i,2,new_popular[i]);
table->SetValue(i,3,periodical[i]);
table->SetValue(i,4,audiobook[i]);
table->SetValue(i,5,video[i]);
}
// Add multiple line plots, setting the colors etc
vtkPlotStacked *stack = 0;
// Books
stack = vtkPlotStacked::SafeDownCast(chart->AddPlot(vtkChart::STACKED));
stack->SetInput(table, 0, 1);
stack->SetColor(120, 120, 254, 255);
// New / Popular
stack = vtkPlotStacked::SafeDownCast(chart->AddPlot(vtkChart::STACKED));
stack->SetInput(table, 0, 2);
stack->SetColor(254, 118, 118, 255);
// Periodical
stack = vtkPlotStacked::SafeDownCast(chart->AddPlot(vtkChart::STACKED));
stack->SetInput(table, 0, 3);
stack->SetColor(170, 170, 254, 255);
// Audiobook
stack = vtkPlotStacked::SafeDownCast(chart->AddPlot(vtkChart::STACKED));
stack->SetInput(table, 0, 4);
stack->SetColor(91, 91, 254, 255);
// Video
stack = vtkPlotStacked::SafeDownCast(chart->AddPlot(vtkChart::STACKED));
stack->SetInput(table, 0, 5);
stack->SetColor(253, 158, 158, 255);
//Finally render the scene and compare the image to a reference image
view->GetRenderWindow()->SetMultiSamples(0);
int retVal = vtkRegressionTestImageThreshold(view->GetRenderWindow(), 25);
if(retVal == vtkRegressionTester::DO_INTERACTOR)
{
view->GetInteractor()->Initialize();
view->GetInteractor()->Start();
}
return !retVal;
}
#!/usr/bin/env python
# Run this test like so:
# vtkpython TestStackedPlot.py -D $VTK_DATA_ROOT \
# -B $VTK_DATA_ROOT/Baseline/Charts/
import os
import vtk
import vtk.test.Testing
import math
book = [5675, 5902, 6388, 5990, 5575, 7393, 9878, 8082, 6417, 5946, 5526, 5166];
new_popular = [701, 687, 736, 696, 750, 814, 923, 860, 786, 735, 680, 741];
periodical = [184, 176, 166, 131, 171, 191, 231, 166, 197, 162, 152, 143];
audiobook = [903, 1038, 987, 1073, 1144, 1203, 1173, 1196, 1213, 1076, 926, 874];
video = [1524, 1565, 1627, 1445, 1179, 1816, 2293, 1811, 1588, 1561, 1542, 1563];
class TestStackedPlot(vtk.test.Testing.vtkTest):
def testStackedPlot(self):
"Test if stacked plots can be built with python"
# Set up a 2D scene, add an XY chart to it
view = vtk.vtkContextView()
view.GetRenderer().SetBackground(1.0,1.0,1.0)
view.GetRenderWindow().SetSize(400,300)
chart = vtk.vtkChartXY()
view.GetScene().AddItem(chart)
# Create a table with some points in it
table = vtk.vtkTable()
arrMonth = vtk.vtkIntArray()
arrMonth.SetName("Month")
arrBooks = vtk.vtkIntArray()
arrBooks.SetName("Books")
arrNew = vtk.vtkIntArray()
arrNew.SetName("New / Popular")
arrPeriodical = vtk.vtkIntArray()
arrPeriodical.SetName("Periodical")
arrAudiobook = vtk.vtkIntArray()
arrAudiobook.SetName("Audiobook")
arrVideo = vtk.vtkIntArray()
arrVideo.SetName("Video")
numMonths = 12
for i in range(0,numMonths):
arrMonth.InsertNextValue(i + 1)
arrBooks.InsertNextValue(book[i])
arrNew.InsertNextValue(new_popular[i])
arrPeriodical.InsertNextValue(periodical[i])
arrAudiobook.InsertNextValue(audiobook[i])
arrVideo.InsertNextValue(video[i])
table.AddColumn(arrMonth)
table.AddColumn(arrBooks)
table.AddColumn(arrNew)
table.AddColumn(arrPeriodical)
table.AddColumn(arrAudiobook)
table.AddColumn(arrVideo)
# Now add the line plots with appropriate colors
# Books
line = chart.AddPlot(3)
line.SetInput(table,0,1)
line.SetColor(120,120,254,255)
# New / Popular
line = chart.AddPlot(3)
line.SetInput(table,0,2)
line.SetColor(254,118,118,255)
# Periodical
line = chart.AddPlot(3)
line.SetInput(table,0,3)
line.SetColor(170,170,254,255)
# Audiobook
line = chart.AddPlot(3)
line.SetInput(table,0,4)
line.SetColor(91,91,254,255)
# Video
line = chart.AddPlot(3)
line.SetInput(table,0,5)
line.SetColor(253,158,158,255)
view.GetRenderWindow().SetMultiSamples(0)
# view.GetRenderWindow().GetInteractor().Start()
img_file = "TestStackedPlot.png"
img_file2 = "TestStackedPlot0Hidden.png"
vtk.test.Testing.compareImage(view.GetRenderWindow(),vtk.test.Testing.getAbsImagePath(img_file),threshold=25)
vtk.test.Testing.interact()
chart.GetPlot(0).SetVisible(False)
vtk.test.Testing.compareImage(view.GetRenderWindow(),vtk.test.Testing.getAbsImagePath(img_file2),threshold=25)
vtk.test.Testing.interact()
if __name__ == "__main__":
vtk.test.Testing.main([(TestStackedPlot, 'test')])
......@@ -27,6 +27,7 @@
#include "vtkPlot.h"
#include "vtkPlotBar.h"
#include "vtkPlotStacked.h"
#include "vtkPlotLine.h"
#include "vtkPlotPoints.h"
#include "vtkContextMapper2D.h"
......@@ -54,6 +55,7 @@
#include "vtkTextProperty.h"
#include "vtksys/ios/sstream"
#include "vtkDataArray.h"
// My STL containers
#include <vtkstd/vector>
......@@ -68,6 +70,8 @@ public:
this->PlotCorners.resize(4);
this->PlotTransforms.resize(4);
this->PlotTransforms[0] = vtkSmartPointer<vtkTransform2D>::New();
this->StackedPlotAccumulator = vtkSmartPointer<vtkDataArray>();
this->StackParticipantsChanged.Modified();
}
vtkstd::vector<vtkPlot *> plots; // Charts can contain multiple plots of data
......@@ -75,6 +79,8 @@ public:
vtkstd::vector< vtkSmartPointer<vtkTransform2D> > PlotTransforms; // Transforms
vtkstd::vector<vtkAxis *> axes; // Charts can contain multiple axes
vtkSmartPointer<vtkColorSeries> Colors; // Colors in the chart
vtkSmartPointer<vtkDataArray> StackedPlotAccumulator;
vtkTimeStamp StackParticipantsChanged; // Plot added or plot visibility changed
};
//-----------------------------------------------------------------------------
......@@ -144,6 +150,10 @@ vtkChartXY::~vtkChartXY()
//-----------------------------------------------------------------------------
void vtkChartXY::Update()
{
// The Stack accumulator should be re-initialized at the start of every
// update cycle.
this->ChartPrivate->StackedPlotAccumulator = NULL;
// Perform any necessary updates that are not graphical
// Update the plots if necessary
for (size_t i = 0; i < this->ChartPrivate->plots.size(); ++i)
......@@ -216,7 +226,8 @@ bool vtkChartXY::Paint(vtkContext2D *painter)
recalculateTransform = true;
}
if (this->ChartPrivate->plots[0]->GetData()->GetInput()->GetMTime() > this->MTime)
if (this->ChartPrivate->plots[0]->GetData()->GetInput()->GetMTime() > this->MTime ||
this->ChartPrivate->StackParticipantsChanged > this->MTime)
{
this->RecalculateBounds();
}
......@@ -745,6 +756,14 @@ vtkPlot * vtkChartXY::AddPlot(int type)
plot = bar;
break;
}
case STACKED:
{
vtkPlotStacked *stacked = vtkPlotStacked::New();
stacked->SetParent(this);
stacked->GetBrush()->SetColor(color.GetData());
plot = stacked;
break;
}
default:
plot = NULL;
}
......@@ -849,6 +868,66 @@ void vtkChartXY::SetScene(vtkContextScene *scene)
this->vtkContextItem::SetScene(scene);
this->Tooltip->SetScene(scene);
}
//-----------------------------------------------------------------------------
namespace {
template <class A>
void InitializeAccumulator(A *a, int n)
{
for (int i = 0; i < n; ++i)
{
a[i] = 0;
}
}
}
//-----------------------------------------------------------------------------
vtkDataArray *vtkChartXY::GetStackedPlotAccumulator(int dataType, int n)
{
if (!this->ChartPrivate->StackedPlotAccumulator)
{
this->ChartPrivate->StackedPlotAccumulator = vtkDataArray::SafeDownCast(vtkDataArray::CreateArray(dataType));
if (!this->ChartPrivate->StackedPlotAccumulator)
{
return NULL;
}
this->ChartPrivate->StackedPlotAccumulator->SetNumberOfTuples(n);
switch (dataType)
{
vtkTemplateMacro(
InitializeAccumulator(static_cast<VTK_TT*>(this->ChartPrivate->StackedPlotAccumulator->GetVoidPointer(0)),n));
}
return this->ChartPrivate->StackedPlotAccumulator;
}
else
{
if (this->ChartPrivate->StackedPlotAccumulator->GetDataType() != dataType)
{
vtkErrorMacro("DataType of Accumulator " << this->ChartPrivate->StackedPlotAccumulator->GetDataType() <<
"does not match request " << dataType);
return NULL;
}
if (this->ChartPrivate->StackedPlotAccumulator->GetNumberOfTuples() != n)
{
vtkErrorMacro("Number of tuples in Accumulator " << this->ChartPrivate->StackedPlotAccumulator->GetNumberOfTuples() <<
"does not match request " << n);
return NULL;
}
return this->ChartPrivate->StackedPlotAccumulator;
}
}
//-----------------------------------------------------------------------------
vtkTimeStamp vtkChartXY::GetStackParticipantsChanged()
{
return this->ChartPrivate->StackParticipantsChanged;
}
//-----------------------------------------------------------------------------
void vtkChartXY::SetStackPartipantsChanged()
{
this->ChartPrivate->StackParticipantsChanged.Modified();
}
//-----------------------------------------------------------------------------
bool vtkChartXY::Hit(const vtkContextMouseEvent &mouse)
......
......@@ -30,6 +30,7 @@ class vtkTable;
class vtkChartLegend;
class vtkTooltipItem;
class vtkContextMouseEvent;
class vtkDataArray;
class vtkChartXYPrivate; // Private class to keep my STL vector in...
class VTK_CHARTS_EXPORT vtkChartXY : public vtkChart
......@@ -144,6 +145,18 @@ public:
// Set the vtkContextScene for the item, always set for an item in a scene.
virtual void SetScene(vtkContextScene *scene);
// Description:
// Return the Stacked plot accumulator so that each vtkPlotStacked can
// use it to determine its base and contribute to the position of the next
// stacked plot.
vtkDataArray *GetStackedPlotAccumulator(int dataType, int n);
// Description:
// Timestamp identifying the last time the participants in a stacked plot
// have changed (either by being added or having their visibility change)
vtkTimeStamp GetStackParticipantsChanged();
void SetStackPartipantsChanged();
//BTX
protected:
vtkChartXY();
......
This diff is collapsed.
/*=========================================================================
Program: Visualization Toolkit
Module: vtkPlotPoints.h
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.
=========================================================================*/
// .NAME vtkPlotStacked - Class for drawing an stacked polygon plot
// given an X, Ybase, Yextent in a vtkTable.
//
// .SECTION Description
//
#ifndef __vtkPlotStacked_h
#define __vtkPlotStacked_h
#include "vtkPlot.h"
class vtkChartXY;
class vtkContext2D;
class vtkTable;
class vtkPoints2D;
class vtkStdString;
class vtkImageData;
class VTK_CHARTS_EXPORT vtkPlotStacked : public vtkPlot
{
public:
vtkTypeMacro(vtkPlotStacked, vtkPlot);
virtual void PrintSelf(ostream &os, vtkIndent indent);
// Description:
// Creates a Stacked Plot Object
static vtkPlotStacked *New();
// Description:
// Set the plot color
virtual void SetColor(unsigned char r, unsigned char g, unsigned char b,
unsigned char a);
virtual void SetColor(double r, double g, double b);
virtual void GetColor(double rgb[3]);
// Description:
// Perform any updates to the item that may be necessary before rendering.
// The scene should take care of calling this on all items before their
// Paint function is invoked.
virtual void Update();
// Description:
// Paint event for the Stacked plot, called whenever the chart needs to be drawn
virtual bool Paint(vtkContext2D *painter);
// Description:
// Paint legend event for the Stacked plot, called whenever the legend needs the
// plot items symbol/mark/line drawn. A rect is supplied with the lower left
// corner of the rect (elements 0 and 1) and with width x height (elements 2
// and 3). The plot can choose how to fill the space supplied.
virtual bool PaintLegend(vtkContext2D *painter, float rect[4]);
// Description:
// Get the bounds for this mapper as (Xmin,Xmax,Ymin,Ymax,Zmin,Zmax).
virtual void GetBounds(double bounds[4]);
//BTX
// Description:
// Function to query a plot for the nearest point to the specified coordinate.
virtual bool GetNearestPoint(const vtkVector2f& point,
const vtkVector2f& tolerance,
vtkVector2f* location);
// Description:
// Select all points in the specified rectangle.
virtual bool SelectPoints(const vtkVector2f& min, const vtkVector2f& max);
// Description:
// Set the parent, required to accumlate base points when positioning is implicit
virtual void SetParent(vtkChartXY *parent);
// Description:
// Make this plot visible or invisible
virtual void SetVisible(bool visible);
//BTX
protected:
vtkPlotStacked();
~vtkPlotStacked();
// Description:
// Update the table cache.
bool UpdateTableCache(vtkTable *table);
// Descript:
// For stacked plots the Extent data must be greater than (or equal to) the
// base data. Insure that this is true
void FixExtent();
// Description:
// Handle calculating the log of the x or y series if necessary. Should be
// called by UpdateTableCache once the data has been updated in Points.
void CalculateLogSeries();
// Description:
// Find all of the "bad points" in the series. This is mainly used to cache
// bad points for performance reasons, but could also be used plot the bad
// points in the future.
void FindBadPoints();
// Description:
// Calculate the bounds of the plot, ignoring the bad points.
void CalculateBounds(vtkPoints2D *points, vtkIdTypeArray *badPoints, double bounds[4]);
// Description:
// Store a well packed set of XY coordinates for the base of this series
vtkPoints2D *BasePoints;
// Description:
// Store a well packed set of XY coordinates for the extent of this series
vtkPoints2D *ExtentPoints;
// Description:
// Sorted points, used when searching for the nearest point.
class VectorPIMPL;
VectorPIMPL* Sorted;
// Description:
// An array containing the indices of all the "bad base points", meaning any x, y
// pair that has an infinity, -infinity or not a number value.
vtkIdTypeArray* BaseBadPoints;
// Description:
// An array containing the indices of all the "bad extent points", meaning any x, y
// pair that has an infinity, -infinity or not a number value.
vtkIdTypeArray* ExtentBadPoints;
// Description:
// The point cache is marked dirty until it has been initialized.
vtkTimeStamp BuildTime;
bool LogX, LogY;
// Description:
// The parent Chart of this Plot
vtkChartXY *Parent;
private:
vtkPlotStacked(const vtkPlotStacked &); // Not implemented.
void operator=(const vtkPlotStacked &); // Not implemented.
//ETX
};
#endif //__vtkPlotStacked_h
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