From 36d35682a2002c3affb4550a5f5c1f2fa7553ab5 Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Wed, 5 Oct 2022 00:08:40 +0200 Subject: Move the RHI implementation into separate folder Makes it easier to work with the code, the implementations have little in common that is not anyway abstracted into the common private base class. Change-Id: I4d957833f5a881e1b445ff6e912ffa036d10321a Reviewed-by: Ivan Solovev (cherry picked from commit 26a2e42c34b0effac4ffcba59a07df238980282e) Reviewed-by: Volker Hilsheimer --- src/location/CMakeLists.txt | 26 +- .../quickmapitems/qdeclarativecirclemapitem.cpp | 5 +- .../quickmapitems/qdeclarativecirclemapitem_p_p.h | 169 ----- .../quickmapitems/qdeclarativepolygonmapitem.cpp | 391 +---------- .../quickmapitems/qdeclarativepolygonmapitem_p_p.h | 326 +-------- .../quickmapitems/qdeclarativepolylinemapitem.cpp | 711 +------------------- .../qdeclarativepolylinemapitem_p_p.h | 508 +------------- .../quickmapitems/qdeclarativerectanglemapitem.cpp | 3 +- .../qdeclarativerectanglemapitem_p_p.h | 167 ----- src/location/quickmapitems/qgeosimplify.cpp | 167 ----- src/location/quickmapitems/qgeosimplify_p.h | 75 --- .../rhi/qdeclarativecirclemapitem_rhi.cpp | 158 +++++ .../rhi/qdeclarativecirclemapitem_rhi_p.h | 133 ++++ .../rhi/qdeclarativepolygonmapitem_rhi.cpp | 303 +++++++++ .../rhi/qdeclarativepolygonmapitem_rhi_p.h | 243 +++++++ .../rhi/qdeclarativepolylinemapitem_rhi.cpp | 517 +++++++++++++++ .../rhi/qdeclarativepolylinemapitem_rhi_p.h | 333 ++++++++++ .../rhi/qdeclarativerectanglemapitem_rhi.cpp | 159 +++++ .../rhi/qdeclarativerectanglemapitem_rhi_p.h | 131 ++++ .../quickmapitems/rhi/qgeomapitemgeometry_rhi.cpp | 729 +++++++++++++++++++++ .../quickmapitems/rhi/qgeomapitemgeometry_rhi_p.h | 221 +++++++ src/location/quickmapitems/rhi/qgeosimplify.cpp | 167 +++++ src/location/quickmapitems/rhi/qgeosimplify_p.h | 75 +++ .../quickmapitems/rhi/shaders/polygon.frag | 16 + .../quickmapitems/rhi/shaders/polygon.vert | 20 + .../rhi/shaders/polyline_extruded.frag | 20 + .../rhi/shaders/polyline_extruded.vert | 104 +++ .../rhi/shaders/polyline_linestrip.frag | 17 + .../rhi/shaders/polyline_linestrip.vert | 21 + src/location/quickmapitems/shaders/polygon.frag | 16 - src/location/quickmapitems/shaders/polygon.vert | 20 - .../quickmapitems/shaders/polyline_extruded.frag | 20 - .../quickmapitems/shaders/polyline_extruded.vert | 104 --- .../quickmapitems/shaders/polyline_linestrip.frag | 17 - .../quickmapitems/shaders/polyline_linestrip.vert | 21 - 35 files changed, 3391 insertions(+), 2722 deletions(-) delete mode 100644 src/location/quickmapitems/qgeosimplify.cpp delete mode 100644 src/location/quickmapitems/qgeosimplify_p.h create mode 100644 src/location/quickmapitems/rhi/qdeclarativecirclemapitem_rhi.cpp create mode 100644 src/location/quickmapitems/rhi/qdeclarativecirclemapitem_rhi_p.h create mode 100644 src/location/quickmapitems/rhi/qdeclarativepolygonmapitem_rhi.cpp create mode 100644 src/location/quickmapitems/rhi/qdeclarativepolygonmapitem_rhi_p.h create mode 100644 src/location/quickmapitems/rhi/qdeclarativepolylinemapitem_rhi.cpp create mode 100644 src/location/quickmapitems/rhi/qdeclarativepolylinemapitem_rhi_p.h create mode 100644 src/location/quickmapitems/rhi/qdeclarativerectanglemapitem_rhi.cpp create mode 100644 src/location/quickmapitems/rhi/qdeclarativerectanglemapitem_rhi_p.h create mode 100644 src/location/quickmapitems/rhi/qgeomapitemgeometry_rhi.cpp create mode 100644 src/location/quickmapitems/rhi/qgeomapitemgeometry_rhi_p.h create mode 100644 src/location/quickmapitems/rhi/qgeosimplify.cpp create mode 100644 src/location/quickmapitems/rhi/qgeosimplify_p.h create mode 100644 src/location/quickmapitems/rhi/shaders/polygon.frag create mode 100644 src/location/quickmapitems/rhi/shaders/polygon.vert create mode 100644 src/location/quickmapitems/rhi/shaders/polyline_extruded.frag create mode 100644 src/location/quickmapitems/rhi/shaders/polyline_extruded.vert create mode 100644 src/location/quickmapitems/rhi/shaders/polyline_linestrip.frag create mode 100644 src/location/quickmapitems/rhi/shaders/polyline_linestrip.vert delete mode 100644 src/location/quickmapitems/shaders/polygon.frag delete mode 100644 src/location/quickmapitems/shaders/polygon.vert delete mode 100644 src/location/quickmapitems/shaders/polyline_extruded.frag delete mode 100644 src/location/quickmapitems/shaders/polyline_extruded.vert delete mode 100644 src/location/quickmapitems/shaders/polyline_linestrip.frag delete mode 100644 src/location/quickmapitems/shaders/polyline_linestrip.vert diff --git a/src/location/CMakeLists.txt b/src/location/CMakeLists.txt index 341129c0..4db9c43b 100644 --- a/src/location/CMakeLists.txt +++ b/src/location/CMakeLists.txt @@ -82,13 +82,13 @@ qt_internal_add_module(Location declarativemaps/qdeclarativegeocodemodel.cpp declarativemaps/qdeclarativegeocodemodel_p.h declarativemaps/qdeclarativegeoroute.cpp declarativemaps/qdeclarativegeoroute_p.h declarativemaps/qdeclarativegeoroutemodel.cpp declarativemaps/qdeclarativegeoroutemodel_p.h + declarativemaps/qdeclarativegeoroutesegment.cpp declarativemaps/qdeclarativegeoroutesegment_p.h quickmapitems/qgeomapitemgeometry.cpp quickmapitems/qgeomapitemgeometry_p.h quickmapitems/qdeclarativegeomap_p.h quickmapitems/qdeclarativegeomap.cpp quickmapitems/qdeclarativegeomapitembase_p.h quickmapitems/qdeclarativegeomapitembase.cpp quickmapitems/qdeclarativegeomapitemview_p.h quickmapitems/qdeclarativegeomapitemview.cpp - quickmapitems/qgeosimplify.cpp quickmapitems/qgeosimplify_p.h quickmapitems/qdeclarativegeomapitemutils.cpp quickmapitems/qdeclarativegeomapitemutils_p.h quickmapitems/qdeclarativegeomapquickitem_p.h quickmapitems/qdeclarativegeomapquickitem.cpp @@ -111,8 +111,16 @@ qt_internal_add_module(Location quickmapitems/qdeclarativegeomapcopyrightsnotice.cpp quickmapitems/qdeclarativegeomapitemtransitionmanager_p.h quickmapitems/qdeclarativegeomapitemtransitionmanager.cpp - declarativemaps/qdeclarativegeoroutesegment.cpp - declarativemaps/qdeclarativegeoroutesegment_p.h + quickmapitems/rhi/qdeclarativepolygonmapitem_rhi_p.h + quickmapitems/rhi/qdeclarativepolygonmapitem_rhi.cpp + quickmapitems/rhi/qdeclarativepolylinemapitem_rhi_p.h + quickmapitems/rhi/qdeclarativepolylinemapitem_rhi.cpp + quickmapitems/rhi/qdeclarativerectanglemapitem_rhi_p.h + quickmapitems/rhi/qdeclarativerectanglemapitem_rhi.cpp + quickmapitems/rhi/qdeclarativecirclemapitem_rhi_p.h + quickmapitems/rhi/qdeclarativecirclemapitem_rhi.cpp + quickmapitems/rhi/qgeomapitemgeometry_rhi_p.h quickmapitems/rhi/qgeomapitemgeometry_rhi.cpp + quickmapitems/rhi/qgeosimplify.cpp quickmapitems/rhi/qgeosimplify_p.h declarativeplaces/qdeclarativecategory.cpp declarativeplaces/qdeclarativecategory_p.h declarativeplaces/qdeclarativecontactdetail.cpp @@ -183,12 +191,12 @@ qt_internal_add_shaders(Location "declarative_location_shaders" PREFIX "/location" FILES - "quickmapitems/shaders/polyline_linestrip.vert" - "quickmapitems/shaders/polyline_linestrip.frag" - "quickmapitems/shaders/polyline_extruded.vert" - "quickmapitems/shaders/polyline_extruded.frag" - "quickmapitems/shaders/polygon.vert" - "quickmapitems/shaders/polygon.frag" + "quickmapitems/rhi/shaders/polyline_linestrip.vert" + "quickmapitems/rhi/shaders/polyline_linestrip.frag" + "quickmapitems/rhi/shaders/polyline_extruded.vert" + "quickmapitems/rhi/shaders/polyline_extruded.frag" + "quickmapitems/rhi/shaders/polygon.vert" + "quickmapitems/rhi/shaders/polygon.frag" ) qt_internal_add_docs(Location diff --git a/src/location/quickmapitems/qdeclarativecirclemapitem.cpp b/src/location/quickmapitems/qdeclarativecirclemapitem.cpp index 8f0ba330..927ce6ae 100644 --- a/src/location/quickmapitems/qdeclarativecirclemapitem.cpp +++ b/src/location/quickmapitems/qdeclarativecirclemapitem.cpp @@ -38,12 +38,11 @@ ****************************************************************************/ #include "qdeclarativecirclemapitem_p.h" -#include "qdeclarativepolygonmapitem_p.h" #include "qdeclarativecirclemapitem_p_p.h" +#include "rhi/qdeclarativecirclemapitem_rhi_p.h" #include #include -#include #include #include @@ -561,8 +560,6 @@ QDeclarativeCircleMapItemPrivate::~QDeclarativeCircleMapItemPrivate() {} QDeclarativeCircleMapItemPrivateCPU::~QDeclarativeCircleMapItemPrivateCPU() {} -QDeclarativeCircleMapItemPrivateOpenGL::~QDeclarativeCircleMapItemPrivateOpenGL() {} - bool QDeclarativeCircleMapItemPrivate::preserveCircleGeometry (QList &path, const QGeoCoordinate ¢er, qreal distance, const QGeoProjectionWebMercator &p) { diff --git a/src/location/quickmapitems/qdeclarativecirclemapitem_p_p.h b/src/location/quickmapitems/qdeclarativecirclemapitem_p_p.h index 49677e2a..319ff635 100644 --- a/src/location/quickmapitems/qdeclarativecirclemapitem_p_p.h +++ b/src/location/quickmapitems/qdeclarativecirclemapitem_p_p.h @@ -172,175 +172,6 @@ public: MapPolygonNode *m_node = nullptr; }; -class Q_LOCATION_PRIVATE_EXPORT QDeclarativeCircleMapItemPrivateOpenGL: public QDeclarativeCircleMapItemPrivate -{ -public: - QDeclarativeCircleMapItemPrivateOpenGL(QDeclarativeCircleMapItem &circle) : QDeclarativeCircleMapItemPrivate(circle) - { - } - - QDeclarativeCircleMapItemPrivateOpenGL(QDeclarativeCircleMapItemPrivate &other) - : QDeclarativeCircleMapItemPrivate(other) - { - } - - ~QDeclarativeCircleMapItemPrivateOpenGL() override; - - void onLinePropertiesChanged() override - { - m_circle.m_dirtyMaterial = true; - afterViewportChanged(); - } - void markScreenDirtyAndUpdate() - { - // preserveGeometry is cleared in updateMapItemPaintNode - m_geometry.markScreenDirty(); - m_borderGeometry.markScreenDirty(); - m_circle.polishAndUpdate(); - } - void markSourceDirtyAndUpdate() override - { - updateCirclePath(); - preserveGeometry(); - m_geometry.markSourceDirty(); - m_borderGeometry.markSourceDirty(); - m_circle.polishAndUpdate(); - } - void preserveGeometry() - { - m_geometry.setPreserveGeometry(true, m_leftBound); - m_borderGeometry.setPreserveGeometry(true, m_leftBound); - } - void onMapSet() override - { - markSourceDirtyAndUpdate(); - } - void onGeoGeometryChanged() override - { - - markSourceDirtyAndUpdate(); - } - void onItemGeometryChanged() override - { - onGeoGeometryChanged(); - } - void afterViewportChanged() override - { - preserveGeometry(); - markScreenDirtyAndUpdate(); - } - void updatePolish() override - { - if (m_circle.m_circle.isEmpty()) { - m_geometry.clear(); - m_borderGeometry.clear(); - m_circle.setWidth(0); - m_circle.setHeight(0); - return; - } - - QScopedValueRollback rollback(m_circle.m_updatingGeometry); - m_circle.m_updatingGeometry = true; - const qreal lineWidth = m_circle.m_border.width(); - const QColor &lineColor = m_circle.m_border.color(); - const QColor &fillColor = m_circle.color(); - if (fillColor.alpha() != 0) { - m_geometry.updateSourcePoints(*m_circle.map(), m_circlePath); - m_geometry.markScreenDirty(); - m_geometry.updateScreenPoints(*m_circle.map(), lineWidth, lineColor); - } else { - m_geometry.clearBounds(); - } - - QGeoMapItemGeometry * geom = &m_geometry; - m_borderGeometry.clearScreen(); - if (lineColor.alpha() != 0 && lineWidth > 0) { - m_borderGeometry.updateSourcePoints(*m_circle.map(), m_circle.m_circle); - m_borderGeometry.markScreenDirty(); - m_borderGeometry.updateScreenPoints(*m_circle.map(), lineWidth); - geom = &m_borderGeometry; - } - m_circle.setWidth(geom->sourceBoundingBox().width()); - m_circle.setHeight(geom->sourceBoundingBox().height()); - m_circle.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 QDeclarativePolygonMapItemPrivateOpenGL::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(oldNode); - } - - const QGeoMap *map = m_circle.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_circle.m_border.color(), - float(m_circle.m_border.width()), - &m_borderGeometry, - combinedMatrix, - cameraCenter, - Qt::SquareCap, - true, - 30); // No LOD for circles - m_borderGeometry.setPreserveGeometry(false); - m_borderGeometry.markClean(); - } else { - m_polylinenode->setSubtreeBlocked(true); - } - if (m_geometry.isScreenDirty()) { - m_node->update(m_circle.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_circle.m_border.width(); - const QColor &lineColor = m_circle.m_border.color(); - const QRectF &bounds = (lineColor.alpha() != 0 && lineWidth > 0) ? m_borderGeometry.sourceBoundingBox() : m_geometry.sourceBoundingBox(); - if (bounds.contains(point)) { - QDeclarativeGeoMap *m = m_circle.quickMap(); - if (m) { - const QGeoCoordinate crd = m->toCoordinate(m->mapFromItem(&m_circle, point)); - return m_circle.m_circle.contains(crd) || m_borderGeometry.contains(m_circle.mapToItem(m_circle.quickMap(), point), - m_circle.border()->width(), - static_cast(m_circle.map()->geoProjection())); - } else { - return true; - } - } - return false; - } - - QGeoMapPolygonGeometryOpenGL m_geometry; - QGeoMapPolylineGeometryOpenGL m_borderGeometry; - QDeclarativePolygonMapItemPrivateOpenGL::RootNode *m_rootNode = nullptr; - MapPolygonNodeGL *m_node = nullptr; - MapPolylineNodeOpenGLExtruded *m_polylinenode = nullptr; -}; - QT_END_NAMESPACE #endif // QDECLARATIVECIRCLEMAPITEM_P_P_H diff --git a/src/location/quickmapitems/qdeclarativepolygonmapitem.cpp b/src/location/quickmapitems/qdeclarativepolygonmapitem.cpp index 3ef35bb5..eb99a264 100644 --- a/src/location/quickmapitems/qdeclarativepolygonmapitem.cpp +++ b/src/location/quickmapitems/qdeclarativepolygonmapitem.cpp @@ -39,20 +39,14 @@ #include "qdeclarativegeomapitemutils_p.h" #include "qdeclarativepolygonmapitem_p.h" -#include "qdeclarativepolylinemapitem_p_p.h" #include "qdeclarativepolygonmapitem_p_p.h" -#include "qdeclarativerectanglemapitem_p_p.h" -#include "error_messages_p.h" +#include "rhi/qdeclarativepolygonmapitem_rhi_p.h" #include "locationvaluetypehelper_p.h" #include #include -#include #include -#include -#include -#include #include #include #include @@ -428,267 +422,6 @@ bool QDeclarativePolygonMapItemPrivateCPU::contains(const QPointF &point) const return (m_geometry.contains(point) || m_borderGeometry.contains(point)); } - -QGeoMapPolygonGeometryOpenGL::QGeoMapPolygonGeometryOpenGL() -{ -} - -void QGeoMapPolygonGeometryOpenGL::updateSourcePoints(const QGeoMap &map, const QList &path) -{ - QList geopath; - for (const auto &c: path) - geopath.append(QWebMercator::mercatorToCoord(c)); - updateSourcePoints(map, geopath); -} - -// wrapPath always preserves the geometry -// This one handles holes -static void wrapPath(const QGeoPolygon &poly - ,const QGeoCoordinate &geoLeftBound - ,const QGeoProjectionWebMercator &p - ,QList > &wrappedPaths - ,QDoubleVector2D *leftBoundWrapped = nullptr) -{ - QList > paths; - for (qsizetype i = 0; i < 1 + poly.holesCount(); ++i) { - QList path; - if (!i) { - for (const QGeoCoordinate &c : poly.perimeter()) - path << p.geoToMapProjection(c); - } else { - for (const QGeoCoordinate &c : poly.holePath(i-1)) - path << p.geoToMapProjection(c); - } - paths.append(path); - } - - const QDoubleVector2D leftBound = p.geoToMapProjection(geoLeftBound); - wrappedPaths.clear(); - - QList wrappedPath; - // compute 3 sets of "wrapped" coordinates: one w regular mercator, one w regular mercator +- 1.0 - for (const auto &path : paths) { - wrappedPath.clear(); - for (QDoubleVector2D coord : path) { - // We can get NaN if the map isn't set up correctly, or the projection - // is faulty -- probably best thing to do is abort - if (!qIsFinite(coord.x()) || !qIsFinite(coord.y())) { - wrappedPaths.clear(); - return; - } - - const bool isPointLessThanUnwrapBelowX = (coord.x() < leftBound.x()); - // unwrap x to preserve geometry if moved to border of map - if (isPointLessThanUnwrapBelowX) - coord.setX(coord.x() + 1.0); - wrappedPath.append(coord); - } - wrappedPaths.append(wrappedPath); - } - - if (leftBoundWrapped) - *leftBoundWrapped = leftBound; -} - -static void cutPathEars(const QList> &wrappedPaths, - QList &screenVertices, - QList &screenIndices) -{ - using Coord = double; - using N = uint32_t; - using Point = std::array; - screenVertices.clear(); - screenIndices.clear(); - - std::vector> polygon; - std::vector poly; - - for (const QList &wrappedPath: wrappedPaths) { - poly.clear(); - for (const QDoubleVector2D &v: wrappedPath) { - screenVertices << v; - Point pt = {{ v.x(), v.y() }}; - poly.push_back( pt ); - } - polygon.push_back(poly); - } - - std::vector indices = qt_mapbox::earcut(polygon); - - for (const auto &i: indices) - screenIndices << quint32(i); -} - -static void cutPathEars(const QList &wrappedPath, - QList &screenVertices, - QList &screenIndices) -{ - using Coord = double; - using N = uint32_t; - using Point = std::array; - screenVertices.clear(); - screenIndices.clear(); - - std::vector> polygon; - std::vector poly; - - for (const QDoubleVector2D &v: wrappedPath) { - screenVertices << v; - Point pt = {{ v.x(), v.y() }}; - poly.push_back( pt ); - } - polygon.push_back(poly); - - std::vector indices = qt_mapbox::earcut(polygon); - - for (const auto &i: indices) - screenIndices << quint32(i); -} - -/*! - \internal -*/ -// This one does only a perimeter -void QGeoMapPolygonGeometryOpenGL::updateSourcePoints(const QGeoMap &map, - const QList &perimeter) -{ - if (!sourceDirty_) - return; - const QGeoProjectionWebMercator &p = static_cast(map.geoProjection()); - - // build the actual path - // The approach is the same as described in QGeoMapPolylineGeometry::updateSourcePoints - srcOrigin_ = geoLeftBound_; - - QDoubleVector2D leftBoundWrapped; - // 1) pre-compute 3 sets of "wrapped" coordinates: one w regular mercator, one w regular mercator +- 1.0 - QList wrappedPath; - QDeclarativeGeoMapItemUtils::wrapPath(perimeter, geoLeftBound_, p, - wrappedPath, &leftBoundWrapped); - - // 1.1) do the same for the bbox - QList wrappedBbox, wrappedBboxPlus1, wrappedBboxMinus1; - QGeoPolygon bbox(QGeoPath(perimeter).boundingGeoRectangle()); - QDeclarativeGeoMapItemUtils::wrapPath(bbox.perimeter(), bbox.boundingGeoRectangle().topLeft(), p, - wrappedBbox, wrappedBboxMinus1, wrappedBboxPlus1, &m_bboxLeftBoundWrapped); - - // 2) Store the triangulated polygon, and the wrapped bbox paths. - // the triangulations can be used as they are, as they "bypass" the QtQuick display chain - // the bbox wraps have to be however clipped, and then projected, in order to figure out the geometry. - // Note that this might still cause the geometryChange method to fail under some extreme conditions. - cutPathEars(wrappedPath, m_screenVertices, m_screenIndices); - - m_wrappedPolygons.resize(3); - m_wrappedPolygons[0].wrappedBboxes = wrappedBboxMinus1; - m_wrappedPolygons[1].wrappedBboxes = wrappedBbox; - m_wrappedPolygons[2].wrappedBboxes = wrappedBboxPlus1; -} - -// This one handles whole QGeoPolygon w. holes -void QGeoMapPolygonGeometryOpenGL::updateSourcePoints(const QGeoMap &map, const QGeoPolygon &poly) -{ - if (!sourceDirty_) - return; - const QGeoProjectionWebMercator &p = static_cast(map.geoProjection()); - - // build the actual path - // The approach is the same as described in QGeoMapPolylineGeometry::updateSourcePoints - srcOrigin_ = geoLeftBound_; - - QDoubleVector2D leftBoundWrapped; - QList> wrappedPath; - // 1) pre-compute 3 sets of "wrapped" coordinates: one w regular mercator, one w regular mercator +- 1.0 - wrapPath(poly, geoLeftBound_, p, - wrappedPath, &leftBoundWrapped); - - // 1.1) do the same for the bbox - QList wrappedBbox, wrappedBboxPlus1, wrappedBboxMinus1; - QGeoPolygon bbox(poly.boundingGeoRectangle()); - QDeclarativeGeoMapItemUtils::wrapPath(bbox.perimeter(), bbox.boundingGeoRectangle().topLeft(), p, - wrappedBbox, wrappedBboxMinus1, wrappedBboxPlus1, &m_bboxLeftBoundWrapped); - - // 2) Store the triangulated polygon, and the wrapped bbox paths. - // the triangulations can be used as they are, as they "bypass" the QtQuick display chain - // the bbox wraps have to be however clipped, and then projected, in order to figure out the geometry. - // Note that this might still cause the geometryChange method to fail under some extreme conditions. - cutPathEars(wrappedPath, m_screenVertices, m_screenIndices); - m_wrappedPolygons.resize(3); - m_wrappedPolygons[0].wrappedBboxes = wrappedBboxMinus1; - m_wrappedPolygons[1].wrappedBboxes = wrappedBbox; - m_wrappedPolygons[2].wrappedBboxes = wrappedBboxPlus1; -} - -void QGeoMapPolygonGeometryOpenGL::updateSourcePoints(const QGeoMap &map, const QGeoRectangle &rect) -{ - if (!sourceDirty_) - return; - const QList perimeter = QGeoMapItemGeometry::path(rect); - updateSourcePoints(map, perimeter); -} - -/*! - \internal -*/ -void QGeoMapPolygonGeometryOpenGL::updateScreenPoints(const QGeoMap &map, qreal strokeWidth , const QColor &strokeColor) -{ - if (map.viewportWidth() == 0 || map.viewportHeight() == 0) { - clear(); - return; - } - - // 1) identify which set to use: std, +1 or -1 - const QGeoProjectionWebMercator &p = static_cast(map.geoProjection()); - const QDoubleVector2D leftBoundMercator = p.geoToMapProjection(srcOrigin_); - m_wrapOffset = p.projectionWrapFactor(leftBoundMercator) + 1; // +1 to get the offset into QLists - - // 1.1) select geometry set - // This could theoretically be skipped for those polygons whose bbox is not even projectable. - // However, such optimization could only be introduced if not calculating bboxes lazily. - // Hence not doing it. - if (sourceDirty_) { - m_dataChanged = true; - } - - if (strokeWidth == 0.0 || strokeColor.alpha() == 0) // or else the geometry of the border is used, so no point in calculating 2 of them - updateQuickGeometry(p, strokeWidth); -} - -void QGeoMapPolygonGeometryOpenGL::updateQuickGeometry(const QGeoProjectionWebMercator &p, qreal /*strokeWidth*/) -{ - // 2) clip bbox - // BBox handling -- this is related to the bounding box geometry - // that has to inevitably follow the old projection codepath - // As it needs to provide projected coordinates for QtQuick interaction. - // This could be futher optimized to be updated in a lazy fashion. - const QList &wrappedBbox = m_wrappedPolygons.at(m_wrapOffset).wrappedBboxes; - QList > clippedBbox; - QDoubleVector2D bboxLeftBoundWrapped = m_bboxLeftBoundWrapped; - bboxLeftBoundWrapped.setX(bboxLeftBoundWrapped.x() + double(m_wrapOffset - 1)); - QDeclarativeGeoMapItemUtils::clipPolygon(wrappedBbox, p, clippedBbox, &bboxLeftBoundWrapped); - - // 3) project bbox - QPainterPath ppi; - if (!clippedBbox.size() || clippedBbox.first().size() < 3) { - sourceBounds_ = screenBounds_ = QRectF(); - firstPointOffset_ = QPointF(); - screenOutline_ = ppi; - return; - } - - QDeclarativeGeoMapItemUtils::projectBbox(clippedBbox.first(), p, ppi); // Using first because a clipped box should always result in one polygon - const QRectF brect = ppi.boundingRect(); - firstPointOffset_ = QPointF(brect.topLeft()); - screenOutline_ = ppi; - - // 4) Set Screen bbox - screenBounds_ = brect; - sourceBounds_.setX(0); - sourceBounds_.setY(0); - sourceBounds_.setWidth(brect.width()); - sourceBounds_.setHeight(brect.height()); -} - -QDeclarativePolygonMapItemPrivateOpenGL::~QDeclarativePolygonMapItemPrivateOpenGL() {} /* * QDeclarativePolygonMapItem Implementation */ @@ -986,26 +719,6 @@ void QDeclarativePolygonMapItem::geometryChange(const QRectF &newGeometry, const ////////////////////////////////////////////////////////////////////// -QSGMaterialShader *MapPolygonMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const -{ - Q_UNUSED(renderMode); - return new MapPolygonShader(); -} - -int MapPolygonMaterial::compare(const QSGMaterial *other) const -{ - const MapPolygonMaterial &o = *static_cast(other); - if (o.m_center == m_center && o.m_geoProjection == m_geoProjection && o.m_wrapOffset == m_wrapOffset) - return QSGFlatColorMaterial::compare(other); - return -1; -} - -QSGMaterialType *MapPolygonMaterial::type() const -{ - static QSGMaterialType type; - return &type; -} - MapPolygonNode::MapPolygonNode() : border_(new MapPolylineNode()), geometry_(QSGGeometry::defaultAttributes_Point2D(), 0) @@ -1056,106 +769,4 @@ void MapPolygonNode::update(const QColor &fillColor, const QColor &borderColor, } } -MapPolygonNodeGL::MapPolygonNodeGL() : - //fill_material_(this), - fill_material_(), - geometry_(QSGGeometry::defaultAttributes_Point2D(), 0) -{ - geometry_.setDrawingMode(QSGGeometry::DrawTriangles); - QSGGeometryNode::setMaterial(&fill_material_); - QSGGeometryNode::setGeometry(&geometry_); -} - -MapPolygonNodeGL::~MapPolygonNodeGL() -{ -} - -/*! - \internal -*/ -void MapPolygonNodeGL::update(const QColor &fillColor, - const QGeoMapPolygonGeometryOpenGL *fillShape, - const QMatrix4x4 &geoProjection, - const QDoubleVector3D ¢er) -{ - if (fillShape->m_screenIndices.size() < 3 || fillColor.alpha() == 0) { - setSubtreeBlocked(true); - return; - } - setSubtreeBlocked(false); - - QSGGeometry *fill = QSGGeometryNode::geometry(); - if (fillShape->m_dataChanged || !fill->vertexCount()) { - fillShape->allocateAndFillPolygon(fill); - markDirty(DirtyGeometry); - fillShape->m_dataChanged = false; - } - - //if (fillColor != fill_material_.color()) // Any point in optimizing this? - { - fill_material_.setColor(fillColor); - fill_material_.setGeoProjection(geoProjection); - fill_material_.setCenter(center); - fill_material_.setWrapOffset(fillShape->m_wrapOffset - 1); - setMaterial(&fill_material_); - markDirty(DirtyMaterial); - } -} - -MapPolygonShader::MapPolygonShader() : QSGMaterialShader(*new QSGMaterialShaderPrivate(this)) -{ - setShaderFileName(VertexStage, QLatin1String(":/location/quickmapitems/shaders/polygon.vert.qsb")); - setShaderFileName(FragmentStage, QLatin1String(":/location/quickmapitems/shaders/polygon.frag.qsb")); -} - -bool MapPolygonShader::updateUniformData(RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) -{ - Q_ASSERT(oldEffect == nullptr || newEffect->type() == oldEffect->type()); - MapPolygonMaterial *oldMaterial = static_cast(oldEffect); - MapPolygonMaterial *newMaterial = static_cast(newEffect); - - const QColor &c = newMaterial->color(); - const QMatrix4x4 &geoProjection = newMaterial->geoProjection(); - const QDoubleVector3D ¢er = newMaterial->center(); - - // It is safer to use vec4 instead on vec3, as described in: - // https://www.khronos.org/opengl/wiki/Interface_Block_(GLSL)#Memory_layout - QVector4D vecCenter, vecCenter_lowpart; - for (int i = 0; i < 3; i++) - QLocationUtils::split_double(center.get(i), &vecCenter[i], &vecCenter_lowpart[i]); - vecCenter[3] = 0; - vecCenter_lowpart[3] = 0; - - int offset = 0; - char *buf_p = state.uniformData()->data(); - - if (state.isMatrixDirty()) { - const QMatrix4x4 m = state.projectionMatrix(); - memcpy(buf_p + offset, m.constData(), 4*4*4); - } - offset += 4*4*4; - - memcpy(buf_p + offset, geoProjection.constData(), 4*4*4); offset+=4*4*4; - - memcpy(buf_p + offset, &vecCenter, 4*4); offset += 4*4; - - memcpy(buf_p + offset, &vecCenter_lowpart, 4*4); offset+=4*4; - - const float wrapOffset = newMaterial->wrapOffset(); - memcpy(buf_p + offset, &wrapOffset, 4); offset+=4; - - offset += 4+4+4; // Padding - - if (oldMaterial == nullptr || c != oldMaterial->color() || state.isOpacityDirty()) { - float opacity = state.opacity() * c.alphaF(); - QVector4D v(c.redF() * opacity, - c.greenF() * opacity, - c.blueF() * opacity, - opacity); - memcpy(buf_p + offset, &v, 4*4); - } - offset+=4*4; - - return true; -} QT_END_NAMESPACE diff --git a/src/location/quickmapitems/qdeclarativepolygonmapitem_p_p.h b/src/location/quickmapitems/qdeclarativepolygonmapitem_p_p.h index 7c5eb9ac..20871847 100644 --- a/src/location/quickmapitems/qdeclarativepolygonmapitem_p_p.h +++ b/src/location/quickmapitems/qdeclarativepolygonmapitem_p_p.h @@ -57,20 +57,13 @@ #include #include -#include #include -#include -#include -#include - #include #include -#include -#include -#include #include #include + #include QT_BEGIN_NAMESPACE @@ -92,123 +85,6 @@ protected: bool assumeSimple_ = false; }; -class Q_LOCATION_PRIVATE_EXPORT QGeoMapPolygonGeometryOpenGL : public QGeoMapItemGeometry -{ -public: - typedef struct { - QList wrappedBboxes; - } WrappedPolygon; - QGeoMapPolygonGeometryOpenGL(); - ~QGeoMapPolygonGeometryOpenGL() override {} - - // Temporary method for compatibility in MapCircleObject. Remove when MapObjects are ported. - void updateSourcePoints(const QGeoMap &map, - const QList &path); - - void updateSourcePoints(const QGeoMap &map, - const QList &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 QList &vx = m_screenVertices; - const QList &ix = m_screenIndices; - - geom->allocate(vx.size(), ix.size()); - if (geom->indexType() == QSGGeometry::UnsignedShortType) { - quint16 *its = geom->indexDataAsUShort(); - for (qsizetype i = 0; i < ix.size(); ++i) - its[i] = ix[i]; - } else if (geom->indexType() == QSGGeometry::UnsignedIntType) { - quint32 *its = geom->indexDataAsUInt(); - for (qsizetype i = 0; i < ix.size(); ++i) - its[i] = ix[i]; - } - - QSGGeometry::Point2D *pts = geom->vertexDataAsPoint2D(); - for (qsizetype i = 0; i < vx.size(); ++i) - pts[i].set(vx[i].x, vx[i].y); - } - - QList m_screenVertices; - QList m_screenIndices; - QDoubleVector2D m_bboxLeftBoundWrapped; - QList m_wrappedPolygons; - int m_wrapOffset = 0; -}; - -class Q_LOCATION_PRIVATE_EXPORT MapPolygonShader : public QSGMaterialShader -{ -public: - MapPolygonShader(); - - bool updateUniformData(RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; -}; - -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); - } - - QSGMaterialShader *createShader(QSGRendererInterface::RenderMode renderMode) 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 { @@ -225,22 +101,6 @@ private: 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: @@ -352,190 +212,6 @@ public: 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.perimeter().length() == 0) { // Possibly cleared - m_geometry.clear(); - m_borderGeometry.clear(); - m_poly.setWidth(0); - m_poly.setHeight(0); - return; - } - - QScopedValueRollback 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(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(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 diff --git a/src/location/quickmapitems/qdeclarativepolylinemapitem.cpp b/src/location/quickmapitems/qdeclarativepolylinemapitem.cpp index 59a6a2b2..c3bb8122 100644 --- a/src/location/quickmapitems/qdeclarativepolylinemapitem.cpp +++ b/src/location/quickmapitems/qdeclarativepolylinemapitem.cpp @@ -39,53 +39,27 @@ #include "qdeclarativepolylinemapitem_p.h" #include "qdeclarativepolylinemapitem_p_p.h" -#include "qdeclarativerectanglemapitem_p_p.h" -#include "qdeclarativecirclemapitem_p_p.h" +#include "rhi/qdeclarativepolylinemapitem_rhi_p.h" #include "qdeclarativegeomapitemutils_p.h" #include "error_messages_p.h" #include "locationvaluetypehelper_p.h" -#include "qgeosimplify_p.h" #include #include -#include -#include -#include #include -#include #include -#include -#include #include #include #include #include #include -#include #include #include QT_BEGIN_NAMESPACE -struct ThreadPool // to have a thread pool with max 1 thread for geometry processing -{ - ThreadPool () - { - m_threadPool.setMaxThreadCount(1); - } - - void start(QRunnable *runnable, int priority = 0) - { - m_threadPool.start(runnable, priority); - } - - QThreadPool m_threadPool; -}; - -Q_GLOBAL_STATIC(ThreadPool, threadPool) - static bool get_line_intersection(const double p0_x, const double p0_y, @@ -823,165 +797,6 @@ bool QDeclarativePolylineMapItemPrivateCPU::contains(const QPointF &point) const return m_geometry.contains(point); } - -void QGeoMapPolylineGeometryOpenGL::updateSourcePoints(const QGeoMap &map, const QGeoPolygon &poly) -{ - if (!sourceDirty_) - return; - QGeoPath p(poly.perimeter()); - if (poly.perimeter().size() && poly.perimeter().last() != poly.perimeter().first()) - p.addCoordinate(poly.perimeter().first()); - updateSourcePoints(map, p); -} - -void QGeoMapPolylineGeometryOpenGL::updateSourcePoints(const QGeoMap &map, const QGeoPath &poly) -{ - if (!sourceDirty_) - return; - const QGeoProjectionWebMercator &p = static_cast(map.geoProjection()); - - // build the actual path - // The approach is the same as described in QGeoMapPolylineGeometry::updateSourcePoints - - - QDoubleVector2D leftBoundWrapped; - // 1) pre-compute 3 sets of "wrapped" coordinates: one w regular mercator, one w regular mercator +- 1.0 - QList wrappedPath; - QDeclarativeGeoMapItemUtils::wrapPath(poly.path(), geoLeftBound_, p, - wrappedPath, &leftBoundWrapped); - - const QGeoRectangle &boundingRectangle = poly.boundingGeoRectangle(); - updateSourcePoints(p, wrappedPath, boundingRectangle); -} - -void QGeoMapPolylineGeometryOpenGL::updateSourcePoints(const QGeoProjectionWebMercator &p, - const QList &wrappedPath, - const QGeoRectangle &boundingRectangle) { - if (!sourceDirty_) - return; - // 1.1) do the same for the bbox - // Beware: vertical lines (or horizontal lines) might have an "empty" bbox. Check for that - - QGeoCoordinate topLeft = boundingRectangle.topLeft(); - QGeoCoordinate bottomRight = boundingRectangle.bottomRight(); - const qreal epsilon = 0.000001; - if (qFuzzyCompare(topLeft.latitude(), bottomRight.latitude())) { - topLeft.setLatitude(qBound(-90.0, topLeft.latitude() + epsilon ,90.0)); - bottomRight.setLatitude(qBound(-90.0, bottomRight.latitude() - epsilon ,90.0)); - } - if (qFuzzyCompare(topLeft.longitude(), bottomRight.longitude())) { - topLeft.setLongitude(QLocationUtils::wrapLong(topLeft.longitude() - epsilon)); - bottomRight.setLongitude(QLocationUtils::wrapLong(bottomRight.longitude() + epsilon)); - } - QGeoPolygon bbox(QGeoRectangle(topLeft, bottomRight)); - QList wrappedBbox, wrappedBboxPlus1, wrappedBboxMinus1; - QDeclarativeGeoMapItemUtils::wrapPath(bbox.perimeter(), bbox.boundingGeoRectangle().topLeft(), p, - wrappedBbox, wrappedBboxMinus1, wrappedBboxPlus1, &m_bboxLeftBoundWrapped); - - // New pointers, some old LOD task might still be running and operating on the old pointers. - resetLOD(); - - for (const auto &v: qAsConst(wrappedPath)) m_screenVertices->append(v); - - m_wrappedPolygons.resize(3); - m_wrappedPolygons[0].wrappedBboxes = wrappedBboxMinus1; - m_wrappedPolygons[1].wrappedBboxes = wrappedBbox; - m_wrappedPolygons[2].wrappedBboxes = wrappedBboxPlus1; - srcOrigin_ = geoLeftBound_; -} - -void QGeoMapPolylineGeometryOpenGL::updateSourcePoints(const QGeoMap &map, const QGeoRectangle &rect) -{ - const QList perimeter = QGeoMapItemGeometry::perimeter(rect); - updateSourcePoints(map, QGeoPath(perimeter)); -} - -void QGeoMapPolylineGeometryOpenGL::updateSourcePoints(const QGeoMap &map, const QGeoCircle &circle) -{ - if (!sourceDirty_) - return; - const QGeoProjectionWebMercator &p = static_cast(map.geoProjection()); - - QDoubleVector2D leftBoundWrapped; - // 1) pre-compute 3 sets of "wrapped" coordinates: one w regular mercator, one w regular mercator +- 1.0 - QList path; - QGeoCoordinate leftBound; - QList wrappedPath; - QDeclarativeCircleMapItemPrivateCPU::calculatePeripheralPoints(path, circle.center(), circle.radius(), QDeclarativeCircleMapItemPrivateCPU::CircleSamples, leftBound); - path << path.first(); - geoLeftBound_ = leftBound; - QDeclarativeGeoMapItemUtils::wrapPath(path, leftBound, p, wrappedPath, &leftBoundWrapped); - const QGeoRectangle &boundingRectangle = circle.boundingGeoRectangle(); - updateSourcePoints(p, wrappedPath, boundingRectangle); -} - -void QGeoMapPolylineGeometryOpenGL::updateScreenPoints(const QGeoMap &map, qreal strokeWidth, bool /*adjustTranslation*/) -{ - if (map.viewportWidth() == 0 || map.viewportHeight() == 0) { - clear(); - return; - } - - // 1) identify which set to use: std, +1 or -1 - const QGeoProjectionWebMercator &p = static_cast(map.geoProjection()); - const QDoubleVector2D leftBoundMercator = p.geoToMapProjection(srcOrigin_); - m_wrapOffset = p.projectionWrapFactor(leftBoundMercator) + 1; // +1 to get the offset into QLists - - if (sourceDirty_) { - // 1.1) select geometry set - // This could theoretically be skipped for those polylines whose bbox is not even projectable. - // However, such optimization could only be introduced if not calculating bboxes lazily. - // Hence not doing it. -// if (m_screenVertices.size() > 1) - m_dataChanged = true; - } - - updateQuickGeometry(p, strokeWidth); -} - -void QGeoMapPolylineGeometryOpenGL::updateQuickGeometry(const QGeoProjectionWebMercator &p, qreal strokeWidth) -{ - // 2) clip bbox - // BBox handling -- this is related to the bounding box geometry - // that has to inevitably follow the old projection codepath - // As it needs to provide projected coordinates for QtQuick interaction. - // This could be futher optimized to be updated in a lazy fashion. - const QList &wrappedBbox = m_wrappedPolygons.at(m_wrapOffset).wrappedBboxes; - QList > clippedBbox; - QDoubleVector2D bboxLeftBoundWrapped = m_bboxLeftBoundWrapped; - bboxLeftBoundWrapped.setX(bboxLeftBoundWrapped.x() + double(m_wrapOffset - 1)); - QDeclarativeGeoMapItemUtils::clipPolygon(wrappedBbox, p, clippedBbox, &bboxLeftBoundWrapped, false); - - // 3) project bbox - QPainterPath ppi; - - if ( !clippedBbox.size() || - clippedBbox.first().size() < 3) { - sourceBounds_ = screenBounds_ = QRectF(); - firstPointOffset_ = QPointF(); - screenOutline_ = ppi; - return; - } - - QDeclarativeGeoMapItemUtils::projectBbox(clippedBbox.first(), p, ppi); // Using first because a clipped box should always result in one polygon - const QRectF brect = ppi.boundingRect(); - firstPointOffset_ = QPointF(brect.topLeft()); - sourceBounds_ = brect; - screenOutline_ = ppi; - - // 4) Set Screen bbox - screenBounds_ = brect; - sourceBounds_.setX(0); - sourceBounds_.setY(0); - sourceBounds_.setWidth(brect.width() + strokeWidth); - sourceBounds_.setHeight(brect.height() + strokeWidth); -} - - -QDeclarativePolylineMapItemPrivateOpenGLLineStrip::~QDeclarativePolylineMapItemPrivateOpenGLLineStrip() {} - -QDeclarativePolylineMapItemPrivateOpenGLExtruded::~QDeclarativePolylineMapItemPrivateOpenGLExtruded() {} - /* * QDeclarativePolygonMapItem Implementation */ @@ -1469,528 +1284,4 @@ void MapPolylineNode::update(const QColor &fillColor, } } -MapPolylineNodeOpenGLLineStrip::MapPolylineNodeOpenGLLineStrip() -: geometry_(QSGGeometry::defaultAttributes_Point2D(), 0) -{ - geometry_.setDrawingMode(QSGGeometry::DrawLineStrip); - QSGGeometryNode::setMaterial(&fill_material_); - QSGGeometryNode::setGeometry(&geometry_); -} - -MapPolylineNodeOpenGLLineStrip::~MapPolylineNodeOpenGLLineStrip() -{ - -} - -void MapPolylineNodeOpenGLLineStrip::update(const QColor &fillColor, - const qreal lineWidth, - const QGeoMapPolylineGeometryOpenGL *shape, - const QMatrix4x4 &geoProjection, - const QDoubleVector3D ¢er, - const Qt::PenCapStyle /*capStyle*/) -{ - if (shape->m_screenVertices->size() < 2) { - setSubtreeBlocked(true); - return; - } else { - setSubtreeBlocked(false); - } - - QSGGeometry *fill = QSGGeometryNode::geometry(); - if (shape->m_dataChanged) { - shape->allocateAndFillLineStrip(fill); - markDirty(DirtyGeometry); - shape->m_dataChanged = false; - } - fill->setLineWidth(lineWidth); - fill_material_.setLineWidth(lineWidth); // to make the material not compare equal if linewidth changes - -// if (fillColor != fill_material_.color()) - { - fill_material_.setWrapOffset(shape->m_wrapOffset - 1); - fill_material_.setColor(fillColor); - fill_material_.setGeoProjection(geoProjection); - fill_material_.setCenter(center); - setMaterial(&fill_material_); - markDirty(DirtyMaterial); - } -} - -MapPolylineShaderLineStrip::MapPolylineShaderLineStrip() : QSGMaterialShader(*new QSGMaterialShaderPrivate(this)) -{ - setShaderFileName(VertexStage, QLatin1String(":/location/quickmapitems/shaders/polyline_linestrip.vert.qsb")); - setShaderFileName(FragmentStage, QLatin1String(":/location/quickmapitems/shaders/polyline_linestrip.frag.qsb")); -} - -bool MapPolylineShaderLineStrip::updateUniformData(QSGMaterialShader::RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) -{ - Q_ASSERT(oldEffect == nullptr || newEffect->type() == oldEffect->type()); - MapPolylineMaterial *oldMaterial = static_cast(oldEffect); - MapPolylineMaterial *newMaterial = static_cast(newEffect); - - const QColor &c = newMaterial->color(); - const QMatrix4x4 &geoProjection = newMaterial->geoProjection(); - const QDoubleVector3D ¢er = newMaterial->center(); - - QVector4D vecCenter, vecCenter_lowpart; - for (int i = 0; i < 3; i++) - QLocationUtils::split_double(center.get(i), &vecCenter[i], &vecCenter_lowpart[i]); - vecCenter[3] = 0; - vecCenter_lowpart[3] = 0; - - int offset = 0; - char *buf_p = state.uniformData()->data(); - - if (state.isMatrixDirty()) { - const QMatrix4x4 m = state.projectionMatrix(); - memcpy(buf_p + offset, m.constData(), 4*4*4); - } - offset += 4*4*4; - - memcpy(buf_p + offset, geoProjection.constData(), 4*4*4); offset+=4*4*4; - - memcpy(buf_p + offset, &vecCenter, 4*4); offset += 4*4; - - memcpy(buf_p + offset, &vecCenter_lowpart, 4*4); offset+=4*4; - - if (state.isOpacityDirty()) { - const float opacity = state.opacity(); - memcpy(buf_p + offset, &opacity, 4); - } - offset += 4; - - float wrapOffset = newMaterial->wrapOffset(); - memcpy(buf_p + offset, &wrapOffset, 4); offset+=4; - - offset+=8; // float padding - - if (oldMaterial == nullptr || c != oldMaterial->color() || state.isOpacityDirty()) { - float opacity = state.opacity() * c.alphaF(); - QVector4D v(c.redF() * opacity, - c.greenF() * opacity, - c.blueF() * opacity, - opacity); - memcpy(buf_p + offset, &v, 4*4); - } - offset+=4*4; - - return true; -} - -QSGMaterialShader *MapPolylineMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const -{ - Q_UNUSED(renderMode); - return new MapPolylineShaderLineStrip(); -} - -QSGMaterialType *MapPolylineMaterial::type() const -{ - static QSGMaterialType type; - return &type; -} - -int MapPolylineMaterial::compare(const QSGMaterial *other) const -{ - const MapPolylineMaterial &o = *static_cast(other); - if (o.m_center == m_center && o.m_geoProjection == m_geoProjection && o.m_wrapOffset == m_wrapOffset && o.m_lineWidth == m_lineWidth) - return QSGFlatColorMaterial::compare(other); - return -1; -} - -const QSGGeometry::AttributeSet &MapPolylineNodeOpenGLExtruded::attributesMapPolylineTriangulated() -{ - return MapPolylineEntry::attributes(); -} - -MapPolylineNodeOpenGLExtruded::MapPolylineNodeOpenGLExtruded() -: m_geometryTriangulating(MapPolylineNodeOpenGLExtruded::attributesMapPolylineTriangulated(), - 0 /* vtx cnt */, 0 /* index cnt */, QSGGeometry::UnsignedIntType /* index type */) -{ - m_geometryTriangulating.setDrawingMode(QSGGeometry::DrawTriangles); - QSGGeometryNode::setMaterial(&fill_material_); - QSGGeometryNode::setGeometry(&m_geometryTriangulating); -} - -MapPolylineNodeOpenGLExtruded::~MapPolylineNodeOpenGLExtruded() -{ - -} - -bool QGeoMapPolylineGeometryOpenGL::allocateAndFillEntries(QSGGeometry *geom, - bool closed, - unsigned int zoom) const -{ - // Select LOD. Generate if not present. Assign it to m_screenVertices; - if (m_dataChanged) { - // it means that the data really changed. - // So synchronously produce LOD 1, and enqueue the requested one if != 0 or 1. - // Select 0 if 0 is requested, or 1 in all other cases. - selectLODOnDataChanged(zoom, m_bboxLeftBoundWrapped.x()); - } else { - // Data has not changed, but active LOD != requested LOD. - // So, if there are no active tasks, try to change to the correct one. - if (!selectLODOnLODMismatch(zoom, m_bboxLeftBoundWrapped.x(), closed)) - return false; - } - - const QList &v = *m_screenVertices; - if (v.size() < 2) { - geom->allocate(0, 0); - return true; - } - const int numSegments = (v.size() - 1); - - const int numIndices = numSegments * 6; // six vertices per line segment - geom->allocate(numIndices); - MapPolylineNodeOpenGLExtruded::MapPolylineEntry *vertices = - static_cast(geom->vertexData()); - - for (int i = 0; i < numSegments; ++i) { - MapPolylineNodeOpenGLExtruded::MapPolylineEntry e; - const QDeclarativeGeoMapItemUtils::vec2 &cur = v[i]; - const QDeclarativeGeoMapItemUtils::vec2 &next = v[i+1]; - e.triangletype = 1.0; - e.next = next; - e.prev = cur; - e.pos = cur; - e.direction = 1.0; - e.vertextype = -1.0; - vertices[i*6] = e; - e.direction = -1.0; - vertices[i*6+1] = e; - e.pos = next; - e.vertextype = 1.0; - vertices[i*6+2] = e; - - // Second tri - e.triangletype = -1.0; - e.direction = -1.0; - vertices[i*6+3] = e; - e.direction = 1.0; - vertices[i*6+4] = e; - e.pos = cur; - e.vertextype = -1.0; - vertices[i*6+5] = e; - - if (i != 0) { - vertices[i*6].prev = vertices[i*6+1].prev = vertices[i*6+5].prev = v[i-1]; - } else { - if (closed) { - vertices[i*6].prev = vertices[i*6+1].prev = vertices[i*6+5].prev = v[numSegments - 1]; - } else { - vertices[i*6].triangletype = vertices[i*6+1].triangletype = vertices[i*6+5].triangletype = 2.0; - } - } - if (i != numSegments - 1) { - vertices[i*6+2].next = vertices[i*6+3].next = vertices[i*6+4].next = v[i+2]; - } else { - if (closed) { - vertices[i*6+2].next = vertices[i*6+3].next = vertices[i*6+4].next = v[1]; - } else { - vertices[i*6+2].triangletype = vertices[i*6+3].triangletype = vertices[i*6+4].triangletype = 3.0; - } - } - } - return true; -} - -void QGeoMapPolylineGeometryOpenGL::allocateAndFillLineStrip(QSGGeometry *geom, - int lod) const -{ - // Select LOD. Generate if not present. Assign it to m_screenVertices; - Q_UNUSED(lod); - - const QList &vx = *m_screenVertices; - geom->allocate(vx.size()); - - QSGGeometry::Point2D *pts = geom->vertexDataAsPoint2D(); - for (qsizetype i = 0; i < vx.size(); ++i) - pts[i].set(vx[i].x, vx[i].y); -} - -void MapPolylineNodeOpenGLExtruded::update(const QColor &fillColor, - float lineWidth, - const QGeoMapPolylineGeometryOpenGL *shape, - const QMatrix4x4 &geoProjection, - const QDoubleVector3D ¢er, - const Qt::PenCapStyle capStyle, - bool closed, - unsigned int zoom) -{ - // shape->size() == number of triangles - if (shape->m_screenVertices->size() < 2 - || lineWidth < 0.5 || fillColor.alpha() == 0) { // number of points - setSubtreeBlocked(true); - return; - } else { - setSubtreeBlocked(false); - } - - QSGGeometry *fill = QSGGeometryNode::geometry(); - if (shape->m_dataChanged || !shape->isLODActive(zoom) || !fill->vertexCount()) { // fill->vertexCount for when node gets destroyed by MapItemBase bcoz of opacity, then recreated. - if (shape->allocateAndFillEntries(fill, closed, zoom)) { - markDirty(DirtyGeometry); - shape->m_dataChanged = false; - } - } - - // Update this -// if (fillColor != fill_material_.color()) - { - fill_material_.setWrapOffset(shape->m_wrapOffset - 1); - fill_material_.setColor(fillColor); - fill_material_.setGeoProjection(geoProjection); - fill_material_.setCenter(center); - fill_material_.setLineWidth(lineWidth); - fill_material_.setMiter(capStyle != Qt::FlatCap); - setMaterial(&fill_material_); - markDirty(DirtyMaterial); - } -} - -MapPolylineShaderExtruded::MapPolylineShaderExtruded() : QSGMaterialShader(*new QSGMaterialShaderPrivate(this)) -{ - // 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. - setShaderFileName(VertexStage, QLatin1String(":/location/quickmapitems/shaders/polyline_extruded.vert.qsb")); - setShaderFileName(FragmentStage, QLatin1String(":/location/quickmapitems/shaders/polyline_extruded.frag.qsb")); -} - -bool MapPolylineShaderExtruded::updateUniformData(QSGMaterialShader::RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) -{ - Q_ASSERT(oldEffect == nullptr || newEffect->type() == oldEffect->type()); - MapPolylineMaterialExtruded *oldMaterial = static_cast(oldEffect); - MapPolylineMaterialExtruded *newMaterial = static_cast(newEffect); - - const QColor &c = newMaterial->color(); - const QMatrix4x4 &geoProjection = newMaterial->geoProjection(); - const QDoubleVector3D ¢er = newMaterial->center(); - - // It is safer to use vec4 instead on vec3, as described in: - // https://www.khronos.org/opengl/wiki/Interface_Block_(GLSL)#Memory_layout - QVector4D vecCenter, vecCenter_lowpart; - for (int i = 0; i < 3; i++) - QLocationUtils::split_double(center.get(i), &vecCenter[i], &vecCenter_lowpart[i]); - vecCenter[3] = 0; - vecCenter_lowpart[3] = 0; - - int offset = 0; - char *buf_p = state.uniformData()->data(); - - if (state.isMatrixDirty()) { - const QMatrix4x4 m = state.projectionMatrix(); - memcpy(buf_p + offset, m.constData(), 4*4*4); - } - offset += 4*4*4; - - memcpy(buf_p + offset, geoProjection.constData(), 4*4*4); offset+=4*4*4; - - memcpy(buf_p + offset, &vecCenter, 4*4); offset += 4*4; - - memcpy(buf_p + offset, &vecCenter_lowpart, 4*4); offset+=4*4; - - const float lineWidth = newMaterial->lineWidth(); - memcpy(buf_p + offset, &lineWidth, 4); offset+=4; - - const QRectF viewportRect = state.viewportRect(); - const float aspect = float(viewportRect.width() / viewportRect.height()); - memcpy(buf_p + offset, &aspect, 4); offset+=4; - - offset += 4; // Padding - - int miter = newMaterial->miter(); - memcpy(buf_p + offset, &miter, 4); offset+=4; - - if (oldMaterial == nullptr || c != oldMaterial->color() || state.isOpacityDirty()) { - float opacity = state.opacity() * c.alphaF(); - QVector4D v(c.redF() * opacity, - c.greenF() * opacity, - c.blueF() * opacity, - opacity); - memcpy(buf_p + offset, &v, 4*4); - } - offset+=4*4; - - const float wrapOffset = newMaterial->wrapOffset(); - memcpy(buf_p + offset, &wrapOffset, 4); offset+=4; - - return true; -} - -QSGMaterialShader *MapPolylineMaterialExtruded::createShader(QSGRendererInterface::RenderMode renderMode) const -{ - Q_UNUSED(renderMode); - return new MapPolylineShaderExtruded(); -} - -QSGMaterialType *MapPolylineMaterialExtruded::type() const -{ - static QSGMaterialType type; - return &type; -} - -int MapPolylineMaterialExtruded::compare(const QSGMaterial *other) const -{ - const MapPolylineMaterialExtruded &o = *static_cast(other); - if (o.m_miter == m_miter) - return MapPolylineMaterial::compare(other); - return -1; -} - -QList QGeoMapItemLODGeometry::getSimplified( - QList - &wrappedPath, // reference as it gets copied in the nested call - double leftBoundWrapped, unsigned int zoom) -{ - // Try a simplify step - QList data; - for (auto e: wrappedPath) - data << e.toDoubleVector2D(); - const QList simplified = QGeoSimplify::geoSimplifyZL(data, - leftBoundWrapped, - zoom); - - data.clear(); - QList simple; - for (auto e: simplified) - simple << e; - return simple; -} - - -bool QGeoMapItemLODGeometry::isLODActive(unsigned int lod) const -{ - return m_screenVertices == m_verticesLOD[zoomToLOD(lod)].data(); -} - -class PolylineSimplifyTask : public QRunnable -{ -public: - PolylineSimplifyTask(const QSharedPointer> - &input, // reference as it gets copied in the nested call - const QSharedPointer> &output, - double leftBound, unsigned int zoom, QSharedPointer &working) - : m_zoom(zoom), m_leftBound(leftBound), m_input(input), m_output(output), m_working(working) - { - Q_ASSERT(!input.isNull()); - Q_ASSERT(!output.isNull()); - } - - ~PolylineSimplifyTask() override; - - void run() override - { - // Skip sending notifications for now. Updated data will be picked up eventually. - // ToDo: figure out how to connect a signal from here to a slot in the item. - *m_working = QGeoMapPolylineGeometryOpenGL::zoomToLOD(m_zoom); - const QList res = - QGeoMapPolylineGeometryOpenGL::getSimplified( - *m_input, m_leftBound, QGeoMapPolylineGeometryOpenGL::zoomForLOD(m_zoom)); - *m_output = res; - *m_working = 0; - } - - unsigned int m_zoom; - double m_leftBound; - QSharedPointer> m_input, m_output; - QSharedPointer m_working; -}; - -void QGeoMapItemLODGeometry::enqueueSimplificationTask( - const QSharedPointer> &input, - const QSharedPointer> &output, double leftBound, - unsigned int zoom, QSharedPointer &working) -{ - Q_ASSERT(!input.isNull()); - Q_ASSERT(!output.isNull()); - PolylineSimplifyTask *task = new PolylineSimplifyTask(input, - output, - leftBound, - zoom, - working); - threadPool->start(task); -} - -PolylineSimplifyTask::~PolylineSimplifyTask() {} - -void QGeoMapItemLODGeometry::selectLOD(unsigned int zoom, double leftBound, bool /* closed */) // closed to tell if this is a polygon or a polyline. -{ - unsigned int requestedLod = zoomToLOD(zoom); - if (!m_verticesLOD[requestedLod].isNull()) { - m_screenVertices = m_verticesLOD[requestedLod].data(); - } else if (!m_verticesLOD.at(0)->isEmpty()) { - // if here, zoomToLOD != 0 and no current working task. - // So select the last filled LOD != m_working (lower-bounded by 1, - // guaranteed to exist), and enqueue the right one - m_verticesLOD[requestedLod] = QSharedPointer>( - new QList); - - for (unsigned int i = requestedLod - 1; i >= 1; i--) { - if (*m_working != i && !m_verticesLOD[i].isNull()) { - m_screenVertices = m_verticesLOD[i].data(); - break; - } else if (i == 1) { - // get 1 synchronously if not computed already - m_verticesLOD[1] = QSharedPointer>( - new QList); - *m_verticesLOD[1] = getSimplified( *m_verticesLOD[0], - leftBound, - zoomForLOD(0)); - if (requestedLod == 1) - return; - } - } - - enqueueSimplificationTask( m_verticesLOD.at(0), - m_verticesLOD[requestedLod], - leftBound, - zoom, - m_working); - - } -} - -void QGeoMapItemLODGeometry::selectLODOnDataChanged(unsigned int zoom, double leftBound) const -{ - unsigned int lod = zoomToLOD(zoom); - if (lod > 0) { - // Generate ZL 1 as fallback for all cases != 0. Do not do if 0 is requested - // (= old behavior, LOD disabled) - m_verticesLOD[1] = QSharedPointer>( - new QList); - *m_verticesLOD[1] = getSimplified( *m_verticesLOD[0], - leftBound, - zoomForLOD(0)); - } - if (lod > 1) { - if (!m_verticesLOD[lod]) - m_verticesLOD[lod] = QSharedPointer>( - new QList); - enqueueSimplificationTask( m_verticesLOD.at(0), - m_verticesLOD[lod], - leftBound, - zoom, - m_working); - } - m_screenVertices = m_verticesLOD[qMin(lod, 1)].data(); // return only 0,1 synchronously -} - -unsigned int QGeoMapItemLODGeometry::zoomToLOD(unsigned int zoom) -{ - unsigned int res; - if (zoom > 20) - res = 0; - else - res = qBound(3, zoom, 20) / 3; // bound LOD'ing between ZL 3 and 20. Every 3 ZoomLevels - return res; -} - -unsigned int QGeoMapItemLODGeometry::zoomForLOD(unsigned int zoom) -{ - unsigned int res = (qBound(3, zoom, 20) / 3) * 3; - if (zoom < 6) - return res; - return res + 1; // give more resolution when closing in -} - QT_END_NAMESPACE diff --git a/src/location/quickmapitems/qdeclarativepolylinemapitem_p_p.h b/src/location/quickmapitems/qdeclarativepolylinemapitem_p_p.h index 3a446a10..676e6f71 100644 --- a/src/location/quickmapitems/qdeclarativepolylinemapitem_p_p.h +++ b/src/location/quickmapitems/qdeclarativepolylinemapitem_p_p.h @@ -52,28 +52,20 @@ // We mean it. // -#include #include #include #include -#include -#include -#include -#include -#include #include #include -#include -#include #include #include -#include - QT_BEGIN_NAMESPACE +class QSGMaterialShader; + class Q_LOCATION_PRIVATE_EXPORT QGeoMapPolylineGeometry : public QGeoMapItemGeometry { public: @@ -135,72 +127,6 @@ public: 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); - } - - QSGMaterialShader *createShader(QSGRendererInterface::RenderMode renderMode) 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; - } - - void setLineWidth(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: @@ -214,272 +140,6 @@ protected: QSGGeometry geometry_; }; -class Q_LOCATION_PRIVATE_EXPORT QGeoMapItemLODGeometry -{ -public: - mutable std::array>, 7> - m_verticesLOD; // fix it to 7, - // do not allow simplifications beyond ZL 20. This could actually be - // limited even further - mutable QList *m_screenVertices; - mutable QSharedPointer 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>( - new QList); - 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 QList - getSimplified(QList &wrappedPath, double leftBoundWrapped, - unsigned int zoom); - - static void enqueueSimplificationTask( - const QSharedPointer> - &input, // reference as it gets copied in the nested call - const QSharedPointer> &output, - double leftBound, unsigned int zoom, QSharedPointer &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(this)->selectLOD(zoom, - leftBound, - closed); - return true; - } -}; - -class Q_LOCATION_PRIVATE_EXPORT QGeoMapPolylineGeometryOpenGL : public QGeoMapItemGeometry, public QGeoMapItemLODGeometry -{ -public: - typedef struct { - QList wrappedBboxes; - } WrappedPolyline; - - QGeoMapPolylineGeometryOpenGL() - { - m_working = QSharedPointer(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 &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 (qsizetype i = 1; i < m_screenVertices->size(); ++i) { - const auto &screenVertice = m_screenVertices->at(i); - if (!a.isFinite()) { - a = p.wrappedMapProjectionToItemPosition(p.wrapMapProjection(screenVertice.toDoubleVector2D())); - continue; - } - - b = p.wrappedMapProjectionToItemPosition(p.wrapMapProjection(screenVertice.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; - QList m_wrappedPolygons; - int m_wrapOffset; - - friend class QDeclarativeCircleMapItem; - friend class QDeclarativePolygonMapItem; - friend class QDeclarativeRectangleMapItem; -}; - -class Q_LOCATION_PRIVATE_EXPORT MapPolylineShaderLineStrip : public QSGMaterialShader -{ -public: - MapPolylineShaderLineStrip(); - - bool updateUniformData(RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; -}; - -class Q_LOCATION_PRIVATE_EXPORT MapPolylineShaderExtruded : public QSGMaterialShader -{ -public: - MapPolylineShaderExtruded(); - - bool updateUniformData(RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; -}; - -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(QSGRendererInterface::RenderMode renderMode) const override; - - void setMiter(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 MapPolylineEntry { - QDeclarativeGeoMapItemUtils::vec2 pos; - QDeclarativeGeoMapItemUtils::vec2 prev; - QDeclarativeGeoMapItemUtils::vec2 next; - float direction; - float triangletype; // es2 does not support int attribs - float vertextype; - - 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, - float lineWidth, - const QGeoMapPolylineGeometryOpenGL *shape, - const QMatrix4x4 &geoProjection, - const QDoubleVector3D ¢er, - 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: @@ -571,170 +231,6 @@ public: 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(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 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(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(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(oldNode); - } - - //TODO: update only material - if (m_geometry.isScreenDirty() || m_poly.m_dirtyMaterial) { - nodeTri->update(color, - lineWidth , - &m_geometry, - combinedMatrix, - cameraCenter, - Qt::FlatCap, - 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 diff --git a/src/location/quickmapitems/qdeclarativerectanglemapitem.cpp b/src/location/quickmapitems/qdeclarativerectanglemapitem.cpp index dcce6f42..20930d40 100644 --- a/src/location/quickmapitems/qdeclarativerectanglemapitem.cpp +++ b/src/location/quickmapitems/qdeclarativerectanglemapitem.cpp @@ -39,6 +39,7 @@ #include "qdeclarativerectanglemapitem_p.h" #include "qdeclarativerectanglemapitem_p_p.h" +#include "rhi/qdeclarativerectanglemapitem_rhi_p.h" #include "qdeclarativepolygonmapitem_p.h" #include @@ -491,6 +492,4 @@ bool QDeclarativeRectangleMapItemPrivateCPU::contains(const QPointF &point) cons return (m_geometry.contains(point) || m_borderGeometry.contains(point)); } -QDeclarativeRectangleMapItemPrivateOpenGL::~QDeclarativeRectangleMapItemPrivateOpenGL() {} - QT_END_NAMESPACE diff --git a/src/location/quickmapitems/qdeclarativerectanglemapitem_p_p.h b/src/location/quickmapitems/qdeclarativerectanglemapitem_p_p.h index 1c4d2bc0..4f76d445 100644 --- a/src/location/quickmapitems/qdeclarativerectanglemapitem_p_p.h +++ b/src/location/quickmapitems/qdeclarativerectanglemapitem_p_p.h @@ -138,173 +138,6 @@ public: MapPolygonNode *m_node = nullptr; }; -class Q_LOCATION_PRIVATE_EXPORT QDeclarativeRectangleMapItemPrivateOpenGL: public QDeclarativeRectangleMapItemPrivate -{ -public: - QDeclarativeRectangleMapItemPrivateOpenGL(QDeclarativeRectangleMapItem &rect) : QDeclarativeRectangleMapItemPrivate(rect) - { - } - - QDeclarativeRectangleMapItemPrivateOpenGL(QDeclarativeRectangleMapItemPrivate &other) - : QDeclarativeRectangleMapItemPrivate(other) - { - } - - ~QDeclarativeRectangleMapItemPrivateOpenGL() override; - - void markScreenDirtyAndUpdate() - { - // preserveGeometry is cleared in updateMapItemPaintNode - m_geometry.markScreenDirty(); - m_borderGeometry.markScreenDirty(); - m_rect.polishAndUpdate(); - } - void onLinePropertiesChanged() override - { - m_rect.m_dirtyMaterial = true; - afterViewportChanged(); - } - void markSourceDirtyAndUpdate() override - { - m_geometry.markSourceDirty(); - m_borderGeometry.markSourceDirty(); - m_rect.polishAndUpdate(); - } - void preserveGeometry() - { - m_geometry.setPreserveGeometry(true, m_rect.m_rectangle.topLeft()); - m_borderGeometry.setPreserveGeometry(true, m_rect.m_rectangle.topLeft()); - } - void onMapSet() override - { - markSourceDirtyAndUpdate(); - } - void onGeoGeometryChanged() override - { - preserveGeometry(); - markSourceDirtyAndUpdate(); - } - void onItemGeometryChanged() override - { - onGeoGeometryChanged(); - } - void afterViewportChanged() override - { - preserveGeometry(); - markScreenDirtyAndUpdate(); - } - void updatePolish() override - { - if (!m_rect.topLeft().isValid() || !m_rect.bottomRight().isValid()) { - m_geometry.clear(); - m_borderGeometry.clear(); - m_rect.setWidth(0); - m_rect.setHeight(0); - return; - } - - QScopedValueRollback rollback(m_rect.m_updatingGeometry); - m_rect.m_updatingGeometry = true; - const qreal lineWidth = m_rect.m_border.width(); - const QColor &lineColor = m_rect.m_border.color(); - const QColor &fillColor = m_rect.color(); - if (fillColor.alpha() != 0) { - m_geometry.updateSourcePoints(*m_rect.map(), m_rect.m_rectangle); - m_geometry.markScreenDirty(); - m_geometry.updateScreenPoints(*m_rect.map(), lineWidth, lineColor); - } else { - m_geometry.clearBounds(); - } - - QGeoMapItemGeometry * geom = &m_geometry; - m_borderGeometry.clearScreen(); - if (lineColor.alpha() != 0 && lineWidth > 0) { - m_borderGeometry.updateSourcePoints(*m_rect.map(), m_rect.m_rectangle); - m_borderGeometry.markScreenDirty(); - m_borderGeometry.updateScreenPoints(*m_rect.map(), lineWidth); - geom = &m_borderGeometry; - } - m_rect.setWidth(geom->sourceBoundingBox().width()); - m_rect.setHeight(geom->sourceBoundingBox().height()); - m_rect.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 QDeclarativePolygonMapItemPrivateOpenGL::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(oldNode); - } - - const QGeoMap *map = m_rect.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_rect.m_border.color(), - float(m_rect.m_border.width()), - &m_borderGeometry, - combinedMatrix, - cameraCenter, - Qt::SquareCap, - true, - 30); // No LOD for rectangles - m_borderGeometry.setPreserveGeometry(false); - m_borderGeometry.markClean(); - } else { - m_polylinenode->setSubtreeBlocked(true); - } - if (m_geometry.isScreenDirty()) { - m_node->update(m_rect.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_rect.m_border.width(); - const QColor &lineColor = m_rect.m_border.color(); - const QRectF &bounds = (lineColor.alpha() != 0 && lineWidth > 0) ? m_borderGeometry.sourceBoundingBox() : m_geometry.sourceBoundingBox(); - if (bounds.contains(point)) { - QDeclarativeGeoMap *m = m_rect.quickMap(); - if (m) { - const QGeoCoordinate crd = m->toCoordinate(m->mapFromItem(&m_rect, point)); - return m_rect.m_rectangle.contains(crd) || m_borderGeometry.contains(m_rect.mapToItem(m_rect.quickMap(), point), - m_rect.border()->width(), - static_cast(m_rect.map()->geoProjection())); - } else { - return true; - } - } - return false; - } - - QGeoMapPolygonGeometryOpenGL m_geometry; - QGeoMapPolylineGeometryOpenGL m_borderGeometry; - QDeclarativePolygonMapItemPrivateOpenGL::RootNode *m_rootNode = nullptr; - MapPolygonNodeGL *m_node = nullptr; - MapPolylineNodeOpenGLExtruded *m_polylinenode = nullptr; -}; - QT_END_NAMESPACE #endif // QDECLARATIVERECTANGLEMAPITEM_P_P_H diff --git a/src/location/quickmapitems/qgeosimplify.cpp b/src/location/quickmapitems/qgeosimplify.cpp deleted file mode 100644 index ad5a3c5d..00000000 --- a/src/location/quickmapitems/qgeosimplify.cpp +++ /dev/null @@ -1,167 +0,0 @@ -/**************************************************************************** -** -** Qt adaptation of geosimplify-js -** Copyright (C) 2017 Daniel Patterson -** See 3rdParty/geosimplify.js for the original license. -** -** Copyright (C) 2020 Paolo Angelelli -** Copyright (C) 2022 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtLocation module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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 https://www.qt.io/terms-conditions. For further -** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qgeosimplify_p.h" -#include -#include -#include - -QT_BEGIN_NAMESPACE - -namespace { - -// p, a and b are intended as "unwrapped" around the left bound -inline QDoubleVector2D closestPoint(const QDoubleVector2D &p, - const QDoubleVector2D &a, - const QDoubleVector2D &b) -{ - if (a == b) - return a; - - const double u = ((p.x() - a.x()) * (b.x() - a.x()) + (p.y() - a.y()) * (b.y() - a.y())) - / (b - a).lengthSquared(); - const 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 candidate; -} - -inline double getDist(QDoubleVector2D a, QDoubleVector2D b, double leftBound) -{ - if (a.x() > 1.0) - a.setX(a.x() - leftBound); // wrap X - if (b.x() > 1.0) - b.setX(b.x() - leftBound); // wrap X - return QWebMercator::mercatorToCoord(a).distanceTo(QWebMercator::mercatorToCoord(b)); -} - -// doublevectors Intended as wrapped -inline double getSegDist(const QDoubleVector2D &p, - const QDoubleVector2D &a, - const QDoubleVector2D &b, - double leftBound) -{ - const QDoubleVector2D intersection = closestPoint(p, a, b); - return getDist(intersection, p, leftBound); -} - -inline QGeoCoordinate unwrappedToGeo(QDoubleVector2D p, double leftBound) -{ - if (p.x() > 1.0) - p.setX(p.x() - leftBound); - return QWebMercator::mercatorToCoord(p); -} - -double pixelDistanceAtZoomAndLatitude(int zoom, double latitude) -{ - const double den = double((1 << (zoom + 8))); - const double pixelDist = (QLocationUtils::earthMeanCircumference() - * std::cos(QLocationUtils::radians(latitude))) - / den; - return pixelDist; -} - -// simplification using Ramer-Douglas-Peucker algorithm -void simplifyDouglasPeuckerStepZL(const QList &points, double leftBound, - qsizetype first, qsizetype last, int zoomLevel, - QList &simplified) -{ - const QGeoCoordinate firstC = unwrappedToGeo(points.at(first), leftBound); - const QGeoCoordinate lastC = unwrappedToGeo(points.at(last), leftBound); - double maxDistanceFound = (pixelDistanceAtZoomAndLatitude(zoomLevel, firstC.latitude()) - + pixelDistanceAtZoomAndLatitude(zoomLevel, lastC.latitude())) * 0.5; - qsizetype index = -1; - - const auto &firstPoint = points.at(first); - const auto &lastPoint = points.at(last); - for (qsizetype i = first + 1; i < last; i++) { - const double distance = getSegDist(points.at(i), - firstPoint, - lastPoint, - leftBound); - - if (distance > maxDistanceFound) { - index = i; - maxDistanceFound = distance; - } - } - - if (index > 0) { - if (index - first > 1) { - simplifyDouglasPeuckerStepZL(points, leftBound, - first, index, zoomLevel, - simplified); - } - simplified.append(points.at(index)); - if (last - index > 1) { - simplifyDouglasPeuckerStepZL(points, leftBound, - index, last, zoomLevel, - simplified); - } - } -} - -} // anonymous namespace - -namespace QGeoSimplify { - -QList geoSimplifyZL(const QList &points, - double leftBound, int zoomLevel) -{ - if (points.size() <= 2) - return points; - - const qsizetype last = points.size() - 1; - QList simplified { points.first() }; - simplifyDouglasPeuckerStepZL(points, leftBound, 0, last, zoomLevel, simplified); - simplified.append(points.at(last)); - return simplified; -} - -} - -QT_END_NAMESPACE diff --git a/src/location/quickmapitems/qgeosimplify_p.h b/src/location/quickmapitems/qgeosimplify_p.h deleted file mode 100644 index 1f5c9783..00000000 --- a/src/location/quickmapitems/qgeosimplify_p.h +++ /dev/null @@ -1,75 +0,0 @@ -/**************************************************************************** -** -** Qt adaptation of geosimplify.js, https://github.com/mapbox/geosimplify-js, (c) 2017, Mapbox -** -** Copyright (C) 2020 Paolo Angelelli -** Copyright (C) 2022 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtLocation module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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 https://www.qt.io/terms-conditions. For further -** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QGEOSIMPLIFY_P_H -#define QGEOSIMPLIFY_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 - -QT_BEGIN_NAMESPACE - -class QDoubleVector2D; - -namespace QGeoSimplify -{ - // offsetTolerance - how far outside the straight line a point - // needs to be for it to be "kept" - // This function tries to be adaptive in the offsetTolerance across latitudes, - // and return a simplification adequate for the given zoomLevel. - QList geoSimplifyZL(const QList &points, - double leftBound, int zoomLevel); // in meters -} - -QT_END_NAMESPACE - -#endif // QGEOSIMPLIFY_P_H diff --git a/src/location/quickmapitems/rhi/qdeclarativecirclemapitem_rhi.cpp b/src/location/quickmapitems/rhi/qdeclarativecirclemapitem_rhi.cpp new file mode 100644 index 00000000..0a09aa4e --- /dev/null +++ b/src/location/quickmapitems/rhi/qdeclarativecirclemapitem_rhi.cpp @@ -0,0 +1,158 @@ +/**************************************************************************** + ** + ** Copyright (C) 2022 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the QtLocation module of the Qt Toolkit. + ** + ** $QT_BEGIN_LICENSE:LGPL$ + ** 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 https://www.qt.io/terms-conditions. For further + ** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General + ** Public license version 3 or any later version approved by the KDE Free + ** Qt Foundation. The licenses are as published by the Free Software + ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 + ** included in the packaging of this file. Please review the following + ** information to ensure the GNU General Public License requirements will + ** be met: https://www.gnu.org/licenses/gpl-2.0.html and + ** https://www.gnu.org/licenses/gpl-3.0.html. + ** + ** $QT_END_LICENSE$ + ** + ****************************************************************************/ + +#include "qdeclarativecirclemapitem_rhi_p.h" + +QT_BEGIN_NAMESPACE + +QDeclarativeCircleMapItemPrivateOpenGL::~QDeclarativeCircleMapItemPrivateOpenGL() {} + +void QDeclarativeCircleMapItemPrivateOpenGL::updatePolish() +{ + if (m_circle.m_circle.isEmpty()) { + m_geometry.clear(); + m_borderGeometry.clear(); + m_circle.setWidth(0); + m_circle.setHeight(0); + return; + } + + QScopedValueRollback rollback(m_circle.m_updatingGeometry); + m_circle.m_updatingGeometry = true; + const qreal lineWidth = m_circle.m_border.width(); + const QColor &lineColor = m_circle.m_border.color(); + const QColor &fillColor = m_circle.color(); + if (fillColor.alpha() != 0) { + m_geometry.updateSourcePoints(*m_circle.map(), m_circlePath); + m_geometry.markScreenDirty(); + m_geometry.updateScreenPoints(*m_circle.map(), lineWidth, lineColor); + } else { + m_geometry.clearBounds(); + } + + QGeoMapItemGeometry * geom = &m_geometry; + m_borderGeometry.clearScreen(); + if (lineColor.alpha() != 0 && lineWidth > 0) { + m_borderGeometry.updateSourcePoints(*m_circle.map(), m_circle.m_circle); + m_borderGeometry.markScreenDirty(); + m_borderGeometry.updateScreenPoints(*m_circle.map(), lineWidth); + geom = &m_borderGeometry; + } + m_circle.setWidth(geom->sourceBoundingBox().width()); + m_circle.setHeight(geom->sourceBoundingBox().height()); + m_circle.setPosition(1.0 * geom->firstPointOffset() - QPointF(lineWidth * 0.5,lineWidth * 0.5)); +} + +QSGNode *QDeclarativeCircleMapItemPrivateOpenGL::updateMapItemPaintNode(QSGNode *oldNode, + QQuickItem::UpdatePaintNodeData *data) +{ + Q_UNUSED(data); + + if (!m_rootNode || !oldNode) { + m_rootNode = new QDeclarativePolygonMapItemPrivateOpenGL::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(oldNode); + } + + const QGeoMap *map = m_circle.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_circle.m_border.color(), + float(m_circle.m_border.width()), + &m_borderGeometry, + combinedMatrix, + cameraCenter, + Qt::SquareCap, + true, + 30); // No LOD for circles + m_borderGeometry.setPreserveGeometry(false); + m_borderGeometry.markClean(); + } else { + m_polylinenode->setSubtreeBlocked(true); + } + if (m_geometry.isScreenDirty()) { + m_node->update(m_circle.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 QDeclarativeCircleMapItemPrivateOpenGL::contains(const QPointF &point) const +{ + const qreal lineWidth = m_circle.m_border.width(); + const QColor &lineColor = m_circle.m_border.color(); + const QRectF &bounds = (lineColor.alpha() != 0 && lineWidth > 0) + ? m_borderGeometry.sourceBoundingBox() + : m_geometry.sourceBoundingBox(); + if (bounds.contains(point)) { + QDeclarativeGeoMap *m = m_circle.quickMap(); + if (m) { + const QGeoCoordinate crd = m->toCoordinate(m->mapFromItem(&m_circle, point)); + const auto &projection = static_cast( + m_circle.map()->geoProjection()); + return m_circle.m_circle.contains(crd) + || m_borderGeometry.contains(m_circle.mapToItem(m_circle.quickMap(), point), + m_circle.border()->width(), projection); + } else { + return true; + } + } + return false; +} + +QT_END_NAMESPACE + diff --git a/src/location/quickmapitems/rhi/qdeclarativecirclemapitem_rhi_p.h b/src/location/quickmapitems/rhi/qdeclarativecirclemapitem_rhi_p.h new file mode 100644 index 00000000..9bf60106 --- /dev/null +++ b/src/location/quickmapitems/rhi/qdeclarativecirclemapitem_rhi_p.h @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Paolo Angelelli +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVECIRCLEMAPITEM_RHI_P_H +#define QDECLARATIVECIRCLEMAPITEM_RHI_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 +#include +#include + +#include "qdeclarativepolygonmapitem_rhi_p.h" + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeCircleMapItemPrivateOpenGL: public QDeclarativeCircleMapItemPrivate +{ +public: + QDeclarativeCircleMapItemPrivateOpenGL(QDeclarativeCircleMapItem &circle) : QDeclarativeCircleMapItemPrivate(circle) + { + } + + QDeclarativeCircleMapItemPrivateOpenGL(QDeclarativeCircleMapItemPrivate &other) + : QDeclarativeCircleMapItemPrivate(other) + { + } + + ~QDeclarativeCircleMapItemPrivateOpenGL() override; + + void onLinePropertiesChanged() override + { + m_circle.m_dirtyMaterial = true; + afterViewportChanged(); + } + void markScreenDirtyAndUpdate() + { + // preserveGeometry is cleared in updateMapItemPaintNode + m_geometry.markScreenDirty(); + m_borderGeometry.markScreenDirty(); + m_circle.polishAndUpdate(); + } + void markSourceDirtyAndUpdate() override + { + updateCirclePath(); + preserveGeometry(); + m_geometry.markSourceDirty(); + m_borderGeometry.markSourceDirty(); + m_circle.polishAndUpdate(); + } + void preserveGeometry() + { + m_geometry.setPreserveGeometry(true, m_leftBound); + m_borderGeometry.setPreserveGeometry(true, m_leftBound); + } + void onMapSet() override + { + markSourceDirtyAndUpdate(); + } + void onGeoGeometryChanged() override + { + + markSourceDirtyAndUpdate(); + } + void onItemGeometryChanged() override + { + onGeoGeometryChanged(); + } + void afterViewportChanged() override + { + preserveGeometry(); + markScreenDirtyAndUpdate(); + } + void updatePolish() override; + QSGNode * updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) override; + bool contains(const QPointF &point) const override; + + QGeoMapPolygonGeometryOpenGL m_geometry; + QGeoMapPolylineGeometryOpenGL m_borderGeometry; + QDeclarativePolygonMapItemPrivateOpenGL::RootNode *m_rootNode = nullptr; + MapPolygonNodeGL *m_node = nullptr; + MapPolylineNodeOpenGLExtruded *m_polylinenode = nullptr; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVECIRCLEMAPITEM_RHI_P_H diff --git a/src/location/quickmapitems/rhi/qdeclarativepolygonmapitem_rhi.cpp b/src/location/quickmapitems/rhi/qdeclarativepolygonmapitem_rhi.cpp new file mode 100644 index 00000000..1dd5efea --- /dev/null +++ b/src/location/quickmapitems/rhi/qdeclarativepolygonmapitem_rhi.cpp @@ -0,0 +1,303 @@ +/**************************************************************************** + ** + ** Copyright (C) 2022 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the QtLocation module of the Qt Toolkit. + ** + ** $QT_BEGIN_LICENSE:LGPL$ + ** 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 https://www.qt.io/terms-conditions. For further + ** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General + ** Public license version 3 or any later version approved by the KDE Free + ** Qt Foundation. The licenses are as published by the Free Software + ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 + ** included in the packaging of this file. Please review the following + ** information to ensure the GNU General Public License requirements will + ** be met: https://www.gnu.org/licenses/gpl-2.0.html and + ** https://www.gnu.org/licenses/gpl-3.0.html. + ** + ** $QT_END_LICENSE$ + ** + ****************************************************************************/ + +#include "qdeclarativegeomapitemutils_p.h" +#include "qdeclarativepolygonmapitem_p.h" +#include "qdeclarativepolylinemapitem_p_p.h" +#include "qdeclarativepolygonmapitem_p_p.h" +#include "qdeclarativepolygonmapitem_rhi_p.h" +#include "qdeclarativerectanglemapitem_p_p.h" +#include "error_messages_p.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +MapPolygonShader::MapPolygonShader() : QSGMaterialShader(*new QSGMaterialShaderPrivate(this)) +{ + setShaderFileName(VertexStage, QLatin1String(":/location/quickmapitems/rhi/shaders/polygon.vert.qsb")); + setShaderFileName(FragmentStage, QLatin1String(":/location/quickmapitems/rhi/shaders/polygon.frag.qsb")); +} + +bool MapPolygonShader::updateUniformData(RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) +{ + Q_ASSERT(oldEffect == nullptr || newEffect->type() == oldEffect->type()); + MapPolygonMaterial *oldMaterial = static_cast(oldEffect); + MapPolygonMaterial *newMaterial = static_cast(newEffect); + + const QColor &c = newMaterial->color(); + const QMatrix4x4 &geoProjection = newMaterial->geoProjection(); + const QDoubleVector3D ¢er = newMaterial->center(); + + // It is safer to use vec4 instead on vec3, as described in: + // https://www.khronos.org/opengl/wiki/Interface_Block_(GLSL)#Memory_layout + QVector4D vecCenter, vecCenter_lowpart; + for (int i = 0; i < 3; i++) + QLocationUtils::split_double(center.get(i), &vecCenter[i], &vecCenter_lowpart[i]); + vecCenter[3] = 0; + vecCenter_lowpart[3] = 0; + + int offset = 0; + char *buf_p = state.uniformData()->data(); + + if (state.isMatrixDirty()) { + const QMatrix4x4 m = state.projectionMatrix(); + memcpy(buf_p + offset, m.constData(), 4*4*4); + } + offset += 4*4*4; + + memcpy(buf_p + offset, geoProjection.constData(), 4*4*4); offset+=4*4*4; + + memcpy(buf_p + offset, &vecCenter, 4*4); offset += 4*4; + + memcpy(buf_p + offset, &vecCenter_lowpart, 4*4); offset+=4*4; + + const float wrapOffset = newMaterial->wrapOffset(); + memcpy(buf_p + offset, &wrapOffset, 4); offset+=4; + + offset += 4+4+4; // Padding + + if (oldMaterial == nullptr || c != oldMaterial->color() || state.isOpacityDirty()) { + float opacity = state.opacity() * c.alphaF(); + QVector4D v(c.redF() * opacity, + c.greenF() * opacity, + c.blueF() * opacity, + opacity); + memcpy(buf_p + offset, &v, 4*4); + } + offset+=4*4; + + return true; +} + +QSGMaterialShader *MapPolygonMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const +{ + Q_UNUSED(renderMode); + return new MapPolygonShader(); +} + +int MapPolygonMaterial::compare(const QSGMaterial *other) const +{ + const MapPolygonMaterial &o = *static_cast(other); + if (o.m_center == m_center && o.m_geoProjection == m_geoProjection && o.m_wrapOffset == m_wrapOffset) + return QSGFlatColorMaterial::compare(other); + return -1; +} + +QSGMaterialType *MapPolygonMaterial::type() const +{ + static QSGMaterialType type; + return &type; +} + +MapPolygonNodeGL::MapPolygonNodeGL() : + //fill_material_(this), + fill_material_(), + geometry_(QSGGeometry::defaultAttributes_Point2D(), 0) +{ + geometry_.setDrawingMode(QSGGeometry::DrawTriangles); + QSGGeometryNode::setMaterial(&fill_material_); + QSGGeometryNode::setGeometry(&geometry_); +} + +MapPolygonNodeGL::~MapPolygonNodeGL() +{ +} + +/*! + \internal +*/ +void MapPolygonNodeGL::update(const QColor &fillColor, + const QGeoMapPolygonGeometryOpenGL *fillShape, + const QMatrix4x4 &geoProjection, + const QDoubleVector3D ¢er) +{ + if (fillShape->m_screenIndices.size() < 3 || fillColor.alpha() == 0) { + setSubtreeBlocked(true); + return; + } + setSubtreeBlocked(false); + + QSGGeometry *fill = QSGGeometryNode::geometry(); + if (fillShape->m_dataChanged || !fill->vertexCount()) { + fillShape->allocateAndFillPolygon(fill); + markDirty(DirtyGeometry); + fillShape->m_dataChanged = false; + } + + //if (fillColor != fill_material_.color()) // Any point in optimizing this? + { + fill_material_.setColor(fillColor); + fill_material_.setGeoProjection(geoProjection); + fill_material_.setCenter(center); + fill_material_.setWrapOffset(fillShape->m_wrapOffset - 1); + setMaterial(&fill_material_); + markDirty(DirtyMaterial); + } +} + +QDeclarativePolygonMapItemPrivateOpenGL::~QDeclarativePolygonMapItemPrivateOpenGL() {} + + +void QDeclarativePolygonMapItemPrivateOpenGL::updatePolish() +{ + if (m_poly.m_geopoly.perimeter().length() == 0) { // Possibly cleared + m_geometry.clear(); + m_borderGeometry.clear(); + m_poly.setWidth(0); + m_poly.setHeight(0); + return; + } + + QScopedValueRollback 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 *QDeclarativePolygonMapItemPrivateOpenGL::updateMapItemPaintNode(QSGNode *oldNode, + QQuickItem::UpdatePaintNodeData *data) +{ + 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(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 QDeclarativePolygonMapItemPrivateOpenGL::contains(const QPointF &point) const +{ + 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)); + const auto &projection = static_cast( + m_poly.map()->geoProjection()); + return m_poly.m_geopoly.contains(crd) + || m_borderGeometry.contains(m_poly.mapToItem(m_poly.quickMap(), point), + m_poly.border()->width(), projection); + } else { + return true; + } + } + return false; +} + +QT_END_NAMESPACE diff --git a/src/location/quickmapitems/rhi/qdeclarativepolygonmapitem_rhi_p.h b/src/location/quickmapitems/rhi/qdeclarativepolygonmapitem_rhi_p.h new file mode 100644 index 00000000..5d178aad --- /dev/null +++ b/src/location/quickmapitems/rhi/qdeclarativepolygonmapitem_rhi_p.h @@ -0,0 +1,243 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Paolo Angelelli +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPOLYGONMAPITEM_RHI_P_H +#define QDECLARATIVEPOLYGONMAPITEM_RHI_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 +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +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); + } + + QSGMaterialShader *createShader(QSGRendererInterface::RenderMode renderMode) 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 MapPolygonShader : public QSGMaterialShader +{ +public: + MapPolygonShader(); + + bool updateUniformData(RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; +}; + +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 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; + QSGNode * updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) override; + bool contains(const QPointF &point) const override; + + QGeoMapPolygonGeometryOpenGL m_geometry; + QGeoMapPolylineGeometryOpenGL m_borderGeometry; + RootNode *m_rootNode = nullptr; + MapPolygonNodeGL *m_node = nullptr; + MapPolylineNodeOpenGLExtruded *m_polylinenode = nullptr; +}; + + +QT_END_NAMESPACE + +#endif // QDECLARATIVEPOLYGONMAPITEM_RHI_P_H diff --git a/src/location/quickmapitems/rhi/qdeclarativepolylinemapitem_rhi.cpp b/src/location/quickmapitems/rhi/qdeclarativepolylinemapitem_rhi.cpp new file mode 100644 index 00000000..b7ddf22a --- /dev/null +++ b/src/location/quickmapitems/rhi/qdeclarativepolylinemapitem_rhi.cpp @@ -0,0 +1,517 @@ +/**************************************************************************** + ** + ** Copyright (C) 2022 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the QtLocation module of the Qt Toolkit. + ** + ** $QT_BEGIN_LICENSE:LGPL$ + ** 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 https://www.qt.io/terms-conditions. For further + ** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General + ** Public license version 3 or any later version approved by the KDE Free + ** Qt Foundation. The licenses are as published by the Free Software + ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 + ** included in the packaging of this file. Please review the following + ** information to ensure the GNU General Public License requirements will + ** be met: https://www.gnu.org/licenses/gpl-2.0.html and + ** https://www.gnu.org/licenses/gpl-3.0.html. + ** + ** $QT_END_LICENSE$ + ** + ****************************************************************************/ + +#include "qdeclarativepolygonmapitem_rhi_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +MapPolylineNodeOpenGLLineStrip::MapPolylineNodeOpenGLLineStrip() +: geometry_(QSGGeometry::defaultAttributes_Point2D(), 0) +{ + geometry_.setDrawingMode(QSGGeometry::DrawLineStrip); + QSGGeometryNode::setMaterial(&fill_material_); + QSGGeometryNode::setGeometry(&geometry_); +} + +MapPolylineNodeOpenGLLineStrip::~MapPolylineNodeOpenGLLineStrip() +{ + +} + +void MapPolylineNodeOpenGLLineStrip::update(const QColor &fillColor, + const qreal lineWidth, + const QGeoMapPolylineGeometryOpenGL *shape, + const QMatrix4x4 &geoProjection, + const QDoubleVector3D ¢er, + const Qt::PenCapStyle /*capStyle*/) +{ + if (shape->m_screenVertices->size() < 2) { + setSubtreeBlocked(true); + return; + } else { + setSubtreeBlocked(false); + } + + QSGGeometry *fill = QSGGeometryNode::geometry(); + if (shape->m_dataChanged) { + shape->allocateAndFillLineStrip(fill); + markDirty(DirtyGeometry); + shape->m_dataChanged = false; + } + fill->setLineWidth(lineWidth); + fill_material_.setLineWidth(lineWidth); // to make the material not compare equal if linewidth changes + +// if (fillColor != fill_material_.color()) + { + fill_material_.setWrapOffset(shape->m_wrapOffset - 1); + fill_material_.setColor(fillColor); + fill_material_.setGeoProjection(geoProjection); + fill_material_.setCenter(center); + setMaterial(&fill_material_); + markDirty(DirtyMaterial); + } +} + +MapPolylineShaderLineStrip::MapPolylineShaderLineStrip() : QSGMaterialShader(*new QSGMaterialShaderPrivate(this)) +{ + setShaderFileName(VertexStage, QLatin1String(":/location/quickmapitems/rhi/shaders/polyline_linestrip.vert.qsb")); + setShaderFileName(FragmentStage, QLatin1String(":/location/quickmapitems/rhi/shaders/polyline_linestrip.frag.qsb")); +} + +bool MapPolylineShaderLineStrip::updateUniformData(QSGMaterialShader::RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) +{ + Q_ASSERT(oldEffect == nullptr || newEffect->type() == oldEffect->type()); + MapPolylineMaterial *oldMaterial = static_cast(oldEffect); + MapPolylineMaterial *newMaterial = static_cast(newEffect); + + const QColor &c = newMaterial->color(); + const QMatrix4x4 &geoProjection = newMaterial->geoProjection(); + const QDoubleVector3D ¢er = newMaterial->center(); + + QVector4D vecCenter, vecCenter_lowpart; + for (int i = 0; i < 3; i++) + QLocationUtils::split_double(center.get(i), &vecCenter[i], &vecCenter_lowpart[i]); + vecCenter[3] = 0; + vecCenter_lowpart[3] = 0; + + int offset = 0; + char *buf_p = state.uniformData()->data(); + + if (state.isMatrixDirty()) { + const QMatrix4x4 m = state.projectionMatrix(); + memcpy(buf_p + offset, m.constData(), 4*4*4); + } + offset += 4*4*4; + + memcpy(buf_p + offset, geoProjection.constData(), 4*4*4); offset+=4*4*4; + + memcpy(buf_p + offset, &vecCenter, 4*4); offset += 4*4; + + memcpy(buf_p + offset, &vecCenter_lowpart, 4*4); offset+=4*4; + + if (state.isOpacityDirty()) { + const float opacity = state.opacity(); + memcpy(buf_p + offset, &opacity, 4); + } + offset += 4; + + float wrapOffset = newMaterial->wrapOffset(); + memcpy(buf_p + offset, &wrapOffset, 4); offset+=4; + + offset+=8; // float padding + + if (oldMaterial == nullptr || c != oldMaterial->color() || state.isOpacityDirty()) { + float opacity = state.opacity() * c.alphaF(); + QVector4D v(c.redF() * opacity, + c.greenF() * opacity, + c.blueF() * opacity, + opacity); + memcpy(buf_p + offset, &v, 4*4); + } + offset+=4*4; + + return true; +} + +QSGMaterialShader *MapPolylineMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const +{ + Q_UNUSED(renderMode); + return new MapPolylineShaderLineStrip(); +} + +QSGMaterialType *MapPolylineMaterial::type() const +{ + static QSGMaterialType type; + return &type; +} + +int MapPolylineMaterial::compare(const QSGMaterial *other) const +{ + const MapPolylineMaterial &o = *static_cast(other); + if (o.m_center == m_center && o.m_geoProjection == m_geoProjection && o.m_wrapOffset == m_wrapOffset && o.m_lineWidth == m_lineWidth) + return QSGFlatColorMaterial::compare(other); + return -1; +} + + +const QSGGeometry::AttributeSet &MapPolylineNodeOpenGLExtruded::attributesMapPolylineTriangulated() +{ + return MapPolylineEntry::attributes(); +} + +MapPolylineNodeOpenGLExtruded::MapPolylineNodeOpenGLExtruded() +: m_geometryTriangulating(MapPolylineNodeOpenGLExtruded::attributesMapPolylineTriangulated(), + 0 /* vtx cnt */, 0 /* index cnt */, QSGGeometry::UnsignedIntType /* index type */) +{ + m_geometryTriangulating.setDrawingMode(QSGGeometry::DrawTriangles); + QSGGeometryNode::setMaterial(&fill_material_); + QSGGeometryNode::setGeometry(&m_geometryTriangulating); +} + +MapPolylineNodeOpenGLExtruded::~MapPolylineNodeOpenGLExtruded() +{ + +} + +bool QGeoMapPolylineGeometryOpenGL::allocateAndFillEntries(QSGGeometry *geom, + bool closed, + unsigned int zoom) const +{ + // Select LOD. Generate if not present. Assign it to m_screenVertices; + if (m_dataChanged) { + // it means that the data really changed. + // So synchronously produce LOD 1, and enqueue the requested one if != 0 or 1. + // Select 0 if 0 is requested, or 1 in all other cases. + selectLODOnDataChanged(zoom, m_bboxLeftBoundWrapped.x()); + } else { + // Data has not changed, but active LOD != requested LOD. + // So, if there are no active tasks, try to change to the correct one. + if (!selectLODOnLODMismatch(zoom, m_bboxLeftBoundWrapped.x(), closed)) + return false; + } + + const QList &v = *m_screenVertices; + if (v.size() < 2) { + geom->allocate(0, 0); + return true; + } + const int numSegments = (v.size() - 1); + + const int numIndices = numSegments * 6; // six vertices per line segment + geom->allocate(numIndices); + MapPolylineNodeOpenGLExtruded::MapPolylineEntry *vertices = + static_cast(geom->vertexData()); + + for (int i = 0; i < numSegments; ++i) { + MapPolylineNodeOpenGLExtruded::MapPolylineEntry e; + const QDeclarativeGeoMapItemUtils::vec2 &cur = v[i]; + const QDeclarativeGeoMapItemUtils::vec2 &next = v[i+1]; + e.triangletype = 1.0; + e.next = next; + e.prev = cur; + e.pos = cur; + e.direction = 1.0; + e.vertextype = -1.0; + vertices[i*6] = e; + e.direction = -1.0; + vertices[i*6+1] = e; + e.pos = next; + e.vertextype = 1.0; + vertices[i*6+2] = e; + + // Second tri + e.triangletype = -1.0; + e.direction = -1.0; + vertices[i*6+3] = e; + e.direction = 1.0; + vertices[i*6+4] = e; + e.pos = cur; + e.vertextype = -1.0; + vertices[i*6+5] = e; + + if (i != 0) { + vertices[i*6].prev = vertices[i*6+1].prev = vertices[i*6+5].prev = v[i-1]; + } else { + if (closed) { + vertices[i*6].prev = vertices[i*6+1].prev = vertices[i*6+5].prev = v[numSegments - 1]; + } else { + vertices[i*6].triangletype = vertices[i*6+1].triangletype = vertices[i*6+5].triangletype = 2.0; + } + } + if (i != numSegments - 1) { + vertices[i*6+2].next = vertices[i*6+3].next = vertices[i*6+4].next = v[i+2]; + } else { + if (closed) { + vertices[i*6+2].next = vertices[i*6+3].next = vertices[i*6+4].next = v[1]; + } else { + vertices[i*6+2].triangletype = vertices[i*6+3].triangletype = vertices[i*6+4].triangletype = 3.0; + } + } + } + return true; +} + +void QGeoMapPolylineGeometryOpenGL::allocateAndFillLineStrip(QSGGeometry *geom, + int lod) const +{ + // Select LOD. Generate if not present. Assign it to m_screenVertices; + Q_UNUSED(lod); + + const QList &vx = *m_screenVertices; + geom->allocate(vx.size()); + + QSGGeometry::Point2D *pts = geom->vertexDataAsPoint2D(); + for (qsizetype i = 0; i < vx.size(); ++i) + pts[i].set(vx[i].x, vx[i].y); +} + +void MapPolylineNodeOpenGLExtruded::update(const QColor &fillColor, + float lineWidth, + const QGeoMapPolylineGeometryOpenGL *shape, + const QMatrix4x4 &geoProjection, + const QDoubleVector3D ¢er, + const Qt::PenCapStyle capStyle, + bool closed, + unsigned int zoom) +{ + // shape->size() == number of triangles + if (shape->m_screenVertices->size() < 2 + || lineWidth < 0.5 || fillColor.alpha() == 0) { // number of points + setSubtreeBlocked(true); + return; + } else { + setSubtreeBlocked(false); + } + + QSGGeometry *fill = QSGGeometryNode::geometry(); + if (shape->m_dataChanged || !shape->isLODActive(zoom) || !fill->vertexCount()) { // fill->vertexCount for when node gets destroyed by MapItemBase bcoz of opacity, then recreated. + if (shape->allocateAndFillEntries(fill, closed, zoom)) { + markDirty(DirtyGeometry); + shape->m_dataChanged = false; + } + } + + // Update this +// if (fillColor != fill_material_.color()) + { + fill_material_.setWrapOffset(shape->m_wrapOffset - 1); + fill_material_.setColor(fillColor); + fill_material_.setGeoProjection(geoProjection); + fill_material_.setCenter(center); + fill_material_.setLineWidth(lineWidth); + fill_material_.setMiter(capStyle != Qt::FlatCap); + setMaterial(&fill_material_); + markDirty(DirtyMaterial); + } +} + +MapPolylineShaderExtruded::MapPolylineShaderExtruded() : QSGMaterialShader(*new QSGMaterialShaderPrivate(this)) +{ + // 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. + setShaderFileName(VertexStage, QLatin1String(":/location/quickmapitems/rhi/shaders/polyline_extruded.vert.qsb")); + setShaderFileName(FragmentStage, QLatin1String(":/location/quickmapitems/rhi/shaders/polyline_extruded.frag.qsb")); +} + +bool MapPolylineShaderExtruded::updateUniformData(QSGMaterialShader::RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) +{ + Q_ASSERT(oldEffect == nullptr || newEffect->type() == oldEffect->type()); + MapPolylineMaterialExtruded *oldMaterial = static_cast(oldEffect); + MapPolylineMaterialExtruded *newMaterial = static_cast(newEffect); + + const QColor &c = newMaterial->color(); + const QMatrix4x4 &geoProjection = newMaterial->geoProjection(); + const QDoubleVector3D ¢er = newMaterial->center(); + + // It is safer to use vec4 instead on vec3, as described in: + // https://www.khronos.org/opengl/wiki/Interface_Block_(GLSL)#Memory_layout + QVector4D vecCenter, vecCenter_lowpart; + for (int i = 0; i < 3; i++) + QLocationUtils::split_double(center.get(i), &vecCenter[i], &vecCenter_lowpart[i]); + vecCenter[3] = 0; + vecCenter_lowpart[3] = 0; + + int offset = 0; + char *buf_p = state.uniformData()->data(); + + if (state.isMatrixDirty()) { + const QMatrix4x4 m = state.projectionMatrix(); + memcpy(buf_p + offset, m.constData(), 4*4*4); + } + offset += 4*4*4; + + memcpy(buf_p + offset, geoProjection.constData(), 4*4*4); offset+=4*4*4; + + memcpy(buf_p + offset, &vecCenter, 4*4); offset += 4*4; + + memcpy(buf_p + offset, &vecCenter_lowpart, 4*4); offset+=4*4; + + const float lineWidth = newMaterial->lineWidth(); + memcpy(buf_p + offset, &lineWidth, 4); offset+=4; + + const QRectF viewportRect = state.viewportRect(); + const float aspect = float(viewportRect.width() / viewportRect.height()); + memcpy(buf_p + offset, &aspect, 4); offset+=4; + + offset += 4; // Padding + + int miter = newMaterial->miter(); + memcpy(buf_p + offset, &miter, 4); offset+=4; + + if (oldMaterial == nullptr || c != oldMaterial->color() || state.isOpacityDirty()) { + float opacity = state.opacity() * c.alphaF(); + QVector4D v(c.redF() * opacity, + c.greenF() * opacity, + c.blueF() * opacity, + opacity); + memcpy(buf_p + offset, &v, 4*4); + } + offset+=4*4; + + const float wrapOffset = newMaterial->wrapOffset(); + memcpy(buf_p + offset, &wrapOffset, 4); offset+=4; + + return true; +} + +QSGMaterialShader *MapPolylineMaterialExtruded::createShader(QSGRendererInterface::RenderMode renderMode) const +{ + Q_UNUSED(renderMode); + return new MapPolylineShaderExtruded(); +} + +QSGMaterialType *MapPolylineMaterialExtruded::type() const +{ + static QSGMaterialType type; + return &type; +} + +int MapPolylineMaterialExtruded::compare(const QSGMaterial *other) const +{ + const MapPolylineMaterialExtruded &o = *static_cast(other); + if (o.m_miter == m_miter) + return MapPolylineMaterial::compare(other); + return -1; +} + +QDeclarativePolylineMapItemPrivateOpenGLLineStrip::~QDeclarativePolylineMapItemPrivateOpenGLLineStrip() {} + +bool QDeclarativePolylineMapItemPrivateOpenGLLineStrip::contains(const QPointF &point) const +{ + return m_geometry.contains(m_poly.mapToItem(m_poly.quickMap(), point), + m_poly.line()->width(), + static_cast(m_poly.map()->geoProjection())); +} + +void QDeclarativePolylineMapItemPrivateOpenGLLineStrip::updatePolish() +{ + 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 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 *QDeclarativePolylineMapItemPrivateOpenGLLineStrip::updateMapItemPaintNode(QSGNode *oldNode, + QQuickItem::UpdatePaintNodeData *data) +{ + Q_UNUSED(data); + + if (!m_node || !oldNode) { + m_node = new MapPolylineNodeOpenGLLineStrip(); + if (oldNode) + delete oldNode; + } else { + m_node = static_cast(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; +} + +QDeclarativePolylineMapItemPrivateOpenGLExtruded::~QDeclarativePolylineMapItemPrivateOpenGLExtruded() {} + +QSGNode *QDeclarativePolylineMapItemPrivateOpenGLExtruded::updateMapItemPaintNode(QSGNode *oldNode, + QQuickItem::UpdatePaintNodeData *data) +{ + Q_UNUSED(data); + const QGeoMap *map = m_poly.map(); + const QGeoProjectionWebMercator &p = static_cast(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(oldNode); + } + + //TODO: update only material + if (m_geometry.isScreenDirty() || m_poly.m_dirtyMaterial) { + nodeTri->update(color, + lineWidth , + &m_geometry, + combinedMatrix, + cameraCenter, + Qt::FlatCap, + 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; +} + + +QT_END_NAMESPACE diff --git a/src/location/quickmapitems/rhi/qdeclarativepolylinemapitem_rhi_p.h b/src/location/quickmapitems/rhi/qdeclarativepolylinemapitem_rhi_p.h new file mode 100644 index 00000000..8387eae6 --- /dev/null +++ b/src/location/quickmapitems/rhi/qdeclarativepolylinemapitem_rhi_p.h @@ -0,0 +1,333 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Paolo Angelelli +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPOLYLINEMAPITEM_RHI_P_H +#define QDECLARATIVEPOLYLINEMAPITEM_RHI_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 +#include + +#include +#include +#include +#include +#include + +#include + +#include + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT MapPolylineShaderLineStrip : public QSGMaterialShader +{ +public: + MapPolylineShaderLineStrip(); + + bool updateUniformData(RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) 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); + } + + QSGMaterialShader *createShader(QSGRendererInterface::RenderMode renderMode) 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; + } + + void setLineWidth(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 MapPolylineShaderExtruded : public QSGMaterialShader +{ +public: + MapPolylineShaderExtruded(); + + bool updateUniformData(RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; +}; + +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(QSGRendererInterface::RenderMode renderMode) const override; + + void setMiter(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 MapPolylineEntry { + QDeclarativeGeoMapItemUtils::vec2 pos; + QDeclarativeGeoMapItemUtils::vec2 prev; + QDeclarativeGeoMapItemUtils::vec2 next; + float direction; + float triangletype; // es2 does not support int attribs + float vertextype; + + 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, + float lineWidth, + const QGeoMapPolylineGeometryOpenGL *shape, + const QMatrix4x4 &geoProjection, + const QDoubleVector3D ¢er, + 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 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; + void updatePolish() override; + QSGNode * updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) override; + + 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; + + MapPolylineNodeOpenGLExtruded *m_nodeTri = nullptr; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEPOLYLINEMAPITEM_RHI_P_H diff --git a/src/location/quickmapitems/rhi/qdeclarativerectanglemapitem_rhi.cpp b/src/location/quickmapitems/rhi/qdeclarativerectanglemapitem_rhi.cpp new file mode 100644 index 00000000..9125aca9 --- /dev/null +++ b/src/location/quickmapitems/rhi/qdeclarativerectanglemapitem_rhi.cpp @@ -0,0 +1,159 @@ +/**************************************************************************** + ** + ** Copyright (C) 2022 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the QtLocation module of the Qt Toolkit. + ** + ** $QT_BEGIN_LICENSE:LGPL$ + ** 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 https://www.qt.io/terms-conditions. For further + ** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General + ** Public license version 3 or any later version approved by the KDE Free + ** Qt Foundation. The licenses are as published by the Free Software + ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 + ** included in the packaging of this file. Please review the following + ** information to ensure the GNU General Public License requirements will + ** be met: https://www.gnu.org/licenses/gpl-2.0.html and + ** https://www.gnu.org/licenses/gpl-3.0.html. + ** + ** $QT_END_LICENSE$ + ** + ****************************************************************************/ + +#include "qdeclarativerectanglemapitem_rhi_p.h" + +QT_BEGIN_NAMESPACE + +QDeclarativeRectangleMapItemPrivateOpenGL::~QDeclarativeRectangleMapItemPrivateOpenGL() {} + + +void QDeclarativeRectangleMapItemPrivateOpenGL::updatePolish() +{ + if (!m_rect.topLeft().isValid() || !m_rect.bottomRight().isValid()) { + m_geometry.clear(); + m_borderGeometry.clear(); + m_rect.setWidth(0); + m_rect.setHeight(0); + return; + } + + QScopedValueRollback rollback(m_rect.m_updatingGeometry); + m_rect.m_updatingGeometry = true; + const qreal lineWidth = m_rect.m_border.width(); + const QColor &lineColor = m_rect.m_border.color(); + const QColor &fillColor = m_rect.color(); + if (fillColor.alpha() != 0) { + m_geometry.updateSourcePoints(*m_rect.map(), m_rect.m_rectangle); + m_geometry.markScreenDirty(); + m_geometry.updateScreenPoints(*m_rect.map(), lineWidth, lineColor); + } else { + m_geometry.clearBounds(); + } + + QGeoMapItemGeometry * geom = &m_geometry; + m_borderGeometry.clearScreen(); + if (lineColor.alpha() != 0 && lineWidth > 0) { + m_borderGeometry.updateSourcePoints(*m_rect.map(), m_rect.m_rectangle); + m_borderGeometry.markScreenDirty(); + m_borderGeometry.updateScreenPoints(*m_rect.map(), lineWidth); + geom = &m_borderGeometry; + } + m_rect.setWidth(geom->sourceBoundingBox().width()); + m_rect.setHeight(geom->sourceBoundingBox().height()); + m_rect.setPosition(1.0 * geom->firstPointOffset() - QPointF(lineWidth * 0.5,lineWidth * 0.5)); +} + +QSGNode *QDeclarativeRectangleMapItemPrivateOpenGL::updateMapItemPaintNode(QSGNode *oldNode, + QQuickItem::UpdatePaintNodeData *data) +{ + Q_UNUSED(data); + + if (!m_rootNode || !oldNode) { + m_rootNode = new QDeclarativePolygonMapItemPrivateOpenGL::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(oldNode); + } + + const QGeoMap *map = m_rect.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_rect.m_border.color(), + float(m_rect.m_border.width()), + &m_borderGeometry, + combinedMatrix, + cameraCenter, + Qt::SquareCap, + true, + 30); // No LOD for rectangles + m_borderGeometry.setPreserveGeometry(false); + m_borderGeometry.markClean(); + } else { + m_polylinenode->setSubtreeBlocked(true); + } + if (m_geometry.isScreenDirty()) { + m_node->update(m_rect.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 QDeclarativeRectangleMapItemPrivateOpenGL::contains(const QPointF &point) const +{ + const qreal lineWidth = m_rect.m_border.width(); + const QColor &lineColor = m_rect.m_border.color(); + const QRectF &bounds = (lineColor.alpha() != 0 && lineWidth > 0) + ? m_borderGeometry.sourceBoundingBox() + : m_geometry.sourceBoundingBox(); + if (bounds.contains(point)) { + QDeclarativeGeoMap *m = m_rect.quickMap(); + if (m) { + const QGeoCoordinate crd = m->toCoordinate(m->mapFromItem(&m_rect, point)); + const auto &projection = static_cast( + m_rect.map()->geoProjection()); + return m_rect.m_rectangle.contains(crd) + || m_borderGeometry.contains(m_rect.mapToItem(m_rect.quickMap(), point), + m_rect.border()->width(), projection); + } else { + return true; + } + } + return false; +} + +QT_END_NAMESPACE + diff --git a/src/location/quickmapitems/rhi/qdeclarativerectanglemapitem_rhi_p.h b/src/location/quickmapitems/rhi/qdeclarativerectanglemapitem_rhi_p.h new file mode 100644 index 00000000..65cf682f --- /dev/null +++ b/src/location/quickmapitems/rhi/qdeclarativerectanglemapitem_rhi_p.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Paolo Angelelli +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVERECTANGLEMAPITEM_RHI_P_H +#define QDECLARATIVERECTANGLEMAPITEM_RHI_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 +#include +#include + +#include "qdeclarativepolygonmapitem_rhi_p.h" + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeRectangleMapItemPrivateOpenGL: public QDeclarativeRectangleMapItemPrivate +{ +public: + QDeclarativeRectangleMapItemPrivateOpenGL(QDeclarativeRectangleMapItem &rect) : QDeclarativeRectangleMapItemPrivate(rect) + { + } + + QDeclarativeRectangleMapItemPrivateOpenGL(QDeclarativeRectangleMapItemPrivate &other) + : QDeclarativeRectangleMapItemPrivate(other) + { + } + + ~QDeclarativeRectangleMapItemPrivateOpenGL() override; + + void markScreenDirtyAndUpdate() + { + // preserveGeometry is cleared in updateMapItemPaintNode + m_geometry.markScreenDirty(); + m_borderGeometry.markScreenDirty(); + m_rect.polishAndUpdate(); + } + void onLinePropertiesChanged() override + { + m_rect.m_dirtyMaterial = true; + afterViewportChanged(); + } + void markSourceDirtyAndUpdate() override + { + m_geometry.markSourceDirty(); + m_borderGeometry.markSourceDirty(); + m_rect.polishAndUpdate(); + } + void preserveGeometry() + { + m_geometry.setPreserveGeometry(true, m_rect.m_rectangle.topLeft()); + m_borderGeometry.setPreserveGeometry(true, m_rect.m_rectangle.topLeft()); + } + void onMapSet() override + { + markSourceDirtyAndUpdate(); + } + void onGeoGeometryChanged() override + { + preserveGeometry(); + markSourceDirtyAndUpdate(); + } + void onItemGeometryChanged() override + { + onGeoGeometryChanged(); + } + void afterViewportChanged() override + { + preserveGeometry(); + markScreenDirtyAndUpdate(); + } + void updatePolish() override; + QSGNode *updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) override; + bool contains(const QPointF &point) const override; + + QGeoMapPolygonGeometryOpenGL m_geometry; + QGeoMapPolylineGeometryOpenGL m_borderGeometry; + QDeclarativePolygonMapItemPrivateOpenGL::RootNode *m_rootNode = nullptr; + MapPolygonNodeGL *m_node = nullptr; + MapPolylineNodeOpenGLExtruded *m_polylinenode = nullptr; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVERECTANGLEMAPITEM_RHI_P_H diff --git a/src/location/quickmapitems/rhi/qgeomapitemgeometry_rhi.cpp b/src/location/quickmapitems/rhi/qgeomapitemgeometry_rhi.cpp new file mode 100644 index 00000000..dddd9d07 --- /dev/null +++ b/src/location/quickmapitems/rhi/qgeomapitemgeometry_rhi.cpp @@ -0,0 +1,729 @@ +/**************************************************************************** + ** + ** Copyright (C) 2022 The Qt Company Ltd. + ** Contact: https://www.qt.io/licensing/ + ** + ** This file is part of the QtLocation module of the Qt Toolkit. + ** + ** $QT_BEGIN_LICENSE:LGPL$ + ** 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 https://www.qt.io/terms-conditions. For further + ** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General + ** Public license version 3 or any later version approved by the KDE Free + ** Qt Foundation. The licenses are as published by the Free Software + ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 + ** included in the packaging of this file. Please review the following + ** information to ensure the GNU General Public License requirements will + ** be met: https://www.gnu.org/licenses/gpl-2.0.html and + ** https://www.gnu.org/licenses/gpl-3.0.html. + ** + ** $QT_END_LICENSE$ + ** + ****************************************************************************/ + +#include "qgeomapitemgeometry_rhi_p.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* poly2tri triangulator includes */ +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \internal + Implementation of QGeoMapItemLODGeometry +*/ + +struct ThreadPool // to have a thread pool with max 1 thread for geometry processing +{ + ThreadPool () + { + m_threadPool.setMaxThreadCount(1); + } + + void start(QRunnable *runnable, int priority = 0) + { + m_threadPool.start(runnable, priority); + } + + QThreadPool m_threadPool; +}; + +Q_GLOBAL_STATIC(ThreadPool, threadPool) + +class PolylineSimplifyTask : public QRunnable +{ +public: + PolylineSimplifyTask(const QSharedPointer> + &input, // reference as it gets copied in the nested call + const QSharedPointer> &output, + double leftBound, unsigned int zoom, QSharedPointer &working) + : m_zoom(zoom), m_leftBound(leftBound), m_input(input), m_output(output), m_working(working) + { + Q_ASSERT(!input.isNull()); + Q_ASSERT(!output.isNull()); + } + + void run() override + { + // Skip sending notifications for now. Updated data will be picked up eventually. + // ToDo: figure out how to connect a signal from here to a slot in the item. + *m_working = QGeoMapItemLODGeometry::zoomToLOD(m_zoom); + const QList res = + QGeoMapItemLODGeometry::getSimplified( + *m_input, m_leftBound, QGeoMapItemLODGeometry::zoomForLOD(m_zoom)); + *m_output = res; + *m_working = 0; + } + + unsigned int m_zoom; + double m_leftBound; + QSharedPointer> m_input, m_output; + QSharedPointer m_working; +}; + + +QGeoMapItemLODGeometry::QGeoMapItemLODGeometry() +{ + resetLOD(); +} + +void QGeoMapItemLODGeometry::resetLOD() +{ + // New pointer, some old LOD task might still be running and operating on the old pointers. + m_verticesLOD[0] = QSharedPointer>( + new QList); + 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 +} + +QList QGeoMapItemLODGeometry::getSimplified( + QList + &wrappedPath, // reference as it gets copied in the nested call + double leftBoundWrapped, unsigned int zoom) +{ + // Try a simplify step + QList data; + for (auto e: wrappedPath) + data << e.toDoubleVector2D(); + const QList simplified = QGeoSimplify::geoSimplifyZL(data, + leftBoundWrapped, + zoom); + + data.clear(); + QList simple; + for (auto e: simplified) + simple << e; + return simple; +} + +bool QGeoMapItemLODGeometry::isLODActive(unsigned int lod) const +{ + return m_screenVertices == m_verticesLOD[zoomToLOD(lod)].data(); +} + +bool QGeoMapItemLODGeometry::selectLODOnLODMismatch(unsigned int zoom, double leftBound, + bool closed) const +{ + if (*m_working > 0) + return false; + const_cast(this)->selectLOD(zoom, leftBound, closed); + return true; +} + +void QGeoMapItemLODGeometry::enqueueSimplificationTask( + const QSharedPointer> &input, + const QSharedPointer> &output, double leftBound, + unsigned int zoom, QSharedPointer &working) +{ + Q_ASSERT(!input.isNull()); + Q_ASSERT(!output.isNull()); + PolylineSimplifyTask *task = new PolylineSimplifyTask(input, + output, + leftBound, + zoom, + working); + threadPool->start(task); +} + +void QGeoMapItemLODGeometry::selectLOD(unsigned int zoom, double leftBound, bool /* closed */) // closed to tell if this is a polygon or a polyline. +{ + unsigned int requestedLod = zoomToLOD(zoom); + if (!m_verticesLOD[requestedLod].isNull()) { + m_screenVertices = m_verticesLOD[requestedLod].data(); + } else if (!m_verticesLOD.at(0)->isEmpty()) { + // if here, zoomToLOD != 0 and no current working task. + // So select the last filled LOD != m_working (lower-bounded by 1, + // guaranteed to exist), and enqueue the right one + m_verticesLOD[requestedLod] = QSharedPointer>( + new QList); + + for (unsigned int i = requestedLod - 1; i >= 1; i--) { + if (*m_working != i && !m_verticesLOD[i].isNull()) { + m_screenVertices = m_verticesLOD[i].data(); + break; + } else if (i == 1) { + // get 1 synchronously if not computed already + m_verticesLOD[1] = QSharedPointer>( + new QList); + *m_verticesLOD[1] = getSimplified(*m_verticesLOD[0], leftBound, zoomForLOD(0)); + if (requestedLod == 1) + return; + } + } + + enqueueSimplificationTask(m_verticesLOD.at(0), + m_verticesLOD[requestedLod], + leftBound, + zoom, + m_working); + + } +} + +void QGeoMapItemLODGeometry::selectLODOnDataChanged(unsigned int zoom, double leftBound) const +{ + unsigned int lod = zoomToLOD(zoom); + if (lod > 0) { + // Generate ZL 1 as fallback for all cases != 0. Do not do if 0 is requested + // (= old behavior, LOD disabled) + m_verticesLOD[1] = QSharedPointer>( + new QList); + *m_verticesLOD[1] = getSimplified( *m_verticesLOD[0], leftBound, zoomForLOD(0)); + } + if (lod > 1) { + if (!m_verticesLOD[lod]) { + m_verticesLOD[lod] = QSharedPointer>( + new QList); + } + enqueueSimplificationTask(m_verticesLOD.at(0), + m_verticesLOD[lod], + leftBound, + zoom, + m_working); + } + m_screenVertices = m_verticesLOD[qMin(lod, 1)].data(); // return only 0,1 synchronously +} + +unsigned int QGeoMapItemLODGeometry::zoomToLOD(unsigned int zoom) +{ + unsigned int res; + if (zoom > 20) + res = 0; + else + res = qBound(3, zoom, 20) / 3; // bound LOD'ing between ZL 3 and 20. Every 3 ZoomLevels + return res; +} + +unsigned int QGeoMapItemLODGeometry::zoomForLOD(unsigned int zoom) +{ + unsigned int res = (qBound(3, zoom, 20) / 3) * 3; + if (zoom < 6) + return res; + return res + 1; // give more resolution when closing in +} + +// wrapPath always preserves the geometry +// This one handles holes +static void wrapPath(const QGeoPolygon &poly + ,const QGeoCoordinate &geoLeftBound + ,const QGeoProjectionWebMercator &p + ,QList > &wrappedPaths + ,QDoubleVector2D *leftBoundWrapped = nullptr) +{ + QList > paths; + for (qsizetype i = 0; i < 1 + poly.holesCount(); ++i) { + QList path; + if (!i) { + for (const QGeoCoordinate &c : poly.perimeter()) + path << p.geoToMapProjection(c); + } else { + for (const QGeoCoordinate &c : poly.holePath(i-1)) + path << p.geoToMapProjection(c); + } + paths.append(path); + } + + const QDoubleVector2D leftBound = p.geoToMapProjection(geoLeftBound); + wrappedPaths.clear(); + + QList wrappedPath; + // compute 3 sets of "wrapped" coordinates: one w regular mercator, one w regular mercator +- 1.0 + for (const auto &path : paths) { + wrappedPath.clear(); + for (QDoubleVector2D coord : path) { + // We can get NaN if the map isn't set up correctly, or the projection + // is faulty -- probably best thing to do is abort + if (!qIsFinite(coord.x()) || !qIsFinite(coord.y())) { + wrappedPaths.clear(); + return; + } + + const bool isPointLessThanUnwrapBelowX = (coord.x() < leftBound.x()); + // unwrap x to preserve geometry if moved to border of map + if (isPointLessThanUnwrapBelowX) + coord.setX(coord.x() + 1.0); + wrappedPath.append(coord); + } + wrappedPaths.append(wrappedPath); + } + + if (leftBoundWrapped) + *leftBoundWrapped = leftBound; +} + +static void cutPathEars(const QList> &wrappedPaths, + QList &screenVertices, + QList &screenIndices) +{ + using Coord = double; + using N = uint32_t; + using Point = std::array; + screenVertices.clear(); + screenIndices.clear(); + + std::vector> polygon; + std::vector poly; + + for (const QList &wrappedPath: wrappedPaths) { + poly.clear(); + for (const QDoubleVector2D &v: wrappedPath) { + screenVertices << v; + Point pt = {{ v.x(), v.y() }}; + poly.push_back( pt ); + } + polygon.push_back(poly); + } + + std::vector indices = qt_mapbox::earcut(polygon); + + for (const auto &i: indices) + screenIndices << quint32(i); +} + +static void cutPathEars(const QList &wrappedPath, + QList &screenVertices, + QList &screenIndices) +{ + using Coord = double; + using N = uint32_t; + using Point = std::array; + screenVertices.clear(); + screenIndices.clear(); + + std::vector> polygon; + std::vector poly; + + for (const QDoubleVector2D &v: wrappedPath) { + screenVertices << v; + Point pt = {{ v.x(), v.y() }}; + poly.push_back( pt ); + } + polygon.push_back(poly); + + std::vector indices = qt_mapbox::earcut(polygon); + + for (const auto &i: indices) + screenIndices << quint32(i); +} + +void QGeoMapPolygonGeometryOpenGL::allocateAndFillPolygon(QSGGeometry *geom) const +{ + + const QList &vx = m_screenVertices; + const QList &ix = m_screenIndices; + + geom->allocate(vx.size(), ix.size()); + if (geom->indexType() == QSGGeometry::UnsignedShortType) { + quint16 *its = geom->indexDataAsUShort(); + for (qsizetype i = 0; i < ix.size(); ++i) + its[i] = ix[i]; + } else if (geom->indexType() == QSGGeometry::UnsignedIntType) { + quint32 *its = geom->indexDataAsUInt(); + for (qsizetype i = 0; i < ix.size(); ++i) + its[i] = ix[i]; + } + + QSGGeometry::Point2D *pts = geom->vertexDataAsPoint2D(); + for (qsizetype i = 0; i < vx.size(); ++i) + pts[i].set(vx[i].x, vx[i].y); +} + + +QGeoMapPolygonGeometryOpenGL::QGeoMapPolygonGeometryOpenGL() +{ +} + +void QGeoMapPolygonGeometryOpenGL::updateSourcePoints(const QGeoMap &map, const QList &path) +{ + QList geopath; + for (const auto &c: path) + geopath.append(QWebMercator::mercatorToCoord(c)); + updateSourcePoints(map, geopath); +} + +/*! + \internal +*/ +// This one does only a perimeter +void QGeoMapPolygonGeometryOpenGL::updateSourcePoints(const QGeoMap &map, + const QList &perimeter) +{ + if (!sourceDirty_) + return; + const QGeoProjectionWebMercator &p = static_cast(map.geoProjection()); + + // build the actual path + // The approach is the same as described in QGeoMapPolylineGeometry::updateSourcePoints + srcOrigin_ = geoLeftBound_; + + QDoubleVector2D leftBoundWrapped; + // 1) pre-compute 3 sets of "wrapped" coordinates: one w regular mercator, one w regular mercator +- 1.0 + QList wrappedPath; + QDeclarativeGeoMapItemUtils::wrapPath(perimeter, geoLeftBound_, p, + wrappedPath, &leftBoundWrapped); + + // 1.1) do the same for the bbox + QList wrappedBbox, wrappedBboxPlus1, wrappedBboxMinus1; + QGeoPolygon bbox(QGeoPath(perimeter).boundingGeoRectangle()); + QDeclarativeGeoMapItemUtils::wrapPath(bbox.perimeter(), bbox.boundingGeoRectangle().topLeft(), p, + wrappedBbox, wrappedBboxMinus1, wrappedBboxPlus1, &m_bboxLeftBoundWrapped); + + // 2) Store the triangulated polygon, and the wrapped bbox paths. + // the triangulations can be used as they are, as they "bypass" the QtQuick display chain + // the bbox wraps have to be however clipped, and then projected, in order to figure out the geometry. + // Note that this might still cause the geometryChange method to fail under some extreme conditions. + cutPathEars(wrappedPath, m_screenVertices, m_screenIndices); + + m_wrappedPolygons.resize(3); + m_wrappedPolygons[0].wrappedBboxes = wrappedBboxMinus1; + m_wrappedPolygons[1].wrappedBboxes = wrappedBbox; + m_wrappedPolygons[2].wrappedBboxes = wrappedBboxPlus1; +} + +// This one handles whole QGeoPolygon w. holes +void QGeoMapPolygonGeometryOpenGL::updateSourcePoints(const QGeoMap &map, const QGeoPolygon &poly) +{ + if (!sourceDirty_) + return; + const QGeoProjectionWebMercator &p = static_cast(map.geoProjection()); + + // build the actual path + // The approach is the same as described in QGeoMapPolylineGeometry::updateSourcePoints + srcOrigin_ = geoLeftBound_; + + QDoubleVector2D leftBoundWrapped; + QList> wrappedPath; + // 1) pre-compute 3 sets of "wrapped" coordinates: one w regular mercator, one w regular mercator +- 1.0 + wrapPath(poly, geoLeftBound_, p, + wrappedPath, &leftBoundWrapped); + + // 1.1) do the same for the bbox + QList wrappedBbox, wrappedBboxPlus1, wrappedBboxMinus1; + QGeoPolygon bbox(poly.boundingGeoRectangle()); + QDeclarativeGeoMapItemUtils::wrapPath(bbox.perimeter(), bbox.boundingGeoRectangle().topLeft(), p, + wrappedBbox, wrappedBboxMinus1, wrappedBboxPlus1, &m_bboxLeftBoundWrapped); + + // 2) Store the triangulated polygon, and the wrapped bbox paths. + // the triangulations can be used as they are, as they "bypass" the QtQuick display chain + // the bbox wraps have to be however clipped, and then projected, in order to figure out the geometry. + // Note that this might still cause the geometryChange method to fail under some extreme conditions. + cutPathEars(wrappedPath, m_screenVertices, m_screenIndices); + m_wrappedPolygons.resize(3); + m_wrappedPolygons[0].wrappedBboxes = wrappedBboxMinus1; + m_wrappedPolygons[1].wrappedBboxes = wrappedBbox; + m_wrappedPolygons[2].wrappedBboxes = wrappedBboxPlus1; +} + +void QGeoMapPolygonGeometryOpenGL::updateSourcePoints(const QGeoMap &map, const QGeoRectangle &rect) +{ + if (!sourceDirty_) + return; + const QList perimeter = QGeoMapItemGeometry::path(rect); + updateSourcePoints(map, perimeter); +} + +/*! + \internal +*/ +void QGeoMapPolygonGeometryOpenGL::updateScreenPoints(const QGeoMap &map, qreal strokeWidth , const QColor &strokeColor) +{ + if (map.viewportWidth() == 0 || map.viewportHeight() == 0) { + clear(); + return; + } + + // 1) identify which set to use: std, +1 or -1 + const QGeoProjectionWebMercator &p = static_cast(map.geoProjection()); + const QDoubleVector2D leftBoundMercator = p.geoToMapProjection(srcOrigin_); + m_wrapOffset = p.projectionWrapFactor(leftBoundMercator) + 1; // +1 to get the offset into QLists + + // 1.1) select geometry set + // This could theoretically be skipped for those polygons whose bbox is not even projectable. + // However, such optimization could only be introduced if not calculating bboxes lazily. + // Hence not doing it. + if (sourceDirty_) { + m_dataChanged = true; + } + + if (strokeWidth == 0.0 || strokeColor.alpha() == 0) // or else the geometry of the border is used, so no point in calculating 2 of them + updateQuickGeometry(p, strokeWidth); +} + +void QGeoMapPolygonGeometryOpenGL::updateQuickGeometry(const QGeoProjectionWebMercator &p, qreal /*strokeWidth*/) +{ + // 2) clip bbox + // BBox handling -- this is related to the bounding box geometry + // that has to inevitably follow the old projection codepath + // As it needs to provide projected coordinates for QtQuick interaction. + // This could be futher optimized to be updated in a lazy fashion. + const QList &wrappedBbox = m_wrappedPolygons.at(m_wrapOffset).wrappedBboxes; + QList > clippedBbox; + QDoubleVector2D bboxLeftBoundWrapped = m_bboxLeftBoundWrapped; + bboxLeftBoundWrapped.setX(bboxLeftBoundWrapped.x() + double(m_wrapOffset - 1)); + QDeclarativeGeoMapItemUtils::clipPolygon(wrappedBbox, p, clippedBbox, &bboxLeftBoundWrapped); + + // 3) project bbox + QPainterPath ppi; + if (!clippedBbox.size() || clippedBbox.first().size() < 3) { + sourceBounds_ = screenBounds_ = QRectF(); + firstPointOffset_ = QPointF(); + screenOutline_ = ppi; + return; + } + + QDeclarativeGeoMapItemUtils::projectBbox(clippedBbox.first(), p, ppi); // Using first because a clipped box should always result in one polygon + const QRectF brect = ppi.boundingRect(); + firstPointOffset_ = QPointF(brect.topLeft()); + screenOutline_ = ppi; + + // 4) Set Screen bbox + screenBounds_ = brect; + sourceBounds_.setX(0); + sourceBounds_.setY(0); + sourceBounds_.setWidth(brect.width()); + sourceBounds_.setHeight(brect.height()); +} + + +void QGeoMapPolylineGeometryOpenGL::updateSourcePoints(const QGeoMap &map, const QGeoPolygon &poly) +{ + if (!sourceDirty_) + return; + QGeoPath p(poly.perimeter()); + if (poly.perimeter().size() && poly.perimeter().last() != poly.perimeter().first()) + p.addCoordinate(poly.perimeter().first()); + updateSourcePoints(map, p); +} + +void QGeoMapPolylineGeometryOpenGL::updateSourcePoints(const QGeoMap &map, const QGeoPath &poly) +{ + if (!sourceDirty_) + return; + const QGeoProjectionWebMercator &p = static_cast(map.geoProjection()); + + // build the actual path + // The approach is the same as described in QGeoMapPolylineGeometry::updateSourcePoints + + + QDoubleVector2D leftBoundWrapped; + // 1) pre-compute 3 sets of "wrapped" coordinates: one w regular mercator, one w regular mercator +- 1.0 + QList wrappedPath; + QDeclarativeGeoMapItemUtils::wrapPath(poly.path(), geoLeftBound_, p, + wrappedPath, &leftBoundWrapped); + + const QGeoRectangle &boundingRectangle = poly.boundingGeoRectangle(); + updateSourcePoints(p, wrappedPath, boundingRectangle); +} + +void QGeoMapPolylineGeometryOpenGL::updateSourcePoints(const QGeoProjectionWebMercator &p, + const QList &wrappedPath, + const QGeoRectangle &boundingRectangle) { + if (!sourceDirty_) + return; + // 1.1) do the same for the bbox + // Beware: vertical lines (or horizontal lines) might have an "empty" bbox. Check for that + + QGeoCoordinate topLeft = boundingRectangle.topLeft(); + QGeoCoordinate bottomRight = boundingRectangle.bottomRight(); + const qreal epsilon = 0.000001; + if (qFuzzyCompare(topLeft.latitude(), bottomRight.latitude())) { + topLeft.setLatitude(qBound(-90.0, topLeft.latitude() + epsilon ,90.0)); + bottomRight.setLatitude(qBound(-90.0, bottomRight.latitude() - epsilon ,90.0)); + } + if (qFuzzyCompare(topLeft.longitude(), bottomRight.longitude())) { + topLeft.setLongitude(QLocationUtils::wrapLong(topLeft.longitude() - epsilon)); + bottomRight.setLongitude(QLocationUtils::wrapLong(bottomRight.longitude() + epsilon)); + } + QGeoPolygon bbox(QGeoRectangle(topLeft, bottomRight)); + QList wrappedBbox, wrappedBboxPlus1, wrappedBboxMinus1; + QDeclarativeGeoMapItemUtils::wrapPath(bbox.perimeter(), bbox.boundingGeoRectangle().topLeft(), p, + wrappedBbox, wrappedBboxMinus1, wrappedBboxPlus1, &m_bboxLeftBoundWrapped); + + // New pointers, some old LOD task might still be running and operating on the old pointers. + resetLOD(); + + for (const auto &v: qAsConst(wrappedPath)) m_screenVertices->append(v); + + m_wrappedPolygons.resize(3); + m_wrappedPolygons[0].wrappedBboxes = wrappedBboxMinus1; + m_wrappedPolygons[1].wrappedBboxes = wrappedBbox; + m_wrappedPolygons[2].wrappedBboxes = wrappedBboxPlus1; + srcOrigin_ = geoLeftBound_; +} + +void QGeoMapPolylineGeometryOpenGL::updateSourcePoints(const QGeoMap &map, const QGeoRectangle &rect) +{ + const QList perimeter = QGeoMapItemGeometry::perimeter(rect); + updateSourcePoints(map, QGeoPath(perimeter)); +} + +void QGeoMapPolylineGeometryOpenGL::updateSourcePoints(const QGeoMap &map, const QGeoCircle &circle) +{ + if (!sourceDirty_) + return; + const QGeoProjectionWebMercator &p = static_cast(map.geoProjection()); + + QDoubleVector2D leftBoundWrapped; + // 1) pre-compute 3 sets of "wrapped" coordinates: one w regular mercator, one w regular mercator +- 1.0 + QList path; + QGeoCoordinate leftBound; + QList wrappedPath; + QDeclarativeCircleMapItemPrivate::calculatePeripheralPoints(path, circle.center(), circle.radius(), QDeclarativeCircleMapItemPrivate::CircleSamples, leftBound); + path << path.first(); + geoLeftBound_ = leftBound; + QDeclarativeGeoMapItemUtils::wrapPath(path, leftBound, p, wrappedPath, &leftBoundWrapped); + const QGeoRectangle &boundingRectangle = circle.boundingGeoRectangle(); + updateSourcePoints(p, wrappedPath, boundingRectangle); +} + +void QGeoMapPolylineGeometryOpenGL::updateScreenPoints(const QGeoMap &map, qreal strokeWidth, bool /*adjustTranslation*/) +{ + if (map.viewportWidth() == 0 || map.viewportHeight() == 0) { + clear(); + return; + } + + // 1) identify which set to use: std, +1 or -1 + const QGeoProjectionWebMercator &p = static_cast(map.geoProjection()); + const QDoubleVector2D leftBoundMercator = p.geoToMapProjection(srcOrigin_); + m_wrapOffset = p.projectionWrapFactor(leftBoundMercator) + 1; // +1 to get the offset into QLists + + if (sourceDirty_) { + // 1.1) select geometry set + // This could theoretically be skipped for those polylines whose bbox is not even projectable. + // However, such optimization could only be introduced if not calculating bboxes lazily. + // Hence not doing it. +// if (m_screenVertices.size() > 1) + m_dataChanged = true; + } + + updateQuickGeometry(p, strokeWidth); +} + +void QGeoMapPolylineGeometryOpenGL::updateQuickGeometry(const QGeoProjectionWebMercator &p, qreal strokeWidth) +{ + // 2) clip bbox + // BBox handling -- this is related to the bounding box geometry + // that has to inevitably follow the old projection codepath + // As it needs to provide projected coordinates for QtQuick interaction. + // This could be futher optimized to be updated in a lazy fashion. + const QList &wrappedBbox = m_wrappedPolygons.at(m_wrapOffset).wrappedBboxes; + QList > clippedBbox; + QDoubleVector2D bboxLeftBoundWrapped = m_bboxLeftBoundWrapped; + bboxLeftBoundWrapped.setX(bboxLeftBoundWrapped.x() + double(m_wrapOffset - 1)); + QDeclarativeGeoMapItemUtils::clipPolygon(wrappedBbox, p, clippedBbox, &bboxLeftBoundWrapped, false); + + // 3) project bbox + QPainterPath ppi; + + if ( !clippedBbox.size() || + clippedBbox.first().size() < 3) { + sourceBounds_ = screenBounds_ = QRectF(); + firstPointOffset_ = QPointF(); + screenOutline_ = ppi; + return; + } + + QDeclarativeGeoMapItemUtils::projectBbox(clippedBbox.first(), p, ppi); // Using first because a clipped box should always result in one polygon + const QRectF brect = ppi.boundingRect(); + firstPointOffset_ = QPointF(brect.topLeft()); + sourceBounds_ = brect; + screenOutline_ = ppi; + + // 4) Set Screen bbox + screenBounds_ = brect; + sourceBounds_.setX(0); + sourceBounds_.setY(0); + sourceBounds_.setWidth(brect.width() + strokeWidth); + sourceBounds_.setHeight(brect.height() + strokeWidth); +} + +// Note: this is also slightly incorrect on joins and in the beginning/end of the line +bool QGeoMapPolylineGeometryOpenGL::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 (qsizetype i = 1; i < m_screenVertices->size(); ++i) { + const auto &screenVertice = m_screenVertices->at(i); + if (!a.isFinite()) { + a = p.wrappedMapProjectionToItemPosition(p.wrapMapProjection(screenVertice.toDoubleVector2D())); + continue; + } + + b = p.wrappedMapProjectionToItemPosition(p.wrapMapProjection(screenVertice.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; +} + +QT_END_NAMESPACE diff --git a/src/location/quickmapitems/rhi/qgeomapitemgeometry_rhi_p.h b/src/location/quickmapitems/rhi/qgeomapitemgeometry_rhi_p.h new file mode 100644 index 00000000..27fb8835 --- /dev/null +++ b/src/location/quickmapitems/rhi/qgeomapitemgeometry_rhi_p.h @@ -0,0 +1,221 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOMAPITEMGEOMETRY_RHI_P_H +#define QGEOMAPITEMGEOMETRY_RHI_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QPointF; +class QSGGeometry; +class QGeoCircle; +class QGeoMap; +class QGeoPath; +class QGeoPolygon; +class QGeoProjectionWebMercator; +class QGeoRectangle; + +class QGeoMapItemLODGeometry +{ + Q_DISABLE_COPY(QGeoMapItemLODGeometry); +public: + mutable std::array>, 7> + m_verticesLOD; // fix it to 7, + // do not allow simplifications beyond ZL 20. This could actually be + // limited even further + mutable QList *m_screenVertices; + mutable QSharedPointer m_working; + + QGeoMapItemLODGeometry(); + + void resetLOD(); + + 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 QList + getSimplified(QList &wrappedPath, double leftBoundWrapped, + unsigned int zoom); + + static void enqueueSimplificationTask( + const QSharedPointer> + &input, // reference as it gets copied in the nested call + const QSharedPointer> &output, + double leftBound, unsigned int zoom, QSharedPointer &working); + + void selectLODOnDataChanged(unsigned int zoom, double leftBound) const; + bool selectLODOnLODMismatch(unsigned int zoom, double leftBound, bool closed) const; +}; + +class Q_LOCATION_PRIVATE_EXPORT QGeoMapPolylineGeometryOpenGL : public QGeoMapItemGeometry, + public QGeoMapItemLODGeometry +{ +public: + typedef struct { + QList wrappedBboxes; + } WrappedPolyline; + + QGeoMapPolylineGeometryOpenGL() + { + m_working = QSharedPointer(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 &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; + + // And it falls in the segment + if (u > 0 && u < 1 && (p-intersection).length() < (p-candidate).length()) { + 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; + +public: + QDoubleVector2D m_bboxLeftBoundWrapped; + QList m_wrappedPolygons; + int m_wrapOffset; + + friend class QDeclarativeCircleMapItem; + friend class QDeclarativePolygonMapItem; + friend class QDeclarativeRectangleMapItem; +}; + +class Q_LOCATION_PRIVATE_EXPORT QGeoMapPolygonGeometryOpenGL : public QGeoMapItemGeometry +{ +public: + typedef struct { + QList wrappedBboxes; + } WrappedPolygon; + QGeoMapPolygonGeometryOpenGL(); + ~QGeoMapPolygonGeometryOpenGL() override {} + + // Temporary method for compatibility in MapCircleObject. Remove when MapObjects are ported. + void updateSourcePoints(const QGeoMap &map, + const QList &path); + + void updateSourcePoints(const QGeoMap &map, + const QList &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; + + QList m_screenVertices; + QList m_screenIndices; + QDoubleVector2D m_bboxLeftBoundWrapped; + QList m_wrappedPolygons; + int m_wrapOffset = 0; +}; + +QT_END_NAMESPACE + +#endif // QGEOMAPITEMGEOMETRY_RHI_P_H diff --git a/src/location/quickmapitems/rhi/qgeosimplify.cpp b/src/location/quickmapitems/rhi/qgeosimplify.cpp new file mode 100644 index 00000000..ad5a3c5d --- /dev/null +++ b/src/location/quickmapitems/rhi/qgeosimplify.cpp @@ -0,0 +1,167 @@ +/**************************************************************************** +** +** Qt adaptation of geosimplify-js +** Copyright (C) 2017 Daniel Patterson +** See 3rdParty/geosimplify.js for the original license. +** +** Copyright (C) 2020 Paolo Angelelli +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeosimplify_p.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace { + +// p, a and b are intended as "unwrapped" around the left bound +inline QDoubleVector2D closestPoint(const QDoubleVector2D &p, + const QDoubleVector2D &a, + const QDoubleVector2D &b) +{ + if (a == b) + return a; + + const double u = ((p.x() - a.x()) * (b.x() - a.x()) + (p.y() - a.y()) * (b.y() - a.y())) + / (b - a).lengthSquared(); + const 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 candidate; +} + +inline double getDist(QDoubleVector2D a, QDoubleVector2D b, double leftBound) +{ + if (a.x() > 1.0) + a.setX(a.x() - leftBound); // wrap X + if (b.x() > 1.0) + b.setX(b.x() - leftBound); // wrap X + return QWebMercator::mercatorToCoord(a).distanceTo(QWebMercator::mercatorToCoord(b)); +} + +// doublevectors Intended as wrapped +inline double getSegDist(const QDoubleVector2D &p, + const QDoubleVector2D &a, + const QDoubleVector2D &b, + double leftBound) +{ + const QDoubleVector2D intersection = closestPoint(p, a, b); + return getDist(intersection, p, leftBound); +} + +inline QGeoCoordinate unwrappedToGeo(QDoubleVector2D p, double leftBound) +{ + if (p.x() > 1.0) + p.setX(p.x() - leftBound); + return QWebMercator::mercatorToCoord(p); +} + +double pixelDistanceAtZoomAndLatitude(int zoom, double latitude) +{ + const double den = double((1 << (zoom + 8))); + const double pixelDist = (QLocationUtils::earthMeanCircumference() + * std::cos(QLocationUtils::radians(latitude))) + / den; + return pixelDist; +} + +// simplification using Ramer-Douglas-Peucker algorithm +void simplifyDouglasPeuckerStepZL(const QList &points, double leftBound, + qsizetype first, qsizetype last, int zoomLevel, + QList &simplified) +{ + const QGeoCoordinate firstC = unwrappedToGeo(points.at(first), leftBound); + const QGeoCoordinate lastC = unwrappedToGeo(points.at(last), leftBound); + double maxDistanceFound = (pixelDistanceAtZoomAndLatitude(zoomLevel, firstC.latitude()) + + pixelDistanceAtZoomAndLatitude(zoomLevel, lastC.latitude())) * 0.5; + qsizetype index = -1; + + const auto &firstPoint = points.at(first); + const auto &lastPoint = points.at(last); + for (qsizetype i = first + 1; i < last; i++) { + const double distance = getSegDist(points.at(i), + firstPoint, + lastPoint, + leftBound); + + if (distance > maxDistanceFound) { + index = i; + maxDistanceFound = distance; + } + } + + if (index > 0) { + if (index - first > 1) { + simplifyDouglasPeuckerStepZL(points, leftBound, + first, index, zoomLevel, + simplified); + } + simplified.append(points.at(index)); + if (last - index > 1) { + simplifyDouglasPeuckerStepZL(points, leftBound, + index, last, zoomLevel, + simplified); + } + } +} + +} // anonymous namespace + +namespace QGeoSimplify { + +QList geoSimplifyZL(const QList &points, + double leftBound, int zoomLevel) +{ + if (points.size() <= 2) + return points; + + const qsizetype last = points.size() - 1; + QList simplified { points.first() }; + simplifyDouglasPeuckerStepZL(points, leftBound, 0, last, zoomLevel, simplified); + simplified.append(points.at(last)); + return simplified; +} + +} + +QT_END_NAMESPACE diff --git a/src/location/quickmapitems/rhi/qgeosimplify_p.h b/src/location/quickmapitems/rhi/qgeosimplify_p.h new file mode 100644 index 00000000..1f5c9783 --- /dev/null +++ b/src/location/quickmapitems/rhi/qgeosimplify_p.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Qt adaptation of geosimplify.js, https://github.com/mapbox/geosimplify-js, (c) 2017, Mapbox +** +** Copyright (C) 2020 Paolo Angelelli +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 https://www.qt.io/terms-conditions. For further +** information use the contact form at https://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.LGPL3 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-3.0.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 (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOSIMPLIFY_P_H +#define QGEOSIMPLIFY_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 + +QT_BEGIN_NAMESPACE + +class QDoubleVector2D; + +namespace QGeoSimplify +{ + // offsetTolerance - how far outside the straight line a point + // needs to be for it to be "kept" + // This function tries to be adaptive in the offsetTolerance across latitudes, + // and return a simplification adequate for the given zoomLevel. + QList geoSimplifyZL(const QList &points, + double leftBound, int zoomLevel); // in meters +} + +QT_END_NAMESPACE + +#endif // QGEOSIMPLIFY_P_H diff --git a/src/location/quickmapitems/rhi/shaders/polygon.frag b/src/location/quickmapitems/rhi/shaders/polygon.frag new file mode 100644 index 00000000..9b9727b6 --- /dev/null +++ b/src/location/quickmapitems/rhi/shaders/polygon.frag @@ -0,0 +1,16 @@ +#version 440 + +layout(location = 0) out vec4 fragColor; + +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; + mat4 mapProjection; + vec4 center; + vec4 center_lowpart; + float wrapOffset; + vec4 color; +}; + +void main() { + fragColor = color; +} diff --git a/src/location/quickmapitems/rhi/shaders/polygon.vert b/src/location/quickmapitems/rhi/shaders/polygon.vert new file mode 100644 index 00000000..06dab03e --- /dev/null +++ b/src/location/quickmapitems/rhi/shaders/polygon.vert @@ -0,0 +1,20 @@ +#version 440 + +layout(location = 0) in highp vec4 vertex; + +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; + mat4 mapProjection; + vec4 center; + vec4 center_lowpart; + float wrapOffset; + vec4 color; +}; + +vec4 wrapped(in vec4 v) { return vec4(v.x + wrapOffset, v.y, 0.0, 1.0); } + +void main() { + vec4 vtx = wrapped(vertex) - center; + vtx = vtx - center_lowpart; + gl_Position = qt_Matrix * mapProjection * vtx; +} diff --git a/src/location/quickmapitems/rhi/shaders/polyline_extruded.frag b/src/location/quickmapitems/rhi/shaders/polyline_extruded.frag new file mode 100644 index 00000000..3530f093 --- /dev/null +++ b/src/location/quickmapitems/rhi/shaders/polyline_extruded.frag @@ -0,0 +1,20 @@ +#version 440 + +layout(location = 0) in vec4 primitivecolor; +layout(location = 0) out vec4 fragColor; + +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; + mat4 mapProjection; + vec4 center; + vec4 center_lowpart; + float lineWidth; + float aspect; + int miter; // currently unused + vec4 color; + float wrapOffset; +}; + +void main() { + fragColor = primitivecolor; +} diff --git a/src/location/quickmapitems/rhi/shaders/polyline_extruded.vert b/src/location/quickmapitems/rhi/shaders/polyline_extruded.vert new file mode 100644 index 00000000..27b69b60 --- /dev/null +++ b/src/location/quickmapitems/rhi/shaders/polyline_extruded.vert @@ -0,0 +1,104 @@ +#version 440 + +layout(location = 0) in vec4 vertex; +layout(location = 1) in vec4 previous; +layout(location = 2) in vec4 next; +layout(location = 3) in float direction; +layout(location = 4) in float triangletype; +layout(location = 5) in float vertextype; // -1.0 if it is the "left" end of the segment, 1.0 if it is the "right" end. +layout(location = 0) out vec4 primitivecolor; + +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; + mat4 mapProjection; + vec4 center; + vec4 center_lowpart; + float lineWidth; + float aspect; + int miter; // currently unused + vec4 color; + float wrapOffset; +}; + + +vec4 wrapped(in vec4 v) { return vec4(v.x + wrapOffset, v.y, 0.0, 1.0); } +void main() { // ln 22 + primitivecolor = color; + vec2 aspectVec = vec2(aspect, 1.0); + mat4 projViewModel = qt_Matrix * mapProjection; + vec4 cur = wrapped(vertex) - center; + cur = cur - center_lowpart; + vec4 prev = wrapped(previous) - center; + prev = prev - center_lowpart; + vec4 nex = wrapped(next) - center; + nex = nex - center_lowpart; + + vec4 centerProjected = projViewModel * (center + vec4(0.0, 0.0, 0.0, 1.0)); + vec4 previousProjected = projViewModel * prev; + vec4 currentProjected = projViewModel * cur; + vec4 nextProjected = projViewModel * nex; + + //get 2D screen space with W divide and aspect correction + vec2 currentScreen = (currentProjected.xy / currentProjected.w) * aspectVec; + vec2 previousScreen = (previousProjected.xy / previousProjected.w) * aspectVec; + vec2 nextScreen = (nextProjected.xy / nextProjected.w) * aspectVec; + float len = (lineWidth); + float orientation = direction; + bool clipped = false; + bool otherEndBelowFrustum = false; + //starting point uses (next - current) + vec2 dir = vec2(0.0); + if (vertextype < 0.0) { + dir = normalize(nextScreen - currentScreen); + if (nextProjected.z < 0.0) dir = -dir; + } else { + dir = normalize(currentScreen - previousScreen); + if (previousProjected.z < 0.0) dir = -dir; + } +// first, clip current, and make sure currentProjected.z is > 0 + if (currentProjected.z < 0.0) { + if ((nextProjected.z > 0.0 && vertextype < 0.0) || (vertextype > 0.0 && previousProjected.z > 0.0)) { + dir = -dir; + clipped = true; + if (vertextype < 0.0 && nextProjected.y / nextProjected.w < -1.0) otherEndBelowFrustum = true; + else if (vertextype > 0.0 && previousProjected.y / previousProjected.w < -1.0) otherEndBelowFrustum = true; + } else { + primitivecolor = vec4(0.0,0.0,0.0,0.0); + gl_Position = vec4(-10000000.0, -1000000000.0, -1000000000.0, 1); // get the vertex out of the way if the segment is fully invisible + return; + } + } else if (triangletype < 2.0) { // vertex in the view, try to miter + //get directions from (C - B) and (B - A) + vec2 dirA = normalize((currentScreen - previousScreen)); + if (previousProjected.z < 0.0) dirA = -dirA; + vec2 dirB = normalize((nextScreen - currentScreen)); + //now compute the miter join normal and length + if (nextProjected.z < 0.0) dirB = -dirB; + vec2 tangent = normalize(dirA + dirB); + vec2 perp = vec2(-dirA.y, dirA.x); + vec2 vmiter = vec2(-tangent.y, tangent.x); + len = lineWidth / dot(vmiter, perp); +// The following is an attempt to have a segment-length based miter threshold. +// A mediocre workaround until better mitering will be added. + float lenTreshold = clamp( min(length((currentProjected.xy - previousProjected.xy) / aspectVec), + length((nextProjected.xy - currentProjected.xy) / aspectVec)), 3.0, 6.0 ) * 0.5; + if (len < lineWidth * lenTreshold && len > -lineWidth * lenTreshold) { + dir = tangent; + } else { + len = lineWidth; + } + } + vec4 offset; + if (!clipped) { + vec2 normal = normalize(vec2(-dir.y, dir.x)); + normal *= len; // fracZL apparently was needed before the (-2.0 / qt_Matrix[1][1]) factor was introduced + normal /= aspectVec; // straighten the normal up again + float scaleFactor = currentProjected.w / centerProjected.w; + offset = vec4(normal * orientation * scaleFactor * (centerProjected.w / (-2.0 / qt_Matrix[1][1])), 0.0, 0.0); // ToDo: figure out why (-2.0 / qt_Matrix[1][1]), that is empirically what works + gl_Position = currentProjected + offset; + } else { + if (otherEndBelowFrustum) offset = vec4((dir * 1.0) / aspectVec, 0.0, 0.0); // the if is necessary otherwise it seems the direction vector still flips in some obscure cases. + else offset = vec4((dir * 500000000000.0) / aspectVec, 0.0, 0.0); // Hack alert: just 1 triangle, long enough to look like a rectangle. + if (vertextype < 0.0) gl_Position = nextProjected - offset; else gl_Position = previousProjected + offset; + } +} diff --git a/src/location/quickmapitems/rhi/shaders/polyline_linestrip.frag b/src/location/quickmapitems/rhi/shaders/polyline_linestrip.frag new file mode 100644 index 00000000..e9124d27 --- /dev/null +++ b/src/location/quickmapitems/rhi/shaders/polyline_linestrip.frag @@ -0,0 +1,17 @@ +#version 440 + +layout(location = 0) out vec4 fragColor; + +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; + mat4 mapProjection; + vec4 center; + vec4 center_lowpart; + float opacity; + float wrapOffset; + vec4 color; +}; + +void main() { + fragColor = color; +} diff --git a/src/location/quickmapitems/rhi/shaders/polyline_linestrip.vert b/src/location/quickmapitems/rhi/shaders/polyline_linestrip.vert new file mode 100644 index 00000000..86b4f3a3 --- /dev/null +++ b/src/location/quickmapitems/rhi/shaders/polyline_linestrip.vert @@ -0,0 +1,21 @@ +#version 440 + +layout(location = 0) in vec4 qt_Vertex; + +layout(std140, binding = 0) uniform buf { + mat4 qt_Matrix; + mat4 mapProjection; + vec4 center; + vec4 center_lowpart; + float opacity; + float wrapOffset; + vec4 color; +}; + +vec4 wrapped(in vec4 v) { return vec4(v.x + wrapOffset, v.y, 0.0, 1.0); } + +void main() { + vec4 vtx = wrapped(qt_Vertex) - center; + vtx = vtx - center_lowpart; + gl_Position = qt_Matrix * mapProjection * vtx; +} diff --git a/src/location/quickmapitems/shaders/polygon.frag b/src/location/quickmapitems/shaders/polygon.frag deleted file mode 100644 index 9b9727b6..00000000 --- a/src/location/quickmapitems/shaders/polygon.frag +++ /dev/null @@ -1,16 +0,0 @@ -#version 440 - -layout(location = 0) out vec4 fragColor; - -layout(std140, binding = 0) uniform buf { - mat4 qt_Matrix; - mat4 mapProjection; - vec4 center; - vec4 center_lowpart; - float wrapOffset; - vec4 color; -}; - -void main() { - fragColor = color; -} diff --git a/src/location/quickmapitems/shaders/polygon.vert b/src/location/quickmapitems/shaders/polygon.vert deleted file mode 100644 index 06dab03e..00000000 --- a/src/location/quickmapitems/shaders/polygon.vert +++ /dev/null @@ -1,20 +0,0 @@ -#version 440 - -layout(location = 0) in highp vec4 vertex; - -layout(std140, binding = 0) uniform buf { - mat4 qt_Matrix; - mat4 mapProjection; - vec4 center; - vec4 center_lowpart; - float wrapOffset; - vec4 color; -}; - -vec4 wrapped(in vec4 v) { return vec4(v.x + wrapOffset, v.y, 0.0, 1.0); } - -void main() { - vec4 vtx = wrapped(vertex) - center; - vtx = vtx - center_lowpart; - gl_Position = qt_Matrix * mapProjection * vtx; -} diff --git a/src/location/quickmapitems/shaders/polyline_extruded.frag b/src/location/quickmapitems/shaders/polyline_extruded.frag deleted file mode 100644 index 3530f093..00000000 --- a/src/location/quickmapitems/shaders/polyline_extruded.frag +++ /dev/null @@ -1,20 +0,0 @@ -#version 440 - -layout(location = 0) in vec4 primitivecolor; -layout(location = 0) out vec4 fragColor; - -layout(std140, binding = 0) uniform buf { - mat4 qt_Matrix; - mat4 mapProjection; - vec4 center; - vec4 center_lowpart; - float lineWidth; - float aspect; - int miter; // currently unused - vec4 color; - float wrapOffset; -}; - -void main() { - fragColor = primitivecolor; -} diff --git a/src/location/quickmapitems/shaders/polyline_extruded.vert b/src/location/quickmapitems/shaders/polyline_extruded.vert deleted file mode 100644 index 27b69b60..00000000 --- a/src/location/quickmapitems/shaders/polyline_extruded.vert +++ /dev/null @@ -1,104 +0,0 @@ -#version 440 - -layout(location = 0) in vec4 vertex; -layout(location = 1) in vec4 previous; -layout(location = 2) in vec4 next; -layout(location = 3) in float direction; -layout(location = 4) in float triangletype; -layout(location = 5) in float vertextype; // -1.0 if it is the "left" end of the segment, 1.0 if it is the "right" end. -layout(location = 0) out vec4 primitivecolor; - -layout(std140, binding = 0) uniform buf { - mat4 qt_Matrix; - mat4 mapProjection; - vec4 center; - vec4 center_lowpart; - float lineWidth; - float aspect; - int miter; // currently unused - vec4 color; - float wrapOffset; -}; - - -vec4 wrapped(in vec4 v) { return vec4(v.x + wrapOffset, v.y, 0.0, 1.0); } -void main() { // ln 22 - primitivecolor = color; - vec2 aspectVec = vec2(aspect, 1.0); - mat4 projViewModel = qt_Matrix * mapProjection; - vec4 cur = wrapped(vertex) - center; - cur = cur - center_lowpart; - vec4 prev = wrapped(previous) - center; - prev = prev - center_lowpart; - vec4 nex = wrapped(next) - center; - nex = nex - center_lowpart; - - vec4 centerProjected = projViewModel * (center + vec4(0.0, 0.0, 0.0, 1.0)); - vec4 previousProjected = projViewModel * prev; - vec4 currentProjected = projViewModel * cur; - vec4 nextProjected = projViewModel * nex; - - //get 2D screen space with W divide and aspect correction - vec2 currentScreen = (currentProjected.xy / currentProjected.w) * aspectVec; - vec2 previousScreen = (previousProjected.xy / previousProjected.w) * aspectVec; - vec2 nextScreen = (nextProjected.xy / nextProjected.w) * aspectVec; - float len = (lineWidth); - float orientation = direction; - bool clipped = false; - bool otherEndBelowFrustum = false; - //starting point uses (next - current) - vec2 dir = vec2(0.0); - if (vertextype < 0.0) { - dir = normalize(nextScreen - currentScreen); - if (nextProjected.z < 0.0) dir = -dir; - } else { - dir = normalize(currentScreen - previousScreen); - if (previousProjected.z < 0.0) dir = -dir; - } -// first, clip current, and make sure currentProjected.z is > 0 - if (currentProjected.z < 0.0) { - if ((nextProjected.z > 0.0 && vertextype < 0.0) || (vertextype > 0.0 && previousProjected.z > 0.0)) { - dir = -dir; - clipped = true; - if (vertextype < 0.0 && nextProjected.y / nextProjected.w < -1.0) otherEndBelowFrustum = true; - else if (vertextype > 0.0 && previousProjected.y / previousProjected.w < -1.0) otherEndBelowFrustum = true; - } else { - primitivecolor = vec4(0.0,0.0,0.0,0.0); - gl_Position = vec4(-10000000.0, -1000000000.0, -1000000000.0, 1); // get the vertex out of the way if the segment is fully invisible - return; - } - } else if (triangletype < 2.0) { // vertex in the view, try to miter - //get directions from (C - B) and (B - A) - vec2 dirA = normalize((currentScreen - previousScreen)); - if (previousProjected.z < 0.0) dirA = -dirA; - vec2 dirB = normalize((nextScreen - currentScreen)); - //now compute the miter join normal and length - if (nextProjected.z < 0.0) dirB = -dirB; - vec2 tangent = normalize(dirA + dirB); - vec2 perp = vec2(-dirA.y, dirA.x); - vec2 vmiter = vec2(-tangent.y, tangent.x); - len = lineWidth / dot(vmiter, perp); -// The following is an attempt to have a segment-length based miter threshold. -// A mediocre workaround until better mitering will be added. - float lenTreshold = clamp( min(length((currentProjected.xy - previousProjected.xy) / aspectVec), - length((nextProjected.xy - currentProjected.xy) / aspectVec)), 3.0, 6.0 ) * 0.5; - if (len < lineWidth * lenTreshold && len > -lineWidth * lenTreshold) { - dir = tangent; - } else { - len = lineWidth; - } - } - vec4 offset; - if (!clipped) { - vec2 normal = normalize(vec2(-dir.y, dir.x)); - normal *= len; // fracZL apparently was needed before the (-2.0 / qt_Matrix[1][1]) factor was introduced - normal /= aspectVec; // straighten the normal up again - float scaleFactor = currentProjected.w / centerProjected.w; - offset = vec4(normal * orientation * scaleFactor * (centerProjected.w / (-2.0 / qt_Matrix[1][1])), 0.0, 0.0); // ToDo: figure out why (-2.0 / qt_Matrix[1][1]), that is empirically what works - gl_Position = currentProjected + offset; - } else { - if (otherEndBelowFrustum) offset = vec4((dir * 1.0) / aspectVec, 0.0, 0.0); // the if is necessary otherwise it seems the direction vector still flips in some obscure cases. - else offset = vec4((dir * 500000000000.0) / aspectVec, 0.0, 0.0); // Hack alert: just 1 triangle, long enough to look like a rectangle. - if (vertextype < 0.0) gl_Position = nextProjected - offset; else gl_Position = previousProjected + offset; - } -} diff --git a/src/location/quickmapitems/shaders/polyline_linestrip.frag b/src/location/quickmapitems/shaders/polyline_linestrip.frag deleted file mode 100644 index e9124d27..00000000 --- a/src/location/quickmapitems/shaders/polyline_linestrip.frag +++ /dev/null @@ -1,17 +0,0 @@ -#version 440 - -layout(location = 0) out vec4 fragColor; - -layout(std140, binding = 0) uniform buf { - mat4 qt_Matrix; - mat4 mapProjection; - vec4 center; - vec4 center_lowpart; - float opacity; - float wrapOffset; - vec4 color; -}; - -void main() { - fragColor = color; -} diff --git a/src/location/quickmapitems/shaders/polyline_linestrip.vert b/src/location/quickmapitems/shaders/polyline_linestrip.vert deleted file mode 100644 index 86b4f3a3..00000000 --- a/src/location/quickmapitems/shaders/polyline_linestrip.vert +++ /dev/null @@ -1,21 +0,0 @@ -#version 440 - -layout(location = 0) in vec4 qt_Vertex; - -layout(std140, binding = 0) uniform buf { - mat4 qt_Matrix; - mat4 mapProjection; - vec4 center; - vec4 center_lowpart; - float opacity; - float wrapOffset; - vec4 color; -}; - -vec4 wrapped(in vec4 v) { return vec4(v.x + wrapOffset, v.y, 0.0, 1.0); } - -void main() { - vec4 vtx = wrapped(qt_Vertex) - center; - vtx = vtx - center_lowpart; - gl_Position = qt_Matrix * mapProjection * vtx; -} -- cgit v1.2.1