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