diff options
Diffstat (limited to 'src/location/declarativemaps/qdeclarativepolygonmapitem_p_p.h')
-rw-r--r-- | src/location/declarativemaps/qdeclarativepolygonmapitem_p_p.h | 668 |
1 files changed, 668 insertions, 0 deletions
diff --git a/src/location/declarativemaps/qdeclarativepolygonmapitem_p_p.h b/src/location/declarativemaps/qdeclarativepolygonmapitem_p_p.h new file mode 100644 index 00000000..8d566e69 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativepolygonmapitem_p_p.h @@ -0,0 +1,668 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Paolo Angelelli <paolo.angelelli@gmail.com> +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPOLYGONMAPITEM_P_P_H +#define QDECLARATIVEPOLYGONMAPITEM_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtLocation/private/qlocationglobal_p.h> +#include <QtLocation/private/qgeomapitemgeometry_p.h> +#include <QtLocation/private/qdeclarativegeomapitembase_p.h> +#include <QtLocation/private/qdeclarativepolylinemapitem_p.h> +#include <QtLocation/private/qdeclarativegeomapitemutils_p.h> +#include <QtLocation/private/qdeclarativepolygonmapitem_p.h> +#include <QtLocation/private/qdeclarativepolylinemapitem_p_p.h> +#include <QSGGeometryNode> +#include <QSGFlatColorMaterial> +#include <QtPositioning/QGeoPath> +#include <QtPositioning/QGeoRectangle> +#include <QtPositioning/QGeoPolygon> +#include <QtPositioning/private/qdoublevector2d_p.h> +#include <QSGFlatColorMaterial> +#include <QSGSimpleMaterial> +#include <QtGui/QMatrix4x4> +#include <QColor> +#include <QList> +#include <QVector> +#include <QtCore/QScopedValueRollback> + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QGeoMapPolygonGeometry : public QGeoMapItemGeometry +{ +public: + QGeoMapPolygonGeometry(); + + inline void setAssumeSimple(bool value) { assumeSimple_ = value; } + + void updateSourcePoints(const QGeoMap &map, + const QList<QDoubleVector2D> &path); + + void updateScreenPoints(const QGeoMap &map, qreal strokeWidth = 0.0); + +protected: + QPainterPath srcPath_; + bool assumeSimple_; +}; + +class Q_LOCATION_PRIVATE_EXPORT QGeoMapPolygonGeometryOpenGL : public QGeoMapItemGeometry +{ +public: + typedef struct { + QList<QDoubleVector2D> wrappedBboxes; + } WrappedPolygon; + QGeoMapPolygonGeometryOpenGL(); + ~QGeoMapPolygonGeometryOpenGL() override {} + + // Temporary method for compatibility in MapCircleObject. Remove when MapObjects are ported. + void updateSourcePoints(const QGeoMap &map, + const QList<QDoubleVector2D> &path); + + void updateSourcePoints(const QGeoMap &map, + const QList<QGeoCoordinate> &perimeter); + + void updateSourcePoints(const QGeoMap &map, + const QGeoPolygon &poly); + + void updateSourcePoints(const QGeoMap &map, + const QGeoRectangle &rect); + + void updateScreenPoints(const QGeoMap &map, qreal strokeWidth = 0.0, const QColor &strokeColor = Qt::transparent); + void updateQuickGeometry(const QGeoProjectionWebMercator &p, qreal strokeWidth = 0.0); + + void allocateAndFillPolygon(QSGGeometry *geom) const + { + + + const QVector<QDeclarativeGeoMapItemUtils::vec2> &vx = m_screenVertices; + const QVector<quint32> &ix = m_screenIndices; + + geom->allocate(vx.size(), ix.size()); + if (geom->indexType() == QSGGeometry::UnsignedShortType) { + quint16 *its = geom->indexDataAsUShort(); + for (int i = 0; i < ix.size(); ++i) + its[i] = ix[i]; + } else if (geom->indexType() == QSGGeometry::UnsignedIntType) { + quint32 *its = geom->indexDataAsUInt(); + for (int i = 0; i < ix.size(); ++i) + its[i] = ix[i]; + } + + QSGGeometry::Point2D *pts = geom->vertexDataAsPoint2D(); + for (int i = 0; i < vx.size(); ++i) + pts[i].set(vx[i].x, vx[i].y); + } + + QVector<QDeclarativeGeoMapItemUtils::vec2> m_screenVertices; + QVector<quint32> m_screenIndices; + QDoubleVector2D m_bboxLeftBoundWrapped; + QVector<WrappedPolygon> m_wrappedPolygons; + int m_wrapOffset; +}; + +class Q_LOCATION_PRIVATE_EXPORT MapPolygonShader : public QSGMaterialShader +{ +public: + MapPolygonShader(); + + const char *vertexShader() const override { + return + "attribute highp vec4 vertex; \n" + "uniform highp mat4 qt_Matrix; \n" + "uniform highp mat4 mapProjection; \n" + "uniform highp vec3 center; \n" + "uniform highp vec3 center_lowpart; \n" + "uniform lowp float wrapOffset; \n" + "vec4 wrapped(in vec4 v) { return vec4(v.x + wrapOffset, v.y, 0.0, 1.0); }\n" + "void main() { \n" + " vec4 vtx = wrapped(vertex) - vec4(center, 0.0); \n" + " vtx = vtx - vec4(center_lowpart, 0.0); \n" + " gl_Position = qt_Matrix * mapProjection * vtx; \n" + "}"; + } + + const char *fragmentShader() const override { + return + "uniform lowp vec4 color; \n" + "void main() { \n" + " gl_FragColor = color; \n" + "}"; + } + + void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; + char const *const *attributeNames() const override + { + static char const *const attr[] = { "vertex", nullptr }; + return attr; + } + +private: + void initialize() override + { + m_matrix_id = program()->uniformLocation("qt_Matrix"); + m_color_id = program()->uniformLocation("color"); + m_mapProjection_id = program()->uniformLocation("mapProjection"); + m_center_id = program()->uniformLocation("center"); + m_center_lowpart_id = program()->uniformLocation("center_lowpart"); + m_wrapOffset_id = program()->uniformLocation("wrapOffset"); + } + int m_center_id; + int m_center_lowpart_id; + int m_mapProjection_id; + int m_matrix_id; + int m_color_id; + int m_wrapOffset_id; +}; + +class Q_LOCATION_PRIVATE_EXPORT MapPolygonMaterial : public QSGFlatColorMaterial +{ +public: + MapPolygonMaterial() + : QSGFlatColorMaterial() + { + // Passing RequiresFullMatrix is essential in order to prevent the + // batch renderer from baking in simple, translate-only transforms into + // the vertex data. The shader will rely on the fact that + // vertexCoord.xy is the Shape-space coordinate and so no modifications + // are welcome. + setFlag(Blending | RequiresFullMatrix | CustomCompileStep); + } + + QSGMaterialShader *createShader() const override; + + void setGeoProjection(const QMatrix4x4 &p) + { + m_geoProjection = p; + } + + QMatrix4x4 geoProjection() const + { + return m_geoProjection; + } + + void setCenter(const QDoubleVector3D &c) + { + m_center = c; + } + + QDoubleVector3D center() const + { + return m_center; + } + + int wrapOffset() const + { + return m_wrapOffset; + } + + void setWrapOffset(int wrapOffset) + { + m_wrapOffset = wrapOffset; + } + + int compare(const QSGMaterial *other) const override; + QSGMaterialType *type() const override; + +protected: + QMatrix4x4 m_geoProjection; + QDoubleVector3D m_center; + int m_wrapOffset = 0; +}; + +class Q_LOCATION_PRIVATE_EXPORT MapPolygonNode : public MapItemGeometryNode +{ + +public: + MapPolygonNode(); + ~MapPolygonNode() override; + + void update(const QColor &fillColor, const QColor &borderColor, + const QGeoMapItemGeometry *fillShape, + const QGeoMapItemGeometry *borderShape); +private: + QSGFlatColorMaterial fill_material_; + MapPolylineNode *border_; + QSGGeometry geometry_; +}; + +class Q_LOCATION_PRIVATE_EXPORT MapPolygonNodeGL : public MapItemGeometryNode +{ + +public: + MapPolygonNodeGL(); + ~MapPolygonNodeGL() override; + + void update(const QColor &fillColor, + const QGeoMapPolygonGeometryOpenGL *fillShape, + const QMatrix4x4 &geoProjection, + const QDoubleVector3D ¢er); + + MapPolygonMaterial fill_material_; + QSGGeometry geometry_; +}; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativePolygonMapItemPrivate +{ +public: + QDeclarativePolygonMapItemPrivate(QDeclarativePolygonMapItem &polygon) : m_poly(polygon) + { + + } + QDeclarativePolygonMapItemPrivate(QDeclarativePolygonMapItemPrivate &other) : m_poly(other.m_poly) + { + } + + virtual ~QDeclarativePolygonMapItemPrivate(); + virtual void onLinePropertiesChanged() = 0; + virtual void markSourceDirtyAndUpdate() = 0; + virtual void onMapSet() = 0; + virtual void onGeoGeometryChanged() = 0; + virtual void onGeoGeometryUpdated() = 0; + virtual void onItemGeometryChanged() = 0; + virtual void updatePolish() = 0; + virtual void afterViewportChanged() = 0; + virtual QSGNode * updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) = 0; + virtual bool contains(const QPointF &point) const = 0; + + QDeclarativePolygonMapItem &m_poly; +}; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativePolygonMapItemPrivateCPU: public QDeclarativePolygonMapItemPrivate +{ +public: + QDeclarativePolygonMapItemPrivateCPU(QDeclarativePolygonMapItem &polygon) : QDeclarativePolygonMapItemPrivate(polygon) + { + } + + QDeclarativePolygonMapItemPrivateCPU(QDeclarativePolygonMapItemPrivate &other) + : QDeclarativePolygonMapItemPrivate(other) + { + } + + ~QDeclarativePolygonMapItemPrivateCPU() override; + void onLinePropertiesChanged() override + { + // mark dirty just in case we're a width change + markSourceDirtyAndUpdate(); + } + void markSourceDirtyAndUpdate() override + { + // preserveGeometry is cleared in updateMapItemPaintNode + m_geometry.markSourceDirty(); + m_borderGeometry.markSourceDirty(); + m_poly.polishAndUpdate(); + } + void regenerateCache() + { + if (!m_poly.map() || m_poly.map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) + return; + const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_poly.map()->geoProjection()); + m_geopathProjected.clear(); + m_geopathProjected.reserve(m_poly.m_geopoly.size()); + for (const QGeoCoordinate &c : m_poly.m_geopoly.path()) + m_geopathProjected << p.geoToMapProjection(c); + } + void updateCache() + { + if (!m_poly.map() || m_poly.map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) + return; + const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_poly.map()->geoProjection()); + m_geopathProjected << p.geoToMapProjection(m_poly.m_geopoly.path().last()); + } + void preserveGeometry() + { + m_geometry.setPreserveGeometry(true, m_poly.m_geopoly.boundingGeoRectangle().topLeft()); + m_borderGeometry.setPreserveGeometry(true, m_poly.m_geopoly.boundingGeoRectangle().topLeft()); + } + void afterViewportChanged() override + { + // preserveGeometry is cleared in updateMapItemPaintNode + preserveGeometry(); + markSourceDirtyAndUpdate(); + } + void onMapSet() override + { + regenerateCache(); + markSourceDirtyAndUpdate(); + } + void onGeoGeometryChanged() override + { + regenerateCache(); + preserveGeometry(); + markSourceDirtyAndUpdate(); + } + void onGeoGeometryUpdated() override + { + updateCache(); + preserveGeometry(); + markSourceDirtyAndUpdate(); + } + void onItemGeometryChanged() override + { + onGeoGeometryChanged(); + } + void updatePolish() override + { + if (m_poly.m_geopoly.path().length() == 0) { // Possibly cleared + m_geometry.clear(); + m_borderGeometry.clear(); + m_poly.setWidth(0); + m_poly.setHeight(0); + return; + } + const QGeoMap *map = m_poly.map(); + const qreal borderWidth = m_poly.m_border.width(); + const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map->geoProjection()); + QScopedValueRollback<bool> rollback(m_poly.m_updatingGeometry); + m_poly.m_updatingGeometry = true; + + m_geometry.updateSourcePoints(*map, m_geopathProjected); + m_geometry.updateScreenPoints(*map, borderWidth); + + QList<QGeoMapItemGeometry *> geoms; + geoms << &m_geometry; + m_borderGeometry.clear(); + + if (m_poly.m_border.color().alpha() != 0 && borderWidth > 0) { + QList<QDoubleVector2D> closedPath = m_geopathProjected; + closedPath << closedPath.first(); + + m_borderGeometry.setPreserveGeometry(true, m_poly.m_geopoly.boundingGeoRectangle().topLeft()); + + const QGeoCoordinate &geometryOrigin = m_geometry.origin(); + + m_borderGeometry.srcPoints_.clear(); + m_borderGeometry.srcPointTypes_.clear(); + + QDoubleVector2D borderLeftBoundWrapped; + QList<QList<QDoubleVector2D > > clippedPaths = m_borderGeometry.clipPath(*map, closedPath, borderLeftBoundWrapped); + if (clippedPaths.size()) { + borderLeftBoundWrapped = p.geoToWrappedMapProjection(geometryOrigin); + m_borderGeometry.pathToScreen(*map, clippedPaths, borderLeftBoundWrapped); + m_borderGeometry.updateScreenPoints(*map, borderWidth); + + geoms << &m_borderGeometry; + } else { + m_borderGeometry.clear(); + } + } + + QRectF combined = QGeoMapItemGeometry::translateToCommonOrigin(geoms); + m_poly.setWidth(combined.width() + 2 * borderWidth); + m_poly.setHeight(combined.height() + 2 * borderWidth); + + m_poly.setPositionOnMap(m_geometry.origin(), -1 * m_geometry.sourceBoundingBox().topLeft() + + QPointF(borderWidth, borderWidth)); + } + QSGNode *updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) override + { + Q_UNUSED(data); + if (!m_node || !oldNode) { + m_node = new MapPolygonNode(); + if (oldNode) { + delete oldNode; + oldNode = nullptr; + } + } else { + m_node = static_cast<MapPolygonNode *>(oldNode); + } + + //TODO: update only material + if (m_geometry.isScreenDirty() + || m_borderGeometry.isScreenDirty() + || m_poly.m_dirtyMaterial + || !oldNode) { + m_node->update(m_poly.m_color, + m_poly.m_border.color(), + &m_geometry, + &m_borderGeometry); + m_geometry.setPreserveGeometry(false); + m_borderGeometry.setPreserveGeometry(false); + m_geometry.markClean(); + m_borderGeometry.markClean(); + m_poly.m_dirtyMaterial = false; + } + return m_node; + } + bool contains(const QPointF &point) const override + { + return (m_geometry.contains(point) || m_borderGeometry.contains(point)); + } + + QList<QDoubleVector2D> m_geopathProjected; + QGeoMapPolygonGeometry m_geometry; + QGeoMapPolylineGeometry m_borderGeometry; + MapPolygonNode *m_node = nullptr; +}; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativePolygonMapItemPrivateOpenGL: public QDeclarativePolygonMapItemPrivate +{ +public: + struct RootNode : public QSGNode /*QSGTransformNode*/, public VisibleNode + { + RootNode() { } + + bool isSubtreeBlocked() const override + { + return subtreeBlocked(); + } + }; + + QDeclarativePolygonMapItemPrivateOpenGL(QDeclarativePolygonMapItem &polygon) : QDeclarativePolygonMapItemPrivate(polygon) + { + } + + QDeclarativePolygonMapItemPrivateOpenGL(QDeclarativePolygonMapItemPrivate &other) + : QDeclarativePolygonMapItemPrivate(other) + { + } + + ~QDeclarativePolygonMapItemPrivateOpenGL() override; + + void markScreenDirtyAndUpdate() + { + // preserveGeometry is cleared in updateMapItemPaintNode + m_geometry.markScreenDirty(); + m_borderGeometry.markScreenDirty(); + m_poly.polishAndUpdate(); + } + void onLinePropertiesChanged() override + { + m_poly.m_dirtyMaterial = true; + afterViewportChanged(); + } + void markSourceDirtyAndUpdate() override + { + // preserveGeometry is cleared in updateMapItemPaintNode + m_geometry.markSourceDirty(); + m_borderGeometry.markSourceDirty(); + m_poly.polishAndUpdate(); + } + void preserveGeometry() + { + m_geometry.setPreserveGeometry(true, m_poly.m_geopoly.boundingGeoRectangle().topLeft()); + m_borderGeometry.setPreserveGeometry(true, m_poly.m_geopoly.boundingGeoRectangle().topLeft()); + } + void afterViewportChanged() override // This is called when the camera changes, or visibleArea changes. + { + // preserveGeometry is cleared in updateMapItemPaintNode + preserveGeometry(); + markScreenDirtyAndUpdate(); + } + void onMapSet() override + { + markSourceDirtyAndUpdate(); + } + void onGeoGeometryChanged() override + { + preserveGeometry(); + markSourceDirtyAndUpdate(); + } + void onGeoGeometryUpdated() override + { + preserveGeometry(); + markSourceDirtyAndUpdate(); + } + void onItemGeometryChanged() override + { + onGeoGeometryChanged(); + } + void updatePolish() override + { + if (m_poly.m_geopoly.path().length() == 0) { // Possibly cleared + m_geometry.clear(); + m_borderGeometry.clear(); + m_poly.setWidth(0); + m_poly.setHeight(0); + return; + } + + QScopedValueRollback<bool> rollback(m_poly.m_updatingGeometry); + m_poly.m_updatingGeometry = true; + const qreal lineWidth = m_poly.m_border.width(); + const QColor &lineColor = m_poly.m_border.color(); + const QColor &fillColor = m_poly.color(); + if (fillColor.alpha() != 0) { + m_geometry.updateSourcePoints(*m_poly.map(), m_poly.m_geopoly); + m_geometry.markScreenDirty(); + m_geometry.updateScreenPoints(*m_poly.map(), lineWidth, lineColor); + } else { + m_geometry.clearBounds(); + } + + QGeoMapItemGeometry * geom = &m_geometry; + m_borderGeometry.clearScreen(); + if (lineColor.alpha() != 0 && lineWidth > 0) { + m_borderGeometry.updateSourcePoints(*m_poly.map(), m_poly.m_geopoly); + m_borderGeometry.markScreenDirty(); + m_borderGeometry.updateScreenPoints(*m_poly.map(), lineWidth); + geom = &m_borderGeometry; + } + m_poly.setWidth(geom->sourceBoundingBox().width()); + m_poly.setHeight(geom->sourceBoundingBox().height()); + m_poly.setPosition(1.0 * geom->firstPointOffset() - QPointF(lineWidth * 0.5,lineWidth * 0.5)); + } + QSGNode * updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) override + { + Q_UNUSED(data); + + if (!m_rootNode || !oldNode) { + m_rootNode = new RootNode(); + m_node = new MapPolygonNodeGL(); + m_rootNode->appendChildNode(m_node); + m_polylinenode = new MapPolylineNodeOpenGLExtruded(); + m_rootNode->appendChildNode(m_polylinenode); + m_rootNode->markDirty(QSGNode::DirtyNodeAdded); + if (oldNode) + delete oldNode; + } else { + m_rootNode = static_cast<RootNode *>(oldNode); + } + + const QGeoMap *map = m_poly.map(); + const QMatrix4x4 &combinedMatrix = map->geoProjection().qsgTransform(); + const QDoubleVector3D &cameraCenter = map->geoProjection().centerMercator(); + + if (m_borderGeometry.isScreenDirty()) { + /* Do the border update first */ + m_polylinenode->update(m_poly.m_border.color(), + float(m_poly.m_border.width()), + &m_borderGeometry, + combinedMatrix, + cameraCenter, + Qt::SquareCap, + true, + 30); // No LOD for polygons just yet. + // First figure out what to do with holes. + m_borderGeometry.setPreserveGeometry(false); + m_borderGeometry.markClean(); + } else { + m_polylinenode->setSubtreeBlocked(true); + } + if (m_geometry.isScreenDirty()) { + m_node->update(m_poly.m_color, + &m_geometry, + combinedMatrix, + cameraCenter); + m_geometry.setPreserveGeometry(false); + m_geometry.markClean(); + } else { + m_node->setSubtreeBlocked(true); + } + + m_rootNode->setSubtreeBlocked(false); + return m_rootNode; + } + bool contains(const QPointF &point) const override + { + const qreal lineWidth = m_poly.m_border.width(); + const QColor &lineColor = m_poly.m_border.color(); + const QRectF &bounds = (lineColor.alpha() != 0 && lineWidth > 0) ? m_borderGeometry.sourceBoundingBox() : m_geometry.sourceBoundingBox(); + if (bounds.contains(point)) { + QDeclarativeGeoMap *m = m_poly.quickMap(); + if (m) { + const QGeoCoordinate crd = m->toCoordinate(m->mapFromItem(&m_poly, point)); + return m_poly.m_geopoly.contains(crd) || m_borderGeometry.contains(m_poly.mapToItem(m_poly.quickMap(), point), + m_poly.border()->width(), + static_cast<const QGeoProjectionWebMercator&>(m_poly.map()->geoProjection())); + } else { + return true; + } + } + return false; + } + + QGeoMapPolygonGeometryOpenGL m_geometry; + QGeoMapPolylineGeometryOpenGL m_borderGeometry; + RootNode *m_rootNode = nullptr; + MapPolygonNodeGL *m_node = nullptr; + MapPolylineNodeOpenGLExtruded *m_polylinenode = nullptr; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEPOLYGONMAPITEM_P_P_H |