diff options
27 files changed, 2951 insertions, 2282 deletions
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 <QtCore/QScopedValueRollback> #include <QPen> -#include <QPainter> #include <qgeocircle.h> #include <QtGui/private/qtriangulator_p.h> @@ -561,8 +560,6 @@ QDeclarativeCircleMapItemPrivate::~QDeclarativeCircleMapItemPrivate() {} QDeclarativeCircleMapItemPrivateCPU::~QDeclarativeCircleMapItemPrivateCPU() {} -QDeclarativeCircleMapItemPrivateOpenGL::~QDeclarativeCircleMapItemPrivateOpenGL() {} - bool QDeclarativeCircleMapItemPrivate::preserveCircleGeometry (QList<QDoubleVector2D> &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<bool> 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<QDeclarativePolygonMapItemPrivateOpenGL::RootNode *>(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<const QGeoProjectionWebMercator&>(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 <QtCore/QScopedValueRollback> #include <qnumeric.h> -#include <QPainter> #include <QPainterPath> -#include <QtQml/QQmlInfo> -#include <QtQuick/qsgnode.h> -#include <QtQuick/private/qsgmaterialshader_p.h> #include <QtLocation/private/qgeomap_p.h> #include <QtPositioning/private/qlocationutils_p.h> #include <QtPositioning/private/qdoublevector2d_p.h> @@ -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<QDoubleVector2D> &path) -{ - QList<QGeoCoordinate> 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<QList<QDoubleVector2D> > &wrappedPaths - ,QDoubleVector2D *leftBoundWrapped = nullptr) -{ - QList<QList<QDoubleVector2D> > paths; - for (qsizetype i = 0; i < 1 + poly.holesCount(); ++i) { - QList<QDoubleVector2D> 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<QDoubleVector2D> 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<QList<QDoubleVector2D>> &wrappedPaths, - QList<QDeclarativeGeoMapItemUtils::vec2> &screenVertices, - QList<quint32> &screenIndices) -{ - using Coord = double; - using N = uint32_t; - using Point = std::array<Coord, 2>; - screenVertices.clear(); - screenIndices.clear(); - - std::vector<std::vector<Point>> polygon; - std::vector<Point> poly; - - for (const QList<QDoubleVector2D> &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<N> indices = qt_mapbox::earcut<N>(polygon); - - for (const auto &i: indices) - screenIndices << quint32(i); -} - -static void cutPathEars(const QList<QDoubleVector2D> &wrappedPath, - QList<QDeclarativeGeoMapItemUtils::vec2> &screenVertices, - QList<quint32> &screenIndices) -{ - using Coord = double; - using N = uint32_t; - using Point = std::array<Coord, 2>; - screenVertices.clear(); - screenIndices.clear(); - - std::vector<std::vector<Point>> polygon; - std::vector<Point> poly; - - for (const QDoubleVector2D &v: wrappedPath) { - screenVertices << v; - Point pt = {{ v.x(), v.y() }}; - poly.push_back( pt ); - } - polygon.push_back(poly); - - std::vector<N> indices = qt_mapbox::earcut<N>(polygon); - - for (const auto &i: indices) - screenIndices << quint32(i); -} - -/*! - \internal -*/ -// This one does only a perimeter -void QGeoMapPolygonGeometryOpenGL::updateSourcePoints(const QGeoMap &map, - const QList<QGeoCoordinate> &perimeter) -{ - if (!sourceDirty_) - return; - const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(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<QDoubleVector2D> wrappedPath; - QDeclarativeGeoMapItemUtils::wrapPath(perimeter, geoLeftBound_, p, - wrappedPath, &leftBoundWrapped); - - // 1.1) do the same for the bbox - QList<QDoubleVector2D> 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<const QGeoProjectionWebMercator&>(map.geoProjection()); - - // build the actual path - // The approach is the same as described in QGeoMapPolylineGeometry::updateSourcePoints - srcOrigin_ = geoLeftBound_; - - QDoubleVector2D leftBoundWrapped; - QList<QList<QDoubleVector2D>> 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<QDoubleVector2D> 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<QGeoCoordinate> 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<const QGeoProjectionWebMercator&>(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<QDoubleVector2D> &wrappedBbox = m_wrappedPolygons.at(m_wrapOffset).wrappedBboxes; - QList<QList<QDoubleVector2D> > 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<const MapPolygonMaterial *>(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<MapPolygonMaterial *>(oldEffect); - MapPolygonMaterial *newMaterial = static_cast<MapPolygonMaterial *>(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 <QtGui/QMatrix4x4> #include <QtGui/QColor> -#include <QSGGeometryNode> #include <QSGFlatColorMaterial> -#include <QtPositioning/QGeoPath> -#include <QtPositioning/QGeoRectangle> -#include <QtPositioning/QGeoPolygon> - #include <QtLocation/private/qlocationglobal_p.h> #include <QtLocation/private/qgeomapitemgeometry_p.h> -#include <QtLocation/private/qdeclarativegeomapitembase_p.h> -#include <QtLocation/private/qdeclarativepolylinemapitem_p.h> -#include <QtLocation/private/qdeclarativegeomapitemutils_p.h> #include <QtLocation/private/qdeclarativepolygonmapitem_p.h> #include <QtLocation/private/qdeclarativepolylinemapitem_p_p.h> + #include <QtPositioning/private/qdoublevector2d_p.h> QT_BEGIN_NAMESPACE @@ -92,123 +85,6 @@ protected: bool assumeSimple_ = false; }; -class Q_LOCATION_PRIVATE_EXPORT QGeoMapPolygonGeometryOpenGL : public QGeoMapItemGeometry -{ -public: - typedef struct { - QList<QDoubleVector2D> wrappedBboxes; - } WrappedPolygon; - QGeoMapPolygonGeometryOpenGL(); - ~QGeoMapPolygonGeometryOpenGL() override {} - - // Temporary method for compatibility in MapCircleObject. Remove when MapObjects are ported. - void updateSourcePoints(const QGeoMap &map, - const QList<QDoubleVector2D> &path); - - void updateSourcePoints(const QGeoMap &map, - const QList<QGeoCoordinate> &perimeter); - - void updateSourcePoints(const QGeoMap &map, - const QGeoPolygon &poly); - - void updateSourcePoints(const QGeoMap &map, - const QGeoRectangle &rect); - - void updateScreenPoints(const QGeoMap &map, qreal strokeWidth = 0.0, const QColor &strokeColor = Qt::transparent); - void updateQuickGeometry(const QGeoProjectionWebMercator &p, qreal strokeWidth = 0.0); - - void allocateAndFillPolygon(QSGGeometry *geom) const - { - - const QList<QDeclarativeGeoMapItemUtils::vec2> &vx = m_screenVertices; - const QList<quint32> &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<QDeclarativeGeoMapItemUtils::vec2> m_screenVertices; - QList<quint32> m_screenIndices; - QDoubleVector2D m_bboxLeftBoundWrapped; - QList<WrappedPolygon> 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<bool> rollback(m_poly.m_updatingGeometry); - m_poly.m_updatingGeometry = true; - const qreal lineWidth = m_poly.m_border.width(); - const QColor &lineColor = m_poly.m_border.color(); - const QColor &fillColor = m_poly.color(); - if (fillColor.alpha() != 0) { - m_geometry.updateSourcePoints(*m_poly.map(), m_poly.m_geopoly); - m_geometry.markScreenDirty(); - m_geometry.updateScreenPoints(*m_poly.map(), lineWidth, lineColor); - } else { - m_geometry.clearBounds(); - } - - QGeoMapItemGeometry * geom = &m_geometry; - m_borderGeometry.clearScreen(); - if (lineColor.alpha() != 0 && lineWidth > 0) { - m_borderGeometry.updateSourcePoints(*m_poly.map(), m_poly.m_geopoly); - m_borderGeometry.markScreenDirty(); - m_borderGeometry.updateScreenPoints(*m_poly.map(), lineWidth); - geom = &m_borderGeometry; - } - m_poly.setWidth(geom->sourceBoundingBox().width()); - m_poly.setHeight(geom->sourceBoundingBox().height()); - m_poly.setPosition(1.0 * geom->firstPointOffset() - QPointF(lineWidth * 0.5,lineWidth * 0.5)); - } - QSGNode * updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) override - { - Q_UNUSED(data); - - if (!m_rootNode || !oldNode) { - m_rootNode = new RootNode(); - m_node = new MapPolygonNodeGL(); - m_rootNode->appendChildNode(m_node); - m_polylinenode = new MapPolylineNodeOpenGLExtruded(); - m_rootNode->appendChildNode(m_polylinenode); - m_rootNode->markDirty(QSGNode::DirtyNodeAdded); - if (oldNode) - delete oldNode; - } else { - m_rootNode = static_cast<RootNode *>(oldNode); - } - - const QGeoMap *map = m_poly.map(); - const QMatrix4x4 &combinedMatrix = map->geoProjection().qsgTransform(); - const QDoubleVector3D &cameraCenter = map->geoProjection().centerMercator(); - - if (m_borderGeometry.isScreenDirty()) { - /* Do the border update first */ - m_polylinenode->update(m_poly.m_border.color(), - float(m_poly.m_border.width()), - &m_borderGeometry, - combinedMatrix, - cameraCenter, - Qt::SquareCap, - true, - 30); // No LOD for polygons just yet. - // First figure out what to do with holes. - m_borderGeometry.setPreserveGeometry(false); - m_borderGeometry.markClean(); - } else { - m_polylinenode->setSubtreeBlocked(true); - } - if (m_geometry.isScreenDirty()) { - m_node->update(m_poly.m_color, - &m_geometry, - combinedMatrix, - cameraCenter); - m_geometry.setPreserveGeometry(false); - m_geometry.markClean(); - } else { - m_node->setSubtreeBlocked(true); - } - - m_rootNode->setSubtreeBlocked(false); - return m_rootNode; - } - bool contains(const QPointF &point) const override - { - const qreal lineWidth = m_poly.m_border.width(); - const QColor &lineColor = m_poly.m_border.color(); - const QRectF &bounds = (lineColor.alpha() != 0 && lineWidth > 0) ? m_borderGeometry.sourceBoundingBox() : m_geometry.sourceBoundingBox(); - if (bounds.contains(point)) { - QDeclarativeGeoMap *m = m_poly.quickMap(); - if (m) { - const QGeoCoordinate crd = m->toCoordinate(m->mapFromItem(&m_poly, point)); - return m_poly.m_geopoly.contains(crd) || m_borderGeometry.contains(m_poly.mapToItem(m_poly.quickMap(), point), - m_poly.border()->width(), - static_cast<const QGeoProjectionWebMercator&>(m_poly.map()->geoProjection())); - } else { - return true; - } - } - return false; - } - - QGeoMapPolygonGeometryOpenGL m_geometry; - QGeoMapPolylineGeometryOpenGL m_borderGeometry; - RootNode *m_rootNode = nullptr; - MapPolygonNodeGL *m_node = nullptr; - MapPolylineNodeOpenGLExtruded *m_polylinenode = nullptr; -}; - QT_END_NAMESPACE #endif // QDECLARATIVEPOLYGONMAPITEM_P_P_H 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 <QtCore/QScopedValueRollback> #include <qnumeric.h> -#include <QThreadPool> -#include <QRunnable> -#include <QPainter> #include <QPainterPath> -#include <QtQml/QQmlInfo> #include <QtGui/private/qtriangulatingstroker_p.h> -#include <QtGui/private/qtriangulator_p.h> -#include <QtQuick/private/qsgmaterialshader_p.h> #include <QtPositioning/private/qlocationutils_p.h> #include <QtPositioning/private/qdoublevector2d_p.h> #include <QtPositioning/private/qwebmercator_p.h> #include <QtPositioning/private/qclipperutils_p.h> #include <QtPositioning/private/qgeopath_p.h> -#include <QtLocation/private/qgeomapparameter_p.h> #include <QtLocation/private/qgeomap_p.h> #include <array> 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<const QGeoProjectionWebMercator&>(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<QDoubleVector2D> 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<QDoubleVector2D> &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<QDoubleVector2D> 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<QGeoCoordinate> 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<const QGeoProjectionWebMercator&>(map.geoProjection()); - - QDoubleVector2D leftBoundWrapped; - // 1) pre-compute 3 sets of "wrapped" coordinates: one w regular mercator, one w regular mercator +- 1.0 - QList<QGeoCoordinate> path; - QGeoCoordinate leftBound; - QList<QDoubleVector2D> 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<const QGeoProjectionWebMercator&>(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<QDoubleVector2D> &wrappedBbox = m_wrappedPolygons.at(m_wrapOffset).wrappedBboxes; - QList<QList<QDoubleVector2D> > 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<MapPolylineMaterial *>(oldEffect); - MapPolylineMaterial *newMaterial = static_cast<MapPolylineMaterial *>(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<const MapPolylineMaterial *>(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<QDeclarativeGeoMapItemUtils::vec2> &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<MapPolylineNodeOpenGLExtruded::MapPolylineEntry *>(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<QDeclarativeGeoMapItemUtils::vec2> &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<MapPolylineMaterialExtruded *>(oldEffect); - MapPolylineMaterialExtruded *newMaterial = static_cast<MapPolylineMaterialExtruded *>(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<const MapPolylineMaterialExtruded *>(other); - if (o.m_miter == m_miter) - return MapPolylineMaterial::compare(other); - return -1; -} - -QList<QDeclarativeGeoMapItemUtils::vec2> QGeoMapItemLODGeometry::getSimplified( - QList<QDeclarativeGeoMapItemUtils::vec2> - &wrappedPath, // reference as it gets copied in the nested call - double leftBoundWrapped, unsigned int zoom) -{ - // Try a simplify step - QList<QDoubleVector2D> data; - for (auto e: wrappedPath) - data << e.toDoubleVector2D(); - const QList<QDoubleVector2D> simplified = QGeoSimplify::geoSimplifyZL(data, - leftBoundWrapped, - zoom); - - data.clear(); - QList<QDeclarativeGeoMapItemUtils::vec2> 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<QList<QDeclarativeGeoMapItemUtils::vec2>> - &input, // reference as it gets copied in the nested call - const QSharedPointer<QList<QDeclarativeGeoMapItemUtils::vec2>> &output, - double leftBound, unsigned int zoom, QSharedPointer<unsigned int> &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<QDeclarativeGeoMapItemUtils::vec2> 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<QList<QDeclarativeGeoMapItemUtils::vec2>> m_input, m_output; - QSharedPointer<unsigned int> m_working; -}; - -void QGeoMapItemLODGeometry::enqueueSimplificationTask( - const QSharedPointer<QList<QDeclarativeGeoMapItemUtils::vec2>> &input, - const QSharedPointer<QList<QDeclarativeGeoMapItemUtils::vec2>> &output, double leftBound, - unsigned int zoom, QSharedPointer<unsigned int> &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<QList<QDeclarativeGeoMapItemUtils::vec2>>( - new QList<QDeclarativeGeoMapItemUtils::vec2>); - - 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<QList<QDeclarativeGeoMapItemUtils::vec2>>( - new QList<QDeclarativeGeoMapItemUtils::vec2>); - *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<QList<QDeclarativeGeoMapItemUtils::vec2>>( - new QList<QDeclarativeGeoMapItemUtils::vec2>); - *m_verticesLOD[1] = getSimplified( *m_verticesLOD[0], - leftBound, - zoomForLOD(0)); - } - if (lod > 1) { - if (!m_verticesLOD[lod]) - m_verticesLOD[lod] = QSharedPointer<QList<QDeclarativeGeoMapItemUtils::vec2>>( - new QList<QDeclarativeGeoMapItemUtils::vec2>); - enqueueSimplificationTask( m_verticesLOD.at(0), - m_verticesLOD[lod], - leftBound, - zoom, - m_working); - } - m_screenVertices = m_verticesLOD[qMin<unsigned int>(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<unsigned int>(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<unsigned int>(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 <QSharedPointer> #include <QtCore/QScopedValueRollback> #include <QSGGeometryNode> #include <QSGFlatColorMaterial> -#include <QSGMaterialShader> -#include <QtPositioning/QGeoPath> -#include <QtPositioning/QGeoPolygon> -#include <QtPositioning/QGeoRectangle> -#include <QtPositioning/QGeoCircle> #include <QtLocation/private/qlocationglobal_p.h> #include <QtLocation/private/qdeclarativepolylinemapitem_p.h> -#include <QtLocation/private/qdeclarativegeomapitemutils_p.h> -#include <QtLocation/private/qdeclarativepolylinemapitem_p.h> #include <QtLocation/private/qgeomapitemgeometry_p.h> #include <QtPositioning/private/qdoublevector2d_p.h> -#include <array> - 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<QSharedPointer<QList<QDeclarativeGeoMapItemUtils::vec2>>, 7> - m_verticesLOD; // fix it to 7, - // do not allow simplifications beyond ZL 20. This could actually be - // limited even further - mutable QList<QDeclarativeGeoMapItemUtils::vec2> *m_screenVertices; - mutable QSharedPointer<unsigned int> m_working; - - QGeoMapItemLODGeometry() - { - resetLOD(); - } - - void resetLOD() - { - // New pointer, some old LOD task might still be running and operating on the old pointers. - m_verticesLOD[0] = QSharedPointer<QList<QDeclarativeGeoMapItemUtils::vec2>>( - new QList<QDeclarativeGeoMapItemUtils::vec2>); - for (unsigned int i = 1; i < m_verticesLOD.size(); ++i) - m_verticesLOD[i] = nullptr; // allocate on first use - m_screenVertices = m_verticesLOD.front().data(); // resetting pointer to data to be LOD 0 - } - - static unsigned int zoomToLOD(unsigned int zoom); - - static unsigned int zoomForLOD(unsigned int zoom); - - bool isLODActive(unsigned int lod) const; - - void selectLOD(unsigned int zoom, double leftBound, bool /*closed*/); - - static QList<QDeclarativeGeoMapItemUtils::vec2> - getSimplified(QList<QDeclarativeGeoMapItemUtils::vec2> &wrappedPath, double leftBoundWrapped, - unsigned int zoom); - - static void enqueueSimplificationTask( - const QSharedPointer<QList<QDeclarativeGeoMapItemUtils::vec2>> - &input, // reference as it gets copied in the nested call - const QSharedPointer<QList<QDeclarativeGeoMapItemUtils::vec2>> &output, - double leftBound, unsigned int zoom, QSharedPointer<unsigned int> &working); - - void selectLODOnDataChanged(unsigned int zoom, double leftBound) const; - - bool selectLODOnLODMismatch(unsigned int zoom, double leftBound, bool closed) const - { - if (*m_working > 0) { - return false; - } - const_cast<QGeoMapItemLODGeometry *>(this)->selectLOD(zoom, - leftBound, - closed); - return true; - } -}; - -class Q_LOCATION_PRIVATE_EXPORT QGeoMapPolylineGeometryOpenGL : public QGeoMapItemGeometry, public QGeoMapItemLODGeometry -{ -public: - typedef struct { - QList<QDoubleVector2D> wrappedBboxes; - } WrappedPolyline; - - QGeoMapPolylineGeometryOpenGL() - { - m_working = QSharedPointer<unsigned int>(new unsigned int(0)); - } - - void updateSourcePoints(const QGeoMap &map, - const QGeoPolygon &poly); - - void updateSourcePoints(const QGeoMap &map, - const QGeoPath &poly); - - void updateSourcePoints(const QGeoProjectionWebMercator &p, - const QList<QDoubleVector2D> &wrappedPath, - const QGeoRectangle &boundingRectangle); - - void updateSourcePoints(const QGeoMap &map, - const QGeoRectangle &rect); - - void updateSourcePoints(const QGeoMap &map, - const QGeoCircle &circle); - - void updateScreenPoints(const QGeoMap &map, - qreal strokeWidth, - bool adjustTranslation = true); - - void updateQuickGeometry(const QGeoProjectionWebMercator &p, qreal strokeWidth = 0.0); - - bool allocateAndFillEntries(QSGGeometry *geom, - bool closed = false, - unsigned int zoom = 0) const; - void allocateAndFillLineStrip(QSGGeometry *geom, - int lod = 0) const; - - bool contains(const QPointF &point) const override - { - Q_UNUSED(point); - return false; - } - - static double distanceTo(const QDoubleVector2D &a, const QDoubleVector2D &b, const QDoubleVector2D &p) - { - double u = ((p.x() - a.x()) * (b.x() - a.x()) + (p.y() - a.y()) * (b.y() - a.y()) ) / (b - a).lengthSquared(); - QDoubleVector2D intersection(a.x() + u * (b.x() - a.x()) , a.y() + u * (b.y() - a.y()) ); - - QDoubleVector2D candidate = ( (p-a).length() < (p-b).length() ) ? a : b; - - if (u > 0 && u < 1 - && (p-intersection).length() < (p-candidate).length() ) // And it falls in the segment - candidate = intersection; - - return qAbs((candidate - p).length()); - } - // Note: this is also slightly incorrect on joins and in the beginning/end of the line - bool contains(const QPointF &point, qreal lineWidth, const QGeoProjectionWebMercator &p) const - { - const double lineHalfWidth = lineWidth * 0.5; - const QDoubleVector2D pt(point); - QDoubleVector2D a; - if (m_screenVertices->size()) - a = p.wrappedMapProjectionToItemPosition(p.wrapMapProjection(m_screenVertices->first().toDoubleVector2D())); - QDoubleVector2D b; - for (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<WrappedPolyline> m_wrappedPolygons; - int m_wrapOffset; - - friend class QDeclarativeCircleMapItem; - friend class QDeclarativePolygonMapItem; - friend class QDeclarativeRectangleMapItem; -}; - -class Q_LOCATION_PRIVATE_EXPORT MapPolylineShaderLineStrip : public QSGMaterialShader -{ -public: - MapPolylineShaderLineStrip(); - - 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<const QGeoProjectionWebMercator&>(m_poly.map()->geoProjection())); - } - void updatePolish() override - { - if (m_poly.m_geopath.path().length() == 0) { // Possibly cleared - m_geometry.clear(); - m_geometry.clear(); - m_poly.setWidth(0); - m_poly.setHeight(0); - return; - } - - QScopedValueRollback<bool> rollback(m_poly.m_updatingGeometry); - m_poly.m_updatingGeometry = true; - const qreal lineWidth = m_poly.m_line.width(); - m_geometry.updateSourcePoints(*m_poly.map(), m_poly.m_geopath); - m_geometry.markScreenDirty(); - m_geometry.updateScreenPoints(*m_poly.map(), lineWidth); - - m_poly.setWidth(m_geometry.sourceBoundingBox().width()); - m_poly.setHeight(m_geometry.sourceBoundingBox().height()); - m_poly.setPosition(1.0 * m_geometry.firstPointOffset() - QPointF(lineWidth * 0.5,lineWidth * 0.5)); - } - QSGNode * updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) override - { - Q_UNUSED(data); - - if (!m_node || !oldNode) { - m_node = new MapPolylineNodeOpenGLLineStrip(); - if (oldNode) - delete oldNode; - } else { - m_node = static_cast<MapPolylineNodeOpenGLLineStrip *>(oldNode); - } - - if (m_geometry.isScreenDirty() || m_poly.m_dirtyMaterial) { - const QGeoMap *map = m_poly.map(); - const QMatrix4x4 &combinedMatrix = map->geoProjection().qsgTransform(); - const QDoubleVector3D &cameraCenter = map->geoProjection().centerMercator(); - m_node->update(m_poly.m_line.color(), // This updates only the material if the geometry is unchanged - m_poly.m_line.width(), - &m_geometry, - combinedMatrix, - cameraCenter); - m_geometry.setPreserveGeometry(false); - m_geometry.markClean(); - m_poly.m_dirtyMaterial = false; - } - return m_node; - } - - QGeoMapPolylineGeometryOpenGL m_geometry; - MapPolylineNodeOpenGLLineStrip *m_node = nullptr; -}; - -class Q_LOCATION_PRIVATE_EXPORT QDeclarativePolylineMapItemPrivateOpenGLExtruded: public QDeclarativePolylineMapItemPrivateOpenGLLineStrip -{ -public: - - QDeclarativePolylineMapItemPrivateOpenGLExtruded(QDeclarativePolylineMapItem &poly) - : QDeclarativePolylineMapItemPrivateOpenGLLineStrip(poly) - { - } - - QDeclarativePolylineMapItemPrivateOpenGLExtruded(QDeclarativePolylineMapItemPrivate &other) - : QDeclarativePolylineMapItemPrivateOpenGLLineStrip(other) - { - } - - ~QDeclarativePolylineMapItemPrivateOpenGLExtruded() override; - - QSGNode * updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) override - { - Q_UNUSED(data); - const QGeoMap *map = m_poly.map(); - const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map->geoProjection()); - const QMatrix4x4 &combinedMatrix = p.qsgTransform(); - const QDoubleVector3D &cameraCenter = p.centerMercator(); - const QColor &color = m_poly.m_line.color(); - const float lineWidth = m_poly.m_line.width(); - - MapPolylineNodeOpenGLExtruded *nodeTri = nullptr; - if (!m_nodeTri || !oldNode) { - if (oldNode) - delete oldNode; - nodeTri = new MapPolylineNodeOpenGLExtruded(); - } else { - nodeTri = static_cast<MapPolylineNodeOpenGLExtruded *>(oldNode); - } - - //TODO: update only material - if (m_geometry.isScreenDirty() || m_poly.m_dirtyMaterial) { - nodeTri->update(color, - lineWidth , - &m_geometry, - combinedMatrix, - cameraCenter, - 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 <QtCore/QScopedValueRollback> @@ -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<bool> 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<QDeclarativePolygonMapItemPrivateOpenGL::RootNode *>(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<const QGeoProjectionWebMercator&>(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/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<bool> 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<QDeclarativePolygonMapItemPrivateOpenGL::RootNode *>(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<const QGeoProjectionWebMercator&>( + 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 <paolo.angelelli@gmail.com> +** 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 <QtLocation/private/qlocationglobal_p.h> +#include <QtLocation/private/qdeclarativecirclemapitem_p_p.h> +#include <QtLocation/private/qdeclarativepolygonmapitem_p_p.h> + +#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 <QtCore/QScopedValueRollback> +#include <qnumeric.h> +#include <QPainter> +#include <QPainterPath> +#include <QtQml/QQmlInfo> +#include <QtQuick/qsgnode.h> + +#include <QtQuick/private/qsgmaterialshader_p.h> +#include <QtLocation/private/qgeomap_p.h> +#include <QtPositioning/private/qlocationutils_p.h> +#include <QtPositioning/private/qdoublevector2d_p.h> +#include <QtPositioning/private/qclipperutils_p.h> +#include <QtPositioning/private/qgeopolygon_p.h> +#include <QtPositioning/private/qwebmercator_p.h> + +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<MapPolygonMaterial *>(oldEffect); + MapPolygonMaterial *newMaterial = static_cast<MapPolygonMaterial *>(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<const MapPolygonMaterial *>(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<bool> rollback(m_poly.m_updatingGeometry); + m_poly.m_updatingGeometry = true; + const qreal lineWidth = m_poly.m_border.width(); + const QColor &lineColor = m_poly.m_border.color(); + const QColor &fillColor = m_poly.color(); + if (fillColor.alpha() != 0) { + m_geometry.updateSourcePoints(*m_poly.map(), m_poly.m_geopoly); + m_geometry.markScreenDirty(); + m_geometry.updateScreenPoints(*m_poly.map(), lineWidth, lineColor); + } else { + m_geometry.clearBounds(); + } + + QGeoMapItemGeometry * geom = &m_geometry; + m_borderGeometry.clearScreen(); + if (lineColor.alpha() != 0 && lineWidth > 0) { + m_borderGeometry.updateSourcePoints(*m_poly.map(), m_poly.m_geopoly); + m_borderGeometry.markScreenDirty(); + m_borderGeometry.updateScreenPoints(*m_poly.map(), lineWidth); + geom = &m_borderGeometry; + } + m_poly.setWidth(geom->sourceBoundingBox().width()); + m_poly.setHeight(geom->sourceBoundingBox().height()); + m_poly.setPosition(1.0 * geom->firstPointOffset() - QPointF(lineWidth * 0.5,lineWidth * 0.5)); +} + +QSGNode *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<RootNode *>(oldNode); + } + + const QGeoMap *map = m_poly.map(); + const QMatrix4x4 &combinedMatrix = map->geoProjection().qsgTransform(); + const QDoubleVector3D &cameraCenter = map->geoProjection().centerMercator(); + + if (m_borderGeometry.isScreenDirty()) { + /* Do the border update first */ + m_polylinenode->update(m_poly.m_border.color(), + float(m_poly.m_border.width()), + &m_borderGeometry, + combinedMatrix, + cameraCenter, + Qt::SquareCap, + true, + 30); // No LOD for polygons just yet. + // First figure out what to do with holes. + m_borderGeometry.setPreserveGeometry(false); + m_borderGeometry.markClean(); + } else { + m_polylinenode->setSubtreeBlocked(true); + } + if (m_geometry.isScreenDirty()) { + m_node->update(m_poly.m_color, + &m_geometry, + combinedMatrix, + cameraCenter); + m_geometry.setPreserveGeometry(false); + m_geometry.markClean(); + } else { + m_node->setSubtreeBlocked(true); + } + + m_rootNode->setSubtreeBlocked(false); + return m_rootNode; +} + +bool 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<const QGeoProjectionWebMercator&>( + 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 <paolo.angelelli@gmail.com> +** 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 <QList> +#include <QtCore/QScopedValueRollback> +#include <QtGui/QMatrix4x4> +#include <QColor> + +#include <QSGGeometryNode> +#include <QSGFlatColorMaterial> + +#include <QtPositioning/QGeoPath> +#include <QtPositioning/QGeoRectangle> +#include <QtPositioning/QGeoPolygon> + +#include <QtLocation/private/qlocationglobal_p.h> +#include <QtLocation/private/qgeomapitemgeometry_p.h> +#include <QtLocation/private/qdeclarativegeomapitembase_p.h> +#include <QtLocation/private/qdeclarativepolylinemapitem_p.h> +#include <QtLocation/private/qdeclarativepolygonmapitem_p.h> +#include <QtLocation/private/qdeclarativepolygonmapitem_p_p.h> +#include <QtLocation/private/qdeclarativepolylinemapitem_rhi_p.h> +#include <QtPositioning/private/qdoublevector2d_p.h> + +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 <QtQuick/private/qsgmaterialshader_p.h> +#include <QtPositioning/private/qlocationutils_p.h> + +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<MapPolylineMaterial *>(oldEffect); + MapPolylineMaterial *newMaterial = static_cast<MapPolylineMaterial *>(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<const MapPolylineMaterial *>(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<QDeclarativeGeoMapItemUtils::vec2> &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<MapPolylineNodeOpenGLExtruded::MapPolylineEntry *>(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<QDeclarativeGeoMapItemUtils::vec2> &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<MapPolylineMaterialExtruded *>(oldEffect); + MapPolylineMaterialExtruded *newMaterial = static_cast<MapPolylineMaterialExtruded *>(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<const MapPolylineMaterialExtruded *>(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<const QGeoProjectionWebMercator&>(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<bool> rollback(m_poly.m_updatingGeometry); + m_poly.m_updatingGeometry = true; + const qreal lineWidth = m_poly.m_line.width(); + m_geometry.updateSourcePoints(*m_poly.map(), m_poly.m_geopath); + m_geometry.markScreenDirty(); + m_geometry.updateScreenPoints(*m_poly.map(), lineWidth); + + m_poly.setWidth(m_geometry.sourceBoundingBox().width()); + m_poly.setHeight(m_geometry.sourceBoundingBox().height()); + m_poly.setPosition(1.0 * m_geometry.firstPointOffset() - QPointF(lineWidth * 0.5,lineWidth * 0.5)); +} + +QSGNode *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<MapPolylineNodeOpenGLLineStrip *>(oldNode); + } + + if (m_geometry.isScreenDirty() || m_poly.m_dirtyMaterial) { + const QGeoMap *map = m_poly.map(); + const QMatrix4x4 &combinedMatrix = map->geoProjection().qsgTransform(); + const QDoubleVector3D &cameraCenter = map->geoProjection().centerMercator(); + m_node->update(m_poly.m_line.color(), // This updates only the material if the geometry is unchanged + m_poly.m_line.width(), + &m_geometry, + combinedMatrix, + cameraCenter); + m_geometry.setPreserveGeometry(false); + m_geometry.markClean(); + m_poly.m_dirtyMaterial = false; + } + return m_node; +} + +QDeclarativePolylineMapItemPrivateOpenGLExtruded::~QDeclarativePolylineMapItemPrivateOpenGLExtruded() {} + +QSGNode *QDeclarativePolylineMapItemPrivateOpenGLExtruded::updateMapItemPaintNode(QSGNode *oldNode, + QQuickItem::UpdatePaintNodeData *data) +{ + Q_UNUSED(data); + const QGeoMap *map = m_poly.map(); + const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map->geoProjection()); + const QMatrix4x4 &combinedMatrix = p.qsgTransform(); + const QDoubleVector3D &cameraCenter = p.centerMercator(); + const QColor &color = m_poly.m_line.color(); + const float lineWidth = m_poly.m_line.width(); + + MapPolylineNodeOpenGLExtruded *nodeTri = nullptr; + if (!m_nodeTri || !oldNode) { + if (oldNode) + delete oldNode; + nodeTri = new MapPolylineNodeOpenGLExtruded(); + } else { + nodeTri = static_cast<MapPolylineNodeOpenGLExtruded *>(oldNode); + } + + //TODO: update only material + if (m_geometry.isScreenDirty() || m_poly.m_dirtyMaterial) { + nodeTri->update(color, + lineWidth , + &m_geometry, + combinedMatrix, + cameraCenter, + 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 <paolo.angelelli@gmail.com> +** 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 <QtCore/QScopedValueRollback> +#include <QSGMaterialShader> + +#include <QtLocation/private/qlocationglobal_p.h> +#include <QtLocation/private/qdeclarativepolylinemapitem_p.h> +#include <QtLocation/private/qdeclarativepolylinemapitem_p_p.h> +#include <QtLocation/private/qgeomapitemgeometry_p.h> +#include <QtLocation/private/qgeomapitemgeometry_rhi_p.h> + +#include <QtPositioning/private/qdoublevector2d_p.h> + +#include <array> + +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<bool> 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<QDeclarativePolygonMapItemPrivateOpenGL::RootNode *>(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<const QGeoProjectionWebMercator&>( + 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 <paolo.angelelli@gmail.com> +** 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 <QtLocation/private/qlocationglobal_p.h> +#include <QtLocation/private/qdeclarativerectanglemapitem_p_p.h> +#include <QtLocation/private/qdeclarativepolygonmapitem_p_p.h> + +#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 <QThreadPool> +#include <QRunnable> + +#include <QtQuick/QSGGeometry> +#include <QtPositioning/private/qwebmercator_p.h> +#include <QtPositioning/private/qlocationutils_p.h> +#include <QtPositioning/QGeoCircle> +#include <QtPositioning/QGeoPolygon> +#include <QtPositioning/QGeoPath> +#include <QtPositioning/QGeoRectangle> + +#include <QtLocation/private/qgeomap_p.h> +#include <QtLocation/private/qdeclarativecirclemapitem_p_p.h> +#include <QtLocation/private/qgeosimplify_p.h> + +/* poly2tri triangulator includes */ +#include <earcut.hpp> +#include <array> + +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<QList<QDeclarativeGeoMapItemUtils::vec2>> + &input, // reference as it gets copied in the nested call + const QSharedPointer<QList<QDeclarativeGeoMapItemUtils::vec2>> &output, + double leftBound, unsigned int zoom, QSharedPointer<unsigned int> &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<QDeclarativeGeoMapItemUtils::vec2> 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<QList<QDeclarativeGeoMapItemUtils::vec2>> m_input, m_output; + QSharedPointer<unsigned int> 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<QList<QDeclarativeGeoMapItemUtils::vec2>>( + new QList<QDeclarativeGeoMapItemUtils::vec2>); + for (unsigned int i = 1; i < m_verticesLOD.size(); ++i) + m_verticesLOD[i] = nullptr; // allocate on first use + m_screenVertices = m_verticesLOD.front().data(); // resetting pointer to data to be LOD 0 +} + +QList<QDeclarativeGeoMapItemUtils::vec2> QGeoMapItemLODGeometry::getSimplified( + QList<QDeclarativeGeoMapItemUtils::vec2> + &wrappedPath, // reference as it gets copied in the nested call + double leftBoundWrapped, unsigned int zoom) +{ + // Try a simplify step + QList<QDoubleVector2D> data; + for (auto e: wrappedPath) + data << e.toDoubleVector2D(); + const QList<QDoubleVector2D> simplified = QGeoSimplify::geoSimplifyZL(data, + leftBoundWrapped, + zoom); + + data.clear(); + QList<QDeclarativeGeoMapItemUtils::vec2> 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<QGeoMapItemLODGeometry *>(this)->selectLOD(zoom, leftBound, closed); + return true; +} + +void QGeoMapItemLODGeometry::enqueueSimplificationTask( + const QSharedPointer<QList<QDeclarativeGeoMapItemUtils::vec2>> &input, + const QSharedPointer<QList<QDeclarativeGeoMapItemUtils::vec2>> &output, double leftBound, + unsigned int zoom, QSharedPointer<unsigned int> &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<QList<QDeclarativeGeoMapItemUtils::vec2>>( + new QList<QDeclarativeGeoMapItemUtils::vec2>); + + 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<QList<QDeclarativeGeoMapItemUtils::vec2>>( + new QList<QDeclarativeGeoMapItemUtils::vec2>); + *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<QList<QDeclarativeGeoMapItemUtils::vec2>>( + new QList<QDeclarativeGeoMapItemUtils::vec2>); + *m_verticesLOD[1] = getSimplified( *m_verticesLOD[0], leftBound, zoomForLOD(0)); + } + if (lod > 1) { + if (!m_verticesLOD[lod]) { + m_verticesLOD[lod] = QSharedPointer<QList<QDeclarativeGeoMapItemUtils::vec2>>( + new QList<QDeclarativeGeoMapItemUtils::vec2>); + } + enqueueSimplificationTask(m_verticesLOD.at(0), + m_verticesLOD[lod], + leftBound, + zoom, + m_working); + } + m_screenVertices = m_verticesLOD[qMin<unsigned int>(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<unsigned int>(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<unsigned int>(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<QList<QDoubleVector2D> > &wrappedPaths + ,QDoubleVector2D *leftBoundWrapped = nullptr) +{ + QList<QList<QDoubleVector2D> > paths; + for (qsizetype i = 0; i < 1 + poly.holesCount(); ++i) { + QList<QDoubleVector2D> 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<QDoubleVector2D> 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<QList<QDoubleVector2D>> &wrappedPaths, + QList<QDeclarativeGeoMapItemUtils::vec2> &screenVertices, + QList<quint32> &screenIndices) +{ + using Coord = double; + using N = uint32_t; + using Point = std::array<Coord, 2>; + screenVertices.clear(); + screenIndices.clear(); + + std::vector<std::vector<Point>> polygon; + std::vector<Point> poly; + + for (const QList<QDoubleVector2D> &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<N> indices = qt_mapbox::earcut<N>(polygon); + + for (const auto &i: indices) + screenIndices << quint32(i); +} + +static void cutPathEars(const QList<QDoubleVector2D> &wrappedPath, + QList<QDeclarativeGeoMapItemUtils::vec2> &screenVertices, + QList<quint32> &screenIndices) +{ + using Coord = double; + using N = uint32_t; + using Point = std::array<Coord, 2>; + screenVertices.clear(); + screenIndices.clear(); + + std::vector<std::vector<Point>> polygon; + std::vector<Point> poly; + + for (const QDoubleVector2D &v: wrappedPath) { + screenVertices << v; + Point pt = {{ v.x(), v.y() }}; + poly.push_back( pt ); + } + polygon.push_back(poly); + + std::vector<N> indices = qt_mapbox::earcut<N>(polygon); + + for (const auto &i: indices) + screenIndices << quint32(i); +} + +void QGeoMapPolygonGeometryOpenGL::allocateAndFillPolygon(QSGGeometry *geom) const +{ + + const QList<QDeclarativeGeoMapItemUtils::vec2> &vx = m_screenVertices; + const QList<quint32> &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<QDoubleVector2D> &path) +{ + QList<QGeoCoordinate> 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<QGeoCoordinate> &perimeter) +{ + if (!sourceDirty_) + return; + const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(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<QDoubleVector2D> wrappedPath; + QDeclarativeGeoMapItemUtils::wrapPath(perimeter, geoLeftBound_, p, + wrappedPath, &leftBoundWrapped); + + // 1.1) do the same for the bbox + QList<QDoubleVector2D> 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<const QGeoProjectionWebMercator&>(map.geoProjection()); + + // build the actual path + // The approach is the same as described in QGeoMapPolylineGeometry::updateSourcePoints + srcOrigin_ = geoLeftBound_; + + QDoubleVector2D leftBoundWrapped; + QList<QList<QDoubleVector2D>> 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<QDoubleVector2D> 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<QGeoCoordinate> 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<const QGeoProjectionWebMercator&>(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<QDoubleVector2D> &wrappedBbox = m_wrappedPolygons.at(m_wrapOffset).wrappedBboxes; + QList<QList<QDoubleVector2D> > 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<const QGeoProjectionWebMercator&>(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<QDoubleVector2D> 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<QDoubleVector2D> &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<QDoubleVector2D> 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<QGeoCoordinate> 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<const QGeoProjectionWebMercator&>(map.geoProjection()); + + QDoubleVector2D leftBoundWrapped; + // 1) pre-compute 3 sets of "wrapped" coordinates: one w regular mercator, one w regular mercator +- 1.0 + QList<QGeoCoordinate> path; + QGeoCoordinate leftBound; + QList<QDoubleVector2D> 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<const QGeoProjectionWebMercator&>(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<QDoubleVector2D> &wrappedBbox = m_wrappedPolygons.at(m_wrapOffset).wrappedBboxes; + QList<QList<QDoubleVector2D> > 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 <QtCore/QList> +#include <QtCore/QSharedPointer> +#include <QtGui/QColor> +#include <QtPositioning/private/qdoublevector2d_p.h> +#include <QtLocation/private/qdeclarativegeomapitemutils_p.h> +#include <QtLocation/private/qgeomapitemgeometry_p.h> + +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<QSharedPointer<QList<QDeclarativeGeoMapItemUtils::vec2>>, 7> + m_verticesLOD; // fix it to 7, + // do not allow simplifications beyond ZL 20. This could actually be + // limited even further + mutable QList<QDeclarativeGeoMapItemUtils::vec2> *m_screenVertices; + mutable QSharedPointer<unsigned int> 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<QDeclarativeGeoMapItemUtils::vec2> + getSimplified(QList<QDeclarativeGeoMapItemUtils::vec2> &wrappedPath, double leftBoundWrapped, + unsigned int zoom); + + static void enqueueSimplificationTask( + const QSharedPointer<QList<QDeclarativeGeoMapItemUtils::vec2>> + &input, // reference as it gets copied in the nested call + const QSharedPointer<QList<QDeclarativeGeoMapItemUtils::vec2>> &output, + double leftBound, unsigned int zoom, QSharedPointer<unsigned int> &working); + + void selectLODOnDataChanged(unsigned int zoom, double leftBound) const; + bool selectLODOnLODMismatch(unsigned int zoom, double leftBound, bool closed) const; +}; + +class Q_LOCATION_PRIVATE_EXPORT QGeoMapPolylineGeometryOpenGL : public QGeoMapItemGeometry, + public QGeoMapItemLODGeometry +{ +public: + typedef struct { + QList<QDoubleVector2D> wrappedBboxes; + } WrappedPolyline; + + QGeoMapPolylineGeometryOpenGL() + { + m_working = QSharedPointer<unsigned int>(new unsigned int(0)); + } + + void updateSourcePoints(const QGeoMap &map, + const QGeoPolygon &poly); + + void updateSourcePoints(const QGeoMap &map, + const QGeoPath &poly); + + void updateSourcePoints(const QGeoProjectionWebMercator &p, + const QList<QDoubleVector2D> &wrappedPath, + const QGeoRectangle &boundingRectangle); + + void updateSourcePoints(const QGeoMap &map, + const QGeoRectangle &rect); + + void updateSourcePoints(const QGeoMap &map, + const QGeoCircle &circle); + + void updateScreenPoints(const QGeoMap &map, + qreal strokeWidth, + bool adjustTranslation = true); + + void updateQuickGeometry(const QGeoProjectionWebMercator &p, qreal strokeWidth = 0.0); + + bool allocateAndFillEntries(QSGGeometry *geom, + bool closed = false, + unsigned int zoom = 0) const; + void allocateAndFillLineStrip(QSGGeometry *geom, + int lod = 0) const; + + bool contains(const QPointF &point) const override + { + Q_UNUSED(point); + return false; + } + + static double distanceTo(const QDoubleVector2D &a, const QDoubleVector2D &b, const QDoubleVector2D &p) + { + double u = ((p.x() - a.x()) * (b.x() - a.x()) + (p.y() - a.y()) * (b.y() - a.y())) + / (b - a).lengthSquared(); + QDoubleVector2D intersection(a.x() + u * (b.x() - a.x()) , a.y() + u * (b.y() - a.y())); + + QDoubleVector2D candidate = ( (p-a).length() < (p-b).length() ) ? a : b; + + // 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<WrappedPolyline> 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<QDoubleVector2D> wrappedBboxes; + } WrappedPolygon; + QGeoMapPolygonGeometryOpenGL(); + ~QGeoMapPolygonGeometryOpenGL() override {} + + // Temporary method for compatibility in MapCircleObject. Remove when MapObjects are ported. + void updateSourcePoints(const QGeoMap &map, + const QList<QDoubleVector2D> &path); + + void updateSourcePoints(const QGeoMap &map, + const QList<QGeoCoordinate> &perimeter); + + void updateSourcePoints(const QGeoMap &map, + const QGeoPolygon &poly); + + void updateSourcePoints(const QGeoMap &map, + const QGeoRectangle &rect); + + void updateScreenPoints(const QGeoMap &map, qreal strokeWidth = 0.0, + const QColor &strokeColor = Qt::transparent); + void updateQuickGeometry(const QGeoProjectionWebMercator &p, qreal strokeWidth = 0.0); + + void allocateAndFillPolygon(QSGGeometry *geom) const; + + QList<QDeclarativeGeoMapItemUtils::vec2> m_screenVertices; + QList<quint32> m_screenIndices; + QDoubleVector2D m_bboxLeftBoundWrapped; + QList<WrappedPolygon> m_wrappedPolygons; + int m_wrapOffset = 0; +}; + +QT_END_NAMESPACE + +#endif // QGEOMAPITEMGEOMETRY_RHI_P_H diff --git a/src/location/quickmapitems/qgeosimplify.cpp b/src/location/quickmapitems/rhi/qgeosimplify.cpp index ad5a3c5d..ad5a3c5d 100644 --- a/src/location/quickmapitems/qgeosimplify.cpp +++ b/src/location/quickmapitems/rhi/qgeosimplify.cpp diff --git a/src/location/quickmapitems/qgeosimplify_p.h b/src/location/quickmapitems/rhi/qgeosimplify_p.h index 1f5c9783..1f5c9783 100644 --- a/src/location/quickmapitems/qgeosimplify_p.h +++ b/src/location/quickmapitems/rhi/qgeosimplify_p.h diff --git a/src/location/quickmapitems/shaders/polygon.frag b/src/location/quickmapitems/rhi/shaders/polygon.frag index 9b9727b6..9b9727b6 100644 --- a/src/location/quickmapitems/shaders/polygon.frag +++ b/src/location/quickmapitems/rhi/shaders/polygon.frag diff --git a/src/location/quickmapitems/shaders/polygon.vert b/src/location/quickmapitems/rhi/shaders/polygon.vert index 06dab03e..06dab03e 100644 --- a/src/location/quickmapitems/shaders/polygon.vert +++ b/src/location/quickmapitems/rhi/shaders/polygon.vert diff --git a/src/location/quickmapitems/shaders/polyline_extruded.frag b/src/location/quickmapitems/rhi/shaders/polyline_extruded.frag index 3530f093..3530f093 100644 --- a/src/location/quickmapitems/shaders/polyline_extruded.frag +++ b/src/location/quickmapitems/rhi/shaders/polyline_extruded.frag diff --git a/src/location/quickmapitems/shaders/polyline_extruded.vert b/src/location/quickmapitems/rhi/shaders/polyline_extruded.vert index 27b69b60..27b69b60 100644 --- a/src/location/quickmapitems/shaders/polyline_extruded.vert +++ b/src/location/quickmapitems/rhi/shaders/polyline_extruded.vert diff --git a/src/location/quickmapitems/shaders/polyline_linestrip.frag b/src/location/quickmapitems/rhi/shaders/polyline_linestrip.frag index e9124d27..e9124d27 100644 --- a/src/location/quickmapitems/shaders/polyline_linestrip.frag +++ b/src/location/quickmapitems/rhi/shaders/polyline_linestrip.frag diff --git a/src/location/quickmapitems/shaders/polyline_linestrip.vert b/src/location/quickmapitems/rhi/shaders/polyline_linestrip.vert index 86b4f3a3..86b4f3a3 100644 --- a/src/location/quickmapitems/shaders/polyline_linestrip.vert +++ b/src/location/quickmapitems/rhi/shaders/polyline_linestrip.vert |