vtkChartXY.cxx 74.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkChartXY.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 "vtkChartXY.h"

#include "vtkContext2D.h"
#include "vtkPen.h"
#include "vtkBrush.h"
21
#include "vtkColorSeries.h"
22
#include "vtkChartSelectionHelper.h"
23

24
#include "vtkMath.h"
25 26
#include "vtkTransform2D.h"
#include "vtkContextScene.h"
27
#include "vtkContextMouseEvent.h"
28
#include "vtkContextKeyEvent.h"
29
#include "vtkContextTransform.h"
30
#include "vtkContextClip.h"
31
#include "vtkPoints2D.h"
32
#include "vtkVector.h"
33
#include "vtkVectorOperators.h"
34

35
#include "vtkPlotArea.h"
36
#include "vtkPlotBar.h"
37
#include "vtkPlotBag.h"
38
#include "vtkPlotFunctionalBag.h"
Keith Fieldhouse's avatar
Keith Fieldhouse committed
39
#include "vtkPlotStacked.h"
40 41
#include "vtkPlotLine.h"
#include "vtkPlotPoints.h"
42
#include "vtkContextMapper2D.h"
43 44 45

#include "vtkAxis.h"
#include "vtkPlotGrid.h"
46
#include "vtkChartLegend.h"
47
#include "vtkTooltipItem.h"
48

49
#include "vtkDataSetAttributes.h"
50 51 52 53 54 55
#include "vtkTable.h"
#include "vtkIdTypeArray.h"

#include "vtkAnnotationLink.h"
#include "vtkSelection.h"
#include "vtkSelectionNode.h"
56
#include "vtkSmartPointer.h"
57 58

#include "vtkObjectFactory.h"
59
#include "vtkCommand.h"
60 61 62 63

#include "vtkStdString.h"
#include "vtkTextProperty.h"

Keith Fieldhouse's avatar
Keith Fieldhouse committed
64
#include "vtkDataArray.h"
65
#include "vtkStringArray.h"
66

67
// My STL containers
68
#include <vector>
69
#include <algorithm>
70 71 72 73

//-----------------------------------------------------------------------------
class vtkChartXYPrivate
{
74 75 76 77
public:
  vtkChartXYPrivate()
    {
    this->Colors = vtkSmartPointer<vtkColorSeries>::New();
78
    this->Clip = vtkSmartPointer<vtkContextClip>::New();
79 80 81 82
    this->Borders[0] = 60;
    this->Borders[1] = 50;
    this->Borders[2] = 20;
    this->Borders[3] = 20;
83
    }
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
  vtkPlot* GetPlotByColumn(vtkIdType columnId)
    {
    std::vector<vtkPlot*>::iterator it =
          this->plots.begin();
      for ( ; it != this->plots.end(); ++it)
        {
        vtkPlot* plot = *it;
        vtkTable* table = plot->GetInput();
        const int idx = 1; // column
        if (table &&
            table->GetColumn(columnId) ==
            plot->GetData()->GetInputAbstractArrayToProcess(idx, table))
          {
          return plot;
          }
        }
      return 0;
    }
102

103 104 105
  std::vector<vtkPlot *> plots; // Charts can contain multiple plots of data
  std::vector<vtkContextTransform *> PlotCorners; // Stored by corner...
  std::vector<vtkAxis *> axes; // Charts can contain multiple axes
106
  vtkSmartPointer<vtkColorSeries> Colors; // Colors in the chart
107
  vtkSmartPointer<vtkContextClip> Clip; // Colors in the chart
108
  int Borders[4];
109 110 111 112 113 114 115 116
};

//-----------------------------------------------------------------------------
vtkStandardNewMacro(vtkChartXY);

//-----------------------------------------------------------------------------
vtkChartXY::vtkChartXY()
{
117
  this->ChartPrivate = new vtkChartXYPrivate;
118

119
  this->AutoAxes = true;
120
  this->HiddenAxisBorder = 20;
121

122 123 124 125
  // The plots are drawn in a clipped, transformed area.
  this->AddItem(this->ChartPrivate->Clip);

  // The grid is drawn first in this clipped, transformed area.
126
  vtkPlotGrid *grid1 = vtkPlotGrid::New();
127
  this->ChartPrivate->Clip->AddItem(grid1);
128
  grid1->Delete();
129 130

  // The second grid for the far side/top axis
131
  vtkPlotGrid *grid2 = vtkPlotGrid::New();
132
  this->ChartPrivate->Clip->AddItem(grid2);
133
  grid2->Delete();
134

135 136 137 138 139
  // Set up the bottom-left transform, the rest are often not required (set up
  // on demand if used later). Add it as a child item, rendered automatically.
  vtkSmartPointer<vtkContextTransform> corner =
      vtkSmartPointer<vtkContextTransform>::New();
  this->ChartPrivate->PlotCorners.push_back(corner);
140
  this->ChartPrivate->Clip->AddItem(corner); // Child list maintains ownership.
141

142
  // Next is the axes
143 144 145 146 147
  for (int i = 0; i < 4; ++i)
    {
    this->ChartPrivate->axes.push_back(vtkAxis::New());
    // By default just show the left and bottom axes
    this->ChartPrivate->axes.back()->SetVisible(i < 2 ? true : false);
148
    this->AttachAxisRangeListener(this->ChartPrivate->axes.back());
149
    this->AddItem(this->ChartPrivate->axes.back());
150
    }
151 152 153 154
  this->ChartPrivate->axes[vtkAxis::LEFT]->SetPosition(vtkAxis::LEFT);
  this->ChartPrivate->axes[vtkAxis::BOTTOM]->SetPosition(vtkAxis::BOTTOM);
  this->ChartPrivate->axes[vtkAxis::RIGHT]->SetPosition(vtkAxis::RIGHT);
  this->ChartPrivate->axes[vtkAxis::TOP]->SetPosition(vtkAxis::TOP);
155

156
  // Set up the x and y axes - should be configured based on data
157 158 159
  this->ChartPrivate->axes[vtkAxis::LEFT]->SetTitle("Y Axis");
  this->ChartPrivate->axes[vtkAxis::BOTTOM]->SetTitle("X Axis");

160 161 162 163
  grid1->SetXAxis(this->ChartPrivate->axes[vtkAxis::BOTTOM]);
  grid1->SetYAxis(this->ChartPrivate->axes[vtkAxis::LEFT]);
  grid2->SetXAxis(this->ChartPrivate->axes[vtkAxis::TOP]);
  grid2->SetYAxis(this->ChartPrivate->axes[vtkAxis::RIGHT]);
164

165
  // Then the legend is drawn
166
  this->Legend = vtkSmartPointer<vtkChartLegend>::New();
167 168 169 170
  this->Legend->SetChart(this);
  this->Legend->SetVisible(false);
  this->AddItem(this->Legend);

171
  this->PlotTransformValid = false;
172 173

  this->DrawBox = false;
174
  this->DrawSelectionPolygon = false;
175
  this->DrawNearestPoint = false;
176
  this->DrawAxesAtOrigin = false;
177
  this->BarWidthFraction = 0.8f;
178

179
  this->Tooltip = vtkSmartPointer<vtkTooltipItem>::New();
180
  this->Tooltip->SetVisible(false);
181
  this->AddItem(this->Tooltip);
182
  this->LayoutChanged = true;
183 184

  this->ForceAxesToBounds = false;
185
  this->ZoomWithMouseWheel = true;
186 187 188 189 190
}

