diff --git a/src/GUI/axisitem.cpp b/src/GUI/axisitem.cpp index 98c5819d..375e8ffe 100644 --- a/src/GUI/axisitem.cpp +++ b/src/GUI/axisitem.cpp @@ -11,23 +11,28 @@ #define XTICKS 15 #define YTICKS 10 -struct Label { - double min; - double max; - double d; +class Ticks +{ +public: + Ticks(double min, double max, int count); + + int count() const {return ((int)((_max - _min) / _d)) + 1;} + double val(int i) const {return _min + i * _d;} + double min() const {return _min;} + double max() const {return _max;} + +private: + double _min; + double _max; + double _d; }; -static struct Label label(double min, double max, int ticks) +Ticks::Ticks(double min, double max, int count) { - double range; - struct Label l; - - range = niceNum(max - min, 0); - l.d = niceNum(range / ticks, 1); - l.min = ceil(min / l.d) * l.d; - l.max = floor(max / l.d) * l.d; - - return l; + double range = niceNum(max - min, 0); + _d = niceNum(range / count, 1); + _min = ceil(min / _d) * _d; + _max = floor(max / _d) * _d; } @@ -48,6 +53,16 @@ void AxisItem::setRange(const RangeF &range) { prepareGeometryChange(); _range = range; + + QFontMetrics fm(_font); + Ticks ticks(_range.min(), _range.max(), (_type == X) ? XTICKS : YTICKS); + _ticks = QVector(ticks.count()); + for (int i = 0; i < ticks.count(); i++) { + Tick &t = _ticks[i]; + t.value = ticks.val(i); + t.boundingBox = fm.tightBoundingRect(QString::number(t.value)); + } + updateBoundingRect(); update(); } @@ -63,7 +78,9 @@ void AxisItem::setSize(qreal size) void AxisItem::setLabel(const QString& label) { prepareGeometryChange(); + QFontMetrics fm(_font); _label = label; + _labelBB = fm.tightBoundingRect(label); updateBoundingRect(); update(); } @@ -71,31 +88,18 @@ void AxisItem::setLabel(const QString& label) void AxisItem::updateBoundingRect() { QFontMetrics fm(_font); - QRect ss, es, ls; - struct Label l; - - - l = label(_range.min(), _range.max(), (_type == X) ? XTICKS : YTICKS); - es = fm.tightBoundingRect(QString::number(l.max)); - ss = fm.tightBoundingRect(QString::number(l.min)); - ls = fm.tightBoundingRect(_label); + QRect es = _ticks.isEmpty() ? QRect() : _ticks.last().boundingBox; + QRect ss = _ticks.isEmpty() ? QRect() : _ticks.first().boundingBox; + QRect ls(_labelBB); if (_type == X) { - _boundingRect = QRectF(-ss.width()/2, -TICK/2, - _size + es.width()/2 + ss.width()/2, - ls.height() + es.height() - fm.descent() + TICK + 2*PADDING + 1); + _boundingRect = QRectF(-ss.width()/2, -TICK/2, _size + es.width()/2 + + ss.width()/2, ls.height() + es.height() - fm.descent() + TICK + + 2*PADDING + 1); } else { int mtw = 0; - QRect ts; - qreal val; - - for (int i = 0; i < ((l.max - l.min) / l.d) + 1; i++) { - val = l.min + i * l.d; - QString str = QString::number(val); - ts = fm.tightBoundingRect(str); - mtw = qMax(ts.width(), mtw); - } - + for (int i = 0; i < _ticks.count(); i++) + mtw = qMax(_ticks.at(i).boundingBox.width(), mtw); _boundingRect = QRectF(-(ls.height() + mtw + 2*PADDING + TICK/2), -(_size + es.height()/2 + fm.descent()), ls.height() + mtw + 2*PADDING + TICK, _size + es.height()/2 + fm.descent() + ss.height()/2); @@ -108,8 +112,6 @@ void AxisItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, Q_UNUSED(option); Q_UNUSED(widget); QFontMetrics fm(_font); - QRect ls(fm.tightBoundingRect(_label)); - qreal range = _range.size(); QRect ts; @@ -120,40 +122,39 @@ void AxisItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, if (_type == X) { painter->drawLine(0, 0, _size, 0); - Label l = label(_range.min(), _range.max(), XTICKS); - for (int i = 0; i < ((l.max - l.min) / l.d) + 1; i++) { - qreal val = l.min + i * l.d; - QString str = QString::number(val); + for (int i = 0; i < _ticks.count(); i++) { + qreal val = _ticks.at(i).value; + ts = _ticks.at(i).boundingBox; - painter->drawLine((_size/range) * (val - _range.min()), TICK/2, - (_size/range) * (val - _range.min()), -TICK/2); - ts = fm.tightBoundingRect(str); - painter->drawText(((_size/range) * (val - _range.min())) - - (ts.width()/2), ts.height() + TICK/2 + PADDING, str); + painter->drawLine((_size/_range.size()) * (val - _range.min()), + TICK/2, (_size/_range.size()) * (val - _range.min()), -TICK/2); + painter->drawText(((_size/_range.size()) * (val - _range.min())) + - (ts.width()/2), ts.height() + TICK/2 + PADDING, + QString::number(val)); } - painter->drawText(_size/2 - ls.width()/2, ls.height() + ts.height() - - 2*fm.descent() + TICK/2 + 2*PADDING, _label); + painter->drawText(_size/2 - _labelBB.width()/2, _labelBB.height() + + ts.height() - 2*fm.descent() + TICK/2 + 2*PADDING, _label); } else { painter->drawLine(0, 0, 0, -_size); - Label l = label(_range.min(), _range.max(), YTICKS); int mtw = 0; - for (int i = 0; i < ((l.max - l.min) / l.d) + 1; i++) { - qreal val = l.min + i * l.d; - QString str = QString::number(val); - - painter->drawLine(TICK/2, -((_size/range) * (val - _range.min())), - -TICK/2, -((_size/range) * (val - _range.min()))); - ts = fm.tightBoundingRect(str); + for (int i = 0; i < _ticks.count(); i++) { + qreal val = _ticks.at(i).value; + ts = _ticks.at(i).boundingBox; mtw = qMax(ts.width(), mtw); - painter->drawText(-(ts.width() + PADDING + TICK/2), -((_size/range) - * (val - _range.min())) + (ts.height()/2), str); + + painter->drawLine(TICK/2, -((_size/_range.size()) + * (val - _range.min())), -TICK/2, -((_size/_range.size()) + * (val - _range.min()))); + painter->drawText(-(ts.width() + PADDING + TICK/2), + -((_size/_range.size()) * (val - _range.min())) + (ts.height()/2), + QString::number(val)); } painter->rotate(-90); - painter->drawText(_size/2 - ls.width()/2, -(mtw + 2*PADDING + TICK/2), - _label); + painter->drawText(_size/2 - _labelBB.width()/2, -(mtw + 2*PADDING + + TICK/2), _label); painter->rotate(90); } @@ -165,46 +166,28 @@ void AxisItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QSizeF AxisItem::margin() const { - QFont font; - font.setPixelSize(FONT_SIZE); - QFontMetrics fm(font); - QRect ss, es, ls; - struct Label l; - - - l = label(_range.min(), _range.max(), (_type == X) ? XTICKS : YTICKS); - es = fm.tightBoundingRect(QString::number(l.max)); - ss = fm.tightBoundingRect(QString::number(l.min)); - ls = fm.tightBoundingRect(_label); + QFontMetrics fm(_font); + QRect es = _ticks.isEmpty() ? QRect() : _ticks.last().boundingBox; if (_type == X) { - return QSizeF(es.width()/2, - ls.height() + es.height() - fm.descent() + TICK/2 + 2*PADDING); + return QSizeF(es.width()/2, _labelBB.height() + es.height() + - fm.descent() + TICK/2 + 2*PADDING); } else { int mtw = 0; - QRect ts; - qreal val; + for (int i = 0; i < _ticks.count(); i++) + mtw = qMax(_ticks.at(i).boundingBox.width(), mtw); - for (int i = 0; i < ((l.max - l.min) / l.d) + 1; i++) { - val = l.min + i * l.d; - QString str = QString::number(val); - ts = fm.tightBoundingRect(str); - mtw = qMax(ts.width(), mtw); - } - - return QSizeF(ls.height() -fm.descent() + mtw + 2*PADDING + return QSizeF(_labelBB.height() -fm.descent() + mtw + 2*PADDING + TICK/2, es.height()/2 + fm.descent()); } } QList AxisItem::ticks() const { - struct Label l; QList list; - l = label(_range.min(), _range.max(), (_type == X) ? XTICKS : YTICKS); - for (int i = 0; i < ((l.max - l.min) / l.d) + 1; i++) - list.append(((_size/_range.size()) * ((l.min + i * l.d) + for (int i = 0; i < _ticks.count(); i++) + list.append(((_size/_range.size()) * (_ticks.at(i).value - _range.min()))); return list; diff --git a/src/GUI/axisitem.h b/src/GUI/axisitem.h index c1f94bec..f72cbe96 100644 --- a/src/GUI/axisitem.h +++ b/src/GUI/axisitem.h @@ -2,6 +2,7 @@ #define AXISITEM_H #include +#include #include "common/range.h" class AxisItem : public QGraphicsItem @@ -23,12 +24,19 @@ public: QList ticks() const; private: + struct Tick { + double value; + QRect boundingBox; + }; + void updateBoundingRect(); Type _type; RangeF _range; qreal _size; QString _label; + QRect _labelBB; + QVector _ticks; QRectF _boundingRect; QFont _font; }; diff --git a/src/GUI/scaleitem.cpp b/src/GUI/scaleitem.cpp index 11031b6f..94f0188c 100644 --- a/src/GUI/scaleitem.cpp +++ b/src/GUI/scaleitem.cpp @@ -26,20 +26,6 @@ ScaleItem::ScaleItem(QGraphicsItem *parent) : QGraphicsItem(parent) #endif // Q_OS_MAC } -void ScaleItem::updateBoundingRect() -{ - QFontMetrics fm(_font); - QRect ss, es, us; - - ss = fm.tightBoundingRect(QString::number(0)); - es = fm.tightBoundingRect(QString::number(_length * SEGMENTS)); - us = fm.tightBoundingRect(units()); - - _boundingRect = QRectF(-ss.width()/2, 0, _width * SEGMENTS + ss.width()/2 - + qMax(us.width() + PADDING, es.width()/2) + 1, SCALE_HEIGHT + PADDING - + ss.height() + 2*fm.descent()); -} - void ScaleItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { @@ -53,13 +39,13 @@ void ScaleItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, painter->setFont(_font); painter->setPen(QPen(Qt::black, BORDER_WIDTH)); - for (int i = 0; i <= SEGMENTS; i++) { - QString label = QString::number(_length * i); - br = fm.tightBoundingRect(label); - painter->drawText(_width * i - br.width()/2, br.height() + 1, label); + for (int i = 0; i < _ticks.size(); i++) { + br = _ticks.at(i).boundingBox; + painter->drawText(_width * i - br.width()/2, br.height() + 1, + QString::number(_ticks.at(i).value)); } painter->drawText(_width * SEGMENTS + PADDING, SCALE_HEIGHT + PADDING - + br.height() + fm.descent(), units()); + + br.height() + fm.descent(), _unitsStr); painter->drawRect(QRectF(0, br.height() + PADDING, SEGMENTS * _width, SCALE_HEIGHT)); @@ -73,19 +59,6 @@ void ScaleItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, */ } -QString ScaleItem::units() const -{ - if (_units == Imperial) - return _scale ? qApp->translate("ScaleItem", "mi") - : qApp->translate("ScaleItem", "ft"); - else if (_units == Nautical) - return _scale ? qApp->translate("ScaleItem", "nmi") - : qApp->translate("ScaleItem", "ft"); - else - return _scale ? qApp->translate("ScaleItem", "km") - : qApp->translate("ScaleItem", "m"); -} - void ScaleItem::computeScale() { qreal res = _res * pow(2, -_digitalZoom); @@ -123,21 +96,50 @@ void ScaleItem::computeScale() } } +void ScaleItem::updateCache() +{ + QFontMetrics fm(_font); + + _ticks = QVector(SEGMENTS + 1); + for (int i = 0; i < _ticks.size(); i++) { + Tick &t = _ticks[i]; + t.value = _length * i; + t.boundingBox = fm.tightBoundingRect(QString::number(t.value)); + } + + if (_units == Imperial) + _unitsStr = _scale ? qApp->translate("ScaleItem", "mi") + : qApp->translate("ScaleItem", "ft"); + else if (_units == Nautical) + _unitsStr = _scale ? qApp->translate("ScaleItem", "nmi") + : qApp->translate("ScaleItem", "ft"); + else + _unitsStr = _scale ? qApp->translate("ScaleItem", "km") + : qApp->translate("ScaleItem", "m"); + _unitsBB = fm.tightBoundingRect(_unitsStr); + + QRect ss = _ticks.isEmpty() ? QRect() : _ticks.first().boundingBox; + QRect es = _ticks.isEmpty() ? QRect() : _ticks.last().boundingBox; + _boundingRect = QRectF(-ss.width()/2, 0, _width * SEGMENTS + ss.width()/2 + + qMax(_unitsBB.width() + PADDING, es.width()/2) + 1, SCALE_HEIGHT + + PADDING + ss.height() + 2*fm.descent()); +} + void ScaleItem::setResolution(qreal res) { prepareGeometryChange(); _res = res; computeScale(); - updateBoundingRect(); + updateCache(); update(); } -void ScaleItem::setUnits(enum Units units) +void ScaleItem::setUnits(Units units) { prepareGeometryChange(); _units = units; computeScale(); - updateBoundingRect(); + updateCache(); update(); } @@ -146,7 +148,7 @@ void ScaleItem::setDigitalZoom(qreal zoom) prepareGeometryChange(); _digitalZoom = zoom; computeScale(); - updateBoundingRect(); + updateCache(); update(); setScale(pow(2, -_digitalZoom)); diff --git a/src/GUI/scaleitem.h b/src/GUI/scaleitem.h index 3c3ab11d..53b27287 100644 --- a/src/GUI/scaleitem.h +++ b/src/GUI/scaleitem.h @@ -14,13 +14,17 @@ public: QWidget *widget); void setResolution(qreal res); - void setUnits(enum Units units); + void setUnits(Units units); void setDigitalZoom(qreal zoom); private: - void updateBoundingRect(); + struct Tick { + double value; + QRect boundingBox; + }; + void computeScale(); - QString units() const; + void updateCache(); qreal _res; qreal _width; @@ -30,6 +34,9 @@ private: qreal _digitalZoom; QRectF _boundingRect; QFont _font; + QVector _ticks; + QRect _unitsBB; + QString _unitsStr; }; #endif // SCALEITEM_H