1 | // Copyright (C) 2023 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
3 | |
4 | #include <QtGraphsWidgets/q3dsurfacewidgetitem.h> |
5 | #include <private/q3dsurfacewidgetitem_p.h> |
6 | #include <private/qquickgraphssurface_p.h> |
7 | |
8 | QT_BEGIN_NAMESPACE |
9 | |
10 | /*! |
11 | * \class Q3DSurfaceWidgetItem |
12 | * \inmodule QtGraphsWidgets |
13 | * \ingroup graphs_3D_widgets |
14 | * \brief The Q3DSurfaceWidgetItem class provides methods for rendering 3D surface plots. |
15 | * |
16 | * This class enables developers to render 3D surface plots and to view them by |
17 | * rotating the scene freely. The visual properties of the surface such as draw |
18 | * mode and shading can be controlled via QSurface3DSeries. |
19 | * |
20 | * The Q3DSurfaceWidgetItem supports selection by showing a highlighted ball on the data |
21 | * point where the user has clicked with the left mouse button (when the default |
22 | * input handler is in use) or selected via QSurface3DSeries. The selection pointer |
23 | * is accompanied by a label, which in the default case shows the value of the data |
24 | * point and the coordinates of the point. |
25 | * |
26 | * The value range and the label format shown on the axis can be controlled |
27 | * through QValue3DAxis. |
28 | * |
29 | * To rotate the graph, hold down the right mouse button and move the mouse. |
30 | * Zooming is done using the mouse wheel. Both actions assume the default input |
31 | * handler is in use. |
32 | * |
33 | * If no axes are set explicitly for Q3DSurfaceWidgetItem, temporary default axes with no |
34 | * labels are created. These default axes can be modified via axis accessors, |
35 | * but as soon as any axis is set explicitly for the orientation, the default |
36 | * axis for that orientation is destroyed. |
37 | * |
38 | * \section1 How to construct a minimal Q3DSurfaceWidgetItem graph |
39 | * |
40 | * First, construct Q3DSurfaceWidgetItem. Since we are running the graph as a top-level |
41 | * window in this example, we need to clear the \c Qt::FramelessWindowHint flag, |
42 | * which is set by default: |
43 | * |
44 | * \snippet doc_src_q3dsurface_construction.cpp 0 |
45 | * |
46 | * Now Q3DSurfaceWidgetItem is ready to receive data to be rendered. Create data elements |
47 | * to receive values: |
48 | * |
49 | * \note In the new proxy-series relationship, data is held in series. |
50 | * Therefore, for the proxy to be able to add, delete, or edit the data, it is |
51 | * a prerequisite to create a series first. |
52 | * |
53 | * \snippet doc_src_q3dsurface_construction.cpp 1 |
54 | * |
55 | * First feed the data to the row elements and then add their pointers to the |
56 | * data element: |
57 | * |
58 | * \snippet doc_src_q3dsurface_construction.cpp 2 |
59 | * |
60 | * Create a new series and set data to it: |
61 | * |
62 | * \snippet doc_src_q3dsurface_construction.cpp 3 |
63 | * |
64 | * Finally you will need to set it visible: |
65 | * |
66 | * \snippet doc_src_q3dsurface_construction.cpp 4 |
67 | * |
68 | * The complete code needed to create and display this graph is: |
69 | * |
70 | * \snippet doc_src_q3dsurface_construction.cpp 5 |
71 | * |
72 | * And this is what those few lines of code produce: |
73 | * |
74 | * \image q3dsurface-minimal.png |
75 | * |
76 | * The scene can be rotated, zoomed into, and a surface point can be selected to |
77 | * view its position, but no other interactions are included in this minimal code |
78 | * example. You can learn more by familiarizing yourself with the examples |
79 | * provided, like the \l{Surface Graph Gallery}. |
80 | * |
81 | * |
82 | * \sa Q3DBarsWidgetItem, Q3DScatterWidgetItem, {Qt Graphs C++ Classes for 3D} |
83 | */ |
84 | |
85 | /*! |
86 | * Constructs a new 3D surface graph with the optional \a parent. |
87 | */ |
88 | Q3DSurfaceWidgetItem::Q3DSurfaceWidgetItem(QObject *parent) |
89 | : Q3DGraphsWidgetItem(*(new Q3DSurfaceWidgetItemPrivate()), parent, QStringLiteral("Surface3D" )) |
90 | {} |
91 | |
92 | /*! |
93 | * Destroys the 3D surface graph. |
94 | */ |
95 | Q3DSurfaceWidgetItem::~Q3DSurfaceWidgetItem() {} |
96 | |
97 | /*! |
98 | * Adds the \a series to the graph. A graph can contain multiple series, but |
99 | * has only one set of axes. If the newly added series has specified a selected |
100 | * item, it will be highlighted and any existing selection will be cleared. Only |
101 | * one added series can have an active selection. |
102 | * |
103 | * \sa Q3DGraphsWidgetItem::hasSeries() |
104 | */ |
105 | void Q3DSurfaceWidgetItem::addSeries(QSurface3DSeries *series) |
106 | { |
107 | graphSurface()->addSeries(series); |
108 | } |
109 | |
110 | /*! |
111 | * Removes the \a series from the graph. |
112 | * |
113 | * \sa Q3DGraphsWidgetItem::hasSeries() |
114 | */ |
115 | void Q3DSurfaceWidgetItem::removeSeries(QSurface3DSeries *series) |
116 | { |
117 | graphSurface()->removeSeries(series); |
118 | } |
119 | |
120 | /*! |
121 | * Returns the list of series added to this graph. |
122 | * |
123 | * \sa Q3DGraphsWidgetItem::hasSeries() |
124 | */ |
125 | QList<QSurface3DSeries *> Q3DSurfaceWidgetItem::seriesList() const |
126 | { |
127 | QList<QSurface3DSeries *> surfaceSeriesList; |
128 | for (QAbstract3DSeries *abstractSeries : graphSurface()->m_seriesList) { |
129 | QSurface3DSeries *surfaceSeries = qobject_cast<QSurface3DSeries *>(object: abstractSeries); |
130 | if (surfaceSeries) |
131 | surfaceSeriesList.append(t: surfaceSeries); |
132 | } |
133 | |
134 | return surfaceSeriesList; |
135 | } |
136 | |
137 | /*! |
138 | * \property Q3DSurfaceWidgetItem::axisX |
139 | * |
140 | * \brief The active x-axis. |
141 | * |
142 | * Sets \a axis as the active x-axis. Implicitly calls addAxis() to transfer the |
143 | * ownership of the axis to this graph. |
144 | * |
145 | * If \a axis is null, a temporary default axis with no labels and an |
146 | * automatically adjusting range is created. |
147 | * |
148 | * This temporary axis is destroyed if another axis is set explicitly to the |
149 | * same orientation. |
150 | * |
151 | * \sa addAxis(), releaseAxis() |
152 | */ |
153 | void Q3DSurfaceWidgetItem::setAxisX(QValue3DAxis *axis) |
154 | { |
155 | graphSurface()->setAxisX(axis); |
156 | } |
157 | |
158 | QValue3DAxis *Q3DSurfaceWidgetItem::axisX() const |
159 | { |
160 | return static_cast<QValue3DAxis *>(graphSurface()->axisX()); |
161 | } |
162 | |
163 | /*! |
164 | * \property Q3DSurfaceWidgetItem::axisY |
165 | * |
166 | * \brief The active y-axis. |
167 | * |
168 | * Sets \a axis as the active y-axis. Implicitly calls addAxis() to transfer the |
169 | * ownership of the axis to this graph. |
170 | * |
171 | * If \a axis is null, a temporary default axis with no labels and an |
172 | * automatically adjusting range is created. |
173 | * |
174 | * This temporary axis is destroyed if another axis is set explicitly to the |
175 | * same orientation. |
176 | * |
177 | * \sa addAxis(), releaseAxis() |
178 | */ |
179 | void Q3DSurfaceWidgetItem::setAxisY(QValue3DAxis *axis) |
180 | { |
181 | graphSurface()->setAxisY(axis); |
182 | } |
183 | |
184 | QValue3DAxis *Q3DSurfaceWidgetItem::axisY() const |
185 | { |
186 | return static_cast<QValue3DAxis *>(graphSurface()->axisY()); |
187 | } |
188 | |
189 | /*! |
190 | * \property Q3DSurfaceWidgetItem::axisZ |
191 | * |
192 | * \brief The active z-axis. |
193 | * |
194 | * Sets \a axis as the active z-axis. Implicitly calls addAxis() to transfer the |
195 | * ownership of the axis to this graph. |
196 | * |
197 | * If \a axis is null, a temporary default axis with no labels and an |
198 | * automatically adjusting range is created. |
199 | * |
200 | * This temporary axis is destroyed if another axis is set explicitly to the |
201 | * same orientation. |
202 | * |
203 | * \sa addAxis(), releaseAxis() |
204 | */ |
205 | void Q3DSurfaceWidgetItem::setAxisZ(QValue3DAxis *axis) |
206 | { |
207 | graphSurface()->setAxisZ(axis); |
208 | } |
209 | |
210 | QValue3DAxis *Q3DSurfaceWidgetItem::axisZ() const |
211 | { |
212 | return static_cast<QValue3DAxis *>(graphSurface()->axisZ()); |
213 | } |
214 | |
215 | /*! |
216 | * \property Q3DSurfaceWidgetItem::selectedSeries |
217 | * \readonly |
218 | * |
219 | * \brief The selected series or null. |
220 | * |
221 | * If selectionMode has \c MultiSeries set, this |
222 | * property holds the series which owns the selected point. |
223 | */ |
224 | QSurface3DSeries *Q3DSurfaceWidgetItem::selectedSeries() const |
225 | { |
226 | return graphSurface()->selectedSeries(); |
227 | } |
228 | |
229 | /*! |
230 | * \property Q3DSurfaceWidgetItem::flipHorizontalGrid |
231 | * |
232 | * \brief Whether the horizontal axis grid is displayed on top of the graph |
233 | * rather than on the bottom. |
234 | * |
235 | * In some use cases, the horizontal axis grid is mostly covered by the surface, |
236 | * so it can be more useful to display the horizontal axis grid on top of the |
237 | * graph rather than on the bottom. A typical use case for this is showing 2D |
238 | * spectrograms using orthographic projection with a top-down viewpoint. |
239 | * |
240 | * If \c{false}, the horizontal axis grid and labels are drawn on the horizontal |
241 | * background of the graph. If \c{true}, the horizontal axis grid and labels are |
242 | * drawn on the opposite side of the graph from the horizontal background. |
243 | * Defaults to \c{false}. |
244 | */ |
245 | void Q3DSurfaceWidgetItem::setFlipHorizontalGrid(bool flip) |
246 | { |
247 | graphSurface()->setFlipHorizontalGrid(flip); |
248 | } |
249 | |
250 | bool Q3DSurfaceWidgetItem::flipHorizontalGrid() const |
251 | { |
252 | return graphSurface()->flipHorizontalGrid(); |
253 | } |
254 | |
255 | bool Q3DSurfaceWidgetItem::event(QEvent *event) |
256 | { |
257 | return Q3DGraphsWidgetItem::event(event); |
258 | } |
259 | |
260 | /*! |
261 | * Adds \a axis to the graph. The axes added via addAxis are not yet taken to |
262 | * use, addAxis is simply used to give the ownership of the \a axis to the |
263 | * graph. The \a axis must not be null or added to another graph. |
264 | * |
265 | * \sa releaseAxis(), setAxisX(), setAxisY(), setAxisZ() |
266 | */ |
267 | void Q3DSurfaceWidgetItem::addAxis(QValue3DAxis *axis) |
268 | { |
269 | return graphSurface()->addAxis(axis); |
270 | } |
271 | |
272 | /*! |
273 | * Releases the ownership of the \a axis back to the caller, if it is added to |
274 | * this graph. If the released \a axis is in use, a new default axis will be |
275 | * created and set active. |
276 | * |
277 | * If the default axis is released and added back later, it behaves as any other |
278 | * axis would. |
279 | * |
280 | * \sa addAxis(), setAxisX(), setAxisY(), setAxisZ() |
281 | */ |
282 | void Q3DSurfaceWidgetItem::releaseAxis(QValue3DAxis *axis) |
283 | { |
284 | return graphSurface()->releaseAxis(axis); |
285 | } |
286 | |
287 | /*! |
288 | * Returns the list of all added axes. |
289 | * |
290 | * \sa addAxis() |
291 | */ |
292 | QList<QValue3DAxis *> Q3DSurfaceWidgetItem::axes() const |
293 | { |
294 | QList<QAbstract3DAxis *> abstractAxes = graphSurface()->axes(); |
295 | QList<QValue3DAxis *> retList; |
296 | for (QAbstract3DAxis *axis : abstractAxes) |
297 | retList.append(t: static_cast<QValue3DAxis *>(axis)); |
298 | |
299 | return retList; |
300 | } |
301 | |
302 | /*! |
303 | * \internal |
304 | */ |
305 | QQuickGraphsSurface *Q3DSurfaceWidgetItem::graphSurface() |
306 | { |
307 | Q_D(Q3DSurfaceWidgetItem); |
308 | return static_cast<QQuickGraphsSurface *>(d->m_graphsItem.get()); |
309 | } |
310 | |
311 | /*! |
312 | * \internal |
313 | */ |
314 | const QQuickGraphsSurface *Q3DSurfaceWidgetItem::graphSurface() const |
315 | { |
316 | Q_D(const Q3DSurfaceWidgetItem); |
317 | return static_cast<const QQuickGraphsSurface *>(d->m_graphsItem.get()); |
318 | } |
319 | |
320 | QT_END_NAMESPACE |
321 | |