vtkChartLegend.cxx 10.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
/*=========================================================================

  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 "vtkContext2D.h"
#include "vtkPen.h"
#include "vtkBrush.h"
#include "vtkChart.h"
#include "vtkPlot.h"
#include "vtkTextProperty.h"
#include "vtkStdString.h"
#include "vtkVector.h"
26
#include "vtkVectorOperators.h"
27 28
#include "vtkWeakPointer.h"
#include "vtkSmartPointer.h"
29
#include "vtkStringArray.h"
30 31
#include "vtkContextScene.h"
#include "vtkContextMouseEvent.h"
32 33 34

#include "vtkObjectFactory.h"

35
#include <vector>
36 37 38 39 40

//-----------------------------------------------------------------------------
class vtkChartLegend::Private
{
public:
41
  Private() : Point(0, 0)
42 43
  {
  }
44
  ~Private() = default;
45 46 47

  vtkVector2f Point;
  vtkWeakPointer<vtkChart> Chart;
48
  std::vector<vtkPlot*> ActivePlots;
49 50 51 52 53 54 55 56 57 58
};

//-----------------------------------------------------------------------------
vtkStandardNewMacro(vtkChartLegend);

//-----------------------------------------------------------------------------
vtkChartLegend::vtkChartLegend()
{
  this->Storage = new vtkChartLegend::Private;
  this->Point = this->Storage->Point.GetData();
59
  this->Rect.Set(0, 0, 0, 0);
60
  // Defaults to 12pt text, with top, right alignment to the specified point.
61 62 63 64 65 66 67 68 69 70 71 72
  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;
73
  this->Inline = true;
74 75
  this->Button = -1;
  this->DragEnabled = true;
76
  this->CacheBounds = true;
77 78 79 80 81 82
}

//-----------------------------------------------------------------------------
vtkChartLegend::~vtkChartLegend()
{
  delete this->Storage;
83 84
  this->Storage = nullptr;
  this->Point = nullptr;
85 86 87 88 89 90
}

//-----------------------------------------------------------------------------
void vtkChartLegend::Update()
{
  this->Storage->ActivePlots.clear();
91
  for (int i = 0; i < this->Storage->Chart->GetNumberOfPlots(); ++i)
92
  {
93 94
    if (this->Storage->Chart->GetPlot(i)->GetVisible()
        && this->Storage->Chart->GetPlot(i)->GetLabel().length() > 0)
95
    {
96
      this->Storage->ActivePlots.push_back(this->Storage->Chart->GetPlot(i));
97
    }
98 99 100
    // 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.
101 102
    if (this->Storage->Chart->GetPlot(i)->GetLabels() &&
        this->Storage->Chart->GetPlot(i)->GetLabels()->GetNumberOfTuples() > 1)
103
    {
104
      break;
105
    }
106
  }
107
  this->PlotTime.Modified();
108 109 110 111 112 113 114 115
}

//-----------------------------------------------------------------------------
bool vtkChartLegend::Paint(vtkContext2D *painter)
{
  // This is where everything should be drawn, or dispatched to other methods.
  vtkDebugMacro(<< "Paint event called in vtkChartLegend.");

116
  if (!this->Visible || this->Storage->ActivePlots.empty())
117
  {
118
    return true;
119
  }
120

121 122 123
  this->GetBoundingRect(painter);

  // Now draw a box for the legend.
124 125
  painter->ApplyPen(this->Pen);
  painter->ApplyBrush(this->Brush);
126 127
  painter->DrawRect(this->Rect.GetX(), this->Rect.GetY(),
                    this->Rect.GetWidth(), this->Rect.GetHeight());
128

129
  painter->ApplyTextProp(this->LabelProperties);
130 131 132 133 134 135

  vtkVector2f stringBounds[2];
  painter->ComputeStringBounds("Tgyf", stringBounds->GetData());
  float height = stringBounds[1].GetY();
  painter->ComputeStringBounds("The", stringBounds->GetData());
  float baseHeight = stringBounds[1].GetY();
136

137 138 139
  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(),
140 141 142 143
               this->SymbolWidth-3, ceil(height));

  // Draw all of the legend labels and marks
  for(size_t i = 0; i < this->Storage->ActivePlots.size(); ++i)
144
  {
145
    if (!this->Storage->ActivePlots[i]->GetLegendVisibility())
146
    {
147 148
      // skip if legend is not visible.
      continue;
149
    }
150

151 152
    vtkStringArray *labels = this->Storage->ActivePlots[i]->GetLabels();
    for (vtkIdType l = 0; labels && (l < labels->GetNumberOfValues()); ++l)
153
    {
154 155 156 157 158 159 160 161 162
      // 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());
163
      painter->DrawString(pos.GetX(), rect.GetY() + (baseHeight-stringBounds[1].GetY()),
164 165 166
                          labels->GetValue(l));

      // Paint the legend mark and increment out y value.
167
      this->Storage->ActivePlots[i]->PaintLegend(painter, rect, l);
168
      rect.SetY(rect.GetY() - height - this->Padding);
169
    }
170
  }
171 172 173 174 175 176 177

  return true;
}

//-----------------------------------------------------------------------------
vtkRectf vtkChartLegend::GetBoundingRect(vtkContext2D *painter)
{
178 179
  if (this->CacheBounds && this->RectTime > this->GetMTime() &&
      this->RectTime > this->PlotTime)
180
  {
181
    return this->Rect;
182
  }
183

184
  painter->ApplyTextProp(this->LabelProperties);
185 186 187 188

  vtkVector2f stringBounds[2];
  painter->ComputeStringBounds("Tgyf", stringBounds->GetData());
  float height = stringBounds[1].GetY();
189 190 191 192
  float maxWidth = 0.0f;

  // Calculate the widest legend label - needs the context to calculate font
  // metrics, but these could be cached.
193
  for(size_t i = 0; i < this->Storage->ActivePlots.size(); ++i)
194
  {
195
    if (!this->Storage->ActivePlots[i]->GetLegendVisibility())
196
    {
197 198
      // skip if legend is not visible.
      continue;
199
    }
200
    vtkStringArray *labels = this->Storage->ActivePlots[i]->GetLabels();
201
    for (vtkIdType l = 0; labels && (l < labels->GetNumberOfTuples()); ++l)
202
    {
203
      painter->ComputeStringBounds(labels->GetValue(l),
204
                                   stringBounds->GetData());
205
      if (stringBounds[1].GetX() > maxWidth)
206
      {
207
        maxWidth = stringBounds[1].GetX();
208 209
      }
    }
210
  }
211 212

  // Figure out the size of the legend box and store locally.
213 214
  int numLabels = 0;
  for(size_t i = 0; i < this->Storage->ActivePlots.size(); ++i)
215
  {
216
    if (!this->Storage->ActivePlots[i]->GetLegendVisibility())
217
    {
218 219
      // skip if legend is not visible.
      continue;
220
    }
221 222
    numLabels += this->Storage->ActivePlots[i]->GetNumberOfLabels();
  }
223

224
  // Default point placement is bottom left.
225 226
  this->Rect = vtkRectf(floor(this->Storage->Point.GetX()),
                        floor(this->Storage->Point.GetY()),
227
                        ceil(maxWidth + 2 * this->Padding + this->SymbolWidth),
228
                        ceil((numLabels * (height + this->Padding)) + this->Padding));
229

230 231
  this->RectTime.Modified();
  return this->Rect;
232 233 234 235 236 237
}

//-----------------------------------------------------------------------------
void vtkChartLegend::SetPoint(const vtkVector2f &point)
{
  this->Storage->Point = point;
238
  this->Modified();
239 240 241 242 243 244 245 246
}

//-----------------------------------------------------------------------------
const vtkVector2f& vtkChartLegend::GetPointVector()
{
  return this->Storage->Point;
}

247 248 249 250 251 252 253 254 255 256 257 258
//-----------------------------------------------------------------------------
void vtkChartLegend::SetLabelSize(int size)
{
  this->LabelProperties->SetFontSize(size);
}

//-----------------------------------------------------------------------------
int vtkChartLegend::GetLabelSize()
{
  return this->LabelProperties->GetFontSize();
}

259 260 261
//-----------------------------------------------------------------------------
vtkPen * vtkChartLegend::GetPen()
{
262
  return this->Pen;
263 264 265 266 267
}

//-----------------------------------------------------------------------------
vtkBrush * vtkChartLegend::GetBrush()
{
268
  return this->Brush;
269 270
}

271 272 273
//-----------------------------------------------------------------------------
vtkTextProperty * vtkChartLegend::GetLabelProperties()
{
274
  return this->LabelProperties;
275 276
}

277 278 279 280
//-----------------------------------------------------------------------------
void vtkChartLegend::SetChart(vtkChart* chart)
{
  if (this->Storage->Chart == chart)
281
  {
282
    return;
283
  }
284
  else
285
  {
286 287
    this->Storage->Chart = chart;
    this->Modified();
288
  }
289 290 291 292 293 294 295 296
}

//-----------------------------------------------------------------------------
vtkChart* vtkChartLegend::GetChart()
{
  return this->Storage->Chart;
}

297 298 299
//-----------------------------------------------------------------------------
bool vtkChartLegend::Hit(const vtkContextMouseEvent &mouse)
{
300
  if (!this->GetVisible())
301
  {
302
    return false;
303
  }
304 305 306 307
  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())
308
  {
309
    return true;
310
  }
311
  else
312
  {
313
    return false;
314
  }
315 316 317 318 319 320
}

//-----------------------------------------------------------------------------
bool vtkChartLegend::MouseMoveEvent(const vtkContextMouseEvent &mouse)
{
  if (this->Button == vtkContextMouseEvent::LEFT_BUTTON)
321
  {
322
    vtkVector2f delta = mouse.GetPos() - mouse.GetLastPos();
323 324
    this->Storage->Point = this->Storage->Point + delta;
    this->GetScene()->SetDirty(true);
325
    this->Modified();
326
  }
327 328 329 330 331 332
  return true;
}

//-----------------------------------------------------------------------------
bool vtkChartLegend::MouseButtonPressEvent(const vtkContextMouseEvent &mouse)
{
333
  if (mouse.GetButton() == vtkContextMouseEvent::LEFT_BUTTON)
334
  {
335 336
    this->Button = vtkContextMouseEvent::LEFT_BUTTON;
    return true;
337
  }
338 339 340 341
  return false;
}

//-----------------------------------------------------------------------------
342
bool vtkChartLegend::MouseButtonReleaseEvent(const vtkContextMouseEvent &)
343 344 345 346 347
{
  this->Button = -1;
  return true;
}

348 349 350 351 352
//-----------------------------------------------------------------------------
void vtkChartLegend::PrintSelf(ostream &os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
}