diff options
author | Alex Wilson <alex.wilson@nokia.com> | 2012-01-23 13:41:34 +1000 |
---|---|---|
committer | Qt by Nokia <qt-info@nokia.com> | 2012-01-24 01:56:25 +0100 |
commit | 377262a1fe43dc0b0d2f0d4c807488f347187655 (patch) | |
tree | 1e7005b463857437c5114fb34d9699cc66bbfc82 /src/imports/location | |
parent | 5831ab342123f3946df6858208bb8170dfde8ba7 (diff) | |
download | qtlocation-377262a1fe43dc0b0d2f0d4c807488f347187655.tar.gz |
Use triangulated solid MapPolylines, make MapCircle use triangulator
* MapPolyline is now rendered by stroking the path and
triangulating the stroke. This gives it a solid form for use
in clipping/culling and mouse areas (mouse stuff in another
commit to come)
* MapCircle and MapPolygon are adjusted to clip and cull their
geometry using the triangulator, and use the new MapPolyline.
* MapRectangle now uses the new MapPolyline.
Change-Id: I989ec73065be104637116960aa73b7deae78a199
Reviewed-by: David Laing <david.laing@nokia.com>
Diffstat (limited to 'src/imports/location')
9 files changed, 223 insertions, 147 deletions
diff --git a/src/imports/location/qdeclarativecirclemapitem.cpp b/src/imports/location/qdeclarativecirclemapitem.cpp index 16e58ba4..ec9dd74c 100644 --- a/src/imports/location/qdeclarativecirclemapitem.cpp +++ b/src/imports/location/qdeclarativecirclemapitem.cpp @@ -41,6 +41,7 @@ #include "qdeclarativecirclemapitem_p.h" #include "qdeclarativegeomapquickitem_p.h" +#include "qdeclarativepolygonmapitem_p.h" #include "qgeoprojection_p.h" #include <cmath> #include <QPen> @@ -126,41 +127,6 @@ inline static qreal qgeocoordinate_radToDeg(qreal rad) return rad * 180 / M_PI; } -static void updatePolygon(QPolygonF& points,const QGeoMap& map, const QList<QGeoCoordinate> &path, qreal& w, qreal& h) -{ - - qreal minX, maxX, minY, maxY; - //TODO: dateline handling - - for (int i = 0; i < path.size(); ++i) { - - const QGeoCoordinate &coord = path.at(i); - - if (!coord.isValid()) - continue; - - QPointF point = map.coordinateToScreenPosition(coord, false); - - if (i == 0) { - minX = point.x(); - maxX = point.x(); - minY = point.y(); - maxY = point.y(); - } else { - minX = qMin(point.x(), minX); - maxX = qMax(point.x(), maxX); - minY = qMin(point.y(), minY); - maxY = qMax(point.y(), maxY); - } - points.append(point); - } - - points.translate(-minX, -minY); - - w = maxX - minX; - h = maxY - minY; -} - static void calculatePeripheralPoints(QList<QGeoCoordinate>& path, const QGeoCoordinate& center, qreal distance, int steps) { // get angular distance in radians @@ -374,17 +340,39 @@ void QDeclarativeCircleMapItem::updateMapItem() if (dirtyPixelGeometry_) { qreal w = 0; qreal h = 0; - circlePolygon_.clear(); - updatePolygon(circlePolygon_, *map(), circlePath_, w, h); + + QPolygonF newPoly, newBorderPoly; + QPointF offset; + + QDeclarativePolygonMapItem::updatePolygon(newPoly, *map(), + circlePath_, w, h, + offset); + + if (newPoly.size() > 0) { + circlePolygon_ = newPoly; + offset_ = offset; + } + QList<QGeoCoordinate> pathClosed = circlePath_; pathClosed.append(pathClosed.at(0)); - borderPolygon_.clear(); - QDeclarativePolylineMapItem::updatePolyline(borderPolygon_, *map(), pathClosed, w, h); + + QPainterPath outline; + QDeclarativePolylineMapItem::updatePolyline(newBorderPoly, *map(), + pathClosed, w, h, + border_.width(), + outline, offset); + + if (newBorderPoly.size() > 0) { + borderPolygon_ = newBorderPoly; + borderPolygon_.translate(offset_ - offset); + } + setWidth(w); setHeight(h); } - setPositionOnMap(center()->coordinate(), QPointF(width(),height()) / 2); + //setPositionOnMap(center()->coordinate(), QPointF(width(),height()) / 2); + setPositionOnMap(circlePath_.at(0), offset_); update(); } @@ -392,8 +380,8 @@ void QDeclarativeCircleMapItem::handleCameraDataChanged(const QGeoCameraData& ca { if (cameraData.zoomFactor() != zoomLevel_) { zoomLevel_ = cameraData.zoomFactor(); - dirtyPixelGeometry_ = true; } + dirtyPixelGeometry_ = true; updateMapItem(); } @@ -418,7 +406,7 @@ MapCircleNode::MapCircleNode(): border_(new MapPolylineNode()), geometry_(QSGGeometry::defaultAttributes_Point2D(),0) { - geometry_.setDrawingMode(GL_TRIANGLE_FAN); + geometry_.setDrawingMode(GL_TRIANGLES); QSGGeometryNode::setMaterial(&fill_material_); QSGGeometryNode::setGeometry(&geometry_); @@ -438,17 +426,13 @@ void MapCircleNode::update(const QColor& fillColor, const QPolygonF& circleShape int fillVertexCount = 0; //note this will not allocate new buffer if the size has not changed - fill->allocate(circleShape.size() + 1 + 1); //one for center + one to close the circle + fill->allocate(circleShape.size()); //one for center + one to close the circle Vertex *vertices = (Vertex *)fill->vertexData(); - //set center - vertices[fillVertexCount++].position = QVector2D(center); - for (int i = 0; i < circleShape.size(); ++i) { vertices[fillVertexCount++].position = QVector2D(circleShape.at(i)); } - //close circle - vertices[fillVertexCount++].position = QVector2D(circleShape.at(0)); + Q_ASSERT(fillVertexCount == fill->vertexCount()); markDirty(DirtyGeometry); diff --git a/src/imports/location/qdeclarativecirclemapitem_p.h b/src/imports/location/qdeclarativecirclemapitem_p.h index 8801e557..1a0e8b6f 100644 --- a/src/imports/location/qdeclarativecirclemapitem_p.h +++ b/src/imports/location/qdeclarativecirclemapitem_p.h @@ -109,6 +109,7 @@ private: bool dirtyPixelGeometry_; bool dirtyGeoGeometry_; bool dirtyMaterial_; + QPointF offset_; }; ////////////////////////////////////////////////////////////////////// diff --git a/src/imports/location/qdeclarativepolygonmapitem.cpp b/src/imports/location/qdeclarativepolygonmapitem.cpp index 427533c3..9c7fe33a 100644 --- a/src/imports/location/qdeclarativepolygonmapitem.cpp +++ b/src/imports/location/qdeclarativepolygonmapitem.cpp @@ -57,8 +57,7 @@ QT_BEGIN_NAMESPACE The MapPolygon element displays a polygon on a Map, specified in terms of an ordered list of \l Coordinate elements. For best appearance and results, - polygons should be simple (not self-intersecting) -- the result of drawing - a non-simple polygon is not well-defined. + polygons should be simple (not self-intersecting). The Coordinate elements on the path can be changed after being added to the Polygon, and these changes will be reflected in the next frame on the @@ -77,9 +76,10 @@ QT_BEGIN_NAMESPACE MapPolygons have a rendering cost that is O(n) with respect to the number of vertices. This means that the per frame cost of having a Polygon on the - Map grows in direct proportion to the number of points on the Polygon. - Additionally, there is a triangulation cost any time the points of the - Polygon are updated, which is approximately O(n log n). + Map grows in direct proportion to the number of points on the Polygon. There + is an additional triangulation cost (approximately O(n log n)) which is + currently paid with each frame, but in future may be paid only upon adding + or removing points. Like the other map objects, MapPolygon is normally drawn without a smooth appearance. Setting the \l opacity property will force the object to be @@ -113,12 +113,21 @@ struct Vertex QVector2D position; }; -static void updatePolygon(QPolygonF& points, const QGeoMap& map, - const QList<QGeoCoordinate> &path, - qreal& w, qreal& h, QPointF& offset) +void QDeclarativePolygonMapItem::updatePolygon(QPolygonF &points, + const QGeoMap &map, + const QList<QGeoCoordinate> &path, + qreal& w, qreal& h, + QPointF& offset) { - qreal minX, maxX, minY, maxY; - + // viewport rectangle, for clipping later + QPainterPath viewport; + viewport.moveTo(0, 0); + viewport.lineTo(map.width(), 0); + viewport.lineTo(map.width(), map.height()); + viewport.lineTo(0, map.height()); + viewport.closeSubpath(); + + // build the actual path QPainterPath pp; for (int i = 0; i < path.size(); ++i) { const QGeoCoordinate &coord = path.at(i); @@ -130,23 +139,29 @@ static void updatePolygon(QPolygonF& points, const QGeoMap& map, if (i == 0) { offset = point; pp.moveTo(point); - minX = point.x(); - maxX = point.x(); - minY = point.y(); - maxY = point.y(); } else { pp.lineTo(point); - minX = qMin(point.x(), minX); - maxX = qMax(point.x(), maxX); - minY = qMin(point.y(), minY); - maxY = qMax(point.y(), maxY); } } pp.closeSubpath(); - pp.translate(-minX, -minY); - offset += QPointF(-minX, -minY); - QTriangleSet ts = qTriangulate(pp); + // really big numbers (> 2^21) break qTriangulate, so we must clip here + // to avoid asserting + QPainterPath ppi = pp.intersected(viewport); + + // nothing is on the screen + if (ppi.elementCount() == 0) + return; + + QRectF bb = ppi.boundingRect(); + w = bb.width(); + h = bb.height(); + + // we need relative coordinates + ppi.translate(-bb.left(), -bb.top()); + offset += QPointF(-bb.left(), -bb.top()); + + QTriangleSet ts = qTriangulate(ppi); qreal *vx = ts.vertices.data(); if (ts.indices.type() == QVertexIndexVector::UnsignedInt) { const quint32 *tx = reinterpret_cast<const quint32*>(ts.indices.data()); @@ -159,9 +174,6 @@ static void updatePolygon(QPolygonF& points, const QGeoMap& map, points.append(QPointF(vx[tx[i]*2], vx[tx[i]*2+1])); } } - - w = maxX - minX; - h = maxY - minY; } QDeclarativePolygonMapItem::QDeclarativePolygonMapItem(QQuickItem *parent) : @@ -385,13 +397,28 @@ void QDeclarativePolygonMapItem::updateMapItem() if (dirtyGeometry_) { qreal h = 0; qreal w = 0; - polygon_.clear(); - borderPolygon_.clear(); - updatePolygon(polygon_, *map(), path_, w, h, offset_); + + QPolygonF newPoly, newBorderPoly; + QPointF offset; + updatePolygon(newPoly, *map(), path_, w, h, offset); + + if (newPoly.size() > 0) { + polygon_ = newPoly; + offset_ = offset; + } QList<QGeoCoordinate> pathClosed = path_; pathClosed.append(path_.at(0)); - QDeclarativePolylineMapItem::updatePolyline(borderPolygon_, *map(), pathClosed, w, h); + QPainterPath outline; + QDeclarativePolylineMapItem::updatePolyline(newBorderPoly, *map(), + pathClosed, w, h, + border_.width(), + outline, offset); + if (newBorderPoly.size() > 0) { + borderPolygon_ = newBorderPoly; + borderPolygon_.translate(offset_ - offset); + } + setWidth(w); setHeight(h); } @@ -403,8 +430,8 @@ void QDeclarativePolygonMapItem::handleCameraDataChanged(const QGeoCameraData& c { if (cameraData.zoomFactor() != zoomLevel_) { zoomLevel_ = cameraData.zoomFactor(); - dirtyGeometry_ = true; } + dirtyGeometry_ = true; updateMapItem(); } diff --git a/src/imports/location/qdeclarativepolygonmapitem_p.h b/src/imports/location/qdeclarativepolygonmapitem_p.h index d7522a62..63287e3a 100644 --- a/src/imports/location/qdeclarativepolygonmapitem_p.h +++ b/src/imports/location/qdeclarativepolygonmapitem_p.h @@ -63,6 +63,10 @@ public: QDeclarativePolygonMapItem(QQuickItem *parent = 0); ~QDeclarativePolygonMapItem(); + static void updatePolygon(QPolygonF &points, const QGeoMap &map, + const QList<QGeoCoordinate> &path, + qreal &w, qreal &h, QPointF &offset); + virtual void setMap(QDeclarativeGeoMap* quickMap, QGeoMap *map); //from QuickItem virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); diff --git a/src/imports/location/qdeclarativepolylinemapitem.cpp b/src/imports/location/qdeclarativepolylinemapitem.cpp index 9cd2efff..4286f699 100644 --- a/src/imports/location/qdeclarativepolylinemapitem.cpp +++ b/src/imports/location/qdeclarativepolylinemapitem.cpp @@ -42,6 +42,9 @@ #include "qdeclarativepolylinemapitem_p.h" #include <QDeclarativeInfo> #include <QPainter> +#include <QPainterPath> +#include <QPainterPathStroker> +#include <QtGui/private/qtriangulator_p.h> QT_BEGIN_NAMESPACE @@ -143,13 +146,25 @@ struct Vertex QVector2D position; }; -void QDeclarativePolylineMapItem::updatePolyline(QPolygonF& points,const QGeoMap& map, const QList<QGeoCoordinate> &path, qreal& w, qreal& h) +void QDeclarativePolylineMapItem::updatePolyline(QPolygonF& points, + const QGeoMap& map, + const QList<QGeoCoordinate> &path, + qreal& w, qreal& h, + qreal strokeW, + QPainterPath &outline, + QPointF &offset) { - qreal minX, maxX, minY, maxY; - //TODO: dateline handling + // viewport rectangle, for clipping later + QPainterPath viewport; + QRectF viewRect(-strokeW, -strokeW, map.width()+2*strokeW, map.height()+2*strokeW); + viewport.addRect(viewRect); - for (int i = 0; i < path.size(); ++i) { + QPainterPath pp; + QPointF lastPoint, lastAddedPoint; + + bool inView = false; + for (int i = 0; i < path.size(); ++i) { const QGeoCoordinate &coord = path.at(i); if (!coord.isValid()) @@ -157,24 +172,68 @@ void QDeclarativePolylineMapItem::updatePolyline(QPolygonF& points,const QGeoMap QPointF point = map.coordinateToScreenPosition(coord, false); - if (i == 0) { - minX = point.x(); - maxX = point.x(); - minY = point.y(); - maxY = point.y(); - } else { - minX = qMin(point.x(), minX); - maxX = qMax(point.x(), maxX); - minY = qMin(point.y(), minY); - maxY = qMax(point.y(), maxY); + if (i == 0) + offset = point; + + if (!inView && viewRect.contains(point)) { + if (i > 0) { + pp.moveTo(lastPoint); + pp.lineTo(point); + } else { + pp.moveTo(point); + } + lastAddedPoint = point; + inView = true; + } else if (inView) { + if ((point - lastAddedPoint).manhattanLength() > 3 || + i == path.size() - 1) { + pp.lineTo(point); + lastAddedPoint = point; + } + + if (!viewRect.contains(point)) + inView = false; } - points.append(point); + + lastPoint = point; } - points.translate(-minX, -minY); + QPainterPath quickClip; + quickClip.addRect(-0.5*map.width(), -0.5*map.height(), map.width()*2, map.height()*2); + + QPainterPathStroker st; + st.setWidth(strokeW); + outline = st.createStroke(pp.intersected(quickClip)); + + // really big numbers (> 2^21) break qTriangulate, so we must clip here + // to avoid asserting + QPainterPath ppi = outline.intersected(viewport); + + // nothing is on the screen + if (ppi.elementCount() == 0) + return; + + QRectF bb = ppi.boundingRect(); + w = bb.width(); + h = bb.height(); - w = maxX - minX; - h = maxY - minY; + // we need relative coordinates + ppi.translate(-bb.left(), -bb.top()); + offset += QPointF(-bb.left(), -bb.top()); + + QTriangleSet ts = qTriangulate(ppi); + qreal *vx = ts.vertices.data(); + if (ts.indices.type() == QVertexIndexVector::UnsignedInt) { + const quint32 *tx = reinterpret_cast<const quint32*>(ts.indices.data()); + for (int i = 0; i < (ts.indices.size()/3*3); i++) { + points.append(QPointF(vx[tx[i]*2], vx[tx[i]*2+1])); + } + } else { + const quint16 *tx = reinterpret_cast<const quint16*>(ts.indices.data()); + for (int i = 0; i < (ts.indices.size()/3*3); i++) { + points.append(QPointF(vx[tx[i]*2], vx[tx[i]*2+1])); + } + } } QDeclarativePolylineMapItem::QDeclarativePolylineMapItem(QQuickItem *parent) : @@ -374,13 +433,22 @@ void QDeclarativePolylineMapItem::updateMapItem() if (dirtyGeometry_) { qreal h = 0; qreal w = 0; - polyline_.clear(); - updatePolyline(polyline_, *map(), path_, w, h); + + QPolygonF newPolyline; + QPointF offset; + + updatePolyline(newPolyline, *map(), path_, w, h, line_.width(), + outline_, offset); + if (newPolyline.size() > 0) { + polyline_ = newPolyline; + offset_ = offset; + } + setWidth(w); setHeight(h); } - setPositionOnMap(path_.at(0),polyline_.at(0)); + setPositionOnMap(path_.at(0), offset_); update(); } @@ -388,8 +456,8 @@ void QDeclarativePolylineMapItem::handleCameraDataChanged(const QGeoCameraData& { if (cameraData.zoomFactor() != zoomLevel_) { zoomLevel_ = cameraData.zoomFactor(); - dirtyGeometry_ = true; } + dirtyGeometry_ = true; updateMapItem(); } @@ -405,7 +473,7 @@ bool QDeclarativePolylineMapItem::contains(QPointF point) MapPolylineNode::MapPolylineNode() : geometry_(QSGGeometry::defaultAttributes_Point2D(),0) { - geometry_.setDrawingMode(GL_LINE_STRIP); + geometry_.setDrawingMode(GL_TRIANGLES); QSGGeometryNode::setMaterial(&fill_material_); QSGGeometryNode::setGeometry(&geometry_); } diff --git a/src/imports/location/qdeclarativepolylinemapitem_p.h b/src/imports/location/qdeclarativepolylinemapitem_p.h index 76b414e5..cf3b500c 100644 --- a/src/imports/location/qdeclarativepolylinemapitem_p.h +++ b/src/imports/location/qdeclarativepolylinemapitem_p.h @@ -86,7 +86,10 @@ public: QDeclarativePolylineMapItem(QQuickItem *parent = 0); ~QDeclarativePolylineMapItem(); - static void updatePolyline(QPolygonF& points,const QGeoMap& map, const QList<QGeoCoordinate> &path, qreal& w, qreal& h); + static void updatePolyline(QPolygonF& points, const QGeoMap& map, + const QList<QGeoCoordinate> &path, + qreal& w, qreal& h, qreal strokeW, + QPainterPath &outline, QPointF &offset); virtual void setMap(QDeclarativeGeoMap* quickMap, QGeoMap *map); //from QuickItem @@ -129,6 +132,8 @@ private: QPolygonF polyline_; bool dirtyGeometry_; bool dirtyMaterial_; + QPainterPath outline_; + QPointF offset_; }; ////////////////////////////////////////////////////////////////////// diff --git a/src/imports/location/qdeclarativerectanglemapitem.cpp b/src/imports/location/qdeclarativerectanglemapitem.cpp index d0801e71..9b93bc86 100644 --- a/src/imports/location/qdeclarativerectanglemapitem.cpp +++ b/src/imports/location/qdeclarativerectanglemapitem.cpp @@ -265,12 +265,24 @@ void QDeclarativeRectangleMapItem::updateMapItem() rectangle_.setTopLeft(QPointF(0, 0)); rectangle_.setBottomRight(QPointF(w, h)); - borderPoly_.clear(); - borderPoly_.append(QPointF(0, 0)); - borderPoly_.append(QPointF(w, 0)); - borderPoly_.append(QPointF(w, h)); - borderPoly_.append(QPointF(0, h)); - borderPoly_.append(borderPoly_.at(0)); + QList<QGeoCoordinate> pathClosed; + pathClosed << topLeft_->coordinate(); + pathClosed << QGeoCoordinate(topLeft_->latitude(), bottomRight_->longitude()); + pathClosed << bottomRight_->coordinate(); + pathClosed << QGeoCoordinate(bottomRight_->latitude(), topLeft_->longitude()); + pathClosed << pathClosed.first(); + + QPolygonF newBorderPoly; + QPainterPath outline; + QPointF offset; + QDeclarativePolylineMapItem::updatePolyline(newBorderPoly, *map(), + pathClosed, w, h, + border_.width(), + outline, offset); + if (newBorderPoly.size() > 0) { + newBorderPoly.translate(-1*offset); + borderPoly_ = newBorderPoly; + } setWidth(w); setHeight(h); diff --git a/src/imports/location/qdeclarativeroutemapitem.cpp b/src/imports/location/qdeclarativeroutemapitem.cpp index 111e1ccd..6e3fcf97 100644 --- a/src/imports/location/qdeclarativeroutemapitem.cpp +++ b/src/imports/location/qdeclarativeroutemapitem.cpp @@ -72,42 +72,6 @@ */ /* TODO: an example here. do we want to use a RouteModel? */ - -static void updatePolyline(QPolygonF& points,const QGeoMap& map, const QList<QGeoCoordinate> &path, qreal& w, qreal& h) -{ - - qreal minX, maxX, minY, maxY; - //TODO: dateline handling - - for (int i = 0; i < path.size(); ++i) { - - const QGeoCoordinate &coord = path.at(i); - - if (!coord.isValid()) - continue; - - QPointF point = map.coordinateToScreenPosition(coord, false); - - if (i == 0) { - minX = point.x(); - maxX = point.x(); - minY = point.y(); - maxY = point.y(); - } else { - minX = qMin(point.x(), minX); - maxX = qMax(point.x(), maxX); - minY = qMin(point.y(), minY); - maxY = qMax(point.y(), maxY); - } - points.append(point); - } - - points.translate(-minX, -minY); - - w = maxX - minX; - h = maxY - minY; -} - QDeclarativeRouteMapItem::QDeclarativeRouteMapItem(QQuickItem *parent): QDeclarativeGeoMapItemBase(parent), route_(0), @@ -215,20 +179,29 @@ void QDeclarativeRouteMapItem::updateMapItem() if (dirtyGeometry_) { qreal h = 0; qreal w = 0; - polyline_.clear(); - updatePolyline(polyline_, *map(), path_, w, h); + QPolygonF newPolyline; + QPointF offset; + + QDeclarativePolylineMapItem::updatePolyline(newPolyline, *map(), path_, + w, h, line_.width(), + outline_, offset); + if (newPolyline.size() > 0) { + polyline_ = newPolyline; + offset_ = offset; + } + setWidth(w); setHeight(h); } - setPositionOnMap(path_.at(0), polyline_.at(0)); + setPositionOnMap(path_.at(0), offset_); } void QDeclarativeRouteMapItem::handleCameraDataChanged(const QGeoCameraData& cameraData) { if (cameraData.zoomFactor() != zoomLevel_) { zoomLevel_ = cameraData.zoomFactor(); - dirtyGeometry_ = true; } + dirtyGeometry_ = true; updateMapItem(); } diff --git a/src/imports/location/qdeclarativeroutemapitem_p.h b/src/imports/location/qdeclarativeroutemapitem_p.h index db283fbb..94e5edcf 100644 --- a/src/imports/location/qdeclarativeroutemapitem_p.h +++ b/src/imports/location/qdeclarativeroutemapitem_p.h @@ -95,6 +95,8 @@ private: bool dirtyGeometry_; bool dirtyMaterial_; bool dragActive_; + QPointF offset_; + QPainterPath outline_; }; QT_END_NAMESPACE |