1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qvalue3daxis_p.h"
5#include "qvalue3daxisformatter_p.h"
6#include "qgraphs3dlogging_p.h"
7
8QT_BEGIN_NAMESPACE
9
10/*!
11 * \class QValue3DAxis
12 * \inmodule QtGraphs
13 * \ingroup graphs_3D
14 * \brief The QValue3DAxis class manipulates an axis of a graph.
15 *
16 * A value axis can be given a range of values and segment and subsegment
17 * counts to divide the range into.
18 *
19 * Labels are drawn between each segment, and grid lines are drawn between each
20 * segment and each subsegment. \note If visible, there will always be at least
21 * two grid lines and labels indicating the minimum and maximum values of
22 * the range, as there is always at least one segment.
23 */
24
25/*!
26 * \qmltype Value3DAxis
27 * \inqmlmodule QtGraphs
28 * \ingroup graphs_qml_3D
29 * \nativetype QValue3DAxis
30 * \inherits Abstract3DAxis
31 * \brief Manipulates an axis of a graph.
32 *
33 * This type provides an axis that can be given a range of values and segment
34 * and subsegment counts to divide the range into.
35 */
36
37/*!
38 * \qmlproperty qsizetype Value3DAxis::segmentCount
39 *
40 * The number of segments on the axis. This indicates how many labels are drawn.
41 * The number of grid lines to be drawn is calculated with the following
42 * formula: \c {segments * subsegments + 1}. The preset default is \c 5. The
43 * value cannot be below \c 1.
44 */
45
46/*!
47 * \qmlproperty qsizetype Value3DAxis::subSegmentCount
48 *
49 * The number of subsegments inside each segment on the axis. Grid lines are
50 * drawn between each subsegment, in addition to each segment. The preset
51 * default is \c 1. The value cannot be below \c 1.
52 */
53
54/*!
55 * \qmlproperty string Value3DAxis::labelFormat
56 *
57 * The label format to be used for the labels on this axis.
58 *
59 * The format string supports the following conversion specifiers, length
60 * modifiers, and flags provided by \c printf() in the standard C++ library:
61 * d, i, o, x, X, f, F, e, E, g, G, c.
62 *
63 * If GraphsItem3D::locale is anything else than \c{"C"}, the supported
64 * specifiers are limited to: d, e, E, f, g, G, and i. Also, only the precision
65 * modifier is supported. The rest of the formatting comes from the default
66 * \l [QML] Locale of the application.
67 *
68 * \sa GraphsItem3D::locale
69 */
70
71/*!
72 * \qmlproperty Value3DAxisFormatter Value3DAxis::formatter
73 *
74 * The axis formatter to be used. Any existing formatter is deleted when a new
75 * formatter is set.
76 *
77 */
78
79/*!
80 * \qmlproperty bool Value3DAxis::reversed
81 *
82 * If \c{true}, the axis will be rendered in reverse. That is, the positions of
83 * the minimum and maximum values are swapped when the graph is rendered. This
84 * property does not affect the actual minimum and maximum values of the axis.
85 */
86
87/*!
88 \qmlsignal Value3DAxis::segmentCountChanged(qsizetype count)
89
90 This signal is emitted when segmentCount changes to \a count.
91*/
92/*!
93 \qmlsignal Value3DAxis::subSegmentCountChanged(qsizetype count)
94
95 This signal is emitted when subSegmentCount changes to \a count.
96*/
97/*!
98 \qmlsignal Value3DAxis::labelFormatChanged(string format)
99
100 This signal is emitted when labelFormat changes to \a format.
101*/
102/*!
103 \qmlsignal Value3DAxis::formatterChanged(Value3DAxisFormatter formatter)
104
105 This signal is emitted when \l formatter changes to \a formatter.
106*/
107/*!
108 \qmlsignal Value3DAxis::reversedChanged(bool enable)
109
110 This signal is emitted when \l reversed changes to \a enable.
111*/
112
113/*!
114 * Constructs QValue3DAxis with the given \a parent.
115 */
116QValue3DAxis::QValue3DAxis(QObject *parent)
117 : QAbstract3DAxis(*(new QValue3DAxisPrivate()), parent)
118{
119 setFormatter(new QValue3DAxisFormatter);
120}
121
122/*!
123 * Destroys QValue3DAxis.
124 */
125QValue3DAxis::~QValue3DAxis() {}
126
127/*!
128 * \property QValue3DAxis::segmentCount
129 *
130 * \brief The number of segments on the axis.
131 *
132 * This indicates how many labels are drawn. The number
133 * of grid lines to be drawn is calculated with formula: \c {segments *
134 * subsegments + 1}. The preset default is \c 5. The value cannot be below \c 1.
135 *
136 * \sa setSubSegmentCount()
137 */
138void QValue3DAxis::setSegmentCount(qsizetype count)
139{
140 Q_D(QValue3DAxis);
141 if (count <= 0) {
142 qCWarning(lcAProperties3D,
143 "%s illegal segment count automatically adjusted to a legal one: %" PRIdQSIZETYPE
144 " --> 1",
145 qUtf8Printable(QLatin1String(__FUNCTION__)),
146 count);
147 count = 1;
148 }
149 if (d->m_segmentCount == count) {
150 qCDebug(lcAProperties3D, "%s Segment value is already set to: %" PRIdQSIZETYPE,
151 qUtf8Printable(QLatin1String(__FUNCTION__)), count);
152 return;
153 }
154
155 d->m_segmentCount = count;
156 d->emitLabelsChanged();
157 emit segmentCountChanged(count);
158}
159
160qsizetype QValue3DAxis::segmentCount() const
161{
162 Q_D(const QValue3DAxis);
163 return d->m_segmentCount;
164}
165
166/*!
167 * \property QValue3DAxis::subSegmentCount
168 *
169 * \brief The number of subsegments inside each segment on the axis.
170 *
171 * Grid lines are drawn between
172 * each subsegment, in addition to each segment.
173 * The preset default is \c 1. The value cannot be below \c 1.
174 *
175 * \sa setSegmentCount()
176 */
177void QValue3DAxis::setSubSegmentCount(qsizetype count)
178{
179 Q_D(QValue3DAxis);
180 if (count <= 0) {
181 qCWarning(lcAProperties3D,
182 "%s illegal subsegment count automatically adjusted to a legal one: "
183 "%" PRIdQSIZETYPE " -> 1",
184 qUtf8Printable(QLatin1String(__FUNCTION__)),
185 count);
186 count = 1;
187 }
188 if (d->m_subSegmentCount == count) {
189 qCDebug(lcAProperties3D, "%s subsegment value is already set to: %" PRIdQSIZETYPE,
190 qUtf8Printable(QLatin1String(__FUNCTION__)), count);
191 return;
192 }
193
194 d->m_subSegmentCount = count;
195 emit subSegmentCountChanged(count);
196}
197
198qsizetype QValue3DAxis::subSegmentCount() const
199{
200 Q_D(const QValue3DAxis);
201 return d->m_subSegmentCount;
202}
203
204/*!
205 * \property QValue3DAxis::labelFormat
206 *
207 * \brief The label format to be used for the labels on this axis.
208 *
209 * The format string supports the following conversion specifiers, length
210 * modifiers, and flags provided by \c printf() in the standard C++ library:
211 * d, i, o, x, X, f, F, e, E, g, G, c.
212 *
213 * If Q3DGraphsWidgetItem::locale is anything else than \c{"C"}, the supported
214 * specifiers are limited to: d, e, E, f, g, G, and i. Also, only the precision
215 * modifier is supported. The rest of the formatting comes from the default
216 * QLocale of the application.
217 *
218 * Usage example:
219 *
220 * \c {axis->setLabelFormat("%.2f mm");}
221 *
222 * \sa formatter, Q3DGraphsWidgetItem::locale
223 */
224void QValue3DAxis::setLabelFormat(const QString &format)
225{
226 Q_D(QValue3DAxis);
227 if (d->m_labelFormat == format) {
228 qCDebug(lcAProperties3D, "%s format is already: %s",
229 qUtf8Printable(QLatin1String(__FUNCTION__)), qUtf8Printable(format));
230 return;
231 }
232
233 d->m_labelFormat = format;
234 d->emitLabelsChanged();
235 emit labelFormatChanged(format);
236}
237
238QString QValue3DAxis::labelFormat() const
239{
240 Q_D(const QValue3DAxis);
241 return d->m_labelFormat;
242}
243
244/*!
245 * \property QValue3DAxis::formatter
246 *
247 * \brief The axis formatter to be used.
248 *
249 * Any existing formatter is deleted when a new formatter
250 * is set.
251 */
252void QValue3DAxis::setFormatter(QValue3DAxisFormatter *formatter)
253{
254 Q_ASSERT(formatter);
255 Q_D(QValue3DAxis);
256
257 if (formatter == d->m_formatter) {
258 qCDebug(lcAProperties3D) << __FUNCTION__
259 << "formatter is already set to:" << formatter;
260 return;
261 }
262 delete d->m_formatter;
263 d->m_formatter = formatter;
264 formatter->setParent(this);
265 formatter->setAxis(this);
266 emit formatterChanged(formatter);
267 emit formatterDirty();
268}
269
270QValue3DAxisFormatter *QValue3DAxis::formatter() const
271{
272 Q_D(const QValue3DAxis);
273 return d->m_formatter;
274}
275
276/*!
277 * \property QValue3DAxis::reversed
278 *
279 * \brief Whether the axis is rendered in reverse.
280 *
281 * If \c{true}, the axis will be rendered in reverse, which means the positions
282 * of minimum and maximum values are swapped when the graph is rendered. This
283 * property doesn't affect the actual minimum and maximum values of the axis.
284 */
285void QValue3DAxis::setReversed(bool enable)
286{
287 Q_D(QValue3DAxis);
288 if (d->m_reversed == enable) {
289 qCDebug(lcAProperties3D) << __FUNCTION__
290 << "axis Reverse value is already set to:" << enable;
291 return;
292 }
293
294 d->m_reversed = enable;
295 emit reversedChanged(enable);
296}
297
298bool QValue3DAxis::reversed() const
299{
300 Q_D(const QValue3DAxis);
301 return d->m_reversed;
302}
303
304void QValue3DAxis::recalculate()
305{
306 formatter()->d_func()->recalculate();
307}
308
309qsizetype QValue3DAxis::gridSize()
310{
311 return formatter()->gridPositions().size();
312}
313
314qsizetype QValue3DAxis::subGridSize()
315{
316 return formatter()->subGridPositions().size();
317}
318
319float QValue3DAxis::gridPositionAt(qsizetype gridLine)
320{
321 return formatter()->gridPositions().at(i: gridLine);
322}
323
324float QValue3DAxis::subGridPositionAt(qsizetype gridLine)
325{
326 return formatter()->subGridPositions().at(i: gridLine);
327}
328
329float QValue3DAxis::labelPositionAt(qsizetype index)
330{
331 return formatter()->labelPositions().at(i: index);
332}
333
334float QValue3DAxis::positionAt(float x)
335{
336 return formatter()->positionAt(value: x);
337}
338
339QString QValue3DAxis::stringForValue(float x)
340{
341 return formatter()->stringForValue(value: x, format: labelFormat());
342}
343
344QValue3DAxisPrivate::QValue3DAxisPrivate()
345 : QAbstract3DAxisPrivate(QAbstract3DAxis::AxisType::Value)
346 , m_segmentCount(5)
347 , m_subSegmentCount(1)
348 , m_labelFormat(Utils::defaultLabelFormat())
349 , m_labelsDirty(true)
350 , m_formatter(0)
351 , m_reversed(false)
352{}
353
354QValue3DAxisPrivate::~QValue3DAxisPrivate() {}
355
356void QValue3DAxisPrivate::setRange(float min, float max, bool suppressWarnings)
357{
358 bool dirty = (min != m_min || max != m_max);
359
360 QAbstract3DAxisPrivate::setRange(min, max, suppressWarnings);
361
362 if (dirty)
363 emitLabelsChanged();
364}
365
366void QValue3DAxisPrivate::setMin(float min)
367{
368 bool dirty = (min != m_min);
369
370 QAbstract3DAxisPrivate::setMin(min);
371
372 if (dirty)
373 emitLabelsChanged();
374}
375
376void QValue3DAxisPrivate::setMax(float max)
377{
378 bool dirty = (max != m_max);
379
380 QAbstract3DAxisPrivate::setMax(max);
381
382 if (dirty)
383 emitLabelsChanged();
384}
385
386void QValue3DAxisPrivate::emitLabelsChanged()
387{
388 Q_Q(QValue3DAxis);
389 m_labelsDirty = true;
390 emit q->labelsChanged();
391}
392
393void QValue3DAxisPrivate::emitFormatterDirty()
394{
395 Q_Q(QValue3DAxis);
396 m_labelsDirty = true;
397 emit q->formatterDirty();
398}
399
400void QValue3DAxisPrivate::updateLabels()
401{
402 if (!m_labelsDirty)
403 return;
404
405 m_labelsDirty = false;
406
407 m_formatter->d_func()->recalculate();
408
409 m_labels = m_formatter->labelStrings();
410}
411
412bool QValue3DAxisPrivate::allowZero()
413{
414 return m_formatter->allowZero();
415}
416
417bool QValue3DAxisPrivate::allowNegatives()
418{
419 return m_formatter->allowNegatives();
420}
421
422bool QValue3DAxisPrivate::allowMinMaxSame()
423{
424 return false;
425}
426
427QT_END_NAMESPACE
428

source code of qtgraphs/src/graphs3d/axis/qvalue3daxis.cpp