1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2020 Paolo Angelelli <paolo.angelelli@gmail.com> |
4 | ** Copyright (C) 2020 The Qt Company Ltd. |
5 | ** Contact: http://www.qt.io/licensing/ |
6 | ** |
7 | ** This file is part of the QtLocation module of the Qt Toolkit. |
8 | ** |
9 | ** $QT_BEGIN_LICENSE:LGPL3$ |
10 | ** Commercial License Usage |
11 | ** Licensees holding valid commercial Qt licenses may use this file in |
12 | ** accordance with the commercial license agreement provided with the |
13 | ** Software or, alternatively, in accordance with the terms contained in |
14 | ** a written agreement between you and The Qt Company. For licensing terms |
15 | ** and conditions see http://www.qt.io/terms-conditions. For further |
16 | ** information use the contact form at http://www.qt.io/contact-us. |
17 | ** |
18 | ** GNU Lesser General Public License Usage |
19 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
20 | ** General Public License version 3 as published by the Free Software |
21 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the |
22 | ** packaging of this file. Please review the following information to |
23 | ** ensure the GNU Lesser General Public License version 3 requirements |
24 | ** will be met: https://www.gnu.org/licenses/lgpl.html. |
25 | ** |
26 | ** GNU General Public License Usage |
27 | ** Alternatively, this file may be used under the terms of the GNU |
28 | ** General Public License version 2.0 or later as published by the Free |
29 | ** Software Foundation and appearing in the file LICENSE.GPL included in |
30 | ** the packaging of this file. Please review the following information to |
31 | ** ensure the GNU General Public License version 2.0 requirements will be |
32 | ** met: http://www.gnu.org/licenses/gpl-2.0.html. |
33 | ** |
34 | ** $QT_END_LICENSE$ |
35 | ** |
36 | ****************************************************************************/ |
37 | |
38 | #ifndef QDECLARATIVEPOLYGONMAPITEM_P_P_H |
39 | #define QDECLARATIVEPOLYGONMAPITEM_P_P_H |
40 | |
41 | // |
42 | // W A R N I N G |
43 | // ------------- |
44 | // |
45 | // This file is not part of the Qt API. It exists purely as an |
46 | // implementation detail. This header file may change from version to |
47 | // version without notice, or even be removed. |
48 | // |
49 | // We mean it. |
50 | // |
51 | |
52 | #include <QtLocation/private/qlocationglobal_p.h> |
53 | #include <QtLocation/private/qgeomapitemgeometry_p.h> |
54 | #include <QtLocation/private/qdeclarativegeomapitembase_p.h> |
55 | #include <QtLocation/private/qdeclarativepolylinemapitem_p.h> |
56 | #include <QtLocation/private/qdeclarativegeomapitemutils_p.h> |
57 | #include <QtLocation/private/qdeclarativepolygonmapitem_p.h> |
58 | #include <QtLocation/private/qdeclarativepolylinemapitem_p_p.h> |
59 | #include <QSGGeometryNode> |
60 | #include <QSGFlatColorMaterial> |
61 | #include <QtPositioning/QGeoPath> |
62 | #include <QtPositioning/QGeoRectangle> |
63 | #include <QtPositioning/QGeoPolygon> |
64 | #include <QtPositioning/private/qdoublevector2d_p.h> |
65 | #include <QSGFlatColorMaterial> |
66 | #include <QSGSimpleMaterial> |
67 | #include <QtGui/QMatrix4x4> |
68 | #include <QColor> |
69 | #include <QList> |
70 | #include <QVector> |
71 | #include <QtCore/QScopedValueRollback> |
72 | |
73 | QT_BEGIN_NAMESPACE |
74 | |
75 | class Q_LOCATION_PRIVATE_EXPORT QGeoMapPolygonGeometry : public QGeoMapItemGeometry |
76 | { |
77 | public: |
78 | QGeoMapPolygonGeometry(); |
79 | |
80 | inline void setAssumeSimple(bool value) { assumeSimple_ = value; } |
81 | |
82 | void updateSourcePoints(const QGeoMap &map, |
83 | const QList<QDoubleVector2D> &path); |
84 | |
85 | void updateScreenPoints(const QGeoMap &map, qreal strokeWidth = 0.0); |
86 | |
87 | protected: |
88 | QPainterPath srcPath_; |
89 | bool assumeSimple_; |
90 | }; |
91 | |
92 | #if QT_CONFIG(opengl) |
93 | class Q_LOCATION_PRIVATE_EXPORT QGeoMapPolygonGeometryOpenGL : public QGeoMapItemGeometry |
94 | { |
95 | public: |
96 | typedef struct { |
97 | QList<QDoubleVector2D> wrappedBboxes; |
98 | } WrappedPolygon; |
99 | QGeoMapPolygonGeometryOpenGL(); |
100 | ~QGeoMapPolygonGeometryOpenGL() override {} |
101 | |
102 | // Temporary method for compatibility in MapCircleObject. Remove when MapObjects are ported. |
103 | void updateSourcePoints(const QGeoMap &map, |
104 | const QList<QDoubleVector2D> &path); |
105 | |
106 | void updateSourcePoints(const QGeoMap &map, |
107 | const QList<QGeoCoordinate> &perimeter); |
108 | |
109 | void updateSourcePoints(const QGeoMap &map, |
110 | const QGeoPolygon &poly); |
111 | |
112 | void updateSourcePoints(const QGeoMap &map, |
113 | const QGeoRectangle &rect); |
114 | |
115 | void updateScreenPoints(const QGeoMap &map, qreal strokeWidth = 0.0, const QColor &strokeColor = Qt::transparent); |
116 | void updateQuickGeometry(const QGeoProjectionWebMercator &p, qreal strokeWidth = 0.0); |
117 | |
118 | void allocateAndFillPolygon(QSGGeometry *geom) const |
119 | { |
120 | |
121 | |
122 | const QVector<QDeclarativeGeoMapItemUtils::vec2> &vx = m_screenVertices; |
123 | const QVector<quint32> &ix = m_screenIndices; |
124 | |
125 | geom->allocate(vertexCount: vx.size(), indexCount: ix.size()); |
126 | if (geom->indexType() == QSGGeometry::UnsignedShortType) { |
127 | quint16 *its = geom->indexDataAsUShort(); |
128 | for (int i = 0; i < ix.size(); ++i) |
129 | its[i] = ix[i]; |
130 | } else if (geom->indexType() == QSGGeometry::UnsignedIntType) { |
131 | quint32 *its = geom->indexDataAsUInt(); |
132 | for (int i = 0; i < ix.size(); ++i) |
133 | its[i] = ix[i]; |
134 | } |
135 | |
136 | QSGGeometry::Point2D *pts = geom->vertexDataAsPoint2D(); |
137 | for (int i = 0; i < vx.size(); ++i) |
138 | pts[i].set(nx: vx[i].x, ny: vx[i].y); |
139 | } |
140 | |
141 | QVector<QDeclarativeGeoMapItemUtils::vec2> m_screenVertices; |
142 | QVector<quint32> m_screenIndices; |
143 | QDoubleVector2D m_bboxLeftBoundWrapped; |
144 | QVector<WrappedPolygon> m_wrappedPolygons; |
145 | int m_wrapOffset; |
146 | }; |
147 | |
148 | class Q_LOCATION_PRIVATE_EXPORT MapPolygonShader : public QSGMaterialShader |
149 | { |
150 | public: |
151 | MapPolygonShader(); |
152 | |
153 | const char *vertexShader() const override { |
154 | return |
155 | "attribute highp vec4 vertex; \n" |
156 | "uniform highp mat4 qt_Matrix; \n" |
157 | "uniform highp mat4 mapProjection; \n" |
158 | "uniform highp vec3 center; \n" |
159 | "uniform highp vec3 center_lowpart; \n" |
160 | "uniform lowp float wrapOffset; \n" |
161 | "vec4 wrapped(in vec4 v) { return vec4(v.x + wrapOffset, v.y, 0.0, 1.0); }\n" |
162 | "void main() { \n" |
163 | " vec4 vtx = wrapped(vertex) - vec4(center, 0.0); \n" |
164 | " vtx = vtx - vec4(center_lowpart, 0.0); \n" |
165 | " gl_Position = qt_Matrix * mapProjection * vtx; \n" |
166 | "}" ; |
167 | } |
168 | |
169 | const char *fragmentShader() const override { |
170 | return |
171 | "uniform lowp vec4 color; \n" |
172 | "void main() { \n" |
173 | " gl_FragColor = color; \n" |
174 | "}" ; |
175 | } |
176 | |
177 | void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; |
178 | char const *const *attributeNames() const override |
179 | { |
180 | static char const *const attr[] = { "vertex" , nullptr }; |
181 | return attr; |
182 | } |
183 | |
184 | private: |
185 | void initialize() override |
186 | { |
187 | m_matrix_id = program()->uniformLocation(name: "qt_Matrix" ); |
188 | m_color_id = program()->uniformLocation(name: "color" ); |
189 | m_mapProjection_id = program()->uniformLocation(name: "mapProjection" ); |
190 | m_center_id = program()->uniformLocation(name: "center" ); |
191 | m_center_lowpart_id = program()->uniformLocation(name: "center_lowpart" ); |
192 | m_wrapOffset_id = program()->uniformLocation(name: "wrapOffset" ); |
193 | } |
194 | int m_center_id; |
195 | int m_center_lowpart_id; |
196 | int m_mapProjection_id; |
197 | int m_matrix_id; |
198 | int m_color_id; |
199 | int m_wrapOffset_id; |
200 | }; |
201 | #endif // QT_CONFIG(opengl) |
202 | |
203 | class Q_LOCATION_PRIVATE_EXPORT MapPolygonMaterial : public QSGFlatColorMaterial |
204 | { |
205 | public: |
206 | MapPolygonMaterial() |
207 | : QSGFlatColorMaterial() |
208 | { |
209 | // Passing RequiresFullMatrix is essential in order to prevent the |
210 | // batch renderer from baking in simple, translate-only transforms into |
211 | // the vertex data. The shader will rely on the fact that |
212 | // vertexCoord.xy is the Shape-space coordinate and so no modifications |
213 | // are welcome. |
214 | setFlag(flags: Blending | RequiresFullMatrix | CustomCompileStep); |
215 | } |
216 | |
217 | QSGMaterialShader *createShader() const override; |
218 | |
219 | void setGeoProjection(const QMatrix4x4 &p) |
220 | { |
221 | m_geoProjection = p; |
222 | } |
223 | |
224 | QMatrix4x4 geoProjection() const |
225 | { |
226 | return m_geoProjection; |
227 | } |
228 | |
229 | void setCenter(const QDoubleVector3D &c) |
230 | { |
231 | m_center = c; |
232 | } |
233 | |
234 | QDoubleVector3D center() const |
235 | { |
236 | return m_center; |
237 | } |
238 | |
239 | int wrapOffset() const |
240 | { |
241 | return m_wrapOffset; |
242 | } |
243 | |
244 | void setWrapOffset(int wrapOffset) |
245 | { |
246 | m_wrapOffset = wrapOffset; |
247 | } |
248 | |
249 | int compare(const QSGMaterial *other) const override; |
250 | QSGMaterialType *type() const override; |
251 | |
252 | protected: |
253 | QMatrix4x4 m_geoProjection; |
254 | QDoubleVector3D m_center; |
255 | int m_wrapOffset = 0; |
256 | }; |
257 | |
258 | class Q_LOCATION_PRIVATE_EXPORT MapPolygonNode : public MapItemGeometryNode |
259 | { |
260 | |
261 | public: |
262 | MapPolygonNode(); |
263 | ~MapPolygonNode() override; |
264 | |
265 | void update(const QColor &fillColor, const QColor &borderColor, |
266 | const QGeoMapItemGeometry *fillShape, |
267 | const QGeoMapItemGeometry *borderShape); |
268 | private: |
269 | QSGFlatColorMaterial fill_material_; |
270 | MapPolylineNode *border_; |
271 | QSGGeometry geometry_; |
272 | }; |
273 | |
274 | #if QT_CONFIG(opengl) |
275 | class Q_LOCATION_PRIVATE_EXPORT MapPolygonNodeGL : public MapItemGeometryNode |
276 | { |
277 | |
278 | public: |
279 | MapPolygonNodeGL(); |
280 | ~MapPolygonNodeGL() override; |
281 | |
282 | void update(const QColor &fillColor, |
283 | const QGeoMapPolygonGeometryOpenGL *fillShape, |
284 | const QMatrix4x4 &geoProjection, |
285 | const QDoubleVector3D ¢er); |
286 | |
287 | MapPolygonMaterial fill_material_; |
288 | QSGGeometry geometry_; |
289 | }; |
290 | #endif // QT_CONFIG(opengl) |
291 | |
292 | class Q_LOCATION_PRIVATE_EXPORT QDeclarativePolygonMapItemPrivate |
293 | { |
294 | public: |
295 | QDeclarativePolygonMapItemPrivate(QDeclarativePolygonMapItem &polygon) : m_poly(polygon) |
296 | { |
297 | |
298 | } |
299 | QDeclarativePolygonMapItemPrivate(QDeclarativePolygonMapItemPrivate &other) : m_poly(other.m_poly) |
300 | { |
301 | } |
302 | |
303 | virtual ~QDeclarativePolygonMapItemPrivate(); |
304 | virtual void onLinePropertiesChanged() = 0; |
305 | virtual void markSourceDirtyAndUpdate() = 0; |
306 | virtual void onMapSet() = 0; |
307 | virtual void onGeoGeometryChanged() = 0; |
308 | virtual void onGeoGeometryUpdated() = 0; |
309 | virtual void onItemGeometryChanged() = 0; |
310 | virtual void updatePolish() = 0; |
311 | virtual void afterViewportChanged() = 0; |
312 | virtual QSGNode * updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) = 0; |
313 | virtual bool contains(const QPointF &point) const = 0; |
314 | |
315 | QDeclarativePolygonMapItem &m_poly; |
316 | }; |
317 | |
318 | class Q_LOCATION_PRIVATE_EXPORT QDeclarativePolygonMapItemPrivateCPU: public QDeclarativePolygonMapItemPrivate |
319 | { |
320 | public: |
321 | QDeclarativePolygonMapItemPrivateCPU(QDeclarativePolygonMapItem &polygon) : QDeclarativePolygonMapItemPrivate(polygon) |
322 | { |
323 | } |
324 | |
325 | QDeclarativePolygonMapItemPrivateCPU(QDeclarativePolygonMapItemPrivate &other) |
326 | : QDeclarativePolygonMapItemPrivate(other) |
327 | { |
328 | } |
329 | |
330 | ~QDeclarativePolygonMapItemPrivateCPU() override; |
331 | void onLinePropertiesChanged() override |
332 | { |
333 | // mark dirty just in case we're a width change |
334 | markSourceDirtyAndUpdate(); |
335 | } |
336 | void markSourceDirtyAndUpdate() override |
337 | { |
338 | // preserveGeometry is cleared in updateMapItemPaintNode |
339 | m_geometry.markSourceDirty(); |
340 | m_borderGeometry.markSourceDirty(); |
341 | m_poly.polishAndUpdate(); |
342 | } |
343 | void regenerateCache() |
344 | { |
345 | if (!m_poly.map() || m_poly.map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) |
346 | return; |
347 | const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_poly.map()->geoProjection()); |
348 | m_geopathProjected.clear(); |
349 | m_geopathProjected.reserve(alloc: m_poly.m_geopoly.size()); |
350 | for (const QGeoCoordinate &c : m_poly.m_geopoly.path()) |
351 | m_geopathProjected << p.geoToMapProjection(coordinate: c); |
352 | } |
353 | void updateCache() |
354 | { |
355 | if (!m_poly.map() || m_poly.map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) |
356 | return; |
357 | const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_poly.map()->geoProjection()); |
358 | m_geopathProjected << p.geoToMapProjection(coordinate: m_poly.m_geopoly.path().last()); |
359 | } |
360 | void preserveGeometry() |
361 | { |
362 | m_geometry.setPreserveGeometry(value: true, geoLeftBound: m_poly.m_geopoly.boundingGeoRectangle().topLeft()); |
363 | m_borderGeometry.setPreserveGeometry(value: true, geoLeftBound: m_poly.m_geopoly.boundingGeoRectangle().topLeft()); |
364 | } |
365 | void afterViewportChanged() override |
366 | { |
367 | // preserveGeometry is cleared in updateMapItemPaintNode |
368 | preserveGeometry(); |
369 | markSourceDirtyAndUpdate(); |
370 | } |
371 | void onMapSet() override |
372 | { |
373 | regenerateCache(); |
374 | markSourceDirtyAndUpdate(); |
375 | } |
376 | void onGeoGeometryChanged() override |
377 | { |
378 | regenerateCache(); |
379 | preserveGeometry(); |
380 | markSourceDirtyAndUpdate(); |
381 | } |
382 | void onGeoGeometryUpdated() override |
383 | { |
384 | updateCache(); |
385 | preserveGeometry(); |
386 | markSourceDirtyAndUpdate(); |
387 | } |
388 | void onItemGeometryChanged() override |
389 | { |
390 | onGeoGeometryChanged(); |
391 | } |
392 | void updatePolish() override |
393 | { |
394 | if (m_poly.m_geopoly.path().length() == 0) { // Possibly cleared |
395 | m_geometry.clear(); |
396 | m_borderGeometry.clear(); |
397 | m_poly.setWidth(0); |
398 | m_poly.setHeight(0); |
399 | return; |
400 | } |
401 | const QGeoMap *map = m_poly.map(); |
402 | const qreal borderWidth = m_poly.m_border.width(); |
403 | const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map->geoProjection()); |
404 | QScopedValueRollback<bool> rollback(m_poly.m_updatingGeometry); |
405 | m_poly.m_updatingGeometry = true; |
406 | |
407 | m_geometry.updateSourcePoints(map: *map, path: m_geopathProjected); |
408 | m_geometry.updateScreenPoints(map: *map, strokeWidth: borderWidth); |
409 | |
410 | QList<QGeoMapItemGeometry *> geoms; |
411 | geoms << &m_geometry; |
412 | m_borderGeometry.clear(); |
413 | |
414 | if (m_poly.m_border.color().alpha() != 0 && borderWidth > 0) { |
415 | QList<QDoubleVector2D> closedPath = m_geopathProjected; |
416 | closedPath << closedPath.first(); |
417 | |
418 | m_borderGeometry.setPreserveGeometry(value: true, geoLeftBound: m_poly.m_geopoly.boundingGeoRectangle().topLeft()); |
419 | |
420 | const QGeoCoordinate &geometryOrigin = m_geometry.origin(); |
421 | |
422 | m_borderGeometry.srcPoints_.clear(); |
423 | m_borderGeometry.srcPointTypes_.clear(); |
424 | |
425 | QDoubleVector2D borderLeftBoundWrapped; |
426 | QList<QList<QDoubleVector2D > > clippedPaths = m_borderGeometry.clipPath(map: *map, path: closedPath, leftBoundWrapped&: borderLeftBoundWrapped); |
427 | if (clippedPaths.size()) { |
428 | borderLeftBoundWrapped = p.geoToWrappedMapProjection(coordinate: geometryOrigin); |
429 | m_borderGeometry.pathToScreen(map: *map, clippedPaths, leftBoundWrapped: borderLeftBoundWrapped); |
430 | m_borderGeometry.updateScreenPoints(map: *map, strokeWidth: borderWidth); |
431 | |
432 | geoms << &m_borderGeometry; |
433 | } else { |
434 | m_borderGeometry.clear(); |
435 | } |
436 | } |
437 | |
438 | QRectF combined = QGeoMapItemGeometry::translateToCommonOrigin(geoms); |
439 | m_poly.setWidth(combined.width() + 2 * borderWidth); |
440 | m_poly.setHeight(combined.height() + 2 * borderWidth); |
441 | |
442 | m_poly.setPositionOnMap(coordinate: m_geometry.origin(), offset: -1 * m_geometry.sourceBoundingBox().topLeft() |
443 | + QPointF(borderWidth, borderWidth)); |
444 | } |
445 | QSGNode *updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) override |
446 | { |
447 | Q_UNUSED(data); |
448 | if (!m_node || !oldNode) { |
449 | m_node = new MapPolygonNode(); |
450 | if (oldNode) { |
451 | delete oldNode; |
452 | oldNode = nullptr; |
453 | } |
454 | } else { |
455 | m_node = static_cast<MapPolygonNode *>(oldNode); |
456 | } |
457 | |
458 | //TODO: update only material |
459 | if (m_geometry.isScreenDirty() |
460 | || m_borderGeometry.isScreenDirty() |
461 | || m_poly.m_dirtyMaterial |
462 | || !oldNode) { |
463 | m_node->update(fillColor: m_poly.m_color, |
464 | borderColor: m_poly.m_border.color(), |
465 | fillShape: &m_geometry, |
466 | borderShape: &m_borderGeometry); |
467 | m_geometry.setPreserveGeometry(value: false); |
468 | m_borderGeometry.setPreserveGeometry(value: false); |
469 | m_geometry.markClean(); |
470 | m_borderGeometry.markClean(); |
471 | m_poly.m_dirtyMaterial = false; |
472 | } |
473 | return m_node; |
474 | } |
475 | bool contains(const QPointF &point) const override |
476 | { |
477 | return (m_geometry.contains(screenPoint: point) || m_borderGeometry.contains(point)); |
478 | } |
479 | |
480 | QList<QDoubleVector2D> m_geopathProjected; |
481 | QGeoMapPolygonGeometry m_geometry; |
482 | QGeoMapPolylineGeometry m_borderGeometry; |
483 | MapPolygonNode *m_node = nullptr; |
484 | }; |
485 | |
486 | #if QT_CONFIG(opengl) |
487 | class Q_LOCATION_PRIVATE_EXPORT QDeclarativePolygonMapItemPrivateOpenGL: public QDeclarativePolygonMapItemPrivate |
488 | { |
489 | public: |
490 | struct RootNode : public QSGNode /*QSGTransformNode*/, public VisibleNode |
491 | { |
492 | RootNode() { } |
493 | |
494 | bool isSubtreeBlocked() const override |
495 | { |
496 | return subtreeBlocked(); |
497 | } |
498 | }; |
499 | |
500 | QDeclarativePolygonMapItemPrivateOpenGL(QDeclarativePolygonMapItem &polygon) : QDeclarativePolygonMapItemPrivate(polygon) |
501 | { |
502 | } |
503 | |
504 | QDeclarativePolygonMapItemPrivateOpenGL(QDeclarativePolygonMapItemPrivate &other) |
505 | : QDeclarativePolygonMapItemPrivate(other) |
506 | { |
507 | } |
508 | |
509 | ~QDeclarativePolygonMapItemPrivateOpenGL() override; |
510 | |
511 | void markScreenDirtyAndUpdate() |
512 | { |
513 | // preserveGeometry is cleared in updateMapItemPaintNode |
514 | m_geometry.markScreenDirty(); |
515 | m_borderGeometry.markScreenDirty(); |
516 | m_poly.polishAndUpdate(); |
517 | } |
518 | void onLinePropertiesChanged() override |
519 | { |
520 | m_poly.m_dirtyMaterial = true; |
521 | afterViewportChanged(); |
522 | } |
523 | void markSourceDirtyAndUpdate() override |
524 | { |
525 | // preserveGeometry is cleared in updateMapItemPaintNode |
526 | m_geometry.markSourceDirty(); |
527 | m_borderGeometry.markSourceDirty(); |
528 | m_poly.polishAndUpdate(); |
529 | } |
530 | void preserveGeometry() |
531 | { |
532 | m_geometry.setPreserveGeometry(value: true, geoLeftBound: m_poly.m_geopoly.boundingGeoRectangle().topLeft()); |
533 | m_borderGeometry.setPreserveGeometry(value: true, geoLeftBound: m_poly.m_geopoly.boundingGeoRectangle().topLeft()); |
534 | } |
535 | void afterViewportChanged() override // This is called when the camera changes, or visibleArea changes. |
536 | { |
537 | // preserveGeometry is cleared in updateMapItemPaintNode |
538 | preserveGeometry(); |
539 | markScreenDirtyAndUpdate(); |
540 | } |
541 | void onMapSet() override |
542 | { |
543 | markSourceDirtyAndUpdate(); |
544 | } |
545 | void onGeoGeometryChanged() override |
546 | { |
547 | preserveGeometry(); |
548 | markSourceDirtyAndUpdate(); |
549 | } |
550 | void onGeoGeometryUpdated() override |
551 | { |
552 | preserveGeometry(); |
553 | markSourceDirtyAndUpdate(); |
554 | } |
555 | void onItemGeometryChanged() override |
556 | { |
557 | onGeoGeometryChanged(); |
558 | } |
559 | void updatePolish() override |
560 | { |
561 | if (m_poly.m_geopoly.path().length() == 0) { // Possibly cleared |
562 | m_geometry.clear(); |
563 | m_borderGeometry.clear(); |
564 | m_poly.setWidth(0); |
565 | m_poly.setHeight(0); |
566 | return; |
567 | } |
568 | |
569 | QScopedValueRollback<bool> rollback(m_poly.m_updatingGeometry); |
570 | m_poly.m_updatingGeometry = true; |
571 | const qreal lineWidth = m_poly.m_border.width(); |
572 | const QColor &lineColor = m_poly.m_border.color(); |
573 | const QColor &fillColor = m_poly.color(); |
574 | if (fillColor.alpha() != 0) { |
575 | m_geometry.updateSourcePoints(map: *m_poly.map(), poly: m_poly.m_geopoly); |
576 | m_geometry.markScreenDirty(); |
577 | m_geometry.updateScreenPoints(map: *m_poly.map(), strokeWidth: lineWidth, strokeColor: lineColor); |
578 | } else { |
579 | m_geometry.clearBounds(); |
580 | } |
581 | |
582 | QGeoMapItemGeometry * geom = &m_geometry; |
583 | m_borderGeometry.clearScreen(); |
584 | if (lineColor.alpha() != 0 && lineWidth > 0) { |
585 | m_borderGeometry.updateSourcePoints(map: *m_poly.map(), poly: m_poly.m_geopoly); |
586 | m_borderGeometry.markScreenDirty(); |
587 | m_borderGeometry.updateScreenPoints(map: *m_poly.map(), strokeWidth: lineWidth); |
588 | geom = &m_borderGeometry; |
589 | } |
590 | m_poly.setWidth(geom->sourceBoundingBox().width()); |
591 | m_poly.setHeight(geom->sourceBoundingBox().height()); |
592 | m_poly.setPosition(1.0 * geom->firstPointOffset() - QPointF(lineWidth * 0.5,lineWidth * 0.5)); |
593 | } |
594 | QSGNode * updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) override |
595 | { |
596 | Q_UNUSED(data); |
597 | |
598 | if (!m_rootNode || !oldNode) { |
599 | m_rootNode = new RootNode(); |
600 | m_node = new MapPolygonNodeGL(); |
601 | m_rootNode->appendChildNode(node: m_node); |
602 | m_polylinenode = new MapPolylineNodeOpenGLExtruded(); |
603 | m_rootNode->appendChildNode(node: m_polylinenode); |
604 | m_rootNode->markDirty(bits: QSGNode::DirtyNodeAdded); |
605 | if (oldNode) |
606 | delete oldNode; |
607 | } else { |
608 | m_rootNode = static_cast<RootNode *>(oldNode); |
609 | } |
610 | |
611 | const QGeoMap *map = m_poly.map(); |
612 | const QMatrix4x4 &combinedMatrix = map->geoProjection().qsgTransform(); |
613 | const QDoubleVector3D &cameraCenter = map->geoProjection().centerMercator(); |
614 | |
615 | if (m_borderGeometry.isScreenDirty()) { |
616 | /* Do the border update first */ |
617 | m_polylinenode->update(fillColor: m_poly.m_border.color(), |
618 | lineWidth: float(m_poly.m_border.width()), |
619 | shape: &m_borderGeometry, |
620 | geoProjection: combinedMatrix, |
621 | center: cameraCenter, |
622 | capStyle: Qt::SquareCap, |
623 | closed: true, |
624 | zoom: 30); // No LOD for polygons just yet. |
625 | // First figure out what to do with holes. |
626 | m_borderGeometry.setPreserveGeometry(value: false); |
627 | m_borderGeometry.markClean(); |
628 | } else { |
629 | m_polylinenode->setSubtreeBlocked(true); |
630 | } |
631 | if (m_geometry.isScreenDirty()) { |
632 | m_node->update(fillColor: m_poly.m_color, |
633 | fillShape: &m_geometry, |
634 | geoProjection: combinedMatrix, |
635 | center: cameraCenter); |
636 | m_geometry.setPreserveGeometry(value: false); |
637 | m_geometry.markClean(); |
638 | } else { |
639 | m_node->setSubtreeBlocked(true); |
640 | } |
641 | |
642 | m_rootNode->setSubtreeBlocked(false); |
643 | return m_rootNode; |
644 | } |
645 | bool contains(const QPointF &point) const override |
646 | { |
647 | const qreal lineWidth = m_poly.m_border.width(); |
648 | const QColor &lineColor = m_poly.m_border.color(); |
649 | const QRectF &bounds = (lineColor.alpha() != 0 && lineWidth > 0) ? m_borderGeometry.sourceBoundingBox() : m_geometry.sourceBoundingBox(); |
650 | if (bounds.contains(p: point)) { |
651 | QDeclarativeGeoMap *m = m_poly.quickMap(); |
652 | if (m) { |
653 | const QGeoCoordinate crd = m->toCoordinate(position: m->mapFromItem(item: &m_poly, point)); |
654 | return m_poly.m_geopoly.contains(coordinate: crd) || m_borderGeometry.contains(point: m_poly.mapToItem(item: m_poly.quickMap(), point), |
655 | lineWidth: m_poly.border()->width(), |
656 | p: static_cast<const QGeoProjectionWebMercator&>(m_poly.map()->geoProjection())); |
657 | } else { |
658 | return true; |
659 | } |
660 | } |
661 | return false; |
662 | } |
663 | |
664 | QGeoMapPolygonGeometryOpenGL m_geometry; |
665 | QGeoMapPolylineGeometryOpenGL m_borderGeometry; |
666 | RootNode *m_rootNode = nullptr; |
667 | MapPolygonNodeGL *m_node = nullptr; |
668 | MapPolylineNodeOpenGLExtruded *m_polylinenode = nullptr; |
669 | }; |
670 | #endif // QT_CONFIG(opengl) |
671 | |
672 | QT_END_NAMESPACE |
673 | |
674 | #endif // QDECLARATIVEPOLYGONMAPITEM_P_P_H |
675 | |