From 0951ff7ee36b1a6a1a9e4641f42d6bbb9419bcc8 Mon Sep 17 00:00:00 2001 From: Martin Dobias Date: Sun, 1 Oct 2017 21:33:04 +0200 Subject: [PATCH] Fix crash in tessellation (polygon 3D symbol) for touching rings --- src/3d/qgstessellator.cpp | 37 ++++++++++++++++++++++ src/3d/terrain/qgsflatterraingenerator.cpp | 4 +++ src/3d/terrain/qgsflatterraingenerator.h | 3 +- 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/3d/qgstessellator.cpp b/src/3d/qgstessellator.cpp index f32a70ff1229..94c8e518c498 100644 --- a/src/3d/qgstessellator.cpp +++ b/src/3d/qgstessellator.cpp @@ -16,6 +16,7 @@ #include "qgstessellator.h" #include "qgscurve.h" +#include "qgsgeometry.h" #include "qgspoint.h" #include "qgspolygon.h" @@ -111,6 +112,42 @@ static void _makeWalls( const QgsCurve &ring, bool ccw, float extrusionHeight, Q void QgsTessellator::addPolygon( const QgsPolygonV2 &polygon, float extrusionHeight ) { + // At this point we assume that input polygons are valid according to the OGC definition. + // This means e.g. no duplicate points, polygons are simple (no butterfly shaped polygon with self-intersection), + // internal rings are inside exterior rings, rings do not cross each other, no dangles. + + // There is however an issue with polygons where rings touch: + // +---+ + // | | + // | +-+-+ + // | | | | + // | +-+ | + // | | + // +-----+ + // This is a valid polygon with one exterior and one interior ring that touch at one point, + // but poly2tri library does not allow interior rings touch each other or exterior ring. + // TODO: Handle the situation better - rather than just detecting the problem, try to fix + // it by converting touching rings into one ring. + + if ( polygon.numInteriorRings() > 0 ) + { + QList geomRings; + geomRings << QgsGeometry( polygon.exteriorRing()->clone() ); + for ( int i = 0; i < polygon.numInteriorRings(); ++i ) + geomRings << QgsGeometry( polygon.interiorRing( i )->clone() ); + + for ( int i = 0; i < geomRings.count(); ++i ) + for ( int j = i + 1; j < geomRings.count(); ++j ) + { + if ( geomRings[i].intersects( geomRings[j] ) ) + { + // skip the polygon - it would cause a crash inside poly2tri library + qDebug() << "polygon rings intersect each other - skipping"; + return; + } + } + } + const QgsCurve *exterior = polygon.exteriorRing(); QList< std::vector > polylinesToDelete; diff --git a/src/3d/terrain/qgsflatterraingenerator.cpp b/src/3d/terrain/qgsflatterraingenerator.cpp index dc77055e9832..da27be71726b 100644 --- a/src/3d/terrain/qgsflatterraingenerator.cpp +++ b/src/3d/terrain/qgsflatterraingenerator.cpp @@ -93,6 +93,10 @@ Qt3DCore::QEntity *FlatTerrainChunkLoader::createEntity( Qt3DCore::QEntity *pare // --------------- +QgsFlatTerrainGenerator::QgsFlatTerrainGenerator() +{ +} + QgsChunkLoader *QgsFlatTerrainGenerator::createChunkLoader( QgsChunkNode *node ) const { return new FlatTerrainChunkLoader( mTerrain, node ); diff --git a/src/3d/terrain/qgsflatterraingenerator.h b/src/3d/terrain/qgsflatterraingenerator.h index 5a96ba54e5c5..a6041f714924 100644 --- a/src/3d/terrain/qgsflatterraingenerator.h +++ b/src/3d/terrain/qgsflatterraingenerator.h @@ -31,7 +31,8 @@ class _3D_EXPORT QgsFlatTerrainGenerator : public QgsTerrainGenerator { public: - QgsFlatTerrainGenerator() = default; + //! Creates flat terrain generator object + QgsFlatTerrainGenerator(); virtual QgsChunkLoader *createChunkLoader( QgsChunkNode *node ) const override SIP_FACTORY;