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