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