1 /****************************************************************************
2 **
3 ** Copyright (C) 2018 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtLocation module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL3$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or later as published by the Free
28 ** Software Foundation and appearing in the file LICENSE.GPL included in
29 ** the packaging of this file. Please review the following information to
30 ** ensure the GNU General Public License version 2.0 requirements will be
31 ** met: http://www.gnu.org/licenses/gpl-2.0.html.
32 **
33 ** $QT_END_LICENSE$
34 **
35 ****************************************************************************/
36
37 #include "qmapcircleobjectqsg_p_p.h"
38
39 QT_BEGIN_NAMESPACE
40
41 static const int CircleSamples = 128;
42
QMapCircleObjectPrivateQSG(QGeoMapObject * q)43 QMapCircleObjectPrivateQSG::QMapCircleObjectPrivateQSG(QGeoMapObject *q)
44 : QMapCircleObjectPrivateDefault(q), m_dataCPU(new CircleDataCPU)
45 {
46
47 }
48
QMapCircleObjectPrivateQSG(const QMapCircleObjectPrivate & other)49 QMapCircleObjectPrivateQSG::QMapCircleObjectPrivateQSG(const QMapCircleObjectPrivate &other)
50 : QMapCircleObjectPrivateDefault(other), m_dataCPU(new CircleDataCPU)
51 {
52 // Data already cloned by the *Default copy constructor, but necessary
53 // update operations triggered only by setters overrides
54 if (!QDeclarativeCircleMapItemPrivateCPU::crossEarthPole(center(), radius()))
55 switchToGL(); // this marks source dirty
56
57 updateGeometry();
58 if (m_map)
59 emit m_map->sgNodeChanged();
60 }
61
~QMapCircleObjectPrivateQSG()62 QMapCircleObjectPrivateQSG::~QMapCircleObjectPrivateQSG()
63 {
64 if (m_map)
65 m_map->removeMapObject(q);
66 }
67
updateGeometry()68 void QMapCircleObjectPrivateQSG::updateGeometry()
69 {
70 if (!m_dataGL.isNull())
71 updateGeometryGL();
72 else
73 updateGeometryCPU();
74 }
75
updateCirclePath(const QGeoCoordinate & center,qreal radius,const QGeoProjectionWebMercator & p)76 void QMapCircleObjectPrivateQSG::CircleDataCPU::updateCirclePath(const QGeoCoordinate ¢er, qreal radius, const QGeoProjectionWebMercator &p)
77 {
78 QList<QGeoCoordinate> path;
79 QDeclarativeCircleMapItemPrivateCPU::calculatePeripheralPoints(path, center, radius, CircleSamples, m_leftBound);
80 m_circlePath.clear();
81 for (const QGeoCoordinate &c : path)
82 m_circlePath << p.geoToMapProjection(c);
83 }
84
updateGeometryCPU()85 void QMapCircleObjectPrivateQSG::updateGeometryCPU()
86 {
87 if (!m_map || m_map->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator
88 || !qIsFinite(radius()) || !center().isValid())
89 return;
90
91 const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_map->geoProjection());
92 QScopedValueRollback<bool> rollback(m_dataCPU->m_updatingGeometry);
93 m_dataCPU->m_updatingGeometry = true;
94
95 m_dataCPU->updateCirclePath(center(), radius(), p);
96 QList<QDoubleVector2D> circlePath = m_dataCPU->m_circlePath;
97
98 int pathCount = circlePath.size();
99 bool preserve = QDeclarativeCircleMapItemPrivateCPU::preserveCircleGeometry(circlePath, center(), radius(), p);
100 // using leftBound_ instead of the analytically calculated circle_.boundingGeoRectangle().topLeft());
101 // to fix QTBUG-62154
102 m_dataCPU->m_geometry.markSourceDirty();
103 m_dataCPU->m_geometry.setPreserveGeometry(true, m_dataCPU->m_leftBound); // to set the geoLeftBound_
104 m_dataCPU->m_geometry.setPreserveGeometry(preserve, m_dataCPU->m_leftBound);
105
106 bool invertedCircle = false;
107 if (QDeclarativeCircleMapItemPrivateCPU::crossEarthPole(center(), radius()) && circlePath.size() == pathCount) {
108 m_dataCPU->m_geometry.updateScreenPointsInvert(circlePath, *m_map); // invert fill area for really huge circles
109 invertedCircle = true;
110 } else {
111 m_dataCPU->m_geometry.updateSourcePoints(*m_map, circlePath);
112 m_dataCPU->m_geometry.updateScreenPoints(*m_map);
113 }
114
115 m_dataCPU->m_borderGeometry.clear();
116
117 //if (borderColor() != Qt::transparent && borderWidth() > 0)
118 {
119 QList<QDoubleVector2D> closedPath = circlePath;
120 closedPath << closedPath.first();
121
122 if (invertedCircle) {
123 closedPath = m_dataCPU->m_circlePath;
124 closedPath << closedPath.first();
125 std::reverse(closedPath.begin(), closedPath.end());
126 }
127
128 m_dataCPU->m_borderGeometry.markSourceDirty();
129 m_dataCPU->m_borderGeometry.setPreserveGeometry(true, m_dataCPU->m_leftBound);
130 m_dataCPU->m_borderGeometry.setPreserveGeometry(preserve, m_dataCPU->m_leftBound);
131
132 // Use srcOrigin_ from fill geometry after clipping to ensure that translateToCommonOrigin won't fail.
133 const QGeoCoordinate &geometryOrigin = m_dataCPU->m_geometry.origin();
134
135 m_dataCPU->m_borderGeometry.clearSource();
136
137 QDoubleVector2D borderLeftBoundWrapped;
138 QList<QList<QDoubleVector2D > > clippedPaths =
139 m_dataCPU->m_borderGeometry.clipPath(*m_map, closedPath, borderLeftBoundWrapped);
140 if (clippedPaths.size()) {
141 borderLeftBoundWrapped = p.geoToWrappedMapProjection(geometryOrigin);
142 m_dataCPU->m_borderGeometry.pathToScreen(*m_map, clippedPaths, borderLeftBoundWrapped);
143 m_dataCPU->m_borderGeometry.updateScreenPoints(*m_map, borderWidth(), false);
144 } else {
145 m_dataCPU->m_borderGeometry.clear();
146 }
147 }
148
149 QPointF origin = m_map->geoProjection().coordinateToItemPosition(m_dataCPU->m_geometry.origin(), false).toPointF();
150 m_dataCPU->m_geometry.translate(origin - m_dataCPU->m_geometry.firstPointOffset());
151 m_dataCPU->m_borderGeometry.translate(origin - m_dataCPU->m_borderGeometry.firstPointOffset());
152 }
153
updateCirclePath(const QGeoCoordinate & center,qreal radius,const QGeoProjectionWebMercator & p)154 void QMapCircleObjectPrivateQSG::CircleDataGL::updateCirclePath(const QGeoCoordinate ¢er, qreal radius, const QGeoProjectionWebMercator &p)
155 {
156 m_circlePath.clear();
157 if (radius < 0.001) // 1mm is small enough, probably already way too small.
158 return;
159 QDeclarativeCircleMapItemPrivate::calculatePeripheralPoints(m_circlePath,
160 center,
161 radius,
162 CircleSamples,
163 m_leftBound);
164
165 m_leftBoundMercator = p.geoToMapProjection(m_leftBound);
166 m_geometry.setPreserveGeometry(true, m_leftBound);
167 m_borderGeometry.setPreserveGeometry(true, m_leftBound);
168 }
169
updateGeometryGL()170 void QMapCircleObjectPrivateQSG::updateGeometryGL()
171 {
172 if (!m_map || m_map->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator)
173 return;
174
175 const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_map->geoProjection());
176 if (m_dataGL->m_geometry.isSourceDirty()
177 || m_dataGL->m_borderGeometry.isSourceDirty()) {
178 m_dataGL->updateCirclePath(center(), radius(), p);
179
180 if (m_dataGL->m_circlePath.length() == 0) { // Possibly cleared
181 m_dataGL->m_geometry.clear();
182 m_dataGL->m_borderGeometry.clear();
183 return;
184 }
185 m_dataGL->m_geometry.m_dataChanged = m_dataGL->m_borderGeometry.m_dataChanged = true;
186 m_dataGL->m_geometry.updateSourcePoints(*m_map, m_dataGL->m_circlePath);
187 m_dataGL->m_borderGeometry.updateSourcePoints(*m_map, QGeoCircle(center(), radius()));
188 m_dataGL->m_circlePath.clear(); // not needed anymore
189 }
190 m_dataGL->m_geometry.markScreenDirty(); // ToDo: this needs refactor. It's useless, remove screenDirty_ altogether.
191 m_dataGL->m_borderGeometry.markScreenDirty();
192 m_dataGL->m_borderGeometry.m_wrapOffset = m_dataGL->m_geometry.m_wrapOffset = p.projectionWrapFactor(m_dataGL->m_leftBoundMercator) + 1;
193 }
194
clone()195 QGeoMapObjectPrivate *QMapCircleObjectPrivateQSG::clone()
196 {
197 return new QMapCircleObjectPrivateQSG(static_cast<QMapCircleObjectPrivate &>(*this));
198 }
199
switchToGL()200 void QMapCircleObjectPrivateQSG::switchToGL()
201 {
202 if (!m_dataGL.isNull())
203 return;
204 QScopedPointer<CircleDataGL> data(new CircleDataGL);
205 m_dataGL.swap(data);
206 m_dataGL->markSourceDirty();
207 m_dataCPU.reset(nullptr);
208 }
209
switchToCPU()210 void QMapCircleObjectPrivateQSG::switchToCPU()
211 {
212 if (!m_dataCPU.isNull())
213 return;
214 QScopedPointer<CircleDataCPU> data(new CircleDataCPU);
215 m_dataCPU.swap(data);
216 m_dataGL.reset(nullptr);
217 }
218
updateMapObjectNode(QSGNode * oldNode,VisibleNode ** visibleNode,QSGNode * root,QQuickWindow * window)219 QSGNode *QMapCircleObjectPrivateQSG::updateMapObjectNode(QSGNode *oldNode,
220 VisibleNode **visibleNode,
221 QSGNode *root,
222 QQuickWindow * window)
223 {
224 if (!m_dataGL.isNull())
225 return updateMapObjectNodeGL(oldNode, visibleNode, root, window);
226 else
227 return updateMapObjectNodeCPU(oldNode, visibleNode, root, window);
228 }
229
updateMapObjectNodeCPU(QSGNode * oldNode,VisibleNode ** visibleNode,QSGNode * root,QQuickWindow *)230 QSGNode *QMapCircleObjectPrivateQSG::updateMapObjectNodeCPU(QSGNode *oldNode,
231 VisibleNode **visibleNode,
232 QSGNode *root,
233 QQuickWindow */*window*/)
234 {
235 if (!m_dataCPU->m_node || !oldNode) {
236 m_dataCPU->m_node = new MapPolygonNode();
237 *visibleNode = static_cast<VisibleNode *>(m_dataCPU->m_node);
238 if (oldNode)
239 delete oldNode;
240 } else {
241 m_dataCPU->m_node = static_cast<MapPolygonNode *>(oldNode);
242 }
243
244 if (!m_dataCPU->m_geometry.size() && !m_dataCPU->m_borderGeometry.size()) {
245 visibleNode = nullptr;
246 return nullptr;
247 }
248
249 //TODO: update only material
250 if (m_dataCPU->m_geometry.isScreenDirty() || m_dataCPU->m_borderGeometry.isScreenDirty() || oldNode != m_dataCPU->m_node) {
251 //QMapPolygonObject *p = static_cast<QMapPolygonObject *>(q);
252 m_dataCPU->m_node->update(color(), borderColor(), &m_dataCPU->m_geometry, &m_dataCPU->m_borderGeometry);
253 m_dataCPU->m_geometry.setPreserveGeometry(false);
254 m_dataCPU->m_borderGeometry.setPreserveGeometry(false);
255 m_dataCPU->m_geometry.markClean();
256 m_dataCPU->m_borderGeometry.markClean();
257 }
258
259 if (m_dataCPU->m_geometry.size() || m_dataCPU->m_borderGeometry.size()) {
260 root->appendChildNode(m_dataCPU->m_node);
261 } else {
262 delete m_dataCPU->m_node;
263 m_dataCPU->m_node = nullptr;
264 visibleNode = nullptr;
265 return nullptr;
266 }
267 return m_dataCPU->m_node;
268 }
269
updateMapObjectNodeGL(QSGNode * oldNode,VisibleNode ** visibleNode,QSGNode * root,QQuickWindow *)270 QSGNode *QMapCircleObjectPrivateQSG::updateMapObjectNodeGL(QSGNode *oldNode,
271 VisibleNode **visibleNode,
272 QSGNode *root,
273 QQuickWindow */*window*/)
274 {
275 if (!m_dataGL->m_rootNode || !oldNode) {
276 m_dataGL->m_rootNode = new QDeclarativePolygonMapItemPrivateOpenGL::RootNode();
277 m_dataGL->m_node = new MapPolygonNodeGL();
278 m_dataGL->m_rootNode->appendChildNode(m_dataGL->m_node);
279 m_dataGL->m_polylinenode = new MapPolylineNodeOpenGLExtruded();
280 m_dataGL->m_rootNode->appendChildNode(m_dataGL->m_polylinenode);
281 m_dataGL->m_rootNode->markDirty(QSGNode::DirtyNodeAdded);
282 *visibleNode = static_cast<VisibleNode *>(m_dataGL->m_rootNode);
283 if (oldNode)
284 delete oldNode;
285 } else {
286 m_dataGL->m_rootNode = static_cast<QDeclarativePolygonMapItemPrivateOpenGL::RootNode *>(oldNode);
287 }
288
289 const QMatrix4x4 &combinedMatrix = m_map->geoProjection().qsgTransform();
290 const QDoubleVector3D &cameraCenter = m_map->geoProjection().centerMercator();
291
292 if (m_dataGL->m_borderGeometry.isScreenDirty()) {
293 /* Do the border update first */
294 m_dataGL->m_polylinenode->update(borderColor(),
295 float(borderWidth()),
296 &m_dataGL->m_borderGeometry,
297 combinedMatrix,
298 cameraCenter,
299 Qt::SquareCap,
300 true); // No LOD for circles ATM
301 m_dataGL->m_borderGeometry.setPreserveGeometry(false);
302 m_dataGL->m_borderGeometry.markClean();
303 }
304 if (m_dataGL->m_geometry.isScreenDirty()) {
305 m_dataGL->m_node->update(color(),
306 &m_dataGL->m_geometry,
307 combinedMatrix,
308 cameraCenter);
309 m_dataGL->m_geometry.setPreserveGeometry(false);
310 m_dataGL->m_geometry.markClean();
311 }
312
313 if (!m_dataGL->m_polylinenode->isSubtreeBlocked() || !m_dataGL->m_node->isSubtreeBlocked()) {
314 m_dataGL->m_rootNode->setSubtreeBlocked(false);
315 root->appendChildNode(m_dataGL->m_rootNode);
316 return m_dataGL->m_rootNode;
317 } else {
318 delete m_dataGL->m_rootNode;
319 m_dataGL->m_rootNode = nullptr;
320 m_dataGL->m_node = nullptr;
321 m_dataGL->m_polylinenode = nullptr;
322 *visibleNode = nullptr;
323 return nullptr;
324 }
325 }
326
setCenter(const QGeoCoordinate & center)327 void QMapCircleObjectPrivateQSG::setCenter(const QGeoCoordinate ¢er)
328 {
329 QMapCircleObjectPrivateDefault::setCenter(center);
330 if (!QDeclarativeCircleMapItemPrivate::crossEarthPole(this->center(), this->radius())) // Switching implementation for circles crossing/not crossing poles
331 switchToGL();
332 else
333 switchToCPU();
334
335 if (!m_dataGL.isNull())
336 m_dataGL->markSourceDirty();
337
338 updateGeometry();
339 if (m_map)
340 emit m_map->sgNodeChanged();
341 }
342
setRadius(qreal radius)343 void QMapCircleObjectPrivateQSG::setRadius(qreal radius)
344 {
345 QMapCircleObjectPrivateDefault::setRadius(radius);
346 if (!QDeclarativeCircleMapItemPrivate::crossEarthPole(this->center(), this->radius())) // Switching implementation for circles crossing/not crossing poles
347 switchToGL();
348 else
349 switchToCPU();
350
351 if (!m_dataGL.isNull())
352 m_dataGL->markSourceDirty();
353
354 updateGeometry();
355 if (m_map)
356 emit m_map->sgNodeChanged();
357 }
358
setColor(const QColor & color)359 void QMapCircleObjectPrivateQSG::setColor(const QColor &color)
360 {
361 QMapCircleObjectPrivateDefault::setColor(color);
362 if (!m_dataCPU.isNull())
363 updateGeometry();
364 if (m_map)
365 emit m_map->sgNodeChanged();
366 }
367
setBorderColor(const QColor & color)368 void QMapCircleObjectPrivateQSG::setBorderColor(const QColor &color)
369 {
370 QMapCircleObjectPrivateDefault::setBorderColor(color);
371 if (!m_dataCPU.isNull())
372 updateGeometry();
373 if (m_map)
374 emit m_map->sgNodeChanged();
375 }
376
setBorderWidth(qreal width)377 void QMapCircleObjectPrivateQSG::setBorderWidth(qreal width)
378 {
379 QMapCircleObjectPrivateDefault::setBorderWidth(width);
380 if (!m_dataCPU.isNull())
381 updateGeometry();
382 if (m_map)
383 emit m_map->sgNodeChanged();
384 }
385
386 QT_END_NAMESPACE
387