Commit 728192ea authored by Zack Galbreath's avatar Zack Galbreath
Browse files

Add a dendrogram for the columns of the heatmap

vtkTreeHeatmapItem now supports a dendrogram for the columns of the
heatmap.  This provides users with a way to interactively collapse
and expand columns.

The GetBounds() methods of vtkDendrogram, vtkHeatmapItem, and
vtkTreeHeatmapItem now include the size of any displayed text labels.
This required some changes to the way that the components of
vtkTanglegramItem are laid out.

Change-Id: Icdc9342d360b772819ff7863ed2a971e8865003e
parent 3c2603db
......@@ -57,6 +57,8 @@ vtkDendrogramItem::vtkDendrogramItem() : PositionVector(0, 0)
this->MaxX = 0.0;
this->MaxY = 0.0;
this->LabelWidth = 0.0;
this->NumberOfLeafNodes = 0;
this->MultiplierX = 100.0;
this->MultiplierY = 100.0;
......@@ -174,19 +176,20 @@ bool vtkDendrogramItem::Paint(vtkContext2D *painter)
return true;
}
this->PrepareToPaint();
this->PrepareToPaint(painter);
this->PaintBuffers(painter);
return true;
}
//-----------------------------------------------------------------------------
void vtkDendrogramItem::PrepareToPaint()
void vtkDendrogramItem::PrepareToPaint(vtkContext2D *painter)
{
if (this->IsDirty())
{
this->RebuildBuffers();
}
this->ComputeLabelWidth(painter);
}
//-----------------------------------------------------------------------------
......@@ -399,7 +402,6 @@ void vtkDendrogramItem::PaintBuffers(vtkContext2D *painter)
double xStart, yStart;
double sourcePoint[3];
double targetPoint[3];
double spacing = 25;
int numberOfCollapsedSubTrees = 0;
vtkUnsignedIntArray *vertexIsPruned = vtkUnsignedIntArray::SafeDownCast(
......@@ -564,119 +566,119 @@ void vtkDendrogramItem::PaintBuffers(vtkContext2D *painter)
}
}
// special case: all the true leaf nodes have been collapsed
if (this->NumberOfLeafNodes <= numberOfCollapsedSubTrees)
// the remainder of this function involves drawing the leaf node labels,
// so we can return now if that feature has been disabled.
if (!this->DrawLabels)
{
return;
}
// get array of node names from the tree
vtkStringArray *vertexNames = vtkStringArray::SafeDownCast(
this->LayoutTree->GetVertexData()->GetAbstractArray("node name"));
// leave a small amount of space between the tree and the vertex labels
spacing = this->LeafSpacing * 0.5;
// special case: all the true leaf nodes have been collapsed.
// This means that there aren't any labels left to draw.
if (this->NumberOfLeafNodes <= numberOfCollapsedSubTrees)
{
return;
}
//"Igq" selected for range of height
int fontSize = painter->ComputeFontSizeForBoundedString("Igq", VTK_FLOAT_MAX,
this->LeafSpacing);
bool canDrawText = true;
// make sure our current zoom level allows for a legibily-sized font
if (fontSize < 8)
{
canDrawText = false;
return;
}
if (this->DrawLabels)
// leave a small amount of space between the tree and the vertex labels
double spacing = this->LeafSpacing * 0.5;
// set up our text property to draw leaf node labels
painter->GetTextProp()->SetColor(0.0, 0.0, 0.0);
painter->GetTextProp()->SetJustificationToLeft();
painter->GetTextProp()->SetVerticalJustificationToCentered();
painter->GetTextProp()->SetOrientation(0);
painter->GetTextProp()->SetOrientation(
this->GetTextAngleForOrientation(orientation));
// make sure some of the labels would be visible on screen
switch (orientation)
{
// make sure some of the labels would be visible on screen
if (!canDrawText)
case vtkDendrogramItem::DOWN_TO_UP:
if (this->SceneBottomLeft[1] > this->MaxY + spacing ||
this->SceneTopRight[1] < this->MaxY + spacing)
{
return;
}
break;
case vtkDendrogramItem::RIGHT_TO_LEFT:
if (this->SceneBottomLeft[0] > this->MinX - spacing ||
this->SceneTopRight[0] < this->MinX - spacing)
{
return;
}
painter->GetTextProp()->SetJustificationToRight();
break;
case vtkDendrogramItem::UP_TO_DOWN:
if (this->SceneBottomLeft[1] > this->MinY - spacing ||
this->SceneTopRight[1] < this->MinY - spacing)
{
return;
}
painter->GetTextProp()->SetJustificationToRight();
break;
case vtkDendrogramItem::LEFT_TO_RIGHT:
default:
if (this->SceneBottomLeft[0] > this->MaxX + spacing ||
this->SceneTopRight[0] < this->MaxX + spacing)
{
return;
}
break;
}
// get array of node names from the tree
vtkStringArray *vertexNames = vtkStringArray::SafeDownCast(
this->LayoutTree->GetVertexData()->GetAbstractArray("node name"));
// find our leaf nodes & draw their labels
for (vtkIdType vertex = 0; vertex < this->LayoutTree->GetNumberOfVertices();
++vertex)
{
if (!this->LayoutTree->IsLeaf(vertex))
{
return;
continue;
}
// set up our text property to draw row names
painter->GetTextProp()->SetColor(0.0, 0.0, 0.0);
painter->GetTextProp()->SetJustificationToLeft();
painter->GetTextProp()->SetVerticalJustificationToCentered();
painter->GetTextProp()->SetOrientation(0);
double point[3];
this->LayoutTree->GetPoint(vertex, point);
switch (orientation)
{
case vtkDendrogramItem::DOWN_TO_UP:
if (this->SceneBottomLeft[1] > this->MaxY + spacing ||
this->SceneTopRight[1] < this->MaxY + spacing)
{
return;
}
xStart = this->Position[0] + point[0] * this->MultiplierX;
yStart = this->MaxY + spacing;
break;
case vtkDendrogramItem::RIGHT_TO_LEFT:
if (this->SceneBottomLeft[0] > this->MinX - spacing ||
this->SceneTopRight[0] < this->MinX - spacing)
{
return;
}
painter->GetTextProp()->SetJustificationToRight();
xStart = this->MinX - spacing;
yStart = this->Position[1] + point[1] * this->MultiplierY;
break;
case vtkDendrogramItem::UP_TO_DOWN:
if (this->SceneBottomLeft[1] > this->MinY - spacing ||
this->SceneTopRight[1] < this->MinY - spacing)
{
return;
}
painter->GetTextProp()->SetJustificationToRight();
xStart = this->Position[0] + point[0] * this->MultiplierX;
yStart = this->MinY - spacing;
break;
case vtkDendrogramItem::LEFT_TO_RIGHT:
default:
if (this->SceneBottomLeft[0] > this->MaxX + spacing ||
this->SceneTopRight[0] < this->MaxX + spacing)
{
return;
}
xStart = this->MaxX + spacing;
yStart = this->Position[1] + point[1] * this->MultiplierY;
break;
}
painter->GetTextProp()->SetOrientation(
this->GetTextAngleForOrientation(orientation));
for (vtkIdType vertex = 0; vertex < this->LayoutTree->GetNumberOfVertices();
++vertex)
std::string vertexName = vertexNames->GetValue(vertex);
if (this->SceneBottomLeft[0] < xStart &&
this->SceneTopRight[0] > xStart &&
this->SceneBottomLeft[1] < yStart &&
this->SceneTopRight[1] > yStart)
{
if (!this->LayoutTree->IsLeaf(vertex))
{
continue;
}
double point[3];
this->LayoutTree->GetPoint(vertex, point);
switch (orientation)
{
case vtkDendrogramItem::DOWN_TO_UP:
xStart = this->Position[0] + point[0] * this->MultiplierX;
yStart = this->MaxY + spacing;
break;
case vtkDendrogramItem::RIGHT_TO_LEFT:
xStart = this->MinX - spacing;
yStart = this->Position[1] + point[1] * this->MultiplierY;
break;
case vtkDendrogramItem::UP_TO_DOWN:
xStart = this->Position[0] + point[0] * this->MultiplierX;
yStart = this->MinY - spacing;
break;
case vtkDendrogramItem::LEFT_TO_RIGHT:
default:
xStart = this->MaxX + spacing;
yStart = this->Position[1] + point[1] * this->MultiplierY;
break;
}
std::string vertexName = vertexNames->GetValue(vertex);
if (this->SceneBottomLeft[0] < xStart &&
this->SceneTopRight[0] > xStart &&
this->SceneBottomLeft[1] < yStart &&
this->SceneTopRight[1] > yStart)
{
painter->DrawString(xStart, yStart, vertexName);
}
painter->DrawString(xStart, yStart, vertexName);
}
}
}
......@@ -1294,17 +1296,55 @@ void vtkDendrogramItem::GetBounds(double bounds[4])
bounds[1] = this->MaxX;
bounds[2] = this->MinY;
bounds[3] = this->MaxY;
if (this->LabelWidth == 0.0)
{
return;
}
double spacing = this->LeafSpacing * 0.5;
switch (this->GetOrientation())
{
case vtkDendrogramItem::LEFT_TO_RIGHT:
default:
bounds[1] += spacing + this->LabelWidth;
break;
case vtkDendrogramItem::UP_TO_DOWN:
bounds[2] -= spacing + this->LabelWidth;
break;
case vtkDendrogramItem::RIGHT_TO_LEFT:
bounds[0] -= spacing + this->LabelWidth;
break;
case vtkDendrogramItem::DOWN_TO_UP:
bounds[3] += spacing + this->LabelWidth;
break;
}
}
//-----------------------------------------------------------------------------
float vtkDendrogramItem::GetLabelWidth()
{
return this->LabelWidth;
}
//-----------------------------------------------------------------------------
float vtkDendrogramItem::GetLabelWidth(vtkContext2D *painter)
void vtkDendrogramItem::ComputeLabelWidth(vtkContext2D *painter)
{
int fontSize = painter->ComputeFontSizeForBoundedString("Igq", VTK_FLOAT_MAX,
this->LabelWidth = 0.0;
if (!this->DrawLabels)
{
return;
}
int fontSize = painter->ComputeFontSizeForBoundedString("Igq", VTK_FLOAT_MAX,
this->LeafSpacing);
if (fontSize < 8)
{
return 0.0;
}
if (fontSize < 8)
{
return;
}
// temporarily set text to default orientation
int orientation = painter->GetTextProp()->GetOrientation();
......@@ -1313,21 +1353,19 @@ float vtkDendrogramItem::GetLabelWidth(vtkContext2D *painter)
// get array of node names from the tree
vtkStringArray *vertexNames = vtkStringArray::SafeDownCast(
this->LayoutTree->GetVertexData()->GetAbstractArray("node name"));
double maxLength = 0.0;
float bounds[4];
for (vtkIdType i = 0; i < vertexNames->GetNumberOfTuples(); ++i)
{
painter->ComputeStringBounds(vertexNames->GetValue(i), bounds);
if (bounds[2] > maxLength)
if (bounds[2] > this->LabelWidth)
{
maxLength = bounds[2];
this->LabelWidth = bounds[2];
}
}
// restore orientation
painter->GetTextProp()->SetOrientation(orientation);
return maxLength;
}
//-----------------------------------------------------------------------------
......
......@@ -137,7 +137,7 @@ public:
// This function calls RebuildBuffers() if necessary.
// Once PrepareToPaint() has been called, GetBounds() is guaranteed
// to provide useful information.
void PrepareToPaint();
void PrepareToPaint(vtkContext2D *painter);
// Description:
// Get the bounds for this item as (Xmin,Xmax,Ymin,Ymax).
......@@ -145,9 +145,13 @@ public:
// PrepareToPaint() has been called.
virtual void GetBounds(double bounds[4]);
// Description:
// Compute the width of the longest leaf node label.
void ComputeLabelWidth(vtkContext2D *painter);
// Description:
// Get the width of the longest leaf node label.
float GetLabelWidth(vtkContext2D *painter);
float GetLabelWidth();
// Description:
// Find the position of the vertex with the specified name. Store
......@@ -155,6 +159,10 @@ public:
// was found, false otherwise.
bool GetPositionOfVertex(std::string vertexName, double position[2]);
// Description:
// Paints the input tree as a dendrogram.
virtual bool Paint(vtkContext2D *painter);
// this struct & class allow us to generate a priority queue of vertices.
struct WeightedVertex
{
......@@ -226,10 +234,6 @@ protected:
// Compute the bounds of our tree in pixel coordinates.
void ComputeBounds();
// Description:
// Paints the input tree as a dendrogram.
virtual bool Paint(vtkContext2D *painter);
// Description:
// Count the number of leaf nodes in the tree
void CountLeafNodes();
......@@ -307,6 +311,7 @@ private:
double MaxY;
double SceneBottomLeft[3];
double SceneTopRight[3];
float LabelWidth;
bool ColorTree;
bool ExtendLeafNodes;
bool DrawLabels;
......
This diff is collapsed.
......@@ -33,6 +33,7 @@
#include <set> // For blank row support
#include <vector> // For row mapping
class vtkBitArray;
class vtkLookupTable;
class vtkStringArray;
class vtkTable;
......@@ -99,6 +100,16 @@ public:
// Used by vtkTreeHeatmapItem to represent missing data.
void MarkRowAsBlank(std::string rowName);
// Description:
// Paints the table as a heatmap.
virtual bool Paint(vtkContext2D *painter);
// Description:
// Get the width of the largest row or column label drawn by this
// heatmap.
vtkGetMacro(RowLabelWidth, float);
vtkGetMacro(ColumnLabelWidth, float);
//BTX
// Description:
......@@ -146,10 +157,6 @@ protected:
// Generate a separate vtkLookupTable for each column in the table.
void InitializeLookupTables();
// Description:
// Paints the table as a heatmap.
virtual bool Paint(vtkContext2D *painter);
// Description:
// Helper function. Find the prominent, distinct values in the specified
// column of strings and add it to our "master list" of categorical values.
......@@ -186,6 +193,16 @@ protected:
// visible scene. Returns false otherwise.
bool LineIsVisible(double x0, double y0, double x1, double y1);
// Description:
// Compute the extent of the heatmap. This does not include
// the text labels.
void ComputeBounds();
// Description:
// Compute the width of our longest row label and the width of our
// longest column label. These values are used by GetBounds().
void ComputeLabelWidth(vtkContext2D *painter);
vtkSmartPointer<vtkTable> Table;
private:
......@@ -202,6 +219,7 @@ private:
std::map< vtkIdType, std::pair< double, double > > ColumnRanges;
std::vector< vtkIdType > SceneRowToTableRowMap;
std::vector< vtkIdType > SceneColumnToTableColumnMap;
std::set<std::string> BlankRows;
double MinX;
......@@ -210,6 +228,11 @@ private:
double MaxY;
double SceneBottomLeft[3];
double SceneTopRight[3];
float RowLabelWidth;
float ColumnLabelWidth;
vtkBitArray* CollapsedRowsArray;
vtkBitArray* CollapsedColumnsArray;
};
#endif
......@@ -115,6 +115,10 @@ bool vtkTanglegramItem::Paint(vtkContext2D *painter)
if (!this->TreeReordered)
{
this->ReorderTree();
// this will force Dendrogram2's PrunedTree to re-copy itself from the
// newly rearranged tree.
this->Dendrogram2->PrepareToPaint(painter);
}
if (!this->PositionSet)
......@@ -140,14 +144,14 @@ bool vtkTanglegramItem::Paint(vtkContext2D *painter)
//-----------------------------------------------------------------------------
void vtkTanglegramItem::RefreshBuffers(vtkContext2D *painter)
{
this->Dendrogram1->PrepareToPaint();
this->Dendrogram1->PrepareToPaint(painter);
this->Spacing = this->Dendrogram1->GetLeafSpacing();
this->Dendrogram1->GetBounds(this->Tree1Bounds);
this->LabelWidth1 = this->Dendrogram1->GetLabelWidth(painter);
this->LabelWidth1 = this->Dendrogram1->GetLabelWidth();
this->Dendrogram2->PrepareToPaint();
this->Dendrogram2->PrepareToPaint(painter);
this->Dendrogram2->GetBounds(this->Tree2Bounds);
this->LabelWidth2 = this->Dendrogram2->GetLabelWidth(painter);
this->LabelWidth2 = this->Dendrogram2->GetLabelWidth();
this->Tree1Names = vtkStringArray::SafeDownCast(
this->Dendrogram1->GetPrunedTree()->GetVertexData()->
......@@ -169,8 +173,6 @@ void vtkTanglegramItem::PositionTree2()
double averageY =
((abs(this->Tree1Bounds[3] - this->Tree1Bounds[2]) +
abs(this->Tree2Bounds[3] - this->Tree2Bounds[2])) / 2.0);
double extraSpacing =
this->Spacing + (this->LabelWidth1 + this->LabelWidth2) * 1.5;
// the starting X position for tree #2
double x, x1, x2;
......@@ -187,7 +189,7 @@ void vtkTanglegramItem::PositionTree2()
y = this->Tree1Bounds[3] +
abs(this->Tree2Bounds[3] - this->Tree2Bounds[2]) +
averageY + extraSpacing;
averageY;
break;
case vtkDendrogramItem::UP_TO_DOWN:
......@@ -197,14 +199,14 @@ void vtkTanglegramItem::PositionTree2()
y = this->Tree1Bounds[2] -
abs(this->Tree2Bounds[3] - this->Tree2Bounds[2]) -
averageY - extraSpacing;
averageY;
break;
case vtkDendrogramItem::RIGHT_TO_LEFT:
x = this->Tree1Bounds[0] -
abs(this->Tree2Bounds[1] - this->Tree2Bounds[0]) -
averageX - extraSpacing;
averageX;
y1 = (this->Tree1Bounds[3] + this->Tree1Bounds[2]) / 2.0;
y2 = (this->Tree2Bounds[3] + this->Tree2Bounds[2]) / 2.0;
......@@ -216,7 +218,7 @@ void vtkTanglegramItem::PositionTree2()
x = this->Tree1Bounds[1] +
abs(this->Tree2Bounds[1] - this->Tree2Bounds[0]) +
averageX + extraSpacing;
averageX;
y1 = (this->Tree1Bounds[3] + this->Tree1Bounds[2]) / 2.0;
y2 = (this->Tree2Bounds[3] + this->Tree2Bounds[2]) / 2.0;
......@@ -300,17 +302,15 @@ void vtkTanglegramItem::PaintCorrespondenceLines(vtkContext2D *painter)
float stringBounds[4];
painter->ComputeStringBounds(source, stringBounds);
sourcePosition[1] =
this->Tree1Bounds[3] + this->Spacing + stringBounds[2];
this->Tree1Bounds[3] - (this->LabelWidth1 - stringBounds[2]);
sourceEdgePosition[1] =
this->Tree1Bounds[3] + this->Spacing + this->LabelWidth1;
sourceEdgePosition[1] = this->Tree1Bounds[3] + this->Spacing;
targetEdgePosition[1] =
this->Tree2Bounds[2] - this->Spacing - this->LabelWidth2;
targetEdgePosition[1] = this->Tree2Bounds[2] - this->Spacing;
painter->ComputeStringBounds(target, stringBounds);
targetPosition[1] =
this->Tree2Bounds[2] - this->Spacing - stringBounds[2];
this->Tree2Bounds[2] + (this->LabelWidth2 - stringBounds[2]);
}
break;
......@@ -325,17 +325,15 @@ void vtkTanglegramItem::PaintCorrespondenceLines(vtkContext2D *painter)
float stringBounds[4];
painter->ComputeStringBounds(source, stringBounds);
sourcePosition[1] =
this->Tree1Bounds[2] - this->Spacing - stringBounds[2];
this->Tree1Bounds[2] + (this->LabelWidth1 - stringBounds[2]);
sourceEdgePosition[1] =
this->Tree1Bounds[2] - this->Spacing - this->LabelWidth1;
sourceEdgePosition[1] = this->Tree1Bounds[2] - this->Spacing;
targetEdgePosition[1] =
this->Tree2Bounds[3] + this->Spacing + this->LabelWidth2;
targetEdgePosition[1] = this->Tree2Bounds[3] + this->Spacing;
painter->ComputeStringBounds(target, stringBounds);
targetPosition[1] =
this->Tree2Bounds[3] + this->Spacing + stringBounds[2];
this->Tree2Bounds[3] - (this->LabelWidth2 - stringBounds[2]);
}
break;
......@@ -350,17 +348,15 @@ void vtkTanglegramItem::PaintCorrespondenceLines(vtkContext2D *painter)
float stringBounds[4];
painter->ComputeStringBounds(source, stringBounds);
sourcePosition[0] =
this->Tree1Bounds[0] - this->Spacing - stringBounds[2];
this->Tree1Bounds[0] + (this->LabelWidth1 - stringBounds[2]);
sourceEdgePosition[0] =
this->Tree1Bounds[0] - this->Spacing - this->LabelWidth1;
sourceEdgePosition[0] = this->Tree1Bounds[0] - this->Spacing;
targetEdgePosition[0] =
this->Tree2Bounds[1] + this->Spacing + this->LabelWidth2;
targetEdgePosition[0] = this->Tree2Bounds[1] + this->Spacing;
painter->ComputeStringBounds(target, stringBounds);
targetPosition[0] =
this->Tree2Bounds[1] + this->Spacing + stringBounds[2];
this->Tree2Bounds[1] - (this->LabelWidth2 - stringBounds[2]);
}
break;
......@@ -376,17 +372,15 @@ void vtkTanglegramItem::PaintCorrespondenceLines(vtkContext2D *painter)
float stringBounds[4];
painter->ComputeStringBounds(source, stringBounds);
sourcePosition[0] =
this->Tree1Bounds[1] + this->Spacing + stringBounds[2];
this->Tree1Bounds[1] - (this->LabelWidth1 - stringBounds[2]);
sourceEdgePosition[0] =
this->Tree1Bounds[1] + this->Spacing + this->LabelWidth1;
sourceEdgePosition[0] = this->Tree1Bounds[1] + this->Spacing;
targetEdgePosition[0] =
this->Tree2Bounds[0] - this->Spacing - this->LabelWidth2;
targetEdgePosition[0] = this->Tree2Bounds[0] - this->Spacing;