Commit 8558697a authored by Zack Galbreath's avatar Zack Galbreath Committed by Code Review
Browse files

Merge topic 'heatmap_legends' into master

a3761161 prevent segmentation fault
3eba81ad tests to cover new legends functionality
352f55b5 new class: vtkCategoryLegend
e0cd2a5e add scalar bar legend to vtkHeatmapItem
parents 48b0d395 a3761161
set(Module_SRCS
vtkAxis.cxx
vtkAxisExtended.cxx
vtkCategoryLegend.cxx
vtkChart.cxx
vtkChartHistogram2D.cxx
vtkChartLegend.cxx
......
......@@ -13,6 +13,7 @@ vtk_add_test_cxx(
TestAxes.cxx
TestBarGraph.cxx
TestBarGraphHorizontal.cxx
TestCategoryLegend.cxx
TestColorTransferFunction.cxx,-E80
TestChartDouble.cxx
TestChartMatrix.cxx
......
/*=========================================================================
Program: Visualization Toolkit
Module: TestCategoryLegend.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 "vtkCategoryLegend.h"
#include "vtkColorSeries.h"
#include "vtkLookupTable.h"
#include "vtkNew.h"
#include "vtkVariantArray.h"
#include "vtkRenderer.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkContextScene.h"
#include "vtkContextTransform.h"
#include "vtkContextView.h"
#include "vtkRegressionTestImage.h"
//----------------------------------------------------------------------------
int TestCategoryLegend(int argc, char* argv[])
{
vtkNew<vtkVariantArray> values;
values->InsertNextValue(vtkVariant("a"));
values->InsertNextValue(vtkVariant("b"));
values->InsertNextValue(vtkVariant("c"));
vtkNew<vtkLookupTable> lut;
for (int i = 0; i < values->GetNumberOfTuples(); ++i)
{
lut->SetAnnotation(values->GetValue(i), values->GetValue(i).ToString());
}
vtkNew<vtkColorSeries> colorSeries;
colorSeries->SetColorScheme(vtkColorSeries::BREWER_QUALITATIVE_SET3);
colorSeries->BuildLookupTable(lut.GetPointer());
vtkNew<vtkCategoryLegend> legend;
legend->SetScalarsToColors(lut.GetPointer());
legend->SetValues(values.GetPointer());
legend->SetTitle("legend");
vtkNew<vtkContextTransform> trans;
trans->SetInteractive(true);
trans->AddItem(legend.GetPointer());
trans->Translate(180, 70);
vtkNew<vtkContextView> contextView;
contextView->GetScene()->AddItem(trans.GetPointer());
contextView->GetRenderer()->SetBackground(1.0, 1.0, 1.0);
contextView->GetRenderWindow()->SetSize(300,200);
contextView->GetRenderWindow()->SetMultiSamples(0);
contextView->GetRenderWindow()->Render();
int retVal = vtkRegressionTestImage(contextView->GetRenderWindow());
if (retVal == vtkRegressionTester::DO_INTERACTOR)
{
contextView->GetRenderWindow()->Render();
contextView->GetInteractor()->Start();
retVal = vtkRegressionTester::PASSED;
}
return !retVal;
}
/*=========================================================================
Program: Visualization Toolkit
Module: vtkCategoryLegend.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 "vtkBrush.h"
#include "vtkCategoryLegend.h"
#include "vtkContext2D.h"
#include "vtkObjectFactory.h"
#include "vtkScalarsToColors.h"
#include "vtkTextProperty.h"
#include "vtkVariantArray.h"
//-----------------------------------------------------------------------------
vtkStandardNewMacro(vtkCategoryLegend);
//-----------------------------------------------------------------------------
vtkCategoryLegend::vtkCategoryLegend()
{
this->SetInline(false);
this->SetHorizontalAlignment(vtkChartLegend::RIGHT);
this->SetVerticalAlignment(vtkChartLegend::BOTTOM);
this->ScalarsToColors = NULL;
this->Values = NULL;
this->TitleProperties->SetColor(this->LabelProperties->GetColor());
this->TitleProperties->SetFontSize(this->LabelProperties->GetFontSize());
this->TitleProperties->SetFontFamily(this->LabelProperties->GetFontFamily());
this->TitleProperties->SetJustificationToCentered();
this->TitleProperties->SetVerticalJustificationToTop();
this->TitleProperties->SetBold(1);
this->TitleWidthOffset = 0.0;
this->HasOutliers = false;
this->OutlierLabel = "outliers";
}
//-----------------------------------------------------------------------------
vtkCategoryLegend::~vtkCategoryLegend()
{
}
//-----------------------------------------------------------------------------
bool vtkCategoryLegend::Paint(vtkContext2D* painter)
{
if (!this->Visible || this->ScalarsToColors == NULL || this->Values == NULL)
{
return true;
}
// Draw a box around the legend.
painter->ApplyPen(this->Pen.GetPointer());
painter->ApplyBrush(this->Brush.GetPointer());
this->GetBoundingRect(painter);
painter->DrawRect(this->Rect.GetX(), this->Rect.GetY(),
this->Rect.GetWidth(), this->Rect.GetHeight());
// Draw the title (if any)
vtkVector2f stringBounds[2];
float titleWidth = 0.0;
float titleHeight = 0.0;
if (this->Title != "")
{
painter->ApplyTextProp(this->TitleProperties.GetPointer());
painter->ComputeStringBounds(this->Title, stringBounds->GetData());
titleWidth = stringBounds[1].GetX();
titleHeight = stringBounds[1].GetY() + this->Padding;
float x = this->Rect.GetX() + this->Rect.GetWidth() / 2.0;
float y = this->Rect.GetY() + this->Rect.GetHeight() - this->Padding;
painter->DrawString(x, y, this->Title);
}
painter->ApplyTextProp(this->LabelProperties.GetPointer());
// compute the height of a sample string.
// The height of this string will also be used as the size of
// the color marks.
painter->ComputeStringBounds("Tgyf", stringBounds->GetData());
float stringHeight = stringBounds[1].GetY();
// the starting X positions of our marks & labels
float markX = this->Rect.GetX() + this->TitleWidthOffset + this->Padding;
float labelX = markX + stringHeight + this->Padding;
// the Y value of the row that we're currently drawing
float y = this->Rect.GetY() + this->Rect.GetHeight() -
this->Padding - floor(stringHeight) - titleHeight;
// draw all of the marks & labels
for (vtkIdType l = 0; l < this->Values->GetNumberOfTuples(); ++l)
{
vtkStdString currentString = this->Values->GetValue(l).ToString();
if (currentString == "")
{
continue;
}
if (this->ScalarsToColors->GetAnnotatedValueIndex(
this->Values->GetValue(l)) == -1)
{
continue;
}
// paint the color mark for this category
double color[4];
this->ScalarsToColors->GetAnnotationColor(this->Values->GetValue(l), color);
painter->GetBrush()->SetColorF(color[0], color[1], color[2]);
painter->DrawRect(markX, y, stringHeight, stringHeight);
// draw this category's label
painter->DrawString(labelX, y, this->Values->GetValue(l).ToString());
// Move y position down another row
y -= stringHeight + this->Padding;
}
if (this->HasOutliers)
{
// paint the outlier color mark
double color[4];
this->ScalarsToColors->GetAnnotationColor(
this->ScalarsToColors->GetAnnotatedValue(-1), color);
painter->GetBrush()->SetColorF(color[0], color[1], color[2]);
painter->DrawRect(markX, y, stringHeight, stringHeight);
// draw the outlier label
painter->DrawString(labelX, y, this->OutlierLabel);
}
return true;
}
//-----------------------------------------------------------------------------
void vtkCategoryLegend::SetScalarsToColors(vtkScalarsToColors* stc)
{
this->ScalarsToColors = stc;
}
//-----------------------------------------------------------------------------
vtkScalarsToColors * vtkCategoryLegend::GetScalarsToColors()
{
return this->ScalarsToColors;
}
//-----------------------------------------------------------------------------
vtkRectf vtkCategoryLegend::GetBoundingRect(vtkContext2D *painter)
{
if (this->CacheBounds && this->RectTime > this->GetMTime() &&
this->RectTime > this->PlotTime &&
this->RectTime > this->ScalarsToColors->GetMTime() &&
this->RectTime > this->Values->GetMTime())
{
return this->Rect;
}
painter->ApplyTextProp(this->LabelProperties.GetPointer());
vtkVector2f stringBounds[2];
painter->ComputeStringBounds("Tgyf", stringBounds->GetData());
float height = stringBounds[1].GetY();
// programmatically set Padding here. This results in better
// appearance when we zoom in or out on the legend.
this->Padding = height / 4.0;
if (this->Padding < 1)
{
this->Padding = 1;
}
// Calculate size of title (if any)
float titleHeight = 0.0f;
float titleWidth = 0.0f;
if (this->Title != "")
{
painter->ApplyTextProp(this->TitleProperties.GetPointer());
painter->ComputeStringBounds(this->Title, stringBounds->GetData());
titleWidth = stringBounds[1].GetX();
titleHeight = stringBounds[1].GetY() + this->Padding;
painter->ApplyTextProp(this->LabelProperties.GetPointer());
}
// Calculate the widest legend label
float maxWidth = 0.0;
int numSkippedValues = 0;
this->TitleWidthOffset = 0.0;
this->HasOutliers = false;
for (vtkIdType l = 0; l < this->Values->GetNumberOfTuples(); ++l)
{
if (this->Values->GetValue(l).ToString() == "")
{
++numSkippedValues;
continue;
}
if (this->ScalarsToColors->GetAnnotatedValueIndex(
this->Values->GetValue(l)) == -1)
{
++numSkippedValues;
this->HasOutliers = true;
continue;
}
painter->ComputeStringBounds(this->Values->GetValue(l).ToString(),
stringBounds->GetData());
if (stringBounds[1].GetX() > maxWidth)
{
maxWidth = stringBounds[1].GetX();
}
}
// Calculate size of outlier label (if necessary)
if (this->HasOutliers)
{
painter->ComputeStringBounds(this->OutlierLabel,
stringBounds->GetData());
if (stringBounds[1].GetX() > maxWidth)
{
maxWidth = stringBounds[1].GetX();
}
}
if (titleWidth > maxWidth)
{
this->TitleWidthOffset = (titleWidth - maxWidth) / 2.0;
maxWidth = titleWidth;
}
int numLabels = this->Values->GetNumberOfTuples() - numSkippedValues;
if (this->HasOutliers)
{
++numLabels;
}
// 3 paddings: one on the left, one on the right, and one between the
// color mark and its label.
float w = ceil(maxWidth + 3 * this->Padding + height);
float h = ceil((numLabels * (height + this->Padding)) + this->Padding
+ titleHeight);
float x = floor(this->Point[0]);
float y = floor(this->Point[1]);
// Compute bottom left point based on current alignment.
if (this->HorizontalAlignment == vtkChartLegend::CENTER)
{
x -= w / 2.0;
}
else if (this->HorizontalAlignment == vtkChartLegend::RIGHT)
{
x -= w;
}
if (this->VerticalAlignment == vtkChartLegend::CENTER)
{
y -= h / 2.0;
}
else if (this->VerticalAlignment == vtkChartLegend::TOP)
{
y -= h;
}
this->Rect = vtkRectf(x, y, w, h);
this->RectTime.Modified();
return this->Rect;
}
//-----------------------------------------------------------------------------
void vtkCategoryLegend::SetTitle(const vtkStdString &title)
{
this->Title = title;
}
//-----------------------------------------------------------------------------
vtkStdString vtkCategoryLegend::GetTitle()
{
return this->Title;
}
/*=========================================================================
Program: Visualization Toolkit
Module: vtkCategoryLegend.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 vtkCategoryLegend - Legend item to display categorical data.
// .SECTION Description
// vtkCategoryLegend will display a label and color patch for each value
// in a categorical data set. To use this class, you must first populate
// a vtkScalarsToColors by using the SetAnnotation() method. The other
// input to this class is a vtkVariantArray. This should contain the
// annotated values from the vtkScalarsToColors that you wish to include
// within the legend.
#ifndef __vtkCategoryLegend_h
#define __vtkCategoryLegend_h
#include "vtkChartsCoreModule.h" // For export macro
#include "vtkChartLegend.h"
#include "vtkNew.h" // For vtkNew ivars
#include "vtkStdString.h" // For vtkStdString ivars
#include "vtkVector.h" // For vtkRectf
class vtkScalarsToColors;
class vtkTextProperty;
class vtkVariantArray;
class VTKCHARTSCORE_EXPORT vtkCategoryLegend: public vtkChartLegend
{
public:
vtkTypeMacro(vtkCategoryLegend, vtkChartLegend);
static vtkCategoryLegend* New();
// Description:
// Enum of legend orientation types
enum {
VERTICAL = 0,
HORIZONTAL
};
// Description:
// Paint the legend into a rectangle defined by the bounds.
virtual bool Paint(vtkContext2D *painter);
// Description:
// Compute and return the lower left corner of this legend, along
// with its width and height.
virtual vtkRectf GetBoundingRect(vtkContext2D* painter);
// Description:
// Get/Set the vtkScalarsToColors used to draw this legend.
// Since this legend represents categorical data, this
// vtkScalarsToColors must have been populated using SetAnnotation().
virtual void SetScalarsToColors(vtkScalarsToColors* stc);
virtual vtkScalarsToColors * GetScalarsToColors();
// Description:
// Get/Set the array of values that will be represented by this legend.
// This array must contain distinct annotated values from the ScalarsToColors.
// Each value in this array will be drawn as a separate entry within this
// legend.
vtkGetMacro(Values, vtkVariantArray*);
vtkSetMacro(Values, vtkVariantArray*);
// Description:
// Get/set the title text of the legend.
virtual void SetTitle(const vtkStdString &title);
virtual vtkStdString GetTitle();
// Description:
// Get/set the label to use for outlier data.
vtkGetMacro(OutlierLabel, vtkStdString);
vtkSetMacro(OutlierLabel, vtkStdString);
protected:
vtkCategoryLegend();
virtual ~vtkCategoryLegend();
bool HasOutliers;
float TitleWidthOffset;
vtkScalarsToColors* ScalarsToColors;
vtkStdString OutlierLabel;
vtkStdString Title;
vtkNew<vtkTextProperty> TitleProperties;
vtkVariantArray* Values;
private:
vtkCategoryLegend(const vtkCategoryLegend &); // Not implemented.
void operator=(const vtkCategoryLegend &); // Not implemented.
};
#endif
......@@ -75,6 +75,7 @@ vtkChartLegend::vtkChartLegend()
this->Inline = true;
this->Button = -1;
this->DragEnabled = true;
this->CacheBounds = true;
}
//-----------------------------------------------------------------------------
......@@ -170,7 +171,8 @@ bool vtkChartLegend::Paint(vtkContext2D *painter)
//-----------------------------------------------------------------------------
vtkRectf vtkChartLegend::GetBoundingRect(vtkContext2D *painter)
{
if (this->RectTime > this->GetMTime() && this->RectTime > this->PlotTime)
if (this->CacheBounds && this->RectTime > this->GetMTime() &&
this->RectTime > this->PlotTime)
{
return this->Rect;
}
......@@ -281,10 +283,14 @@ vtkChart* vtkChartLegend::GetChart()
//-----------------------------------------------------------------------------
bool vtkChartLegend::Hit(const vtkContextMouseEvent &mouse)
{
if (this->DragEnabled && mouse.GetScreenPos().GetX() > this->Rect.GetX() &&
mouse.GetScreenPos().GetX() < this->Rect.GetX() + this->Rect.GetWidth() &&
mouse.GetScreenPos().GetY() > this->Rect.GetY() &&
mouse.GetScreenPos().GetY() < this->Rect.GetY() + this->Rect.GetHeight())
if (!this->GetVisible())
{
return false;
}
if (this->DragEnabled && mouse.GetPos().GetX() > this->Rect.GetX() &&
mouse.GetPos().GetX() < this->Rect.GetX() + this->Rect.GetWidth() &&
mouse.GetPos().GetY() > this->Rect.GetY() &&
mouse.GetPos().GetY() < this->Rect.GetY() + this->Rect.GetHeight())
{
return true;
}
......@@ -299,10 +305,10 @@ bool vtkChartLegend::MouseMoveEvent(const vtkContextMouseEvent &mouse)
{
if (this->Button == vtkContextMouseEvent::LEFT_BUTTON)
{
vtkVector2f delta = mouse.GetScenePos() - mouse.GetLastScenePos();
this->HorizontalAlignment = vtkChartLegend::CUSTOM;
vtkVector2f delta = mouse.GetPos() - mouse.GetLastPos();
this->Storage->Point = this->Storage->Point + delta;
this->GetScene()->SetDirty(true);
this->Modified();
}
return true;
}
......
......@@ -161,6 +161,17 @@ public:
// Get the vtkTextProperty for the legend's labels.
vtkTextProperty * GetLabelProperties();
// Description:
// Toggle whether or not this legend should attempt to cache its position
// and size. The default value is true. If this value is set to false,
// the legend will recalculate its position and bounds every time it is
// drawn. If users will be able to zoom in or out on your legend, you
// may want to set this to false. Otherwise, the border around the legend
// may not resize appropriately.
vtkSetMacro(CacheBounds, bool);
vtkGetMacro(CacheBounds, bool);
vtkBooleanMacro(CacheBounds, bool);
// Description:
// Return true if the supplied x, y coordinate is inside the item.
virtual bool Hit(const vtkContextMouseEvent &mouse);
......@@ -201,6 +212,11 @@ protected:
// Should we move the legend box around in response to the mouse drag?
bool DragEnabled;
// Description:
// Should the legend attempt to avoid recalculating its position &
// bounds unnecessarily?
bool CacheBounds;
// Description:
// Last button to be pressed.
int Button;
......
......@@ -45,7 +45,12 @@ vtkColorLegend::vtkColorLegend()
this->Callback->SetClientData(this);
this->Callback->SetCallback(vtkColorLegend::OnScalarsToColorsModified);
this->TransferFunction = 0;
this->TransferFunction = NULL;
this->Orientation = vtkColorLegend::VERTICAL;
this->CustomPositionSet = false;
this->DrawBorder = false;
}
//-----------------------------------------------------------------------------
......@@ -85,12 +90,46 @@ void vtkColorLegend::Update()
{
this->ComputeTexture();
}
// check if the range of our TransferFunction changed
double bounds[4];
this->GetBounds(bounds);
if (bounds[0] == bounds[1])
{
vtkWarningMacro(<< "The color transfer function seems to be empty.");
this->Axis->Update();
return;
}
double axisBounds[2];
this->Axis->GetUnscaledRange(axisBounds);
if (bounds[0] != axisBounds[0] || bounds[1] != axisBounds[1])
{
this->Axis->SetUnscaledRange(bounds[0], bounds[1]);
}
this->Axis->Update();