//-----------------------------------------------------------------------------
vtkChartXY::~vtkChartXY()
{
191
  for (unsigned int i = 0; i < this->ChartPrivate->plots.size(); ++i)
192 193 194
    {
    this->ChartPrivate->plots[i]->Delete();
    }
195
  for (size_t i = 0; i < 4; ++i)
196 197 198
    {
    this->ChartPrivate->axes[i]->Delete();
    }
199 200 201 202
  delete this->ChartPrivate;
  this->ChartPrivate = 0;
}

203 204 205 206 207 208 209 210 211
//-----------------------------------------------------------------------------
void vtkChartXY::Update()
{
  // Perform any necessary updates that are not graphical
  // Update the plots if necessary
  for (size_t i = 0; i < this->ChartPrivate->plots.size(); ++i)
    {
    this->ChartPrivate->plots[i]->Update();
    }
212
  this->Legend->Update();
213 214 215 216 217 218

  // Update the selections if necessary.
  if (this->AnnotationLink)
    {
    this->AnnotationLink->Update();
    vtkSelection *selection =
219
      vtkSelection::SafeDownCast(this->AnnotationLink->GetOutputDataObject(2));
220
    // Two major selection methods - row based or plot based.
221
    if (this->SelectionMethod == vtkChart::SELECTION_ROWS)
222
      {
223 224 225
      vtkSelectionNode *node = selection->GetNumberOfNodes() > 0?
        selection->GetNode(0) : NULL;
      vtkIdTypeArray *idArray = node?
226
          vtkArrayDownCast<vtkIdTypeArray>(node->GetSelectionList()) : NULL;
227
      std::vector<vtkPlot*>::iterator it =
228 229 230
          this->ChartPrivate->plots.begin();
      for ( ; it != this->ChartPrivate->plots.end(); ++it)
        {
231
        // Use the first selection node for all plots to select the rows.
232 233 234
        (*it)->SetSelection(idArray);
        }
      }
235 236 237 238 239 240
    else if (this->SelectionMethod == vtkChart::SELECTION_PLOTS)
      {
      for (unsigned int i = 0; i < selection->GetNumberOfNodes(); ++i)
        {
        vtkSelectionNode *node = selection->GetNode(i);
        vtkIdTypeArray *idArray =
241
            vtkArrayDownCast<vtkIdTypeArray>(node->GetSelectionList());
242 243 244 245 246 247 248 249 250 251 252 253 254 255
        vtkPlot *selectionPlot =
            vtkPlot::SafeDownCast(node->GetProperties()->Get(vtkSelectionNode::PROP()));
        // Now iterate through the plots to update selection data
        std::vector<vtkPlot*>::iterator it =
            this->ChartPrivate->plots.begin();
        for ( ; it != this->ChartPrivate->plots.end(); ++it)
          {
          if (selectionPlot == *it)
            {
            (*it)->SetSelection(idArray);
            }
          }
        }
      }
256 257 258 259 260 261 262 263
    else if (this->SelectionMethod == vtkChart::SELECTION_COLUMNS)
      {
      // Retrieve all the selected plots
      std::vector<vtkPlot*> selectedPlots;
      for (unsigned int i = 0; i < selection->GetNumberOfNodes(); ++i)
        {
        vtkSelectionNode *node = selection->GetNode(i);
        vtkIdTypeArray *selectedColumns =
264
            vtkArrayDownCast<vtkIdTypeArray>(node->GetSelectionList());
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
        vtkIdType* ptr = reinterpret_cast<vtkIdType*>(selectedColumns->GetVoidPointer(0));
        for (vtkIdType j = 0; j < selectedColumns->GetNumberOfTuples(); ++j)
          {
          vtkPlot* selectedPlot = this->ChartPrivate->GetPlotByColumn(ptr[j]);
          if (selectedPlot)
            {
            selectedPlots.push_back(selectedPlot);
            }
          }
        }
      // Now iterate through the plots to update selection data
      std::vector<vtkPlot*>::iterator it =
          this->ChartPrivate->plots.begin();
      for ( ; it != this->ChartPrivate->plots.end(); ++it)
        {
        vtkPlot* plot = *it;
        vtkIdTypeArray* plotSelection = 0;
        bool ownPlotSelection = false;
        bool isSelected =
          std::find(selectedPlots.begin(), selectedPlots.end(), plot) !=
          selectedPlots.end();
        if (isSelected)
          {
          static int idx = 1; // y
          vtkAbstractArray* column = plot->GetData()->GetInputAbstractArrayToProcess(
            idx, plot->GetInput());
          plotSelection = plot->GetSelection();
          if (!plotSelection || plotSelection->GetNumberOfTuples() != column->GetNumberOfTuples())
            {
            plotSelection = vtkIdTypeArray::New();
            ownPlotSelection = true;
            for (vtkIdType j = 0; j < column->GetNumberOfTuples(); ++j)
              {
              plotSelection->InsertNextValue(j);
              }
            }
          }
        plot->SetSelection(plotSelection);
        if (ownPlotSelection)
          {
          plotSelection->Delete();
          }
        }
      }
309 310 311 312 313
    }
  else
    {
    vtkDebugMacro("No annotation link set.");
    }
314 315

  this->CalculateBarPlots();
316 317 318

  if (this->AutoAxes)
    {
319 320
    vtkTuple<bool, 4> visibilities(false);
    for (int i = 0; i < static_cast<int>(this->ChartPrivate->PlotCorners.size()); ++i)
321 322 323 324 325
      {
      int visible = 0;
      for (unsigned int j = 0;
           j < this->ChartPrivate->PlotCorners[i]->GetNumberOfItems(); ++j)
        {
326 327
        if (vtkPlot::SafeDownCast(this->ChartPrivate->PlotCorners[i]
                                  ->GetItem(j))->GetVisible())
328 329 330 331 332 333
          {
          ++visible;
          }
        }
      if (visible)
        {
334 335
        visibilities[i % 4] = true;
        visibilities[(i+1) % 4] = true;
336 337
        }
      }
338 339 340 341
    for (int i = 0; i < 4; ++i)
      {
      this->ChartPrivate->axes[i]->SetVisible(visibilities[i]);
      }
342
    }
343 344
}

