/*========================================================================= Program: Visualization Toolkit Module: vtkPlotBar.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 "vtkPlotBar.h" #include "vtkAxis.h" #include "vtkBrush.h" #include "vtkColorSeries.h" #include "vtkContext2D.h" #include "vtkContextDevice2D.h" #include "vtkContextMapper2D.h" #include "vtkDataSetAttributes.h" #include "vtkExecutive.h" #include "vtkFloatArray.h" #include "vtkIdTypeArray.h" #include "vtkInformation.h" #include "vtkLookupTable.h" #include "vtkNew.h" #include "vtkPen.h" #include "vtkPoints2D.h" #include "vtkRect.h" #include "vtkSmartPointer.h" #include "vtkStringArray.h" #include "vtkTable.h" #include "vtkTimeStamp.h" #include "vtkObjectFactory.h" #include #include #include #include //------------------------------------------------------------------------------ namespace { // Copy the two arrays into the points array template void CopyToPoints(vtkPoints2D* points, vtkPoints2D* previousPoints, A* a, B* b, int n, int logScale, const vtkRectd& ss) { points->SetNumberOfPoints(n); float* data = static_cast(points->GetVoidPointer(0)); float* prevData = nullptr; if (previousPoints && static_cast(previousPoints->GetNumberOfPoints()) == n) { prevData = static_cast(previousPoints->GetVoidPointer(0)); } float prev = 0.0; for (int i = 0; i < n; ++i) { if (prevData) { prev = prevData[2 * i + 1]; } A tmpA(static_cast((a[i] + ss[0]) * ss[2])); B tmpB(static_cast((b[i] + ss[1]) * ss[3])); data[2 * i] = static_cast((logScale & 1) ? log10(static_cast(tmpA)) : tmpA); data[2 * i + 1] = static_cast((logScale & 2) ? log10(static_cast(tmpB + prev)) : (tmpB + prev)); } } // Copy one array into the points array, use the index of that array as x template void CopyToPoints( vtkPoints2D* points, vtkPoints2D* previousPoints, A* a, int n, int logScale, const vtkRectd& ss) { points->SetNumberOfPoints(n); float* data = static_cast(points->GetVoidPointer(0)); float* prevData = nullptr; if (previousPoints && static_cast(previousPoints->GetNumberOfPoints()) == n) { prevData = static_cast(previousPoints->GetVoidPointer(0)); } float prev = 0.0; for (int i = 0; i < n; ++i) { if (prevData) { prev = prevData[2 * i + 1]; } A tmpA(static_cast((a[i] + ss[1]) * ss[3])); data[2 * i] = static_cast((logScale & 1) ? log10(static_cast(i + 1.0)) : i); data[2 * i + 1] = static_cast((logScale & 2) ? log10(static_cast(tmpA + prev)) : (tmpA + prev)); } } // Copy the two arrays into the points array template void CopyToPointsSwitch(vtkPoints2D* points, vtkPoints2D* previousPoints, A* a, vtkDataArray* b, int n, int logScale, const vtkRectd& ss) { switch (b->GetDataType()) { vtkTemplateMacro(CopyToPoints( points, previousPoints, a, static_cast(b->GetVoidPointer(0)), n, logScale, ss)); } } } // namespace //------------------------------------------------------------------------------ class vtkPlotBarSegment : public vtkObject { public: vtkTypeMacro(vtkPlotBarSegment, vtkObject); static vtkPlotBarSegment* New(); vtkPlotBarSegment() { this->Bar = nullptr; this->Points = nullptr; this->Sorted = nullptr; this->Previous = nullptr; this->Colors = nullptr; } ~vtkPlotBarSegment() override { delete this->Sorted; } void Configure(vtkPlotBar* bar, vtkDataArray* xArray, vtkDataArray* yArray, vtkAxis* xAxis, vtkAxis* yAxis, vtkPlotBarSegment* prev) { this->Bar = bar; this->Previous = prev; if (!this->Points) { this->Points = vtkSmartPointer::New(); } // For the atypical case that Configure is called on a non-fresh "this" delete this->Sorted; int logScale = (xAxis->GetLogScaleActive() ? 1 : 0) + (yAxis->GetLogScaleActive() ? 2 : 0); if (xArray) { switch (xArray->GetDataType()) { vtkTemplateMacro( CopyToPointsSwitch(this->Points, this->Previous ? this->Previous->Points : nullptr, static_cast(xArray->GetVoidPointer(0)), yArray, xArray->GetNumberOfTuples(), logScale, this->Bar->GetShiftScale())); } } else { // Using Index for X Series switch (yArray->GetDataType()) { vtkTemplateMacro( CopyToPoints(this->Points, this->Previous ? this->Previous->Points : nullptr, static_cast(yArray->GetVoidPointer(0)), yArray->GetNumberOfTuples(), logScale, this->Bar->GetShiftScale())); } } } void Paint( vtkContext2D* painter, vtkPen* pen, vtkBrush* brush, float width, float offset, int orientation) { painter->ApplyPen(pen); painter->ApplyBrush(brush); int n = this->Points->GetNumberOfPoints(); float* f = vtkArrayDownCast(this->Points->GetData())->GetPointer(0); float* p = nullptr; if (this->Previous) { p = vtkArrayDownCast(this->Previous->Points->GetData())->GetPointer(0); } for (int i = 0; i < n; ++i) { if (this->Colors) { if (this->Colors->GetNumberOfComponents() == 3) { painter->GetBrush()->SetColor(vtkColor3ub(this->Colors->GetPointer(i * 3))); } else if (this->Colors->GetNumberOfComponents() == 4) { painter->GetBrush()->SetColor(vtkColor4ub(this->Colors->GetPointer(i * 4))); } else { vtkErrorMacro(<< "Number of components not supported: " << this->Colors->GetNumberOfComponents()); } } if (orientation == vtkPlotBar::VERTICAL) { if (p) { painter->DrawRect( f[2 * i] - (width / 2) - offset, p[2 * i + 1], width, f[2 * i + 1] - p[2 * i + 1]); } else { painter->DrawRect(f[2 * i] - (width / 2) - offset, 0.0, width, f[2 * i + 1]); } } else // HORIZONTAL orientation { if (p) { painter->DrawRect( p[2 * i + 1], f[2 * i] - (width / 2) - offset, f[2 * i + 1] - p[2 * i + 1], width); } else { painter->DrawRect(0.0, f[2 * i] - (width / 2) - offset, f[2 * i + 1], width); } } } // Paint selections if there are any. vtkIdTypeArray* selection = this->Bar->GetSelection(); if (!selection) { return; } painter->ApplyBrush(this->Bar->GetSelectionBrush()); for (vtkIdType j = 0; j < selection->GetNumberOfTuples(); ++j) { int i = selection->GetValue(j); if (orientation == vtkPlotBar::VERTICAL) { if (p) { painter->DrawRect( f[2 * i] - (width / 2) - offset, p[2 * i + 1], width, f[2 * i + 1] - p[2 * i + 1]); } else { painter->DrawRect(f[2 * i] - (width / 2) - offset, 0.0, width, f[2 * i + 1]); } } else // HORIZONTAL orientation { if (p) { painter->DrawRect( p[2 * i + 1], f[2 * i] - (width / 2) - offset, f[2 * i + 1] - p[2 * i + 1], width); } else { painter->DrawRect(0.0, f[2 * i] - (width / 2) - offset, f[2 * i + 1], width); } } } } vtkIdType GetNearestPoint( const vtkVector2f& point, vtkVector2f* location, float width, float offset, int orientation) { if (!this->Points && this->Points->GetNumberOfPoints()) { return -1; } // The extent of any given bar is half a width on either // side of the point with which it is associated. float halfWidth = width / 2.0; // If orientation is VERTICAL, search normally. For HORIZONTAL, // simply transpose the X and Y coordinates of the target, as the rest // of the search uses the assumption that X = bar position, Y = bar // value; swapping the target X and Y is simpler than swapping the // X and Y of all the other references to the bar data. vtkVector2f targetPoint(point); if (orientation == vtkPlotBar::HORIZONTAL) { targetPoint.Set(point.GetY(), point.GetX()); // Swap x and y } this->CreateSortedPoints(); // Get the left-most bar we might hit vtkIndexedVector2f lowPoint; lowPoint.index = 0; lowPoint.pos = vtkVector2f(targetPoint.GetX() - (offset * -1) - halfWidth, 0.0f); // Set up our search array, use the STL lower_bound algorithm VectorPIMPL::iterator low; VectorPIMPL& v = *this->Sorted; low = std::lower_bound(v.begin(), v.end(), lowPoint); while (low != v.end()) { // Does the bar surround the point? if (low->pos.GetX() - halfWidth - offset < targetPoint.GetX() && low->pos.GetX() + halfWidth - offset > targetPoint.GetX()) { // Is the point within the vertical extent of the bar? if ((targetPoint.GetY() >= 0 && targetPoint.GetY() < low->pos.GetY()) || (targetPoint.GetY() < 0 && targetPoint.GetY() > low->pos.GetY())) { *location = low->pos; vtkRectd ss = this->Bar->GetShiftScale(); location->SetX((location->GetX() - ss.GetX()) / ss.GetWidth()); location->SetY((location->GetY() - ss.GetY()) / ss.GetHeight()); return static_cast(low->index); } } // Is the left side of the bar beyond the point? if (low->pos.GetX() - offset - halfWidth > targetPoint.GetX()) { break; } ++low; } return -1; } void CreateSortedPoints() { // Sorted points, used when searching for the nearest point. if (!this->Sorted) { vtkIdType n = this->Points->GetNumberOfPoints(); vtkVector2f* data = static_cast(this->Points->GetVoidPointer(0)); this->Sorted = new VectorPIMPL(data, n); std::sort(this->Sorted->begin(), this->Sorted->end()); } } bool SelectPoints( const vtkVector2f& min, const vtkVector2f& max, float width, float offset, int orientation) { if (!this->Points) { return false; } this->CreateSortedPoints(); // If orientation is VERTICAL, search normally. For HORIZONTAL, // transpose the selection box. vtkVector2f targetMin(min); vtkVector2f targetMax(max); if (orientation == vtkPlotBar::HORIZONTAL) { targetMin.Set(min.GetY(), min.GetX()); targetMax.Set(max.GetY(), max.GetX()); } // The extent of any given bar is half a width on either // side of the point with which it is associated. float halfWidth = width / 2.0; // Get the lowest X coordinate we might hit vtkIndexedVector2f lowPoint; lowPoint.index = 0; lowPoint.pos = vtkVector2f(targetMin.GetX() - (offset * -1) - halfWidth, 0.0f); // Set up our search array, use the STL lower_bound algorithm VectorPIMPL::iterator low; VectorPIMPL& v = *this->Sorted; low = std::lower_bound(v.begin(), v.end(), lowPoint); std::vector selected; while (low != v.end()) { // Is the bar's X coordinates at least partially within the box? if (low->pos.GetX() + halfWidth - offset > targetMin.GetX() && low->pos.GetX() - halfWidth - offset < targetMax.GetX()) { // Is the bar within the vertical extent of the box? if ((targetMin.GetY() > 0 && low->pos.GetY() >= targetMin.GetY()) || (targetMax.GetY() < 0 && low->pos.GetY() <= targetMax.GetY()) || (targetMin.GetY() < 0 && targetMax.GetY() > 0)) { selected.push_back(static_cast(low->index)); } } // Is the left side of the bar beyond the box? if (low->pos.GetX() - offset - halfWidth > targetMax.GetX()) { break; } ++low; } if (selected.empty()) { return false; } else { this->Bar->GetSelection()->SetNumberOfTuples(static_cast(selected.size())); vtkIdType* ptr = static_cast(this->Bar->GetSelection()->GetVoidPointer(0)); for (size_t i = 0; i < selected.size(); ++i) { ptr[i] = selected[i]; } this->Bar->GetSelection()->Modified(); return true; } } // Indexed vector for sorting struct vtkIndexedVector2f { size_t index; vtkVector2f pos; // Compare two vtkIndexedVector2f, in X component only bool operator<(const vtkIndexedVector2f& v2) const { return (this->pos.GetX() < v2.pos.GetX()); } }; class VectorPIMPL : public std::vector { public: VectorPIMPL(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); } } }; vtkSmartPointer Previous; vtkSmartPointer Points; vtkPlotBar* Bar; VectorPIMPL* Sorted; vtkVector2d ScalingFactor; vtkUnsignedCharArray* Colors; }; vtkStandardNewMacro(vtkPlotBarSegment); class vtkPlotBarPrivate { public: vtkPlotBarPrivate(vtkPlotBar* bar) : Bar(bar) { } void Update() { this->Segments.clear(); } vtkPlotBarSegment* AddSegment(vtkDataArray* xArray, vtkDataArray* yArray, vtkAxis* xAxis, vtkAxis* yAxis, vtkPlotBarSegment* prev = nullptr) { vtkNew segment; segment->Configure(this->Bar, xArray, yArray, xAxis, yAxis, prev); this->Segments.emplace_back(segment.GetPointer()); return segment.GetPointer(); } void PaintSegments(vtkContext2D* painter, vtkColorSeries* colorSeries, vtkPen* pen, vtkBrush* brush, float width, float offset, int orientation) { int colorInSeries = 0; bool useColorSeries = this->Segments.size() > 1; for (std::vector>::iterator it = this->Segments.begin(); it != this->Segments.end(); ++it) { if (useColorSeries && colorSeries) { brush->SetColor(colorSeries->GetColorRepeating(colorInSeries++).GetData()); } (*it)->Paint(painter, pen, brush, width, offset, orientation); } } vtkIdType GetNearestPoint(const vtkVector2f& point, vtkVector2f* location, float width, float offset, int orientation, vtkIdType* segmentIndex) { vtkIdType segmentIndexCtr = 0; for (std::vector>::iterator it = this->Segments.begin(); it != this->Segments.end(); ++it) { int barIndex = (*it)->GetNearestPoint(point, location, width, offset, orientation); if (barIndex != -1) { if (segmentIndex) { *segmentIndex = segmentIndexCtr; } return barIndex; } ++segmentIndexCtr; } if (segmentIndex) { *segmentIndex = -1; } return -1; } bool SelectPoints( const vtkVector2f& min, const vtkVector2f& max, float width, float offset, int orientation) { // Selection functionality not supported for stacked plots (yet)... if (this->Segments.size() != 1) { return false; } return this->Segments[0]->SelectPoints(min, max, width, offset, orientation); } std::vector> Segments; vtkPlotBar* Bar; std::map AdditionalSeries; vtkStdString GroupName; }; //------------------------------------------------------------------------------ vtkStandardNewMacro(vtkPlotBar); //------------------------------------------------------------------------------ vtkPlotBar::vtkPlotBar() { this->Private = new vtkPlotBarPrivate(this); this->Points = nullptr; this->AutoLabels = nullptr; this->Width = 1.0; this->Pen->SetWidth(1.0); this->Offset = 1.0; this->ColorSeries = nullptr; this->Orientation = vtkPlotBar::VERTICAL; this->ScalarVisibility = false; this->EnableOpacityMapping = true; this->LogX = false; this->LogY = false; } //------------------------------------------------------------------------------ vtkPlotBar::~vtkPlotBar() { if (this->Points) { this->Points->Delete(); this->Points = nullptr; } delete this->Private; } //------------------------------------------------------------------------------ bool vtkPlotBar::Paint(vtkContext2D* painter) { // This is where everything should be drawn, or dispatched to other methods. vtkDebugMacro(<< "Paint event called in vtkPlotBar."); if (!this->Visible) { return false; } this->Private->PaintSegments(painter, this->ColorSeries, this->Pen, this->Brush, this->Width, this->Offset, this->Orientation); return true; } //------------------------------------------------------------------------------ bool vtkPlotBar::PaintLegend(vtkContext2D* painter, const vtkRectf& rect, int legendIndex) { if (this->ColorSeries) { this->Brush->SetColor(this->ColorSeries->GetColorRepeating(legendIndex).GetData()); } painter->ApplyPen(this->Pen); painter->ApplyBrush(this->Brush); painter->DrawRect(rect[0], rect[1], rect[2], rect[3]); return true; } //------------------------------------------------------------------------------ void vtkPlotBar::GetBounds(double bounds[4], bool unscaled) { int seriesLow, seriesHigh, valuesLow, valuesHigh; // Don't re-orient the axes for vertical plots or unscaled bounds: if (this->Orientation == vtkPlotBar::VERTICAL || unscaled) { seriesLow = 0; // Xmin seriesHigh = 1; // Xmax valuesLow = 2; // Ymin valuesHigh = 3; // Ymax } else // HORIZONTAL orientation { seriesLow = 2; // Ymin seriesHigh = 3; // Ymax valuesLow = 0; // Xmin valuesHigh = 1; // Xmax } // Get the x and y arrays (index 0 and 1 respectively) vtkTable* table = this->Data->GetInput(); vtkDataArray* x = this->UseIndexForXSeries ? nullptr : this->Data->GetInputArrayToProcess(0, table); vtkDataArray* y = this->Data->GetInputArrayToProcess(1, table); vtkDataSetAttributes* rowData = table->GetRowData(); if (!y) { return; } if (this->UseIndexForXSeries) { bounds[seriesLow] = 0 - (this->Width / 2); bounds[seriesHigh] = y->GetNumberOfTuples() + (this->Width / 2); } else if (x) { rowData->GetRange(x->GetName(), &bounds[seriesLow]); // We surround our point by Width/2 on either side bounds[seriesLow] -= this->Width / 2.0 + this->Offset; bounds[seriesHigh] += this->Width / 2.0 - this->Offset; } else { return; } rowData->GetRange(y->GetName(), &bounds[valuesLow]); double yRange[2]; std::map::iterator it; for (it = this->Private->AdditionalSeries.begin(); it != this->Private->AdditionalSeries.end(); ++it) { y = vtkArrayDownCast(table->GetColumnByName((*it).second.c_str())); rowData->GetRange(y->GetName(), yRange); bounds[valuesHigh] += yRange[1]; } // Bar plots always have one of the value bounds at the origin if (bounds[valuesLow] > 0.0f) { bounds[valuesLow] = 0.0; } else if (bounds[valuesHigh] < 0.0f) { bounds[valuesHigh] = 0.0; } if (unscaled) { vtkAxis* axes[2]; axes[seriesLow / 2] = this->GetXAxis(); axes[valuesLow / 2] = this->GetYAxis(); if (axes[0]->GetLogScaleActive()) { bounds[0] = log10(fabs(bounds[0])); bounds[1] = log10(fabs(bounds[1])); } if (axes[1]->GetLogScaleActive()) { bounds[2] = log10(fabs(bounds[2])); bounds[3] = log10(fabs(bounds[3])); } } vtkDebugMacro(<< "Bounds: " << bounds[0] << "\t" << bounds[1] << "\t" << bounds[2] << "\t" << bounds[3]); } //------------------------------------------------------------------------------ void vtkPlotBar::GetBounds(double bounds[4]) { this->GetBounds(bounds, false); } //------------------------------------------------------------------------------ void vtkPlotBar::GetUnscaledInputBounds(double bounds[4]) { this->GetBounds(bounds, true); } //------------------------------------------------------------------------------ void vtkPlotBar::SetOrientation(int orientation) { if (orientation < 0 || orientation > 1) { vtkErrorMacro("Error, invalid orientation value supplied: " << orientation); return; } this->Orientation = orientation; } //------------------------------------------------------------------------------ void vtkPlotBar::SetColor(unsigned char r, unsigned char g, unsigned char b, unsigned char a) { this->Brush->SetColor(r, g, b, a); } //------------------------------------------------------------------------------ void vtkPlotBar::SetColor(double r, double g, double b) { this->Brush->SetColorF(r, g, b); } //------------------------------------------------------------------------------ void vtkPlotBar::GetColor(double rgb[3]) { double rgba[4]; this->Brush->GetColorF(rgba); rgb[0] = rgba[0]; rgb[1] = rgba[1]; rgb[2] = rgba[2]; } //------------------------------------------------------------------------------ vtkIdType vtkPlotBar::GetNearestPoint(const vtkVector2f& point, const vtkVector2f& vtkNotUsed(tolerance), vtkVector2f* location, vtkIdType* segmentIndex) { return this->Private->GetNearestPoint( point, location, this->Width, this->Offset, this->Orientation, segmentIndex); } //------------------------------------------------------------------------------ vtkStringArray* vtkPlotBar::GetLabels() { // If the label string is empty, return the y column name if (this->Labels) { return this->Labels; } else if (this->AutoLabels) { return this->AutoLabels; } else if (this->Data->GetInput() && this->Data->GetInputArrayToProcess(1, this->Data->GetInput())) { this->AutoLabels = vtkSmartPointer::New(); this->AutoLabels->InsertNextValue( this->Data->GetInputArrayToProcess(1, this->Data->GetInput())->GetName()); std::map::iterator it; for (it = this->Private->AdditionalSeries.begin(); it != this->Private->AdditionalSeries.end(); ++it) { this->AutoLabels->InsertNextValue((*it).second); } return this->AutoLabels; } else { return nullptr; } } void vtkPlotBar::SetGroupName(const vtkStdString& name) { if (this->Private->GroupName != name) { this->Private->GroupName = name; this->Modified(); } } vtkStdString vtkPlotBar::GetGroupName() { return this->Private->GroupName; } //------------------------------------------------------------------------------ bool vtkPlotBar::CacheRequiresUpdate() { return this->Superclass::CacheRequiresUpdate() || (this->XAxis && this->LogX != this->XAxis->GetLogScaleActive()) || (this->YAxis && this->LogY != this->YAxis->GetLogScaleActive()) || (this->LookupTable && this->LookupTable->GetMTime() > this->BuildTime); } //------------------------------------------------------------------------------ bool vtkPlotBar::UpdateCache() { if (!this->Superclass::UpdateCache()) { return false; } vtkTable* table = this->Data->GetInput(); // Get the x and y arrays (index 0 and 1 respectively) vtkDataArray* x = this->UseIndexForXSeries ? nullptr : this->Data->GetInputArrayToProcess(0, table); vtkDataArray* y = this->Data->GetInputArrayToProcess(1, table); if (!x && !this->UseIndexForXSeries) { vtkErrorMacro(<< "No X column is set (index 0)."); return false; } else if (!y) { vtkErrorMacro(<< "No Y column is set (index 1)."); return false; } else if (!this->UseIndexForXSeries && x->GetNumberOfTuples() != y->GetNumberOfTuples()) { vtkErrorMacro("The x and y columns must have the same number of elements."); return false; } this->LogX = this->XAxis->GetLogScaleActive(); this->LogY = this->YAxis->GetLogScaleActive(); this->Private->Update(); vtkPlotBarSegment* prev = this->Private->AddSegment(x, y, this->GetXAxis(), this->GetYAxis()); // Additions for color mapping if (this->ScalarVisibility && !this->ColorArrayName.empty()) { vtkDataArray* c = vtkArrayDownCast(table->GetColumnByName(this->ColorArrayName)); // TODO: Should add support for categorical coloring & try enum lookup if (c) { if (!this->LookupTable) { this->CreateDefaultLookupTable(); } int outputFormat = this->EnableOpacityMapping ? VTK_RGBA : VTK_RGB; this->Colors = this->LookupTable->MapScalars(c, VTK_COLOR_MODE_MAP_SCALARS, -1, outputFormat); prev->Colors = this->Colors; this->Colors->Delete(); } else { this->Colors = nullptr; prev->Colors = nullptr; } } std::map::iterator it; for (it = this->Private->AdditionalSeries.begin(); it != this->Private->AdditionalSeries.end(); ++it) { y = vtkArrayDownCast(table->GetColumnByName((*it).second.c_str())); prev = this->Private->AddSegment(x, y, this->GetXAxis(), this->GetYAxis(), prev); } this->TooltipDefaultLabelFormat.clear(); // Set the default tooltip according to the segments if (this->Private->Segments.size() > 1) { this->TooltipDefaultLabelFormat = "%s: "; } if (this->IndexedLabels) { this->TooltipDefaultLabelFormat += "%i: "; } this->TooltipDefaultLabelFormat += "%x, %y"; this->BuildTime.Modified(); return true; } //------------------------------------------------------------------------------ void vtkPlotBar::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); } //------------------------------------------------------------------------------ void vtkPlotBar::SetInputArray(int index, const vtkStdString& name) { if (index == 0 || index == 1) { vtkPlot::SetInputArray(index, name); } else { this->Private->AdditionalSeries[index] = name; } this->AutoLabels = nullptr; // No longer valid } //------------------------------------------------------------------------------ void vtkPlotBar::SetColorSeries(vtkColorSeries* colorSeries) { if (this->ColorSeries == colorSeries) { return; } this->ColorSeries = colorSeries; this->Modified(); } //------------------------------------------------------------------------------ vtkColorSeries* vtkPlotBar::GetColorSeries() { return this->ColorSeries; } //------------------------------------------------------------------------------ void vtkPlotBar::SetLookupTable(vtkScalarsToColors* lut) { if (this->LookupTable != lut) { this->LookupTable = lut; this->Modified(); } } //------------------------------------------------------------------------------ vtkScalarsToColors* vtkPlotBar::GetLookupTable() { if (!this->LookupTable) { this->CreateDefaultLookupTable(); } return this->LookupTable; } //------------------------------------------------------------------------------ void vtkPlotBar::CreateDefaultLookupTable() { vtkSmartPointer lut = vtkSmartPointer::New(); // rainbow - blue to red lut->SetHueRange(0.6667, 0.0); lut->Build(); double bounds[4]; this->GetBounds(bounds); lut->SetRange(bounds[0], bounds[1]); this->LookupTable = lut; } //------------------------------------------------------------------------------ void vtkPlotBar::SelectColorArray(const vtkStdString& arrayName) { if (this->ColorArrayName == arrayName) { return; } vtkTable* table = this->Data->GetInput(); if (!table) { vtkWarningMacro(<< "SelectColorArray called with no input table set."); return; } for (vtkIdType i = 0; i < table->GetNumberOfColumns(); ++i) { if (arrayName == table->GetColumnName(i)) { this->ColorArrayName = arrayName; this->Modified(); return; } } vtkDebugMacro(<< "SelectColorArray called with invalid column name."); this->ColorArrayName = ""; this->Modified(); } //------------------------------------------------------------------------------ void vtkPlotBar::SelectColorArray(vtkIdType arrayNum) { vtkTable* table = this->Data->GetInput(); if (!table) { vtkWarningMacro(<< "SelectColorArray called with no input table set."); return; } vtkDataArray* col = vtkArrayDownCast(table->GetColumn(arrayNum)); // TODO: Should add support for categorical coloring & try enum lookup if (!col) { vtkDebugMacro(<< "SelectColorArray called with invalid column index"); return; } else { const char* arrayName = table->GetColumnName(arrayNum); if (this->ColorArrayName == arrayName || arrayName == nullptr) { return; } else { this->ColorArrayName = arrayName; this->Modified(); } } } //------------------------------------------------------------------------------ vtkStdString vtkPlotBar::GetColorArrayName() { return this->ColorArrayName; } //------------------------------------------------------------------------------ bool vtkPlotBar::SelectPoints(const vtkVector2f& min, const vtkVector2f& max) { if (!this->Selection) { this->Selection = vtkIdTypeArray::New(); } this->Selection->SetNumberOfTuples(0); return this->Private->SelectPoints(min, max, this->Width, this->Offset, this->Orientation); } //------------------------------------------------------------------------------ vtkStdString vtkPlotBar::GetTooltipLabel( const vtkVector2d& plotPos, vtkIdType seriesIndex, vtkIdType segmentIndex) { vtkStdString baseLabel = Superclass::GetTooltipLabel(plotPos, seriesIndex, segmentIndex); vtkStdString tooltipLabel; bool escapeNext = false; for (size_t i = 0; i < baseLabel.length(); ++i) { if (escapeNext) { switch (baseLabel[i]) { case 's': if (segmentIndex >= 0 && this->GetLabels() && segmentIndex < this->GetLabels()->GetNumberOfTuples()) { tooltipLabel += this->GetLabels()->GetValue(segmentIndex); } break; default: // If no match, insert the entire format tag tooltipLabel += "%"; tooltipLabel += baseLabel[i]; break; } escapeNext = false; } else { if (baseLabel[i] == '%') { escapeNext = true; } else { tooltipLabel += baseLabel[i]; } } } return tooltipLabel; } //------------------------------------------------------------------------------ int vtkPlotBar::GetBarsCount() { vtkTable* table = this->Data->GetInput(); if (!table) { vtkWarningMacro(<< "GetBarsCount called with no input table set."); return 0; } vtkDataArray* x = this->Data->GetInputArrayToProcess(0, table); return x ? x->GetNumberOfTuples() : 0; } //------------------------------------------------------------------------------ void vtkPlotBar::GetDataBounds(double bounds[2]) { assert(bounds); // Get the x and y arrays (index 0 and 1 respectively) vtkTable* table = this->Data->GetInput(); if (!table) { vtkWarningMacro(<< "GetDataBounds called with no input table set."); bounds[0] = VTK_DOUBLE_MAX; bounds[1] = VTK_DOUBLE_MIN; return; } vtkDataArray* x = this->Data->GetInputArrayToProcess(0, table); if (x) { table->GetRowData()->GetRange(x->GetName(), bounds); } }