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 &center, 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 &center, 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 &center)
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