345 346 347 348 349
//-----------------------------------------------------------------------------
bool vtkChartXY::Paint(vtkContext2D *painter)
{
  // This is where everything should be drawn, or dispatched to other methods.
  vtkDebugMacro(<< "Paint event called.");
350 351 352 353 354
  if (!this->Visible)
    {
    // The geometry of the chart must be valid before anything can be drawn
    return false;
    }
355

356
  vtkVector2i geometry(0, 0);
357
  bool recalculateTransform = false;
358
  if (this->LayoutStrategy == vtkChart::FILL_SCENE)
359 360 361
    {
    geometry = vtkVector2i(this->GetScene()->GetSceneWidth(),
                           this->GetScene()->GetSceneHeight());
362
    if (geometry.GetX() != this->Geometry[0] || geometry.GetY() != this->Geometry[1])
363 364 365 366
      {
      recalculateTransform = true;
      this->LayoutChanged = true;
      }
367
    this->SetSize(vtkRectf(0.0, 0.0, geometry.GetX(), geometry.GetY()));
368 369
    }

370 371 372 373 374 375 376 377
  int visiblePlots = 0;
  for (size_t i = 0; i < this->ChartPrivate->plots.size(); ++i)
    {
    if (this->ChartPrivate->plots[i]->GetVisible())
      {
      ++visiblePlots;
      }
    }
378
  if (visiblePlots == 0 && !this->RenderEmpty)
379 380 381 382 383
    {
    // Nothing to plot, so don't draw anything.
    return false;
    }

384
  this->Update();
385

386
  if (this->MTime < this->ChartPrivate->axes[0]->GetMTime())
387
    {
388 389
    // Cause the plot transform to be recalculated if necessary
    recalculateTransform = true;
390
    this->LayoutChanged = true;
391 392
    }

393
  this->UpdateLayout(painter);
394

395 396
  // Recalculate the plot transform, min and max values if necessary
  if (!this->PlotTransformValid)
397
    {
398
    this->RecalculatePlotBounds();
399
    recalculateTransform = true;
400
    }
401
  if (this->UpdateLayout(painter) || recalculateTransform)
402
    {
403
    this->RecalculatePlotTransforms();
404
    }
405

406 407 408 409 410 411 412 413
  // Now that plot transforms, including whether to use log scaling and the
  // shift-scale factors, have been updated, we give the vtkPlot instances an
  // opportunity to update caches.
  for (size_t i = 0; i < this->ChartPrivate->plots.size(); ++i)
    {
    this->ChartPrivate->plots[i]->UpdateCache();
    }

414 415 416 417
  // Update the clipping if necessary
  this->ChartPrivate->Clip->SetClip(this->Point1[0], this->Point1[1],
                                    this->Point2[0]-this->Point1[0],
                                    this->Point2[1]-this->Point1[1]);
418

419 420 421
  // draw background
  if(this->BackgroundBrush)
    {
422
    painter->GetPen()->SetLineType(vtkPen::NO_PEN);
423 424 425 426 427
    painter->ApplyBrush(this->BackgroundBrush);
    painter->DrawRect(this->Point1[0], this->Point1[1],
                      this->Geometry[0], this->Geometry[1]);
    }

428 429 430
  // Use the scene to render most of the chart.
  this->PaintChildren(painter);

431 432 433 434 435 436
  // Draw the selection box if necessary
  if (this->DrawBox)
    {
    painter->GetBrush()->SetColor(255, 255, 255, 0);
    painter->GetPen()->SetColor(0, 0, 0, 255);
    painter->GetPen()->SetWidth(1.0);
437
    painter->GetPen()->SetLineType(vtkPen::SOLID_LINE);
438 439
    painter->DrawRect(this->MouseBox.GetX(), this->MouseBox.GetY(),
                      this->MouseBox.GetWidth(), this->MouseBox.GetHeight());
440 441
    }

442 443 444 445 446 447
  // Draw the selection polygon if necessary
  if (this->DrawSelectionPolygon)
    {
    painter->GetBrush()->SetColor(255, 0, 0, 0);
    painter->GetPen()->SetColor(0, 255, 0, 255);
    painter->GetPen()->SetWidth(2.0);
448
    painter->GetPen()->SetLineType(vtkPen::SOLID_LINE);
449 450 451 452 453 454 455 456 457

    const vtkContextPolygon &polygon = this->SelectionPolygon;

    // draw each line segment
    for(vtkIdType i = 0; i < polygon.GetNumberOfPoints() - 1; i++)
      {
      const vtkVector2f &a = polygon.GetPoint(i);
      const vtkVector2f &b = polygon.GetPoint(i+1);

458
      painter->DrawLine(a.GetX(), a.GetY(), b.GetX(), b.GetY());
459 460 461 462 463 464 465 466
      }

    // draw a line from the end to the start
    if(polygon.GetNumberOfPoints() >= 3)
      {
      const vtkVector2f &start = polygon.GetPoint(0);
      const vtkVector2f &end = polygon.GetPoint(polygon.GetNumberOfPoints() - 1);

467
      painter->DrawLine(start.GetX(), start.GetY(), end.GetX(), end.GetY());
468 469 470
      }
    }

471 472
  if (this->Title)
    {
473 474 475 476 477 478 479
    int offset = 0; // title margin.
    vtkAxis* topAxis = this->ChartPrivate->axes[vtkAxis::TOP];
    if (topAxis->GetVisible())
      {
      vtkRectf bounds = topAxis->GetBoundingRect(painter);
      offset += static_cast<int>(bounds.GetHeight());
      }
480
    vtkPoints2D *rect = vtkPoints2D::New();
481
    rect->InsertNextPoint(this->Point1[0], this->Point2[1] + offset);
482 483 484 485 486 487
    rect->InsertNextPoint(this->Point2[0]-this->Point1[0], 10);
    painter->ApplyTextProp(this->TitleProperties);
    painter->DrawStringRect(rect, this->Title);
    rect->Delete();
    }

488 489 490
  return true;
}

