Skip to content

Commit

Permalink
[3d] Fix crash in tessellator with near coords (fixes #17286, fixes #…
Browse files Browse the repository at this point in the history
…17515)

As the readme of poly2tri library says: "Poly2Tri does not support repeat points within epsilon."

When the coordinates are very near to each other, we get crashes in triangulation code.
To prevent that, we try to simplify geometries to hopefully fix the problem automatically,
if that fails we just skip the polygon as the last resort.

Usually this happens if user tries to use 3D renderer on unprojected lat/lon coordinates.
  • Loading branch information
wonder-sk committed Nov 27, 2017
1 parent d147064 commit 7bce7ea
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 0 deletions.
43 changes: 43 additions & 0 deletions src/3d/qgstessellator.cpp
Expand Up @@ -265,8 +265,51 @@ static bool _check_intersecting_rings( const QgsPolygon &polygon )
}


double _minimum_distance_between_coordinates( const QgsPolygon &polygon )
{
double min_d = 1e20;
auto it = polygon.vertices_begin();

if ( it == polygon.vertices_end() )
return min_d;

QgsPoint p0 = *it;
++it;
for ( ; it != polygon.vertices_end(); ++it )
{
QgsPoint p1 = *it;
double d = p0.distance( p1 );
if ( d < min_d )
min_d = d;
p0 = p1;
}
return min_d;
}


void QgsTessellator::addPolygon( const QgsPolygon &polygon, float extrusionHeight )
{
if ( _minimum_distance_between_coordinates( polygon ) < 0.001 )
{
// when the distances between coordinates of input points are very small,
// the triangulation likes to crash on numerical errors - when the distances are ~ 1e-5
// Assuming that the coordinates should be in a projected CRS, we should be able
// to simplify geometries that may cause problems and avoid possible crashes
QgsGeometry polygonSimplified = QgsGeometry( polygon.clone() ).simplify( 0.001 );
const QgsPolygon *polygonSimplifiedData = qgsgeometry_cast<const QgsPolygon *>( polygonSimplified.constGet() );
if ( _minimum_distance_between_coordinates( *polygonSimplifiedData ) < 0.001 )
{
// Failed to fix that. It could be a really tiny geometry... or maybe they gave us
// geometry in unprojected lat/lon coordinates
qDebug() << "geometry's coordinates are too close to each other and simplification failed - skipping";
}
else
{
addPolygon( *polygonSimplifiedData, extrusionHeight );
}
return;
}

if ( !_check_intersecting_rings( polygon ) )
{
// skip the polygon - it would cause a crash inside poly2tri library
Expand Down
19 changes: 19 additions & 0 deletions tests/src/3d/testqgstessellator.cpp
Expand Up @@ -78,7 +78,10 @@ bool checkTriangleOutput( const QVector<float> &data, bool withNormals, const QL
{
int valuesPerTriangle = withNormals ? 18 : 9;
if ( data.count() != expected.count() * valuesPerTriangle )
{
qDebug() << "expected" << expected.count() << "triangles, got" << data.count() / valuesPerTriangle;
return false;
}

// TODO: allow arbitrary order of triangles in output
const float *dataRaw = data.constData();
Expand Down Expand Up @@ -117,6 +120,7 @@ class TestQgsTessellator : public QObject
void testBasic();
void testWalls();
void asMultiPolygon();
void testBadCoordinates();

private:
};
Expand Down Expand Up @@ -208,7 +212,22 @@ void TestQgsTessellator::asMultiPolygon()
QgsTessellator t2( 0, 0, false );
t2.addPolygon( polygonZ, 0 );
QCOMPARE( t2.asMultiPolygon()->asWkt(), QStringLiteral( "MultiPolygonZ (((1 2 4, 2 1 2, 3 2 3, 1 2 4)),((1 2 4, 1 1 1, 2 1 2, 1 2 4)))" ) );
}

void TestQgsTessellator::testBadCoordinates()
{
// triangulation would crash for me with this polygon if there is no simplification
// to remove the coordinates that are very close to each other
QgsPolygon polygon;
polygon.fromWkt( "POLYGON((1 1, 2 1, 2.0000001 1.0000001, 2.0000002 1.0000001, 2.0000001 1.0000002, 2.0000002 1.0000002, 3 2, 1 2, 1 1))" );

QList<TriangleCoords> tc;
tc << TriangleCoords( QVector3D( 1, 2, 0 ), QVector3D( 2, 1, 0 ), QVector3D( 3, 2, 0 ) );
tc << TriangleCoords( QVector3D( 1, 2, 0 ), QVector3D( 1, 1, 0 ), QVector3D( 2, 1, 0 ) );

QgsTessellator t( 0, 0, false );
t.addPolygon( polygon, 0 );
QVERIFY( checkTriangleOutput( t.data(), false, tc ) );
}


Expand Down

0 comments on commit 7bce7ea

Please sign in to comment.