File indexing completed on 2024-04-28 15:29:29

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()
0043 {
0044     delete d;
0045 }
0046 
0047 bool KPlotAxis::isVisible() const
0048 {
0049     return d->m_visible;
0050 }
0051 
0052 void KPlotAxis::setVisible(bool visible)
0053 {
0054     d->m_visible = visible;
0055 }
0056 
0057 bool KPlotAxis::areTickLabelsShown() const
0058 {
0059     return d->m_showTickLabels;
0060 }
0061 
0062 void KPlotAxis::setTickLabelsShown(bool b)
0063 {
0064     d->m_showTickLabels = b;
0065 }
0066 
0067 void KPlotAxis::setLabel(const QString &label)
0068 {
0069     d->m_label = label;
0070 }
0071 
0072 QString KPlotAxis::label() const
0073 {
0074     return d->m_label;
0075 }
0076 
0077 void KPlotAxis::setTickLabelFormat(char format, int fieldWidth, int precision)
0078 {
0079     d->m_labelFieldWidth = fieldWidth;
0080     d->m_labelFmt = format;
0081     d->m_labelPrec = precision;
0082 }
0083 
0084 int KPlotAxis::tickLabelWidth() const
0085 {
0086     return d->m_labelFieldWidth;
0087 }
0088 
0089 char KPlotAxis::tickLabelFormat() const
0090 {
0091     return d->m_labelFmt;
0092 }
0093 
0094 int KPlotAxis::tickLabelPrecision() const
0095 {
0096     return d->m_labelPrec;
0097 }
0098 
0099 void KPlotAxis::setTickMarks(double x0, double length)
0100 {
0101     d->m_MajorTickMarks.clear();
0102     d->m_MinorTickMarks.clear();
0103 
0104     // s is the power-of-ten factor of length:
0105     // length = t * s; s = 10^(pwr).  e.g., length=350.0 then t=3.5, s = 100.0; pwr = 2.0
0106     double pwr = 0.0;
0107     modf(log10(length), &pwr);
0108     double s = pow(10.0, pwr);
0109     double t = length / s;
0110 
0111     double TickDistance = 0.0; // The distance between major tickmarks
0112     int NumMajorTicks = 0; // will be between 3 and 5
0113     int NumMinorTicks = 0; // The number of minor ticks between major ticks (will be 4 or 5)
0114 
0115     // adjust s and t such that t is between 3 and 5:
0116     if (t < 3.0) {
0117         t *= 10.0;
0118         s /= 10.0;
0119         // t is now between 3 and 30
0120     }
0121 
0122     if (t < 6.0) { // accept current values
0123         TickDistance = s;
0124         NumMajorTicks = int(t);
0125         NumMinorTicks = 5;
0126     } else if (t < 10.0) { // adjust by a factor of 2
0127         TickDistance = s * 2.0;
0128         NumMajorTicks = int(t / 2.0);
0129         NumMinorTicks = 4;
0130     } else if (t < 20.0) { // adjust by a factor of 4
0131         TickDistance = s * 4.0;
0132         NumMajorTicks = int(t / 4.0);
0133         NumMinorTicks = 4;
0134     } else { // adjust by a factor of 5
0135         TickDistance = s * 5.0;
0136         NumMajorTicks = int(t / 5.0);
0137         NumMinorTicks = 5;
0138     }
0139 
0140     // We have determined the number of tickmarks and their separation
0141     // Now we determine their positions in the Data space.
0142 
0143     // Tick0 is the position of a "virtual" tickmark; the first major tickmark
0144     // position beyond the "minimum" edge of the data range.
0145     double Tick0 = x0 - fmod(x0, TickDistance);
0146     if (x0 < 0.0) {
0147         Tick0 -= TickDistance;
0148         NumMajorTicks++;
0149     }
0150 
0151     for (int i = 0; i < NumMajorTicks + 2; i++) {
0152         double xmaj = Tick0 + i * TickDistance;
0153         if (xmaj >= x0 && xmaj <= x0 + length) {
0154             d->m_MajorTickMarks.append(xmaj);
0155         }
0156 
0157         for (int j = 1; j < NumMinorTicks; j++) {
0158             double xmin = xmaj + TickDistance * j / NumMinorTicks;
0159             if (xmin >= x0 && xmin <= x0 + length) {
0160                 d->m_MinorTickMarks.append(xmin);
0161             }
0162         }
0163     }
0164 }
0165 
0166 QString KPlotAxis::tickLabel(double val) const
0167 {
0168     if (d->m_labelFmt == 't') {
0169         while (val < 0.0) {
0170             val += 24.0;
0171         }
0172         while (val >= 24.0) {
0173             val -= 24.0;
0174         }
0175 
0176         int h = int(val);
0177         int m = int(60. * (val - h));
0178         return QStringLiteral("%1:%2").arg(h, 2, 10, QLatin1Char('0')).arg(m, 2, 10, QLatin1Char('0'));
0179     }
0180 
0181     return QStringLiteral("%1").arg(val, d->m_labelFieldWidth, d->m_labelFmt, d->m_labelPrec);
0182 }
0183 
0184 QList<double> KPlotAxis::majorTickMarks() const
0185 {
0186     return d->m_MajorTickMarks;
0187 }
0188 
0189 QList<double> KPlotAxis::minorTickMarks() const
0190 {
0191     return d->m_MinorTickMarks;
0192 }