491 492 493 494 495
//-----------------------------------------------------------------------------
void vtkChartXY::CalculateBarPlots()
{
  // Calculate the width, spacing and offsets for the bar plot - they are grouped
  size_t n = this->ChartPrivate->plots.size();
496
  std::vector<vtkPlotBar *> bars;
497 498 499 500 501 502 503 504 505 506 507
  for (size_t i = 0; i < n; ++i)
    {
    vtkPlotBar* bar = vtkPlotBar::SafeDownCast(this->ChartPrivate->plots[i]);
    if (bar && bar->GetVisible())
      {
      bars.push_back(bar);
      }
    }
  if (bars.size())
    {
    // We have some bar plots - work out offsets etc.
508
    float barWidth = 0.1;
509 510 511 512
    vtkPlotBar* bar = bars[0];
    if (!bar->GetUseIndexForXSeries())
      {
      vtkTable *table = bar->GetData()->GetInput();
513
      if (table)
514
        {
515
        vtkDataArray* x = bar->GetData()->GetInputArrayToProcess(0, table);
516
        if (x && x->GetNumberOfTuples() > 1)
517 518 519
          {
          double x0 = x->GetTuple1(0);
          double x1 = x->GetTuple1(1);
520
          float width = static_cast<float>(fabs(x1 - x0) * this->BarWidthFraction);
521 522
          barWidth = width / bars.size();
          }
523 524 525 526 527 528
        }
      }
    else
      {
      barWidth = 1.0f / bars.size() * this->BarWidthFraction;
      }
529

530
    // Now set the offsets and widths on each bar
531 532
    // The offsetIndex deals with the fact that half the bars
    // must shift to the left of the point and half to the right
533
    int offsetIndex = static_cast<int>(bars.size() - 1);
534
    for (size_t i = 0; i < bars.size(); ++i)
535 536
      {
      bars[i]->SetWidth(barWidth);
537 538 539 540 541 542 543 544
      bars[i]->SetOffset(offsetIndex * (barWidth / 2));
      // Increment by two since we need to shift by half widths
      // but make room for entire bars. Increment backwards because
      // offsets are always subtracted and Positive offsets move
      // the bar leftwards.  Negative offsets will shift the bar
      // to the right.
      offsetIndex -= 2;
      //bars[i]->SetOffset(float(bars.size()-i-1)*(barWidth/2));
545 546 547 548
      }
    }
}

549
//-----------------------------------------------------------------------------
550
void vtkChartXY::RecalculatePlotTransforms()
551
{
552
  for (int i = 0; i < int(this->ChartPrivate->PlotCorners.size()); ++i)
553
    {
554
    if (this->ChartPrivate->PlotCorners[i]->GetNumberOfItems())
555
      {
556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579
      vtkAxis *xAxis = 0;
      vtkAxis *yAxis = 0;
      // Get the appropriate axes, and recalculate the transform.
      switch (i)
        {
        case 0:
          {
          xAxis = this->ChartPrivate->axes[vtkAxis::BOTTOM];
          yAxis = this->ChartPrivate->axes[vtkAxis::LEFT];
          break;
          }
        case 1:
          xAxis = this->ChartPrivate->axes[vtkAxis::BOTTOM];
          yAxis = this->ChartPrivate->axes[vtkAxis::RIGHT];
          break;
        case 2:
          xAxis = this->ChartPrivate->axes[vtkAxis::TOP];
          yAxis = this->ChartPrivate->axes[vtkAxis::RIGHT];
          break;
        case 3:
          xAxis = this->ChartPrivate->axes[vtkAxis::TOP];
          yAxis = this->ChartPrivate->axes[vtkAxis::LEFT];
          break;
        default:
580 581
          vtkWarningMacro(
            "Error: default case in recalculate plot transforms.");
582
        }
583 584
      this->CalculatePlotTransform(
        xAxis, yAxis, this->ChartPrivate->PlotCorners[i]->GetTransform());
585 586
      // Now we need to set the scale factor on the plots to ensure they rescale
      // their input data when necessary.
587 588
      vtkRectd shiftScale(xAxis->GetShift(), yAxis->GetShift(),
                          xAxis->GetScalingFactor(), yAxis->GetScalingFactor());
589
      for (unsigned int j = 0;
590 591 592 593 594 595
           j < this->ChartPrivate->PlotCorners[i]->GetNumberOfItems(); ++j)
        {
        vtkPlot *plot =
            vtkPlot::SafeDownCast(this->ChartPrivate->PlotCorners[i]->GetItem(j));
        if (plot)
          {
596
          plot->SetShiftScale(shiftScale);
597 598
          }
        }
599 600
      }
    }
601
  this->PlotTransformValid = true;
602 603 604
}

