diff options
Diffstat (limited to 'src/location/quickmapitems')
18 files changed, 353 insertions, 849 deletions
diff --git a/src/location/quickmapitems/qdeclarativecirclemapitem.cpp b/src/location/quickmapitems/qdeclarativecirclemapitem.cpp index 0e45d439..34aa4fb5 100644 --- a/src/location/quickmapitems/qdeclarativecirclemapitem.cpp +++ b/src/location/quickmapitems/qdeclarativecirclemapitem.cpp @@ -11,7 +11,6 @@ #include <QtGui/private/qtriangulator_p.h> #include <QtLocation/private/qgeomap_p.h> #include <QtPositioning/private/qlocationutils_p.h> -#include <QtPositioning/private/qclipperutils_p.h> #include <qmath.h> #include <algorithm> @@ -108,136 +107,8 @@ QGeoMapCircleGeometry::QGeoMapCircleGeometry() { } -/*! - \internal -*/ -void QGeoMapCircleGeometry::updateSourceAndScreenPointsInvert(const QList<QDoubleVector2D> &circlePath, const QGeoMap &map) -{ - const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map.geoProjection()); - // Not checking for !screenDirty anymore, as everything is now recalculated. - clear(); - srcPath_ = QPainterPath(); - if (map.viewportWidth() == 0 || map.viewportHeight() == 0 || circlePath.size() < 3) // a circle requires at least 3 points; - return; - - /* - * No special case for no tilting as these items are very rare, and usually at most one per map. - * - * Approach: - * 1) subtract the circle from a rectangle filling the whole map, *in wrapped mercator space* - * 2) clip the resulting geometries against the visible region, *in wrapped mercator space* - * 3) create a QPainterPath with each of the resulting polygons projected to screen - * 4) use qTriangulate() to triangulate the painter path - */ - - // 1) - const double topLati = QLocationUtils::mercatorMaxLatitude(); - const double bottomLati = -(QLocationUtils::mercatorMaxLatitude()); - const double leftLongi = QLocationUtils::mapLeftLongitude(map.cameraData().center().longitude()); - const double rightLongi = QLocationUtils::mapRightLongitude(map.cameraData().center().longitude()); - - srcOrigin_ = QGeoCoordinate(topLati,leftLongi); - const QDoubleVector2D tl = p.geoToWrappedMapProjection(QGeoCoordinate(topLati,leftLongi)); - const QDoubleVector2D tr = p.geoToWrappedMapProjection(QGeoCoordinate(topLati,rightLongi)); - const QDoubleVector2D br = p.geoToWrappedMapProjection(QGeoCoordinate(bottomLati,rightLongi)); - const QDoubleVector2D bl = p.geoToWrappedMapProjection(QGeoCoordinate(bottomLati,leftLongi)); - - QList<QDoubleVector2D> fill; - fill << tl << tr << br << bl; - - QList<QDoubleVector2D> hole; - for (const QDoubleVector2D &c: circlePath) - hole << p.wrapMapProjection(c); - - QClipperUtils clipper; - clipper.addSubjectPath(fill, true); - clipper.addClipPolygon(hole); - auto difference = clipper.execute(QClipperUtils::Difference, QClipperUtils::pftEvenOdd, - QClipperUtils::pftEvenOdd); - - // 2) - QDoubleVector2D lb = p.geoToWrappedMapProjection(srcOrigin_); - QList<QList<QDoubleVector2D> > clippedPaths; - const QList<QDoubleVector2D> &visibleRegion = p.visibleGeometry(); - if (visibleRegion.size()) { - clipper.clearClipper(); - for (const auto &p: difference) - clipper.addSubjectPath(p, true); - clipper.addClipPolygon(visibleRegion); - clippedPaths = clipper.execute(QClipperUtils::Intersection, QClipperUtils::pftEvenOdd, - QClipperUtils::pftEvenOdd); - - // 2.1) update srcOrigin_ with the point with minimum X/Y - lb = QDoubleVector2D(qInf(), qInf()); - for (const QList<QDoubleVector2D> &path: clippedPaths) { - for (const QDoubleVector2D &p: path) { - if (p.x() < lb.x() || (p.x() == lb.x() && p.y() < lb.y())) { - lb = p; - } - } - } - if (qIsInf(lb.x())) - return; - - // Prevent the conversion to and from clipper from introducing negative offsets which - // in turn will make the geometry wrap around. - lb.setX(qMax(tl.x(), lb.x())); - srcOrigin_ = p.mapProjectionToGeo(p.unwrapMapProjection(lb)); - } else { - clippedPaths = difference; - } - - //3) - const QDoubleVector2D origin = p.wrappedMapProjectionToItemPosition(lb); - - for (const QList<QDoubleVector2D> &path: clippedPaths) { - QDoubleVector2D lastAddedPoint; - for (qsizetype i = 0; i < path.size(); ++i) { - QDoubleVector2D point = p.wrappedMapProjectionToItemPosition(path.at(i)); - //point = point - origin; // Do this using ppi.translate() - - const QDoubleVector2D pt = point - origin; - if (qMax(pt.x(), pt.y()) > maxCoord_) - maxCoord_ = qMax(pt.x(), pt.y()); - - if (i == 0) { - srcPath_.moveTo(point.toPointF()); - lastAddedPoint = point; - } else if ((point - lastAddedPoint).manhattanLength() > 3 || i == path.size() - 1) { - srcPath_.lineTo(point.toPointF()); - lastAddedPoint = point; - } - } - srcPath_.closeSubpath(); - } - - QPainterPath ppi = srcPath_; - ppi.translate(-1 * origin.toPointF()); - - QTriangleSet ts = qTriangulate(ppi); - qreal *vx = ts.vertices.data(); - - screenIndices_.reserve(ts.indices.size()); - screenVertices_.reserve(ts.vertices.size()); - - if (ts.indices.type() == QVertexIndexVector::UnsignedInt) { - const quint32 *ix = reinterpret_cast<const quint32 *>(ts.indices.data()); - for (qsizetype i = 0; i < (ts.indices.size()/3*3); ++i) - screenIndices_ << ix[i]; - } else { - const quint16 *ix = reinterpret_cast<const quint16 *>(ts.indices.data()); - for (qsizetype i = 0; i < (ts.indices.size()/3*3); ++i) - screenIndices_ << ix[i]; - } - for (qsizetype i = 0; i < (ts.vertices.size()/2*2); i += 2) - screenVertices_ << QPointF(vx[i], vx[i + 1]); - - screenBounds_ = ppi.boundingRect(); - sourceBounds_ = srcPath_.boundingRect(); -} - QDeclarativeCircleMapItem::QDeclarativeCircleMapItem(QQuickItem *parent) -: QDeclarativeGeoMapItemBase(parent), m_border(this), m_color(Qt::transparent), m_dirtyMaterial(true), +: QDeclarativeGeoMapItemBase(parent), m_border(this), m_color(Qt::transparent), m_updatingGeometry(false) , m_d(new QDeclarativeCircleMapItemPrivateCPU(*this)) { @@ -248,12 +119,6 @@ QDeclarativeCircleMapItem::QDeclarativeCircleMapItem(QQuickItem *parent) this, &QDeclarativeCircleMapItem::onLinePropertiesChanged); QObject::connect(&m_border, &QDeclarativeMapLineProperties::widthChanged, this, &QDeclarativeCircleMapItem::onLinePropertiesChanged); - - // assume that circles are not self-intersecting - // to speed up processing - // FIXME: unfortunately they self-intersect at the poles due to current drawing method - // so the line is commented out until fixed - //geometry_.setAssumeSimple(true); } QDeclarativeCircleMapItem::~QDeclarativeCircleMapItem() @@ -326,9 +191,9 @@ void QDeclarativeCircleMapItem::setColor(const QColor &color) { if (m_color == color) return; + m_color = color; - m_dirtyMaterial = true; - update(); + polishAndUpdate(); // in case color was transparent and now is not or vice versa emit colorChanged(m_color); } @@ -404,7 +269,6 @@ void QDeclarativeCircleMapItem::afterViewportChanged(const QGeoMapViewportChange bool QDeclarativeCircleMapItem::contains(const QPointF &point) const { return m_d->contains(point); - // } const QGeoShape &QDeclarativeCircleMapItem::geoShape() const @@ -475,94 +339,59 @@ QDeclarativeCircleMapItemPrivateCPU::~QDeclarativeCircleMapItemPrivateCPU() delete m_shape; } -bool QDeclarativeCircleMapItemPrivate::preserveCircleGeometry (QList<QDoubleVector2D> &path, - const QGeoCoordinate ¢er, qreal distance, const QGeoProjectionWebMercator &p) -{ - // if circle crosses north/south pole, then don't preserve circular shape, - if ( crossEarthPole(center, distance)) { - updateCirclePathForRendering(path, center, distance, p); - return false; - } - return true; -} - /* * A workaround for circle path to be drawn correctly using a polygon geometry * This method generates a polygon like - * _____________ - * | | - * \ / - * | | - * / \ - * | | - * ------------- - * - * or a polygon like - * * ______________ * | ____ | * \__/ \__/ */ -void QDeclarativeCircleMapItemPrivate::updateCirclePathForRendering(QList<QDoubleVector2D> &path, +void QDeclarativeCircleMapItemPrivate::includeOnePoleInPath(QList<QDoubleVector2D> &path, const QGeoCoordinate ¢er, qreal distance, const QGeoProjectionWebMercator &p) { const qreal poleLat = 90; const qreal distanceToNorthPole = center.distanceTo(QGeoCoordinate(poleLat, 0)); const qreal distanceToSouthPole = center.distanceTo(QGeoCoordinate(-poleLat, 0)); - bool crossNorthPole = distanceToNorthPole < distance; - bool crossSouthPole = distanceToSouthPole < distance; - - QList<qsizetype> wrapPathIndex; - QDoubleVector2D prev = p.wrapMapProjection(path.at(0)); - - for (qsizetype i = 1; i <= path.count(); ++i) { - const auto index = i % path.count(); - const QDoubleVector2D point = p.wrapMapProjection(path.at(index)); - double diff = qAbs(point.x() - prev.x()); - if (diff > 0.5) { - continue; - } - } + const bool crossNorthPole = distanceToNorthPole < distance; + const bool crossSouthPole = distanceToSouthPole < distance; - // find the points in path where wrapping occurs - for (qsizetype i = 1; i <= path.count(); ++i) { - const auto index = i % path.count(); - const QDoubleVector2D point = p.wrapMapProjection(path.at(index)); - if ((qAbs(point.x() - prev.x())) >= 0.5) { - wrapPathIndex << index; - if (wrapPathIndex.size() == 2 || !(crossNorthPole && crossSouthPole)) - break; - } - prev = point; - } - // insert two additional coords at top/bottom map corners of the map for shape - // to be drawn correctly - if (wrapPathIndex.size() > 0) { - qreal newPoleLat = 0; // 90 latitude - QDoubleVector2D wrapCoord = path.at(wrapPathIndex[0]); - if (wrapPathIndex.size() == 2) { - QDoubleVector2D wrapCoord2 = path.at(wrapPathIndex[1]); - if (wrapCoord2.y() < wrapCoord.y()) - newPoleLat = 1; // -90 latitude - } else if (center.latitude() < 0) { - newPoleLat = 1; // -90 latitude - } - for (qsizetype i = 0; i < wrapPathIndex.size(); ++i) { - const qsizetype index = wrapPathIndex[i] == 0 ? 0 : wrapPathIndex[i] + i*2; - const qsizetype prevIndex = (index - 1) < 0 ? (path.count() - 1) : index - 1; - QDoubleVector2D coord0 = path.at(prevIndex); - QDoubleVector2D coord1 = path.at(index); - coord0.setY(newPoleLat); - coord1.setY(newPoleLat); - path.insert(index ,coord1); - path.insert(index, coord0); - newPoleLat = 1.0 - newPoleLat; - } + if (!crossNorthPole && !crossSouthPole) + return; + + if (crossNorthPole && crossSouthPole) + return; + + const QRectF cameraRect = QDeclarativeGeoMapItemUtils::boundingRectangleFromList(p.visibleGeometry()); + const qreal xAtBorder = cameraRect.left(); + + // The strategy is to order the points from left to right as they appear on the screen. + // Then add the 3 missing sides that form the box for painting at the front and at the end of the list. + // We ensure that the box aligns with the cameraRect in order to avoid rendering it twice (wrap around). + // Notably, this leads to outlines at the right side of the map. + // Set xAtBorder to 0.0 to avoid this, however, for an increased rendering cost. + for (auto &c : path) { + c.setX(c.x()); + while (c.x() - xAtBorder > 1.0) + c.setX(c.x() - 1.0); + while (c.x() - xAtBorder < 0.0) + c.setX(c.x() + 1.0); } + + std::sort(path.begin(), path.end(), + [](const QDoubleVector2D &a, const QDoubleVector2D &b) -> bool + {return a.x() < b.x();}); + + const qreal newPoleLat = crossNorthPole ? 0.0 : 1.0; + const QDoubleVector2D P1 = path.first() + QDoubleVector2D(1.0, 0.0); + const QDoubleVector2D P2 = path.last() - QDoubleVector2D(1.0, 0.0); + path.push_front(P2); + path.push_front(QDoubleVector2D(P2.x(), newPoleLat)); + path.append(P1); + path.append(QDoubleVector2D(P1.x(), newPoleLat)); } -bool QDeclarativeCircleMapItemPrivate::crossEarthPole(const QGeoCoordinate ¢er, qreal distance) +int QDeclarativeCircleMapItemPrivate::crossEarthPole(const QGeoCoordinate ¢er, qreal distance) { qreal poleLat = 90; QGeoCoordinate northPole = QGeoCoordinate(poleLat, center.longitude()); @@ -570,16 +399,15 @@ bool QDeclarativeCircleMapItemPrivate::crossEarthPole(const QGeoCoordinate ¢ // approximate using great circle distance qreal distanceToNorthPole = center.distanceTo(northPole); qreal distanceToSouthPole = center.distanceTo(southPole); - if (distanceToNorthPole < distance || distanceToSouthPole < distance) - return true; - return false; + return (distanceToNorthPole < distance? 1 : 0) + + (distanceToSouthPole < distance? 1 : 0); } -void QDeclarativeCircleMapItemPrivate::calculatePeripheralPoints(QList<QGeoCoordinate> &path, +void QDeclarativeCircleMapItemPrivate::calculatePeripheralPoints(QList<QDoubleVector2D> &path, const QGeoCoordinate ¢er, qreal distance, - int steps, - QGeoCoordinate &leftBound) + const QGeoProjectionWebMercator &p, + int steps) { // Calculate points based on great-circle distance // Calculation is the same as GeoCoordinate's atDistanceAndAzimuth function @@ -588,7 +416,6 @@ void QDeclarativeCircleMapItemPrivate::calculatePeripheralPoints(QList<QGeoCoord // pre-calculations steps = qMax(steps, 3); qreal centerLon = center.longitude(); - qreal minLon = centerLon; qreal latRad = QLocationUtils::radians(center.latitude()); qreal lonRad = QLocationUtils::radians(centerLon); qreal cosLatRad = std::cos(latRad); @@ -598,7 +425,6 @@ void QDeclarativeCircleMapItemPrivate::calculatePeripheralPoints(QList<QGeoCoord qreal sinRatio = std::sin(ratio); qreal sinLatRad_x_cosRatio = sinLatRad * cosRatio; qreal cosLatRad_x_sinRatio = cosLatRad * sinRatio; - int idx = 0; for (int i = 0; i < steps; ++i) { const qreal azimuthRad = 2 * M_PI * i / steps; const qreal resultLatRad = std::asin(sinLatRad_x_cosRatio @@ -606,20 +432,20 @@ void QDeclarativeCircleMapItemPrivate::calculatePeripheralPoints(QList<QGeoCoord const qreal resultLonRad = lonRad + std::atan2(std::sin(azimuthRad) * cosLatRad_x_sinRatio, cosRatio - sinLatRad * std::sin(resultLatRad)); const qreal lat2 = QLocationUtils::degrees(resultLatRad); - qreal lon2 = QLocationUtils::wrapLong(QLocationUtils::degrees(resultLonRad)); - - path << QGeoCoordinate(lat2, lon2, center.altitude()); - // Consider only points in the left half of the circle for the left bound. - if (azimuthRad > M_PI) { - if (lon2 > centerLon) // if point and center are on different hemispheres - lon2 -= 360; - if (lon2 < minLon) { - minLon = lon2; - idx = i; - } + qreal lon2 = QLocationUtils::degrees(resultLonRad); + + //Workaround as QGeoCoordinate does not take Longitudes outside [-180,180] + qreal offset = 0.0; + while (lon2 > 180.0) { + offset += 1.0; + lon2 -= 360.0; } + while (lon2 < -180.0) { + offset -= 1.0; + lon2 += 360.0; + } + path << p.geoToMapProjection(QGeoCoordinate(lat2, lon2, center.altitude())) + QDoubleVector2D(offset, 0.0); } - leftBound = path.at(idx); } ////////////////////////////////////////////////////////////////////// @@ -640,22 +466,41 @@ void QDeclarativeCircleMapItemPrivateCPU::updatePolish() QList<QDoubleVector2D> circlePath = m_circlePath; - int pathCount = circlePath.size(); - bool preserve = preserveCircleGeometry(circlePath, m_circle.m_circle.center(), - m_circle.m_circle.radius(), p); - // using leftBound_ instead of the analytically calculated - // circle_.boundingGeoRectangle().topLeft()); - // to fix QTBUG-62154 - m_geometry.setPreserveGeometry(true, m_leftBound); // to set the geoLeftBound_ - m_geometry.setPreserveGeometry(preserve, m_leftBound); - - bool invertedCircle = false; - if (crossEarthPole(m_circle.m_circle.center(), m_circle.m_circle.radius()) && circlePath.size() == pathCount) { - // invert fill area for really huge circles - m_geometry.updateSourceAndScreenPointsInvert(circlePath, *m_circle.map()); - invertedCircle = true; + const QGeoCoordinate ¢er = m_circle.m_circle.center(); + const qreal &radius = m_circle.m_circle.radius(); + + // if circle crosses north/south pole, then don't preserve circular shape, + int crossingPoles = crossEarthPole(center, radius); + if (crossingPoles == 1) { // If the circle crosses both poles, we will remove it from a rectangle + includeOnePoleInPath(circlePath, center, radius, p); + m_geometry.updateSourcePoints(*m_circle.map(), QList<QList<QDoubleVector2D>>{circlePath}, QGeoMapPolygonGeometry::DrawOnce); + } + else if (crossingPoles == 2) { // If the circle crosses both poles, we will remove it from a rectangle + // The circle covers both poles. This appears on the map as a total fill with a hole on the opposite side of the planet + // This can be represented by a rectangle that spans the entire planet with a hole defined by the calculated points. + // The points on one side have to be wraped around the globe + const qreal centerX = p.geoToMapProjection(center).x(); + for (int i = 0; i < circlePath.count(); i++) { + if (circlePath.at(i).x() > centerX) + circlePath[i].setX(circlePath.at(i).x() - 1.0); + } + QRectF cameraRect = QDeclarativeGeoMapItemUtils::boundingRectangleFromList(p.visibleGeometry()); + const QRectF circleRect = QDeclarativeGeoMapItemUtils::boundingRectangleFromList(circlePath); + QGeoMapPolygonGeometry::MapBorderBehaviour wrappingMode = QGeoMapPolygonGeometry::DrawOnce; + QList<QDoubleVector2D> surroundingRect; + if (cameraRect.contains(circleRect)){ + cameraRect = cameraRect.adjusted(-0.1, 0.0, 0.2, 0.0); + surroundingRect = {{cameraRect.left(), cameraRect.top()}, {cameraRect.right(), cameraRect.top()}, + {cameraRect.right(), cameraRect.bottom()}, {cameraRect.left() , cameraRect.bottom()}}; + } else { + const qreal anchorRect = centerX; + surroundingRect = {{anchorRect, 0.0}, {anchorRect + 1.0, 0.0}, + {anchorRect + 1.0, 1.0}, {anchorRect, 1.0}}; + wrappingMode = QGeoMapPolygonGeometry::WrapAround; + } + m_geometry.updateSourcePoints(*m_circle.map(), {surroundingRect, circlePath}, wrappingMode); } else { - m_geometry.updateSourcePoints(*m_circle.map(), circlePath); + m_geometry.updateSourcePoints(*m_circle.map(), QList<QList<QDoubleVector2D>>{circlePath}); } m_circle.setShapeTriangulationScale(m_shape, m_geometry.maxCoord()); @@ -672,9 +517,7 @@ void QDeclarativeCircleMapItemPrivateCPU::updatePolish() path.closeSubpath(); m_painterPath->setPath(path); - m_circle.setSize(invertedCircle || !preserve - ? bb.size() - : bb.size() + QSize(2 * borderWidth, 2 * borderWidth)); + m_circle.setSize(bb.size()); m_shape->setSize(m_circle.size()); m_shape->setOpacity(m_circle.zoomLevelOpacity()); m_shape->setVisible(true); @@ -687,10 +530,8 @@ QSGNode *QDeclarativeCircleMapItemPrivateCPU::updateMapItemPaintNode(QSGNode *ol { Q_UNUSED(data); delete oldNode; - if (m_geometry.isScreenDirty() || m_circle.m_dirtyMaterial) { - m_geometry.setPreserveGeometry(false); + if (m_geometry.isScreenDirty()) { m_geometry.markClean(); - m_circle.m_dirtyMaterial = false; } return nullptr; } diff --git a/src/location/quickmapitems/qdeclarativecirclemapitem_p.h b/src/location/quickmapitems/qdeclarativecirclemapitem_p.h index 211cd914..305fd719 100644 --- a/src/location/quickmapitems/qdeclarativecirclemapitem_p.h +++ b/src/location/quickmapitems/qdeclarativecirclemapitem_p.h @@ -79,7 +79,6 @@ private: QGeoCircle m_circle; QDeclarativeMapLineProperties m_border; QColor m_color; - bool m_dirtyMaterial; bool m_updatingGeometry; std::unique_ptr<QDeclarativeCircleMapItemPrivate> m_d; diff --git a/src/location/quickmapitems/qdeclarativecirclemapitem_p_p.h b/src/location/quickmapitems/qdeclarativecirclemapitem_p_p.h index 03b4f1b7..117dcb63 100644 --- a/src/location/quickmapitems/qdeclarativecirclemapitem_p_p.h +++ b/src/location/quickmapitems/qdeclarativecirclemapitem_p_p.h @@ -60,27 +60,21 @@ public: if (!m_circle.map() || m_circle.map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) return; - const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_circle.map()->geoProjection()); - QList<QGeoCoordinate> path; - calculatePeripheralPoints(path, m_circle.center(), m_circle.radius(), CircleSamples, m_leftBound); m_circlePath.clear(); - for (const QGeoCoordinate &c : path) - m_circlePath << p.geoToMapProjection(c); + const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_circle.map()->geoProjection()); + calculatePeripheralPoints(m_circlePath, m_circle.center(), m_circle.radius(), p, CircleSamples); } - static bool crossEarthPole(const QGeoCoordinate ¢er, qreal distance); + static int crossEarthPole(const QGeoCoordinate ¢er, qreal distance); - static bool preserveCircleGeometry(QList<QDoubleVector2D> &path, const QGeoCoordinate ¢er, - qreal distance, const QGeoProjectionWebMercator &p); - static void updateCirclePathForRendering(QList<QDoubleVector2D> &path, const QGeoCoordinate ¢er, + static void includeOnePoleInPath(QList<QDoubleVector2D> &path, const QGeoCoordinate ¢er, qreal distance, const QGeoProjectionWebMercator &p); - static void calculatePeripheralPoints(QList<QGeoCoordinate> &path, const QGeoCoordinate ¢er, - qreal distance, int steps, QGeoCoordinate &leftBound); + static void calculatePeripheralPoints(QList<QDoubleVector2D> &path, const QGeoCoordinate ¢er, + qreal distance, const QGeoProjectionWebMercator &p, int steps); QDeclarativeCircleMapItem &m_circle; QList<QDoubleVector2D> m_circlePath; - QGeoCoordinate m_leftBound; }; class Q_LOCATION_PRIVATE_EXPORT QDeclarativeCircleMapItemPrivateCPU: public QDeclarativeCircleMapItemPrivate @@ -96,7 +90,6 @@ public: } void markSourceDirtyAndUpdate() override { - // preserveGeometry is cleared in updateMapItemPaintNode m_geometry.markSourceDirty(); m_circle.polishAndUpdate(); } diff --git a/src/location/quickmapitems/qdeclarativegeomap.cpp b/src/location/quickmapitems/qdeclarativegeomap.cpp index 0b4fcde9..c232010b 100644 --- a/src/location/quickmapitems/qdeclarativegeomap.cpp +++ b/src/location/quickmapitems/qdeclarativegeomap.cpp @@ -1868,23 +1868,12 @@ void QDeclarativeGeoMap::updateItemToWindowTransform() return; // Update itemToWindowTransform into QGeoProjection - const QTransform item2WindowOld = m_map->geoProjection().itemToWindowTransform(); QTransform item2Window = QQuickItemPrivate::get(this)->itemToWindowTransform(); if (!property("layer").isNull() && property("layer").value<QObject *>()->property("enabled").toBool()) item2Window.reset(); // When layer is enabled, the item is rendered offscreen with no transformation, then the layer is applied m_map->setItemToWindowTransform(item2Window); - // This method is called at every redraw, including those redraws not generated by - // sgNodeChanged. - // In these cases, *if* the item2windowTransform has changed (e.g., if transformation of - // the item or one of its ancestors changed), a forced update of the map items using accelerated - // GL implementation has to be performed in order to have them pulling the updated itemToWindowTransform. - if (!m_sgNodeHasChanged && item2WindowOld != item2Window) { - for (auto i: std::as_const(m_mapItems)) - i->setMaterialDirty(); - } - m_sgNodeHasChanged = false; } @@ -2112,10 +2101,11 @@ void QDeclarativeGeoMap::fitViewportToMapItemsRefine(const QList<QPointer<QDecla bottomRightX = topLeftX + brect.width(); bottomRightY = topLeftY + brect.height(); } else { - topLeftX = item->position().x(); - topLeftY = item->position().y(); - bottomRightX = topLeftX + item->width(); - bottomRightY = topLeftY + item->height(); + QGeoRectangle brect = item->geoShape().boundingGeoRectangle(); + topLeftX = fromCoordinate(brect.topLeft(), false).x(); + topLeftY = fromCoordinate(brect.topLeft(), false).y(); + bottomRightX = fromCoordinate(brect.bottomRight(), false).x(); + bottomRightY = fromCoordinate(brect.bottomRight(), false).y(); } minX = qMin(minX, topLeftX); diff --git a/src/location/quickmapitems/qdeclarativegeomapitembase.cpp b/src/location/quickmapitems/qdeclarativegeomapitembase.cpp index c5b1cc8c..233ea82d 100644 --- a/src/location/quickmapitems/qdeclarativegeomapitembase.cpp +++ b/src/location/quickmapitems/qdeclarativegeomapitembase.cpp @@ -77,7 +77,7 @@ void QDeclarativeGeoMapItemBase::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *m // For performance reasons we're not connecting map_'s and quickMap_'s signals to this. // Rather, the handling of cameraDataChanged, visibleAreaChanged, heightChanged and widthChanged is done explicitly in QDeclarativeGeoMap by directly calling methods on the items. // See QTBUG-76950 - lastSize_ = QSizeF(quickMap_->width(), quickMap_->height()); + lastMapSize_ = QSizeF(quickMap_->width(), quickMap_->height()); lastCameraData_ = map_->cameraData(); } } @@ -91,7 +91,7 @@ void QDeclarativeGeoMapItemBase::baseCameraDataChanged(const QGeoCameraData &cam evt.cameraData = cameraData; evt.mapSize = QSizeF(quickMap_->width(), quickMap_->height()); - if (evt.mapSize != lastSize_) + if (evt.mapSize != lastMapSize_) evt.mapSizeChanged = true; if (cameraData.bearing() != lastCameraData_.bearing()) @@ -105,7 +105,7 @@ void QDeclarativeGeoMapItemBase::baseCameraDataChanged(const QGeoCameraData &cam if (cameraData.zoomLevel() != lastCameraData_.zoomLevel()) evt.zoomLevelChanged = true; - lastSize_ = evt.mapSize; + lastMapSize_ = evt.mapSize; lastCameraData_ = cameraData; afterViewportChanged(evt); @@ -286,8 +286,6 @@ bool QDeclarativeGeoMapItemBase::isPolishScheduled() const return QQuickItemPrivate::get(this)->polishScheduled; } -void QDeclarativeGeoMapItemBase::setMaterialDirty() {} - void QDeclarativeGeoMapItemBase::polishAndUpdate() { polish(); diff --git a/src/location/quickmapitems/qdeclarativegeomapitembase_p.h b/src/location/quickmapitems/qdeclarativegeomapitembase_p.h index 6888596a..38eef2db 100644 --- a/src/location/quickmapitems/qdeclarativegeomapitembase_p.h +++ b/src/location/quickmapitems/qdeclarativegeomapitembase_p.h @@ -111,7 +111,6 @@ protected Q_SLOTS: protected: float zoomLevelOpacity() const; bool isPolishScheduled() const; - virtual void setMaterialDirty(); QGeoMap::ItemType m_itemType = QGeoMap::NoItem; @@ -123,7 +122,7 @@ private: QPointer<QGeoMap> map_; QDeclarativeGeoMap *quickMap_ = nullptr; - QSizeF lastSize_; + QSizeF lastMapSize_; QGeoCameraData lastCameraData_; QDeclarativeGeoMapItemGroup *parentGroup_ = nullptr; diff --git a/src/location/quickmapitems/qdeclarativegeomapitemutils.cpp b/src/location/quickmapitems/qdeclarativegeomapitemutils.cpp index 6a79b33b..a1501dd6 100644 --- a/src/location/quickmapitems/qdeclarativegeomapitemutils.cpp +++ b/src/location/quickmapitems/qdeclarativegeomapitemutils.cpp @@ -16,6 +16,31 @@ QT_BEGIN_NAMESPACE namespace QDeclarativeGeoMapItemUtils { +double distanceSqrPointLine(double p0_x + , double p0_y + , double p1_x + , double p1_y + , double p2_x + , double p2_y) +{ + const double t_x = p2_x - p1_x; + const double t_y = p2_y - p1_y; + const double p_x = p0_x - p1_x; + const double p_y = p0_y - p1_y; + const double tsqr = t_x * t_x + t_y * t_y; + + if (tsqr == 0) + return qInf(); + + double alpha = (p_x * t_x + p_y * t_y) / tsqr; + alpha = qBound<double>(0, alpha, 1); + + const double dx = p_x - t_x * alpha; + const double dy = p_y - t_y * alpha; + + return dx * dx + dy * dy; +} + void wrapPath(const QList<QGeoCoordinate> &perimeter, const QGeoCoordinate &geoLeftBound, const QGeoProjectionWebMercator &p, @@ -150,6 +175,22 @@ void projectBbox(const QList<QDoubleVector2D> &clippedBbox, projectedBbox.closeSubpath(); } + +QRectF boundingRectangleFromList(const QList<QDoubleVector2D> &list) +{ + double xMin = qInf(); + double xMax = -qInf(); + double yMin = qInf(); + double yMax = -qInf(); + for (const auto &coord : list) { + xMin = qMin(xMin, coord.x()); + xMax = qMax(xMax, coord.x()); + yMin = qMin(yMin, coord.y()); + yMax = qMax(yMax, coord.y()); + } + return QRectF(xMin, yMin, xMax - xMin, yMax - yMin); +} + } // namespace QDeclarativeGeoMapItemUtils QT_END_NAMESPACE diff --git a/src/location/quickmapitems/qdeclarativegeomapitemutils_p.h b/src/location/quickmapitems/qdeclarativegeomapitemutils_p.h index 2f117520..50ece528 100644 --- a/src/location/quickmapitems/qdeclarativegeomapitemutils_p.h +++ b/src/location/quickmapitems/qdeclarativegeomapitemutils_p.h @@ -52,6 +52,14 @@ namespace QDeclarativeGeoMapItemUtils } }; + + double distanceSqrPointLine(double p0_x + , double p0_y + , double p1_x + , double p1_y + , double p2_x + , double p2_y); + void wrapPath(const QList<QGeoCoordinate> &perimeter , const QGeoCoordinate &geoLeftBound , const QGeoProjectionWebMercator &p @@ -80,6 +88,8 @@ namespace QDeclarativeGeoMapItemUtils , const QGeoProjectionWebMercator &p , QPainterPath &projectedBbox); + QRectF boundingRectangleFromList(const QList<QDoubleVector2D> &list); + }; QT_END_NAMESPACE diff --git a/src/location/quickmapitems/qdeclarativepolygonmapitem.cpp b/src/location/quickmapitems/qdeclarativepolygonmapitem.cpp index 1f34d0c1..1eee3eb0 100644 --- a/src/location/quickmapitems/qdeclarativepolygonmapitem.cpp +++ b/src/location/quickmapitems/qdeclarativepolygonmapitem.cpp @@ -115,98 +115,105 @@ QGeoMapPolygonGeometry::QGeoMapPolygonGeometry() = default; \internal */ void QGeoMapPolygonGeometry::updateSourcePoints(const QGeoMap &map, - const QList<QDoubleVector2D> &path) + const QList<QList <QDoubleVector2D>> &paths, + MapBorderBehaviour wrapping) { + // A polygon consists of mutliple paths. This is usually a perimeter and multiple holes + // We move all paths into a single QPainterPath. The filling rule EvenOdd will then ensure that the paths are shown correctly if (!sourceDirty_) return; const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map.geoProjection()); srcPath_ = QPainterPath(); + srcOrigin_ = p.mapProjectionToGeo(QDoubleVector2D(0.0, 0.0)); //avoid warning of NaN values if function is returned early + + //1 The bounding rectangle of the polygon and camera view are compared to determine if the polygon is visible + // The viewport is periodic in x-direction in the interval [-1; 1]. + // The polygon (maybe) has to be ploted periodically too by shifting it by -1 or +1; + const QRectF cameraRect = QDeclarativeGeoMapItemUtils::boundingRectangleFromList(p.visibleGeometry()); + QRectF itemRect; + for (const auto &path : paths) + itemRect |= QDeclarativeGeoMapItemUtils::boundingRectangleFromList(path); + QList<QList<QDoubleVector2D>> wrappedPaths; + + if (wrapping == WrapAround) { + for (double xoffset : {-1.0, 0.0, 1.0}) { + if (!cameraRect.intersects(itemRect.translated(QPointF(xoffset, 0.0)))) + continue; + for (const auto &path : paths) { + wrappedPaths.append(QList<QDoubleVector2D>()); + QList<QDoubleVector2D> &wP = wrappedPaths.last(); + wP.reserve(path.size()); + for (const QDoubleVector2D &coord : path) + wP.append(coord+QDoubleVector2D(xoffset, 0.0)); + } + } + } else + wrappedPaths = paths; + + if (wrappedPaths.isEmpty()) // the polygon boundary rectangle does not overlap with the viewport rectangle + return; - // build the actual path - // The approach is the same as described in QGeoMapPolylineGeometry::updateSourcePoints - srcOrigin_ = geoLeftBound_; - double unwrapBelowX = 0; - QDoubleVector2D leftBoundWrapped = p.wrapMapProjection(p.geoToMapProjection(geoLeftBound_)); - if (preserveGeometry_) - unwrapBelowX = leftBoundWrapped.x(); - - QList<QDoubleVector2D> wrappedPath; - wrappedPath.reserve(path.size()); - QDoubleVector2D wrappedLeftBound(qInf(), qInf()); - // 1) - for (const auto &coord : path) { - QDoubleVector2D wrappedProjection = p.wrapMapProjection(coord); - - // 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(wrappedProjection.x()) || !qIsFinite(wrappedProjection.y())) - return; - - const bool isPointLessThanUnwrapBelowX = (wrappedProjection.x() < leftBoundWrapped.x()); - // unwrap x to preserve geometry if moved to border of map - if (preserveGeometry_ && isPointLessThanUnwrapBelowX) { - double distance = wrappedProjection.x() - unwrapBelowX; - if (distance < 0.0) - distance += 1.0; - wrappedProjection.setX(unwrapBelowX + distance); + + //2 The polygons that are at least partially in the viewport are cliped to reduce their size + QList<QList<QDoubleVector2D>> clippedPaths; + const QList<QDoubleVector2D> &visibleRegion = p.visibleGeometryExpanded(); + for (const auto &path : wrappedPaths) { + if (visibleRegion.size()) { + QClipperUtils clipper; + clipper.addSubjectPath(path, true); + clipper.addClipPolygon(visibleRegion); + clippedPaths << clipper.execute(QClipperUtils::Intersection, QClipperUtils::pftEvenOdd, + QClipperUtils::pftEvenOdd); } - if (wrappedProjection.x() < wrappedLeftBound.x() || (wrappedProjection.x() == wrappedLeftBound.x() && wrappedProjection.y() < wrappedLeftBound.y())) { - wrappedLeftBound = wrappedProjection; + else { + clippedPaths.append(path); //Do we really need this if there are no visible regions?? } - wrappedPath.append(wrappedProjection); - } - - // 2) - QList<QList<QDoubleVector2D> > clippedPaths; - const QList<QDoubleVector2D> &visibleRegion = p.projectableGeometry(); - if (visibleRegion.size()) { - QClipperUtils clipper; - clipper.addSubjectPath(wrappedPath, true); - clipper.addClipPolygon(visibleRegion); - clippedPaths = clipper.execute(QClipperUtils::Intersection, QClipperUtils::pftEvenOdd, - QClipperUtils::pftEvenOdd); - - // 2.1) update srcOrigin_ and leftBoundWrapped with the point with minimum X - QDoubleVector2D lb(qInf(), qInf()); - for (const QList<QDoubleVector2D> &path: clippedPaths) - for (const QDoubleVector2D &p: path) - if (p.x() < lb.x() || (p.x() == lb.x() && p.y() < lb.y())) - // y-minimization needed to find the same point on polygon and border - lb = p; - - if (qIsInf(lb.x())) // e.g., when the polygon is clipped entirely - return; - - // 2.2) Prevent the conversion to and from clipper from introducing negative offsets which - // in turn will make the geometry wrap around. - lb.setX(qMax(wrappedLeftBound.x(), lb.x())); - leftBoundWrapped = lb; - srcOrigin_ = p.mapProjectionToGeo(p.unwrapMapProjection(lb)); - } else { - clippedPaths.append(wrappedPath); } + if (clippedPaths.isEmpty()) //the polygon is entirely outside visibleRegion + return; - // 3) + QRectF bb; + for (const auto &path: clippedPaths) + bb |= QDeclarativeGeoMapItemUtils::boundingRectangleFromList(path); + //Offset by origin, find the maximum coordinate + srcOrigin_ = p.mapProjectionToGeo(QDoubleVector2D(bb.left(), bb.top())); + QDoubleVector2D origin = p.wrappedMapProjectionToItemPosition(p.geoToWrappedMapProjection(srcOrigin_)); //save way: redo all projections maxCoord_ = 0.0; - QDoubleVector2D origin = p.wrappedMapProjectionToItemPosition(leftBoundWrapped); - for (const QList<QDoubleVector2D> &path: clippedPaths) { - QDoubleVector2D lastAddedPoint; - for (qsizetype i = 0; i < path.size(); ++i) { - QDoubleVector2D point = p.wrappedMapProjectionToItemPosition(path.at(i)); - point = point - origin; // (0,0) if point == geoLeftBound_ + for (const auto &path: clippedPaths) { + QDoubleVector2D prevPoint = p.wrappedMapProjectionToItemPosition(path.at(0)) - origin; + QDoubleVector2D nextPoint = p.wrappedMapProjectionToItemPosition(path.at(1)) - origin; + srcPath_.moveTo(prevPoint.toPointF()); + maxCoord_ = qMax(maxCoord_, qMax(prevPoint.x(), prevPoint.y())); + qsizetype pointsAdded = 1; + for (qsizetype i = 1; i < path.size(); ++i) { + const QDoubleVector2D point = nextPoint; if (qMax(point.x(), point.y()) > maxCoord_) maxCoord_ = qMax(point.x(), point.y()); - if (i == 0) { - srcPath_.moveTo(point.toPointF()); - lastAddedPoint = point; + if (i == path.size() - 1) { + srcPath_.lineTo(point.toPointF()); //close the path } else { - if ((point - lastAddedPoint).manhattanLength() > 3 || - i == path.size() - 1) { + nextPoint = p.wrappedMapProjectionToItemPosition(path.at(i+1)) - origin; + + bool addPoint = ( i > pointsAdded * 10 || //make sure that at least every 10th point is drawn + path.size() < 10 ); //draw small paths completely + + const double tolerance = 0.1; + if (!addPoint) { //add the point to the shape if it deflects the boundary by more than the tolerance + const double dsqr = QDeclarativeGeoMapItemUtils::distanceSqrPointLine( + point.x(), point.y(), + nextPoint.x(), nextPoint.y(), + prevPoint.x(), prevPoint.y()); + addPoint = addPoint || (dsqr > (tolerance*tolerance)); + } + + if (addPoint) { srcPath_.lineTo(point.toPointF()); - lastAddedPoint = point; + pointsAdded++; + prevPoint = point; } + } } srcPath_.closeSubpath(); @@ -292,10 +299,8 @@ QSGNode *QDeclarativePolygonMapItemPrivateCPU::updateMapItemPaintNode(QSGNode *o { Q_UNUSED(data); delete oldNode; - if (m_geometry.isScreenDirty() || m_poly.m_dirtyMaterial) { - m_geometry.setPreserveGeometry(false); + if (m_geometry.isScreenDirty()) { m_geometry.markClean(); - m_poly.m_dirtyMaterial = false; } return nullptr; } @@ -310,7 +315,7 @@ bool QDeclarativePolygonMapItemPrivateCPU::contains(const QPointF &point) const */ QDeclarativePolygonMapItem::QDeclarativePolygonMapItem(QQuickItem *parent) -: QDeclarativeGeoMapItemBase(parent), m_border(this), m_color(Qt::transparent), m_dirtyMaterial(true), +: QDeclarativeGeoMapItemBase(parent), m_border(this), m_color(Qt::transparent), m_updatingGeometry(false) , m_d(new QDeclarativePolygonMapItemPrivateCPU(*this)) @@ -443,7 +448,6 @@ void QDeclarativePolygonMapItem::setColor(const QColor &color) return; m_color = color; - m_dirtyMaterial = true; polishAndUpdate(); // in case color was transparent and now is not or vice versa emit colorChanged(m_color); } @@ -466,12 +470,6 @@ void QDeclarativePolygonMapItem::updatePolish() m_d->updatePolish(); } -void QDeclarativePolygonMapItem::setMaterialDirty() -{ - m_dirtyMaterial = true; - update(); -} - void QDeclarativePolygonMapItem::markSourceDirtyAndUpdate() { m_d->markSourceDirtyAndUpdate(); diff --git a/src/location/quickmapitems/qdeclarativepolygonmapitem_p.h b/src/location/quickmapitems/qdeclarativepolygonmapitem_p.h index 1462bca4..1a99983d 100644 --- a/src/location/quickmapitems/qdeclarativepolygonmapitem_p.h +++ b/src/location/quickmapitems/qdeclarativepolygonmapitem_p.h @@ -68,7 +68,6 @@ protected Q_SLOTS: protected: void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override; void updatePolish() override; - void setMaterialDirty() override; #ifdef QT_LOCATION_DEBUG public: @@ -76,8 +75,6 @@ public: QGeoPolygon m_geopoly; QDeclarativeMapLineProperties m_border; QColor m_color; - bool m_dirtyMaterial; -// bool m_dirtyGeometry = false; bool m_updatingGeometry; std::unique_ptr<QDeclarativePolygonMapItemPrivate> m_d; diff --git a/src/location/quickmapitems/qdeclarativepolygonmapitem_p_p.h b/src/location/quickmapitems/qdeclarativepolygonmapitem_p_p.h index 449a1a1d..ce0ed18e 100644 --- a/src/location/quickmapitems/qdeclarativepolygonmapitem_p_p.h +++ b/src/location/quickmapitems/qdeclarativepolygonmapitem_p_p.h @@ -38,12 +38,18 @@ class QQuickShapePath; class Q_LOCATION_PRIVATE_EXPORT QGeoMapPolygonGeometry : public QGeoMapItemGeometry { public: + enum MapBorderBehaviour { + DrawOnce, + WrapAround + }; + QGeoMapPolygonGeometry(); inline void setAssumeSimple(bool value) { assumeSimple_ = value; } void updateSourcePoints(const QGeoMap &map, - const QList<QDoubleVector2D> &path); + const QList<QList<QDoubleVector2D>> &path, + MapBorderBehaviour wrapping = WrapAround); QPainterPath srcPath() const { return srcPath_; } qreal maxCoord() const { return maxCoord_; } @@ -91,7 +97,6 @@ public: } void markSourceDirtyAndUpdate() override { - // preserveGeometry is cleared in updateMapItemPaintNode m_geometry.markSourceDirty(); m_poly.polishAndUpdate(); } @@ -101,25 +106,29 @@ public: return; const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_poly.map()->geoProjection()); m_geopathProjected.clear(); - m_geopathProjected.reserve(m_poly.m_geopoly.size()); + m_geopathProjected << QList<QDoubleVector2D>(); + QList<QDoubleVector2D> &pP = m_geopathProjected.last(); + pP.reserve(m_poly.m_geopoly.perimeter().size()); for (const QGeoCoordinate &c : m_poly.m_geopoly.perimeter()) - m_geopathProjected << p.geoToMapProjection(c); + pP << p.geoToMapProjection(c); + for (int i = 0; i < m_poly.m_geopoly.holesCount(); i++) { + m_geopathProjected << QList<QDoubleVector2D>(); + QList<QDoubleVector2D> &pH = m_geopathProjected.last(); + pH.reserve(m_poly.m_geopoly.holePath(i).size()); + for (const QGeoCoordinate &c : m_poly.m_geopoly.holePath(i)) + pH << p.geoToMapProjection(c); + } } void updateCache() { if (!m_poly.map() || m_poly.map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) return; const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_poly.map()->geoProjection()); - m_geopathProjected << p.geoToMapProjection(m_poly.m_geopoly.perimeter().last()); - } - void preserveGeometry() - { - m_geometry.setPreserveGeometry(true, m_poly.m_geopoly.boundingGeoRectangle().topLeft()); + QList<QDoubleVector2D> &pP = m_geopathProjected.first(); + pP << p.geoToMapProjection(m_poly.m_geopoly.perimeter().last()); } void afterViewportChanged() override { - // preserveGeometry is cleared in updateMapItemPaintNode - preserveGeometry(); markSourceDirtyAndUpdate(); } void onMapSet() override @@ -130,13 +139,11 @@ public: void onGeoGeometryChanged() override { regenerateCache(); - preserveGeometry(); markSourceDirtyAndUpdate(); } void onGeoGeometryUpdated() override { updateCache(); - preserveGeometry(); markSourceDirtyAndUpdate(); } void onItemGeometryChanged() override @@ -147,7 +154,7 @@ public: QSGNode *updateMapItemPaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data) override; bool contains(const QPointF &point) const override; - QList<QDoubleVector2D> m_geopathProjected; + QList<QList<QDoubleVector2D>> m_geopathProjected; QGeoMapPolygonGeometry m_geometry; QQuickShape *m_shape = nullptr; QQuickShapePath *m_shapePath = nullptr; diff --git a/src/location/quickmapitems/qdeclarativepolylinemapitem.cpp b/src/location/quickmapitems/qdeclarativepolylinemapitem.cpp index 2477a4d8..e80afbee 100644 --- a/src/location/quickmapitems/qdeclarativepolylinemapitem.cpp +++ b/src/location/quickmapitems/qdeclarativepolylinemapitem.cpp @@ -3,7 +3,6 @@ #include "qdeclarativepolylinemapitem_p.h" #include "qdeclarativepolylinemapitem_p_p.h" -#include "qdeclarativegeomapitemutils_p.h" #include <QtCore/QScopedValueRollback> #include <qnumeric.h> @@ -296,421 +295,97 @@ void QDeclarativeMapLineProperties::setWidth(qreal width) emit widthChanged(width_); } -QGeoMapPolylineGeometry::QGeoMapPolylineGeometry() -{ -} - -QList<QList<QDoubleVector2D> > QGeoMapPolylineGeometry::clipPath(const QGeoMap &map, - const QList<QDoubleVector2D> &path, - QDoubleVector2D &leftBoundWrapped) -{ - /* - * Approach: - * 1) project coordinates to wrapped web mercator, and do unwrapBelowX - * 2) if the scene is tilted, clip the geometry against the visible region (this may generate multiple polygons) - * 2.1) recalculate the origin and geoLeftBound to prevent these parameters from ending in unprojectable areas - * 2.2) ensure the left bound does not wrap around due to QGeoCoordinate <-> clipper conversions - */ - const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map.geoProjection()); - srcOrigin_ = geoLeftBound_; - - double unwrapBelowX = 0; - leftBoundWrapped = p.wrapMapProjection(p.geoToMapProjection(geoLeftBound_)); - if (preserveGeometry_) - unwrapBelowX = leftBoundWrapped.x(); - - QList<QDoubleVector2D> wrappedPath; - wrappedPath.reserve(path.size()); - QDoubleVector2D wrappedLeftBound(qInf(), qInf()); - // 1) - for (const auto &coord : path) { - QDoubleVector2D wrappedProjection = p.wrapMapProjection(coord); - - // 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(wrappedProjection.x()) || !qIsFinite(wrappedProjection.y())) - return QList<QList<QDoubleVector2D> >(); - - const bool isPointLessThanUnwrapBelowX = (wrappedProjection.x() < leftBoundWrapped.x()); - // unwrap x to preserve geometry if moved to border of map - if (preserveGeometry_ && isPointLessThanUnwrapBelowX) { - double distance = wrappedProjection.x() - unwrapBelowX; - if (distance < 0.0) - distance += 1.0; - wrappedProjection.setX(unwrapBelowX + distance); - } - if (wrappedProjection.x() < wrappedLeftBound.x() || (wrappedProjection.x() == wrappedLeftBound.x() && wrappedProjection.y() < wrappedLeftBound.y())) { - wrappedLeftBound = wrappedProjection; - } - wrappedPath.append(wrappedProjection); - } - -#ifdef QT_LOCATION_DEBUG - m_wrappedPath = wrappedPath; -#endif - - // 2) - QList<QList<QDoubleVector2D> > clippedPaths; - const QList<QDoubleVector2D> &visibleRegion = p.projectableGeometry(); - if (visibleRegion.size()) { - clippedPaths = clipLine(wrappedPath, visibleRegion); - - // 2.1) update srcOrigin_ and leftBoundWrapped with the point with minimum X - QDoubleVector2D lb(qInf(), qInf()); - for (const QList<QDoubleVector2D> &path: clippedPaths) { - for (const QDoubleVector2D &p: path) { - if (p == leftBoundWrapped) { - lb = p; - break; - } else if (p.x() < lb.x() || (p.x() == lb.x() && p.y() < lb.y())) { - // y-minimization needed to find the same point on polygon and border - lb = p; - } - } - } - if (qIsInf(lb.x())) - return QList<QList<QDoubleVector2D> >(); - - // 2.2) Prevent the conversion to and from clipper from introducing negative offsets which - // in turn will make the geometry wrap around. - lb.setX(qMax(wrappedLeftBound.x(), lb.x())); - leftBoundWrapped = lb; - } else { - clippedPaths.append(wrappedPath); - } - -#ifdef QT_LOCATION_DEBUG - m_clippedPaths = clippedPaths; -#endif - - return clippedPaths; -} - -void QGeoMapPolylineGeometry::pathToScreen(const QGeoMap &map, - const QList<QList<QDoubleVector2D> > &clippedPaths, - const QDoubleVector2D &leftBoundWrapped) -{ - const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map.geoProjection()); - // 3) project the resulting geometry to screen position and calculate screen bounds - double minX = qInf(); - double minY = qInf(); - double maxX = -qInf(); - double maxY = -qInf(); - srcOrigin_ = p.mapProjectionToGeo(p.unwrapMapProjection(leftBoundWrapped)); - QDoubleVector2D origin = p.wrappedMapProjectionToItemPosition(leftBoundWrapped); - for (const QList<QDoubleVector2D> &path: clippedPaths) { - QDoubleVector2D lastAddedPoint; - for (qsizetype i = 0; i < path.size(); ++i) { - QDoubleVector2D point = p.wrappedMapProjectionToItemPosition(path.at(i)); - point = point - origin; // (0,0) if point == geoLeftBound_ - - minX = qMin(point.x(), minX); - minY = qMin(point.y(), minY); - maxX = qMax(point.x(), maxX); - maxY = qMax(point.y(), maxY); - - if (i == 0) { - srcPoints_ << point.x() << point.y(); - srcPointTypes_ << QPainterPath::MoveToElement; - lastAddedPoint = point; - } else { - if ((point - lastAddedPoint).manhattanLength() > 3 || - i == path.size() - 1) { - srcPoints_ << point.x() << point.y(); - srcPointTypes_ << QPainterPath::LineToElement; - lastAddedPoint = point; - } - } - } - } - - sourceBounds_ = QRectF(QPointF(minX, minY), QPointF(maxX, maxY)); -} - /*! \internal */ void QGeoMapPolylineGeometry::updateSourcePoints(const QGeoMap &map, - const QList<QDoubleVector2D> &path, - const QGeoCoordinate geoLeftBound) + const QList<QDoubleVector2D> &basePath) { + // A polygon consists of mutliple paths. This is usually a perimeter and multiple holes + // We move all paths into a single QPainterPath. The filling rule EvenOdd will then ensure that the paths are shown correctly if (!sourceDirty_) return; - - geoLeftBound_ = geoLeftBound; - - // clear the old data and reserve enough memory - srcPoints_.clear(); - srcPoints_.reserve(path.size() * 2); - srcPointTypes_.clear(); - srcPointTypes_.reserve(path.size()); - - /* - * Approach: - * 1) project coordinates to wrapped web mercator, and do unwrapBelowX - * 2) if the scene is tilted, clip the geometry against the visible region (this may generate multiple polygons) - * 3) project the resulting geometry to screen position and calculate screen bounds - */ - - QDoubleVector2D leftBoundWrapped; - // 1, 2) - const QList<QList<QDoubleVector2D> > &clippedPaths = clipPath(map, path, leftBoundWrapped); - - // 3) - pathToScreen(map, clippedPaths, leftBoundWrapped); - + const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map.geoProjection()); srcPath_ = QPainterPath(); - maxCoord_ = 0.0; - const int elemCount = srcPointTypes_.count(); - for (int i = 0; i < elemCount; ++i) { - switch (srcPointTypes_[i]) { - case QPainterPath::MoveToElement: - { - const qreal x = srcPoints_[2 * i]; - const qreal y = srcPoints_[2 * i + 1]; - if (qMax(x, y) > maxCoord_) - maxCoord_ = qMax(x, y); - srcPath_.moveTo(x, y); - } - break; - case QPainterPath::LineToElement: - { - const qreal x = srcPoints_[2 * i]; - const qreal y = srcPoints_[2 * i + 1]; - if (qMax(x, y) > maxCoord_) - maxCoord_ = qMax(x, y); - srcPath_.lineTo(x, y); - } - break; - default: - break; - } + srcOrigin_ = p.mapProjectionToGeo(QDoubleVector2D(0, 0)); //avoid warning of NaN values if function is returned early + + //0 Wrap the points around the globe if the path makes more sense that way. + // Ultimately, this is done if it is closer to walk around the day-border than the other direction + QVarLengthArray<QList<QDoubleVector2D>, 3> wrappedPaths; + wrappedPaths << QList<QDoubleVector2D>({basePath[0]}); + wrappedPaths.last().reserve(basePath.size()); + for (int i = 1; i < basePath.size(); i++) { + if (basePath[i].x() > wrappedPaths.last().last().x() + 0.5) + wrappedPaths.last() << basePath[i] - QDoubleVector2D(1.0, 0.0); + else if (basePath[i].x() < wrappedPaths.last().last().x() - 0.5) + wrappedPaths.last() << basePath[i] + QDoubleVector2D(1.0, 0.0); + else + wrappedPaths.last() << basePath[i]; } -} - -// *** SCREEN CLIPPING *** // - -enum ClipPointType { - InsidePoint = 0x00, - LeftPoint = 0x01, - RightPoint = 0x02, - BottomPoint = 0x04, - TopPoint = 0x08 -}; -static inline int clipPointType(qreal x, qreal y, const QRectF &rect) -{ - int type = InsidePoint; - if (x < rect.left()) - type |= LeftPoint; - else if (x > rect.right()) - type |= RightPoint; - if (y < rect.top()) - type |= TopPoint; - else if (y > rect.bottom()) - type |= BottomPoint; - return type; -} - -static void clipSegmentToRect(qreal x0, qreal y0, qreal x1, qreal y1, const QRectF &clipRect, - QList<qreal> &outPoints, QList<QPainterPath::ElementType> &outTypes) -{ - int type0 = clipPointType(x0, y0, clipRect); - int type1 = clipPointType(x1, y1, clipRect); - bool accept = false; - - while (true) { - if (!(type0 | type1)) { - accept = true; - break; - } else if (type0 & type1) { - break; - } else { - qreal x = 0.0; - qreal y = 0.0; - int outsideType = type0 ? type0 : type1; - - if (outsideType & BottomPoint) { - x = x0 + (x1 - x0) * (clipRect.bottom() - y0) / (y1 - y0); - y = clipRect.bottom() - 0.1; - } else if (outsideType & TopPoint) { - x = x0 + (x1 - x0) * (clipRect.top() - y0) / (y1 - y0); - y = clipRect.top() + 0.1; - } else if (outsideType & RightPoint) { - y = y0 + (y1 - y0) * (clipRect.right() - x0) / (x1 - x0); - x = clipRect.right() - 0.1; - } else if (outsideType & LeftPoint) { - y = y0 + (y1 - y0) * (clipRect.left() - x0) / (x1 - x0); - x = clipRect.left() + 0.1; - } - - if (outsideType == type0) { - x0 = x; - y0 = y; - type0 = clipPointType(x0, y0, clipRect); - } else { - x1 = x; - y1 = y; - type1 = clipPointType(x1, y1, clipRect); - } - } + //1 The bounding rectangle of the polygon and camera view are compared to determine if the polygon is visible + // The viewport is periodic in x-direction in the interval [-1; 1]. + // The polygon (maybe) has to be ploted periodically too by shifting it by -1 or +1; + const QRectF cameraRect = QDeclarativeGeoMapItemUtils::boundingRectangleFromList(p.visibleGeometry()); + QRectF itemRect; + for (const auto &path : wrappedPaths) + itemRect |= QDeclarativeGeoMapItemUtils::boundingRectangleFromList(path).adjusted(-1e-6, -1e-6, 2e-6, 2e-6); //TODO: Maybe use linewidth? + for (double xoffset : {-1.0, 1.0}) { + if (!cameraRect.intersects(itemRect.translated(QPointF(xoffset,0)))) + continue; + wrappedPaths.append(QList<QDoubleVector2D>()); + QList<QDoubleVector2D> &wP = wrappedPaths.last(); + wP.reserve(wrappedPaths.first().size()); + for (const QDoubleVector2D &coord : wrappedPaths.first()) + wP.append(coord + QDoubleVector2D(xoffset, 0.0)); } + if (wrappedPaths.isEmpty()) // the polygon boundary rectangle does not overlap with the viewport rectangle + return; - if (accept) { - if (outPoints.size() >= 2) { - qreal lastX, lastY; - lastY = outPoints.at(outPoints.size() - 1); - lastX = outPoints.at(outPoints.size() - 2); - - if (!qFuzzyCompare(lastY, y0) || !qFuzzyCompare(lastX, x0)) { - outTypes << QPainterPath::MoveToElement; - outPoints << x0 << y0; - } + //2 The polygons that are at least partially in the viewport are cliped to reduce their size + QList<QList<QDoubleVector2D>> clippedPaths; + const QList<QDoubleVector2D> &visibleRegion = p.visibleGeometryExpanded(); + for (const auto &path : wrappedPaths) { + if (visibleRegion.size()) { + clippedPaths << clipLine(path, visibleRegion); + //TODO: Replace clipping with Clipper lib, similar to QPolygonMapItem } else { - outTypes << QPainterPath::MoveToElement; - outPoints << x0 << y0; - } - - outTypes << QPainterPath::LineToElement; - outPoints << x1 << y1; - } -} - -static void clipPathToRect(const QList<qreal> &points, - const QList<QPainterPath::ElementType> &types, const QRectF &clipRect, - QList<qreal> &outPoints, QList<QPainterPath::ElementType> &outTypes) -{ - outPoints.clear(); - outPoints.reserve(points.size()); - outTypes.clear(); - outTypes.reserve(types.size()); - - qreal lastX = 0; - qreal lastY = 0; // or else used uninitialized - for (qsizetype i = 0; i < types.size(); ++i) { - if (i > 0 && types[i] != QPainterPath::MoveToElement) { - qreal x = points[i * 2], y = points[i * 2 + 1]; - clipSegmentToRect(lastX, lastY, x, y, clipRect, outPoints, outTypes); + clippedPaths.append(path); //Do we really need this if there are no visible regions?? } - - lastX = points[i * 2]; - lastY = points[i * 2 + 1]; - } -} - -//////////////////////////////////////////////////////////////////////////// - -/*! - \internal -*/ -void QGeoMapPolylineGeometry::updateScreenPoints(const QGeoMap &map, - qreal strokeWidth, - bool adjustTranslation) -{ - if (!screenDirty_) - return; - - QPointF origin = map.geoProjection().coordinateToItemPosition(srcOrigin_, false).toPointF(); - - if (!qIsFinite(origin.x()) || !qIsFinite(origin.y()) || srcPointTypes_.size() < 2) { // the line might have been clipped away. - clear(); - return; - } - - // Create the viewport rect in the same coordinate system - // as the actual points - QRectF viewport(0, 0, map.viewportWidth(), map.viewportHeight()); - viewport.adjust(-strokeWidth, -strokeWidth, strokeWidth * 2, strokeWidth * 2); - viewport.translate(-1 * origin); - - QList<qreal> points; - QList<QPainterPath::ElementType> types; - - if (clipToViewport_) { - // Although the geometry has already been clipped against the visible region in wrapped mercator space. - // This is currently still needed to prevent a number of artifacts deriving from QTriangulatingStroker processing - // very large lines (that is, polylines that span many pixels in screen space) - clipPathToRect(srcPoints_, srcPointTypes_, viewport, points, types); - } else { - points = srcPoints_; - types = srcPointTypes_; } - - QVectorPath vp(points.data(), types.size(), types.data()); - QTriangulatingStroker ts; - // As of Qt5.11, the clip argument is not actually used, in the call below. - ts.process(vp, QPen(QBrush(Qt::black), strokeWidth), QRectF(), QPainter::Antialiasing); - - clear(); - - // Nothing is on the screen - if (ts.vertexCount() == 0) + if (clippedPaths.isEmpty()) //the polygon is entirely outside visibleRegion return; - // QTriangulatingStroker#vertexCount is actually the length of the array, - // not the number of vertices - screenVertices_.reserve(ts.vertexCount()); - QRectF bb; + for (const auto &path: clippedPaths) + bb |= QDeclarativeGeoMapItemUtils::boundingRectangleFromList(path); + //Offset by origin, find the maximum coordinate + maxCoord_ = 0.0; + srcOrigin_ = p.mapProjectionToGeo(QDoubleVector2D(bb.left(), bb.top())); + QDoubleVector2D origin = p.wrappedMapProjectionToItemPosition(p.geoToWrappedMapProjection(srcOrigin_)); //save way: redo all projections + for (const auto &path: clippedPaths) { + QDoubleVector2D lastAddedPoint; + for (qsizetype i = 0; i < path.size(); ++i) { + QDoubleVector2D point = p.wrappedMapProjectionToItemPosition(path.at(i)); + point = point - origin; // (0,0) if point == origin - QPointF pt; - const float *vs = ts.vertices(); - for (int i = 0; i < (ts.vertexCount()/2*2); i += 2) { - pt = QPointF(vs[i], vs[i + 1]); - screenVertices_ << pt; - - if (!qIsFinite(pt.x()) || !qIsFinite(pt.y())) - break; - - if (!bb.contains(pt)) { - if (pt.x() < bb.left()) - bb.setLeft(pt.x()); - - if (pt.x() > bb.right()) - bb.setRight(pt.x()); - - if (pt.y() < bb.top()) - bb.setTop(pt.y()); - - if (pt.y() > bb.bottom()) - bb.setBottom(pt.y()); - } - } - - screenBounds_ = bb; - - const QPointF strokeOffset = (adjustTranslation) ? QPointF(strokeWidth, strokeWidth) * 0.5: QPointF(); - const QPointF offset = -1 * sourceBounds_.topLeft() + strokeOffset; - for (qsizetype i = 0; i < screenVertices_.size(); ++i) - screenVertices_[i] += offset; - - firstPointOffset_ += offset; - screenOutline_.translate(offset); - screenBounds_.translate(offset); -} - -void QGeoMapPolylineGeometry::clearSource() -{ - srcPoints_.clear(); - srcPointTypes_.clear(); -} + if (qMax(point.x(), point.y()) > maxCoord_) + maxCoord_ = qMax(point.x(), point.y()); -bool QGeoMapPolylineGeometry::contains(const QPointF &point) const -{ - // screenOutline_.contains(screenPoint) doesn't work, as, it appears, that - // screenOutline_ for QGeoMapPolylineGeometry is empty (QRectF(0,0 0x0)) - const QList<QPointF> &verts = vertices(); - QPolygonF tri; - for (const auto &v : verts) { - tri << v; - if (tri.size() == 3) { - if (tri.containsPoint(point, Qt::OddEvenFill)) - return true; - tri.remove(0); + if (i == 0) { + srcPath_.moveTo(point.toPointF()); + lastAddedPoint = point; + } else { + if ((point - lastAddedPoint).manhattanLength() > 3 || + i == path.size() - 1) { + srcPath_.lineTo(point.toPointF()); + lastAddedPoint = point; + } + } } } - return false; + sourceBounds_ = srcPath_.boundingRect(); } /* @@ -750,7 +425,7 @@ void QDeclarativePolylineMapItemPrivateCPU::regenerateCache() return; const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(m_poly.map()->geoProjection()); m_geopathProjected.clear(); - m_geopathProjected.reserve(m_poly.m_geopath.size()); + m_geopathProjected.reserve(m_poly.m_geopath.path().size()); for (const QGeoCoordinate &c : m_poly.m_geopath.path()) m_geopathProjected << p.geoToMapProjection(c); } @@ -778,16 +453,14 @@ void QDeclarativePolylineMapItemPrivateCPU::updatePolish() const QGeoMap *map = m_poly.map(); const qreal borderWidth = m_poly.m_line.width(); - m_geometry.updateSourcePoints(*map, m_geopathProjected, m_poly.m_geopath.boundingGeoRectangle().topLeft()); - - // still needed even with Shapes, due to contains() - m_geometry.updateScreenPoints(*map, borderWidth); + m_geometry.updateSourcePoints(*map, m_geopathProjected); const QRectF bb = m_geometry.sourceBoundingBox(); m_poly.setSize(bb.size() + QSizeF(borderWidth, borderWidth)); // it has to be shifted so that the center of the line is on the correct geocoord m_poly.setPositionOnMap(m_geometry.origin(), -1 * bb.topLeft() + QPointF(borderWidth, borderWidth) * 0.5); + m_poly.setShapeTriangulationScale(m_shape, m_geometry.maxCoord_); m_shapePath->setStrokeColor(m_poly.m_line.color()); @@ -808,11 +481,8 @@ QSGNode *QDeclarativePolylineMapItemPrivateCPU::updateMapItemPaintNode(QSGNode * { delete oldNode; - //TODO: update only material - if (m_geometry.isScreenDirty() || m_poly.m_dirtyMaterial || !oldNode) { - m_geometry.setPreserveGeometry(false); + if (m_geometry.isScreenDirty() || !oldNode) { m_geometry.markClean(); - m_poly.m_dirtyMaterial = false; } return nullptr; } @@ -822,7 +492,21 @@ bool QDeclarativePolylineMapItemPrivateCPU::contains(const QPointF &point) const // With Shapes, do not just call // m_shape->contains(m_poly.mapToItem(m_shape, point)) because that can // only do FillContains at best, whereas the polyline relies on stroking. - return m_geometry.contains(point); + + const QPainterPath &path = m_geometry.srcPath_; + const double &lineWidth = m_poly.m_line.width(); + const QPointF p = m_poly.mapToItem(m_shape, point) - QPointF(lineWidth, lineWidth) * 0.5; + + for (int i = 1; i < path.elementCount(); i++) { + if (path.elementAt(i).type == QPainterPath::MoveToElement) + continue; + const double dsqr = QDeclarativeGeoMapItemUtils::distanceSqrPointLine(p.x(), p.y(), + path.elementAt(i - 1).x, path.elementAt(i - 1).y, + path.elementAt(i).x, path.elementAt(i).y); + if (dsqr < 0.25 * lineWidth * lineWidth) + return true; + } + return false; } /* diff --git a/src/location/quickmapitems/qdeclarativepolylinemapitem_p.h b/src/location/quickmapitems/qdeclarativepolylinemapitem_p.h index 377fb239..ce5d29cc 100644 --- a/src/location/quickmapitems/qdeclarativepolylinemapitem_p.h +++ b/src/location/quickmapitems/qdeclarativepolylinemapitem_p.h @@ -109,7 +109,6 @@ public: QGeoPath m_geopath; QDeclarativeMapLineProperties m_line; - bool m_dirtyMaterial = true; bool m_updatingGeometry = false; std::unique_ptr<QDeclarativePolylineMapItemPrivate> m_d; diff --git a/src/location/quickmapitems/qdeclarativepolylinemapitem_p_p.h b/src/location/quickmapitems/qdeclarativepolylinemapitem_p_p.h index 4ade0fba..ef482f3c 100644 --- a/src/location/quickmapitems/qdeclarativepolylinemapitem_p_p.h +++ b/src/location/quickmapitems/qdeclarativepolylinemapitem_p_p.h @@ -31,47 +31,15 @@ QT_BEGIN_NAMESPACE class QQuickShape; class QQuickShapePath; -class Q_LOCATION_PRIVATE_EXPORT QGeoMapPolylineGeometry : public QGeoMapItemGeometry +struct QGeoMapPolylineGeometry : QGeoMapItemGeometry { -public: - QGeoMapPolylineGeometry(); - void updateSourcePoints(const QGeoMap &map, - const QList<QDoubleVector2D> &path, - const QGeoCoordinate geoLeftBound); - - void updateScreenPoints(const QGeoMap &map, - qreal strokeWidth, - bool adjustTranslation = true); - - void clearSource(); - - bool contains(const QPointF &point) const override; - - QList<QList<QDoubleVector2D> > clipPath(const QGeoMap &map, - const QList<QDoubleVector2D> &path, - QDoubleVector2D &leftBoundWrapped); - - void pathToScreen(const QGeoMap &map, - const QList<QList<QDoubleVector2D> > &clippedPaths, - const QDoubleVector2D &leftBoundWrapped); + const QList<QDoubleVector2D> &basePath); QPainterPath srcPath() const { return srcPath_; } -public: - QList<qreal> srcPoints_; - QList<QPainterPath::ElementType> srcPointTypes_; QPainterPath srcPath_; qreal maxCoord_ = 0.0; - -#ifdef QT_LOCATION_DEBUG - QList<QDoubleVector2D> m_wrappedPath; - QList<QList<QDoubleVector2D>> m_clippedPaths; -#endif - - friend class QDeclarativeCircleMapItem; - friend class QDeclarativePolygonMapItem; - friend class QDeclarativeRectangleMapItem; }; class Q_LOCATION_PRIVATE_EXPORT QDeclarativePolylineMapItemPrivate @@ -116,14 +84,9 @@ public: } void regenerateCache(); void updateCache(); - void preserveGeometry() - { - m_geometry.setPreserveGeometry(true, m_poly.m_geopath.boundingGeoRectangle().topLeft()); - } void afterViewportChanged() override { // preserveGeometry is cleared in updateMapItemPaintNode - preserveGeometry(); markSourceDirtyAndUpdate(); } void onMapSet() override @@ -134,13 +97,11 @@ public: void onGeoGeometryChanged() override { regenerateCache(); - preserveGeometry(); markSourceDirtyAndUpdate(); } void onGeoGeometryUpdated() override { updateCache(); - preserveGeometry(); markSourceDirtyAndUpdate(); } void onItemGeometryChanged() override diff --git a/src/location/quickmapitems/qdeclarativerectanglemapitem.cpp b/src/location/quickmapitems/qdeclarativerectanglemapitem.cpp index 1fbc9215..0e4e9e02 100644 --- a/src/location/quickmapitems/qdeclarativerectanglemapitem.cpp +++ b/src/location/quickmapitems/qdeclarativerectanglemapitem.cpp @@ -211,7 +211,6 @@ void QDeclarativeRectangleMapItem::setColor(const QColor &color) if (m_color == color) return; m_color = color; - m_dirtyMaterial = true; polishAndUpdate(); emit colorChanged(m_color); } @@ -359,8 +358,7 @@ void QDeclarativeRectangleMapItemPrivateCPU::updatePolish() const QList<QGeoCoordinate> perimeter = QGeoMapItemGeometry::path(m_rect.m_rectangle); const QList<QDoubleVector2D> pathMercator_ = QGeoMapItemGeometry::pathMercator(perimeter); - m_geometry.setPreserveGeometry(true, m_rect.m_rectangle.topLeft()); - m_geometry.updateSourcePoints(*m_rect.map(), pathMercator_); + m_geometry.updateSourcePoints(*m_rect.map(), QList<QList<QDoubleVector2D>>{pathMercator_}); m_rect.setShapeTriangulationScale(m_shape, m_geometry.maxCoord()); @@ -389,10 +387,8 @@ QSGNode *QDeclarativeRectangleMapItemPrivateCPU::updateMapItemPaintNode(QSGNode { Q_UNUSED(data); delete oldNode; - if (m_geometry.isScreenDirty() || m_rect.m_dirtyMaterial) { - m_geometry.setPreserveGeometry(false); + if (m_geometry.isScreenDirty()) { m_geometry.markClean(); - m_rect.m_dirtyMaterial = false; } return nullptr; } diff --git a/src/location/quickmapitems/qdeclarativerectanglemapitem_p.h b/src/location/quickmapitems/qdeclarativerectanglemapitem_p.h index 883b261e..467857fe 100644 --- a/src/location/quickmapitems/qdeclarativerectanglemapitem_p.h +++ b/src/location/quickmapitems/qdeclarativerectanglemapitem_p.h @@ -80,7 +80,6 @@ private: QGeoRectangle m_rectangle; QDeclarativeMapLineProperties m_border; QColor m_color = Qt::transparent; - bool m_dirtyMaterial = true; bool m_updatingGeometry = false; diff --git a/src/location/quickmapitems/qdeclarativerectanglemapitem_p_p.h b/src/location/quickmapitems/qdeclarativerectanglemapitem_p_p.h index 3f914dd0..2df6767d 100644 --- a/src/location/quickmapitems/qdeclarativerectanglemapitem_p_p.h +++ b/src/location/quickmapitems/qdeclarativerectanglemapitem_p_p.h @@ -72,12 +72,10 @@ public: } void onItemGeometryChanged() override { - m_geometry.setPreserveGeometry(true, m_rect.m_rectangle.topLeft()); markSourceDirtyAndUpdate(); } void afterViewportChanged() override { - m_geometry.setPreserveGeometry(true, m_rect.m_rectangle.topLeft()); markSourceDirtyAndUpdate(); } void updatePolish() override; diff --git a/src/location/quickmapitems/qgeomapitemgeometry_p.h b/src/location/quickmapitems/qgeomapitemgeometry_p.h index c7fd2f57..0fa90ae4 100644 --- a/src/location/quickmapitems/qgeomapitemgeometry_p.h +++ b/src/location/quickmapitems/qgeomapitemgeometry_p.h @@ -74,12 +74,6 @@ public: inline void markClean() { screenDirty_ = (sourceDirty_ = false); clipToViewport_ = true;} inline void clearScreen() { screenDirty_ = false; } - inline void setPreserveGeometry(bool value, const QGeoCoordinate &geoLeftBound = QGeoCoordinate()) - { - preserveGeometry_ = value; - if (preserveGeometry_) - geoLeftBound_ = geoLeftBound; - } inline QGeoCoordinate geoLeftBound() { return geoLeftBound_; } inline QRectF sourceBoundingBox() const { return sourceBounds_; } |