xychart.cpp
304 lines
| 9.6 KiB
| text/x-c
|
CppLexer
|
r794 | /**************************************************************************** | ||
|
r1217 | ** | ||
** Copyright (C) 2012 Digia Plc | ||||
** All rights reserved. | ||||
** For any questions to Digia, please use contact form at http://qt.digia.com | ||||
** | ||||
** This file is part of the Qt Commercial Charts Add-on. | ||||
** | ||||
** $QT_BEGIN_LICENSE$ | ||||
** Licensees holding valid Qt Commercial licenses may use this file in | ||||
** accordance with the Qt Commercial License Agreement provided with the | ||||
** Software or, alternatively, in accordance with the terms contained in | ||||
** a written agreement between you and Digia. | ||||
** | ||||
** If you have questions regarding the use of this file, please use | ||||
** contact form at http://qt.digia.com | ||||
** $QT_END_LICENSE$ | ||||
** | ||||
****************************************************************************/ | ||||
|
r794 | |||
|
r1218 | #include "xychart_p.h" | ||
|
r466 | #include "qxyseries.h" | ||
|
r938 | #include "qxyseries_p.h" | ||
|
r466 | #include "chartpresenter_p.h" | ||
|
r530 | #include "chartanimator_p.h" | ||
|
r466 | #include <QPainter> | ||
|
r1055 | #include <QAbstractItemModel> | ||
|
r1164 | #include "qxymodelmapper.h" | ||
|
r466 | |||
QTCOMMERCIALCHART_BEGIN_NAMESPACE | ||||
//TODO: optimize : remove points which are not visible | ||||
|
r1218 | XYChart::XYChart(QXYSeries *series, ChartPresenter *presenter):Chart(presenter), | ||
|
r1217 | m_minX(0), | ||
m_maxX(0), | ||||
m_minY(0), | ||||
m_maxY(0), | ||||
m_series(series), | ||||
m_animation(0) | ||||
{ | ||||
QObject::connect(series->d_func(),SIGNAL(pointReplaced(int)),this,SLOT(handlePointReplaced(int))); | ||||
QObject::connect(series->d_func(),SIGNAL(pointAdded(int)),this,SLOT(handlePointAdded(int))); | ||||
QObject::connect(series->d_func(),SIGNAL(pointsAdded(int, int)),this,SLOT(handlePointsAdded(int, int))); | ||||
QObject::connect(series->d_func(),SIGNAL(pointRemoved(int)),this,SLOT(handlePointRemoved(int))); | ||||
QObject::connect(series->d_func(),SIGNAL(pointsRemoved(int, int)),this,SLOT(handlePointsRemoved(int, int))); | ||||
QObject::connect(series->d_func(),SIGNAL(reinitialized()),this,SLOT(handleReinitialized())); | ||||
QObject::connect(this,SIGNAL(clicked(QPointF)),series,SIGNAL(clicked(QPointF))); | ||||
} | ||||
|
r1218 | void XYChart::setGeometryPoints(QVector<QPointF>& points) | ||
|
r1217 | { | ||
m_points = points; | ||||
} | ||||
|
r1218 | void XYChart::setClipRect(const QRectF &rect) | ||
|
r1217 | { | ||
m_clipRect = rect; | ||||
} | ||||
|
r1218 | void XYChart::setAnimation(XYAnimation* animation) | ||
|
r466 | { | ||
|
r1217 | m_animation=animation; | ||
|
r466 | } | ||
|
r1218 | QPointF XYChart::calculateGeometryPoint(const QPointF &point) const | ||
|
r466 | { | ||
const qreal deltaX = m_size.width()/(m_maxX-m_minX); | ||||
const qreal deltaY = m_size.height()/(m_maxY-m_minY); | ||||
qreal x = (point.x() - m_minX)* deltaX; | ||||
qreal y = (point.y() - m_minY)*-deltaY + m_size.height(); | ||||
return QPointF(x,y); | ||||
} | ||||
|
r1218 | QPointF XYChart::calculateGeometryPoint(int index) const | ||
|
r466 | { | ||
const qreal deltaX = m_size.width()/(m_maxX-m_minX); | ||||
const qreal deltaY = m_size.height()/(m_maxY-m_minY); | ||||
|
r1057 | const QList<QPointF>& vector = m_series->points(); | ||
qreal x = (vector[index].x() - m_minX)* deltaX; | ||||
qreal y = (vector[index].y() - m_minY)*-deltaY + m_size.height(); | ||||
|
r466 | return QPointF(x,y); | ||
} | ||||
|
r1218 | QVector<QPointF> XYChart::calculateGeometryPoints() const | ||
|
r466 | { | ||
const qreal deltaX = m_size.width()/(m_maxX-m_minX); | ||||
const qreal deltaY = m_size.height()/(m_maxY-m_minY); | ||||
|
r1057 | QVector<QPointF> result; | ||
result.resize(m_series->count()); | ||||
const QList<QPointF>& vector = m_series->points(); | ||||
|
r466 | for (int i = 0; i < m_series->count(); ++i) { | ||
|
r1057 | qreal x = (vector[i].x() - m_minX)* deltaX; | ||
qreal y = (vector[i].y() - m_minY)*-deltaY + m_size.height(); | ||||
result[i].setX(x); | ||||
result[i].setY(y); | ||||
|
r466 | } | ||
|
r1057 | return result; | ||
|
r466 | } | ||
|
r1218 | QPointF XYChart::calculateDomainPoint(const QPointF &point) const | ||
|
r544 | { | ||
const qreal deltaX = m_size.width()/(m_maxX-m_minX); | ||||
const qreal deltaY = m_size.height()/(m_maxY-m_minY); | ||||
qreal x = point.x()/deltaX +m_minX; | ||||
qreal y = (point.y()-m_size.height())/(-deltaY)+ m_minY; | ||||
return QPointF(x,y); | ||||
} | ||||
|
r1218 | void XYChart::updateChart(QVector<QPointF> &oldPoints, QVector<QPointF> &newPoints,int index) | ||
|
r466 | { | ||
|
r1217 | if (m_animation) { | ||
m_animation->setValues(oldPoints, newPoints, index); | ||||
animator()->startAnimation(m_animation); | ||||
} | ||||
else { | ||||
setGeometryPoints(newPoints); | ||||
updateGeometry(); | ||||
|
r530 | } | ||
|
r466 | } | ||
//handlers | ||||
|
r1218 | void XYChart::handlePointAdded(int index) | ||
|
r466 | { | ||
|
r1054 | if (m_series->model() == 0) { | ||
Q_ASSERT(index<m_series->count()); | ||||
Q_ASSERT(index>=0); | ||||
} | ||||
|
r1217 | |||
|
r1018 | QVector<QPointF> points = m_points; | ||
QPointF point; | ||||
|
r990 | point = calculateGeometryPoint(index); | ||
points.insert(index, point); | ||||
|
r1217 | |||
if(m_animation) { | ||||
m_animation->setAnimationType(XYAnimation::LineDrawAnimation); | ||||
} | ||||
updateChart(m_points,points,index); | ||||
|
r466 | } | ||
|
r1054 | |||
|
r1218 | void XYChart::handlePointsAdded(int start, int end) | ||
|
r1217 | { | ||
|
r1054 | if (m_series->model() == 0) { | ||
for (int i = start; i <= end; i++) | ||||
|
r1217 | handlePointAdded(i); | ||
} | ||||
else { | ||||
|
r1164 | int mapFirst = m_series->modelMapper()->first(); | ||
int mapCount = m_series->modelMapper()->count(); | ||||
if (mapCount != -1 && start >= mapFirst + mapCount) { | ||||
return; | ||||
|
r1217 | } | ||
else { | ||||
|
r1164 | int addedCount = end - start + 1; | ||
if (mapCount != -1 && addedCount > mapCount) | ||||
|
r1217 | addedCount = mapCount; | ||
int first = qMax(start, mapFirst); // get the index of the first item that will be added | ||||
int last = qMin(first + addedCount - 1, mapCount + mapFirst - 1);// get the index of the last item that will be added | ||||
|
r1164 | for (int i = first; i <= last; i++) { | ||
handlePointAdded(i - mapFirst); | ||||
} | ||||
// the map is limited therefore the items that are now outside the map | ||||
// need to be removed from the drawn points | ||||
if (mapCount != -1 && m_points.size() > mapCount) | ||||
|
r1217 | for (int i = m_points.size() - 1; i >= mapCount; i--) | ||
handlePointRemoved(i); | ||||
|
r1054 | } | ||
} | ||||
} | ||||
|
r1218 | void XYChart::handlePointRemoved(int index) | ||
|
r938 | { | ||
|
r1054 | if (m_series->model() == 0) { | ||
Q_ASSERT(index<m_series->count() + 1); | ||||
Q_ASSERT(index>=0); | ||||
} | ||||
|
r1018 | QVector<QPointF> points = m_points; | ||
|
r990 | points.remove(index); | ||
|
r1217 | |||
if(m_animation) { | ||||
m_animation->setAnimationType(XYAnimation::LineDrawAnimation); | ||||
} | ||||
updateChart(m_points,points,index); | ||||
|
r466 | } | ||
|
r1218 | void XYChart::handlePointsRemoved(int start, int end) | ||
|
r1054 | { | ||
Q_UNUSED(start) | ||||
Q_UNUSED(end) | ||||
|
r1055 | if (m_series->model() == 0) { | ||
for (int i = end; i >= start; i--) | ||||
|
r1217 | handlePointRemoved(i); | ||
} | ||||
else { | ||||
|
r1055 | // series uses model as a data source | ||
|
r1164 | int mapFirst = m_series->modelMapper()->first(); | ||
int mapCount = m_series->modelMapper()->count(); | ||||
|
r1055 | int removedCount = end - start + 1; | ||
|
r1065 | if (mapCount != -1 && start >= mapFirst + mapCount) { | ||
|
r1055 | return; | ||
|
r1217 | } | ||
else { | ||||
int toRemove = qMin(m_points.size(), removedCount); // first find how many items can actually be removed | ||||
int first = qMax(start, mapFirst);// get the index of the first item that will be removed. | ||||
int last = qMin(first + toRemove - 1, m_points.size() + mapFirst - 1);// get the index of the last item that will be removed. | ||||
|
r1090 | if (last - first == 0) { | ||
for (int i = last; i >= first; i--) { | ||||
handlePointRemoved(i - mapFirst); | ||||
} | ||||
|
r1217 | } | ||
else { | ||||
|
r1090 | QVector<QPointF> points = m_points; | ||
for (int i = last; i >= first; i--) | ||||
|
r1217 | points.remove(i - mapFirst); | ||
setGeometryPoints(points); | ||||
updateGeometry(); | ||||
|
r1072 | } | ||
|
r1065 | if (mapCount != -1) { | ||
|
r1217 | int itemsAvailable; // check how many are available to be added | ||
|
r1164 | if (m_series->modelMapper()->orientation() == Qt::Vertical) | ||
|
r1217 | itemsAvailable = m_series->model()->rowCount() - mapFirst - m_points.size(); | ||
|
r1065 | else | ||
|
r1217 | itemsAvailable = m_series->model()->columnCount() - mapFirst - m_points.size(); | ||
int toBeAdded = qMin(itemsAvailable, mapCount - m_points.size());// add not more items than there is space left to be filled. | ||||
|
r1065 | int currentSize = m_points.size(); | ||
if (toBeAdded > 0) | ||||
|
r1217 | for (int i = m_points.size(); i < currentSize + toBeAdded; i++) { | ||
handlePointAdded(i); | ||||
} | ||||
|
r1065 | } | ||
|
r1055 | } | ||
} | ||||
|
r1054 | } | ||
|
r1218 | void XYChart::handlePointReplaced(int index) | ||
|
r466 | { | ||
Q_ASSERT(index<m_series->count()); | ||||
Q_ASSERT(index>=0); | ||||
QPointF point = calculateGeometryPoint(index); | ||||
|
r476 | QVector<QPointF> points = m_points; | ||
|
r527 | points.replace(index,point); | ||
|
r1217 | |||
if(m_animation) { | ||||
m_animation->setAnimationType(XYAnimation::MoveDownAnimation); | ||||
} | ||||
updateChart(m_points,points,index); | ||||
|
r466 | } | ||
|
r1218 | void XYChart::handleReinitialized() | ||
|
r1085 | { | ||
QVector<QPointF> points = calculateGeometryPoints(); | ||||
|
r1217 | |||
if(m_animation) { | ||||
m_animation->setAnimationType(XYAnimation::LineDrawAnimation); | ||||
} | ||||
updateChart(m_points,points); | ||||
|
r1085 | } | ||
|
r1218 | void XYChart::handleDomainChanged(qreal minX, qreal maxX, qreal minY, qreal maxY) | ||
|
r466 | { | ||
m_minX=minX; | ||||
m_maxX=maxX; | ||||
m_minY=minY; | ||||
m_maxY=maxY; | ||||
|
r743 | if (isEmpty()) return; | ||
|
r466 | QVector<QPointF> points = calculateGeometryPoints(); | ||
|
r1217 | |||
if(m_animation) { | ||||
|
r1220 | m_animation->setAnimationType(XYAnimation::MoveDownAnimation); | ||
|
r1217 | } | ||
updateChart(m_points,points); | ||||
|
r466 | } | ||
|
r1218 | void XYChart::handleGeometryChanged(const QRectF &rect) | ||
|
r466 | { | ||
|
r833 | Q_ASSERT(rect.isValid()); | ||
m_size=rect.size(); | ||||
m_clipRect=rect.translated(-rect.topLeft()); | ||||
|
r1218 | m_origin=rect.topLeft(); | ||
|
r466 | |||
|
r743 | if (isEmpty()) return; | ||
|
r833 | QVector<QPointF> points = calculateGeometryPoints(); | ||
|
r1217 | if(m_animation) { | ||
m_animation->setAnimationType(XYAnimation::LineDrawAnimation); | ||||
} | ||||
updateChart(m_points,points); | ||||
|
r466 | } | ||
|
r1218 | bool XYChart::isEmpty() | ||
|
r466 | { | ||
|
r1082 | return !m_clipRect.isValid() || qFuzzyIsNull(m_maxX - m_minX) || qFuzzyIsNull(m_maxY - m_minY) || m_series->points().isEmpty(); | ||
|
r466 | } | ||
|
r1218 | #include "moc_xychart_p.cpp" | ||
|
r466 | |||
QTCOMMERCIALCHART_END_NAMESPACE | ||||