//-----------------------------------------------------------------------------
605
int vtkChartXY::GetPlotCorner(vtkPlot *plot)
606
{
607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
  vtkAxis *x = plot->GetXAxis();
  vtkAxis *y = plot->GetYAxis();
  if (x == this->ChartPrivate->axes[vtkAxis::BOTTOM] &&
      y == this->ChartPrivate->axes[vtkAxis::LEFT])
    {
    return 0;
    }
  else if (x == this->ChartPrivate->axes[vtkAxis::BOTTOM] &&
           y == this->ChartPrivate->axes[vtkAxis::RIGHT])
    {
    return 1;
    }
  else if (x == this->ChartPrivate->axes[vtkAxis::TOP] &&
           y == this->ChartPrivate->axes[vtkAxis::RIGHT])
    {
    return 2;
    }
  else if (x == this->ChartPrivate->axes[vtkAxis::TOP] &&
           y == this->ChartPrivate->axes[vtkAxis::LEFT])
    {
    return 3;
    }
  else
    {
    // Should never happen.
    return 4;
    }
}
635

636 637 638 639 640 641 642 643 644
//-----------------------------------------------------------------------------
void vtkChartXY::SetPlotCorner(vtkPlot *plot, int corner)
{
  if (corner < 0 || corner > 3)
    {
    vtkWarningMacro("Invalid corner specified, should be between 0 and 3: "
                    << corner);
    return;
    }
645 646 647 648
  if (this->GetPlotCorner(plot) == corner)
    {
    return;
    }
649
  this->RemovePlotFromCorners(plot);
650
  // Grow the plot corners if necessary
651
  while (static_cast<int>(this->ChartPrivate->PlotCorners.size() - 1) < corner)
652
    {
653 654 655
    vtkNew<vtkContextTransform> transform;
    this->ChartPrivate->PlotCorners.push_back(transform.GetPointer());
    this->ChartPrivate->Clip->AddItem(transform.GetPointer()); // Clip maintains ownership.
656 657
    }
  this->ChartPrivate->PlotCorners[corner]->AddItem(plot);
658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691
  if (corner == 0)
    {
    plot->SetXAxis(this->ChartPrivate->axes[vtkAxis::BOTTOM]);
    plot->SetYAxis(this->ChartPrivate->axes[vtkAxis::LEFT]);
    }
  else if (corner == 1)
    {
    plot->SetXAxis(this->ChartPrivate->axes[vtkAxis::BOTTOM]);
    plot->SetYAxis(this->ChartPrivate->axes[vtkAxis::RIGHT]);
    }
  else if (corner == 2)
    {
    plot->SetXAxis(this->ChartPrivate->axes[vtkAxis::TOP]);
    plot->SetYAxis(this->ChartPrivate->axes[vtkAxis::RIGHT]);
    }
  else if (corner == 3)
    {
    plot->SetXAxis(this->ChartPrivate->axes[vtkAxis::TOP]);
    plot->SetYAxis(this->ChartPrivate->axes[vtkAxis::LEFT]);
    }
  this->PlotTransformValid = false;
}

//-----------------------------------------------------------------------------
void vtkChartXY::RecalculatePlotBounds()
{
  // Get the bounds of each plot, and each axis  - ordering as laid out below
  double y1[] = { 0.0, 0.0 }; // left -> 0
  double x1[] = { 0.0, 0.0 }; // bottom -> 1
  double y2[] = { 0.0, 0.0 }; // right -> 2
  double x2[] = { 0.0, 0.0 }; // top -> 3
  // Store whether the ranges have been initialized - follows same order
  bool initialized[] = { false, false, false, false };

692
  std::vector<vtkPlot*>::iterator it;
693
  double bounds[4] = { 0.0, 0.0, 0.0, 0.0 };
694 695
  for (it = this->ChartPrivate->plots.begin();
       it != this->ChartPrivate->plots.end(); ++it)
696
    {
697
    if ((*it)->GetVisible() == false)
698 699 700
      {
      continue;
      }
701
    (*it)->GetBounds(bounds);
702
    if (bounds[1] - bounds[0] < 0.0)
Utkarsh Ayachit's avatar
Utkarsh Ayachit committed
703 704 705 706
      {
      // skip uninitialized bounds.
      continue;
      }
707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730
    int corner = this->GetPlotCorner(*it);

    // Initialize the appropriate ranges, or push out the ranges
    if ((corner == 0 || corner == 3)) // left
      {
      if (!initialized[0])
        {
        y1[0] = bounds[2];
        y1[1] = bounds[3];
        initialized[0] = true;
        }
      else
        {
        if (y1[0] > bounds[2]) // min
          {
          y1[0] = bounds[2];
          }
        if (y1[1] < bounds[3]) // max
          {
          y1[1] = bounds[3];
          }
        }
      }
    if ((corner == 0 || corner == 1)) // bottom
731
      {
732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748
      if (!initialized[1])
        {
        x1[0] = bounds[0];
        x1[1] = bounds[1];
        initialized[1] = true;
        }
      else
        {
        if (x1[0] > bounds[0]) // min
          {
          x1[0] = bounds[0];
          }
        if (x1[1] < bounds[1]) // max
          {
          x1[1] = bounds[1];
          }
        }
749
      }
750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770
    if ((corner == 1 || corner == 2)) // right
      {
      if (!initialized[2])
        {
        y2[0] = bounds[2];
        y2[1] = bounds[3];
        initialized[2] = true;
        }
      else
        {
        if (y2[0] > bounds[2]) // min
          {
          y2[0] = bounds[2];
          }
        if (y2[1] < bounds[3]) // max
          {
          y2[1] = bounds[3];
          }
        }
      }
    if ((corner == 2 || corner == 3)) // top
771
      {
772
      if (!initialized[3])
773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788
        {
        x2[0] = bounds[0];
        x2[1] = bounds[1];
        initialized[3] = true;
        }
      else
        {
        if (x2[0] > bounds[0]) // min
          {
          x2[0] = bounds[0];
          }
        if (x2[1] < bounds[1]) // max
          {
          x2[1] = bounds[1];
          }
        }
789
      }
790
    }
791

792
  // Now set the newly calculated bounds on the axes
793
  for (int i = 0; i < 4; ++i)
794
    {
795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814
    vtkAxis *axis = this->ChartPrivate->axes[i];
    double *range = 0;
    switch (i)
      {
      case 0:
        range = y1;
        break;
      case 1:
        range = x1;
        break;
      case 2:
        range = y2;
        break;
      case 3:
        range = x2;
        break;
      default:
        return;
      }

815 816
    if (this->ForceAxesToBounds)
      {
817 818
      axis->SetMinimumLimit(range[0]);
      axis->SetMaximumLimit(range[1]);
819
      }
820
    if (axis->GetBehavior() == vtkAxis::AUTO && initialized[i])
821 822 823 824
      {
      axis->SetRange(range[0], range[1]);
      axis->AutoScale();
      }
825
    }
826

827
  this->Modified();
828 829
}

