1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkChartXY.cxx
5 
6   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7   All rights reserved.
8   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9 
10      This software is distributed WITHOUT ANY WARRANTY; without even
11      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12      PURPOSE.  See the above copyright notice for more information.
13 
14 =========================================================================*/
15 
16 #include "vtkChartXY.h"
17 
18 #include "vtkContext2D.h"
19 #include "vtkPen.h"
20 #include "vtkBrush.h"
21 #include "vtkColorSeries.h"
22 #include "vtkChartSelectionHelper.h"
23 
24 #include "vtkMath.h"
25 #include "vtkTransform2D.h"
26 #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 "vtkPlotBar.h"
36 #include "vtkPlotBag.h"
37 #include "vtkPlotFunctionalBag.h"
38 #include "vtkPlotStacked.h"
39 #include "vtkPlotLine.h"
40 #include "vtkPlotPoints.h"
41 #include "vtkContextMapper2D.h"
42 
43 #include "vtkAxis.h"
44 #include "vtkPlotGrid.h"
45 #include "vtkChartLegend.h"
46 #include "vtkTooltipItem.h"
47 
48 #include "vtkDataSetAttributes.h"
49 #include "vtkTable.h"
50 #include "vtkIdTypeArray.h"
51 
52 #include "vtkAnnotationLink.h"
53 #include "vtkSelection.h"
54 #include "vtkSelectionNode.h"
55 #include "vtkSmartPointer.h"
56 
57 #include "vtkObjectFactory.h"
58 #include "vtkCommand.h"
59 
60 #include "vtkStdString.h"
61 #include "vtkTextProperty.h"
62 
63 #include "vtkDataArray.h"
64 #include "vtkStringArray.h"
65 
66 // My STL containers
67 #include <vector>
68 #include <algorithm>
69 
70 //-----------------------------------------------------------------------------
71 class vtkChartXYPrivate
72 {
73 public:
vtkChartXYPrivate()74   vtkChartXYPrivate()
75     {
76     this->Colors = vtkSmartPointer<vtkColorSeries>::New();
77     this->Clip = vtkSmartPointer<vtkContextClip>::New();
78     this->Borders[0] = 60;
79     this->Borders[1] = 50;
80     this->Borders[2] = 20;
81     this->Borders[3] = 20;
82     }
GetPlotByColumn(vtkIdType columnId)83   vtkPlot* GetPlotByColumn(vtkIdType columnId)
84     {
85     std::vector<vtkPlot*>::iterator it =
86           this->plots.begin();
87       for ( ; it != this->plots.end(); ++it)
88         {
89         vtkPlot* plot = *it;
90         vtkTable* table = plot->GetInput();
91         const int idx = 1; // column
92         if (table &&
93             table->GetColumn(columnId) ==
94             plot->GetData()->GetInputAbstractArrayToProcess(idx, table))
95           {
96           return plot;
97           }
98         }
99       return 0;
100     }
101 
102   std::vector<vtkPlot *> plots; // Charts can contain multiple plots of data
103   std::vector<vtkContextTransform *> PlotCorners; // Stored by corner...
104   std::vector<vtkAxis *> axes; // Charts can contain multiple axes
105   vtkSmartPointer<vtkColorSeries> Colors; // Colors in the chart
106   vtkSmartPointer<vtkContextClip> Clip; // Colors in the chart
107   int Borders[4];
108 };
109 
110 //-----------------------------------------------------------------------------
111 vtkStandardNewMacro(vtkChartXY);
112 
113 //-----------------------------------------------------------------------------
vtkChartXY()114 vtkChartXY::vtkChartXY()
115 {
116   this->ChartPrivate = new vtkChartXYPrivate;
117 
118   this->AutoAxes = true;
119   this->HiddenAxisBorder = 20;
120 
121   // The plots are drawn in a clipped, transformed area.
122   this->AddItem(this->ChartPrivate->Clip);
123 
124   // The grid is drawn first in this clipped, transformed area.
125   vtkPlotGrid *grid1 = vtkPlotGrid::New();
126   this->ChartPrivate->Clip->AddItem(grid1);
127   grid1->Delete();
128 
129   // The second grid for the far side/top axis
130   vtkPlotGrid *grid2 = vtkPlotGrid::New();
131   this->ChartPrivate->Clip->AddItem(grid2);
132   grid2->Delete();
133 
134   // Set up the bottom-left transform, the rest are often not required (set up
135   // on demand if used later). Add it as a child item, rendered automatically.
136   vtkSmartPointer<vtkContextTransform> corner =
137       vtkSmartPointer<vtkContextTransform>::New();
138   this->ChartPrivate->PlotCorners.push_back(corner);
139   this->ChartPrivate->Clip->AddItem(corner); // Child list maintains ownership.
140 
141   // Next is the axes
142   for (int i = 0; i < 4; ++i)
143     {
144     this->ChartPrivate->axes.push_back(vtkAxis::New());
145     // By default just show the left and bottom axes
146     this->ChartPrivate->axes.back()->SetVisible(i < 2 ? true : false);
147     this->AttachAxisRangeListener(this->ChartPrivate->axes.back());
148     this->AddItem(this->ChartPrivate->axes.back());
149     }
150   this->ChartPrivate->axes[vtkAxis::LEFT]->SetPosition(vtkAxis::LEFT);
151   this->ChartPrivate->axes[vtkAxis::BOTTOM]->SetPosition(vtkAxis::BOTTOM);
152   this->ChartPrivate->axes[vtkAxis::RIGHT]->SetPosition(vtkAxis::RIGHT);
153   this->ChartPrivate->axes[vtkAxis::TOP]->SetPosition(vtkAxis::TOP);
154 
155   // Set up the x and y axes - should be configured based on data
156   this->ChartPrivate->axes[vtkAxis::LEFT]->SetTitle("Y Axis");
157   this->ChartPrivate->axes[vtkAxis::BOTTOM]->SetTitle("X Axis");
158 
159   grid1->SetXAxis(this->ChartPrivate->axes[vtkAxis::BOTTOM]);
160   grid1->SetYAxis(this->ChartPrivate->axes[vtkAxis::LEFT]);
161   grid2->SetXAxis(this->ChartPrivate->axes[vtkAxis::TOP]);
162   grid2->SetYAxis(this->ChartPrivate->axes[vtkAxis::RIGHT]);
163 
164   // Then the legend is drawn
165   this->Legend = vtkSmartPointer<vtkChartLegend>::New();
166   this->Legend->SetChart(this);
167   this->Legend->SetVisible(false);
168   this->AddItem(this->Legend);
169 
170   this->PlotTransformValid = false;
171 
172   this->DrawBox = false;
173   this->DrawSelectionPolygon = false;
174   this->DrawNearestPoint = false;
175   this->DrawAxesAtOrigin = false;
176   this->BarWidthFraction = 0.8f;
177 
178   this->Tooltip = vtkSmartPointer<vtkTooltipItem>::New();
179   this->Tooltip->SetVisible(false);
180   this->AddItem(this->Tooltip);
181   this->LayoutChanged = true;
182 
183   this->ForceAxesToBounds = false;
184 }
185 
186 //-----------------------------------------------------------------------------
~vtkChartXY()187 vtkChartXY::~vtkChartXY()
188 {
189   for (unsigned int i = 0; i < this->ChartPrivate->plots.size(); ++i)
190     {
191     this->ChartPrivate->plots[i]->Delete();
192     }
193   for (size_t i = 0; i < 4; ++i)
194     {
195     this->ChartPrivate->axes[i]->Delete();
196     }
197   delete this->ChartPrivate;
198   this->ChartPrivate = 0;
199 }
200 
201 //-----------------------------------------------------------------------------
Update()202 void vtkChartXY::Update()
203 {
204   // Perform any necessary updates that are not graphical
205   // Update the plots if necessary
206   for (size_t i = 0; i < this->ChartPrivate->plots.size(); ++i)
207     {
208     this->ChartPrivate->plots[i]->Update();
209     }
210   this->Legend->Update();
211 
212   // Update the selections if necessary.
213   if (this->AnnotationLink)
214     {
215     this->AnnotationLink->Update();
216     vtkSelection *selection =
217       vtkSelection::SafeDownCast(this->AnnotationLink->GetOutputDataObject(2));
218     // Two major selection methods - row based or plot based.
219     if (this->SelectionMethod == vtkChart::SELECTION_ROWS)
220       {
221       vtkSelectionNode *node = selection->GetNumberOfNodes() > 0?
222         selection->GetNode(0) : NULL;
223       vtkIdTypeArray *idArray = node?
224           vtkIdTypeArray::SafeDownCast(node->GetSelectionList()) : NULL;
225       std::vector<vtkPlot*>::iterator it =
226           this->ChartPrivate->plots.begin();
227       for ( ; it != this->ChartPrivate->plots.end(); ++it)
228         {
229         // Use the first selection node for all plots to select the rows.
230         (*it)->SetSelection(idArray);
231         }
232       }
233     else if (this->SelectionMethod == vtkChart::SELECTION_PLOTS)
234       {
235       for (unsigned int i = 0; i < selection->GetNumberOfNodes(); ++i)
236         {
237         vtkSelectionNode *node = selection->GetNode(i);
238         vtkIdTypeArray *idArray =
239             vtkIdTypeArray::SafeDownCast(node->GetSelectionList());
240         vtkPlot *selectionPlot =
241             vtkPlot::SafeDownCast(node->GetProperties()->Get(vtkSelectionNode::PROP()));
242         // Now iterate through the plots to update selection data
243         std::vector<vtkPlot*>::iterator it =
244             this->ChartPrivate->plots.begin();
245         for ( ; it != this->ChartPrivate->plots.end(); ++it)
246           {
247           if (selectionPlot == *it)
248             {
249             (*it)->SetSelection(idArray);
250             }
251           }
252         }
253       }
254     else if (this->SelectionMethod == vtkChart::SELECTION_COLUMNS)
255       {
256       // Retrieve all the selected plots
257       std::vector<vtkPlot*> selectedPlots;
258       for (unsigned int i = 0; i < selection->GetNumberOfNodes(); ++i)
259         {
260         vtkSelectionNode *node = selection->GetNode(i);
261         vtkIdTypeArray *selectedColumns =
262             vtkIdTypeArray::SafeDownCast(node->GetSelectionList());
263         vtkIdType* ptr = reinterpret_cast<vtkIdType*>(selectedColumns->GetVoidPointer(0));
264         for (vtkIdType j = 0; j < selectedColumns->GetNumberOfTuples(); ++j)
265           {
266           vtkPlot* selectedPlot = this->ChartPrivate->GetPlotByColumn(ptr[j]);
267           if (selectedPlot)
268             {
269             selectedPlots.push_back(selectedPlot);
270             }
271           }
272         }
273       // Now iterate through the plots to update selection data
274       std::vector<vtkPlot*>::iterator it =
275           this->ChartPrivate->plots.begin();
276       for ( ; it != this->ChartPrivate->plots.end(); ++it)
277         {
278         vtkPlot* plot = *it;
279         vtkIdTypeArray* plotSelection = 0;
280         bool ownPlotSelection = false;
281         bool isSelected =
282           std::find(selectedPlots.begin(), selectedPlots.end(), plot) !=
283           selectedPlots.end();
284         if (isSelected)
285           {
286           static int idx = 1; // y
287           vtkAbstractArray* column = plot->GetData()->GetInputAbstractArrayToProcess(
288             idx, plot->GetInput());
289           plotSelection = plot->GetSelection();
290           if (!plotSelection || plotSelection->GetNumberOfTuples() != column->GetNumberOfTuples())
291             {
292             plotSelection = vtkIdTypeArray::New();
293             ownPlotSelection = true;
294             for (vtkIdType j = 0; j < column->GetNumberOfTuples(); ++j)
295               {
296               plotSelection->InsertNextValue(j);
297               }
298             }
299           }
300         plot->SetSelection(plotSelection);
301         if (ownPlotSelection)
302           {
303           plotSelection->Delete();
304           }
305         }
306       }
307     }
308   else
309     {
310     vtkDebugMacro("No annotation link set.");
311     }
312 
313   this->CalculateBarPlots();
314 
315   if (this->AutoAxes)
316     {
317     for (int i = 0; i < 4; ++i)
318       {
319       this->ChartPrivate->axes[i]->SetVisible(false);
320       }
321     for (size_t i = 0; i < this->ChartPrivate->PlotCorners.size(); ++i)
322       {
323       int visible = 0;
324       for (unsigned int j = 0;
325            j < this->ChartPrivate->PlotCorners[i]->GetNumberOfItems(); ++j)
326         {
327         if (vtkPlot::SafeDownCast(this->ChartPrivate->PlotCorners[i]
328                                   ->GetItem(j))->GetVisible())
329           {
330           ++visible;
331           }
332         }
333       if (visible)
334         {
335         if (i < 3)
336           {
337           this->ChartPrivate->axes[i]->SetVisible(true);
338           this->ChartPrivate->axes[i+1]->SetVisible(true);
339           }
340         else
341           {
342           this->ChartPrivate->axes[0]->SetVisible(true);
343           this->ChartPrivate->axes[3]->SetVisible(true);
344           }
345         }
346       }
347     }
348 }
349 
350 //-----------------------------------------------------------------------------
Paint(vtkContext2D * painter)351 bool vtkChartXY::Paint(vtkContext2D *painter)
352 {
353   // This is where everything should be drawn, or dispatched to other methods.
354   vtkDebugMacro(<< "Paint event called.");
355   if (!this->Visible)
356     {
357     // The geometry of the chart must be valid before anything can be drawn
358     return false;
359     }
360 
361   vtkVector2i geometry(0, 0);
362   bool recalculateTransform = false;
363   if (this->LayoutStrategy == vtkChart::FILL_SCENE)
364     {
365     geometry = vtkVector2i(this->GetScene()->GetSceneWidth(),
366                            this->GetScene()->GetSceneHeight());
367     if (geometry.GetX() != this->Geometry[0] || geometry.GetY() != this->Geometry[1])
368       {
369       recalculateTransform = true;
370       this->LayoutChanged = true;
371       }
372     this->SetSize(vtkRectf(0.0, 0.0, geometry.GetX(), geometry.GetY()));
373     }
374 
375   int visiblePlots = 0;
376   for (size_t i = 0; i < this->ChartPrivate->plots.size(); ++i)
377     {
378     if (this->ChartPrivate->plots[i]->GetVisible())
379       {
380       ++visiblePlots;
381       }
382     }
383   if (visiblePlots == 0 && !this->RenderEmpty)
384     {
385     // Nothing to plot, so don't draw anything.
386     return false;
387     }
388 
389   this->Update();
390 
391   if (this->MTime < this->ChartPrivate->axes[0]->GetMTime())
392     {
393     // Cause the plot transform to be recalculated if necessary
394     recalculateTransform = true;
395     this->LayoutChanged = true;
396     }
397 
398   this->UpdateLayout(painter);
399   // Recalculate the plot transform, min and max values if necessary
400   if (!this->PlotTransformValid)
401     {
402     this->RecalculatePlotBounds();
403     recalculateTransform = true;
404     }
405   if (this->UpdateLayout(painter) || recalculateTransform)
406     {
407     this->RecalculatePlotTransforms();
408     }
409 
410   // Update the clipping if necessary
411   this->ChartPrivate->Clip->SetClip(this->Point1[0], this->Point1[1],
412                                     this->Point2[0]-this->Point1[0],
413                                     this->Point2[1]-this->Point1[1]);
414 
415   // draw background
416   if(this->BackgroundBrush)
417     {
418     painter->GetPen()->SetLineType(vtkPen::NO_PEN);
419     painter->ApplyBrush(this->BackgroundBrush);
420     painter->DrawRect(this->Point1[0], this->Point1[1],
421                       this->Geometry[0], this->Geometry[1]);
422     }
423 
424   // Use the scene to render most of the chart.
425   this->PaintChildren(painter);
426 
427   // Draw the selection box if necessary
428   if (this->DrawBox)
429     {
430     painter->GetBrush()->SetColor(255, 255, 255, 0);
431     painter->GetPen()->SetColor(0, 0, 0, 255);
432     painter->GetPen()->SetWidth(1.0);
433     painter->GetPen()->SetLineType(vtkPen::SOLID_LINE);
434     painter->DrawRect(this->MouseBox.GetX(), this->MouseBox.GetY(),
435                       this->MouseBox.GetWidth(), this->MouseBox.GetHeight());
436     }
437 
438   // Draw the selection polygon if necessary
439   if (this->DrawSelectionPolygon)
440     {
441     painter->GetBrush()->SetColor(255, 0, 0, 0);
442     painter->GetPen()->SetColor(0, 255, 0, 255);
443     painter->GetPen()->SetWidth(2.0);
444     painter->GetPen()->SetLineType(vtkPen::SOLID_LINE);
445 
446     const vtkContextPolygon &polygon = this->SelectionPolygon;
447 
448     // draw each line segment
449     for(vtkIdType i = 0; i < polygon.GetNumberOfPoints() - 1; i++)
450       {
451       const vtkVector2f &a = polygon.GetPoint(i);
452       const vtkVector2f &b = polygon.GetPoint(i+1);
453 
454       painter->DrawLine(a.GetX(), a.GetY(), b.GetX(), b.GetY());
455       }
456 
457     // draw a line from the end to the start
458     if(polygon.GetNumberOfPoints() >= 3)
459       {
460       const vtkVector2f &start = polygon.GetPoint(0);
461       const vtkVector2f &end = polygon.GetPoint(polygon.GetNumberOfPoints() - 1);
462 
463       painter->DrawLine(start.GetX(), start.GetY(), end.GetX(), end.GetY());
464       }
465     }
466 
467   if (this->Title)
468     {
469     int offset = 0; // title margin.
470     vtkAxis* topAxis = this->ChartPrivate->axes[vtkAxis::TOP];
471     if (topAxis->GetVisible())
472       {
473       vtkRectf bounds = topAxis->GetBoundingRect(painter);
474       offset += static_cast<int>(bounds.GetHeight());
475       }
476     vtkPoints2D *rect = vtkPoints2D::New();
477     rect->InsertNextPoint(this->Point1[0], this->Point2[1] + offset);
478     rect->InsertNextPoint(this->Point2[0]-this->Point1[0], 10);
479     painter->ApplyTextProp(this->TitleProperties);
480     painter->DrawStringRect(rect, this->Title);
481     rect->Delete();
482     }
483 
484   return true;
485 }
486 
487 //-----------------------------------------------------------------------------
CalculateBarPlots()488 void vtkChartXY::CalculateBarPlots()
489 {
490   // Calculate the width, spacing and offsets for the bar plot - they are grouped
491   size_t n = this->ChartPrivate->plots.size();
492   std::vector<vtkPlotBar *> bars;
493   for (size_t i = 0; i < n; ++i)
494     {
495     vtkPlotBar* bar = vtkPlotBar::SafeDownCast(this->ChartPrivate->plots[i]);
496     if (bar && bar->GetVisible())
497       {
498       bars.push_back(bar);
499       }
500     }
501   if (bars.size())
502     {
503     // We have some bar plots - work out offsets etc.
504     float barWidth = 0.1;
505     vtkPlotBar* bar = bars[0];
506     if (!bar->GetUseIndexForXSeries())
507       {
508       vtkTable *table = bar->GetData()->GetInput();
509       if (table)
510         {
511         vtkDataArray* x = bar->GetData()->GetInputArrayToProcess(0, table);
512         if (x && x->GetNumberOfTuples() > 1)
513           {
514           double x0 = x->GetTuple1(0);
515           double x1 = x->GetTuple1(1);
516           float width = static_cast<float>(fabs(x1 - x0) * this->BarWidthFraction);
517           barWidth = width / bars.size();
518           }
519         }
520       }
521     else
522       {
523       barWidth = 1.0f / bars.size() * this->BarWidthFraction;
524       }
525 
526     // Now set the offsets and widths on each bar
527     // The offsetIndex deals with the fact that half the bars
528     // must shift to the left of the point and half to the right
529     int offsetIndex = static_cast<int>(bars.size() - 1);
530     for (size_t i = 0; i < bars.size(); ++i)
531       {
532       bars[i]->SetWidth(barWidth);
533       bars[i]->SetOffset(offsetIndex * (barWidth / 2));
534       // Increment by two since we need to shift by half widths
535       // but make room for entire bars. Increment backwards because
536       // offsets are always subtracted and Positive offsets move
537       // the bar leftwards.  Negative offsets will shift the bar
538       // to the right.
539       offsetIndex -= 2;
540       //bars[i]->SetOffset(float(bars.size()-i-1)*(barWidth/2));
541       }
542     }
543 }
544 
545 //-----------------------------------------------------------------------------
RecalculatePlotTransforms()546 void vtkChartXY::RecalculatePlotTransforms()
547 {
548   for (int i = 0; i < int(this->ChartPrivate->PlotCorners.size()); ++i)
549     {
550     if (this->ChartPrivate->PlotCorners[i]->GetNumberOfItems())
551       {
552       vtkAxis *xAxis = 0;
553       vtkAxis *yAxis = 0;
554       // Get the appropriate axes, and recalculate the transform.
555       switch (i)
556         {
557         case 0:
558           {
559           xAxis = this->ChartPrivate->axes[vtkAxis::BOTTOM];
560           yAxis = this->ChartPrivate->axes[vtkAxis::LEFT];
561           break;
562           }
563         case 1:
564           xAxis = this->ChartPrivate->axes[vtkAxis::BOTTOM];
565           yAxis = this->ChartPrivate->axes[vtkAxis::RIGHT];
566           break;
567         case 2:
568           xAxis = this->ChartPrivate->axes[vtkAxis::TOP];
569           yAxis = this->ChartPrivate->axes[vtkAxis::RIGHT];
570           break;
571         case 3:
572           xAxis = this->ChartPrivate->axes[vtkAxis::TOP];
573           yAxis = this->ChartPrivate->axes[vtkAxis::LEFT];
574           break;
575         default:
576           vtkWarningMacro(
577             "Error: default case in recalculate plot transforms.");
578         }
579       this->CalculatePlotTransform(
580         xAxis, yAxis, this->ChartPrivate->PlotCorners[i]->GetTransform());
581       // Now we need to set the scale factor on the plots to ensure they rescale
582       // their input data when necessary.
583       vtkRectd shiftScale(xAxis->GetShift(), yAxis->GetShift(),
584                           xAxis->GetScalingFactor(), yAxis->GetScalingFactor());
585       for (unsigned int j = 0;
586            j < this->ChartPrivate->PlotCorners[i]->GetNumberOfItems(); ++j)
587         {
588         vtkPlot *plot =
589             vtkPlot::SafeDownCast(this->ChartPrivate->PlotCorners[i]->GetItem(j));
590         if (plot)
591           {
592           plot->SetShiftScale(shiftScale);
593           }
594         }
595       }
596     }
597   this->PlotTransformValid = true;
598 }
599 
600 //-----------------------------------------------------------------------------
GetPlotCorner(vtkPlot * plot)601 int vtkChartXY::GetPlotCorner(vtkPlot *plot)
602 {
603   vtkAxis *x = plot->GetXAxis();
604   vtkAxis *y = plot->GetYAxis();
605   if (x == this->ChartPrivate->axes[vtkAxis::BOTTOM] &&
606       y == this->ChartPrivate->axes[vtkAxis::LEFT])
607     {
608     return 0;
609     }
610   else if (x == this->ChartPrivate->axes[vtkAxis::BOTTOM] &&
611            y == this->ChartPrivate->axes[vtkAxis::RIGHT])
612     {
613     return 1;
614     }
615   else if (x == this->ChartPrivate->axes[vtkAxis::TOP] &&
616            y == this->ChartPrivate->axes[vtkAxis::RIGHT])
617     {
618     return 2;
619     }
620   else if (x == this->ChartPrivate->axes[vtkAxis::TOP] &&
621            y == this->ChartPrivate->axes[vtkAxis::LEFT])
622     {
623     return 3;
624     }
625   else
626     {
627     // Should never happen.
628     return 4;
629     }
630 }
631 
632 //-----------------------------------------------------------------------------
SetPlotCorner(vtkPlot * plot,int corner)633 void vtkChartXY::SetPlotCorner(vtkPlot *plot, int corner)
634 {
635   if (corner < 0 || corner > 3)
636     {
637     vtkWarningMacro("Invalid corner specified, should be between 0 and 3: "
638                     << corner);
639     return;
640     }
641   if (this->GetPlotCorner(plot) == corner)
642     {
643     return;
644     }
645   this->RemovePlotFromCorners(plot);
646   // Grow the plot corners if necessary
647   while (static_cast<int>(this->ChartPrivate->PlotCorners.size() - 1) < corner)
648     {
649     vtkNew<vtkContextTransform> transform;
650     this->ChartPrivate->PlotCorners.push_back(transform.GetPointer());
651     this->ChartPrivate->Clip->AddItem(transform.GetPointer()); // Clip maintains ownership.
652     }
653   this->ChartPrivate->PlotCorners[corner]->AddItem(plot);
654   if (corner == 0)
655     {
656     plot->SetXAxis(this->ChartPrivate->axes[vtkAxis::BOTTOM]);
657     plot->SetYAxis(this->ChartPrivate->axes[vtkAxis::LEFT]);
658     }
659   else if (corner == 1)
660     {
661     plot->SetXAxis(this->ChartPrivate->axes[vtkAxis::BOTTOM]);
662     plot->SetYAxis(this->ChartPrivate->axes[vtkAxis::RIGHT]);
663     }
664   else if (corner == 2)
665     {
666     plot->SetXAxis(this->ChartPrivate->axes[vtkAxis::TOP]);
667     plot->SetYAxis(this->ChartPrivate->axes[vtkAxis::RIGHT]);
668     }
669   else if (corner == 3)
670     {
671     plot->SetXAxis(this->ChartPrivate->axes[vtkAxis::TOP]);
672     plot->SetYAxis(this->ChartPrivate->axes[vtkAxis::LEFT]);
673     }
674   this->PlotTransformValid = false;
675 }
676 
677 //-----------------------------------------------------------------------------
RecalculatePlotBounds()678 void vtkChartXY::RecalculatePlotBounds()
679 {
680   // Get the bounds of each plot, and each axis  - ordering as laid out below
681   double y1[] = { 0.0, 0.0 }; // left -> 0
682   double x1[] = { 0.0, 0.0 }; // bottom -> 1
683   double y2[] = { 0.0, 0.0 }; // right -> 2
684   double x2[] = { 0.0, 0.0 }; // top -> 3
685   // Store whether the ranges have been initialized - follows same order
686   bool initialized[] = { false, false, false, false };
687 
688   std::vector<vtkPlot*>::iterator it;
689   double bounds[4] = { 0.0, 0.0, 0.0, 0.0 };
690   for (it = this->ChartPrivate->plots.begin();
691        it != this->ChartPrivate->plots.end(); ++it)
692     {
693     if ((*it)->GetVisible() == false)
694       {
695       continue;
696       }
697     (*it)->GetBounds(bounds);
698     if (bounds[1] - bounds[0] < 0.0)
699       {
700       // skip uninitialized bounds.
701       continue;
702       }
703     int corner = this->GetPlotCorner(*it);
704 
705     // Initialize the appropriate ranges, or push out the ranges
706     if ((corner == 0 || corner == 3)) // left
707       {
708       if (!initialized[0])
709         {
710         y1[0] = bounds[2];
711         y1[1] = bounds[3];
712         initialized[0] = true;
713         }
714       else
715         {
716         if (y1[0] > bounds[2]) // min
717           {
718           y1[0] = bounds[2];
719           }
720         if (y1[1] < bounds[3]) // max
721           {
722           y1[1] = bounds[3];
723           }
724         }
725       }
726     if ((corner == 0 || corner == 1)) // bottom
727       {
728       if (!initialized[1])
729         {
730         x1[0] = bounds[0];
731         x1[1] = bounds[1];
732         initialized[1] = true;
733         }
734       else
735         {
736         if (x1[0] > bounds[0]) // min
737           {
738           x1[0] = bounds[0];
739           }
740         if (x1[1] < bounds[1]) // max
741           {
742           x1[1] = bounds[1];
743           }
744         }
745       }
746     if ((corner == 1 || corner == 2)) // right
747       {
748       if (!initialized[2])
749         {
750         y2[0] = bounds[2];
751         y2[1] = bounds[3];
752         initialized[2] = true;
753         }
754       else
755         {
756         if (y2[0] > bounds[2]) // min
757           {
758           y2[0] = bounds[2];
759           }
760         if (y2[1] < bounds[3]) // max
761           {
762           y2[1] = bounds[3];
763           }
764         }
765       }
766     if ((corner == 2 || corner == 3)) // top
767       {
768       if (!initialized[3])
769         {
770         x2[0] = bounds[0];
771         x2[1] = bounds[1];
772         initialized[3] = true;
773         }
774       else
775         {
776         if (x2[0] > bounds[0]) // min
777           {
778           x2[0] = bounds[0];
779           }
780         if (x2[1] < bounds[1]) // max
781           {
782           x2[1] = bounds[1];
783           }
784         }
785       }
786     }
787 
788   // Now set the newly calculated bounds on the axes
789   for (int i = 0; i < 4; ++i)
790     {
791     vtkAxis *axis = this->ChartPrivate->axes[i];
792     double *range = 0;
793     switch (i)
794       {
795       case 0:
796         range = y1;
797         break;
798       case 1:
799         range = x1;
800         break;
801       case 2:
802         range = y2;
803         break;
804       case 3:
805         range = x2;
806         break;
807       default:
808         return;
809       }
810 
811     if (this->ForceAxesToBounds)
812       {
813       axis->SetMinimumLimit(range[0]);
814       axis->SetMaximumLimit(range[1]);
815       }
816     if (axis->GetBehavior() == vtkAxis::AUTO && initialized[i])
817       {
818       axis->SetRange(range[0], range[1]);
819       axis->AutoScale();
820       }
821     }
822 
823   this->Modified();
824 }
825 
826 //-----------------------------------------------------------------------------
UpdateLayout(vtkContext2D * painter)827 bool vtkChartXY::UpdateLayout(vtkContext2D* painter)
828 {
829   // The main use of this method is currently to query the visible axes for
830   // their bounds, and to update the chart in response to that.
831   bool changed = false;
832 
833   // Axes
834   if (this->LayoutStrategy == vtkChart::FILL_SCENE ||
835       this->LayoutStrategy == vtkChart::FILL_RECT)
836     {
837     for (int i = 0; i < 4; ++i)
838       {
839       int border = 0;
840       vtkAxis* axis = this->ChartPrivate->axes[i];
841       axis->Update();
842       if (axis->GetVisible())
843         {
844         vtkRectf bounds = axis->GetBoundingRect(painter);
845         if (i == vtkAxis::TOP || i == vtkAxis::BOTTOM)
846           {// Horizontal axes
847           border = int(bounds.GetHeight());
848           }
849         else
850           {// Vertical axes
851           border = int(bounds.GetWidth());
852           }
853         }
854       border += this->GetLegendBorder(painter, i);
855       if (i == vtkAxis::TOP && this->Title)
856         {
857         painter->ApplyTextProp(this->TitleProperties);
858         float bounds[4];
859         painter->ComputeStringBounds(this->Title, bounds);
860         if (bounds[3] > 0)
861           {
862           border += 5 /* title margin */
863                     + bounds[3]; // add the title text height to the border.
864           }
865         }
866 
867       border = border < this->HiddenAxisBorder ? this->HiddenAxisBorder :
868                                                  border;
869       if (this->ChartPrivate->Borders[i] != border)
870         {
871         this->ChartPrivate->Borders[i] = border;
872         changed = true;
873         }
874       }
875     }
876 
877   if (this->LayoutChanged || changed)
878     {
879     if (this->DrawAxesAtOrigin)
880       {
881       this->SetBorders(this->HiddenAxisBorder,
882                        this->HiddenAxisBorder,
883                        this->ChartPrivate->Borders[2],
884                        this->ChartPrivate->Borders[3]);
885       // Get the screen coordinates for the origin, and move the axes there.
886       vtkVector2f origin(0.0);
887       vtkTransform2D* transform =
888           this->ChartPrivate->PlotCorners[0]->GetTransform();
889       transform->TransformPoints(origin.GetData(), origin.GetData(), 1);
890       // Need to clamp the axes in the plot area.
891       if (int(origin[0]) < this->Point1[0])
892         {
893         origin[0] = this->Point1[0];
894         }
895       if (int(origin[0]) > this->Point2[0])
896         {
897         origin[0] = this->Point2[0];
898         }
899       if (int(origin[1]) < this->Point1[1])
900         {
901         origin[1] = this->Point1[1];
902         }
903       if (int(origin[1]) > this->Point2[1])
904         {
905         origin[1] = this->Point2[1];
906         }
907 
908       this->ChartPrivate->axes[vtkAxis::BOTTOM]
909           ->SetPoint1(this->Point1[0], origin[1]);
910       this->ChartPrivate->axes[vtkAxis::BOTTOM]
911           ->SetPoint2(this->Point2[0], origin[1]);
912       this->ChartPrivate->axes[vtkAxis::LEFT]
913           ->SetPoint1(origin[0], this->Point1[1]);
914       this->ChartPrivate->axes[vtkAxis::LEFT]
915           ->SetPoint2(origin[0], this->Point2[1]);
916       }
917     else
918       {
919       if (this->LayoutStrategy == vtkChart::AXES_TO_RECT)
920         {
921         this->SetBorders(0, 0, 0, 0);
922         this->ChartPrivate->axes[0]->GetBoundingRect(painter);
923         this->ChartPrivate->axes[1]->GetBoundingRect(painter);
924         this->ChartPrivate->axes[2]->GetBoundingRect(painter);
925         this->ChartPrivate->axes[3]->GetBoundingRect(painter);
926         }
927       else
928         {
929         this->SetBorders(this->ChartPrivate->Borders[0],
930                          this->ChartPrivate->Borders[1],
931                          this->ChartPrivate->Borders[2],
932                          this->ChartPrivate->Borders[3]);
933         }
934       // This is where we set the axes up too
935       // Y axis (left)
936       this->ChartPrivate->axes[0]->SetPoint1(this->Point1[0], this->Point1[1]);
937       this->ChartPrivate->axes[0]->SetPoint2(this->Point1[0], this->Point2[1]);
938       // X axis (bottom)
939       this->ChartPrivate->axes[1]->SetPoint1(this->Point1[0], this->Point1[1]);
940       this->ChartPrivate->axes[1]->SetPoint2(this->Point2[0], this->Point1[1]);
941       }
942     // Y axis (right)
943     this->ChartPrivate->axes[2]->SetPoint1(this->Point2[0], this->Point1[1]);
944     this->ChartPrivate->axes[2]->SetPoint2(this->Point2[0], this->Point2[1]);
945     // X axis (top)
946     this->ChartPrivate->axes[3]->SetPoint1(this->Point1[0], this->Point2[1]);
947     this->ChartPrivate->axes[3]->SetPoint2(this->Point2[0], this->Point2[1]);
948 
949     for (int i = 0; i < 4; ++i)
950       {
951       this->ChartPrivate->axes[i]->Update();
952       }
953     }
954   this->SetLegendPosition(this->Legend->GetBoundingRect(painter));
955 
956   return changed;
957 }
958 
959 //-----------------------------------------------------------------------------
GetLegendBorder(vtkContext2D * painter,int axisPosition)960 int vtkChartXY::GetLegendBorder(vtkContext2D* painter, int axisPosition)
961 {
962   if (!this->Legend->GetVisible() || this->Legend->GetInline())
963     {
964     return 0;
965     }
966 
967   int padding = 10;
968   vtkVector2i legendSize(0, 0);
969   vtkVector2i legendAlignment(this->Legend->GetHorizontalAlignment(),
970                               this->Legend->GetVerticalAlignment());
971   this->Legend->Update();
972   vtkRectf rect = this->Legend->GetBoundingRect(painter);
973   legendSize.Set(static_cast<int>(rect.GetWidth()),
974                  static_cast<int>(rect.GetHeight()));
975 
976   // Figure out the correct place and alignment based on the legend layout.
977   if (axisPosition == vtkAxis::LEFT &&
978       legendAlignment.GetX() == vtkChartLegend::LEFT)
979     {
980     return legendSize.GetX() + padding;
981     }
982   else if (axisPosition == vtkAxis::RIGHT &&
983            legendAlignment.GetX() == vtkChartLegend::RIGHT)
984     {
985     return legendSize.GetX() + padding;
986     }
987   else if ((axisPosition == vtkAxis::TOP || axisPosition == vtkAxis::BOTTOM) &&
988            (legendAlignment.GetX() == vtkChartLegend::LEFT ||
989             legendAlignment.GetX() == vtkChartLegend::RIGHT))
990     {
991     return 0;
992     }
993   else if (axisPosition == vtkAxis::TOP &&
994            legendAlignment.GetY() == vtkChartLegend::TOP)
995     {
996     return legendSize.GetY() + padding;
997     }
998   else if (axisPosition == vtkAxis::BOTTOM &&
999            legendAlignment.GetY() == vtkChartLegend::BOTTOM)
1000     {
1001     return legendSize.GetY() + padding;
1002     }
1003   else
1004     {
1005     return 0;
1006     }
1007 }
1008 
1009 //-----------------------------------------------------------------------------
SetLegendPosition(const vtkRectf & rect)1010 void vtkChartXY::SetLegendPosition(const vtkRectf& rect)
1011 {
1012   // Put the legend in the top corner of the chart
1013   vtkVector2f pos(0, 0);
1014   int padding = 5;
1015   vtkVector2i legendAlignment(this->Legend->GetHorizontalAlignment(),
1016                               this->Legend->GetVerticalAlignment());
1017 
1018   if (legendAlignment[0] == vtkChartLegend::CUSTOM ||
1019       legendAlignment[1] == vtkChartLegend::CUSTOM)
1020     {
1021     return;
1022     }
1023 
1024   if (this->Legend->GetInline())
1025     {
1026     switch (this->Legend->GetHorizontalAlignment())
1027       {
1028       case vtkChartLegend::LEFT:
1029         pos.SetX(this->Point1[0]);
1030         break;
1031       case vtkChartLegend::CENTER:
1032         pos.SetX(((this->Point2[0] - this->Point1[0]) / 2.0)
1033                  - rect.GetWidth() / 2.0 + this->Point1[0]);
1034         break;
1035       case vtkChartLegend::RIGHT:
1036       default:
1037         pos.SetX(this->Point2[0] - rect.GetWidth());
1038       }
1039     switch (this->Legend->GetVerticalAlignment())
1040       {
1041       case vtkChartLegend::TOP:
1042         pos.SetY(this->Point2[1] - rect.GetHeight());
1043         break;
1044       case vtkChartLegend::CENTER:
1045         pos.SetY((this->Point2[1] - this->Point1[1]) / 2.0
1046                  - rect.GetHeight() / 2.0 + this->Point1[1]);
1047         break;
1048       case vtkChartLegend::BOTTOM:
1049       default:
1050         pos.SetY(this->Point1[1]);
1051       }
1052     }
1053   else
1054     {
1055     // Non-inline legends.
1056     if (legendAlignment.GetX() == vtkChartLegend::LEFT)
1057       {
1058       pos.SetX(this->Point1[0] - this->ChartPrivate->Borders[vtkAxis::LEFT]
1059                + padding);
1060       }
1061     else if (legendAlignment.GetX() == vtkChartLegend::RIGHT)
1062       {
1063       pos.SetX(this->Point2[0] + this->ChartPrivate->Borders[vtkAxis::RIGHT]
1064                - rect.GetWidth() - padding);
1065       }
1066     else if (legendAlignment.GetX() == vtkChartLegend::CENTER)
1067       {
1068       pos.SetX(((this->Point2[0] - this->Point1[0]) / 2.0)
1069                - (rect.GetWidth() / 2.0) + this->Point1[0]);
1070       // Check for the special case where the legend is on the top or bottom
1071       if (legendAlignment.GetY() == vtkChartLegend::TOP)
1072         {
1073         pos.SetY(this->Point2[1] + this->ChartPrivate->Borders[vtkAxis::TOP]
1074                  - rect.GetHeight() - padding);
1075         }
1076       else if (legendAlignment.GetY() == vtkChartLegend::BOTTOM)
1077         {
1078         pos.SetY(this->Point1[1] - this->ChartPrivate->Borders[vtkAxis::BOTTOM]
1079                  + padding);
1080         }
1081       }
1082     // Vertical alignment
1083     if (legendAlignment.GetX() != vtkChartLegend::CENTER)
1084       {
1085       if (legendAlignment.GetY() == vtkChartLegend::TOP)
1086         {
1087         pos.SetY(this->Point2[1] - rect.GetHeight());
1088         }
1089       else if (legendAlignment.GetY() == vtkChartLegend::BOTTOM)
1090         {
1091         pos.SetY(this->Point1[1]);
1092         }
1093       }
1094     if (legendAlignment.GetY() == vtkChartLegend::CENTER)
1095       {
1096       pos.SetY(((this->Point2[1] - this->Point1[1]) / 2.0)
1097                - (rect.GetHeight() / 2.0) + this->Point1[1]);
1098       }
1099     }
1100 
1101   this->Legend->SetPoint(pos);
1102 }
1103 
1104 //-----------------------------------------------------------------------------
AddPlot(int type)1105 vtkPlot * vtkChartXY::AddPlot(int type)
1106 {
1107   // Use a variable to return the object created (or NULL), this is necessary
1108   // as the HP compiler is broken (thinks this function does not return) and
1109   // the MS compiler generates a warning about unreachable code if a redundant
1110   // return is added at the end.
1111   vtkPlot *plot = NULL;
1112   vtkColor3ub color = this->ChartPrivate->Colors->GetColorRepeating(
1113       static_cast<int>(this->ChartPrivate->plots.size()));
1114   switch (type)
1115     {
1116     case LINE:
1117       {
1118       vtkPlotLine *line = vtkPlotLine::New();
1119       line->GetPen()->SetColor(color.GetData());
1120       plot = line;
1121       break;
1122       }
1123     case POINTS:
1124       {
1125       vtkPlotPoints *points = vtkPlotPoints::New();
1126       points->GetPen()->SetColor(color.GetData());
1127       plot = points;
1128       break;
1129       }
1130     case BAR:
1131       {
1132       vtkPlotBar *bar = vtkPlotBar::New();
1133       bar->GetBrush()->SetColor(color.GetData());
1134       plot = bar;
1135       break;
1136       }
1137     case FUNCTIONALBAG:
1138       {
1139       vtkPlotFunctionalBag *bag = vtkPlotFunctionalBag::New();
1140       bag->GetBrush()->SetColor(color.GetData());
1141       plot = bag;
1142       break;
1143       }
1144     case STACKED:
1145       {
1146       vtkPlotStacked *stacked = vtkPlotStacked::New();
1147       stacked->SetParent(this);
1148       stacked->GetBrush()->SetColor(color.GetData());
1149       plot = stacked;
1150       break;
1151       }
1152     case BAG:
1153       {
1154       vtkPlotBag *bag = vtkPlotBag::New();
1155       bag->SetParent(this);
1156       bag->GetBrush()->SetColor(color.GetData());
1157       plot = bag;
1158       break;
1159       }
1160 
1161     default:
1162       plot = NULL;
1163     }
1164   if (plot)
1165     {
1166     this->AddPlot(plot);
1167     plot->Delete();
1168     }
1169   return plot;
1170 }
1171 
1172 //-----------------------------------------------------------------------------
AddPlot(vtkPlot * plot)1173 vtkIdType vtkChartXY::AddPlot(vtkPlot * plot)
1174 {
1175   if (plot == NULL)
1176     {
1177     return -1;
1178     }
1179   plot->Register(this);
1180   this->ChartPrivate->plots.push_back(plot);
1181   vtkIdType plotIndex = this->ChartPrivate->plots.size() - 1;
1182   this->SetPlotCorner(plot, 0);
1183   // Ensure that the bounds are recalculated
1184   this->PlotTransformValid = false;
1185   // Mark the scene as dirty
1186   if (this->Scene)
1187     {
1188     this->Scene->SetDirty(true);
1189     }
1190   return plotIndex;
1191 }
1192 
1193 //-----------------------------------------------------------------------------
RemovePlot(vtkIdType index)1194 bool vtkChartXY::RemovePlot(vtkIdType index)
1195 {
1196   if (index < static_cast<vtkIdType>(this->ChartPrivate->plots.size()))
1197     {
1198     this->RemovePlotFromCorners(this->ChartPrivate->plots[index]);
1199     this->ChartPrivate->plots[index]->Delete();
1200     this->ChartPrivate->plots.erase(this->ChartPrivate->plots.begin()+index);
1201 
1202     // Ensure that the bounds are recalculated
1203     this->PlotTransformValid = false;
1204     if (this->Scene)
1205       {
1206       // Mark the scene as dirty
1207       this->Scene->SetDirty(true);
1208       }
1209     return true;
1210     }
1211   else
1212     {
1213     return false;
1214     }
1215 }
1216 
1217 //-----------------------------------------------------------------------------
ClearPlots()1218 void vtkChartXY::ClearPlots()
1219 {
1220   for (unsigned int i = 0; i < this->ChartPrivate->plots.size(); ++i)
1221     {
1222     this->ChartPrivate->plots[i]->Delete();
1223     }
1224   this->ChartPrivate->plots.clear();
1225   // Clear the corners too
1226   for (int i = 0; i < int(this->ChartPrivate->PlotCorners.size()); ++i)
1227     {
1228     this->ChartPrivate->PlotCorners[i]->ClearItems();
1229     if (i > 0)
1230       {
1231       this->ChartPrivate->Clip->RemoveItem(this->ChartPrivate->PlotCorners[i]);
1232       }
1233     }
1234   this->ChartPrivate->PlotCorners.resize(1);
1235 
1236   // Ensure that the bounds are recalculated
1237   this->PlotTransformValid = false;
1238   if (this->Scene)
1239     {
1240     // Mark the scene as dirty
1241     this->Scene->SetDirty(true);
1242     }
1243 }
1244 
1245 //-----------------------------------------------------------------------------
GetPlot(vtkIdType index)1246 vtkPlot* vtkChartXY::GetPlot(vtkIdType index)
1247 {
1248   if (static_cast<vtkIdType>(this->ChartPrivate->plots.size()) > index)
1249     {
1250     return this->ChartPrivate->plots[index];
1251     }
1252   else
1253     {
1254     return NULL;
1255     }
1256 }
1257 
1258 //-----------------------------------------------------------------------------
GetPlotIndex(vtkPlot * plot)1259 vtkIdType vtkChartXY::GetPlotIndex(vtkPlot* plot)
1260 {
1261   int corner = this->GetPlotCorner(plot);
1262   return corner >= 0 && corner < 4 ?
1263     this->ChartPrivate->PlotCorners[corner]->GetItemIndex(plot) :
1264     static_cast<vtkIdType>(-1);
1265 }
1266 
1267 //-----------------------------------------------------------------------------
RaisePlot(vtkPlot * plot)1268 vtkIdType vtkChartXY::RaisePlot(vtkPlot* plot)
1269 {
1270   vtkIdType plotIndex = this->GetPlotIndex(plot);
1271   int corner = this->GetPlotCorner(plot);
1272   if (corner < 0 || corner >=4)
1273     {
1274     return plotIndex;
1275     }
1276   return this->ChartPrivate->PlotCorners[corner]->Raise(plotIndex);
1277 }
1278 
1279 //-----------------------------------------------------------------------------
StackPlotAbove(vtkPlot * plot,vtkPlot * under)1280 vtkIdType vtkChartXY::StackPlotAbove(vtkPlot* plot, vtkPlot* under)
1281 {
1282   vtkIdType plotIndex = this->GetPlotIndex(plot);
1283   vtkIdType underIndex = this->GetPlotIndex(under);
1284   int corner = this->GetPlotCorner(plot);
1285   if (corner < 0 || corner >=4 ||
1286       underIndex != this->GetPlotCorner(under))
1287     {
1288     return plotIndex;
1289     }
1290   return this->ChartPrivate->PlotCorners[corner]->StackAbove(plotIndex,
1291                                                              underIndex);
1292 }
1293 
1294 //-----------------------------------------------------------------------------
LowerPlot(vtkPlot * plot)1295 vtkIdType vtkChartXY::LowerPlot(vtkPlot* plot)
1296 {
1297   vtkIdType plotIndex = this->GetPlotIndex(plot);
1298   int corner = this->GetPlotCorner(plot);
1299   if (corner < 0 || corner >=4)
1300     {
1301     return plotIndex;
1302     }
1303   return this->ChartPrivate->PlotCorners[corner]->Lower(plotIndex);
1304 }
1305 
1306 //-----------------------------------------------------------------------------
StackPlotUnder(vtkPlot * plot,vtkPlot * above)1307 vtkIdType vtkChartXY::StackPlotUnder(vtkPlot* plot, vtkPlot* above)
1308 {
1309   vtkIdType plotIndex = this->GetPlotIndex(plot);
1310   vtkIdType aboveIndex = this->GetPlotIndex(above);
1311   int corner = this->GetPlotCorner(plot);
1312   if (corner < 0 || corner >=4 ||
1313       corner != this->GetPlotCorner(above))
1314     {
1315     return plotIndex;
1316     }
1317   return this->ChartPrivate->PlotCorners[corner]->StackUnder(plotIndex,
1318                                                              aboveIndex);
1319 }
1320 
1321 //-----------------------------------------------------------------------------
SetShowLegend(bool visible)1322 void vtkChartXY::SetShowLegend(bool visible)
1323 {
1324   this->vtkChart::SetShowLegend(visible);
1325   this->Legend->SetVisible(visible);
1326 }
1327 
1328 //-----------------------------------------------------------------------------
GetLegend()1329 vtkChartLegend* vtkChartXY::GetLegend()
1330 {
1331   return this->Legend;
1332 }
1333 
1334 //-----------------------------------------------------------------------------
SetTooltip(vtkTooltipItem * tooltip)1335 void vtkChartXY::SetTooltip(vtkTooltipItem *tooltip)
1336 {
1337   if(tooltip == this->Tooltip)
1338     {
1339     // nothing to change
1340     return;
1341     }
1342 
1343   if(this->Tooltip)
1344     {
1345     // remove current tooltip from scene
1346     this->RemoveItem(this->Tooltip);
1347     }
1348 
1349   this->Tooltip = tooltip;
1350 
1351   if(this->Tooltip)
1352     {
1353     // add new tooltip to scene
1354     this->AddItem(this->Tooltip);
1355     }
1356 }
1357 
1358 //-----------------------------------------------------------------------------
GetTooltip()1359 vtkTooltipItem* vtkChartXY::GetTooltip()
1360 {
1361   return this->Tooltip;
1362 }
1363 
1364 //-----------------------------------------------------------------------------
GetNumberOfPlots()1365 vtkIdType vtkChartXY::GetNumberOfPlots()
1366 {
1367   return this->ChartPrivate->plots.size();
1368 }
1369 
1370 //-----------------------------------------------------------------------------
GetAxis(int axisIndex)1371 vtkAxis* vtkChartXY::GetAxis(int axisIndex)
1372 {
1373   if (axisIndex < 4)
1374     {
1375     return this->ChartPrivate->axes[axisIndex];
1376     }
1377   else
1378     {
1379     return NULL;
1380     }
1381 }
1382 
1383 //-----------------------------------------------------------------------------
GetNumberOfAxes()1384 vtkIdType vtkChartXY::GetNumberOfAxes()
1385 {
1386   return 4;
1387 }
1388 
1389 
1390 //-----------------------------------------------------------------------------
RecalculateBounds()1391 void vtkChartXY::RecalculateBounds()
1392 {
1393   // Ensure that the bounds are recalculated
1394   this->PlotTransformValid = false;
1395   if (this->Scene)
1396     {
1397     // Mark the scene as dirty
1398     this->Scene->SetDirty(true);
1399     }
1400 }
1401 
1402 //-----------------------------------------------------------------------------
SetSelectionMethod(int method)1403 void vtkChartXY::SetSelectionMethod(int method)
1404 {
1405   if (method == this->SelectionMethod)
1406     {
1407     return;
1408     }
1409   if (method == vtkChart::SELECTION_PLOTS)
1410     {
1411     // Clear the selection on the plots which may be shared between all of them.
1412     // Now iterate through the plots to update selection data
1413     std::vector<vtkPlot*>::iterator it =
1414         this->ChartPrivate->plots.begin();
1415     for ( ; it != this->ChartPrivate->plots.end(); ++it)
1416       {
1417       (*it)->SetSelection(NULL);
1418       }
1419     }
1420   Superclass::SetSelectionMethod(method);
1421 }
1422 
1423 //-----------------------------------------------------------------------------
Hit(const vtkContextMouseEvent & mouse)1424 bool vtkChartXY::Hit(const vtkContextMouseEvent &mouse)
1425 {
1426   if (!this->Interactive)
1427     {
1428     return false;
1429     }
1430   vtkVector2i pos(mouse.GetScreenPos());
1431   if (pos[0] > this->Point1[0] &&
1432       pos[0] < this->Point2[0] &&
1433       pos[1] > this->Point1[1] &&
1434       pos[1] < this->Point2[1])
1435     {
1436     return true;
1437     }
1438   else
1439     {
1440     return false;
1441     }
1442 }
1443 
1444 //-----------------------------------------------------------------------------
MouseEnterEvent(const vtkContextMouseEvent &)1445 bool vtkChartXY::MouseEnterEvent(const vtkContextMouseEvent &)
1446 {
1447   // Find the nearest point on the curves and snap to it
1448   this->DrawNearestPoint = true;
1449   return true;
1450 }
1451 
1452 //-----------------------------------------------------------------------------
MouseMoveEvent(const vtkContextMouseEvent & mouse)1453 bool vtkChartXY::MouseMoveEvent(const vtkContextMouseEvent &mouse)
1454 {
1455   // Iterate through each corner, and check for a nearby point
1456   for (size_t i = 0; i < this->ChartPrivate->PlotCorners.size(); ++i)
1457     {
1458     if (this->ChartPrivate->PlotCorners[i]->MouseMoveEvent(mouse))
1459       {
1460       return true;
1461       }
1462     }
1463 
1464   if (mouse.GetButton() == this->Actions.Pan())
1465     {
1466     // Figure out how much the mouse has moved by in plot coordinates - pan
1467     vtkVector2d screenPos(mouse.GetScreenPos().Cast<double>().GetData());
1468     vtkVector2d lastScreenPos(mouse.GetLastScreenPos().Cast<double>().GetData());
1469     vtkVector2d pos(0.0, 0.0);
1470     vtkVector2d last(0.0, 0.0);
1471 
1472     // Go from screen to scene coordinates to work out the delta
1473     vtkAxis* xAxis = this->ChartPrivate->axes[vtkAxis::BOTTOM];
1474     vtkAxis* yAxis = this->ChartPrivate->axes[vtkAxis::LEFT];
1475     vtkTransform2D *transform =
1476         this->ChartPrivate->PlotCorners[0]->GetTransform();
1477     transform->InverseTransformPoints(screenPos.GetData(), pos.GetData(), 1);
1478     transform->InverseTransformPoints(lastScreenPos.GetData(), last.GetData(), 1);
1479     vtkVector2d delta = last - pos;
1480     delta[0] /= xAxis->GetScalingFactor();
1481     delta[1] /= yAxis->GetScalingFactor();
1482 
1483     // Now move the axes and recalculate the transform
1484     delta[0] = delta[0] > 0 ?
1485       std::min(delta[0], xAxis->GetMaximumLimit() - xAxis->GetMaximum()) :
1486       std::max(delta[0], xAxis->GetMinimumLimit() - xAxis->GetMinimum());
1487     delta[1] = delta[1] > 0 ?
1488       std::min(delta[1], yAxis->GetMaximumLimit() - yAxis->GetMaximum()) :
1489       std::max(delta[1], yAxis->GetMinimumLimit() - yAxis->GetMinimum());
1490     xAxis->SetMinimum(xAxis->GetMinimum() + delta[0]);
1491     xAxis->SetMaximum(xAxis->GetMaximum() + delta[0]);
1492     yAxis->SetMinimum(yAxis->GetMinimum() + delta[1]);
1493     yAxis->SetMaximum(yAxis->GetMaximum() + delta[1]);
1494 
1495     if (this->ChartPrivate->PlotCorners.size() == 2)
1496       {
1497       // Figure out the right axis position, if greater than 2 both will be done
1498       // in the else if block below.
1499       screenPos = vtkVector2d(mouse.GetScreenPos().Cast<double>().GetData());
1500       lastScreenPos =
1501           vtkVector2d(mouse.GetLastScreenPos().Cast<double>().GetData());
1502       pos = vtkVector2d(0.0, 0.0);
1503       last = vtkVector2d(0.0, 0.0);
1504       yAxis = this->ChartPrivate->axes[vtkAxis::RIGHT];
1505       transform = this->ChartPrivate->PlotCorners[1]->GetTransform();
1506       transform->InverseTransformPoints(screenPos.GetData(), pos.GetData(), 1);
1507       transform->InverseTransformPoints(lastScreenPos.GetData(), last.GetData(), 1);
1508       delta = last - pos;
1509       delta[0] /= xAxis->GetScalingFactor();
1510       delta[1] /= yAxis->GetScalingFactor();
1511 
1512       // Now move the axes and recalculate the transform
1513       delta[1] = delta[1] > 0 ?
1514         std::min(delta[1], yAxis->GetMaximumLimit() - yAxis->GetMaximum()) :
1515         std::max(delta[1], yAxis->GetMinimumLimit() - yAxis->GetMinimum());
1516       yAxis->SetMinimum(yAxis->GetMinimum() + delta[1]);
1517       yAxis->SetMaximum(yAxis->GetMaximum() + delta[1]);
1518       }
1519     else if (this->ChartPrivate->PlotCorners.size() > 2)
1520       {
1521       // Figure out the right and top axis positions.
1522       // Go from screen to scene coordinates to work out the delta
1523       screenPos = vtkVector2d(mouse.GetScreenPos().Cast<double>().GetData());
1524       lastScreenPos =
1525           vtkVector2d(mouse.GetLastScreenPos().Cast<double>().GetData());
1526       pos = vtkVector2d(0.0, 0.0);
1527       last = vtkVector2d(0.0, 0.0);
1528       xAxis = this->ChartPrivate->axes[vtkAxis::TOP];
1529       yAxis = this->ChartPrivate->axes[vtkAxis::RIGHT];
1530       transform = this->ChartPrivate->PlotCorners[2]->GetTransform();
1531       transform->InverseTransformPoints(screenPos.GetData(), pos.GetData(), 1);
1532       transform->InverseTransformPoints(lastScreenPos.GetData(), last.GetData(), 1);
1533       delta = last - pos;
1534       delta[0] /= xAxis->GetScalingFactor();
1535       delta[1] /= yAxis->GetScalingFactor();
1536 
1537       // Now move the axes and recalculate the transform
1538       delta[0] = delta[0] > 0 ?
1539         std::min(delta[0], xAxis->GetMaximumLimit() - xAxis->GetMaximum()) :
1540         std::max(delta[0], xAxis->GetMinimumLimit() - xAxis->GetMinimum());
1541       delta[1] = delta[1] > 0 ?
1542         std::min(delta[1], yAxis->GetMaximumLimit() - yAxis->GetMaximum()) :
1543         std::max(delta[1], yAxis->GetMinimumLimit() - yAxis->GetMinimum());
1544       xAxis->SetMinimum(xAxis->GetMinimum() + delta[0]);
1545       xAxis->SetMaximum(xAxis->GetMaximum() + delta[0]);
1546       yAxis->SetMinimum(yAxis->GetMinimum() + delta[1]);
1547       yAxis->SetMaximum(yAxis->GetMaximum() + delta[1]);
1548       }
1549 
1550     this->RecalculatePlotTransforms();
1551     // Mark the scene as dirty
1552     this->Scene->SetDirty(true);
1553 
1554     this->InvokeEvent(vtkCommand::InteractionEvent);
1555     }
1556   else if (mouse.GetButton() == this->Actions.Zoom() ||
1557            mouse.GetButton() == this->Actions.Select())
1558     {
1559     this->MouseBox.SetWidth(mouse.GetPos().GetX() - this->MouseBox.GetX());
1560     this->MouseBox.SetHeight(mouse.GetPos().GetY() - this->MouseBox.GetY());
1561     // Mark the scene as dirty
1562     this->Scene->SetDirty(true);
1563     }
1564   else if (mouse.GetButton() == this->Actions.ZoomAxis())
1565     {
1566     vtkVector2d screenPos(mouse.GetScreenPos().Cast<double>().GetData());
1567     vtkVector2d lastScreenPos(mouse.GetLastScreenPos().Cast<double>().GetData());
1568 
1569     vtkAxis *axes[] = {
1570       this->ChartPrivate->axes[vtkAxis::BOTTOM],
1571       this->ChartPrivate->axes[vtkAxis::LEFT],
1572       this->ChartPrivate->axes[vtkAxis::TOP],
1573       this->ChartPrivate->axes[vtkAxis::RIGHT]
1574     };
1575 
1576     for(int i = 0; i < 4; i++)
1577       {
1578       vtkAxis *axis = axes[i];
1579       if(!axis)
1580         {
1581         continue;
1582         }
1583 
1584       // bottom, top -> 0, right, left -> 1
1585       int side = i % 2;
1586 
1587       // get mouse delta in the given direction for the axis
1588       double delta = lastScreenPos[side] - screenPos[side];
1589       if(std::abs(delta) == 0)
1590         {
1591         continue;
1592         }
1593 
1594       // scale and invert delta
1595       delta /= -100.0;
1596 
1597       // zoom axis range
1598       double min = axis->GetMinimum();
1599       double max = axis->GetMaximum();
1600       double frac = (max - min) * 0.1;
1601       if (frac > 0.0)
1602         {
1603         min += delta*frac;
1604         max -= delta*frac;
1605         }
1606       else
1607         {
1608         min -= delta*frac;
1609         max += delta*frac;
1610         }
1611       axis->SetMinimum(min);
1612       axis->SetMaximum(max);
1613       axis->RecalculateTickSpacing();
1614       }
1615 
1616     this->RecalculatePlotTransforms();
1617 
1618     // Mark the scene as dirty
1619     this->Scene->SetDirty(true);
1620     }
1621   else if (mouse.GetButton() == this->Actions.SelectPolygon())
1622     {
1623     if(this->SelectionPolygon.GetNumberOfPoints() > 0)
1624       {
1625       vtkVector2f lastPoint =
1626         this->SelectionPolygon.GetPoint(
1627           this->SelectionPolygon.GetNumberOfPoints() - 1);
1628 
1629       if((lastPoint - mouse.GetPos()).SquaredNorm() > 100)
1630         {
1631         this->SelectionPolygon.AddPoint(mouse.GetPos());
1632         }
1633 
1634       // Mark the scene as dirty
1635       this->Scene->SetDirty(true);
1636       }
1637     }
1638   else if (mouse.GetButton() == vtkContextMouseEvent::NO_BUTTON)
1639     {
1640     this->Scene->SetDirty(true);
1641 
1642     if(this->Tooltip)
1643       {
1644       this->Tooltip->SetVisible(this->LocatePointInPlots(mouse));
1645       }
1646     }
1647 
1648   return true;
1649 }
1650 
1651 //-----------------------------------------------------------------------------
LocatePointInPlot(const vtkVector2f & position,const vtkVector2f & tolerance,vtkVector2f & plotPos,vtkPlot * plot,vtkIdType & segmentIndex)1652 int vtkChartXY::LocatePointInPlot(const vtkVector2f &position,
1653                                   const vtkVector2f &tolerance,
1654                                   vtkVector2f &plotPos,
1655                                   vtkPlot *plot,
1656                                   vtkIdType &segmentIndex)
1657 {
1658   if (plot && plot->GetVisible())
1659     {
1660     vtkPlotBar* plotBar = vtkPlotBar::SafeDownCast(plot);
1661     if (plotBar)
1662       {
1663       // If the plot is a vtkPlotBar, get the segment index too
1664       return plotBar->GetNearestPoint(position, tolerance,
1665                                       &plotPos, &segmentIndex);
1666       }
1667     else
1668       {
1669       return plot->GetNearestPoint(position, tolerance, &plotPos);
1670       }
1671     }
1672   return -1;
1673 }
1674 
1675 //-----------------------------------------------------------------------------
LocatePointInPlots(const vtkContextMouseEvent & mouse,int invokeEvent)1676 bool vtkChartXY::LocatePointInPlots(const vtkContextMouseEvent &mouse,
1677                                     int invokeEvent)
1678 {
1679   size_t n = this->ChartPrivate->plots.size();
1680   vtkVector2i pos(mouse.GetScreenPos());
1681   if (pos[0] > this->Point1[0] &&
1682       pos[0] < this->Point2[0] &&
1683       pos[1] > this->Point1[1] &&
1684       pos[1] < this->Point2[1] && n)
1685     {
1686     // Iterate through each corner, and check for a nearby point
1687     for (size_t i = 0; i < this->ChartPrivate->PlotCorners.size(); ++i)
1688       {
1689       int items = static_cast<int>(this->ChartPrivate->PlotCorners[i]
1690                                    ->GetNumberOfItems());
1691       if (items)
1692         {
1693         vtkVector2f plotPos, position;
1694         vtkTransform2D* transform =
1695             this->ChartPrivate->PlotCorners[i]->GetTransform();
1696         transform->InverseTransformPoints(mouse.GetPos().GetData(),
1697                                           position.GetData(), 1);
1698         // Use a tolerance of +/- 5 pixels
1699         vtkVector2f tolerance(
1700           std::fabs(5*(1.0/transform->GetMatrix()->GetElement(0, 0))),
1701           std::fabs(5*(1.0/transform->GetMatrix()->GetElement(1, 1))));
1702         // Iterate through the visible plots and return on the first hit
1703         vtkIdType segmentIndex = -1;
1704 
1705         for (int j = items-1; j >= 0; --j)
1706           {
1707           vtkPlot* plot = vtkPlot::SafeDownCast(this->ChartPrivate->
1708                                                 PlotCorners[i]->GetItem(j));
1709           int seriesIndex = LocatePointInPlot(position, tolerance, plotPos,
1710                                               plot, segmentIndex);
1711           if (seriesIndex >= 0)
1712             {
1713             // We found a point, set up the tooltip and return
1714             vtkRectd ss(plot->GetShiftScale());
1715             vtkVector2d plotPosd(plotPos[0] / ss[2] - ss[0],
1716                                  plotPos[1] / ss[3] - ss[1]);
1717             this->SetTooltipInfo(mouse, plotPosd, seriesIndex, plot,
1718                                  segmentIndex);
1719             if (invokeEvent >= 0)
1720               {
1721               vtkChartPlotData plotIndex;
1722               plotIndex.SeriesName = plot->GetLabel();
1723               plotIndex.Position = plotPos;
1724               plotIndex.ScreenPosition = mouse.GetScreenPos();
1725               plotIndex.Index = seriesIndex;
1726               // Invoke an event, with the client data supplied
1727               this->InvokeEvent(invokeEvent, static_cast<void*>(&plotIndex));
1728 
1729               if (invokeEvent == vtkCommand::SelectionChangedEvent)
1730                 {
1731                 // Construct a new selection with the selected point in it.
1732                 vtkNew<vtkIdTypeArray> selectionIds;
1733                 selectionIds->InsertNextValue(seriesIndex);
1734                 plot->SetSelection(selectionIds.GetPointer());
1735 
1736                 if (this->AnnotationLink)
1737                   {
1738                   vtkChartSelectionHelper::MakeSelection(this->AnnotationLink,
1739                                                          selectionIds.GetPointer(),
1740                                                          plot);
1741                   }
1742                 }
1743               }
1744             return true;
1745             }
1746           }
1747         }
1748       }
1749     }
1750   return false;
1751 }
1752 
1753 //-----------------------------------------------------------------------------
SetTooltipInfo(const vtkContextMouseEvent & mouse,const vtkVector2d & plotPos,vtkIdType seriesIndex,vtkPlot * plot,vtkIdType segmentIndex)1754 void vtkChartXY::SetTooltipInfo(const vtkContextMouseEvent& mouse,
1755                                 const vtkVector2d &plotPos,
1756                                 vtkIdType seriesIndex, vtkPlot* plot,
1757                                 vtkIdType segmentIndex)
1758 {
1759   if (!this->Tooltip)
1760     {
1761     return;
1762     }
1763 
1764   // Have the plot generate its tooltip label
1765   vtkStdString tooltipLabel = plot->GetTooltipLabel(plotPos, seriesIndex,
1766                                                     segmentIndex);
1767 
1768   // Set the tooltip
1769   this->Tooltip->SetText(tooltipLabel);
1770   this->Tooltip->SetPosition(mouse.GetScreenPos()[0] + 2,
1771                              mouse.GetScreenPos()[1] + 2);
1772 }
1773 
1774 //-----------------------------------------------------------------------------
MouseLeaveEvent(const vtkContextMouseEvent &)1775 bool vtkChartXY::MouseLeaveEvent(const vtkContextMouseEvent &)
1776 {
1777   this->DrawNearestPoint = false;
1778 
1779   if(this->Tooltip)
1780     {
1781     this->Tooltip->SetVisible(false);
1782     }
1783 
1784   return true;
1785 }
1786 
1787 //-----------------------------------------------------------------------------
MouseButtonPressEvent(const vtkContextMouseEvent & mouse)1788 bool vtkChartXY::MouseButtonPressEvent(const vtkContextMouseEvent &mouse)
1789 {
1790   if(this->Tooltip)
1791     {
1792     this->Tooltip->SetVisible(false);
1793     }
1794 
1795   // Iterate through each corner, and check for a nearby point
1796   for (size_t i = 0; i < this->ChartPrivate->PlotCorners.size(); ++i)
1797     {
1798     if (this->ChartPrivate->PlotCorners[i]->MouseButtonPressEvent(mouse))
1799       {
1800       return true;
1801       }
1802     }
1803   if (mouse.GetButton() == this->Actions.Pan())
1804     {
1805     // The mouse panning action.
1806     this->MouseBox.Set(mouse.GetPos().GetX(), mouse.GetPos().GetY(), 0.0, 0.0);
1807     this->DrawBox = false;
1808     return true;
1809     }
1810   else if (mouse.GetButton() == this->Actions.Zoom() ||
1811            mouse.GetButton() == this->Actions.Select())
1812     {
1813     // Selection, for now at least...
1814     this->MouseBox.Set(mouse.GetPos().GetX(), mouse.GetPos().GetY(), 0.0, 0.0);
1815     this->DrawBox = true;
1816     return true;
1817     }
1818   else if (mouse.GetButton() == this->Actions.ZoomAxis())
1819     {
1820     this->MouseBox.Set(mouse.GetPos().GetX(), mouse.GetPos().GetY(), 0.0, 0.0);
1821     this->DrawBox = false;
1822     return true;
1823     }
1824   else if (mouse.GetButton() == this->Actions.SelectPolygon())
1825     {
1826     this->SelectionPolygon.Clear();
1827     this->SelectionPolygon.AddPoint(mouse.GetPos());
1828     this->DrawSelectionPolygon = true;
1829     return true;
1830     }
1831   else if (mouse.GetButton() == this->ActionsClick.Select() ||
1832            mouse.GetButton() == this->ActionsClick.Notify())
1833     {
1834     return true;
1835     }
1836   else
1837     {
1838     return false;
1839     }
1840 }
1841 
1842 //-----------------------------------------------------------------------------
MouseButtonReleaseEvent(const vtkContextMouseEvent & mouse)1843 bool vtkChartXY::MouseButtonReleaseEvent(const vtkContextMouseEvent &mouse)
1844 {
1845   // Iterate through each corner, and check for a nearby point
1846   for (size_t i = 0; i < this->ChartPrivate->PlotCorners.size(); ++i)
1847     {
1848     if (this->ChartPrivate->PlotCorners[i]->MouseButtonReleaseEvent(mouse))
1849       {
1850       return true;
1851       }
1852     }
1853   if (mouse.GetButton() > vtkContextMouseEvent::NO_BUTTON &&
1854       mouse.GetButton() <= vtkContextMouseEvent::RIGHT_BUTTON)
1855     {
1856     this->MouseBox.SetWidth(mouse.GetPos().GetX() - this->MouseBox.GetX());
1857     this->MouseBox.SetHeight(mouse.GetPos().GetY() - this->MouseBox.GetY());
1858     if ((fabs(this->MouseBox.GetWidth()) < 0.5 && fabs(this->MouseBox.GetHeight()) < 0.5)
1859         && (mouse.GetButton() == this->Actions.Select() ||
1860             mouse.GetButton() == this->Actions.Pan()))
1861       {
1862       // Invalid box size - treat as a single clicke event
1863       this->MouseBox.SetWidth(0.0);
1864       this->MouseBox.SetHeight(0.0);
1865       this->DrawBox = false;
1866       if (mouse.GetButton() == this->ActionsClick.Notify())
1867         {
1868         this->LocatePointInPlots(mouse, vtkCommand::InteractionEvent);
1869         return true;
1870         }
1871       else if (mouse.GetButton() == this->ActionsClick.Select())
1872         {
1873         this->LocatePointInPlots(mouse, vtkCommand::SelectionChangedEvent);
1874         return true;
1875         }
1876       else
1877         {
1878         return false;
1879         }
1880       }
1881     }
1882   if (mouse.GetButton() == this->Actions.Select() ||
1883       mouse.GetButton() == this->Actions.SelectPolygon())
1884     {
1885     // Modifiers or selection modes can affect how selection is performed.
1886     int selectionMode =
1887         vtkChartSelectionHelper::GetMouseSelectionMode(mouse,
1888                                                        this->SelectionMode);
1889     bool polygonMode(mouse.GetButton() == this->Actions.SelectPolygon());
1890     this->Scene->SetDirty(true);
1891 
1892     // Update the polygon or box with the last mouse position.
1893     if (polygonMode)
1894       {
1895       this->SelectionPolygon.AddPoint(mouse.GetPos());
1896       this->DrawSelectionPolygon = false;
1897       }
1898     else
1899       {
1900       this->MouseBox.SetWidth(mouse.GetPos().GetX() - this->MouseBox.GetX());
1901       this->MouseBox.SetHeight(mouse.GetPos().GetY() - this->MouseBox.GetY());
1902       this->DrawBox = false;
1903       }
1904 
1905     // Check whether we have a valid selection area, exit early if not.
1906     if (polygonMode && this->SelectionPolygon.GetNumberOfPoints() < 3)
1907       {
1908       // There is no polygon to select points in.
1909       this->SelectionPolygon.Clear();
1910       return true;
1911       }
1912     else if (fabs(this->MouseBox.GetWidth()) < 0.5 ||
1913              fabs(this->MouseBox.GetHeight()) < 0.5)
1914       {
1915       // The box is too small, and no useful selection can be made.
1916       this->MouseBox.SetWidth(0.0);
1917       this->MouseBox.SetHeight(0.0);
1918       return true;
1919       }
1920 
1921     // Iterate through the plots and build a selection. Two main behaviors are
1922     // supported - row-based selections add all rows from all plots and set that
1923     // as the selection, plot-based selections create a selection node for each
1924     // plot.
1925     vtkNew<vtkIdTypeArray> oldSelection;
1926     vtkNew<vtkIdTypeArray> accumulateSelection;
1927     if (this->SelectionMethod == vtkChart::SELECTION_ROWS)
1928       {
1929       // There is only one global selection, we build up a union of all rows
1930       // selected in all charts and set that on all plots.
1931       for (size_t i = 0; i < this->ChartPrivate->PlotCorners.size(); ++i)
1932         {
1933         int items = static_cast<int>(this->ChartPrivate->PlotCorners[i]
1934                                      ->GetNumberOfItems());
1935         if (items)
1936           {
1937           vtkTransform2D *transform =
1938               this->ChartPrivate->PlotCorners[i]->GetTransform();
1939           vtkVector2f min;
1940           vtkVector2f max;
1941           vtkContextPolygon polygon;
1942           this->TransformBoxOrPolygon(polygonMode, transform, mouse.GetPos(),
1943                                       min, max, polygon);
1944 
1945           // Iterate through the plots and create the selection.
1946           for (int j = 0; j < items; ++j)
1947             {
1948             vtkPlot* plot = vtkPlot::SafeDownCast(this->ChartPrivate->
1949                                                   PlotCorners[i]->GetItem(j));
1950             if (plot && plot->GetVisible())
1951               {
1952               // There is only really one old selection in this mode.
1953               if (i == 0 && j == 0)
1954                 {
1955                 oldSelection->DeepCopy(plot->GetSelection());
1956                 }
1957               // Populate the selection using the appropriate shape.
1958               if (polygonMode)
1959                 {
1960                 plot->SelectPointsInPolygon(polygon);
1961                 }
1962               else
1963                 {
1964                 plot->SelectPoints(min, max);
1965                 }
1966 
1967               // Accumulate the selection in each plot.
1968               vtkChartSelectionHelper::BuildSelection(0,
1969                                                       vtkContextScene::SELECTION_ADDITION,
1970                                                       accumulateSelection.GetPointer(),
1971                                                       plot->GetSelection(),
1972                                                       0);
1973               }
1974             }
1975           }
1976         }
1977       // Now add the accumulated selection to the old selection.
1978       vtkChartSelectionHelper::BuildSelection(this->AnnotationLink,
1979                                               selectionMode,
1980                                               accumulateSelection.GetPointer(),
1981                                               oldSelection.GetPointer(),
1982                                               0);
1983       }
1984     else if (this->SelectionMethod == vtkChart::SELECTION_PLOTS)
1985       {
1986       // We are performing plot based selections.
1987       for (size_t i = 0; i < this->ChartPrivate->PlotCorners.size(); ++i)
1988         {
1989         int items = static_cast<int>(this->ChartPrivate->PlotCorners[i]
1990                                      ->GetNumberOfItems());
1991         if (items)
1992           {
1993           vtkTransform2D *transform =
1994               this->ChartPrivate->PlotCorners[i]->GetTransform();
1995           vtkVector2f min;
1996           vtkVector2f max;
1997           vtkContextPolygon polygon;
1998           this->TransformBoxOrPolygon(polygonMode, transform, mouse.GetPos(),
1999                                       min, max, polygon);
2000 
2001           for (int j = 0; j < items; ++j)
2002             {
2003             vtkPlot* plot = vtkPlot::SafeDownCast(this->ChartPrivate->
2004                                                   PlotCorners[i]->GetItem(j));
2005             if (plot && plot->GetVisible())
2006               {
2007               oldSelection->DeepCopy(plot->GetSelection());
2008               // Populate the selection using the appropriate shape.
2009               if (polygonMode)
2010                 {
2011                 plot->SelectPointsInPolygon(polygon);
2012                 }
2013               else
2014                 {
2015                 plot->SelectPoints(min, max);
2016                 }
2017 
2018               // Combine the selection in this plot with any previous selection.
2019               vtkChartSelectionHelper::BuildSelection(this->AnnotationLink,
2020                                                       selectionMode,
2021                                                       plot->GetSelection(),
2022                                                       oldSelection.GetPointer(),
2023                                                       plot);
2024               }
2025             }
2026           }
2027         }
2028       }
2029     else if (this->SelectionMethod == vtkChart::SELECTION_COLUMNS)
2030       {
2031       if (this->AnnotationLink)
2032         {
2033         this->AnnotationLink->Update();
2034         vtkSelection *selection =
2035           vtkSelection::SafeDownCast(this->AnnotationLink->GetOutputDataObject(2));
2036         vtkSelectionNode *node = selection->GetNumberOfNodes() > 0?
2037             selection->GetNode(0) : NULL;
2038         if (node)
2039           {
2040           oldSelection->DeepCopy(vtkIdTypeArray::SafeDownCast(node->GetSelectionList()));
2041           }
2042         }
2043       vtkNew<vtkIdTypeArray> plotSelection;
2044       // We are performing plot based selections.
2045       for (size_t i = 0; i < this->ChartPrivate->PlotCorners.size(); ++i)
2046         {
2047         int items = static_cast<int>(this->ChartPrivate->PlotCorners[i]
2048                                      ->GetNumberOfItems());
2049         if (items)
2050           {
2051           vtkTransform2D *transform =
2052               this->ChartPrivate->PlotCorners[i]->GetTransform();
2053           vtkVector2f min;
2054           vtkVector2f max;
2055           vtkContextPolygon polygon;
2056           this->TransformBoxOrPolygon(polygonMode, transform, mouse.GetPos(),
2057                                       min, max, polygon);
2058 
2059           for (int j = 0; j < items; ++j)
2060             {
2061             vtkPlot* plot = vtkPlot::SafeDownCast(this->ChartPrivate->
2062                                                   PlotCorners[i]->GetItem(j));
2063             if (plot && plot->GetVisible())
2064               {
2065               bool selected = false;
2066               // Populate the selection using the appropriate shape.
2067               if (polygonMode)
2068                 {
2069                 selected = plot->SelectPointsInPolygon(polygon);
2070                 }
2071               else
2072                 {
2073                 selected = plot->SelectPoints(min, max);
2074                 }
2075               vtkNew<vtkIdTypeArray> plotsSelection;
2076               if (selected)
2077                 {
2078                 int idx = 1; // y
2079                 vtkAbstractArray* column = plot->GetData()->GetInputAbstractArrayToProcess(
2080                   idx, plot->GetInput());
2081                 int columnID = -1;
2082                 plot->GetInput()->GetRowData()->GetAbstractArray(column->GetName(), columnID);
2083                 if (plotSelection->GetNumberOfTuples() != column->GetNumberOfTuples())
2084                   {
2085                   plotSelection->SetNumberOfTuples(0);
2086                   for (vtkIdType k = 0; k < column->GetNumberOfTuples(); ++k)
2087                     {
2088                     plotSelection->InsertNextValue(k);
2089                     }
2090                   }
2091                 plot->SetSelection(plotSelection.GetPointer());
2092                 accumulateSelection->InsertNextValue(columnID);
2093                 }
2094               }
2095             }
2096           }
2097         }
2098       // Now add the accumulated selection to the old selection
2099       vtkChartSelectionHelper::BuildSelection(this->AnnotationLink,
2100                                               selectionMode,
2101                                               accumulateSelection.GetPointer(),
2102                                               oldSelection.GetPointer(),
2103                                               0);
2104       }
2105     this->InvokeEvent(vtkCommand::SelectionChangedEvent);
2106     this->MouseBox.SetWidth(0.0);
2107     this->MouseBox.SetHeight(0.0);
2108     this->SelectionPolygon.Clear();
2109     return true;
2110     }
2111   else if (mouse.GetButton() == this->Actions.Zoom())
2112     {
2113     // Check whether a valid zoom box was drawn
2114     if (fabs(this->MouseBox.GetWidth()) < 0.5 || fabs(this->MouseBox.GetHeight()) < 0.5)
2115       {
2116       // Invalid box size - do nothing
2117       this->MouseBox.SetWidth(0.0);
2118       this->MouseBox.SetHeight(0.0);
2119       this->DrawBox = false;
2120       return true;
2121       }
2122 
2123     // Zoom into the chart by the specified amount, and recalculate the bounds
2124     vtkVector2f point2(mouse.GetPos());
2125 
2126     this->ZoomInAxes(this->ChartPrivate->axes[vtkAxis::BOTTOM],
2127                      this->ChartPrivate->axes[vtkAxis::LEFT],
2128                      this->MouseBox.GetData(), point2.GetData());
2129     this->ZoomInAxes(this->ChartPrivate->axes[vtkAxis::TOP],
2130                      this->ChartPrivate->axes[vtkAxis::RIGHT],
2131                      this->MouseBox.GetData(), point2.GetData());
2132 
2133     this->RecalculatePlotTransforms();
2134     this->MouseBox.SetWidth(0.0);
2135     this->MouseBox.SetHeight(0.0);
2136     this->DrawBox = false;
2137     // Mark the scene as dirty
2138     this->Scene->SetDirty(true);
2139     this->InvokeEvent(vtkCommand::InteractionEvent);
2140     return true;
2141     }
2142   else if (mouse.GetButton() == this->Actions.ZoomAxis())
2143     {
2144     return true;
2145     }
2146   return false;
2147 }
2148 
ZoomInAxes(vtkAxis * x,vtkAxis * y,float * originf,float * maxf)2149 void vtkChartXY::ZoomInAxes(vtkAxis *x, vtkAxis *y, float *originf, float *maxf)
2150 {
2151   vtkNew<vtkTransform2D> transform;
2152   this->CalculateUnscaledPlotTransform(x, y, transform.GetPointer());
2153   vtkVector2d origin(originf[0], originf[1]);
2154   vtkVector2d max(maxf[0], maxf[1]);
2155   vtkVector2d torigin;
2156   transform->InverseTransformPoints(origin.GetData(), torigin.GetData(), 1);
2157   vtkVector2d tmax;
2158   transform->InverseTransformPoints(max.GetData(), tmax.GetData(), 1);
2159 
2160   // Ensure we preserve the directionality of the axes
2161   if (x->GetMaximum() > x->GetMinimum())
2162     {
2163     x->SetMaximum(torigin[0] > tmax[0] ? torigin[0] : tmax[0]);
2164     x->SetMinimum(torigin[0] < tmax[0] ? torigin[0] : tmax[0]);
2165     }
2166   else
2167     {
2168     x->SetMaximum(torigin[0] < tmax[0] ? torigin[0] : tmax[0]);
2169     x->SetMinimum(torigin[0] > tmax[0] ? torigin[0] : tmax[0]);
2170     }
2171   if (y->GetMaximum() > y->GetMinimum())
2172     {
2173     y->SetMaximum(torigin[1] > tmax[1] ? torigin[1] : tmax[1]);
2174     y->SetMinimum(torigin[1] < tmax[1] ? torigin[1] : tmax[1]);
2175     }
2176   else
2177     {
2178     y->SetMaximum(torigin[1] < tmax[1] ? torigin[1] : tmax[1]);
2179     y->SetMinimum(torigin[1] > tmax[1] ? torigin[1] : tmax[1]);
2180     }
2181   x->RecalculateTickSpacing();
2182   y->RecalculateTickSpacing();
2183 }
2184 
2185 //-----------------------------------------------------------------------------
MouseWheelEvent(const vtkContextMouseEvent &,int delta)2186 bool vtkChartXY::MouseWheelEvent(const vtkContextMouseEvent &, int delta)
2187 {
2188   if(this->Tooltip)
2189     {
2190     this->Tooltip->SetVisible(false);
2191     }
2192 
2193   // Get the bounds of each plot.
2194   for (int i = 0; i < 4; ++i)
2195     {
2196     vtkAxis *axis = this->ChartPrivate->axes[i];
2197     double min = axis->GetMinimum();
2198     double max = axis->GetMaximum();
2199     double frac = (max - min) * 0.1;
2200     if (frac > 0.0)
2201       {
2202       min += delta*frac;
2203       max -= delta*frac;
2204       }
2205     else
2206       {
2207       min -= delta*frac;
2208       max += delta*frac;
2209       }
2210     axis->SetMinimum(min);
2211     axis->SetMaximum(max);
2212     axis->RecalculateTickSpacing();
2213     }
2214 
2215   this->RecalculatePlotTransforms();
2216 
2217   // Mark the scene as dirty
2218   this->Scene->SetDirty(true);
2219 
2220   this->InvokeEvent(vtkCommand::InteractionEvent);
2221 
2222   return true;
2223 }
2224 
2225 //-----------------------------------------------------------------------------
KeyPressEvent(const vtkContextKeyEvent & key)2226 bool vtkChartXY::KeyPressEvent(const vtkContextKeyEvent &key)
2227 {
2228   switch (key.GetKeyCode())
2229     {
2230     // Reset the chart axes
2231     case 'r':
2232     case 'R':
2233       this->RecalculateBounds();
2234       this->Scene->SetDirty(true);
2235     }
2236 
2237   return true;
2238 }
2239 
2240 //-----------------------------------------------------------------------------
RemovePlotFromCorners(vtkPlot * plot)2241 bool vtkChartXY::RemovePlotFromCorners(vtkPlot *plot)
2242 {
2243   // We know the plot will only ever be in one of the corners
2244   for (size_t i = 0; i < this->ChartPrivate->PlotCorners.size(); ++i)
2245     {
2246     if (this->ChartPrivate->PlotCorners[i]->RemoveItem(plot))
2247       {
2248       return true;
2249       }
2250     }
2251   return false;
2252 }
2253 
2254 //-----------------------------------------------------------------------------
TransformBoxOrPolygon(bool polygonMode,vtkTransform2D * transform,const vtkVector2f & mousePosition,vtkVector2f & min,vtkVector2f & max,vtkContextPolygon & polygon)2255 inline void vtkChartXY::TransformBoxOrPolygon(bool polygonMode,
2256                                               vtkTransform2D *transform,
2257                                               const vtkVector2f &mousePosition,
2258                                               vtkVector2f &min, vtkVector2f &max,
2259                                               vtkContextPolygon &polygon)
2260 {
2261   if (polygonMode)
2262     {
2263     vtkNew<vtkTransform2D> inverseTransform;
2264     inverseTransform->SetMatrix(transform->GetMatrix());
2265     inverseTransform->Inverse();
2266     polygon =
2267       this->SelectionPolygon.Transformed(inverseTransform.GetPointer());
2268     }
2269   else
2270     {
2271     transform->InverseTransformPoints(this->MouseBox.GetData(),
2272                                       min.GetData(), 1);
2273     transform->InverseTransformPoints(mousePosition.GetData(),
2274                                       max.GetData(), 1);
2275     // Normalize the rectangle selection area before using it.
2276     if (min.GetX() > max.GetX())
2277       {
2278       float tmp = min.GetX();
2279       min.SetX(max.GetX());
2280       max.SetX(tmp);
2281       }
2282     if (min.GetY() > max.GetY())
2283       {
2284       float tmp = min.GetY();
2285       min.SetY(max.GetY());
2286       max.SetY(tmp);
2287       }
2288     }
2289 }
2290 
2291 //-----------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)2292 void vtkChartXY::PrintSelf(ostream &os, vtkIndent indent)
2293 {
2294   this->Superclass::PrintSelf(os, indent);
2295   os << indent << "Axes: " << endl;
2296   for (int i = 0; i < 4; ++i)
2297     {
2298     this->ChartPrivate->axes[i]->PrintSelf(os, indent.GetNextIndent());
2299     }
2300   if (this->ChartPrivate)
2301     {
2302     os << indent << "Number of plots: " << this->ChartPrivate->plots.size()
2303        << endl;
2304     for (unsigned int i = 0; i < this->ChartPrivate->plots.size(); ++i)
2305       {
2306       os << indent << "Plot " << i << ":" << endl;
2307       this->ChartPrivate->plots[i]->PrintSelf(os, indent.GetNextIndent());
2308       }
2309     }
2310 
2311 }
2312