Files
ThirdParty-6/ParaView-5.0.1/VTK/Charts/Core/vtkPlotParallelCoordinates.cxx

526 lines
15 KiB
C++

/*=========================================================================
Program: Visualization Toolkit
Module: vtkPlotParallelCoordinates.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 "vtkPlotParallelCoordinates.h"
#include "vtkChartParallelCoordinates.h"
#include "vtkContext2D.h"
#include "vtkAxis.h"
#include "vtkPen.h"
#include "vtkFloatArray.h"
#include "vtkDoubleArray.h"
#include "vtkVector.h"
#include "vtkTransform2D.h"
#include "vtkContextDevice2D.h"
#include "vtkContextMapper2D.h"
#include "vtkTable.h"
#include "vtkDataArray.h"
#include "vtkIdTypeArray.h"
#include "vtkStringArray.h"
#include "vtkTimeStamp.h"
#include "vtkInformation.h"
#include "vtkSmartPointer.h"
#include "vtkUnsignedCharArray.h"
#include "vtkLookupTable.h"
// Need to turn some arrays of strings into categories
#include "vtkStringToCategory.h"
#include "vtkObjectFactory.h"
#include <vector>
#include <algorithm>
class vtkPlotParallelCoordinates::Private :
public std::vector< std::vector<float> >
{
public:
Private()
{
this->SelectionInitialized = false;
}
std::vector<float> AxisPos;
bool SelectionInitialized;
};
//-----------------------------------------------------------------------------
vtkStandardNewMacro(vtkPlotParallelCoordinates)
//-----------------------------------------------------------------------------
vtkPlotParallelCoordinates::vtkPlotParallelCoordinates()
{
this->Storage = new vtkPlotParallelCoordinates::Private;
this->Pen->SetColor(0, 0, 0, 25);
this->LookupTable = 0;
this->Colors = 0;
this->ScalarVisibility = 0;
}
//-----------------------------------------------------------------------------
vtkPlotParallelCoordinates::~vtkPlotParallelCoordinates()
{
delete this->Storage;
if (this->LookupTable)
{
this->LookupTable->UnRegister(this);
}
if ( this->Colors != 0 )
{
this->Colors->UnRegister(this);
}
}
//-----------------------------------------------------------------------------
void vtkPlotParallelCoordinates::Update()
{
if (!this->Visible)
{
return;
}
// Check if we have an input
vtkTable *table = this->Data->GetInput();
if (!table)
{
vtkDebugMacro(<< "Update event called with no input table set.");
return;
}
this->UpdateTableCache(table);
}
//-----------------------------------------------------------------------------
bool vtkPlotParallelCoordinates::Paint(vtkContext2D *painter)
{
// This is where everything should be drawn, or dispatched to other methods.
vtkDebugMacro(<< "Paint event called in vtkPlotParallelCoordinates.");
if (!this->Visible)
{
return false;
}
painter->ApplyPen(this->Pen);
if (this->Storage->size() == 0)
{
return false;
}
size_t cols = this->Storage->size();
size_t rows = this->Storage->at(0).size();
vtkVector2f* line = new vtkVector2f[cols];
// Update the axis positions
vtkChartParallelCoordinates *parent =
vtkChartParallelCoordinates::SafeDownCast(this->Parent);
for (size_t i = 0; i < cols; ++i)
{
this->Storage->AxisPos[i] = parent->GetAxis(int(i)) ?
parent->GetAxis(int(i))->GetPoint1()[0] :
0;
}
vtkIdType selection = 0;
vtkIdType id = 0;
vtkIdType selectionSize = 0;
if (this->Selection)
{
selectionSize = this->Selection->GetNumberOfTuples();
if (selectionSize)
{
this->Selection->GetTupleValue(selection, &id);
}
}
// Draw all of the lines
painter->ApplyPen(this->Pen);
int ncComps(0);
if (this->ScalarVisibility && this->Colors)
{
ncComps = static_cast<int>(this->Colors->GetNumberOfComponents());
}
if (this->ScalarVisibility && this->Colors && ncComps == 4)
{
for (size_t i = 0, nc = 0; i < rows; ++i, nc += ncComps)
{
for (size_t j = 0; j < cols; ++j)
{
line[j].Set(this->Storage->AxisPos[j], (*this->Storage)[j][i]);
}
painter->GetPen()->SetColor(this->Colors->GetPointer(nc));
painter->DrawPoly(line[0].GetData(), static_cast<int>(cols));
}
}
else
{
for (size_t i = 0; i < rows; ++i)
{
for (size_t j = 0; j < cols; ++j)
{
line[j].Set(this->Storage->AxisPos[j], (*this->Storage)[j][i]);
}
painter->DrawPoly(line[0].GetData(), static_cast<int>(cols));
}
}
// Now draw the selected lines
if (this->Selection)
{
painter->GetPen()->SetColor(255, 0, 0, 100);
for (vtkIdType i = 0; i < this->Selection->GetNumberOfTuples(); ++i)
{
for (size_t j = 0; j < cols; ++j)
{
this->Selection->GetTupleValue(i, &id);
line[j].Set(this->Storage->AxisPos[j], (*this->Storage)[j][id]);
}
painter->DrawPoly(line[0].GetData(), static_cast<int>(cols));
}
}
delete[] line;
return true;
}
//-----------------------------------------------------------------------------
bool vtkPlotParallelCoordinates::PaintLegend(vtkContext2D *painter,
const vtkRectf& rect, int)
{
painter->ApplyPen(this->Pen);
painter->DrawLine(rect[0] , rect[1] + 0.5 * rect[3],
rect[0] + rect[2], rect[1] + 0.5 * rect[3]);
return true;
}
//-----------------------------------------------------------------------------
void vtkPlotParallelCoordinates::GetBounds(double *)
{
}
//-----------------------------------------------------------------------------
bool vtkPlotParallelCoordinates::SetSelectionRange(int axis, float low,
float high)
{
if (!this->Selection)
{
this->Storage->SelectionInitialized = false;
this->Selection = vtkIdTypeArray::New();
}
if (this->Storage->SelectionInitialized)
{
// Further refine the selection that has already been made
vtkIdTypeArray *array = vtkIdTypeArray::New();
std::vector<float>& col = this->Storage->at(axis);
for (vtkIdType i = 0; i < this->Selection->GetNumberOfTuples(); ++i)
{
vtkIdType id = 0;
this->Selection->GetTupleValue(i, &id);
if (col[id] >= low && col[id] <= high)
{
// Remove this point - no longer selected
array->InsertNextValue(id);
}
}
this->Selection->DeepCopy(array);
array->Delete();
}
else
{
// First run - ensure the selection list is empty and build it up
std::vector<float>& col = this->Storage->at(axis);
for (size_t i = 0; i < col.size(); ++i)
{
if (col[i] >= low && col[i] <= high)
{
// Remove this point - no longer selected
this->Selection->InsertNextValue(i);
}
}
this->Storage->SelectionInitialized = true;
}
return true;
}
//-----------------------------------------------------------------------------
bool vtkPlotParallelCoordinates::ResetSelectionRange()
{
this->Storage->SelectionInitialized = false;
if (this->Selection)
{
this->Selection->SetNumberOfTuples(0);
}
return true;
}
//-----------------------------------------------------------------------------
void vtkPlotParallelCoordinates::SetInputData(vtkTable* table)
{
if (table == this->Data->GetInput() && (!table ||
table->GetMTime() < this->BuildTime))
{
return;
}
bool updateVisibility = table != this->Data->GetInput();
this->vtkPlot::SetInputData(table);
vtkChartParallelCoordinates *parent =
vtkChartParallelCoordinates::SafeDownCast(this->Parent);
if (parent && table && updateVisibility)
{
parent->SetColumnVisibilityAll(false);
// By default make the first 10 columns visible in a plot.
for (vtkIdType i = 0; i < table->GetNumberOfColumns() && i < 10; ++i)
{
parent->SetColumnVisibility(table->GetColumnName(i), true);
}
}
else if (parent && updateVisibility)
{
// No table, therefore no visible columns
parent->GetVisibleColumns()->SetNumberOfTuples(0);
}
}
//-----------------------------------------------------------------------------
bool vtkPlotParallelCoordinates::UpdateTableCache(vtkTable *table)
{
// Each axis is a column in our storage array, they are scaled from 0.0 to 1.0
vtkChartParallelCoordinates *parent =
vtkChartParallelCoordinates::SafeDownCast(this->Parent);
if (!parent || !table || table->GetNumberOfColumns() == 0)
{
return false;
}
vtkStringArray* cols = parent->GetVisibleColumns();
this->Storage->resize(cols->GetNumberOfTuples());
this->Storage->AxisPos.resize(cols->GetNumberOfTuples());
vtkIdType rows = table->GetNumberOfRows();
for (vtkIdType i = 0; i < cols->GetNumberOfTuples(); ++i)
{
std::vector<float>& col = this->Storage->at(i);
vtkAxis* axis = parent->GetAxis(i);
col.resize(rows);
vtkSmartPointer<vtkDataArray> data =
vtkDataArray::SafeDownCast(table->GetColumnByName(cols->GetValue(i)));
if (!data)
{
if (table->GetColumnByName(cols->GetValue(i))->IsA("vtkStringArray"))
{
// We have a different kind of column - attempt to make it into an enum
vtkStringToCategory* stoc = vtkStringToCategory::New();
stoc->SetInputData(table);
stoc->SetInputArrayToProcess(0, 0, 0,
vtkDataObject::FIELD_ASSOCIATION_ROWS,
cols->GetValue(i));
stoc->SetCategoryArrayName("enumPC");
stoc->Update();
vtkTable* table2 = vtkTable::SafeDownCast(stoc->GetOutput());
vtkTable* stringTable = vtkTable::SafeDownCast(stoc->GetOutput(1));
if (table2)
{
data = vtkDataArray::SafeDownCast(table2->GetColumnByName("enumPC"));
}
if (stringTable && stringTable->GetColumnByName("Strings"))
{
vtkStringArray* strings =
vtkStringArray::SafeDownCast(stringTable->GetColumnByName("Strings"));
vtkSmartPointer<vtkDoubleArray> arr =
vtkSmartPointer<vtkDoubleArray>::New();
for (vtkIdType j = 0; j < strings->GetNumberOfTuples(); ++j)
{
arr->InsertNextValue(j);
}
// Now we need to set the range on the string axis
axis->SetCustomTickPositions(arr, strings);
if (strings->GetNumberOfTuples() > 1)
{
axis->SetUnscaledRange(0.0, strings->GetNumberOfTuples()-1);
}
else
{
axis->SetUnscaledRange(-0.1, 0.1);
}
axis->Update();
}
stoc->Delete();
}
// If we still don't have a valid data array then skip this column.
if (!data)
{
continue;
}
}
// Also need the range from the appropriate axis, to normalize points
double min = axis->GetUnscaledMinimum();
double max = axis->GetUnscaledMaximum();
double scale = 1.0f / (max - min);
for (vtkIdType j = 0; j < rows; ++j)
{
col[j] = (data->GetTuple1(j) - min) * scale;
}
}
// Additions for color mapping
if (this->ScalarVisibility && !this->ColorArrayName.empty())
{
vtkDataArray* c =
vtkDataArray::SafeDownCast(table->GetColumnByName(this->ColorArrayName));
// TODO: Should add support for categorical coloring & try enum lookup
if (c)
{
if (!this->LookupTable)
{
this->CreateDefaultLookupTable();
}
this->Colors = this->LookupTable->MapScalars(c, VTK_COLOR_MODE_MAP_SCALARS, -1);
// Consistent register and unregisters
this->Colors->Register(this);
this->Colors->Delete();
}
else
{
this->Colors->UnRegister(this);
this->Colors = 0;
}
}
this->BuildTime.Modified();
return true;
}
//-----------------------------------------------------------------------------
void vtkPlotParallelCoordinates::SetLookupTable(vtkScalarsToColors *lut)
{
if ( this->LookupTable != lut )
{
if ( this->LookupTable)
{
this->LookupTable->UnRegister(this);
}
this->LookupTable = lut;
if (lut)
{
lut->Register(this);
}
this->Modified();
}
}
//-----------------------------------------------------------------------------
vtkScalarsToColors *vtkPlotParallelCoordinates::GetLookupTable()
{
if ( this->LookupTable == 0 )
{
this->CreateDefaultLookupTable();
}
return this->LookupTable;
}
//-----------------------------------------------------------------------------
void vtkPlotParallelCoordinates::CreateDefaultLookupTable()
{
if ( this->LookupTable)
{
this->LookupTable->UnRegister(this);
}
this->LookupTable = vtkLookupTable::New();
// Consistent Register/UnRegisters.
this->LookupTable->Register(this);
this->LookupTable->Delete();
}
//-----------------------------------------------------------------------------
void vtkPlotParallelCoordinates::SelectColorArray(const vtkStdString &arrayName)
{
vtkTable *table = this->Data->GetInput();
if (!table)
{
vtkDebugMacro(<< "SelectColorArray called with no input table set.");
return;
}
if (this->ColorArrayName == arrayName)
{
return;
}
for (vtkIdType c = 0; c < table->GetNumberOfColumns(); ++c)
{
if (table->GetColumnName(c) == arrayName)
{
this->ColorArrayName = arrayName;
this->Modified();
return;
}
}
vtkDebugMacro(<< "SelectColorArray called with invalid column name.");
this->ColorArrayName = "";
this->Modified();
return;
}
//-----------------------------------------------------------------------------
vtkStdString vtkPlotParallelCoordinates::GetColorArrayName()
{
return this->ColorArrayName;
}
//-----------------------------------------------------------------------------
void vtkPlotParallelCoordinates::SelectColorArray(vtkIdType arrayNum)
{
vtkTable *table = this->Data->GetInput();
if (!table)
{
vtkDebugMacro(<< "SelectColorArray called with no input table set.");
return;
}
vtkDataArray *col = vtkDataArray::SafeDownCast(table->GetColumn(arrayNum));
// TODO: Should add support for categorical coloring & try enum lookup
if (!col)
{
vtkDebugMacro(<< "SelectColorArray called with invalid column index");
return;
}
else
{
if (this->ColorArrayName == table->GetColumnName(arrayNum))
{
return;
}
else
{
this->ColorArrayName = table->GetColumnName(arrayNum);
this->Modified();
}
}
}
//-----------------------------------------------------------------------------
void vtkPlotParallelCoordinates::PrintSelf(ostream &os, vtkIndent indent)
{
this->Superclass::PrintSelf(os, indent);
}