summaryrefslogtreecommitdiff
path: root/src/location/declarativemaps/qdeclarativepolylinemapitem.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/location/declarativemaps/qdeclarativepolylinemapitem.cpp')
-rw-r--r--src/location/declarativemaps/qdeclarativepolylinemapitem.cpp1077
1 files changed, 952 insertions, 125 deletions
diff --git a/src/location/declarativemaps/qdeclarativepolylinemapitem.cpp b/src/location/declarativemaps/qdeclarativepolylinemapitem.cpp
index 10f3f0c3..d59704dc 100644
--- a/src/location/declarativemaps/qdeclarativepolylinemapitem.cpp
+++ b/src/location/declarativemaps/qdeclarativepolylinemapitem.cpp
@@ -35,11 +35,16 @@
****************************************************************************/
#include "qdeclarativepolylinemapitem_p.h"
+#include "qdeclarativepolylinemapitem_p_p.h"
+#include "qdeclarativerectanglemapitem_p_p.h"
+#include "qdeclarativecirclemapitem_p_p.h"
#include "qlocationutils_p.h"
+#include "qdeclarativegeomapitemutils_p.h"
#include "error_messages_p.h"
#include "locationvaluetypehelper_p.h"
#include "qdoublevector2d_p.h"
#include <QtLocation/private/qgeomap_p.h>
+#include <QtPositioning/private/qwebmercator_p.h>
#include <QtCore/QScopedValueRollback>
#include <QtQml/QQmlInfo>
@@ -55,10 +60,32 @@
#include <QtPositioning/private/qclipperutils_p.h>
#include <QtPositioning/private/qgeopath_p.h>
+#include <QtQuick/private/qsgmaterialshader_p.h>
#include <array>
+#include <QThreadPool>
+#include <QRunnable>
+#include <QtLocation/private/qgeomapparameter_p.h>
+#include "qgeosimplify_p.h"
QT_BEGIN_NAMESPACE
+struct ThreadPool // to have a thread pool with max 1 thread for geometry processing
+{
+ ThreadPool ()
+ {
+ m_threadPool.setMaxThreadCount(1);
+ }
+
+ void start(QRunnable *runnable, int priority = 0)
+ {
+ m_threadPool.start(runnable, priority);
+ }
+
+ QThreadPool m_threadPool;
+};
+
+Q_GLOBAL_STATIC(ThreadPool, threadPool)
+
static const double kClipperScaleFactor = 281474976710656.0; // 48 bits of precision
@@ -125,7 +152,7 @@ static QList<QList<QDoubleVector2D> > clipLine(
const QList<QDoubleVector2D> &poly)
{
QList<QList<QDoubleVector2D> > res;
- if (poly.size() < 3 || l.size() < 2)
+ if (poly.size() < 2 || l.size() < 2)
return res;
// Step 1: build edges
@@ -355,11 +382,6 @@ void QDeclarativeMapLineProperties::setWidth(qreal width)
emit widthChanged(width_);
}
-struct Vertex
-{
- QVector2D position;
-};
-
QGeoMapPolylineGeometry::QGeoMapPolylineGeometry()
{
}
@@ -661,7 +683,7 @@ void QGeoMapPolylineGeometry::updateScreenPoints(const QGeoMap &map,
// 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, strokeWidth);
+ viewport.adjust(-strokeWidth, -strokeWidth, strokeWidth * 2, strokeWidth * 2);
viewport.translate(-1 * origin);
QVector<qreal> points;
@@ -719,7 +741,7 @@ void QGeoMapPolylineGeometry::updateScreenPoints(const QGeoMap &map,
}
screenBounds_ = bb;
- const QPointF strokeOffset = (adjustTranslation) ? QPointF(strokeWidth, strokeWidth) : QPointF();
+ const QPointF strokeOffset = (adjustTranslation) ? QPointF(strokeWidth, strokeWidth) * 0.5: QPointF();
this->translate( -1 * sourceBounds_.topLeft() + strokeOffset);
}
@@ -747,16 +769,202 @@ bool QGeoMapPolylineGeometry::contains(const QPointF &point) const
return false;
}
+void QGeoMapPolylineGeometryOpenGL::updateSourcePoints(const QGeoMap &map, const QGeoPolygon &poly)
+{
+ if (!sourceDirty_)
+ return;
+ QGeoPath p(poly.path());
+ if (poly.path().size() && poly.path().last() != poly.path().first())
+ p.addCoordinate(poly.path().first());
+ updateSourcePoints(map, p);
+}
+
+void QGeoMapPolylineGeometryOpenGL::updateSourcePoints(const QGeoMap &map, const QGeoPath &poly)
+{
+ if (!sourceDirty_)
+ return;
+ const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map.geoProjection());
+
+ // build the actual path
+ // The approach is the same as described in QGeoMapPolylineGeometry::updateSourcePoints
+
+
+ QDoubleVector2D leftBoundWrapped;
+ // 1) pre-compute 3 sets of "wrapped" coordinates: one w regular mercator, one w regular mercator +- 1.0
+ QList<QDoubleVector2D> wrappedPath;
+ QDeclarativeGeoMapItemUtils::wrapPath(poly.path(), geoLeftBound_, p,
+ wrappedPath, &leftBoundWrapped);
+
+ const QGeoRectangle &boundingRectangle = poly.boundingGeoRectangle();
+ updateSourcePoints(p, wrappedPath, boundingRectangle);
+}
+
+void QGeoMapPolylineGeometryOpenGL::updateSourcePoints(const QGeoProjectionWebMercator &p,
+ const QList<QDoubleVector2D> &wrappedPath,
+ const QGeoRectangle &boundingRectangle) {
+ if (!sourceDirty_)
+ return;
+ // 1.1) do the same for the bbox
+ // Beware: vertical lines (or horizontal lines) might have an "empty" bbox. Check for that
+
+ QGeoCoordinate topLeft = boundingRectangle.topLeft();
+ QGeoCoordinate bottomRight = boundingRectangle.bottomRight();
+ const qreal epsilon = 0.000001;
+ if (qFuzzyCompare(topLeft.latitude(), bottomRight.latitude())) {
+ topLeft.setLatitude(qBound(-90.0, topLeft.latitude() + epsilon ,90.0));
+ bottomRight.setLatitude(qBound(-90.0, bottomRight.latitude() - epsilon ,90.0));
+ }
+ if (qFuzzyCompare(topLeft.longitude(), bottomRight.longitude())) {
+ topLeft.setLongitude(QLocationUtils::wrapLong(topLeft.longitude() - epsilon));
+ bottomRight.setLongitude(QLocationUtils::wrapLong(bottomRight.longitude() + epsilon));
+ }
+ QGeoPolygon bbox(QGeoRectangle(topLeft, bottomRight));
+ QList<QDoubleVector2D> wrappedBbox, wrappedBboxPlus1, wrappedBboxMinus1;
+ QDeclarativeGeoMapItemUtils::wrapPath(bbox.path(), bbox.boundingGeoRectangle().topLeft(), p,
+ wrappedBbox, wrappedBboxMinus1, wrappedBboxPlus1, &m_bboxLeftBoundWrapped);
+
+ // New pointers, some old LOD task might still be running and operating on the old pointers.
+ resetLOD();
+
+ for (const auto &v: qAsConst(wrappedPath)) m_screenVertices->append(v);
+
+ m_wrappedPolygons.resize(3);
+ m_wrappedPolygons[0].wrappedBboxes = wrappedBboxMinus1;
+ m_wrappedPolygons[1].wrappedBboxes = wrappedBbox;
+ m_wrappedPolygons[2].wrappedBboxes = wrappedBboxPlus1;
+ srcOrigin_ = geoLeftBound_;
+}
+
+void QGeoMapPolylineGeometryOpenGL::updateSourcePoints(const QGeoMap &map, const QGeoRectangle &rect)
+{
+ const QGeoPath path(QDeclarativeRectangleMapItemPrivateCPU::perimeter(rect));
+ updateSourcePoints(map, path);
+}
+
+void QGeoMapPolylineGeometryOpenGL::updateSourcePoints(const QGeoMap &map, const QGeoCircle &circle)
+{
+ if (!sourceDirty_)
+ return;
+ const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map.geoProjection());
+
+ QDoubleVector2D leftBoundWrapped;
+ // 1) pre-compute 3 sets of "wrapped" coordinates: one w regular mercator, one w regular mercator +- 1.0
+ QList<QGeoCoordinate> path;
+ QGeoCoordinate leftBound;
+ QList<QDoubleVector2D> wrappedPath;
+ QDeclarativeCircleMapItemPrivateCPU::calculatePeripheralPoints(path, circle.center(), circle.radius(), QDeclarativeCircleMapItemPrivateCPU::CircleSamples, leftBound);
+ path << path.first();
+ geoLeftBound_ = leftBound;
+ QDeclarativeGeoMapItemUtils::wrapPath(path, leftBound, p, wrappedPath, &leftBoundWrapped);
+ const QGeoRectangle &boundingRectangle = circle.boundingGeoRectangle();
+ updateSourcePoints(p, wrappedPath, boundingRectangle);
+}
+
+void QGeoMapPolylineGeometryOpenGL::updateScreenPoints(const QGeoMap &map, qreal strokeWidth, bool /*adjustTranslation*/)
+{
+ if (map.viewportWidth() == 0 || map.viewportHeight() == 0) {
+ clear();
+ return;
+ }
+
+ // 1) identify which set to use: std, +1 or -1
+ const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map.geoProjection());
+ const QDoubleVector2D leftBoundMercator = p.geoToMapProjection(srcOrigin_);
+ m_wrapOffset = p.projectionWrapFactor(leftBoundMercator) + 1; // +1 to get the offset into QLists
+
+ if (sourceDirty_) {
+ // 1.1) select geometry set
+ // This could theoretically be skipped for those polylines whose bbox is not even projectable.
+ // However, such optimization could only be introduced if not calculating bboxes lazily.
+ // Hence not doing it.
+// if (m_screenVertices.size() > 1)
+ m_dataChanged = true;
+ }
+
+ updateQuickGeometry(p, strokeWidth);
+}
+
+void QGeoMapPolylineGeometryOpenGL::updateQuickGeometry(const QGeoProjectionWebMercator &p, qreal strokeWidth)
+{
+ // 2) clip bbox
+ // BBox handling -- this is related to the bounding box geometry
+ // that has to inevitably follow the old projection codepath
+ // As it needs to provide projected coordinates for QtQuick interaction.
+ // This could be futher optimized to be updated in a lazy fashion.
+ const QList<QDoubleVector2D> &wrappedBbox = m_wrappedPolygons.at(m_wrapOffset).wrappedBboxes;
+ QList<QList<QDoubleVector2D> > clippedBbox;
+ QDoubleVector2D bboxLeftBoundWrapped = m_bboxLeftBoundWrapped;
+ bboxLeftBoundWrapped.setX(bboxLeftBoundWrapped.x() + double(m_wrapOffset - 1));
+ QDeclarativeGeoMapItemUtils::clipPolygon(wrappedBbox, p, clippedBbox, &bboxLeftBoundWrapped, false);
+
+ // 3) project bbox
+ QPainterPath ppi;
+
+ if ( !clippedBbox.size() ||
+ clippedBbox.first().size() < 3) {
+ sourceBounds_ = screenBounds_ = QRectF();
+ firstPointOffset_ = QPointF();
+ screenOutline_ = ppi;
+ return;
+ }
+
+ QDeclarativeGeoMapItemUtils::projectBbox(clippedBbox.first(), p, ppi); // Using first because a clipped box should always result in one polygon
+ const QRectF brect = ppi.boundingRect();
+ firstPointOffset_ = QPointF(brect.topLeft());
+ sourceBounds_ = brect;
+ screenOutline_ = ppi;
+
+ // 4) Set Screen bbox
+ screenBounds_ = brect;
+ sourceBounds_.setX(0);
+ sourceBounds_.setY(0);
+ sourceBounds_.setWidth(brect.width() + strokeWidth);
+ sourceBounds_.setHeight(brect.height() + strokeWidth);
+}
+
+/*
+ * QDeclarativePolygonMapItem Private Implementations
+ */
+
+QDeclarativePolylineMapItemPrivate::~QDeclarativePolylineMapItemPrivate() {}
+
+
+QDeclarativePolylineMapItemPrivateCPU::~QDeclarativePolylineMapItemPrivateCPU() {}
+
+QDeclarativePolylineMapItemPrivateOpenGLLineStrip::~QDeclarativePolylineMapItemPrivateOpenGLLineStrip() {}
+
+QDeclarativePolylineMapItemPrivateOpenGLExtruded::~QDeclarativePolylineMapItemPrivateOpenGLExtruded() {}
+
+/*
+ * QDeclarativePolygonMapItem Implementation
+ */
+
+struct PolylineBackendSelector
+{
+ PolylineBackendSelector()
+ {
+ backend = (qgetenv("QTLOCATION_OPENGL_ITEMS").toInt()) ? QDeclarativePolylineMapItem::OpenGLExtruded : QDeclarativePolylineMapItem::Software;
+ }
+ QDeclarativePolylineMapItem::Backend backend = QDeclarativePolylineMapItem::Software;
+};
+
+Q_GLOBAL_STATIC(PolylineBackendSelector, mapPolylineBackendSelector)
+
QDeclarativePolylineMapItem::QDeclarativePolylineMapItem(QQuickItem *parent)
-: QDeclarativeGeoMapItemBase(parent), line_(this), dirtyMaterial_(true), updatingGeometry_(false)
+: QDeclarativeGeoMapItemBase(parent),
+ m_line(this),
+ m_dirtyMaterial(true),
+ m_updatingGeometry(false),
+ m_d(new QDeclarativePolylineMapItemPrivateCPU(*this))
{
m_itemType = QGeoMap::MapPolyline;
- geopath_ = QGeoPathEager();
+ m_geopath = QGeoPathEager();
setFlag(ItemHasContents, true);
- QObject::connect(&line_, SIGNAL(colorChanged(QColor)),
+ QObject::connect(&m_line, SIGNAL(colorChanged(QColor)),
this, SLOT(updateAfterLinePropertiesChanged()));
- QObject::connect(&line_, SIGNAL(widthChanged(qreal)),
+ QObject::connect(&m_line, SIGNAL(widthChanged(qreal)),
this, SLOT(updateAfterLinePropertiesChanged()));
+ setBackend(mapPolylineBackendSelector->backend);
}
QDeclarativePolylineMapItem::~QDeclarativePolylineMapItem()
@@ -768,9 +976,7 @@ QDeclarativePolylineMapItem::~QDeclarativePolylineMapItem()
*/
void QDeclarativePolylineMapItem::updateAfterLinePropertiesChanged()
{
- // mark dirty just in case we're a width change
- geometry_.markSourceDirty();
- polishAndUpdate();
+ m_d->onLinePropertiesChanged();
}
/*!
@@ -779,11 +985,8 @@ void QDeclarativePolylineMapItem::updateAfterLinePropertiesChanged()
void QDeclarativePolylineMapItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map)
{
QDeclarativeGeoMapItemBase::setMap(quickMap,map);
- if (map) {
- regenerateCache();
- geometry_.markSourceDirty();
- polishAndUpdate();
- }
+ if (map)
+ m_d->onMapSet();
}
/*!
@@ -795,7 +998,7 @@ void QDeclarativePolylineMapItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *
QJSValue QDeclarativePolylineMapItem::path() const
{
- return fromList(this, geopath_.path());
+ return fromList(this, m_geopath.path());
}
void QDeclarativePolylineMapItem::setPath(const QJSValue &value)
@@ -807,7 +1010,7 @@ void QDeclarativePolylineMapItem::setPath(const QJSValue &value)
}
/*!
- \qmlmethod int MapPolyline::setPath(geopath path)
+ \qmlmethod void MapPolyline::setPath(geopath path)
Sets the \a path using a geopath type.
@@ -817,13 +1020,11 @@ void QDeclarativePolylineMapItem::setPath(const QJSValue &value)
*/
void QDeclarativePolylineMapItem::setPath(const QGeoPath &path)
{
- if (geopath_.path() == path.path())
+ if (m_geopath.path() == path.path())
return;
- geopath_ = QGeoPathEager(path);
- regenerateCache();
- geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft());
- markSourceDirtyAndUpdate();
+ m_geopath = QGeoPathEager(path);
+ m_d->onGeoGeometryChanged();
emit pathChanged();
}
@@ -832,14 +1033,12 @@ void QDeclarativePolylineMapItem::setPath(const QGeoPath &path)
*/
void QDeclarativePolylineMapItem::setPathFromGeoList(const QList<QGeoCoordinate> &path)
{
- if (geopath_.path() == path)
+ if (m_geopath.path() == path)
return;
- geopath_.setPath(path);
+ m_geopath.setPath(path);
- regenerateCache();
- geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft());
- markSourceDirtyAndUpdate();
+ m_d->onGeoGeometryChanged();
emit pathChanged();
}
@@ -854,7 +1053,7 @@ void QDeclarativePolylineMapItem::setPathFromGeoList(const QList<QGeoCoordinate>
*/
int QDeclarativePolylineMapItem::pathLength() const
{
- return geopath_.path().length();
+ return m_geopath.path().length();
}
/*!
@@ -869,11 +1068,9 @@ void QDeclarativePolylineMapItem::addCoordinate(const QGeoCoordinate &coordinate
if (!coordinate.isValid())
return;
- geopath_.addCoordinate(coordinate);
+ m_geopath.addCoordinate(coordinate);
- updateCache();
- geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft());
- markSourceDirtyAndUpdate();
+ m_d->onGeoGeometryUpdated();
emit pathChanged();
}
@@ -888,14 +1085,12 @@ void QDeclarativePolylineMapItem::addCoordinate(const QGeoCoordinate &coordinate
*/
void QDeclarativePolylineMapItem::insertCoordinate(int index, const QGeoCoordinate &coordinate)
{
- if (index < 0 || index > geopath_.path().length())
+ if (index < 0 || index > m_geopath.path().length())
return;
- geopath_.insertCoordinate(index, coordinate);
+ m_geopath.insertCoordinate(index, coordinate);
- regenerateCache();
- geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft());
- markSourceDirtyAndUpdate();
+ m_d->onGeoGeometryChanged();
emit pathChanged();
}
@@ -911,14 +1106,12 @@ void QDeclarativePolylineMapItem::insertCoordinate(int index, const QGeoCoordina
*/
void QDeclarativePolylineMapItem::replaceCoordinate(int index, const QGeoCoordinate &coordinate)
{
- if (index < 0 || index >= geopath_.path().length())
+ if (index < 0 || index >= m_geopath.path().length())
return;
- geopath_.replaceCoordinate(index, coordinate);
+ m_geopath.replaceCoordinate(index, coordinate);
- regenerateCache();
- geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft());
- markSourceDirtyAndUpdate();
+ m_d->onGeoGeometryChanged();
emit pathChanged();
}
@@ -933,10 +1126,10 @@ void QDeclarativePolylineMapItem::replaceCoordinate(int index, const QGeoCoordin
*/
QGeoCoordinate QDeclarativePolylineMapItem::coordinateAt(int index) const
{
- if (index < 0 || index >= geopath_.path().length())
+ if (index < 0 || index >= m_geopath.path().length())
return QGeoCoordinate();
- return geopath_.coordinateAt(index);
+ return m_geopath.coordinateAt(index);
}
/*!
@@ -948,7 +1141,7 @@ QGeoCoordinate QDeclarativePolylineMapItem::coordinateAt(int index) const
*/
bool QDeclarativePolylineMapItem::containsCoordinate(const QGeoCoordinate &coordinate)
{
- return geopath_.containsCoordinate(coordinate);
+ return m_geopath.containsCoordinate(coordinate);
}
/*!
@@ -963,13 +1156,12 @@ bool QDeclarativePolylineMapItem::containsCoordinate(const QGeoCoordinate &coord
*/
void QDeclarativePolylineMapItem::removeCoordinate(const QGeoCoordinate &coordinate)
{
- int length = geopath_.path().length();
- geopath_.removeCoordinate(coordinate);
- if (geopath_.path().length() == length)
+ int length = m_geopath.path().length();
+ m_geopath.removeCoordinate(coordinate);
+ if (m_geopath.path().length() == length)
return;
- regenerateCache();
- markSourceDirtyAndUpdate();
+ m_d->onGeoGeometryChanged();
emit pathChanged();
}
@@ -986,14 +1178,12 @@ void QDeclarativePolylineMapItem::removeCoordinate(const QGeoCoordinate &coordin
*/
void QDeclarativePolylineMapItem::removeCoordinate(int index)
{
- if (index < 0 || index >= geopath_.path().length())
+ if (index < 0 || index >= m_geopath.path().length())
return;
- geopath_.removeCoordinate(index);
+ m_geopath.removeCoordinate(index);
- regenerateCache();
- geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft());
- markSourceDirtyAndUpdate();
+ m_d->onGeoGeometryChanged();
emit pathChanged();
}
@@ -1013,7 +1203,47 @@ void QDeclarativePolylineMapItem::removeCoordinate(int index)
QDeclarativeMapLineProperties *QDeclarativePolylineMapItem::line()
{
- return &line_;
+ return &m_line;
+}
+
+/*!
+ \qmlproperty MapPolyline.Backend QtLocation::MapPolyline::backend
+
+ This property holds which backend is in use to render the map item.
+ Valid values are \b MapPolyline.Software and \b{MapPolyline.OpenGLLineStrip}
+ and \b{MapPolyline.OpenGLExtruded}.
+ The default value is \b{MapPolyline.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
+*/
+QDeclarativePolylineMapItem::Backend QDeclarativePolylineMapItem::backend() const
+{
+ return m_backend;
+}
+
+void QDeclarativePolylineMapItem::setBackend(QDeclarativePolylineMapItem::Backend b)
+{
+ if (b == m_backend)
+ return;
+ m_backend = b;
+ QScopedPointer<QDeclarativePolylineMapItemPrivate> d((m_backend == Software)
+ ? static_cast<QDeclarativePolylineMapItemPrivate *>(new QDeclarativePolylineMapItemPrivateCPU(*this))
+ : ((m_backend == OpenGLExtruded)
+ ? static_cast<QDeclarativePolylineMapItemPrivate * >(new QDeclarativePolylineMapItemPrivateOpenGLExtruded(*this))
+ : static_cast<QDeclarativePolylineMapItemPrivate * >(new QDeclarativePolylineMapItemPrivateOpenGLLineStrip(*this))));
+ m_d.swap(d);
+ m_d->onGeoGeometryChanged();
+ emit backendChanged();
}
/*!
@@ -1021,7 +1251,7 @@ QDeclarativeMapLineProperties *QDeclarativePolylineMapItem::line()
*/
void QDeclarativePolylineMapItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
{
- if (!map() || !geopath_.isValid() || updatingGeometry_ || newGeometry.topLeft() == oldGeometry.topLeft()) {
+ if (newGeometry.topLeft() == oldGeometry.topLeft() || !map() || !m_geopath.isValid() || m_updatingGeometry) {
QDeclarativeGeoMapItemBase::geometryChanged(newGeometry, oldGeometry);
return;
}
@@ -1035,10 +1265,8 @@ void QDeclarativePolylineMapItem::geometryChanged(const QRectF &newGeometry, con
if (offsetLati == 0.0 && offsetLongi == 0.0)
return;
- geopath_.translate(offsetLati, offsetLongi);
- regenerateCache();
- geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft());
- markSourceDirtyAndUpdate();
+ m_geopath.translate(offsetLati, offsetLongi);
+ m_d->onGeoGeometryChanged();
emit pathChanged();
// Not calling QDeclarativeGeoMapItemBase::geometryChanged() as it will be called from a nested
@@ -1050,68 +1278,78 @@ void QDeclarativePolylineMapItem::geometryChanged(const QRectF &newGeometry, con
*/
void QDeclarativePolylineMapItem::afterViewportChanged(const QGeoMapViewportChangeEvent &event)
{
- if (event.mapSize.width() <= 0 || event.mapSize.height() <= 0)
+ if (event.mapSize.isEmpty())
return;
- geometry_.setPreserveGeometry(true, geometry_.geoLeftBound());
- markSourceDirtyAndUpdate();
+ m_d->afterViewportChanged();
}
/*!
\internal
*/
-void QDeclarativePolylineMapItem::regenerateCache()
+void QDeclarativePolylineMapItem::updatePolish()
{
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->updatePolish();
}
-/*!
- \internal
-*/
-void QDeclarativePolylineMapItem::updateCache()
+void QDeclarativePolylineMapItem::updateLineStyleParameter(QGeoMapParameter *p,
+ const char *propertyName,
+ bool update)
{
- if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator)
- return;
- const QGeoProjectionWebMercator &p = static_cast<const QGeoProjectionWebMercator&>(map()->geoProjection());
- geopathProjected_ << p.geoToMapProjection(geopath_.path().last());
+ static const QByteArrayList acceptedParameterTypes = QByteArrayList()
+ << QByteArrayLiteral("lineCap")
+ << QByteArrayLiteral("pen");
+ switch (acceptedParameterTypes.indexOf(QByteArray(propertyName))) {
+ case -1:
+ qWarning() << "Invalid property " << QLatin1String(propertyName) << " for parameter lineStyle";
+ break;
+ case 0: // lineCap
+ {
+ const QVariant lineCap = p->property("lineCap");
+ m_d->m_penCapStyle = lineCap.value<Qt::PenCapStyle>(); // if invalid, will return 0 == FlatCap
+ if (update)
+ markSourceDirtyAndUpdate();
+ break;
+ }
+ case 1: // penStyle
+ {
+ const QVariant penStyle = p->property("pen");
+ m_d->m_penStyle = penStyle.value<Qt::PenStyle>();
+ if (m_d->m_penStyle == Qt::NoPen)
+ m_d->m_penStyle = Qt::SolidLine;
+ if (update)
+ markSourceDirtyAndUpdate();
+ break;
+ }
+ }
}
-/*!
- \internal
-*/
-void QDeclarativePolylineMapItem::updatePolish()
+void QDeclarativePolylineMapItem::updateLineStyleParameter(QGeoMapParameter *p, const char *propertyName)
{
- if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator)
- return;
- if (geopath_.path().length() == 0) { // Possibly cleared
- geometry_.clear();
- setWidth(0);
- setHeight(0);
- return;
- }
-
- QScopedValueRollback<bool> rollback(updatingGeometry_);
- updatingGeometry_ = true;
-
- geometry_.updateSourcePoints(*map(), geopathProjected_, geopath_.boundingGeoRectangle().topLeft());
- geometry_.updateScreenPoints(*map(), line_.width());
-
- setWidth(geometry_.sourceBoundingBox().width() + 2 * line_.width());
- setHeight(geometry_.sourceBoundingBox().height() + 2 * line_.width());
+ updateLineStyleParameter(p, propertyName, true);
+}
- setPositionOnMap(geometry_.origin(), -1 * geometry_.sourceBoundingBox().topLeft() + QPointF(line_.width(), line_.width()));
+void QDeclarativePolylineMapItem::componentComplete()
+{
+ QQuickItem::componentComplete();
+ // Set up Dynamic Parameters
+ QList<QGeoMapParameter *> dynamicParameters = quickChildren<QGeoMapParameter>();
+ for (QGeoMapParameter *p : qAsConst(dynamicParameters)) {
+ if (p->type() == QLatin1String("lineStyle")) {
+ updateLineStyleParameter(p, "lineCap", false);
+ updateLineStyleParameter(p, "pen", false);
+ connect(p, &QGeoMapParameter::propertyUpdated,
+ this, static_cast<void (QDeclarativePolylineMapItem::*)(QGeoMapParameter *, const char *)>(&QDeclarativePolylineMapItem::updateLineStyleParameter));
+ markSourceDirtyAndUpdate();
+ }
+ }
}
void QDeclarativePolylineMapItem::markSourceDirtyAndUpdate()
{
- geometry_.markSourceDirty();
- polishAndUpdate();
+ m_d->markSourceDirtyAndUpdate();
}
/*!
@@ -1119,32 +1357,17 @@ void QDeclarativePolylineMapItem::markSourceDirtyAndUpdate()
*/
QSGNode *QDeclarativePolylineMapItem::updateMapItemPaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
{
- Q_UNUSED(data);
-
- MapPolylineNode *node = static_cast<MapPolylineNode *>(oldNode);
-
- if (!node) {
- node = new MapPolylineNode();
- }
-
- //TODO: update only material
- if (geometry_.isScreenDirty() || dirtyMaterial_ || !oldNode) {
- node->update(line_.color(), &geometry_);
- geometry_.setPreserveGeometry(false);
- geometry_.markClean();
- dirtyMaterial_ = false;
- }
- return node;
+ return m_d->updateMapItemPaintNode(oldNode, data);
}
bool QDeclarativePolylineMapItem::contains(const QPointF &point) const
{
- return geometry_.contains(point);
+ return m_d->contains(point);
}
const QGeoShape &QDeclarativePolylineMapItem::geoShape() const
{
- return geopath_;
+ return m_geopath;
}
void QDeclarativePolylineMapItem::setGeoShape(const QGeoShape &shape)
@@ -1254,4 +1477,608 @@ void MapPolylineNode::update(const QColor &fillColor,
}
}
+MapPolylineNodeOpenGLLineStrip::MapPolylineNodeOpenGLLineStrip()
+: geometry_(QSGGeometry::defaultAttributes_Point2D(), 0)
+{
+ geometry_.setDrawingMode(QSGGeometry::DrawLineStrip);
+ QSGGeometryNode::setMaterial(&fill_material_);
+ QSGGeometryNode::setGeometry(&geometry_);
+}
+
+MapPolylineNodeOpenGLLineStrip::~MapPolylineNodeOpenGLLineStrip()
+{
+
+}
+
+void MapPolylineNodeOpenGLLineStrip::update(const QColor &fillColor,
+ const qreal lineWidth,
+ const QGeoMapPolylineGeometryOpenGL *shape,
+ const QMatrix4x4 &geoProjection,
+ const QDoubleVector3D &center,
+ const Qt::PenCapStyle /*capStyle*/)
+{
+ if (shape->m_screenVertices->size() < 2) {
+ setSubtreeBlocked(true);
+ return;
+ } else {
+ setSubtreeBlocked(false);
+ }
+
+ QSGGeometry *fill = QSGGeometryNode::geometry();
+ if (shape->m_dataChanged) {
+ shape->allocateAndFillLineStrip(fill);
+ markDirty(DirtyGeometry);
+ shape->m_dataChanged = false;
+ }
+ fill->setLineWidth(lineWidth);
+ fill_material_.setLineWidth(lineWidth); // to make the material not compare equal if linewidth changes
+
+// if (fillColor != fill_material_.color())
+ {
+ fill_material_.setWrapOffset(shape->m_wrapOffset - 1);
+ fill_material_.setColor(fillColor);
+ fill_material_.setGeoProjection(geoProjection);
+ fill_material_.setCenter(center);
+ setMaterial(&fill_material_);
+ markDirty(DirtyMaterial);
+ }
+}
+
+MapPolylineShaderLineStrip::MapPolylineShaderLineStrip() : QSGMaterialShader(*new QSGMaterialShaderPrivate)
+{
+
+}
+
+void MapPolylineShaderLineStrip::updateState(const QSGMaterialShader::RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
+{
+ Q_ASSERT(oldEffect == nullptr || newEffect->type() == oldEffect->type());
+ MapPolylineMaterial *oldMaterial = static_cast<MapPolylineMaterial *>(oldEffect);
+ MapPolylineMaterial *newMaterial = static_cast<MapPolylineMaterial *>(newEffect);
+
+ const QColor &c = newMaterial->color();
+ const QMatrix4x4 &geoProjection = newMaterial->geoProjection();
+ const QDoubleVector3D &center = 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()));
+}
+
+const char * const *MapPolylineShaderLineStrip::attributeNames() const
+{
+ static char const *const attr[] = { "vertex", nullptr };
+ return attr;
+}
+
+QSGMaterialShader *MapPolylineMaterial::createShader() const
+{
+ return new MapPolylineShaderLineStrip();
+}
+
+QSGMaterialType *MapPolylineMaterial::type() const
+{
+ static QSGMaterialType type;
+ return &type;
+}
+
+int MapPolylineMaterial::compare(const QSGMaterial *other) const
+{
+ const MapPolylineMaterial &o = *static_cast<const MapPolylineMaterial *>(other);
+ if (o.m_center == m_center && o.m_geoProjection == m_geoProjection && o.m_wrapOffset == m_wrapOffset && o.m_lineWidth == m_lineWidth)
+ return QSGFlatColorMaterial::compare(other);
+ return -1;
+}
+
+const QSGGeometry::AttributeSet &MapPolylineNodeOpenGLExtruded::attributesMapPolylineTriangulated()
+{
+ return MapPolylineEntry::attributes();
+}
+
+MapPolylineNodeOpenGLExtruded::MapPolylineNodeOpenGLExtruded()
+: m_geometryTriangulating(MapPolylineNodeOpenGLExtruded::attributesMapPolylineTriangulated(),
+ 0 /* vtx cnt */, 0 /* index cnt */, QSGGeometry::UnsignedIntType /* index type */)
+{
+ m_geometryTriangulating.setDrawingMode(QSGGeometry::DrawTriangles);
+ QSGGeometryNode::setMaterial(&fill_material_);
+ QSGGeometryNode::setGeometry(&m_geometryTriangulating);
+}
+
+MapPolylineNodeOpenGLExtruded::~MapPolylineNodeOpenGLExtruded()
+{
+
+}
+
+bool QGeoMapPolylineGeometryOpenGL::allocateAndFillEntries(QSGGeometry *geom,
+ bool closed,
+ unsigned int zoom) const
+{
+ // Select LOD. Generate if not present. Assign it to m_screenVertices;
+ if (m_dataChanged) {
+ // it means that the data really changed.
+ // So synchronously produce LOD 1, and enqueue the requested one if != 0 or 1.
+ // Select 0 if 0 is requested, or 1 in all other cases.
+ selectLODOnDataChanged(zoom, m_bboxLeftBoundWrapped.x());
+ } else {
+ // Data has not changed, but active LOD != requested LOD.
+ // So, if there are no active tasks, try to change to the correct one.
+ if (!selectLODOnLODMismatch(zoom, m_bboxLeftBoundWrapped.x(), closed))
+ return false;
+ }
+
+ const QVector<QDeclarativeGeoMapItemUtils::vec2> &v = *m_screenVertices;
+ if (v.size() < 2) {
+ geom->allocate(0, 0);
+ return true;
+ }
+ const int numSegments = (v.size() - 1);
+
+ const int numIndices = numSegments * 6; // six vertices per line segment
+ geom->allocate(numIndices);
+ MapPolylineNodeOpenGLExtruded::MapPolylineEntry *vertices =
+ static_cast<MapPolylineNodeOpenGLExtruded::MapPolylineEntry *>(geom->vertexData());
+
+ for (int i = 0; i < numSegments; ++i) {
+ MapPolylineNodeOpenGLExtruded::MapPolylineEntry e;
+ const QDeclarativeGeoMapItemUtils::vec2 &cur = v[i];
+ const QDeclarativeGeoMapItemUtils::vec2 &next = v[i+1];
+ e.triangletype = 1.0;
+ e.next = next;
+ e.prev = cur;
+ e.pos = cur;
+ e.direction = 1.0;
+ e.vertextype = -1.0;
+ vertices[i*6] = e;
+ e.direction = -1.0;
+ vertices[i*6+1] = e;
+ e.pos = next;
+ e.vertextype = 1.0;
+ vertices[i*6+2] = e;
+
+ // Second tri
+ e.triangletype = -1.0;
+ e.direction = -1.0;
+ vertices[i*6+3] = e;
+ e.direction = 1.0;
+ vertices[i*6+4] = e;
+ e.pos = cur;
+ e.vertextype = -1.0;
+ vertices[i*6+5] = e;
+
+ if (i != 0) {
+ vertices[i*6].prev = vertices[i*6+1].prev = vertices[i*6+5].prev = v[i-1];
+ } else {
+ if (closed) {
+ vertices[i*6].prev = vertices[i*6+1].prev = vertices[i*6+5].prev = v[numSegments - 1];
+ } else {
+ vertices[i*6].triangletype = vertices[i*6+1].triangletype = vertices[i*6+5].triangletype = 2.0;
+ }
+ }
+ if (i != numSegments - 1) {
+ vertices[i*6+2].next = vertices[i*6+3].next = vertices[i*6+4].next = v[i+2];
+ } else {
+ if (closed) {
+ vertices[i*6+2].next = vertices[i*6+3].next = vertices[i*6+4].next = v[1];
+ } else {
+ vertices[i*6+2].triangletype = vertices[i*6+3].triangletype = vertices[i*6+4].triangletype = 3.0;
+ }
+ }
+ }
+ return true;
+}
+
+void QGeoMapPolylineGeometryOpenGL::allocateAndFillLineStrip(QSGGeometry *geom,
+ int lod) const
+{
+ // Select LOD. Generate if not present. Assign it to m_screenVertices;
+ Q_UNUSED(lod)
+
+ const QVector<QDeclarativeGeoMapItemUtils::vec2> &vx = *m_screenVertices;
+ geom->allocate(vx.size());
+
+ QSGGeometry::Point2D *pts = geom->vertexDataAsPoint2D();
+ for (int i = 0; i < vx.size(); ++i)
+ pts[i].set(vx[i].x, vx[i].y);
+}
+
+void MapPolylineNodeOpenGLExtruded::update(const QColor &fillColor,
+ const float lineWidth,
+ const QGeoMapPolylineGeometryOpenGL *shape,
+ const QMatrix4x4 geoProjection,
+ const QDoubleVector3D center,
+ const Qt::PenCapStyle capStyle,
+ bool closed,
+ unsigned int zoom)
+{
+ // shape->size() == number of triangles
+ if (shape->m_screenVertices->size() < 2
+ || lineWidth < 0.5 || fillColor.alpha() == 0) { // number of points
+ setSubtreeBlocked(true);
+ return;
+ } else {
+ setSubtreeBlocked(false);
+ }
+
+ QSGGeometry *fill = QSGGeometryNode::geometry();
+ if (shape->m_dataChanged || !shape->isLODActive(zoom) || !fill->vertexCount()) { // fill->vertexCount for when node gets destroyed by MapItemBase bcoz of opacity, then recreated.
+ if (shape->allocateAndFillEntries(fill, closed, zoom)) {
+ markDirty(DirtyGeometry);
+ shape->m_dataChanged = false;
+ }
+ }
+
+ // Update this
+// if (fillColor != fill_material_.color())
+ {
+ fill_material_.setWrapOffset(shape->m_wrapOffset - 1);
+ fill_material_.setColor(fillColor);
+ fill_material_.setGeoProjection(geoProjection);
+ fill_material_.setCenter(center);
+ fill_material_.setLineWidth(lineWidth);
+ fill_material_.setMiter(capStyle != Qt::FlatCap);
+ setMaterial(&fill_material_);
+ markDirty(DirtyMaterial);
+ }
+}
+
+MapPolylineShaderExtruded::MapPolylineShaderExtruded() : QSGMaterialShader(*new QSGMaterialShaderPrivate)
+{
+
+}
+
+void MapPolylineShaderExtruded::updateState(const QSGMaterialShader::RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
+{
+ Q_ASSERT(oldEffect == nullptr || newEffect->type() == oldEffect->type());
+ MapPolylineMaterialExtruded *oldMaterial = static_cast<MapPolylineMaterialExtruded *>(oldEffect);
+ MapPolylineMaterialExtruded *newMaterial = static_cast<MapPolylineMaterialExtruded *>(newEffect);
+
+ const QColor &c = newMaterial->color();
+ const QMatrix4x4 &geoProjection = newMaterial->geoProjection();
+ const QDoubleVector3D &center = 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());
+ }
+
+ // ToDo: dirty-flag all this
+ program()->setUniformValue(m_mapProjection_id, geoProjection);
+
+ program()->setUniformValue(m_center_id, vecCenter);
+ program()->setUniformValue(m_center_lowpart_id, vecCenter_lowpart);
+ program()->setUniformValue(m_miter_id, newMaterial->miter());
+ program()->setUniformValue(m_lineWidth_id, newMaterial->lineWidth());
+ program()->setUniformValue(m_wrapOffset_id, float(newMaterial->wrapOffset()));
+
+ const QRectF viewportRect = state.viewportRect();
+ const float aspect = float(viewportRect.width() / viewportRect.height());
+ program()->setUniformValue(m_aspect_id, aspect);
+}
+
+const char * const *MapPolylineShaderExtruded::attributeNames() const
+{
+ return MapPolylineNodeOpenGLExtruded::MapPolylineEntry::attributeNames();
+}
+
+QSGMaterialShader *MapPolylineMaterialExtruded::createShader() const
+{
+ return new MapPolylineShaderExtruded();
+}
+
+QSGMaterialType *MapPolylineMaterialExtruded::type() const
+{
+ static QSGMaterialType type;
+ return &type;
+}
+
+int MapPolylineMaterialExtruded::compare(const QSGMaterial *other) const
+{
+ const MapPolylineMaterialExtruded &o = *static_cast<const MapPolylineMaterialExtruded *>(other);
+ if (o.m_miter == m_miter)
+ return MapPolylineMaterial::compare(other);
+ return -1;
+}
+
+const char *MapPolylineShaderExtruded::vertexShaderMiteredSegments() const
+{
+ return
+ "attribute highp vec4 vertex;\n"
+ "attribute highp vec4 previous;\n"
+ "attribute highp vec4 next;\n"
+ "attribute lowp float direction;\n"
+ "attribute lowp float triangletype;\n"
+ "attribute lowp float vertextype;\n" // -1.0 if it is the "left" end of the segment, 1.0 if it is the "right" end.
+ "\n"
+ "uniform highp mat4 qt_Matrix;\n"
+ "uniform highp mat4 mapProjection;\n"
+ "uniform highp vec3 center;\n"
+ "uniform highp vec3 center_lowpart;\n"
+ "uniform lowp float lineWidth;\n"
+ "uniform lowp float aspect;\n"
+ "uniform lowp int miter;\n" // currently unused
+ "uniform lowp vec4 color;\n"
+ "uniform lowp float wrapOffset;\n"
+ "\n"
+ "varying vec4 primitivecolor;\n"
+ "\n"
+ " \n"
+ "vec4 wrapped(in vec4 v) { return vec4(v.x + wrapOffset, v.y, 0.0, 1.0); }\n"
+ "void main() {\n" // ln 22
+ " primitivecolor = color;\n"
+ " vec2 aspectVec = vec2(aspect, 1.0);\n"
+ " mat4 projViewModel = qt_Matrix * mapProjection;\n"
+ " vec4 cur = wrapped(vertex) - vec4(center, 0.0);\n"
+ " cur = cur - vec4(center_lowpart, 0.0);\n"
+ " vec4 prev = wrapped(previous) - vec4(center, 0.0);\n"
+ " prev = prev - vec4(center_lowpart, 0.0);\n"
+ " vec4 nex = wrapped(next) - vec4(center, 0.0);\n"
+ " nex = nex - vec4(center_lowpart, 0.0);\n"
+ "\n"
+ " vec4 centerProjected = projViewModel * vec4(center, 1.0);\n"
+ " vec4 previousProjected = projViewModel * prev;\n"
+ " vec4 currentProjected = projViewModel * cur;\n"
+ " vec4 nextProjected = projViewModel * nex;\n"
+ "\n"
+ " //get 2D screen space with W divide and aspect correction\n"
+ " vec2 currentScreen = (currentProjected.xy / currentProjected.w) * aspectVec;\n"
+ " vec2 previousScreen = (previousProjected.xy / previousProjected.w) * aspectVec;\n"
+ " vec2 nextScreen = (nextProjected.xy / nextProjected.w) * aspectVec;\n"
+ " float len = (lineWidth);\n"
+ " float orientation = direction;\n"
+ " bool clipped = false;\n"
+ " bool otherEndBelowFrustum = false;\n"
+ " //starting point uses (next - current)\n"
+ " vec2 dir = vec2(0.0);\n"
+ " if (vertextype < 0.0) {\n"
+ " dir = normalize(nextScreen - currentScreen);\n"
+ " if (nextProjected.z < 0.0) dir = -dir;\n"
+ " } else { \n"
+ " dir = normalize(currentScreen - previousScreen);\n"
+ " if (previousProjected.z < 0.0) dir = -dir;\n"
+ " }\n"
+ // first, clip current, and make sure currentProjected.z is > 0
+ " if (currentProjected.z < 0.0) {\n"
+ " if ((nextProjected.z > 0.0 && vertextype < 0.0) || (vertextype > 0.0 && previousProjected.z > 0.0)) {\n"
+ " dir = -dir;\n"
+ " clipped = true;\n"
+ " if (vertextype < 0.0 && nextProjected.y / nextProjected.w < -1.0) otherEndBelowFrustum = true;\n"
+ " else if (vertextype > 0.0 && previousProjected.y / previousProjected.w < -1.0) otherEndBelowFrustum = true;\n"
+ " } else {\n"
+ " primitivecolor = vec4(0.0,0.0,0.0,0.0);\n"
+ " gl_Position = vec4(-10000000.0, -1000000000.0, -1000000000.0, 1);\n" // get the vertex out of the way if the segment is fully invisible
+ " return;\n"
+ " }\n"
+ " } else if (triangletype < 2.0) {\n" // vertex in the view, try to miter
+ " //get directions from (C - B) and (B - A)\n"
+ " vec2 dirA = normalize((currentScreen - previousScreen));\n"
+ " if (previousProjected.z < 0.0) dirA = -dirA;\n"
+ " vec2 dirB = normalize((nextScreen - currentScreen));\n"
+ " //now compute the miter join normal and length\n"
+ " if (nextProjected.z < 0.0) dirB = -dirB;\n"
+ " vec2 tangent = normalize(dirA + dirB);\n"
+ " vec2 perp = vec2(-dirA.y, dirA.x);\n"
+ " vec2 vmiter = vec2(-tangent.y, tangent.x);\n"
+ " len = lineWidth / dot(vmiter, perp);\n"
+ // The following is an attempt to have a segment-length based miter threshold.
+ // A mediocre workaround until better mitering will be added.
+ " float lenTreshold = clamp( min(length((currentProjected.xy - previousProjected.xy) / aspectVec),"
+ " length((nextProjected.xy - currentProjected.xy) / aspectVec)), 3.0, 6.0 ) * 0.5;\n"
+ " if (len < lineWidth * lenTreshold && len > -lineWidth * lenTreshold \n"
+ " ) {\n"
+ " dir = tangent;\n"
+ " } else {\n"
+ " len = lineWidth;\n"
+ " }\n"
+ " }\n"
+ " vec4 offset;\n"
+ " if (!clipped) {\n"
+ " vec2 normal = normalize(vec2(-dir.y, dir.x));\n"
+ " normal *= len;\n" // fracZL apparently was needed before the (-2.0 / qt_Matrix[1][1]) factor was introduced
+ " normal /= aspectVec;\n" // straighten the normal up again
+ " float scaleFactor = currentProjected.w / centerProjected.w;\n"
+ " offset = vec4(normal * orientation * scaleFactor * (centerProjected.w / (-2.0 / qt_Matrix[1][1])), 0.0, 0.0);\n" // ToDo: figure out why (-2.0 / qt_Matrix[1][1]), that is empirically what works
+ " gl_Position = currentProjected + offset;\n"
+ " } else {\n"
+ " if (otherEndBelowFrustum) offset = vec4((dir * 1.0) / aspectVec, 0.0, 0.0);\n" // the if is necessary otherwise it seems the direction vector still flips in some obscure cases.
+ " else offset = vec4((dir * 500000000000.0) / aspectVec, 0.0, 0.0);\n" // Hack alert: just 1 triangle, long enough to look like a rectangle.
+ " if (vertextype < 0.0) gl_Position = nextProjected - offset; else gl_Position = previousProjected + offset;\n"
+ " }\n"
+ "}\n";
+}
+
+QVector<QDeclarativeGeoMapItemUtils::vec2> QGeoMapItemLODGeometry::getSimplified(
+ QVector<QDeclarativeGeoMapItemUtils::vec2> &wrappedPath, // reference as it gets copied in the nested call
+ double leftBoundWrapped,
+ unsigned int zoom)
+{
+ // Try a simplify step
+ QList<QDoubleVector2D> data;
+ for (auto e: wrappedPath)
+ data << e.toDoubleVector2D();
+ const QList<QDoubleVector2D> simplified = QGeoSimplify::geoSimplifyZL(data,
+ leftBoundWrapped,
+ zoom);
+
+ data.clear();
+ QVector<QDeclarativeGeoMapItemUtils::vec2> simple;
+ for (auto e: simplified)
+ simple << e;
+ return simple;
+}
+
+
+bool QGeoMapItemLODGeometry::isLODActive(unsigned int lod) const
+{
+ return m_screenVertices == m_verticesLOD[zoomToLOD(lod)].data();
+}
+
+class PolylineSimplifyTask : public QRunnable
+{
+public:
+ PolylineSimplifyTask(const QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2> > &input, // reference as it gets copied in the nested call
+ const QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2> > &output,
+ double leftBound,
+ unsigned int zoom,
+ QSharedPointer<unsigned int> &working)
+ : m_zoom(zoom)
+ , m_leftBound(leftBound)
+ , m_input(input)
+ , m_output(output)
+ , m_working(working)
+ {
+ Q_ASSERT(!input.isNull());
+ Q_ASSERT(!output.isNull());
+ }
+
+ ~PolylineSimplifyTask() override;
+
+ void run() override
+ {
+ // Skip sending notifications for now. Updated data will be picked up eventually.
+ // ToDo: figure out how to connect a signal from here to a slot in the item.
+ *m_working = QGeoMapPolylineGeometryOpenGL::zoomToLOD(m_zoom);
+ const QVector<QDeclarativeGeoMapItemUtils::vec2> res =
+ QGeoMapPolylineGeometryOpenGL::getSimplified( *m_input,
+ m_leftBound,
+ QGeoMapPolylineGeometryOpenGL::zoomForLOD(m_zoom));
+ *m_output = res;
+ *m_working = 0;
+ }
+
+ unsigned int m_zoom;
+ double m_leftBound;
+ QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2> > m_input, m_output;
+ QSharedPointer<unsigned int> m_working;
+};
+
+void QGeoMapItemLODGeometry::enqueueSimplificationTask(const QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2> > &input,
+ const QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2> > &output,
+ double leftBound,
+ unsigned int zoom,
+ QSharedPointer<unsigned int> &working)
+{
+ Q_ASSERT(!input.isNull());
+ Q_ASSERT(!output.isNull());
+ PolylineSimplifyTask *task = new PolylineSimplifyTask(input,
+ output,
+ leftBound,
+ zoom,
+ working);
+ threadPool->start(task);
+}
+
+PolylineSimplifyTask::~PolylineSimplifyTask() {}
+
+void QGeoMapItemLODGeometry::selectLOD(unsigned int zoom, double leftBound, bool /* closed */) // closed to tell if this is a polygon or a polyline.
+{
+ unsigned int requestedLod = zoomToLOD(zoom);
+ if (!m_verticesLOD[requestedLod].isNull()) {
+ m_screenVertices = m_verticesLOD[requestedLod].data();
+ } else if (!m_verticesLOD.at(0)->isEmpty()) {
+ // if here, zoomToLOD != 0 and no current working task.
+ // So select the last filled LOD != m_working (lower-bounded by 1,
+ // guaranteed to exist), and enqueue the right one
+ m_verticesLOD[requestedLod] = QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2>>(
+ new QVector<QDeclarativeGeoMapItemUtils::vec2>);
+
+ for (unsigned int i = requestedLod - 1; i >= 1; i--) {
+ if (*m_working != i && !m_verticesLOD[i].isNull()) {
+ m_screenVertices = m_verticesLOD[i].data();
+ break;
+ } else if (i == 1) {
+ // get 1 synchronously if not computed already
+ m_verticesLOD[1] = QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2>>(
+ new QVector<QDeclarativeGeoMapItemUtils::vec2>);
+ *m_verticesLOD[1] = getSimplified( *m_verticesLOD[0],
+ leftBound,
+ zoomForLOD(0));
+ if (requestedLod == 1)
+ return;
+ }
+ }
+
+ enqueueSimplificationTask( m_verticesLOD.at(0),
+ m_verticesLOD[requestedLod],
+ leftBound,
+ zoom,
+ m_working);
+
+ }
+}
+
+void QGeoMapItemLODGeometry::selectLODOnDataChanged(unsigned int zoom, double leftBound) const
+{
+ unsigned int lod = zoomToLOD(zoom);
+ if (lod > 0) {
+ // Generate ZL 1 as fallback for all cases != 0. Do not do if 0 is requested
+ // (= old behavior, LOD disabled)
+ m_verticesLOD[1] = QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2>>(
+ new QVector<QDeclarativeGeoMapItemUtils::vec2>);
+ *m_verticesLOD[1] = getSimplified( *m_verticesLOD[0],
+ leftBound,
+ zoomForLOD(0));
+ }
+ if (lod > 1) {
+ if (!m_verticesLOD[lod])
+ m_verticesLOD[lod] = QSharedPointer<QVector<QDeclarativeGeoMapItemUtils::vec2>>(
+ new QVector<QDeclarativeGeoMapItemUtils::vec2>);
+ enqueueSimplificationTask( m_verticesLOD.at(0),
+ m_verticesLOD[lod],
+ leftBound,
+ zoom,
+ m_working);
+ }
+ m_screenVertices = m_verticesLOD[qMin<unsigned int>(lod, 1)].data(); // return only 0,1 synchronously
+}
+
+unsigned int QGeoMapItemLODGeometry::zoomToLOD(unsigned int zoom)
+{
+ unsigned int res;
+ if (zoom > 20)
+ res = 0;
+ else
+ res = qBound<unsigned int>(3, zoom, 20) / 3; // bound LOD'ing between ZL 3 and 20. Every 3 ZoomLevels
+ return res;
+}
+
+unsigned int QGeoMapItemLODGeometry::zoomForLOD(unsigned int zoom)
+{
+ unsigned int res = (qBound<unsigned int>(3, zoom, 20) / 3) * 3;
+ if (zoom < 6)
+ return res;
+ return res + 1; // give more resolution when closing in
+}
+
QT_END_NAMESPACE