830
//-----------------------------------------------------------------------------
831
bool vtkChartXY::UpdateLayout(vtkContext2D* painter)
832 833
{
  // The main use of this method is currently to query the visible axes for
834
  // their bounds, and to update the chart in response to that.
835
  bool changed = false;
836

837 838 839
  vtkVector2i tileScale = this->Scene->GetLogicalTileScale();
  vtkVector2i hiddenAxisBorder = tileScale * this->HiddenAxisBorder;

840
  // Axes
841 842
  if (this->LayoutStrategy == vtkChart::FILL_SCENE ||
      this->LayoutStrategy == vtkChart::FILL_RECT)
843
    {
844
    for (int i = 0; i < 4; ++i)
845
      {
846 847 848 849 850 851
      int border = 0;
      vtkAxis* axis = this->ChartPrivate->axes[i];
      axis->Update();
      if (axis->GetVisible())
        {
        vtkRectf bounds = axis->GetBoundingRect(painter);
852
        if (i == vtkAxis::TOP || i == vtkAxis::BOTTOM)
853 854 855 856 857 858 859
          {// Horizontal axes
          border = int(bounds.GetHeight());
          }
        else
          {// Vertical axes
          border = int(bounds.GetWidth());
          }
860
        }
861
      border += this->GetLegendBorder(painter, i);
862 863 864 865 866 867 868
      if (i == vtkAxis::TOP && this->Title)
        {
        painter->ApplyTextProp(this->TitleProperties);
        float bounds[4];
        painter->ComputeStringBounds(this->Title, bounds);
        if (bounds[3] > 0)
          {
869
          border += (5 * tileScale.GetY()) /* title margin */
870 871 872 873
                    + bounds[3]; // add the title text height to the border.
          }
        }

874 875 876 877 878 879 880 881 882
      if (i == vtkAxis::TOP || i == vtkAxis::BOTTOM)
        {
        border = std::max(border, hiddenAxisBorder.GetY());
        }
      else
        {
        border = std::max(border, hiddenAxisBorder.GetX());
        }

883 884 885 886
      if (this->ChartPrivate->Borders[i] != border)
        {
        this->ChartPrivate->Borders[i] = border;
        changed = true;
887 888 889 890 891 892 893 894
        }
      }
    }

  if (this->LayoutChanged || changed)
    {
    if (this->DrawAxesAtOrigin)
      {
895 896
      this->SetBorders(hiddenAxisBorder.GetX(),
                       hiddenAxisBorder.GetY(),
897 898 899
                       this->ChartPrivate->Borders[2],
                       this->ChartPrivate->Borders[3]);
      // Get the screen coordinates for the origin, and move the axes there.
900
      vtkVector2f origin(0.0);
901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932
      vtkTransform2D* transform =
          this->ChartPrivate->PlotCorners[0]->GetTransform();
      transform->TransformPoints(origin.GetData(), origin.GetData(), 1);
      // Need to clamp the axes in the plot area.
      if (int(origin[0]) < this->Point1[0])
        {
        origin[0] = this->Point1[0];
        }
      if (int(origin[0]) > this->Point2[0])
        {
        origin[0] = this->Point2[0];
        }
      if (int(origin[1]) < this->Point1[1])
        {
        origin[1] = this->Point1[1];
        }
      if (int(origin[1]) > this->Point2[1])
        {
        origin[1] = this->Point2[1];
        }

      this->ChartPrivate->axes[vtkAxis::BOTTOM]
          ->SetPoint1(this->Point1[0], origin[1]);
      this->ChartPrivate->axes[vtkAxis::BOTTOM]
          ->SetPoint2(this->Point2[0], origin[1]);
      this->ChartPrivate->axes[vtkAxis::LEFT]
          ->SetPoint1(origin[0], this->Point1[1]);
      this->ChartPrivate->axes[vtkAxis::LEFT]
          ->SetPoint2(origin[0], this->Point2[1]);
      }
    else
      {
933 934 935
      if (this->LayoutStrategy == vtkChart::AXES_TO_RECT)
        {
        this->SetBorders(0, 0, 0, 0);
936 937 938 939
        this->ChartPrivate->axes[0]->GetBoundingRect(painter);
        this->ChartPrivate->axes[1]->GetBoundingRect(painter);
        this->ChartPrivate->axes[2]->GetBoundingRect(painter);
        this->ChartPrivate->axes[3]->GetBoundingRect(painter);
940 941 942 943 944 945 946 947
        }
      else
        {
        this->SetBorders(this->ChartPrivate->Borders[0],
                         this->ChartPrivate->Borders[1],
                         this->ChartPrivate->Borders[2],
                         this->ChartPrivate->Borders[3]);
        }
948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967
      // This is where we set the axes up too
      // Y axis (left)
      this->ChartPrivate->axes[0]->SetPoint1(this->Point1[0], this->Point1[1]);
      this->ChartPrivate->axes[0]->SetPoint2(this->Point1[0], this->Point2[1]);
      // X axis (bottom)
      this->ChartPrivate->axes[1]->SetPoint1(this->Point1[0], this->Point1[1]);
      this->ChartPrivate->axes[1]->SetPoint2(this->Point2[0], this->Point1[1]);
      }
    // Y axis (right)
    this->ChartPrivate->axes[2]->SetPoint1(this->Point2[0], this->Point1[1]);
    this->ChartPrivate->axes[2]->SetPoint2(this->Point2[0], this->Point2[1]);
    // X axis (top)
    this->ChartPrivate->axes[3]->SetPoint1(this->Point1[0], this->Point2[1]);
    this->ChartPrivate->axes[3]->SetPoint2(this->Point2[0], this->Point2[1]);

    for (int i = 0; i < 4; ++i)
      {
      this->ChartPrivate->axes[i]->Update();
      }
    }
968
  this->SetLegendPosition(this->Legend->GetBoundingRect(painter));
969 970

  return changed;
971 972
}

