/*========================================================================= Program: Visualization Toolkit Module: vtkChartLegend.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 "vtkChartLegend.h" #include "vtkBrush.h" #include "vtkChart.h" #include "vtkContext2D.h" #include "vtkContextMouseEvent.h" #include "vtkContextScene.h" #include "vtkPen.h" #include "vtkPlot.h" #include "vtkSmartPointer.h" #include "vtkStdString.h" #include "vtkStringArray.h" #include "vtkTextProperty.h" #include "vtkVector.h" #include "vtkVectorOperators.h" #include "vtkWeakPointer.h" #include "vtkObjectFactory.h" #include //------------------------------------------------------------------------------ class vtkChartLegend::Private { public: Private() : Point(0, 0) { } ~Private() = default; vtkVector2f Point; vtkWeakPointer Chart; std::vector ActivePlots; }; //------------------------------------------------------------------------------ vtkStandardNewMacro(vtkChartLegend); //------------------------------------------------------------------------------ vtkChartLegend::vtkChartLegend() { this->Storage = new vtkChartLegend::Private; this->Point = this->Storage->Point.GetData(); this->Rect.Set(0, 0, 0, 0); // Defaults to 12pt text, with top, right alignment to the specified point. this->LabelProperties->SetFontSize(12); this->LabelProperties->SetColor(0.0, 0.0, 0.0); this->LabelProperties->SetJustificationToLeft(); this->LabelProperties->SetVerticalJustificationToBottom(); this->Pen->SetColor(0, 0, 0); this->Brush->SetColor(255, 255, 255, 255); this->HorizontalAlignment = vtkChartLegend::RIGHT; this->VerticalAlignment = vtkChartLegend::TOP; this->Padding = 5; this->SymbolWidth = 25; this->Inline = true; this->Button = -1; this->DragEnabled = true; this->CacheBounds = true; } //------------------------------------------------------------------------------ vtkChartLegend::~vtkChartLegend() { delete this->Storage; this->Storage = nullptr; this->Point = nullptr; } //------------------------------------------------------------------------------ void vtkChartLegend::Update() { this->Storage->ActivePlots.clear(); for (int i = 0; i < this->Storage->Chart->GetNumberOfPlots(); ++i) { if (this->Storage->Chart->GetPlot(i)->GetVisible() && this->Storage->Chart->GetPlot(i)->GetLabel().length() > 0) { this->Storage->ActivePlots.push_back(this->Storage->Chart->GetPlot(i)); } // If we have a plot with multiple labels, we generally only want to show // the labels/legend symbols for the first one. So truncate at the first // one we encounter. if (this->Storage->Chart->GetPlot(i)->GetLabels() && this->Storage->Chart->GetPlot(i)->GetLabels()->GetNumberOfTuples() > 1) { break; } } this->PlotTime.Modified(); } //------------------------------------------------------------------------------ bool vtkChartLegend::Paint(vtkContext2D* painter) { // This is where everything should be drawn, or dispatched to other methods. vtkDebugMacro(<< "Paint event called in vtkChartLegend."); if (!this->Visible || this->Storage->ActivePlots.empty()) { return true; } this->GetBoundingRect(painter); // Now draw a box for the legend. painter->ApplyPen(this->Pen); painter->ApplyBrush(this->Brush); painter->DrawRect( this->Rect.GetX(), this->Rect.GetY(), this->Rect.GetWidth(), this->Rect.GetHeight()); painter->ApplyTextProp(this->LabelProperties); vtkVector2f stringBounds[2]; painter->ComputeStringBounds("Tgyf", stringBounds->GetData()); float height = stringBounds[1].GetY(); painter->ComputeStringBounds("The", stringBounds->GetData()); float baseHeight = stringBounds[1].GetY(); vtkVector2f pos(this->Rect.GetX() + this->Padding + this->SymbolWidth, this->Rect.GetY() + this->Rect.GetHeight() - this->Padding - floor(height)); vtkRectf rect(this->Rect.GetX() + this->Padding, pos.GetY(), this->SymbolWidth - 3, ceil(height)); // Draw all of the legend labels and marks for (size_t i = 0; i < this->Storage->ActivePlots.size(); ++i) { if (!this->Storage->ActivePlots[i]->GetLegendVisibility()) { // skip if legend is not visible. continue; } vtkStringArray* labels = this->Storage->ActivePlots[i]->GetLabels(); for (vtkIdType l = 0; labels && (l < labels->GetNumberOfValues()); ++l) { // This is fairly hackish, but gets the text looking reasonable... // Calculate a height for a "normal" string, then if this height is greater // that offset is used to move it down. Effectively hacking in a text // base line until better support is in the text rendering code... // There are still several one pixel glitches, but it looks better than // using the default vertical alignment. FIXME! vtkStdString testString = labels->GetValue(l); testString += "T"; painter->ComputeStringBounds(testString, stringBounds->GetData()); painter->DrawString( pos.GetX(), rect.GetY() + (baseHeight - stringBounds[1].GetY()), labels->GetValue(l)); // Paint the legend mark and increment out y value. this->Storage->ActivePlots[i]->PaintLegend(painter, rect, l); rect.SetY(rect.GetY() - height - this->Padding); } } return true; } //------------------------------------------------------------------------------ vtkRectf vtkChartLegend::GetBoundingRect(vtkContext2D* painter) { if (this->CacheBounds && this->RectTime > this->GetMTime() && this->RectTime > this->PlotTime) { return this->Rect; } painter->ApplyTextProp(this->LabelProperties); vtkVector2f stringBounds[2]; painter->ComputeStringBounds("Tgyf", stringBounds->GetData()); float height = stringBounds[1].GetY(); float maxWidth = 0.0f; // Calculate the widest legend label - needs the context to calculate font // metrics, but these could be cached. for (size_t i = 0; i < this->Storage->ActivePlots.size(); ++i) { if (!this->Storage->ActivePlots[i]->GetLegendVisibility()) { // skip if legend is not visible. continue; } vtkStringArray* labels = this->Storage->ActivePlots[i]->GetLabels(); for (vtkIdType l = 0; labels && (l < labels->GetNumberOfTuples()); ++l) { painter->ComputeStringBounds(labels->GetValue(l), stringBounds->GetData()); if (stringBounds[1].GetX() > maxWidth) { maxWidth = stringBounds[1].GetX(); } } } // Figure out the size of the legend box and store locally. int numLabels = 0; for (size_t i = 0; i < this->Storage->ActivePlots.size(); ++i) { if (!this->Storage->ActivePlots[i]->GetLegendVisibility()) { // skip if legend is not visible. continue; } numLabels += this->Storage->ActivePlots[i]->GetNumberOfLabels(); } // Default point placement is bottom left. this->Rect = vtkRectf(floor(this->Storage->Point.GetX()), floor(this->Storage->Point.GetY()), ceil(maxWidth + 2 * this->Padding + this->SymbolWidth), ceil((numLabels * (height + this->Padding)) + this->Padding)); this->RectTime.Modified(); return this->Rect; } //------------------------------------------------------------------------------ void vtkChartLegend::SetPoint(const vtkVector2f& point) { this->Storage->Point = point; this->Modified(); } //------------------------------------------------------------------------------ const vtkVector2f& vtkChartLegend::GetPointVector() { return this->Storage->Point; } //------------------------------------------------------------------------------ void vtkChartLegend::SetLabelSize(int size) { this->LabelProperties->SetFontSize(size); } //------------------------------------------------------------------------------ int vtkChartLegend::GetLabelSize() { return this->LabelProperties->GetFontSize(); } //------------------------------------------------------------------------------ vtkPen* vtkChartLegend::GetPen() { return this->Pen; } //------------------------------------------------------------------------------ vtkBrush* vtkChartLegend::GetBrush() { return this->Brush; } //------------------------------------------------------------------------------ vtkTextProperty* vtkChartLegend::GetLabelProperties() { return this->LabelProperties; } //------------------------------------------------------------------------------ void vtkChartLegend::SetChart(vtkChart* chart) { if (this->Storage->Chart == chart) { return; } else { this->Storage->Chart = chart; this->Modified(); } } //------------------------------------------------------------------------------ vtkChart* vtkChartLegend::GetChart() { return this->Storage->Chart; } //------------------------------------------------------------------------------ bool vtkChartLegend::Hit(const vtkContextMouseEvent& mouse) { 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; } else { return false; } } //------------------------------------------------------------------------------ bool vtkChartLegend::MouseMoveEvent(const vtkContextMouseEvent& mouse) { if (this->Button == vtkContextMouseEvent::LEFT_BUTTON) { vtkVector2f delta = mouse.GetPos() - mouse.GetLastPos(); this->Storage->Point = this->Storage->Point + delta; this->GetScene()->SetDirty(true); this->Modified(); } return true; } //------------------------------------------------------------------------------ bool vtkChartLegend::MouseButtonPressEvent(const vtkContextMouseEvent& mouse) { if (mouse.GetButton() == vtkContextMouseEvent::LEFT_BUTTON) { this->Button = vtkContextMouseEvent::LEFT_BUTTON; return true; } return false; } //------------------------------------------------------------------------------ bool vtkChartLegend::MouseButtonReleaseEvent(const vtkContextMouseEvent&) { this->Button = -1; return true; } //------------------------------------------------------------------------------ void vtkChartLegend::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); }