diff options
Diffstat (limited to 'src/location/declarativemaps/qdeclarativepolygonmapitem.cpp')
-rw-r--r-- | src/location/declarativemaps/qdeclarativepolygonmapitem.cpp | 639 |
1 files changed, 490 insertions, 149 deletions
diff --git a/src/location/declarativemaps/qdeclarativepolygonmapitem.cpp b/src/location/declarativemaps/qdeclarativepolygonmapitem.cpp index 86ff04af..fa6ee174 100644 --- a/src/location/declarativemaps/qdeclarativepolygonmapitem.cpp +++ b/src/location/declarativemaps/qdeclarativepolygonmapitem.cpp @@ -34,7 +34,11 @@ ** ****************************************************************************/ +#include "qdeclarativegeomapitemutils_p.h" #include "qdeclarativepolygonmapitem_p.h" +#include "qdeclarativepolylinemapitem_p_p.h" +#include "qdeclarativepolygonmapitem_p_p.h" +#include "qdeclarativerectanglemapitem_p_p.h" #include "qlocationutils_p.h" #include "error_messages_p.h" #include "locationvaluetypehelper_p.h" @@ -51,6 +55,10 @@ #include <QtPositioning/private/qdoublevector2d_p.h> #include <QtPositioning/private/qclipperutils_p.h> #include <QtPositioning/private/qgeopolygon_p.h> +#include <QtPositioning/private/qwebmercator_p.h> +#include <QtQuick/private/qsgmaterialshader_p.h> +#include <QtQuick/private/qquickitem_p.h> +#include <QtQuick/qsgnode.h> /* poly2tri triangulator includes */ #include <clip2tri.h> @@ -326,17 +334,307 @@ void QGeoMapPolygonGeometry::updateScreenPoints(const QGeoMap &map, qreal stroke this->translate(QPointF(strokeWidth, strokeWidth)); } +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 (int i = 0; i < 1+poly.holesCount(); ++i) { + QList<QDoubleVector2D> path; + if (!i) { + for (const QGeoCoordinate &c : poly.path()) + 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 (int j = 0; j < paths.size(); ++j) { + const QList<QDoubleVector2D> &path = paths.at(j); + wrappedPath.clear(); + for (int i = 0; i < path.size(); ++i) { + QDoubleVector2D coord = path.at(i); + + // 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, + QVector<QDeclarativeGeoMapItemUtils::vec2> &screenVertices, + QVector<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, + QVector<QDeclarativeGeoMapItemUtils::vec2> &screenVertices, + QVector<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.path(), 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 geometryChanged 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.path(), 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 geometryChanged 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 = QDeclarativeRectangleMapItemPrivateCPU::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()); +} + +/* + * QDeclarativePolygonMapItem Private Implementations + */ + +QDeclarativePolygonMapItemPrivate::~QDeclarativePolygonMapItemPrivate() {} + +QDeclarativePolygonMapItemPrivateCPU::~QDeclarativePolygonMapItemPrivateCPU() {} + +QDeclarativePolygonMapItemPrivateOpenGL::~QDeclarativePolygonMapItemPrivateOpenGL() {} + +/* + * QDeclarativePolygonMapItem Implementation + */ + +struct PolygonBackendSelector +{ + PolygonBackendSelector() + { + backend = (qgetenv("QTLOCATION_OPENGL_ITEMS").toInt()) ? QDeclarativePolygonMapItem::OpenGL : QDeclarativePolygonMapItem::Software; + } + QDeclarativePolygonMapItem::Backend backend = QDeclarativePolygonMapItem::Software; +}; + +Q_GLOBAL_STATIC(PolygonBackendSelector, mapPolygonBackendSelector) + QDeclarativePolygonMapItem::QDeclarativePolygonMapItem(QQuickItem *parent) -: QDeclarativeGeoMapItemBase(parent), border_(this), color_(Qt::transparent), dirtyMaterial_(true), - updatingGeometry_(false) +: QDeclarativeGeoMapItemBase(parent), m_border(this), m_color(Qt::transparent), m_dirtyMaterial(true), + m_updatingGeometry(false) + , m_d(new QDeclarativePolygonMapItemPrivateCPU(*this)) + { + // ToDo: handle envvar, and switch implementation. m_itemType = QGeoMap::MapPolygon; - geopath_ = QGeoPolygonEager(); + m_geopoly = QGeoPolygonEager(); setFlag(ItemHasContents, true); - QObject::connect(&border_, SIGNAL(colorChanged(QColor)), - this, SLOT(markSourceDirtyAndUpdate())); - QObject::connect(&border_, SIGNAL(widthChanged(qreal)), - this, SLOT(markSourceDirtyAndUpdate())); + QObject::connect(&m_border, SIGNAL(colorChanged(QColor)), + this, SLOT(onLinePropertiesChanged())); // ToDo: fix this, only flag material? + QObject::connect(&m_border, SIGNAL(widthChanged(qreal)), + this, SLOT(onLinePropertiesChanged())); + setBackend(mapPolygonBackendSelector->backend); } QDeclarativePolygonMapItem::~QDeclarativePolygonMapItem() @@ -359,7 +657,44 @@ QDeclarativePolygonMapItem::~QDeclarativePolygonMapItem() QDeclarativeMapLineProperties *QDeclarativePolygonMapItem::border() { - return &border_; + return &m_border; +} + +/*! + \qmlproperty MapPolygon.Backend QtLocation::MapPolygon::backend + + This property holds which backend is in use to render the map item. + Valid values are \b MapPolygon.Software and \b{MapPolygon.OpenGL}. + The default value is \b{MapPolygon.Software}. + + \note \b{The release of this API with Qt 5.15 is a Technology Preview}. + Ideally, as the OpenGL backends for map items mature, there will be + no more need to also offer the legacy software-projection backend. + So this property will likely disappear at some later point. + To select OpenGL-accelerated item backends without using this property, + it is also possible to set the environment variable \b QTLOCATION_OPENGL_ITEMS + to \b{1}. + Also note that all current OpenGL backends won't work as expected when enabling + layers on the individual item, or when running on OpenGL core profiles greater than 2.x. + + \since 5.15 +*/ +QDeclarativePolygonMapItem::Backend QDeclarativePolygonMapItem::backend() const +{ + return m_backend; +} + +void QDeclarativePolygonMapItem::setBackend(QDeclarativePolygonMapItem::Backend b) +{ + if (b == m_backend) + return; + m_backend = b; + QScopedPointer<QDeclarativePolygonMapItemPrivate> d((m_backend == Software) + ? static_cast<QDeclarativePolygonMapItemPrivate *>(new QDeclarativePolygonMapItemPrivateCPU(*this)) + : static_cast<QDeclarativePolygonMapItemPrivate * >(new QDeclarativePolygonMapItemPrivateOpenGL(*this))); + m_d.swap(d); + m_d->onGeoGeometryChanged(); + emit backendChanged(); } /*! @@ -368,12 +703,8 @@ QDeclarativeMapLineProperties *QDeclarativePolygonMapItem::border() void QDeclarativePolygonMapItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map) { QDeclarativeGeoMapItemBase::setMap(quickMap,map); - if (map) { - regenerateCache(); - geometry_.markSourceDirty(); - borderGeometry_.markSourceDirty(); - polishAndUpdate(); - } + if (map) + m_d->onMapSet(); } /*! @@ -387,7 +718,7 @@ void QDeclarativePolygonMapItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *m */ QJSValue QDeclarativePolygonMapItem::path() const { - return fromList(this, geopath_.path()); + return fromList(this, m_geopoly.path()); } void QDeclarativePolygonMapItem::setPath(const QJSValue &value) @@ -398,15 +729,12 @@ void QDeclarativePolygonMapItem::setPath(const QJSValue &value) QList<QGeoCoordinate> pathList = toList(this, value); // Equivalent to QDeclarativePolylineMapItem::setPathFromGeoList - if (geopath_.path() == pathList) + if (m_geopoly.path() == pathList) return; - geopath_.setPath(pathList); + m_geopoly.setPath(pathList); - regenerateCache(); - geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); - borderGeometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); - markSourceDirtyAndUpdate(); + m_d->onGeoGeometryChanged(); emit pathChanged(); } @@ -423,11 +751,8 @@ void QDeclarativePolygonMapItem::addCoordinate(const QGeoCoordinate &coordinate) if (!coordinate.isValid()) return; - geopath_.addCoordinate(coordinate); - updateCache(); - geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); - borderGeometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); - markSourceDirtyAndUpdate(); + m_geopoly.addCoordinate(coordinate); + m_d->onGeoGeometryUpdated(); emit pathChanged(); } @@ -443,15 +768,12 @@ void QDeclarativePolygonMapItem::addCoordinate(const QGeoCoordinate &coordinate) */ void QDeclarativePolygonMapItem::removeCoordinate(const QGeoCoordinate &coordinate) { - int length = geopath_.path().length(); - geopath_.removeCoordinate(coordinate); - if (geopath_.path().length() == length) + int length = m_geopoly.path().length(); + m_geopoly.removeCoordinate(coordinate); + if (m_geopoly.path().length() == length) return; - regenerateCache(); - geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); - borderGeometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); - markSourceDirtyAndUpdate(); + m_d->onGeoGeometryChanged(); emit pathChanged(); } @@ -465,18 +787,18 @@ void QDeclarativePolygonMapItem::removeCoordinate(const QGeoCoordinate &coordina QColor QDeclarativePolygonMapItem::color() const { - return color_; + return m_color; } void QDeclarativePolygonMapItem::setColor(const QColor &color) { - if (color_ == color) + if (m_color == color) return; - color_ = color; - dirtyMaterial_ = true; - update(); - emit colorChanged(color_); + m_color = color; + m_dirtyMaterial = true; + polishAndUpdate(); // in case color was transparent and now is not or vice versa + emit colorChanged(m_color); } /*! @@ -484,22 +806,7 @@ void QDeclarativePolygonMapItem::setColor(const QColor &color) */ QSGNode *QDeclarativePolygonMapItem::updateMapItemPaintNode(QSGNode *oldNode, UpdatePaintNodeData *data) { - Q_UNUSED(data); - MapPolygonNode *node = static_cast<MapPolygonNode *>(oldNode); - - if (!node) - node = new MapPolygonNode(); - - //TODO: update only material - if (geometry_.isScreenDirty() || borderGeometry_.isScreenDirty() || dirtyMaterial_) { - node->update(color_, border_.color(), &geometry_, &borderGeometry_); - geometry_.setPreserveGeometry(false); - borderGeometry_.setPreserveGeometry(false); - geometry_.markClean(); - borderGeometry_.markClean(); - dirtyMaterial_ = false; - } - return node; + return m_d->updateMapItemPaintNode(oldNode, data); } /*! @@ -509,102 +816,34 @@ void QDeclarativePolygonMapItem::updatePolish() { if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) return; - if (geopath_.path().length() == 0) { // Possibly cleared - geometry_.clear(); - borderGeometry_.clear(); - setWidth(0); - setHeight(0); - return; - } - - const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map()->geoProjection()); - QScopedValueRollback<bool> rollback(updatingGeometry_); - updatingGeometry_ = true; - - geometry_.updateSourcePoints(*map(), geopathProjected_); - geometry_.updateScreenPoints(*map(), border_.width()); - - QList<QGeoMapItemGeometry *> geoms; - geoms << &geometry_; - borderGeometry_.clear(); - - if (border_.color() != Qt::transparent && border_.width() > 0) { - QList<QDoubleVector2D> closedPath = geopathProjected_; - closedPath << closedPath.first(); - - borderGeometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); - - const QGeoCoordinate &geometryOrigin = geometry_.origin(); - - borderGeometry_.srcPoints_.clear(); - borderGeometry_.srcPointTypes_.clear(); - - QDoubleVector2D borderLeftBoundWrapped; - QList<QList<QDoubleVector2D > > clippedPaths = borderGeometry_.clipPath(*map(), closedPath, borderLeftBoundWrapped); - if (clippedPaths.size()) { - borderLeftBoundWrapped = p.geoToWrappedMapProjection(geometryOrigin); - borderGeometry_.pathToScreen(*map(), clippedPaths, borderLeftBoundWrapped); - borderGeometry_.updateScreenPoints(*map(), border_.width()); - - geoms << &borderGeometry_; - } else { - borderGeometry_.clear(); - } - } - - QRectF combined = QGeoMapItemGeometry::translateToCommonOrigin(geoms); - setWidth(combined.width() + 2 * border_.width()); - setHeight(combined.height() + 2 * border_.width()); - - setPositionOnMap(geometry_.origin(), -1 * geometry_.sourceBoundingBox().topLeft() - + QPointF(border_.width(), border_.width())); + m_d->updatePolish(); } -void QDeclarativePolygonMapItem::markSourceDirtyAndUpdate() +void QDeclarativePolygonMapItem::setMaterialDirty() { - geometry_.markSourceDirty(); - borderGeometry_.markSourceDirty(); - polishAndUpdate(); + m_dirtyMaterial = true; + update(); } -/*! - \internal -*/ -void QDeclarativePolygonMapItem::afterViewportChanged(const QGeoMapViewportChangeEvent &event) +void QDeclarativePolygonMapItem::markSourceDirtyAndUpdate() { - if (event.mapSize.width() <= 0 || event.mapSize.height() <= 0) - return; - - geometry_.setPreserveGeometry(true, geometry_.geoLeftBound()); - borderGeometry_.setPreserveGeometry(true, borderGeometry_.geoLeftBound()); - geometry_.markSourceDirty(); - borderGeometry_.markSourceDirty(); - polishAndUpdate(); + m_d->markSourceDirtyAndUpdate(); } -/*! - \internal -*/ -void QDeclarativePolygonMapItem::regenerateCache() +void QDeclarativePolygonMapItem::onLinePropertiesChanged() { - if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) - return; - const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map()->geoProjection()); - geopathProjected_.clear(); - geopathProjected_.reserve(geopath_.path().size()); - for (const QGeoCoordinate &c : geopath_.path()) - geopathProjected_ << p.geoToMapProjection(c); + m_d->onLinePropertiesChanged(); } /*! \internal */ -void QDeclarativePolygonMapItem::updateCache() +void QDeclarativePolygonMapItem::afterViewportChanged(const QGeoMapViewportChangeEvent &event) { - if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) + if (event.mapSize.isEmpty()) return; - const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map()->geoProjection()); - geopathProjected_ << p.geoToMapProjection(geopath_.path().last()); + + m_d->afterViewportChanged(); } /*! @@ -612,24 +851,21 @@ void QDeclarativePolygonMapItem::updateCache() */ bool QDeclarativePolygonMapItem::contains(const QPointF &point) const { - return (geometry_.contains(point) || borderGeometry_.contains(point)); + return m_d->contains(point); } const QGeoShape &QDeclarativePolygonMapItem::geoShape() const { - return geopath_; + return m_geopoly; } void QDeclarativePolygonMapItem::setGeoShape(const QGeoShape &shape) { - if (shape == geopath_) + if (shape == m_geopoly) return; - geopath_ = QGeoPolygonEager(shape); - regenerateCache(); - geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); - borderGeometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); - markSourceDirtyAndUpdate(); + m_geopoly = QGeoPolygonEager(shape); + m_d->onGeoGeometryChanged(); emit pathChanged(); } @@ -638,7 +874,7 @@ void QDeclarativePolygonMapItem::setGeoShape(const QGeoShape &shape) */ void QDeclarativePolygonMapItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) { - if (!map() || !geopath_.isValid() || updatingGeometry_ || newGeometry.topLeft() == oldGeometry.topLeft()) { + if (newGeometry.topLeft() == oldGeometry.topLeft() || !map() || !m_geopoly.isValid() || m_updatingGeometry) { QDeclarativeGeoMapItemBase::geometryChanged(newGeometry, oldGeometry); return; } @@ -652,11 +888,8 @@ void QDeclarativePolygonMapItem::geometryChanged(const QRectF &newGeometry, cons if (offsetLati == 0.0 && offsetLongi == 0.0) return; - geopath_.translate(offsetLati, offsetLongi); - regenerateCache(); - geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); - borderGeometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); - markSourceDirtyAndUpdate(); + m_geopoly.translate(offsetLati, offsetLongi); + m_d->onGeoGeometryChanged(); emit pathChanged(); // Not calling QDeclarativeGeoMapItemBase::geometryChanged() as it will be called from a nested @@ -665,6 +898,25 @@ void QDeclarativePolygonMapItem::geometryChanged(const QRectF &newGeometry, cons ////////////////////////////////////////////////////////////////////// +QSGMaterialShader *MapPolygonMaterial::createShader() const +{ + 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) @@ -701,6 +953,9 @@ void MapPolygonNode::update(const QColor &fillColor, const QColor &borderColor, setSubtreeBlocked(false); + // TODO: do this only if the geometry has changed!! + // No need to do this every frame. + // Then benchmark the difference! QSGGeometry *fill = QSGGeometryNode::geometry(); fillShape->allocateAndFill(fill); markDirty(DirtyGeometry); @@ -712,4 +967,90 @@ 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) +{ + +} + +void MapPolygonShader::updateState(const QSGMaterialShader::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(); + + QVector3D vecCenter, vecCenter_lowpart; + for (int i = 0; i < 3; i++) + QLocationUtils::split_double(center.get(i), &vecCenter[i], &vecCenter_lowpart[i]); + + 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); + program()->setUniformValue(m_color_id, v); + } + + if (state.isMatrixDirty()) + { + program()->setUniformValue(m_matrix_id, state.projectionMatrix()); + } + + program()->setUniformValue(m_mapProjection_id, geoProjection); + + program()->setUniformValue(m_center_id, vecCenter); + program()->setUniformValue(m_center_lowpart_id, vecCenter_lowpart); + program()->setUniformValue(m_wrapOffset_id, float(newMaterial->wrapOffset())); +} + QT_END_NAMESPACE |