Commit 6c877c2f authored by Utkarsh Ayachit's avatar Utkarsh Ayachit

Adding area plot (vtkPlotArea).

Adding a new plot to plot a region named, vtkPlotArea. This can be used
to plot a region enclosed between two series.

Also added a new test, TestAreaPlot, to test this new plot.
parent 1093ae99
......@@ -24,6 +24,7 @@ set(Module_SRCS
vtkPiecewisePointHandleItem.cxx
vtkPlot.cxx
vtkPlot3D.cxx
vtkPlotArea.cxx
vtkPlotBag.cxx
vtkPlotBar.cxx
vtkPlotBox.cxx
......
......@@ -40,6 +40,7 @@ vtk_add_test_cxx(${vtk-module}CxxTests tests
TestContextScene.cxx,NO_DATA,NO_VALID
TestControlPointsItem.cxx,NO_DATA,NO_VALID
TestControlPointsItemEvents.cxx,NO_DATA,NO_VALID
TestAreaPlot.cxx
TestAxes.cxx
TestBagPlot.cxx
TestBarGraph.cxx
......
/*=========================================================================
Program: Visualization Toolkit
Module: TestAreaPlot.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 "vtkAxis.h"
#include "vtkBrush.h"
#include "vtkCharArray.h"
#include "vtkChartXY.h"
#include "vtkContextScene.h"
#include "vtkContextView.h"
#include "vtkFloatArray.h"
#include "vtkNew.h"
#include "vtkPlotArea.h"
#include "vtkPlot.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkSmartPointer.h"
#include "vtkTable.h"
#include <algorithm>
//----------------------------------------------------------------------------
int TestAreaPlot( int, char * [] )
{
// Set up a 2D scene, add an XY chart to it
vtkNew<vtkContextView> view;
view->GetRenderWindow()->SetSize(400, 300);
vtkNew<vtkChartXY> chart;
view->GetScene()->AddItem(chart.GetPointer());
// Create a table with some points in it...
vtkNew<vtkTable> table;
vtkNew<vtkFloatArray> arrX;
arrX->SetName("X Axis");
table->AddColumn(arrX.GetPointer());
vtkNew<vtkFloatArray> arrC;
arrC->SetName("Cosine");
table->AddColumn(arrC.GetPointer());
vtkNew<vtkFloatArray> arrS;
arrS->SetName("Sine");
table->AddColumn(arrS.GetPointer());
vtkNew<vtkFloatArray> arrS2;
arrS2->SetName("Sine2");
table->AddColumn(arrS2.GetPointer());
vtkNew<vtkFloatArray> arrS3;
arrS3->SetName("Sine3");
table->AddColumn(arrS3.GetPointer());
vtkNew<vtkFloatArray> arr1;
arr1->SetName("One");
table->AddColumn(arr1.GetPointer());
vtkNew<vtkCharArray> validMask;
validMask->SetName("ValidMask");
table->AddColumn(validMask.GetPointer());
// Test charting with a few more points...
int numPoints = 69;
float inc = 7.5 / (numPoints-1);
table->SetNumberOfRows(numPoints);
for (int i = 0; i < numPoints; ++i)
{
table->SetValue(i, 0, i * inc + 0.01);
table->SetValue(i, 1, cos(i * inc) + 0.01);
table->SetValue(i, 2, sin(i * inc) + 0.01);
table->SetValue(i, 3, sin(i * inc) + 0.5);
table->SetValue(i, 4, sin(i * inc) * sin(i * inc) + 0.01);
table->SetValue(i, 5, 1.0);
validMask->SetValue(i, (i > 30 && i < 40) ? 0 : 1);
}
// Add multiple line plots, setting the colors etc
vtkPlotArea* area = vtkPlotArea::SafeDownCast(chart->AddPlot(vtkChart::AREA));
area->SetInputData(table.GetPointer());
area->SetInputArray(0, "X Axis");
area->SetInputArray(1, "Sine");
area->SetInputArray(2, "Sine2");
area->SetValidPointMaskName("ValidMask");
area->GetBrush()->SetColorF(0.5, 0.5, 0.5, 0.5);
chart->GetAxis(vtkAxis::LEFT)->SetLogScale(true);
// Render the scene and compare the image to a reference image
view->GetRenderWindow()->SetMultiSamples(0);
view->GetInteractor()->Initialize();
view->GetInteractor()->Start();
return EXIT_SUCCESS;
}
......@@ -53,7 +53,8 @@ public:
BAR,
STACKED,
BAG,
FUNCTIONALBAG};
FUNCTIONALBAG,
AREA};
// Description:
// Enum of valid chart action types.
......
......@@ -32,6 +32,7 @@
#include "vtkVector.h"
#include "vtkVectorOperators.h"
#include "vtkPlotArea.h"
#include "vtkPlotBar.h"
#include "vtkPlotBag.h"
#include "vtkPlotFunctionalBag.h"
......@@ -389,6 +390,7 @@ bool vtkChartXY::Paint(vtkContext2D *painter)
}
this->UpdateLayout(painter);
// Recalculate the plot transform, min and max values if necessary
if (!this->PlotTransformValid)
{
......@@ -400,6 +402,14 @@ bool vtkChartXY::Paint(vtkContext2D *painter)
this->RecalculatePlotTransforms();
}
// Now that plot transforms, including whether to use log scaling and the
// shift-scale factors, have been updated, we give the vtkPlot instances an
// opportunity to update caches.
for (size_t i = 0; i < this->ChartPrivate->plots.size(); ++i)
{
this->ChartPrivate->plots[i]->UpdateCache();
}
// Update the clipping if necessary
this->ChartPrivate->Clip->SetClip(this->Point1[0], this->Point1[1],
this->Point2[0]-this->Point1[0],
......@@ -1150,6 +1160,14 @@ vtkPlot * vtkChartXY::AddPlot(int type)
plot = bag;
break;
}
case AREA:
{
vtkPlotArea* area = vtkPlotArea::New();
area->SetParent(this);
area->GetBrush()->SetColor(color.GetData());
plot = area;
break;
}
default:
plot = NULL;
......
......@@ -281,6 +281,13 @@ public:
return this->GetBounds(bounds);
}
// Description:
// Subclasses that build data caches to speed up painting should override this
// method to update such caches. This is called on each Paint, hence
// subclasses must add checks to avoid rebuilding of cache, unless necessary.
// Default implementation is empty.
virtual void UpdateCache() {}
//BTX
// Description:
// A General setter/getter that should be overridden. It can silently drop
......
/*=========================================================================
Program: Visualization Toolkit
Module: vtkPlotArea.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 "vtkPlotArea.h"
#include "vtkAxis.h"
#include "vtkBoundingBox.h"
#include "vtkBrush.h"
#include "vtkCharArray.h"
#include "vtkContext2D.h"
#include "vtkContextMapper2D.h"
#include "vtkDataArrayIteratorMacro.h"
#include "vtkFloatArray.h"
#include "vtkMath.h"
#include "vtkNew.h"
#include "vtkObjectFactory.h"
#include "vtkPen.h"
#include "vtkPoints2D.h"
#include "vtkTable.h"
#include "vtkWeakPointer.h"
#include "vtkVectorOperators.h"
#include <vector>
#include <set>
#include <cmath>
#include <algorithm>
namespace
{
// PIMPL for STL vector...
struct vtkIndexedVector2f
{
size_t index;
vtkVector2f pos;
static bool compVector3fX(
const vtkIndexedVector2f& v1, const vtkIndexedVector2f& v2)
{
if (v1.pos.GetX() < v2.pos.GetX())
{
return true;
}
else
{
return false;
}
}
// See if the point is within tolerance.
static bool inRange(const vtkVector2f& point, const vtkVector2f& tol,
const vtkVector2f& current)
{
if (current.GetX() > point.GetX() - tol.GetX() && current.GetX() < point.GetX() + tol.GetX() &&
current.GetY() > point.GetY() - tol.GetY() && current.GetY() < point.GetY() + tol.GetY())
{
return true;
}
else
{
return false;
}
}
};
class VectorPIMPL : public std::vector<vtkIndexedVector2f>
{
public:
void Initialize(vtkVector2f* array, size_t n)
{
this->reserve(n);
for (size_t i = 0; i < n; ++i)
{
vtkIndexedVector2f tmp;
tmp.index = i;
tmp.pos = array[i];
this->push_back(tmp);
}
}
//-----------------------------------------------------------------------------
vtkIdType GetNearestPoint(
const vtkVector2f& point, const vtkVector2f& tol, vtkVector2f* location)
{
// Set up our search array, use the STL lower_bound algorithm
VectorPIMPL::iterator low;
VectorPIMPL &v = *this;
// Get the lowest point we might hit within the supplied tolerance
vtkIndexedVector2f lowPoint;
lowPoint.index = 0;
lowPoint.pos = vtkVector2f(point.GetX()-tol.GetX(), 0.0f);
low = std::lower_bound(v.begin(), v.end(), lowPoint, vtkIndexedVector2f::compVector3fX);
// Now consider the y axis
float highX = point.GetX() + tol.GetX();
while (low != v.end())
{
if (vtkIndexedVector2f::inRange(point, tol, (*low).pos))
{
*location = (*low).pos;
return static_cast<int>((*low).index);
}
else if (low->pos.GetX() > highX)
{
break;
}
++low;
}
return -1;
}
};
inline bool vtkIsBadPoint(const vtkVector2f& vec)
{
return (vtkMath::IsNan(vec.GetX()) || vtkMath::IsInf(vec.GetX()) ||
vtkMath::IsNan(vec.GetY()) || vtkMath::IsInf(vec.GetY()));
}
} // end of namespace
// Keeps all data-dependent meta-data that's updated in
// vtkPlotArea::Update.
class vtkPlotArea::vtkTableCache
{
vtkTimeStamp DataMTime;
vtkTimeStamp BoundsMTime;
// Unscaled data bounds.
vtkBoundingBox DataBounds;
vtkRectd ShiftScale;
vtkTuple<double, 2> GetDataRange(vtkDataArray* array)
{
assert(array);
if (this->ValidPointMask)
{
assert(array->GetNumberOfTuples() == this->ValidPointMask->GetNumberOfTuples());
switch (array->GetDataType())
{
vtkDataArrayIteratorMacro(array,
return this->ComputeArrayRange(vtkDABegin, vtkDAEnd, this->ValidPointMask));
}
}
else
{
switch (array->GetDataType())
{
vtkDataArrayIteratorMacro(array,
return this->ComputeArrayRange(vtkDABegin, vtkDAEnd));
}
}
vtkTuple<double, 2> range;
range[0] = VTK_DOUBLE_MAX;
range[1] = VTK_DOUBLE_MIN;
return range;
}
template <class Iterator>
vtkTuple<double, 2> ComputeArrayRange(Iterator begin, Iterator end, vtkCharArray* mask)
{
vtkTuple<double, 2> range;
range[0] = VTK_DOUBLE_MAX;
range[1] = VTK_DOUBLE_MIN;
vtkIdType index=0;
for (Iterator iter=begin; iter != end; ++iter, ++index)
{
if (mask->GetValue(index) != 0)
{
range[0] = std::min(range[0], static_cast<double>(*iter));
range[1] = std::max(range[1], static_cast<double>(*iter));
}
}
return range;
}
template <class Iterator>
vtkTuple<double, 2> ComputeArrayRange(Iterator begin, Iterator end)
{
vtkTuple<double, 2> range;
range[0] = VTK_DOUBLE_MAX;
range[1] = VTK_DOUBLE_MIN;
vtkIdType index=0;
for (Iterator iter=begin; iter != end; ++iter, ++index)
{
range[0] = std::min(range[0], static_cast<double>(*iter));
range[1] = std::max(range[1], static_cast<double>(*iter));
}
return range;
}
template <class Iterator>
void CopyToPoints(
float* data, int data_increment, Iterator begin, Iterator end, const vtkVector2d& ss, bool useLog)
{
if (useLog)
{
for (Iterator iter = begin; iter != end; ++iter, data+= data_increment)
{
*data = log10(static_cast<float>((*iter + ss[0]) * ss[1]));
}
}
else
{
for (Iterator iter = begin; iter != end; ++iter, data+= data_increment)
{
*data = static_cast<float>((*iter + ss[0]) * ss[1]);
}
}
}
template <class T>
class RangeIterator
{
T Value;
public:
RangeIterator(const T& val) : Value(val)
{}
T operator*() const
{ return this->Value; }
bool operator==(const RangeIterator& other) const
{ return this->Value == other.Value; }
bool operator!=(const RangeIterator& other) const
{ return this->Value != other.Value; }
RangeIterator<T>& operator++()
{ ++this->Value; return (*this); }
};
public:
// Array which marks valid points in the array. If NULL (the default), all
// points in the input array are considered valid.
vtkWeakPointer<vtkCharArray> ValidPointMask;
// References to input arrays.
vtkTuple<vtkWeakPointer<vtkDataArray>, 3> InputArrays;
// Array for the points. These maintain the points that form the QuadStrip for
// the area plot.
vtkNew<vtkPoints2D> Points;
// Set of point ids that are invalid or masked out.
std::vector<vtkIdType> BadPoints;
VectorPIMPL SortedPoints;
vtkTableCache()
{
this->Reset();
}
void Reset()
{
this->ValidPointMask = NULL;
this->Points->Initialize();
this->Points->SetDataTypeToFloat();
this->BadPoints.clear();
}
bool IsInputDataValid() const
{
return this->InputArrays[1] != NULL && this->InputArrays[2] != NULL;
}
bool SetPoints(vtkDataArray* x, vtkDataArray* y1, vtkDataArray* y2)
{
if (y1 == NULL || y2 == NULL)
{
return false;
}
vtkIdType numTuples = y1->GetNumberOfTuples();
// sanity check.
assert((x == NULL || x->GetNumberOfTuples() == numTuples) && y2->GetNumberOfTuples() == numTuples);
this->InputArrays[0] = x;
this->InputArrays[1] = y1;
this->InputArrays[2] = y2;
this->Points->SetNumberOfPoints(numTuples * 2);
this->SortedPoints.clear();
this->DataMTime.Modified();
return true;
}
void GetDataBounds(double bounds[4])
{
if (this->DataMTime > this->BoundsMTime)
{
vtkTuple<double, 2> rangeX, rangeY1, rangeY2;
if (this->InputArrays[0])
{
rangeX = this->GetDataRange(this->InputArrays[0]);
}
else
{
rangeX[0] = 0; rangeX[1] = (this->Points->GetNumberOfPoints()/2-1);
}
rangeY1 = this->GetDataRange(this->InputArrays[1]);
rangeY2 = this->GetDataRange(this->InputArrays[2]);
this->DataBounds.Reset();
this->DataBounds.SetMinPoint(rangeX[0], std::min(rangeY1[0], rangeY2[0]), 0);
this->DataBounds.SetMaxPoint(rangeX[1], std::max(rangeY1[1], rangeY2[1]), 0);
this->BoundsMTime.Modified();
}
double bds[6];
this->DataBounds.GetBounds(bds);
std::copy(bds, bds+4, bounds);
}
void UpdateCache(vtkPlotArea* self)
{
const vtkRectd& ss = self->GetShiftScale();
vtkAxis* xaxis = self->GetXAxis();
vtkAxis* yaxis = self->GetYAxis();
if (this->Points->GetMTime()> this->DataMTime &&
this->Points->GetMTime() > xaxis->GetMTime() &&
this->Points->GetMTime() > yaxis->GetMTime() &&
ss == this->ShiftScale)
{
// nothing to do.
return;
}
vtkTuple<bool, 2> useLog;
useLog[0] = xaxis->GetLogScaleActive();
useLog[1] = yaxis->GetLogScaleActive();
vtkIdType numTuples = this->InputArrays[1]->GetNumberOfTuples();
assert(this->Points->GetNumberOfPoints() == 2*numTuples);
float* data = reinterpret_cast<float*>(this->Points->GetVoidPointer(0));
if (this->InputArrays[0])
{
switch (this->InputArrays[0]->GetDataType())
{
vtkDataArrayIteratorMacro(this->InputArrays[0],
this->CopyToPoints(
data, 4, vtkDABegin, vtkDAEnd, vtkVector2d(ss[0], ss[2]), useLog[0]);
this->CopyToPoints(
&data[2], 4, vtkDABegin, vtkDAEnd, vtkVector2d(ss[0], ss[2]), useLog[0]);
);
}
}
else
{
this->CopyToPoints(
data, 4, RangeIterator<vtkIdType>(0), RangeIterator<vtkIdType>(numTuples),
vtkVector2d(ss[0], ss[2]), useLog[0]);
this->CopyToPoints(
&data[2], 4, RangeIterator<vtkIdType>(0), RangeIterator<vtkIdType>(numTuples),
vtkVector2d(ss[0], ss[2]), useLog[0]);
}
switch (this->InputArrays[1]->GetDataType())
{
vtkDataArrayIteratorMacro(this->InputArrays[1],
this->CopyToPoints(
&data[1], 4, vtkDABegin, vtkDAEnd, vtkVector2d(ss[1], ss[3]), useLog[1]));
}
switch (this->InputArrays[2]->GetDataType())
{
vtkDataArrayIteratorMacro(this->InputArrays[2],
this->CopyToPoints(
&data[3], 4, vtkDABegin, vtkDAEnd, vtkVector2d(ss[1], ss[3]), useLog[1]));
}
// Set the bad-points mask.
vtkVector2f* vec2f = reinterpret_cast<vtkVector2f*>(this->Points->GetVoidPointer(0));
for (vtkIdType cc=0; cc < numTuples; cc++)
{
bool is_bad = (this->ValidPointMask && this->ValidPointMask->GetValue(cc) == 0);
is_bad = is_bad || vtkIsBadPoint(vec2f[2*cc]);
is_bad = is_bad || vtkIsBadPoint(vec2f[2*cc + 1]);
if (is_bad)
{
// this ensures that the GetNearestPoint() fails for masked out points.
vec2f[2*cc] = vtkVector2f(vtkMath::Nan(), vtkMath::Nan());
vec2f[2*cc + 1] = vtkVector2f(vtkMath::Nan(), vtkMath::Nan());
this->BadPoints.push_back(cc);
}
}
this->ShiftScale = ss;
this->Points->Modified();
this->SortedPoints.clear();
}
vtkIdType GetNearestPoint(
const vtkVector2f& point, const vtkVector2f& tol, vtkVector2f* location)
{
if (this->Points->GetNumberOfPoints() == 0)
{
return -1;
}
if (this->SortedPoints.size() == 0)
{
float* data = reinterpret_cast<float*>(this->Points->GetVoidPointer(0));
this->SortedPoints.Initialize(reinterpret_cast<vtkVector2f*>(data),
this->Points->GetNumberOfPoints());
std::sort(this->SortedPoints.begin(), this->SortedPoints.end(),
vtkIndexedVector2f::compVector3fX);
}
return this->SortedPoints.GetNearestPoint(point, tol, location);
}
};
vtkStandardNewMacro(vtkPlotArea);
//----------------------------------------------------------------------------
vtkPlotArea::vtkPlotArea()
: TableCache(new vtkPlotArea::vtkTableCache())
{
this->TooltipDefaultLabelFormat = "%l: %x:(%a, %b)";
}
//----------------------------------------------------------------------------
vtkPlotArea::~vtkPlotArea()
{
delete this->TableCache;
this->TableCache = NULL;
}
//----------------------------------------------------------------------------
void vtkPlotArea::Update()
{
if (!this->Visible)
{
return;
}
vtkTable* table = this->GetInput();
if (!table)
{