12#include "KDChartCartesianAxis_p.h"
16#include <QApplication>
23#include "KDChartAbstractDiagram_p.h"
30#include "KDChartPainterSaver_p.h"
34#include <KDABLibFakes>
44 return r - std::numeric_limits<qreal>::epsilon() * 1e-6;
47 qreal diff = qAbs(r) * std::numeric_limits<qreal>::epsilon() * 2.0;
53 static const int maxPlaces = 15;
56 for (; ret > 0; ret--) {
71 , m_majorThinningFactor(majorThinningFactor)
72 , m_majorLabelCount(0)
76 const CartesianAxis::Private *axisPriv = CartesianAxis::Private::get(a);
77 XySwitch xy(axisPriv->isVertical());
82 m_dimension.end -= m_dimension.stepWidth;
85 m_annotations = axisPriv->annotations;
86 m_customTicks = axisPriv->customTicksPositions;
88 const qreal inf = std::numeric_limits<qreal>::infinity();
90 if (m_customTicks.count()) {
91 std::sort(m_customTicks.begin(), m_customTicks.end());
92 m_customTickIndex = 0;
93 m_customTick = m_customTicks.at(m_customTickIndex);
95 m_customTickIndex = -1;
99 if (m_majorThinningFactor > 1 && hasShorterLabels()) {
100 m_manualLabelTexts = m_axis->shortLabels();
102 m_manualLabelTexts = m_axis->labels();
104 m_manualLabelIndex = m_manualLabelTexts.isEmpty() ? -1 : 0;
106 if (!m_dimension.isCalculated) {
115 if (!dataHeaderLabels.
isEmpty()) {
118 if (anchorCount == dataHeaderLabels.
count()) {
119 for (
int i = 0; i < anchorCount; i++) {
121 m_dataHeaderLabels.insert(qreal(i), dataHeaderLabels.
at(i));
127 bool hasMajorTicks = m_axis->rulerAttributes().showMajorTickMarks();
128 bool hasMinorTicks = m_axis->rulerAttributes().showMinorTickMarks();
130 init(xy.isY, hasMajorTicks, hasMinorTicks, plane);
136 const auto constDiagrams = plane->
diagrams();
138 const auto *cd = qobject_cast<const AbstractCartesianDiagram *>(diagram);
142 const auto axes = cd->axes();
144 const CartesianAxis::Private *axisPriv = CartesianAxis::Private::get(axis);
145 if (axisPriv->isVertical() == isY) {
146 annotations.
unite(axisPriv->annotations);
153TickIterator::TickIterator(
bool isY,
const DataDimension &dimension,
bool useAnnotationsForTicks,
156 , m_dimension(dimension)
157 , m_majorThinningFactor(1)
158 , m_majorLabelCount(0)
159 , m_customTickIndex(-1)
160 , m_manualLabelIndex(-1)
162 , m_customTick(std::numeric_limits<qreal>::infinity())
164 if (useAnnotationsForTicks) {
167 init(isY, hasMajorTicks, hasMinorTicks, plane);
170void TickIterator::init(
bool isY,
bool hasMajorTicks,
bool hasMinorTicks,
173 Q_ASSERT(std::numeric_limits<qreal>::has_infinity);
177 hasMajorTicks = hasMajorTicks && (m_dimension.stepWidth > 0 || m_isLogarithmic);
178 hasMinorTicks = hasMinorTicks && (m_dimension.subStepWidth > 0 || m_isLogarithmic);
184 if (!m_isLogarithmic) {
198 m_decimalPlaces = -1;
201 const qreal inf = std::numeric_limits<qreal>::infinity();
205 if (m_isLogarithmic) {
206 if (ISNAN(m_dimension.start) || ISNAN(m_dimension.end)) {
209 m_dimension.start = 0.0;
210 m_dimension.end = 0.0;
214 }
else if (m_dimension.start >= 0) {
215 m_position = m_dimension.start ? pow(10.0, floor(log10(m_dimension.start)) - 1.0)
217 m_majorTick = hasMajorTicks ? m_position : inf;
218 m_minorTick = hasMinorTicks ? m_position * 20.0 : inf;
220 m_position = -pow(10.0, ceil(log10(-m_dimension.start)) + 1.0);
221 m_majorTick = hasMajorTicks ? m_position : inf;
222 m_minorTick = hasMinorTicks ? m_position * 0.09 : inf;
225 m_majorTick = hasMajorTicks ? m_dimension.start : inf;
226 m_minorTick = hasMinorTicks ? m_dimension.start : inf;
233bool TickIterator::areAlmostEqual(qreal r1, qreal r2)
const
235 if (!m_isLogarithmic) {
236 qreal span = m_dimension.end - m_dimension.start;
240 span = qFuzzyIsNull(m_dimension.start) ? 1 : qAbs(m_dimension.start);
242 return qAbs(r2 - r1) < (span) * 1e-6;
244 return qAbs(r2 - r1) < qMax(qAbs(r1), qAbs(r2)) * 0.01;
248bool TickIterator::isHigherPrecedence(qreal importantTick, qreal unimportantTick)
const
250 return importantTick != std::numeric_limits<qreal>::infinity() && (importantTick <= unimportantTick || areAlmostEqual(importantTick, unimportantTick));
253void TickIterator::computeMajorTickLabel(
int decimalPlaces)
255 if (m_manualLabelIndex >= 0) {
256 m_text = m_manualLabelTexts[m_manualLabelIndex++];
257 if (m_manualLabelIndex >= m_manualLabelTexts.count()) {
259 m_manualLabelIndex = 0;
261 m_type = m_majorThinningFactor > 1 ? MajorTickManualShort : MajorTickManualLong;
264 if (m_axis && (m_majorLabelCount++ % m_majorThinningFactor) == 0) {
268 if (it != m_dataHeaderLabels.constEnd() && areAlmostEqual(it.
key(), m_position)) {
270 m_type = MajorTickHeaderDataLabel;
273 if (decimalPlaces < 0) {
286void TickIterator::operator++()
291 const qreal inf = std::numeric_limits<qreal>::infinity();
295 if (!m_annotations.isEmpty()) {
296 auto it = m_annotations.
upperBound(m_position);
297 if (it != m_annotations.constEnd()) {
298 m_position = it.
key();
304 }
else if (!m_isLogarithmic && m_dimension.stepWidth * 1e6 < qMax(qAbs(m_dimension.start), qAbs(m_dimension.end))) {
312 if (m_isLogarithmic) {
313 while (m_majorTick <= m_position) {
314 m_majorTick *= m_position >= 0 ? 10 : 0.1;
316 while (m_minorTick <= m_position) {
318 m_minorTick += m_majorTick * (m_position >= 0 ? 0.1 : 1.0);
321 while (m_majorTick <= m_position) {
322 m_majorTick += m_dimension.stepWidth;
324 while (m_minorTick <= m_position) {
325 m_minorTick += m_dimension.subStepWidth;
329 while (m_customTickIndex >= 0 && m_customTick <= m_position) {
330 if (++m_customTickIndex >= m_customTicks.count()) {
331 m_customTickIndex = -1;
335 m_customTick = m_customTicks.at(m_customTickIndex);
339 if (isHigherPrecedence(m_customTick, m_majorTick) && isHigherPrecedence(m_customTick, m_minorTick)) {
340 m_position = m_customTick;
341 computeMajorTickLabel(-1);
344 if (m_type == MajorTick) {
347 }
else if (isHigherPrecedence(m_majorTick, m_minorTick)) {
348 m_position = m_majorTick;
349 if (m_minorTick != inf) {
351 m_minorTick = m_majorTick;
353 computeMajorTickLabel(m_decimalPlaces);
354 }
else if (m_minorTick != inf) {
355 m_position = m_minorTick;
363 if (m_position > m_dimension.end || ISNAN(m_position)) {
380 while (
d->mDiagram) {
390void CartesianAxis::init()
392 d->customTickLength = 3;
409void CartesianAxis::coordinateSystemChanged()
428 d->titleTextAttributes = a;
429 d->useDefaultTextAttributes =
false;
439 me.setValue(
me.value() * 1.5);
443 return d->titleTextAttributes;
448 d->useDefaultTextAttributes =
true;
455 return d->useDefaultTextAttributes;
460 if (
d->position == p) {
471#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) && defined(Q_COMPILER_MANGLES_RETURN_TYPE)
482 if (!
d->diagram() || !
d->diagram()->coordinatePlane()) {
494 qobject_cast<const AbstractCartesianDiagram *>(diagram);
495 if (dia && dia->referenceDiagram())
496 dia = dia->referenceDiagram();
497 return qobject_cast<const BarDiagram *>(dia) != 0;
503 qobject_cast<const AbstractCartesianDiagram *>(diagram);
504 if (dia && dia->referenceDiagram())
505 dia = dia->referenceDiagram();
506 if (qobject_cast<const BarDiagram *>(dia))
508 if (qobject_cast<const StockDiagram *>(dia))
511 const auto *lineDiagram = qobject_cast<const LineDiagram *>(dia);
512 return lineDiagram && lineDiagram->centerDataPoints();
530 if (!
d->diagram() || !
d->diagram()->coordinatePlane()) {
536 ctx.setCoordinatePlane(plane);
544 if (zoomFactor > 1.0) {
546 d->amountOfRightOverlap + 1,
d->amountOfBottomOverlap + 1));
551const TextAttributes CartesianAxis::Private::titleTextAttributesWithAdjustedRotation()
const
554 int rotation = titleTA.rotation();
555 if (position == Left || position == Right) {
558 if (rotation >= 360) {
562 rotation = (rotation / 90) * 90;
563 titleTA.setRotation(rotation);
571 QString withUnits = diagram()->unitPrefix(
int(value), orientation,
true) + text + diagram()->unitSuffix(
int(value), orientation,
true);
572 return axis()->customizedLabel(withUnits);
582 return d->axisTitleSpace;
598 const QRect &geoRect)
const
600 const TextAttributes titleTA(titleTextAttributesWithAdjustedRotation());
601 if (titleTA.isVisible()) {
605 QSize size = titleItem.sizeHint();
609 point.
setY(geoRect.
top() + (size.
height() / 2) / axisTitleSpace);
610 size.
setWidth(qMin(size.
width(), axis()->geometry().width()));
615 size.
setWidth(qMin(size.
width(), axis()->geometry().width()));
618 point.
setX(geoRect.
left() + (size.
width() / 2) / axisTitleSpace);
623 point.
setX(geoRect.
right() - (size.
width() / 2) / axisTitleSpace);
628 const PainterSaver painterSaver(painter);
632 titleItem.paint(painter);
636bool CartesianAxis::Private::isVertical()
const
638 return axis()->isAbscissa() == AbstractDiagram::Private::get(diagram())->isTransposed();
644 "Function call not allowed: The axis is not assigned to any diagram.");
648 "Bad function call: PaintContext::coordinatePlane() NOT a cartesian plane.");
652 if (!
d->diagram()->model()) {
779 painter->
setPen(
it.type() == TickIterator::MinorTick ?
rulerAttr.minorTickMarkPen()
786 if (
it.text().isEmpty() || !
labelTA.isVisible()) {
794 if (
it.type() == TickIterator::MajorTick) {
797 }
else if (
it.type() == TickIterator::MajorTickHeaderDataLabel) {
840 if (labelMargin < 0) {
848 -0.45 * size.
height() - 0.5 * (p1.
y() + p2.
y()));
852 -0.45 * size.
height() - 0.5 * (p1.
y() + p2.
y()));
856 -size.
height() - labelMargin);
880 if (
it.type() == TickIterator::MajorTick ||
it.type() == TickIterator::MajorTickHeaderDataLabel
911 d->drawTitleText(painter, plane,
geometry());
943 d->cachedMaximumSize =
QSize();
949 if (!
d->cachedMaximumSize.isValid())
950 d->cachedMaximumSize =
d->calculateMaximumSize();
951 return d->cachedMaximumSize;
954QSize CartesianAxis::Private::calculateMaximumSize()
const
964 && axis()->isAbscissa();
972 XySwitch geoXy(isVertical());
977 qreal startOverhang = 0.0;
978 qreal endOverhang = 0.0;
980 if (mAxis->textAttributes().isVisible()) {
982 qreal lowestLabelPosition = signalingNaN;
983 qreal highestLabelPosition = signalingNaN;
984 qreal lowestLabelLongitudinalSize = signalingNaN;
985 qreal highestLabelLongitudinalSize = signalingNaN;
992 for (TickIterator it(axis(), plane, 1, centerTicks); !it.isAtEnd(); ++it) {
993 const qreal drawPos = it.position() + (centerTicks ? 0.5 : 0.);
994 if (!showFirstTick) {
995 showFirstTick =
true;
999 qreal labelSizeTransverse = 0.0;
1000 qreal labelMargin = 0.0;
1004 geoXy(( qreal )1.0, drawPos)));
1005 highestLabelPosition = geoXy(labelPosition.
x(), labelPosition.
y());
1007 if (it.type() == TickIterator::MajorTick) {
1010 }
else if (it.type() == TickIterator::MajorTickHeaderDataLabel) {
1012 text = axis()->customizedLabel(text);
1014 tickLabel.setText(text);
1016 QSize sz = tickLabel.sizeHint();
1017 highestLabelLongitudinalSize = geoXy(sz.
width(), sz.
height());
1018 if (ISNAN(lowestLabelLongitudinalSize)) {
1019 lowestLabelLongitudinalSize = highestLabelLongitudinalSize;
1020 lowestLabelPosition = highestLabelPosition;
1023 labelSizeTransverse = geoXy(sz.
height(), sz.
width());
1025 if (labelMargin < 0) {
1028 labelMargin -= tickLabel.marginWidth();
1030 qreal tickLength = it.type() == TickIterator::CustomTick ? customTickLength : axis()->tickLength(it.type() == TickIterator::MinorTick);
1031 size = qMax(size, tickLength + labelMargin + labelSizeTransverse);
1038 const qreal lowestPosition = geoXy(pt.
x(), pt.
y());
1040 const qreal highestPosition = geoXy(pt.
x(), pt.
y());
1043 startOverhang = qMax(0.0, (lowestPosition - lowestLabelPosition) * geoXy(1.0, -1.0) + lowestLabelLongitudinalSize * 0.5);
1044 endOverhang = qMax(0.0, (highestLabelPosition - highestPosition) * geoXy(1.0, -1.0) + highestLabelLongitudinalSize * 0.5);
1047 amountOfLeftOverlap = geoXy(startOverhang, ( qreal )0.0);
1048 amountOfRightOverlap = geoXy(endOverhang, ( qreal )0.0);
1049 amountOfBottomOverlap = geoXy(( qreal )0.0, startOverhang);
1050 amountOfTopOverlap = geoXy(( qreal )0.0, endOverhang);
1052 const TextAttributes titleTA = titleTextAttributesWithAdjustedRotation();
1053 if (titleTA.
isVisible() && !axis()->titleText().isEmpty()) {
1058 size += geoXy(titleFM.height() * 0.33, titleFM.averageCharWidth() * 0.55);
1059 size += geoXy(title.sizeHint().height(), title.sizeHint().width());
1063 return QSize(geoXy(1,
int(size)), geoXy(
int(size), 1));
1081 if (
d->geometry != r) {
1095 if (
d->customTickLength == value) {
1098 d->customTickLength = value;
1105 return d->customTickLength;
1116 return d->annotations;
1131 return d->customTicksPositions;
static int numSignificantDecimalPlaces(qreal floatNumber)
static QMultiMap< qreal, QString > allAxisAnnotations(const AbstractCoordinatePlane *plane, bool isY)
static bool referenceDiagramIsBarDiagram(const AbstractDiagram *diagram)
static qreal slightlyLessThan(qreal r)
static bool referenceDiagramNeedsCenteredAbscissaTicks(const AbstractDiagram *diagram)
@ MeasureOrientationMinimum
QRect areaGeometry() const override
RulerAttributes rulerAttributes() const
Returns the attributes to be used for painting the rulers.
virtual const QString customizedLabel(const QString &label) const
Reimplement this method if you want to adjust axis labels before they are printed.
const AbstractDiagram * diagram() const
void coordinateSystemChanged()
bool compare(const AbstractAxis *other) const
TextAttributes textAttributes() const
Returns the text attributes to be used for axis labels.
Base class for diagrams based on a cartesian coordianate system.
Base class common for all coordinate planes, CartesianCoordinatePlane, PolarCoordinatePlane,...
virtual qreal zoomFactorY() const
AbstractDiagramList diagrams()
AbstractDiagram * diagram()
DataDimensionsList gridDimensionsList()
virtual qreal zoomFactorX() const
AbstractDiagram defines the interface for diagram classes.
virtual AttributesModel * attributesModel() const
AbstractCoordinatePlane * coordinatePlane() const
QStringList itemRowLabels() const
static const DataDimension adjustedLowerUpperRange(const DataDimension &dim, bool adjustLower, bool adjustUpper)
A proxy model used for decorating data with attributes.
int rowCount(const QModelIndex &) const override
BarDiagram defines a common bar diagram.
Q_DECL_DEPRECATED qreal titleSpace() const
virtual void setPosition(Position p)
void resetTitleTextAttributes()
QString titleText() const
bool isEmpty() const override
void setCustomTickLength(int value)
virtual bool isOrdinate() const
void setGeometry(const QRect &r) override
QSize maximumSize() const override
Q_DECL_DEPRECATED qreal titleSize() const
QList< qreal > customTicks() const
CartesianAxis(AbstractCartesianDiagram *diagram=nullptr)
int customTickLength() const
~CartesianAxis() override
void paintCtx(PaintContext *) override
void setCachedSizeDirty() const
QSize sizeHint() const override
QSize minimumSize() const override
Qt::Orientations expandingDirections() const override
virtual bool isAbscissa() const
void setTitleTextAttributes(const TextAttributes &a)
bool compare(const CartesianAxis *other) const
virtual Position position() const
QRect geometry() const override
QMultiMap< qreal, QString > annotations() const
TextAttributes titleTextAttributes() const
void setAnnotations(const QMultiMap< qreal, QString > &annotations)
void paint(QPainter *) override
void setTitleText(const QString &text)
virtual int tickLength(bool subUnitTicks=false) const
void setCustomTicks(const QList< qreal > &ticksPostions)
bool hasDefaultTitleTextAttributes() const
Q_DECL_DEPRECATED void setTitleSpace(qreal value)
Q_DECL_DEPRECATED void setTitleSize(qreal value)
use setTitleTextAttributes() instead
virtual void layoutPlanes()
Cartesian coordinate plane.
unsigned int autoAdjustVerticalRangeToData() const
Returns the maximal allowed percent of the vertical space covered by the coordinate plane that may be...
unsigned int autoAdjustHorizontalRangeToData() const
Returns the maximal allowed percent of the horizontal space covered by the coordinate plane that may ...
const QPointF translate(const QPointF &diagramPoint) const override
const GridAttributes gridAttributes(Qt::Orientation orientation) const
Helper class for one dimension of data, e.g. for the rows in a data model, or for the labels of an ax...
static QPaintDevice * paintDevice()
A set of attributes controlling the appearance of grids.
bool adjustLowerBoundToGrid() const
bool adjustUpperBoundToGrid() const
Measure is used to specify relative and absolute sizes in KDChart, e.g. font sizes.
Stores information about painting diagrams.
void setPainter(QPainter *painter)
AbstractCoordinatePlane * coordinatePlane() const
QPainter * painter() const
A set of attributes controlling the appearance of axis rulers.
bool showFirstTick() const
A set of text attributes.
qreal height() const const
const T & at(int i) const const
int count(const T &value) const const
bool isEmpty() const const
const Key key(const T &value, const Key &defaultKey) const const
QMap::iterator lowerBound(const Key &key)
QMap::iterator upperBound(const Key &key)
const T value(const Key &key, const T &defaultValue) const const
QMultiMap< K, V > & unite(const QMultiMap< K, V > &other)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
T qobject_cast(QObject *object)
void drawLine(const QLineF &line)
bool hasClipping() const const
void setClipRegion(const QRegion ®ion, Qt::ClipOperation operation)
void setClipping(bool enable)
void setPen(const QColor &color)
void translate(const QPointF &offset)
void setHeight(int height)
qreal height() const const
QSize toSize() const const
qreal width() const const
bool isEmpty() const const
QString number(int n, int base)
QString section(QChar sep, int start, int end, QString::SectionFlags flags) const const