1 /*=========================================================================
2 
3   Library:   CTK
4 
5   Copyright (c) Kitware Inc.
6 
7   Licensed under the Apache License, Version 2.0 (the "License");
8   you may not use this file except in compliance with the License.
9   You may obtain a copy of the License at
10 
11       http://www.apache.org/licenses/LICENSE-2.0.txt
12 
13   Unless required by applicable law or agreed to in writing, software
14   distributed under the License is distributed on an "AS IS" BASIS,
15   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   See the License for the specific language governing permissions and
17   limitations under the License.
18 
19 =========================================================================*/
20 
21 // Qt includes
22 #include <QDebug>
23 #include <QMouseEvent>
24 
25 // CTK includes
26 #include "ctkLogger.h"
27 #include "ctkVTKChartView.h"
28 
29 // VTK includes
30 #include <vtkAxis.h>
31 #include <vtkChartXY.h>
32 #include <vtkContext2D.h>
33 #include <vtkContextMouseEvent.h>
34 #include <vtkContextScene.h>
35 #include <vtkContextView.h>
36 #include <vtkGenericOpenGLRenderWindow.h>
37 #include <vtkOpenGLContextDevice2D.h>
38 #include <vtkPlot.h>
39 #include <vtkRenderWindow.h>
40 
41 //----------------------------------------------------------------------------
42 static ctkLogger logger("org.commontk.visualization.vtk.widgets.ctkVTKChartView");
43 //----------------------------------------------------------------------------
44 
45 class ctkVTKChartViewPrivate
46 {
47   Q_DECLARE_PUBLIC(ctkVTKChartView);
48 protected:
49   ctkVTKChartView* const q_ptr;
50 public:
51   ctkVTKChartViewPrivate(ctkVTKChartView& object);
52   void init();
53   void chartBounds(double* bounds)const;
54 
55 #ifdef CTK_USE_QVTKOPENGLWIDGET
56   vtkSmartPointer<vtkGenericOpenGLRenderWindow> RenderWindow;
57 #endif
58   vtkSmartPointer<vtkContextView> ContextView;
59   vtkSmartPointer<vtkChartXY> Chart;
60   double UserBounds[8];
61   mutable double OldBounds[8];
62 };
63 
64 // ----------------------------------------------------------------------------
65 // ctkVTKChartViewPrivate methods
66 
67 // ----------------------------------------------------------------------------
ctkVTKChartViewPrivate(ctkVTKChartView & object)68 ctkVTKChartViewPrivate::ctkVTKChartViewPrivate(ctkVTKChartView& object)
69   :q_ptr(&object)
70 {
71   this->ContextView = vtkSmartPointer<vtkContextView>::New();
72 #ifdef CTK_USE_QVTKOPENGLWIDGET
73   this->RenderWindow = vtkSmartPointer<vtkGenericOpenGLRenderWindow>::New();
74 #endif
75   this->Chart = vtkSmartPointer<vtkChartXY>::New();
76   this->ContextView->GetScene()->AddItem(this->Chart);
77   this->UserBounds[0] = this->UserBounds[2] = this->UserBounds[4] = this->UserBounds[6] = 0.;
78   this->UserBounds[1] = this->UserBounds[3] = this->UserBounds[5] = this->UserBounds[7] = -1.;
79   this->OldBounds[0] = this->OldBounds[2] = this->OldBounds[4] = this->OldBounds[6] = 0.;
80   this->OldBounds[1] = this->OldBounds[3] = this->OldBounds[5] = this->OldBounds[7] = -1.;
81 }
82 
83 // ----------------------------------------------------------------------------
init()84 void ctkVTKChartViewPrivate::init()
85 {
86   Q_Q(ctkVTKChartView);
87 #ifdef CTK_USE_QVTKOPENGLWIDGET
88   q->SetRenderWindow(this->RenderWindow);
89   this->ContextView->SetRenderWindow(this->RenderWindow);
90 #endif
91   this->ContextView->SetInteractor(q->GetInteractor());
92   q->SetRenderWindow(this->ContextView->GetRenderWindow());
93   // low def for now (faster)
94   //q->GetRenderWindow()->SetMultiSamples(0);
95   //vtkOpenGLContextDevice2D::SafeDownCast(this->ContextView->GetContext()->GetDevice())
96   //                                       ->SetStringRendererToQt();
97 #ifndef Q_WS_X11
98   q->GetRenderWindow()->SetLineSmoothing(true);
99 #endif
100   this->Chart->SetActionToButton(vtkChart::PAN, vtkContextMouseEvent::MIDDLE_BUTTON);
101   this->Chart->SetActionToButton(vtkChart::SELECT, vtkContextMouseEvent::RIGHT_BUTTON);
102 
103   q->qvtkConnect(q->chart()->GetAxis(vtkAxis::BOTTOM),vtkCommand::ModifiedEvent,
104                     q, SIGNAL(extentChanged()));
105   q->qvtkConnect(q->chart()->GetAxis(vtkAxis::LEFT),vtkCommand::ModifiedEvent,
106                     q, SIGNAL(extentChanged()));
107 
108 }
109 
110 // ----------------------------------------------------------------------------
chartBounds(double * bounds) const111 void ctkVTKChartViewPrivate::chartBounds(double* bounds)const
112 {
113   if (!bounds)
114     {
115     return;
116     }
117   Q_Q(const ctkVTKChartView);
118   bounds[0] = bounds[2] = bounds[4] = bounds[6] = VTK_DOUBLE_MAX;
119   bounds[1] = bounds[3] = bounds[5] = bounds[7] = VTK_DOUBLE_MIN;
120   vtkChartXY* chart = q->chart();
121   const vtkIdType plotCount = chart->GetNumberOfPlots();
122   for (vtkIdType i = 0; i < plotCount; ++i)
123     {
124     vtkPlot* plot = chart->GetPlot(i);
125 
126     int corner = chart->GetPlotCorner(plot);
127     double plotBounds[4];
128     plot->GetBounds(plotBounds);
129     switch (corner)
130       {
131       // bottom left
132       case 0:
133         // x
134         bounds[2] = bounds[2] > plotBounds[0] ? plotBounds[0] : bounds[2];
135         bounds[3] = bounds[3] < plotBounds[1] ? plotBounds[1] : bounds[3];
136         // y
137         bounds[0] = bounds[0] > plotBounds[2] ? plotBounds[2] : bounds[0];
138         bounds[1] = bounds[1] < plotBounds[3] ? plotBounds[3] : bounds[1];
139         break;
140       // bottom right
141       case 1:
142         // x
143         bounds[2] = bounds[2] > plotBounds[0] ? plotBounds[0] : bounds[2];
144         bounds[3] = bounds[3] < plotBounds[1] ? plotBounds[1] : bounds[3];
145         // y
146         bounds[4] = bounds[4] > plotBounds[2] ? plotBounds[2] : bounds[4];
147         bounds[5] = bounds[5] < plotBounds[3] ? plotBounds[3] : bounds[5];
148         break;
149       // top right
150       case 2:
151         // x
152         bounds[6] = bounds[6] > plotBounds[0] ? plotBounds[0] : bounds[6];
153         bounds[7] = bounds[7] < plotBounds[1] ? plotBounds[1] : bounds[7];
154         // y
155         bounds[4] = bounds[4] > plotBounds[2] ? plotBounds[2] : bounds[4];
156         bounds[5] = bounds[5] < plotBounds[3] ? plotBounds[3] : bounds[5];
157         break;
158       // top left
159       case 3:
160         // x
161         bounds[6] = bounds[6] > plotBounds[0] ? plotBounds[0] : bounds[6];
162         bounds[7] = bounds[7] < plotBounds[1] ? plotBounds[1] : bounds[7];
163         // y
164         bounds[0] = bounds[0] > plotBounds[2] ? plotBounds[2] : bounds[1];
165         bounds[1] = bounds[0] < plotBounds[3] ? plotBounds[3] : bounds[1];
166         break;
167       }
168     }
169 }
170 
171 // ----------------------------------------------------------------------------
172 // ctkVTKChartView methods
173 
174 // ----------------------------------------------------------------------------
ctkVTKChartView(QWidget * parentWidget)175 ctkVTKChartView::ctkVTKChartView(QWidget* parentWidget)
176   :Superclass(parentWidget)
177   , d_ptr(new ctkVTKChartViewPrivate(*this))
178 {
179   Q_D(ctkVTKChartView);
180   d->init();
181   //this->setAutomaticImageCacheEnabled(true);
182 }
183 
184 // ----------------------------------------------------------------------------
~ctkVTKChartView()185 ctkVTKChartView::~ctkVTKChartView()
186 {
187 }
188 
189 // ----------------------------------------------------------------------------
setTitle(const QString & newTitle)190 void ctkVTKChartView::setTitle(const QString& newTitle)
191 {
192   Q_D(ctkVTKChartView);
193   d->Chart->SetTitle(newTitle.toLatin1().data());
194 }
195 
196 // ----------------------------------------------------------------------------
title() const197 QString ctkVTKChartView::title()const
198 {
199   Q_D(const ctkVTKChartView);
200   return QString(d->Chart->GetTitle());
201 }
202 
203 // ----------------------------------------------------------------------------
chart() const204 vtkChartXY* ctkVTKChartView::chart()const
205 {
206   Q_D(const ctkVTKChartView);
207   return d->Chart;
208 }
209 
210 // ----------------------------------------------------------------------------
scene() const211 vtkContextScene* ctkVTKChartView::scene()const
212 {
213   Q_D(const ctkVTKChartView);
214   return d->ContextView->GetScene();
215 }
216 
217 // ----------------------------------------------------------------------------
addPlot(vtkPlot * plot)218 void ctkVTKChartView::addPlot(vtkPlot* plot)
219 {
220   Q_D(ctkVTKChartView);
221   d->Chart->AddPlot(plot);
222   emit this->plotAdded(plot);
223   this->onChartUpdated();
224 }
225 
226 // ----------------------------------------------------------------------------
removePlot(vtkPlot * plot)227 void ctkVTKChartView::removePlot(vtkPlot* plot)
228 {
229   Q_D(ctkVTKChartView);
230   vtkIdType index = this->plotIndex(plot);
231   if (index == vtkIdType(-1))
232     {
233     return;
234     }
235   d->Chart->RemovePlot(index);
236   emit this->plotRemoved(plot);
237   this->onChartUpdated();
238 }
239 
240 // ----------------------------------------------------------------------------
removeAllPlots()241 void ctkVTKChartView::removeAllPlots()
242 {
243   Q_D(ctkVTKChartView);
244   while(d->Chart->GetNumberOfPlots() > 0)
245     {
246     this->removePlot(d->Chart->GetPlot(0));
247     }
248 }
249 
250 // ----------------------------------------------------------------------------
plotIndex(vtkPlot * plot)251 vtkIdType ctkVTKChartView::plotIndex(vtkPlot* plot)
252 {
253   Q_D(ctkVTKChartView);
254   // GetPlotIndex is missing from vtkChart API
255   for (vtkIdType i = 0; i < d->Chart->GetNumberOfPlots(); ++i)
256     {
257     if (plot == d->Chart->GetPlot(i))
258       {
259       return i;
260       }
261     }
262   return -1;
263 }
264 
265 // ----------------------------------------------------------------------------
onChartUpdated()266 void ctkVTKChartView::onChartUpdated()
267 {
268   Q_D(ctkVTKChartView);
269   double oldBounds[8];
270   memcpy(oldBounds, d->OldBounds, 8 * sizeof(double));
271   double newBounds[8];
272   this->chartBounds(newBounds);
273   if (oldBounds[0] != newBounds[0] ||
274       oldBounds[1] != newBounds[1] ||
275       oldBounds[2] != newBounds[2] ||
276       oldBounds[3] != newBounds[3] ||
277       oldBounds[4] != newBounds[4] ||
278       oldBounds[5] != newBounds[5] ||
279       oldBounds[6] != newBounds[6] ||
280       oldBounds[7] != newBounds[7])
281     {
282     emit boundsChanged();
283     }
284 }
285 
286 // ----------------------------------------------------------------------------
chartExtent(double * extent) const287 void ctkVTKChartView::chartExtent(double* extent)const
288 {
289   if (!extent)
290     {
291     return;
292     }
293   extent[0] = extent[2] = extent[4] = extent[6] = VTK_DOUBLE_MAX;
294   extent[1] = extent[3] = extent[5] = extent[7] = VTK_DOUBLE_MIN;
295   vtkChartXY* chart = this->chart();
296   vtkAxis* axis = chart->GetAxis(vtkAxis::BOTTOM);
297   extent[0] = qMin(axis->GetMinimum(), extent[0]);
298   extent[1] = qMax(axis->GetMaximum(), extent[1]);
299   axis = chart->GetAxis(vtkAxis::LEFT);
300   extent[2] = qMin(axis->GetMinimum(), extent[2]);
301   extent[3] = qMax(axis->GetMaximum(), extent[3]);
302   axis = chart->GetAxis(vtkAxis::TOP);
303   extent[4] = qMin(axis->GetMinimum(), extent[4]);
304   extent[5] = qMax(axis->GetMaximum(), extent[5]);
305   axis = chart->GetAxis(vtkAxis::RIGHT);
306   extent[6] = qMin(axis->GetMinimum(), extent[6]);
307   extent[7] = qMax(axis->GetMaximum(), extent[7]);
308 }
309 
310 // ----------------------------------------------------------------------------
setChartUserExtent(double * userExtent)311 void ctkVTKChartView::setChartUserExtent(double* userExtent)
312 {
313   if (!userExtent)
314     {
315     qCritical() << Q_FUNC_INFO << ": Invalid user extent";
316     return;
317     }
318   vtkChartXY* chart = this->chart();
319   vtkAxis* axis = chart->GetAxis(vtkAxis::BOTTOM);
320   axis->SetRange(userExtent[0], userExtent[1]);
321   axis = chart->GetAxis(vtkAxis::LEFT);
322   axis->SetRange(userExtent[2], userExtent[3]);
323   axis = chart->GetAxis(vtkAxis::TOP);
324   axis->SetRange(userExtent[4], userExtent[5]);
325   axis = chart->GetAxis(vtkAxis::RIGHT);
326   axis->SetRange(userExtent[6], userExtent[7]);
327 }
328 
329 // ----------------------------------------------------------------------------
chartBounds(double * bounds) const330 void ctkVTKChartView::chartBounds(double* bounds)const
331 {
332   Q_D(const ctkVTKChartView);
333   if (d->UserBounds[1] < d->UserBounds[0])
334     {
335     // Invalid user bounds, return the real chart bounds
336     d->chartBounds(bounds);
337     }
338   else
339     {
340     this->chartUserBounds(bounds);
341     }
342   memcpy(d->OldBounds, bounds, 8 * sizeof(double));
343 }
344 
345 // ----------------------------------------------------------------------------
setChartUserBounds(double * userBounds)346 void ctkVTKChartView::setChartUserBounds(double* userBounds)
347 {
348   Q_D(ctkVTKChartView);
349   for (int i= 0; i < 8; ++i)
350     {
351     d->UserBounds[i] = userBounds[i];
352     }
353   this->onChartUpdated();
354 }
355 
356 // ----------------------------------------------------------------------------
chartUserBounds(double * bounds) const357 void ctkVTKChartView::chartUserBounds(double* bounds)const
358 {
359   Q_D(const ctkVTKChartView);
360   for (int i= 0; i < 8; ++i)
361     {
362     bounds[i] = d->UserBounds[i];
363     }
364 }
365 
366 // ----------------------------------------------------------------------------
setAxesToChartBounds()367 void ctkVTKChartView::setAxesToChartBounds()
368 {
369   vtkChartXY* chart = this->chart();
370   double bounds[8];
371   this->chartBounds(bounds);
372   for (int i = 0; i < chart->GetNumberOfAxes(); ++i)
373     {
374     if (bounds[2*i] != VTK_DOUBLE_MAX)
375       {
376       chart->GetAxis(i)->SetRange(bounds[2*i], bounds[2*i+1]);
377       }
378     }
379 }
380 
381 // ----------------------------------------------------------------------------
boundAxesToChartBounds()382 void ctkVTKChartView::boundAxesToChartBounds()
383 {
384   vtkChartXY* chart = this->chart();
385   double bounds[8];
386   this->chartBounds(bounds);
387   for (int i = 0; i < chart->GetNumberOfAxes(); ++i)
388     {
389     if (bounds[2*i] != VTK_DOUBLE_MAX)
390       {
391       chart->GetAxis(i)->SetMinimumLimit(bounds[2*i]);
392       chart->GetAxis(i)->SetMaximumLimit(bounds[2*i + 1]);
393       }
394     }
395 }
396 
397 // ----------------------------------------------------------------------------
chartBoundsToPlotBounds(double bounds[8],double plotBounds[4]) const398 void ctkVTKChartView::chartBoundsToPlotBounds(double bounds[8], double plotBounds[4])const
399 {
400   plotBounds[0] = bounds[vtkAxis::BOTTOM*2];
401   plotBounds[1] = bounds[vtkAxis::BOTTOM*2 + 1];
402   plotBounds[2] = bounds[vtkAxis::LEFT*2];
403   plotBounds[3] = bounds[vtkAxis::LEFT*2+1];
404 }
405 
406 // ----------------------------------------------------------------------------
mouseDoubleClickEvent(QMouseEvent * event)407 void ctkVTKChartView::mouseDoubleClickEvent(QMouseEvent* event)
408 {
409   if (event->button() == Qt::MidButton)
410     {
411     this->setAxesToChartBounds();
412     }
413   this->Superclass::mouseDoubleClickEvent(event);
414 }
415