summaryrefslogtreecommitdiff
path: root/src/location/declarativemaps/qdeclarativepolylinemapitem.cpp
diff options
context:
space:
mode:
authorPaolo Angelelli <paolo.angelelli.qt@gmail.com>2019-11-27 15:37:07 +0100
committerpaolo <paolo.angelelli.qt@gmail.com>2020-02-11 11:46:08 +0100
commitd055098540df99a5d426360e9322c659e678e5ee (patch)
treee7e43a057b7e06814e2ef11eeddc52a4aac06cdb /src/location/declarativemaps/qdeclarativepolylinemapitem.cpp
parent614d67be158e3ef8443c1b7f3126303cfcf4becc (diff)
downloadqtlocation-d055098540df99a5d426360e9322c659e678e5ee.tar.gz
Enable mercator-to-screen projection in GLSL
With this change, all the geo-to-screen conversion, and the triangulation operations for geo polylines and geo polygon are performed either at set time or in the shader. A separate bounding box geometry is processed in the old way to provide a correct QtQuick Item geometry, that can be used for nesting mouse areas, performing translations, input event delivery, etc. With this approach, performance are improved by more than one order of magnitude in average, but complex geometries will of course benefit more. It also adds correct rendering support for polygons with holes, previously only rendered correctly by the MapboxGL plugin. The polyline shader has basic miter joins. The miter is skipped if the angle is too sharp to avoid complicating the implementation. This shader introduces some glitches when the polyline is minified, for which the real fix is to have LOD for the geometry, and render simplified geometries at low zoom levels (added in a subsequent patch). Note: this approach, at least in its current implementation, does not support enabling layers on individual items, only on the Map element. Task-number: QTBUG-49303 Task-number: QTBUG-38459 Change-Id: I0c2dc0bf364d32f74ca7c4014f6d66e6219c8ae4 Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'src/location/declarativemaps/qdeclarativepolylinemapitem.cpp')
-rw-r--r--src/location/declarativemaps/qdeclarativepolylinemapitem.cpp869
1 files changed, 745 insertions, 124 deletions
diff --git a/src/location/declarativemaps/qdeclarativepolylinemapitem.cpp b/src/location/declarativemaps/qdeclarativepolylinemapitem.cpp
index 10f3f0c3..7e484122 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,7 +60,9 @@
#include <QtPositioning/private/qclipperutils_p.h>
#include <QtPositioning/private/qgeopath_p.h>
+#include <QtQuick/private/qsgmaterialshader_p.h>
#include <array>
+#include <QtLocation/private/qgeomapparameter_p.h>
QT_BEGIN_NAMESPACE
@@ -125,7 +132,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 +362,6 @@ void QDeclarativeMapLineProperties::setWidth(qreal width)
emit widthChanged(width_);
}
-struct Vertex
-{
- QVector2D position;
-};
-
QGeoMapPolylineGeometry::QGeoMapPolylineGeometry()
{
}
@@ -661,7 +663,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 +721,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 +749,199 @@ 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);
+
+ m_screenVertices.clear();
+ for (const auto &v: qAsConst(wrappedPath)) m_screenVertices << 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 +953,7 @@ QDeclarativePolylineMapItem::~QDeclarativePolylineMapItem()
*/
void QDeclarativePolylineMapItem::updateAfterLinePropertiesChanged()
{
- // mark dirty just in case we're a width change
- geometry_.markSourceDirty();
- polishAndUpdate();
+ m_d->onLinePropertiesChanged();
}
/*!
@@ -779,11 +962,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 +975,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)
@@ -817,13 +997,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 +1010,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 +1030,7 @@ void QDeclarativePolylineMapItem::setPathFromGeoList(const QList<QGeoCoordinate>
*/
int QDeclarativePolylineMapItem::pathLength() const
{
- return geopath_.path().length();
+ return m_geopath.path().length();
}
/*!
@@ -869,11 +1045,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 +1062,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 +1083,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 +1103,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 +1118,7 @@ QGeoCoordinate QDeclarativePolylineMapItem::coordinateAt(int index) const
*/
bool QDeclarativePolylineMapItem::containsCoordinate(const QGeoCoordinate &coordinate)
{
- return geopath_.containsCoordinate(coordinate);
+ return m_geopath.containsCoordinate(coordinate);
}
/*!
@@ -963,13 +1133,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 +1155,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 +1180,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 +1228,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 +1242,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 +1255,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 +1334,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 +1454,425 @@ 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()
+{
+
+}
+
+void QGeoMapPolylineGeometryOpenGL::allocateAndFillEntries(QSGGeometry *geom, bool closed) const
+{
+ // ToDo: add dirty flag.
+ const QVector<QDeclarativeGeoMapItemUtils::vec2> &v = m_screenVertices;
+ if (v.size() < 2) {
+ geom->allocate(0, 0);
+ return;
+ }
+ 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;
+ }
+ }
+ }
+}
+
+void QGeoMapPolylineGeometryOpenGL::allocateAndFillLineStrip(QSGGeometry *geom) const
+{
+ 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)
+{
+ // 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 || !fill->vertexCount()) { // fill->vertexCount for when node gets destroyed by MapItemBase bcoz of opacity, then recreated.
+ shape->allocateAndFillEntries(fill, closed);
+ 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";
+}
+
QT_END_NAMESPACE
+