| /**************************************************************************** |
| ** |
| ** 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 QDECLARATIVEPOLYLINEMAPITEM_P_P_H |
| #define QDECLARATIVEPOLYLINEMAPITEM_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/qdeclarativepolylinemapitem_p.h> |
| #include <QtLocation/private/qdeclarativegeomapitemutils_p.h> |
| #include <QtLocation/private/qdeclarativepolylinemapitem_p.h> |
| #include <QtLocation/private/qgeomapitemgeometry_p.h> |
| #include <QSGGeometryNode> |
| #include <QSGFlatColorMaterial> |
| #include <QtPositioning/QGeoPath> |
| #include <QtPositioning/QGeoPolygon> |
| #include <QtPositioning/QGeoRectangle> |
| #include <QtPositioning/QGeoCircle> |
| #include <QtPositioning/private/qdoublevector2d_p.h> |
| #include <QtCore/QScopedValueRollback> |
| #include <QSharedPointer> |
| #include <array> |
| |
| QT_BEGIN_NAMESPACE |
| |
| class Q_LOCATION_PRIVATE_EXPORT QGeoMapPolylineGeometry : public QGeoMapItemGeometry |
| { |
| public: |
| QGeoMapPolylineGeometry(); |
| |
| void updateSourcePoints(const QGeoMap &map, |
| const QList<QDoubleVector2D> &path, |
| const QGeoCoordinate geoLeftBound); |
| |
| void updateScreenPoints(const QGeoMap &map, |
| qreal strokeWidth, |
| bool adjustTranslation = true); |
| |
| void clearSource(); |
| |
| bool contains(const QPointF &point) const override; |
| |
| QList<QList<QDoubleVector2D> > clipPath(const QGeoMap &map, |
| const QList<QDoubleVector2D> &path, |
| QDoubleVector2D &leftBoundWrapped); |
| |
| void pathToScreen(const QGeoMap &map, |
| const QList<QList<QDoubleVector2D> > &clippedPaths, |
| const QDoubleVector2D &leftBoundWrapped); |
| |
| public: |
| QVector<qreal> srcPoints_; |
| QVector<QPainterPath::ElementType> srcPointTypes_; |
| |
| #ifdef QT_LOCATION_DEBUG |
| QList<QDoubleVector2D> m_wrappedPath; |
| QList<QList<QDoubleVector2D>> m_clippedPaths; |
| #endif |
| |
| friend class QDeclarativeCircleMapItem; |
| friend class QDeclarativePolygonMapItem; |
| friend class QDeclarativeRectangleMapItem; |
| }; |
| |
| class Q_LOCATION_PRIVATE_EXPORT VisibleNode |
| { |
| public: |
| VisibleNode(); |
| virtual ~VisibleNode(); |
| |
| bool subtreeBlocked() const; |
| void setSubtreeBlocked(bool blocked); |
| bool visible() const; |
| void setVisible(bool visible); |
| |
| bool m_blocked : 1; |
| bool m_visible : 1; |
| }; |
| |
| class Q_LOCATION_PRIVATE_EXPORT MapItemGeometryNode : public QSGGeometryNode, public VisibleNode |
| { |
| public: |
| ~MapItemGeometryNode() override; |
| bool isSubtreeBlocked() const override; |
| }; |
| |
| class Q_LOCATION_PRIVATE_EXPORT MapPolylineMaterial : public QSGFlatColorMaterial |
| { |
| public: |
| MapPolylineMaterial() |
| : 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; |
| } |
| |
| void setColor(const QColor &color) |
| { |
| QSGFlatColorMaterial::setColor(color); |
| setFlag(Blending, true); // ToDo: Needed only temporarily, can be removed after debugging |
| } |
| |
| int wrapOffset() const |
| { |
| return m_wrapOffset; |
| } |
| |
| void setWrapOffset(int wrapOffset) |
| { |
| m_wrapOffset = wrapOffset; |
| } |
| |
| void setLineWidth(const float lw) |
| { |
| m_lineWidth = lw; |
| } |
| |
| float lineWidth() const |
| { |
| return m_lineWidth; |
| } |
| |
| QSGMaterialType *type() const override; |
| int compare(const QSGMaterial *other) const override; |
| |
| protected: |
| QMatrix4x4 m_geoProjection; |
| QDoubleVector3D m_center; |
| int m_wrapOffset = 0; |
| float m_lineWidth = 1.0; |
| }; |
| |
| class Q_LOCATION_PRIVATE_EXPORT MapPolylineNode : public MapItemGeometryNode |
| { |
| public: |
| MapPolylineNode(); |
| ~MapPolylineNode() override; |
| |
| void update(const QColor &fillColor, const QGeoMapItemGeometry *shape); |
| |
| protected: |
| QSGFlatColorMaterial fill_material_; |
| QSGGeometry geometry_; |
| }; |
| |
| class Q_LOCATION_PRIVATE_EXPORT QGeoMapItemLODGeometry |
| { |
| public: |
| mutable std::array<QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2>>, 7> m_verticesLOD; // fix it to 7, |
| // do not allow simplifications beyond ZL 20. This could actually be limited even further |
| mutable QVector<QDeclarativeGeoMapItemUtils::vec2> *m_screenVertices; |
| mutable QSharedPointer<unsigned int> m_working; |
| |
| QGeoMapItemLODGeometry() |
| { |
| resetLOD(); |
| } |
| |
| void resetLOD() |
| { |
| // New pointer, some old LOD task might still be running and operating on the old pointers. |
| m_verticesLOD[0] = QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2>>( |
| new QVector<QDeclarativeGeoMapItemUtils::vec2>); |
| for (unsigned int i = 1; i < m_verticesLOD.size(); ++i) |
| m_verticesLOD[i] = nullptr; // allocate on first use |
| m_screenVertices = m_verticesLOD.front().data(); // resetting pointer to data to be LOD 0 |
| } |
| |
| static unsigned int zoomToLOD(unsigned int zoom); |
| |
| static unsigned int zoomForLOD(unsigned int zoom); |
| |
| bool isLODActive(unsigned int lod) const; |
| |
| void selectLOD(unsigned int zoom, double leftBound, bool /*closed*/); |
| |
| static QVector<QDeclarativeGeoMapItemUtils::vec2> getSimplified ( |
| QVector<QDeclarativeGeoMapItemUtils::vec2> &wrappedPath, |
| double leftBoundWrapped, |
| unsigned int zoom); |
| |
| static void enqueueSimplificationTask(const QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2> > &input, // reference as it gets copied in the nested call |
| const QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2> > &output, |
| double leftBound, |
| unsigned int zoom, |
| QSharedPointer<unsigned int> &working); |
| |
| void selectLODOnDataChanged(unsigned int zoom, double leftBound) const; |
| |
| bool selectLODOnLODMismatch(unsigned int zoom, double leftBound, bool closed) const |
| { |
| if (*m_working > 0) { |
| return false; |
| } |
| const_cast<QGeoMapItemLODGeometry *>(this)->selectLOD(zoom, |
| leftBound, |
| closed); |
| return true; |
| } |
| }; |
| |
| class Q_LOCATION_PRIVATE_EXPORT QGeoMapPolylineGeometryOpenGL : public QGeoMapItemGeometry, public QGeoMapItemLODGeometry |
| { |
| public: |
| typedef struct { |
| QList<QDoubleVector2D> wrappedBboxes; |
| } WrappedPolyline; |
| |
| QGeoMapPolylineGeometryOpenGL() |
| { |
| m_working = QSharedPointer<unsigned int>(new unsigned int(0)); |
| } |
| |
| void updateSourcePoints(const QGeoMap &map, |
| const QGeoPolygon &poly); |
| |
| void updateSourcePoints(const QGeoMap &map, |
| const QGeoPath &poly); |
| |
| void updateSourcePoints(const QGeoProjectionWebMercator &p, |
| const QList<QDoubleVector2D> &wrappedPath, |
| const QGeoRectangle &boundingRectangle); |
| |
| void updateSourcePoints(const QGeoMap &map, |
| const QGeoRectangle &rect); |
| |
| void updateSourcePoints(const QGeoMap &map, |
| const QGeoCircle &circle); |
| |
| void updateScreenPoints(const QGeoMap &map, |
| qreal strokeWidth, |
| bool adjustTranslation = true); |
| |
| void updateQuickGeometry(const QGeoProjectionWebMercator &p, qreal strokeWidth = 0.0); |
| |
| bool allocateAndFillEntries(QSGGeometry *geom, |
| bool closed = false, |
| unsigned int zoom = 0) const; |
| void allocateAndFillLineStrip(QSGGeometry *geom, |
| int lod = 0) const; |
| |
| bool contains(const QPointF &point) const override |
| { |
| Q_UNUSED(point) |
| return false; |
| } |
| |
| static double distanceTo(const QDoubleVector2D &a, const QDoubleVector2D &b, const QDoubleVector2D &p) |
| { |
| double u = ((p.x() - a.x()) * (b.x() - a.x()) + (p.y() - a.y()) * (b.y() - a.y()) ) / (b - a).lengthSquared(); |
| QDoubleVector2D intersection(a.x() + u * (b.x() - a.x()) , a.y() + u * (b.y() - a.y()) ); |
| |
| QDoubleVector2D candidate = ( (p-a).length() < (p-b).length() ) ? a : b; |
| |
| if (u > 0 && u < 1 |
| && (p-intersection).length() < (p-candidate).length() ) // And it falls in the segment |
| candidate = intersection; |
| |
| return qAbs((candidate - p).length()); |
| } |
| // Note: this is also slightly incorrect on joins and in the beginning/end of the line |
| bool contains(const QPointF &point, qreal lineWidth, const QGeoProjectionWebMercator &p) const |
| { |
| const double lineHalfWidth = lineWidth * 0.5; |
| const QDoubleVector2D pt(point); |
| QDoubleVector2D a; |
| if (m_screenVertices->size()) |
| a = p.wrappedMapProjectionToItemPosition(p.wrapMapProjection(m_screenVertices->first().toDoubleVector2D())); |
| QDoubleVector2D b; |
| for (int i = 1; i < m_screenVertices->size(); ++i) |
| { |
| if (!a.isFinite()) { |
| a = p.wrappedMapProjectionToItemPosition(p.wrapMapProjection(m_screenVertices->at(i).toDoubleVector2D())); |
| continue; |
| } |
| |
| b = p.wrappedMapProjectionToItemPosition(p.wrapMapProjection(m_screenVertices->at(i).toDoubleVector2D())); |
| if (!b.isFinite()) { |
| a = b; |
| continue; |
| } |
| |
| if (b == a) |
| continue; |
| |
| // Heavily simplifying it here: if a point is not projectable, skip the segment. |
| // For a correct solution, the segment should be clipped instead. |
| if (distanceTo(a, b, pt) <= lineHalfWidth) |
| return true; |
| |
| a = b; |
| } |
| return false; |
| } |
| |
| public: |
| QDoubleVector2D m_bboxLeftBoundWrapped; |
| QVector<WrappedPolyline> m_wrappedPolygons; |
| int m_wrapOffset; |
| |
| friend class QDeclarativeCircleMapItem; |
| friend class QDeclarativePolygonMapItem; |
| friend class QDeclarativeRectangleMapItem; |
| }; |
| |
| class Q_LOCATION_PRIVATE_EXPORT MapPolylineShaderLineStrip : public QSGMaterialShader |
| { |
| public: |
| MapPolylineShaderLineStrip(); |
| |
| 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; |
| |
| protected: |
| 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 MapPolylineShaderExtruded : public QSGMaterialShader |
| { |
| public: |
| MapPolylineShaderExtruded(); |
| |
| // Heavily adapted from https://github.com/mattdesl/webgl-lines/blob/master/projected/vert.glsl, |
| // that is (c) Matt DesLauriers, and released under the MIT license. |
| const char *vertexShaderMiteredSegments() const; |
| |
| const char *vertexShader() const override |
| { |
| return vertexShaderMiteredSegments(); |
| } |
| |
| const char *fragmentShader() const override |
| { |
| return |
| "varying vec4 primitivecolor; \n" |
| "void main() { \n" |
| " gl_FragColor = primitivecolor; \n" |
| "}"; |
| } |
| |
| void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; |
| char const *const *attributeNames() const override; |
| |
| protected: |
| 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_lineWidth_id = program()->uniformLocation("lineWidth"); |
| m_aspect_id = program()->uniformLocation("aspect"); |
| m_miter_id = program()->uniformLocation("miter"); |
| 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_lineWidth_id; |
| int m_aspect_id; |
| int m_miter_id; |
| int m_wrapOffset_id; |
| }; |
| |
| class Q_LOCATION_PRIVATE_EXPORT MapPolylineNodeOpenGLLineStrip : public MapItemGeometryNode |
| { |
| public: |
| MapPolylineNodeOpenGLLineStrip(); |
| ~MapPolylineNodeOpenGLLineStrip() override; |
| |
| void update(const QColor &fillColor, |
| const qreal lineWidth, |
| const QGeoMapPolylineGeometryOpenGL *shape, |
| const QMatrix4x4 &geoProjection, |
| const QDoubleVector3D ¢er, |
| const Qt::PenCapStyle capStyle = Qt::SquareCap); |
| |
| protected: |
| MapPolylineMaterial fill_material_; |
| QSGGeometry geometry_; |
| }; |
| |
| class Q_LOCATION_PRIVATE_EXPORT MapPolylineMaterialExtruded : public MapPolylineMaterial |
| { |
| public: |
| MapPolylineMaterialExtruded() : MapPolylineMaterial() |
| { |
| |
| } |
| QSGMaterialShader *createShader() const override; |
| |
| void setMiter(const int m) |
| { |
| m_miter = m; |
| } |
| |
| int miter() const |
| { |
| return m_miter; |
| } |
| |
| QSGMaterialType *type() const override; |
| int compare(const QSGMaterial *other) const override; |
| |
| int m_miter = 0; |
| }; |
| |
| class Q_LOCATION_PRIVATE_EXPORT MapPolylineNodeOpenGLExtruded : public MapItemGeometryNode |
| { |
| public: |
| |
| typedef struct { |
| QDeclarativeGeoMapItemUtils::vec2 pos; |
| QDeclarativeGeoMapItemUtils::vec2 prev; |
| QDeclarativeGeoMapItemUtils::vec2 next; |
| float direction; |
| float triangletype; // es2 does not support int attribs |
| float vertextype; |
| |
| static const char * const *attributeNames() |
| { |
| static char const *const attr[] = { "vertex", "previous", "next", "direction", "triangletype", "vertextype", nullptr }; |
| return attr; |
| } |
| static const QSGGeometry::AttributeSet &attributes() |
| { |
| static const QSGGeometry::Attribute dataTri[] = { |
| QSGGeometry::Attribute::createWithAttributeType(0, 2, QSGGeometry::FloatType, QSGGeometry::PositionAttribute) // pos |
| ,QSGGeometry::Attribute::createWithAttributeType(1, 2, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute) // next |
| ,QSGGeometry::Attribute::createWithAttributeType(2, 2, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute) // previous |
| ,QSGGeometry::Attribute::createWithAttributeType(3, 1, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute) // direction |
| ,QSGGeometry::Attribute::createWithAttributeType(4, 1, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute) // triangletype |
| ,QSGGeometry::Attribute::createWithAttributeType(5, 1, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute) // vertextype |
| }; |
| static const QSGGeometry::AttributeSet attrsTri = { 6, sizeof(MapPolylineNodeOpenGLExtruded::MapPolylineEntry), dataTri }; |
| return attrsTri; |
| } |
| } MapPolylineEntry; |
| |
| MapPolylineNodeOpenGLExtruded(); |
| ~MapPolylineNodeOpenGLExtruded() override; |
| |
| void update(const QColor &fillColor, |
| const float lineWidth, |
| const QGeoMapPolylineGeometryOpenGL *shape, |
| const QMatrix4x4 geoProjection, |
| const QDoubleVector3D center, |
| const Qt::PenCapStyle capStyle = Qt::FlatCap, |
| bool closed = false, |
| unsigned int zoom = 30); |
| |
| static const QSGGeometry::AttributeSet &attributesMapPolylineTriangulated(); |
| |
| protected: |
| MapPolylineMaterialExtruded fill_material_; |
| QSGGeometry m_geometryTriangulating; |
| }; |
| |
| class Q_LOCATION_PRIVATE_EXPORT QDeclarativePolylineMapItemPrivate |
| { |
| public: |
| QDeclarativePolylineMapItemPrivate(QDeclarativePolylineMapItem &poly) : m_poly(poly) |
| { |
| |
| } |
| QDeclarativePolylineMapItemPrivate(QDeclarativePolylineMapItemPrivate &other) : m_poly(other.m_poly) |
| { |
| } |
| |
| virtual ~QDeclarativePolylineMapItemPrivate(); |
| virtual void markSourceDirtyAndUpdate() = 0; |
| virtual void onMapSet() = 0; |
| virtual void onLinePropertiesChanged() = 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; |
| |
| QDeclarativePolylineMapItem &m_poly; |
| Qt::PenStyle m_penStyle = Qt::SolidLine; |
| Qt::PenCapStyle m_penCapStyle = Qt::SquareCap; |
| }; |
| |
| class Q_LOCATION_PRIVATE_EXPORT QDeclarativePolylineMapItemPrivateCPU: public QDeclarativePolylineMapItemPrivate |
| { |
| public: |
| QDeclarativePolylineMapItemPrivateCPU(QDeclarativePolylineMapItem &poly) : QDeclarativePolylineMapItemPrivate(poly) |
| { |
| } |
| |
| QDeclarativePolylineMapItemPrivateCPU(QDeclarativePolylineMapItemPrivate &other) |
| : QDeclarativePolylineMapItemPrivate(other) |
| { |
| } |
| |
| ~QDeclarativePolylineMapItemPrivateCPU() override; |
| void onLinePropertiesChanged() override |
| { |
| // mark dirty just in case we're a width change |
| markSourceDirtyAndUpdate(); |
| } |
| void markSourceDirtyAndUpdate() override |
| { |
| m_geometry.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_geopath.size()); |
| for (const QGeoCoordinate &c : m_poly.m_geopath.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_geopath.path().last()); |
| } |
| void preserveGeometry() |
| { |
| m_geometry.setPreserveGeometry(true, m_poly.m_geopath.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_geopath.path().length() < 2) { // Possibly cleared |
| m_geometry.clear(); |
| m_poly.setWidth(0); |
| m_poly.setHeight(0); |
| return; |
| } |
| QScopedValueRollback<bool> rollback(m_poly.m_updatingGeometry); |
| m_poly.m_updatingGeometry = true; |
| |
| const QGeoMap *map = m_poly.map(); |
| const qreal borderWidth = m_poly.m_line.width(); |
| |
| m_geometry.updateSourcePoints(*map, m_geopathProjected, m_poly.m_geopath.boundingGeoRectangle().topLeft()); |
| m_geometry.updateScreenPoints(*map, borderWidth); |
| |
| m_poly.setWidth(m_geometry.sourceBoundingBox().width() + borderWidth); |
| m_poly.setHeight(m_geometry.sourceBoundingBox().height() + borderWidth); |
| |
| m_poly.setPositionOnMap(m_geometry.origin(), -1 * m_geometry.sourceBoundingBox().topLeft() |
| + QPointF(borderWidth, borderWidth) * 0.5 ); // it has to be shifted so that the center of the line is on the correct geocoord |
| } |
| QSGNode *updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData * /*data*/) override |
| { |
| if (!m_node || !oldNode) { |
| m_node = new MapPolylineNode(); |
| if (oldNode) { |
| delete oldNode; |
| oldNode = nullptr; |
| } |
| } else { |
| m_node = static_cast<MapPolylineNode *>(oldNode); |
| } |
| |
| //TODO: update only material |
| if (m_geometry.isScreenDirty() || m_poly.m_dirtyMaterial || !oldNode) { |
| m_node->update(m_poly.m_line.color(), &m_geometry); |
| m_geometry.setPreserveGeometry(false); |
| m_geometry.markClean(); |
| m_poly.m_dirtyMaterial = false; |
| } |
| return m_node; |
| } |
| bool contains(const QPointF &point) const override |
| { |
| return m_geometry.contains(point); |
| } |
| |
| QList<QDoubleVector2D> m_geopathProjected; |
| QGeoMapPolylineGeometry m_geometry; |
| MapPolylineNode *m_node = nullptr; |
| }; |
| |
| class Q_LOCATION_PRIVATE_EXPORT QDeclarativePolylineMapItemPrivateOpenGLLineStrip: public QDeclarativePolylineMapItemPrivate |
| { |
| public: |
| |
| QDeclarativePolylineMapItemPrivateOpenGLLineStrip(QDeclarativePolylineMapItem &poly) : QDeclarativePolylineMapItemPrivate(poly) |
| { |
| } |
| |
| QDeclarativePolylineMapItemPrivateOpenGLLineStrip(QDeclarativePolylineMapItemPrivate &other) |
| : QDeclarativePolylineMapItemPrivate(other) |
| { |
| } |
| |
| ~QDeclarativePolylineMapItemPrivateOpenGLLineStrip() override; |
| void onLinePropertiesChanged() override |
| { |
| afterViewportChanged(); |
| } |
| void markSourceDirtyAndUpdate() override |
| { |
| m_geometry.markSourceDirty(); |
| m_poly.polishAndUpdate(); |
| } |
| void preserveGeometry() |
| { |
| m_geometry.setPreserveGeometry(true, m_poly.m_geopath.boundingGeoRectangle().topLeft()); |
| } |
| void onMapSet() override |
| { |
| markSourceDirtyAndUpdate(); |
| } |
| void onGeoGeometryChanged() override |
| { |
| preserveGeometry(); |
| markSourceDirtyAndUpdate(); |
| } |
| void onGeoGeometryUpdated() override |
| { |
| preserveGeometry(); |
| markSourceDirtyAndUpdate(); |
| } |
| void onItemGeometryChanged() override |
| { |
| onGeoGeometryChanged(); |
| } |
| void afterViewportChanged() override |
| { |
| preserveGeometry(); |
| m_poly.polishAndUpdate(); |
| } |
| bool contains(const QPointF &point) const override |
| { |
| return m_geometry.contains(m_poly.mapToItem(m_poly.quickMap(), point), |
| m_poly.line()->width(), |
| static_cast<const QGeoProjectionWebMercator&>(m_poly.map()->geoProjection())); |
| } |
| void updatePolish() override |
| { |
| if (m_poly.m_geopath.path().length() == 0) { // Possibly cleared |
| m_geometry.clear(); |
| m_geometry.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_line.width(); |
| m_geometry.updateSourcePoints(*m_poly.map(), m_poly.m_geopath); |
| m_geometry.markScreenDirty(); |
| m_geometry.updateScreenPoints(*m_poly.map(), lineWidth); |
| |
| m_poly.setWidth(m_geometry.sourceBoundingBox().width()); |
| m_poly.setHeight(m_geometry.sourceBoundingBox().height()); |
| m_poly.setPosition(1.0 * m_geometry.firstPointOffset() - QPointF(lineWidth * 0.5,lineWidth * 0.5)); |
| } |
| QSGNode * updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) override |
| { |
| Q_UNUSED(data); |
| |
| if (!m_node || !oldNode) { |
| m_node = new MapPolylineNodeOpenGLLineStrip(); |
| if (oldNode) |
| delete oldNode; |
| } else { |
| m_node = static_cast<MapPolylineNodeOpenGLLineStrip *>(oldNode); |
| } |
| |
| if (m_geometry.isScreenDirty() || m_poly.m_dirtyMaterial) { |
| const QGeoMap *map = m_poly.map(); |
| const QMatrix4x4 &combinedMatrix = map->geoProjection().qsgTransform(); |
| const QDoubleVector3D &cameraCenter = map->geoProjection().centerMercator(); |
| m_node->update(m_poly.m_line.color(), // This updates only the material if the geometry is unchanged |
| m_poly.m_line.width(), |
| &m_geometry, |
| combinedMatrix, |
| cameraCenter); |
| m_geometry.setPreserveGeometry(false); |
| m_geometry.markClean(); |
| m_poly.m_dirtyMaterial = false; |
| } |
| return m_node; |
| } |
| |
| QGeoMapPolylineGeometryOpenGL m_geometry; |
| MapPolylineNodeOpenGLLineStrip *m_node = nullptr; |
| }; |
| |
| class Q_LOCATION_PRIVATE_EXPORT QDeclarativePolylineMapItemPrivateOpenGLExtruded: public QDeclarativePolylineMapItemPrivateOpenGLLineStrip |
| { |
| public: |
| |
| QDeclarativePolylineMapItemPrivateOpenGLExtruded(QDeclarativePolylineMapItem &poly) |
| : QDeclarativePolylineMapItemPrivateOpenGLLineStrip(poly) |
| { |
| } |
| |
| QDeclarativePolylineMapItemPrivateOpenGLExtruded(QDeclarativePolylineMapItemPrivate &other) |
| : QDeclarativePolylineMapItemPrivateOpenGLLineStrip(other) |
| { |
| } |
| |
| ~QDeclarativePolylineMapItemPrivateOpenGLExtruded() override; |
| |
| QSGNode * updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) override |
| { |
| Q_UNUSED(data); |
| const QGeoMap *map = m_poly.map(); |
| const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map->geoProjection()); |
| const QMatrix4x4 &combinedMatrix = p.qsgTransform(); |
| const QDoubleVector3D &cameraCenter = p.centerMercator(); |
| const QColor &color = m_poly.m_line.color(); |
| const float lineWidth = m_poly.m_line.width(); |
| |
| MapPolylineNodeOpenGLExtruded *nodeTri = nullptr; |
| if (!m_nodeTri || !oldNode) { |
| if (oldNode) |
| delete oldNode; |
| nodeTri = new MapPolylineNodeOpenGLExtruded(); |
| } else { |
| nodeTri = static_cast<MapPolylineNodeOpenGLExtruded *>(oldNode); |
| } |
| |
| //TODO: update only material |
| if (m_geometry.isScreenDirty() || m_poly.m_dirtyMaterial) { |
| nodeTri->update(color, |
| lineWidth , |
| &m_geometry, |
| combinedMatrix, |
| cameraCenter, |
| m_penCapStyle, |
| false, |
| m_poly.zoomForLOD(int(map->cameraData().zoomLevel()))); |
| m_geometry.setPreserveGeometry(false); |
| m_geometry.markClean(); |
| m_poly.m_dirtyMaterial = false; |
| } |
| m_nodeTri = nodeTri; |
| return nodeTri; |
| } |
| |
| MapPolylineNodeOpenGLExtruded *m_nodeTri = nullptr; |
| }; |
| |
| QT_END_NAMESPACE |
| |
| #endif // QDECLARATIVEPOLYLINEMAPITEM_P_P_H |