973 974 975 976 977 978 979 980
//-----------------------------------------------------------------------------
int vtkChartXY::GetLegendBorder(vtkContext2D* painter, int axisPosition)
{
  if (!this->Legend->GetVisible() || this->Legend->GetInline())
    {
    return 0;
    }

981 982
  vtkVector2i tileScale = this->Scene->GetLogicalTileScale();

983
  int padding = 10;
984
  vtkVector2i legendSize(0, 0);
985 986 987 988
  vtkVector2i legendAlignment(this->Legend->GetHorizontalAlignment(),
                              this->Legend->GetVerticalAlignment());
  this->Legend->Update();
  vtkRectf rect = this->Legend->GetBoundingRect(painter);
989 990
  legendSize.Set(static_cast<int>(rect.GetWidth()),
                 static_cast<int>(rect.GetHeight()));
991 992 993

  // Figure out the correct place and alignment based on the legend layout.
  if (axisPosition == vtkAxis::LEFT &&
994
      legendAlignment.GetX() == vtkChartLegend::LEFT)
995
    {
996
    return legendSize.GetX() + padding * tileScale.GetX();
997 998
    }
  else if (axisPosition == vtkAxis::RIGHT &&
999
           legendAlignment.GetX() == vtkChartLegend::RIGHT)
1000
    {
1001
    return legendSize.GetX() + padding * tileScale.GetX();
1002 1003
    }
  else if ((axisPosition == vtkAxis::TOP || axisPosition == vtkAxis::BOTTOM) &&
1004 1005
           (legendAlignment.GetX() == vtkChartLegend::LEFT ||
            legendAlignment.GetX() == vtkChartLegend::RIGHT))
1006 1007 1008 1009
    {
    return 0;
    }
  else if (axisPosition == vtkAxis::TOP &&
1010
           legendAlignment.GetY() == vtkChartLegend::TOP)
1011
    {
1012
    return legendSize.GetY() + padding * tileScale.GetY();
1013 1014
    }
  else if (axisPosition == vtkAxis::BOTTOM &&
1015
           legendAlignment.GetY() == vtkChartLegend::BOTTOM)
1016
    {
1017
    return legendSize.GetY() + padding * tileScale.GetY();
1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028
    }
  else
    {
    return 0;
    }
}

//-----------------------------------------------------------------------------
void vtkChartXY::SetLegendPosition(const vtkRectf& rect)
{
  // Put the legend in the top corner of the chart
1029
  vtkVector2f pos(0, 0);
1030 1031 1032 1033
  int padding = 5;
  vtkVector2i legendAlignment(this->Legend->GetHorizontalAlignment(),
                              this->Legend->GetVerticalAlignment());

1034 1035 1036 1037 1038 1039
  if (legendAlignment[0] == vtkChartLegend::CUSTOM ||
      legendAlignment[1] == vtkChartLegend::CUSTOM)
    {
    return;
    }

1040 1041 1042 1043 1044 1045 1046 1047 1048
  if (this->Legend->GetInline())
    {
    switch (this->Legend->GetHorizontalAlignment())
      {
      case vtkChartLegend::LEFT:
        pos.SetX(this->Point1[0]);
        break;
      case vtkChartLegend::CENTER:
        pos.SetX(((this->Point2[0] - this->Point1[0]) / 2.0)
1049
                 - rect.GetWidth() / 2.0 + this->Point1[0]);
1050 1051 1052
        break;
      case vtkChartLegend::RIGHT:
      default:
1053
        pos.SetX(this->Point2[0] - rect.GetWidth());
1054 1055 1056 1057
      }
    switch (this->Legend->GetVerticalAlignment())
      {
      case vtkChartLegend::TOP:
1058
        pos.SetY(this->Point2[1] - rect.GetHeight());
1059 1060 1061
        break;
      case vtkChartLegend::CENTER:
        pos.SetY((this->Point2[1] - this->Point1[1]) / 2.0
1062
                 - rect.GetHeight() / 2.0 + this->Point1[1]);
1063 1064 1065 1066 1067 1068 1069 1070 1071
        break;
      case vtkChartLegend::BOTTOM:
      default:
        pos.SetY(this->Point1[1]);
      }
    }
  else
    {
    // Non-inline legends.
1072
    if (legendAlignment.GetX() == vtkChartLegend::LEFT)
1073 1074 1075 1076
      {
      pos.SetX(this->Point1[0] - this->ChartPrivate->Borders[vtkAxis::LEFT]
               + padding);
      }
1077
    else if (legendAlignment.GetX() == vtkChartLegend::RIGHT)
1078 1079
      {
      pos.SetX(this->Point2[0] + this->ChartPrivate->Borders[vtkAxis::RIGHT]
1080
               - rect.GetWidth() - padding);
1081
      }
1082
    else if (legendAlignment.GetX() == vtkChartLegend::CENTER)
1083 1084
      {
      pos.SetX(((this->Point2[0] - this->Point1[0]) / 2.0)
1085
               - (rect.GetWidth() / 2.0) + this->Point1[0]);
1086
      // Check for the special case where the legend is on the top or bottom
1087
      if (legendAlignment.GetY() == vtkChartLegend::TOP)
1088 1089
        {
        pos.SetY(this->Point2[1] + this->ChartPrivate->Borders[vtkAxis::TOP]
1090
                 - rect.GetHeight() - padding);
1091
        }
1092
      else if (legendAlignment.GetY() == vtkChartLegend::BOTTOM)
1093 1094 1095 1096 1097 1098
        {
        pos.SetY(this->Point1[1] - this->ChartPrivate->Borders[vtkAxis::BOTTOM]
                 + padding);
        }
      }
    // Vertical alignment
1099
    if (legendAlignment.GetX() != vtkChartLegend::CENTER)
1100
      {
1101
      if (legendAlignment.GetY() == vtkChartLegend::TOP)
1102
        {
1103
        pos.SetY(this->Point2[1] - rect.GetHeight());
1104
        }
1105
      else if (legendAlignment.GetY() == vtkChartLegend::BOTTOM)
1106 1107 1108 1109
        {
        pos.SetY(this->Point1[1]);
        }
      }
1110
    if (legendAlignment.GetY() == vtkChartLegend::CENTER)
1111 1112
      {
      pos.SetY(((this->Point2[1] - this->Point1[1]) / 2.0)
1113
               - (rect.GetHeight() / 2.0) + this->Point1[1]);
1114 1115 1116 1117 1118 1119
      }
    }

  this->Legend->SetPoint(pos);
}

1120
//-----------------------------------------------------------------------------
1121
vtkPlot * vtkChartXY::AddPlot(int type)
1122
{
1123 1124 1125 1126 1127
  // Use a variable to return the object created (or NULL), this is necessary
  // as the HP compiler is broken (thinks this function does not return) and
  // the MS compiler generates a warning about unreachable code if a redundant
  // return is added at the end.
  vtkPlot *plot = NULL;
1128
  vtkColor3ub color = this->ChartPrivate->Colors->GetColorRepeating(
1129
      static_cast<int>(this->ChartPrivate->plots.size()));
1130 1131 1132 1133 1134
  switch (type)
    {
    case LINE:
      {
      vtkPlotLine *line = vtkPlotLine::New();
1135
      line->GetPen()->SetColor(color.GetData());
1136
      plot = line;
1137 1138 1139 1140 1141
      break;
      }
    case POINTS:
      {
      vtkPlotPoints *points = vtkPlotPoints::New();
1142
      points->GetPen()->SetColor(color.GetData());
1143
      plot = points;
1144 1145
      break;
      }
1146 1147 1148
    case BAR:
      {
      vtkPlotBar *bar = vtkPlotBar::New();
1149
      bar->GetBrush()->SetColor(color.GetData());
1150 1151 1152
      plot = bar;
      break;
      }
1153 1154 1155
    case FUNCTIONALBAG:
      {
      vtkPlotFunctionalBag *bag = vtkPlotFunctionalBag::New();
1156
      bag->GetPen()->SetColor(color.GetData());
1157 1158 1159 1160
      bag->GetBrush()->SetColor(color.GetData());
      plot = bag;
      break;
      }
Keith Fieldhouse's avatar
Keith Fieldhouse committed
1161 1162 1163 1164 1165 1166 1167 1168
    case STACKED:
      {
      vtkPlotStacked *stacked = vtkPlotStacked::New();
      stacked->SetParent(this);
      stacked->GetBrush()->SetColor(color.GetData());
      plot = stacked;
      break;
      }
1169 1170 1171 1172 1173 1174 1175 1176
    case BAG:
      {
      vtkPlotBag *bag = vtkPlotBag::New();
      bag->SetParent(this);
      bag->GetBrush()->SetColor(color.GetData());
      plot = bag;
      break;
      }
1177 1178 1179 1180 1181 1182 1183 1184
    case AREA:
      {
      vtkPlotArea* area = vtkPlotArea::New();
      area->SetParent(this);
      area->GetBrush()->SetColor(color.GetData());
      plot = area;
      break;
      }
1185

1186
    default:
1187
      plot = NULL;
1188
    }
1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204
  if (plot)
    {
    this->AddPlot(plot);
    plot->Delete();
    }
  return plot;
}

//-----------------------------------------------------------------------------
vtkIdType vtkChartXY::AddPlot(vtkPlot * plot)
{
  if (plot == NULL)
    {
    return -1;
    }
  plot->Register(this);
1205
  this->ChartPrivate->plots.push_back(plot);
1206
  vtkIdType plotIndex = this->ChartPrivate->plots.size() - 1;
1207
  this->SetPlotCorner(plot, 0);
1208 1209
  // Ensure that the bounds are recalculated
  this->PlotTransformValid = false;
1210
  // Mark the scene as dirty
1211 1212 1213 1214
  if (this->Scene)
    {
    this->Scene->SetDirty(true);
    }
1215
  return plotIndex;