source: orange/source/orangeqt/curve.cpp @ 9577:7c6860f77fa7

Revision 9577:7c6860f77fa7, 12.1 KB checked in by mstajdohar, 2 years ago (diff)

Bug fixes.

Line 
1/*
2    This file is part of the plot module for Orange
3    Copyright (C) 2011  Miha Čančula <miha@noughmad.eu>
4
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation, either version 3 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17*/
18
19#include "curve.h"
20
21#include <QtCore/QDebug>
22#include <QtGui/QBrush>
23#include <QtGui/QPen>
24
25#include <QtCore/qmath.h>
26#include <QtCore/QtConcurrentRun>
27#include <QtCore/QFutureWatcher>
28
29#include "point.h"
30#include "plot.h"
31
32#include <QtCore/QParallelAnimationGroup>
33#include <QtCore/QCoreApplication>
34
35Curve::Curve(const QList< double >& x_data, const QList< double >& y_data, QGraphicsItem* parent): PlotItem(parent)
36{
37    // Don't make any calls to update_properties() until the constructor is finished
38    // Otherwise, the program hangs if this is called from a subclass constructor
39    m_autoUpdate = false; 
40   
41    m_style = Points;
42    m_continuous = false;
43    m_needsUpdate = UpdateAll;
44    m_lineItem = new QGraphicsPathItem(this);
45    set_data(x_data, y_data);
46    QObject::connect(&m_pos_watcher, SIGNAL(finished()), SLOT(pointMapFinished()));
47    QObject::connect(&m_coords_watcher, SIGNAL(finished()), SLOT(update_point_positions()));
48    m_autoUpdate = true;
49}
50
51Curve::Curve(QGraphicsItem* parent): PlotItem(parent)
52{
53    m_continuous = false;
54    m_autoUpdate = true;
55    m_style = Points;
56    m_lineItem = new QGraphicsPathItem(this);
57    m_needsUpdate = 0;
58    QObject::connect(&m_pos_watcher, SIGNAL(finished()), SLOT(pointMapFinished()));
59    QObject::connect(&m_coords_watcher, SIGNAL(finished()), SLOT(update_point_positions()));
60}
61
62
63Curve::~Curve()
64{
65    cancel_all_updates();
66}
67
68void Curve::update_number_of_items()
69{
70  cancel_all_updates();
71  if (m_continuous || (m_data.size() == m_pointItems.size()))
72  {
73    m_needsUpdate &= ~UpdateNumberOfItems;
74    return;
75  }
76  if (m_pointItems.size() != m_data.size())
77  {
78    resize_item_list<Point>(m_pointItems, m_data.size());
79    register_points();
80  }
81  Q_ASSERT(m_pointItems.size() == m_data.size());
82}
83
84void Curve::update_properties()
85{
86  cancel_all_updates();
87 
88  bool lines = false;
89  bool points = false;
90 
91  switch (m_style)
92  {
93      case Points:
94          points = true;
95          break;
96         
97      case Lines:
98      case Dots:
99          lines = true;
100          break;
101         
102      case LinesPoints:
103          lines = true;
104          points = true;
105          break;
106         
107      default:
108          lines = m_continuous;
109          points = !m_continuous;
110          break;
111  }
112 
113  m_lineItem->setVisible(lines);
114 
115  if (lines)
116  {
117    QPen p = m_pen;
118    p.setCosmetic(true);
119    p.setStyle((Qt::PenStyle)m_style);
120    m_lineItem->setPen(p);
121    m_lineItem->setPath(continuous_path());
122  } 
123 
124  if (points)
125  {
126   
127    if (m_pointItems.size() != m_data.size())
128    {
129        update_number_of_items();
130    }
131   
132    // Move, resize, reshape and/or recolor the items
133    if (m_needsUpdate & UpdatePosition)
134    {
135        update_point_coordinates();
136    } 
137   
138    if (m_needsUpdate & (UpdateBrush | UpdatePen | UpdateSize | UpdateSymbol) )
139    {
140        update_items(m_pointItems, PointUpdater(m_symbol, m_color, m_pointSize, Point::DisplayPath), UpdateSymbol);
141    }
142    m_needsUpdate = 0;
143  }
144  else
145  {
146      qDeleteAll(m_pointItems);
147      m_pointItems.clear();
148  }
149}
150
151Point* Curve::point_item(double x, double y, int size, QGraphicsItem* parent)
152{
153  if (size == 0)
154  {
155    size = point_size();
156  }
157  if (parent == 0)
158  {
159    parent = this;
160  }
161  Point* item = new Point(m_symbol, m_color, m_pointSize, parent);
162  item->setPos(x,y);
163  return item;
164}
165
166Data Curve::data() const
167{
168  return m_data;
169}
170
171void Curve::set_data(const QList< double > x_data, const QList< double > y_data)
172{
173  Q_ASSERT(x_data.size() == y_data.size());
174  int n = qMin(x_data.size(), y_data.size());
175  qDebug() << "Curve::set_data with" << n << "points";
176  if (n != m_data.size())
177  {
178    m_needsUpdate |= UpdateNumberOfItems;
179  }
180  m_data.clear();
181
182#if QT_VERSION >= 0x040700
183  m_data.reserve(n);
184#endif
185
186  for (int i = 0; i < n; ++i)
187  {
188    DataPoint p;
189    p.x = x_data[i];
190    p.y = y_data[i];
191    m_data.append(p);
192  }
193  set_data_rect(rect_from_data(x_data, y_data));
194  m_needsUpdate |= UpdatePosition;
195  checkForUpdate();
196}
197
198QTransform Curve::graph_transform() const
199{
200  return m_graphTransform;
201}
202
203void Curve::set_graph_transform(const QTransform& transform)
204{
205  if (transform == m_graphTransform)
206  {
207    return;
208  }
209  m_needsUpdate |= UpdatePosition;
210  m_graphTransform = transform;
211  checkForUpdate();
212}
213
214bool Curve::is_continuous() const
215{
216  return m_continuous;
217}
218
219void Curve::set_continuous(bool continuous)
220{
221  if (continuous == m_continuous)
222  {
223    return;
224  }
225  m_continuous = continuous;
226  m_needsUpdate |= UpdateContinuous;
227  checkForUpdate();
228}
229
230QColor Curve::color() const
231{
232  return m_color;
233}
234
235void Curve::set_color(const QColor& color)
236{
237    m_color = color;
238    set_pen(color);
239    set_brush(color);
240}
241
242QPen Curve::pen() const
243{
244    return m_pen;
245}
246
247void Curve::set_pen(QPen pen)
248{
249    m_pen = pen;
250    m_needsUpdate |= UpdatePen;
251    checkForUpdate();
252}
253
254QBrush Curve::brush() const
255{
256    return m_brush;
257}
258
259void Curve::set_brush(QBrush brush)
260{
261    m_brush = brush;
262    m_needsUpdate |= UpdateBrush;
263    checkForUpdate();
264}
265
266int Curve::point_size() const
267{
268  return m_pointSize;
269}
270
271void Curve::set_point_size(int size)
272{
273  if (size == m_pointSize)
274  {
275    return;
276  }
277 
278  m_pointSize = size;
279  m_needsUpdate |= UpdateSize;
280  checkForUpdate();
281}
282
283int Curve::symbol() const
284{
285  return m_symbol;
286}
287
288void Curve::set_symbol(int symbol)
289{
290  if (symbol == m_symbol)
291  {
292    return;
293  }
294  m_symbol = symbol;
295  m_needsUpdate |= UpdateSymbol;
296  checkForUpdate();
297}
298
299int Curve::style() const
300{
301    return m_style;
302}
303
304void Curve::set_style(int style)
305{
306    m_style = style;
307    m_needsUpdate |= UpdateAll;
308    checkForUpdate();
309}
310
311
312
313bool Curve::auto_update() const
314{
315  return m_autoUpdate;
316}
317
318void Curve::set_auto_update(bool auto_update)
319{
320  m_autoUpdate = auto_update;
321  checkForUpdate();
322}
323
324void Curve::checkForUpdate()
325{
326  if ( m_autoUpdate && m_needsUpdate )
327  {
328    update_properties();
329  }
330}
331
332void Curve::changeContinuous()
333{
334  cancel_all_updates();
335  if (m_continuous)
336  {
337    qDeleteAll(m_pointItems);
338    m_pointItems.clear();
339   
340    if (!m_lineItem)
341    {
342      m_lineItem = new QGraphicsPathItem(this);
343    }
344  } else {
345    delete m_lineItem;
346    m_lineItem = 0;
347  }
348  register_points();
349}
350
351void Curve::set_dirty(Curve::UpdateFlags flags)
352{
353    m_needsUpdate |= flags;
354    checkForUpdate();
355}
356
357void Curve::set_zoom_transform(const QTransform& transform)
358{
359    m_zoom_transform = transform;
360    m_needsUpdate |= UpdateZoom;
361    checkForUpdate();
362}
363
364QTransform Curve::zoom_transform()
365{
366    return m_zoom_transform;
367}
368
369void Curve::cancel_all_updates()
370{
371    QMap<UpdateFlag, QFuture< void > >::iterator it = m_currentUpdate.begin();
372    QMap<UpdateFlag, QFuture< void > >::iterator end = m_currentUpdate.end();
373    for (it; it != end; ++it)
374    {
375        if (it.value().isRunning())
376        {
377            it.value().cancel();
378        }
379    }
380    for (it = m_currentUpdate.begin(); it != end; ++it)
381    {
382        if (it.value().isRunning())
383        {
384            it.value().waitForFinished();
385        }
386    }
387    m_currentUpdate.clear();
388   
389    m_coords_watcher.blockSignals(true);
390    m_coords_watcher.cancel();
391    m_coords_watcher.waitForFinished();
392    m_coords_watcher.blockSignals(false);
393   
394    m_pos_watcher.blockSignals(true);
395    m_pos_watcher.cancel();
396    m_pos_watcher.waitForFinished();
397    m_pos_watcher.blockSignals(false);
398}
399
400void Curve::register_points()
401{
402    Plot* p = plot();
403    if (p)
404    {
405        p->remove_all_points(this);
406        p->add_points(m_pointItems, this);
407    }
408}
409
410Curve::UpdateFlags Curve::needs_update()
411{
412    return m_needsUpdate;
413}
414
415void Curve::set_updated(Curve::UpdateFlags flags)
416{
417    m_needsUpdate &= ~flags;
418}
419
420void Curve::set_points(const QList< Point* >& points)
421{
422    if (points == m_pointItems)
423    {
424        return;
425    }
426    m_pointItems = points;
427    register_points();
428}
429
430QList< Point* > Curve::points()
431{
432    return m_pointItems;
433}
434
435void Curve::set_labels_on_marked(bool value)
436{
437    m_labels_on_marked = value;
438}
439
440bool Curve::labels_on_marked()
441{
442    return m_labels_on_marked;
443}
444
445void Curve::update_point_coordinates()
446{
447    if (m_coords_watcher.isRunning())
448    {
449        m_coords_watcher.blockSignals(true);
450        m_coords_watcher.cancel();
451        m_coords_watcher.waitForFinished();
452        m_coords_watcher.blockSignals(false);
453    }
454    m_coords_watcher.setFuture(QtConcurrent::run(this, &Curve::update_point_properties_threaded<DataPoint>, QByteArray("coordinates"), m_data));
455}
456
457void Curve::update_point_positions()
458{
459    if (m_pos_watcher.isRunning())
460    {
461        m_pos_watcher.blockSignals(true);
462        m_pos_watcher.cancel();
463        m_pos_watcher.waitForFinished();
464        m_pos_watcher.blockSignals(false);
465    }
466    if (m_pointItems.isEmpty())
467    {
468        return;
469    }
470    if (use_animations())
471    {
472        m_pos_watcher.setFuture(QtConcurrent::mapped(m_pointItems, PointPosMapper(m_graphTransform)));
473    }
474    else
475    {
476        update_items(m_pointItems, PointPosUpdater(m_graphTransform), UpdatePosition);
477    }
478}
479
480void Curve::pointMapFinished()
481{
482    if (m_pointItems.size() != m_pos_watcher.future().results().size())
483    {
484        // The calculation that just finished is already out of date, ignore it
485        return;
486    }
487    QParallelAnimationGroup* group = new QParallelAnimationGroup(this);
488    int n = m_pointItems.size();
489    for (int i = 0; i < n; ++i)
490    {
491        /*
492         * If a point was just created, its position is (0,0)
493         * In this case, animating it would create more confusion that good
494         * So we just move it without an animation.
495         * This is the case (for example) for the anchor curve in RadViz
496         */
497        if (m_pointItems[i]->pos().isNull())
498        {
499            m_pointItems[i]->setPos(m_pos_watcher.resultAt(i));
500            // move point label
501            if (m_pointItems[i]->label)
502            {
503                m_pointItems[i]->label->setPos(m_pos_watcher.resultAt(i));
504            }
505        }
506        else
507        {
508            QPropertyAnimation* a = new QPropertyAnimation(m_pointItems[i], "pos", m_pointItems[i]);
509            a->setEndValue(m_pos_watcher.resultAt(i));
510            group->addAnimation(a);
511
512            // move point label
513            if (m_pointItems[i]->label)
514            {
515                QPropertyAnimation* b = new QPropertyAnimation(m_pointItems[i]->label, "pos", m_pointItems[i]->label);
516                b->setEndValue(m_pos_watcher.resultAt(i));
517                group->addAnimation(b);
518            }
519        }
520    }
521    group->start(QAbstractAnimation::DeleteWhenStopped);
522}
523
524bool Curve::use_animations()
525{
526    return plot() && plot()->animate_points;
527}
528
529void Curve::update_point_properties_same(const QByteArray& property, const QVariant& value, bool animate) {
530    int n = m_pointItems.size();
531
532    if (animate && use_animations())
533    {
534        QParallelAnimationGroup* group = new QParallelAnimationGroup(this);
535        for (int i = 0; i < n; ++i)
536        {
537            QPropertyAnimation* a = new QPropertyAnimation(m_pointItems[i], property, m_pointItems[i]);
538            a->setEndValue(value);
539            group->addAnimation(a);
540        }
541        group->start(QAbstractAnimation::DeleteWhenStopped);
542    }
543    else
544    {
545        m_property_updates[property] = QtConcurrent::map(m_pointItems, PointPropertyUpdater(property, value));
546    }
547}
548
549QPainterPath Curve::continuous_path()
550{
551    QPainterPath path;
552    if (m_data.isEmpty())
553    {
554        return path;
555    }
556    path.moveTo(m_data[0]);
557    int n = m_data.size();
558    QPointF p;
559    for (int i = 1; i < n; ++i)
560    {
561        path.lineTo(m_data[i]);
562    }
563    return m_graphTransform.map(path);
564}
565
566#include "curve.moc"
567
Note: See TracBrowser for help on using the repository browser.