diff --git a/src/axis/axisitem.cpp b/src/axis/axisitem.cpp index 5cd0c82..84067af 100644 --- a/src/axis/axisitem.cpp +++ b/src/axis/axisitem.cpp @@ -4,6 +4,7 @@ #include "chartanimator_p.h" #include #include +#include static int label_padding = 5; @@ -78,16 +79,18 @@ void Axis::updateLayout(QVector& layout) QStringList Axis::createLabels(int ticks, qreal min, qreal max) const { Q_ASSERT(max>=min); - Q_ASSERT(ticks>0); + Q_ASSERT(ticks>1); QStringList labels; + int n = qMax(int(-floor(log10((max-min)/(ticks-1)))),0); + QChartAxisCategories* categories = m_chartAxis->categories(); for(int i=0; i< ticks; i++) { qreal value = min + (i * (max - min)/ (ticks-1)); if(categories->count()==0) { - labels << QString::number(value); + labels << QString::number(value,'f',n); } else { diff --git a/src/axis/qchartaxis.cpp b/src/axis/qchartaxis.cpp index d84a554..3a300a7 100644 --- a/src/axis/qchartaxis.cpp +++ b/src/axis/qchartaxis.cpp @@ -149,7 +149,8 @@ m_shadesVisible(false), m_shadesOpacity(1.0), m_min(0), m_max(0), -m_ticksCount(5) +m_ticksCount(5), +m_selection(NativeLabelsSelection) { } @@ -343,6 +344,7 @@ void QChartAxis::setRange(qreal min, qreal max) if(changed) { emit rangeChanged(m_min,m_max); + emit this->changed(m_min, m_max, m_ticksCount, m_selection); } } @@ -354,6 +356,7 @@ void QChartAxis::setTicksCount(int count) if(m_ticksCount!=count) { m_ticksCount=count; emit ticksCountChanged(count); + emit changed(m_min, m_max, m_ticksCount, m_selection); } } @@ -381,14 +384,18 @@ void QChartAxis::hide() emit updated(); } -void QChartAxis::handleAxisRangeChanged(qreal min, qreal max) +void QChartAxis::handleAxisRangeChanged(qreal min, qreal max,int count) { setRange(min,max); + setTicksCount(count); } -void QChartAxis::handleAxisTicksChanged(int count) +void QChartAxis::setLabelsSelectionMode(LabelsSelection mode) { - setTicksCount(count); + if(m_selection!=mode){ + m_selection=mode; + emit changed(m_min, m_max, m_ticksCount, m_selection); + } } #include "moc_qchartaxis.cpp" diff --git a/src/axis/qchartaxis.h b/src/axis/qchartaxis.h index 5ad82ca..8dac320 100644 --- a/src/axis/qchartaxis.h +++ b/src/axis/qchartaxis.h @@ -1,17 +1,19 @@ #ifndef QCHARTAXIS_H_ #define QCHARTAXIS_H_ -#include +#include "qchartglobal.h" +#include "qchartaxiscategories.h" #include #include - QTCOMMERCIALCHART_BEGIN_NAMESPACE class QTCOMMERCIALCHART_EXPORT QChartAxis : public QObject { Q_OBJECT public: + enum LabelsSelection{ NativeLabelsSelection, LooseLabelsSelection}; + QChartAxis(QObject* parent =0); ~QChartAxis(); @@ -60,6 +62,9 @@ public: void setTicksCount(int count); int ticksCount() const { return m_ticksCount;} + void setLabelsSelectionMode(LabelsSelection mode); + LabelsSelection labelsSelectionMode() const { return m_selection;} + QChartAxisCategories* categories() { return &m_category; } void show(); @@ -73,10 +78,10 @@ signals: //interal signal void updated(); + void changed(qreal min, qreal max, int tickCount,QChartAxis::LabelsSelection mode); //internal slot public slots: - void handleAxisRangeChanged(qreal min, qreal max); - void handleAxisTicksChanged(int count); + void handleAxisRangeChanged(qreal min, qreal max,int count); private: bool m_axisVisible; @@ -102,6 +107,8 @@ private: int m_ticksCount; QChartAxisCategories m_category; + + LabelsSelection m_selection; }; QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/chartdataset.cpp b/src/chartdataset.cpp index 93c56d6..4cb1914 100644 --- a/src/chartdataset.cpp +++ b/src/chartdataset.cpp @@ -47,12 +47,10 @@ void ChartDataSet::addSeries(QSeries* series, QChartAxis *axisY) if(!domain) { domain = new Domain(); - QObject::connect(axisY,SIGNAL(rangeChanged(qreal,qreal)),domain,SLOT(handleAxisRangeYChanged(qreal,qreal))); - QObject::connect(axisX(),SIGNAL(rangeChanged(qreal,qreal)),domain,SLOT(handleAxisRangeXChanged(qreal,qreal))); - QObject::connect(axisY,SIGNAL(ticksCountChanged(int)),domain,SLOT(handleAxisYTicksCountChanged(int))); - QObject::connect(axisX(),SIGNAL(ticksCountChanged(int)),domain,SLOT(handleAxisXTicksCountChanged(int))); - QObject::connect(domain,SIGNAL(rangeYChanged(qreal,qreal,int)),axisY,SLOT(handleAxisRangeChanged(qreal,qreal))); - QObject::connect(domain,SIGNAL(rangeXChanged(qreal,qreal,int)),axisX(),SLOT(handleAxisRangeChanged(qreal,qreal))); + QObject::connect(axisY,SIGNAL(changed(qreal,qreal,int,QChartAxis::LabelsSelection)),domain,SLOT(handleAxisYChanged(qreal,qreal,int,QChartAxis::LabelsSelection))); + QObject::connect(axisX(),SIGNAL(changed(qreal,qreal,int,QChartAxis::LabelsSelection)),domain,SLOT(handleAxisXChanged(qreal,qreal,int,QChartAxis::LabelsSelection))); + QObject::connect(domain,SIGNAL(rangeYChanged(qreal,qreal,int)),axisY,SLOT(handleAxisRangeChanged(qreal,qreal,int))); + QObject::connect(domain,SIGNAL(rangeXChanged(qreal,qreal,int)),axisX(),SLOT(handleAxisRangeChanged(qreal,qreal,int))); //initialize m_axisDomainMap.insert(axisY,domain); emit axisAdded(axisY,domain); diff --git a/src/chartpresenter.cpp b/src/chartpresenter.cpp index 8937572..48c08af 100644 --- a/src/chartpresenter.cpp +++ b/src/chartpresenter.cpp @@ -31,7 +31,6 @@ ChartPresenter::ChartPresenter(QChart* chart,ChartDataSet* dataset):QObject(char m_animator(0), m_dataset(dataset), m_chartTheme(0), - m_zoomIndex(0), m_rect(QRectF(QPoint(0,0),m_chart->size())), m_options(QChart::NoAnimation), m_themeForce(false) @@ -66,6 +65,7 @@ void ChartPresenter::handleGeometryChanged() rect.adjust(m_chart->padding(),m_chart->padding(), -m_chart->padding(), -m_chart->padding()); //rewrite zoom stack + /* for(int i=0;ihandleDomainChanged(domain->minX(),domain->maxX(),domain->minY(),domain->maxY()); if(m_rect.isValid()) item->handleGeometryChanged(m_rect); m_chartItems.insert(series,item); - zoomReset(); } void ChartPresenter::handleSeriesRemoved(QSeries* series) @@ -336,8 +335,6 @@ void ChartPresenter::zoomIn(const QRectF& rect) m_animator->setState(ChartAnimator::ZoomInState,point); } m_dataset->zoomInDomain(r,geometry().size()); - m_zoomStack<setState(ChartAnimator::ShowState); } @@ -345,25 +342,22 @@ void ChartPresenter::zoomIn(const QRectF& rect) void ChartPresenter::zoomOut() { - if(m_zoomIndex==0) return; if(m_animator) { m_animator->setState(ChartAnimator::ZoomOutState); } - m_dataset->zoomOutDomain(m_zoomStack[m_zoomIndex-1],geometry().size()); - m_zoomIndex--; - m_zoomStack.resize(m_zoomIndex); + + QSizeF size = geometry().size(); + QRectF rect = geometry(); + rect.translate(-m_chart->padding(), -m_chart->padding()); + m_dataset->zoomOutDomain(rect.adjusted(size.width()/4,size.height()/4,-size.width()/4,-size.height()/4),size); + //m_dataset->zoomOutDomain(m_zoomStack[m_zoomIndex-1],geometry().size()); + if(m_animator){ m_animator->setState(ChartAnimator::ShowState); } } -void ChartPresenter::zoomReset() -{ - m_zoomIndex=0; - m_zoomStack.resize(m_zoomIndex); -} - void ChartPresenter::scroll(int dx,int dy) { if(m_animator){ diff --git a/src/chartpresenter_p.h b/src/chartpresenter_p.h index 1265f6c..ca8ed7f 100644 --- a/src/chartpresenter_p.h +++ b/src/chartpresenter_p.h @@ -55,7 +55,6 @@ public: void zoomIn(); void zoomIn(const QRectF& rect); void zoomOut(); - void zoomReset(); void scroll(int dx,int dy); private: @@ -77,10 +76,8 @@ private: ChartAnimator* m_animator; ChartDataSet* m_dataset; ChartTheme *m_chartTheme; - int m_zoomIndex; QMap m_chartItems; QMap m_axisItems; - QVector m_zoomStack; QRectF m_rect; QChart::AnimationOptions m_options; bool m_themeForce; diff --git a/src/domain.cpp b/src/domain.cpp index 9eedc66..8ddf727 100644 --- a/src/domain.cpp +++ b/src/domain.cpp @@ -1,4 +1,5 @@ #include "domain_p.h" +#include QTCOMMERCIALCHART_BEGIN_NAMESPACE @@ -8,7 +9,8 @@ m_maxX(0), m_minY(0), m_maxY(0), m_tickXCount(5), -m_tickYCount(5) +m_tickYCount(5), +m_selection(QChartAxis::NativeLabelsSelection) { } @@ -18,55 +20,83 @@ Domain::~Domain() void Domain::setRange(qreal minX, qreal maxX, qreal minY, qreal maxY) { - bool changed = false; + setRange(minX, maxX, minY, maxY,m_tickXCount,m_tickYCount); +} - if(m_minX!=minX || m_maxX!=maxX) - { - m_minX=minX; - m_maxX=maxX; - changed=true; - emit rangeXChanged(minX,maxX, m_tickXCount); - } +void Domain::setRange(qreal minX, qreal maxX, qreal minY, qreal maxY,int tickXCount,int tickYCount) +{ + bool domainChanged = false; + bool tickXChanged = false; + bool tickYChanged = false; - if(m_minY!=minY || m_maxY!=maxY){ - m_minY=minY; - m_maxY=maxY; - changed=true; - emit rangeYChanged(minY,maxY, m_tickYCount); - } + if(m_tickXCount!=tickXCount) { + m_tickXCount=tickXCount; + tickXChanged=true; + } - if(changed){ - emit domainChanged(m_minX, m_maxX, m_minY, m_maxY); - } + if(m_tickXCount!=tickYCount) { + m_tickXCount=tickYCount; + tickYChanged=true; + } + + if(m_minX!=minX || m_maxX!=maxX) { + niceNumbers(minX, maxX, m_tickXCount); + m_minX=minX; + m_maxX=maxX; + domainChanged=true; + tickXChanged=false; + emit rangeXChanged(minX,maxX, m_tickXCount); + } + + if(m_minY!=minY || m_maxY!=maxY) { + niceNumbers(minY, maxY, m_tickYCount); + m_minY=minY; + m_maxY=maxY; + domainChanged=true; + tickYChanged=false; + emit rangeYChanged(minY,maxY, m_tickYCount); + } + + if(domainChanged) { + emit this->domainChanged(m_minX, m_maxX, m_minY, m_maxY); + } + + if(tickXChanged) { + emit rangeXChanged(minX,maxX, m_tickXCount); + } + + if(tickYChanged) { + emit rangeYChanged(minY,maxY, m_tickYCount); + } } + void Domain::setRangeX(qreal min, qreal max) { setRange(min,max,m_minY, m_maxY); } void Domain::setRangeY(qreal min, qreal max) { - setRange(m_minX, m_maxX, min, max); + setRange(m_minX, m_maxX, min, max); } void Domain::setMinX(qreal min) { - setRange(min, m_maxX, m_minY, m_maxY); + setRange(min, m_maxX, m_minY, m_maxY); } void Domain::setMaxX(qreal max) { - setRange(m_minX, max, m_minY, m_maxY); + setRange(m_minX, max, m_minY, m_maxY); } void Domain::setMinY(qreal min) { - setRange(m_minX, m_maxX, min, m_maxY); + setRange(m_minX, m_maxX, min, m_maxY); } void Domain::setMaxY(qreal max) { setRange(m_minX, m_maxX, m_minY, max); - } qreal Domain::spanX() const @@ -96,6 +126,9 @@ void Domain::zoomIn(const QRectF& rect, const QSizeF& size) m_minY = m_maxY - dy * rect.bottom(); m_maxY = m_maxY - dy * rect.top(); + niceNumbers(m_minX, m_maxX, m_tickXCount); + niceNumbers(m_minY, m_maxY, m_tickYCount); + emit domainChanged(m_minX, m_maxX, m_minY, m_maxY); emit rangeXChanged(m_minX, m_maxX, m_tickXCount); emit rangeYChanged(m_minY, m_maxY, m_tickYCount); @@ -111,6 +144,9 @@ void Domain::zoomOut(const QRectF& rect, const QSizeF& size) m_maxY = m_minY + dy * rect.bottom(); m_minY = m_maxY - dy * size.height(); + niceNumbers(m_minX, m_maxX, m_tickXCount); + niceNumbers(m_minY, m_maxY, m_tickYCount); + emit domainChanged(m_minX, m_maxX, m_minY, m_maxY); emit rangeXChanged(m_minX, m_maxX, m_tickXCount); emit rangeYChanged(m_minY, m_maxY, m_tickYCount); @@ -121,44 +157,71 @@ void Domain::move(int dx,int dy,const QSizeF& size) qreal x = spanX() / size.width(); qreal y = spanY() / size.height(); - if(dx!=0){ - m_minX = m_minX + x * dx; - m_maxX = m_maxX + x * dx; - emit rangeXChanged(m_minX, m_maxX, m_tickXCount); + if(dx!=0) { + m_minX = m_minX + x * dx; + m_maxX = m_maxX + x * dx; + emit rangeXChanged(m_minX, m_maxX, m_tickXCount); } - if(dy!=0){ - m_minY = m_minY + y * dy; - m_maxY = m_maxY + y * dy; - emit rangeYChanged(m_minY, m_maxY, m_tickYCount); + if(dy!=0) { + m_minY = m_minY + y * dy; + m_maxY = m_maxY + y * dy; + emit rangeYChanged(m_minY, m_maxY, m_tickYCount); } emit domainChanged(m_minX, m_maxX, m_minY, m_maxY); } -void Domain::handleAxisRangeXChanged(qreal min,qreal max) +void Domain::handleAxisXChanged(qreal min,qreal max,int tickXCount,QChartAxis::LabelsSelection mode) +{ + m_selection=mode; + setRange(min,max,m_minY, m_maxY,tickXCount,m_tickYCount); +} + +void Domain::handleAxisYChanged(qreal min,qreal max,int tickYCount,QChartAxis::LabelsSelection mode) { - setRange(min,max,m_minY, m_maxY); + m_selection=mode; + setRange(m_minX, m_maxX, min, max,m_tickXCount,tickYCount); } -void Domain::handleAxisRangeYChanged(qreal min,qreal max) +//algorithm defined by Paul S.Heckbert GraphicalGems I + +void Domain::niceNumbers(qreal &min, qreal &max, int &ticksCount) { - setRange(m_minX, m_maxX, min, max); + if(m_selection!=QChartAxis::NativeLabelsSelection) + looseNiceNumbers(min,max,ticksCount); } -void Domain::handleAxisXTicksCountChanged(int tickCount) +void Domain::looseNiceNumbers(qreal &min, qreal &max, int &ticksCount) { - if(m_tickXCount!=tickCount){ - m_tickXCount=tickCount; - emit rangeXChanged(m_minX,m_maxX, m_tickXCount); - } + qreal range = niceNumber(max-min,true); //range with ceiling + qreal step = niceNumber(range/(ticksCount-1),false); + min = floor(min/step); + max = ceil(max/step); + ticksCount = int(max-min) +1; + min*=step; + max*=step; } -void Domain::handleAxisYTicksCountChanged(int tickCount) +//nice numbers can be expressed as form of 1*10^n, 2* 10^n or 5*10^n + +qreal Domain::niceNumber(qreal x,bool ceiling) { - if(m_tickYCount!=tickCount){ - m_tickYCount=tickCount; - emit rangeYChanged(m_minY,m_maxY, m_tickYCount); + qreal z = pow(10,floor(log10(x))); //find corresponding number of the form of 10^n than is smaller than x + qreal q = x/z;//q<10 && q>=1; + + if(ceiling) { + if(q <= 1.0) q=1; + else if(q <= 2.0) q=2; + else if(q <= 5.0) q=5; + else q=10; + } + else { + if(q < 1.5) q=1; + else if(q < 3.0) q=2; + else if(q < 7.0) q=5; + else q=10; } + return q*z; } bool operator== (const Domain &domain1, const Domain &domain2) diff --git a/src/domain_p.h b/src/domain_p.h index 4f91e0a..f7bd4c5 100644 --- a/src/domain_p.h +++ b/src/domain_p.h @@ -1,6 +1,7 @@ #ifndef DOMAIN_H_ #define DOMAIN_H_ #include "qchartglobal.h" +#include "qchartaxis.h" #include #include @@ -13,6 +14,7 @@ public: virtual ~Domain(); void setRange(qreal minX, qreal maxX, qreal minY, qreal maxY); + void setRange(qreal minX, qreal maxX, qreal minY, qreal maxY, int tickXCount, int tickYCount); void setRangeX(qreal min, qreal max); void setRangeY(qreal min, qreal max); void setMinX(qreal min); @@ -45,12 +47,14 @@ signals: void rangeXChanged(qreal min, qreal max, int tickXCount); void rangeYChanged(qreal min, qreal max, int tickYCount); - public slots: - void handleAxisRangeXChanged(qreal min,qreal max); - void handleAxisRangeYChanged(qreal min,qreal max); - void handleAxisXTicksCountChanged(int tickCount); - void handleAxisYTicksCountChanged(int tickCount); + void handleAxisXChanged(qreal min,qreal max,int tickXCount,QChartAxis::LabelsSelection mode); + void handleAxisYChanged(qreal min,qreal max,int tickYCount,QChartAxis::LabelsSelection mode); + +private: + void niceNumbers(qreal &min, qreal &max, int &ticksCount); + void looseNiceNumbers(qreal &min, qreal &max, int &ticksCount); + qreal niceNumber(qreal x,bool celing); private: qreal m_minX; @@ -59,6 +63,7 @@ private: qreal m_maxY; int m_tickXCount; int m_tickYCount; + QChartAxis::LabelsSelection m_selection; }; QTCOMMERCIALCHART_END_NAMESPACE diff --git a/src/qchart.cpp b/src/qchart.cpp index da79bbe..2fba18f 100644 --- a/src/qchart.cpp +++ b/src/qchart.cpp @@ -245,14 +245,6 @@ void QChart::zoomOut() } /*! - Resets to the default view. -*/ -void QChart::zoomReset() -{ - m_presenter->zoomReset(); -} - -/*! Returns the pointer to the x axis object of the chart */ QChartAxis* QChart::axisX() const diff --git a/src/qchart.h b/src/qchart.h index c6eb87e..963a699 100644 --- a/src/qchart.h +++ b/src/qchart.h @@ -81,7 +81,6 @@ public: void zoomIn(); void zoomIn(const QRectF& rect); void zoomOut(); - void zoomReset(); void scrollLeft(); void scrollRight(); void scrollUp(); diff --git a/src/qchartview.cpp b/src/qchartview.cpp index 75302a4..d533d38 100644 --- a/src/qchartview.cpp +++ b/src/qchartview.cpp @@ -289,8 +289,10 @@ void QChartView::mouseReleaseEvent(QMouseEvent *event) event->accept(); } - if(event->button()==Qt::RightButton) - m_chart->zoomReset(); + if(event->button()==Qt::RightButton){ + m_chart->zoomOut(); + event->accept(); + } } else { QGraphicsView::mouseReleaseEvent(event);