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