File indexing completed on 2024-04-28 03:56:42

0001 /*  -*- C++ -*-
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2005 Andreas Nicolai <Andreas.Nicolai@gmx.net>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "kplotaxis.h"
0009 
0010 #include <math.h> //for log10(), pow(), modf()
0011 
0012 class KPlotAxis::Private
0013 {
0014 public:
0015     Private(KPlotAxis *qq)
0016         : q(qq)
0017         , m_visible(true)
0018         , m_showTickLabels(false)
0019         , m_labelFmt('g')
0020         , m_labelFieldWidth(0)
0021         , m_labelPrec(-1)
0022     {
0023     }
0024 
0025     KPlotAxis *q;
0026 
0027     bool m_visible : 1; // Property "visible" defines if Axis is drawn or not.
0028     bool m_showTickLabels : 1;
0029     char m_labelFmt; // Number format for number labels, see QString::arg()
0030     QString m_label; // The label of the axis.
0031     int m_labelFieldWidth; // Field width for number labels, see QString::arg()
0032     int m_labelPrec; // Number precision for number labels, see QString::arg()
0033     QList<double> m_MajorTickMarks, m_MinorTickMarks;
0034 };
0035 
0036 KPlotAxis::KPlotAxis(const QString &label)
0037     : d(new Private(this))
0038 {
0039     d->m_label = label;
0040 }
0041 
0042 KPlotAxis::~KPlotAxis() = default;
0043 
0044 bool KPlotAxis::isVisible() const
0045 {
0046     return d->m_visible;
0047 }
0048 
0049 void KPlotAxis::setVisible(bool visible)
0050 {
0051     d->m_visible = visible;
0052 }
0053 
0054 bool KPlotAxis::areTickLabelsShown() const
0055 {
0056     return d->m_showTickLabels;
0057 }
0058 
0059 void KPlotAxis::setTickLabelsShown(bool b)
0060 {
0061     d->m_showTickLabels = b;
0062 }
0063 
0064 void KPlotAxis::setLabel(const QString &label)
0065 {
0066     d->m_label = label;
0067 }
0068 
0069 QString KPlotAxis::label() const
0070 {
0071     return d->m_label;
0072 }
0073 
0074 void KPlotAxis::setTickLabelFormat(char format, int fieldWidth, int precision)
0075 {
0076     d->m_labelFieldWidth = fieldWidth;
0077     d->m_labelFmt = format;
0078     d->m_labelPrec = precision;
0079 }
0080 
0081 int KPlotAxis::tickLabelWidth() const
0082 {
0083     return d->m_labelFieldWidth;
0084 }
0085 
0086 char KPlotAxis::tickLabelFormat() const
0087 {
0088     return d->m_labelFmt;
0089 }
0090 
0091 int KPlotAxis::tickLabelPrecision() const
0092 {
0093     return d->m_labelPrec;
0094 }
0095 
0096 void KPlotAxis::setTickMarks(double x0, double length)
0097 {
0098     d->m_MajorTickMarks.clear();
0099     d->m_MinorTickMarks.clear();
0100 
0101     // s is the power-of-ten factor of length:
0102     // length = t * s; s = 10^(pwr).  e.g., length=350.0 then t=3.5, s = 100.0; pwr = 2.0
0103     double pwr = 0.0;
0104     modf(log10(length), &pwr);
0105     double s = pow(10.0, pwr);
0106     double t = length / s;
0107 
0108     double TickDistance = 0.0; // The distance between major tickmarks
0109     int NumMajorTicks = 0; // will be between 3 and 5
0110     int NumMinorTicks = 0; // The number of minor ticks between major ticks (will be 4 or 5)
0111 
0112     // adjust s and t such that t is between 3 and 5:
0113     if (t < 3.0) {
0114         t *= 10.0;
0115         s /= 10.0;
0116         // t is now between 3 and 30
0117     }
0118 
0119     if (t < 6.0) { // accept current values
0120         TickDistance = s;
0121         NumMajorTicks = int(t);
0122         NumMinorTicks = 5;
0123     } else if (t < 10.0) { // adjust by a factor of 2
0124         TickDistance = s * 2.0;
0125         NumMajorTicks = int(t / 2.0);
0126         NumMinorTicks = 4;
0127     } else if (t < 20.0) { // adjust by a factor of 4
0128         TickDistance = s * 4.0;
0129         NumMajorTicks = int(t / 4.0);
0130         NumMinorTicks = 4;
0131     } else { // adjust by a factor of 5
0132         TickDistance = s * 5.0;
0133         NumMajorTicks = int(t / 5.0);
0134         NumMinorTicks = 5;
0135     }
0136 
0137     // We have determined the number of tickmarks and their separation
0138     // Now we determine their positions in the Data space.
0139 
0140     // Tick0 is the position of a "virtual" tickmark; the first major tickmark
0141     // position beyond the "minimum" edge of the data range.
0142     double Tick0 = x0 - fmod(x0, TickDistance);
0143     if (x0 < 0.0) {
0144         Tick0 -= TickDistance;
0145         NumMajorTicks++;
0146     }
0147 
0148     for (int i = 0; i < NumMajorTicks + 2; i++) {
0149         double xmaj = Tick0 + i * TickDistance;
0150         if (xmaj >= x0 && xmaj <= x0 + length) {
0151             d->m_MajorTickMarks.append(xmaj);
0152         }
0153 
0154         for (int j = 1; j < NumMinorTicks; j++) {
0155             double xmin = xmaj + TickDistance * j / NumMinorTicks;
0156             if (xmin >= x0 && xmin <= x0 + length) {
0157                 d->m_MinorTickMarks.append(xmin);
0158             }
0159         }
0160     }
0161 }
0162 
0163 QString KPlotAxis::tickLabel(double val) const
0164 {
0165     if (d->m_labelFmt == 't') {
0166         while (val < 0.0) {
0167             val += 24.0;
0168         }
0169         while (val >= 24.0) {
0170             val -= 24.0;
0171         }
0172 
0173         int h = int(val);
0174         int m = int(60. * (val - h));
0175         return QStringLiteral("%1:%2").arg(h, 2, 10, QLatin1Char('0')).arg(m, 2, 10, QLatin1Char('0'));
0176     }
0177 
0178     return QStringLiteral("%1").arg(val, d->m_labelFieldWidth, d->m_labelFmt, d->m_labelPrec);
0179 }
0180 
0181 QList<double> KPlotAxis::majorTickMarks() const
0182 {
0183     return d->m_MajorTickMarks;
0184 }
0185 
0186 QList<double> KPlotAxis::minorTickMarks() const
0187 {
0188     return d->m_MinorTickMarks;
0189 }