From 6508543f5f221dc5b7b2b398145299481565fe58 Mon Sep 17 00:00:00 2001 From: Martin Dobias Date: Sat, 30 Dec 2017 00:03:42 +0100 Subject: [PATCH] [3d] Fix tessellator crash with nearly collinear points (fixes #17745) --- src/3d/qgstessellator.cpp | 22 +++++++++++++++++++++- tests/src/3d/testqgstessellator.cpp | 13 +++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/3d/qgstessellator.cpp b/src/3d/qgstessellator.cpp index 11bc8ca6ab9e..1447966b9f91 100644 --- a/src/3d/qgstessellator.cpp +++ b/src/3d/qgstessellator.cpp @@ -231,6 +231,14 @@ static void _ringToPoly2tri( const QgsCurve *ring, std::vector &po } } + +inline double _round_coord( double x ) +{ + const double exp = 1e10; // round to 10 decimal digits + return round( x * exp ) / exp; +} + + static QgsCurve *_transform_ring_to_new_base( const QgsCurve &curve, const QgsPoint &pt0, const QMatrix4x4 *toNewBase ) { int count = curve.numPoints(); @@ -245,7 +253,19 @@ static QgsCurve *_transform_ring_to_new_base( const QgsCurve &curve, const QgsPo QVector4D v( pt2.x(), pt2.y(), pt2.z(), 0 ); if ( toNewBase ) v = toNewBase->map( v ); - pts << QgsPoint( QgsWkbTypes::PointZ, v.x(), v.y(), v.z() ); + + // we also round coordinates before passing them to poly2tri triangulation in order to fix possible numerical + // stability issues. We had crashes with nearly collinear points where one of the points was off by a tiny bit (e.g. by 1e-20). + // See TestQgsTessellator::testIssue17745(). + // + // A hint for a similar issue: https://github.com/greenm01/poly2tri/issues/99 + // + // The collinear tests uses epsilon 1e-12. Seems rounding to 12 places you still + // can get problems with this test when points are pretty much on a straight line. + // I suggest you round to 10 decimals for stability and you can live with that + // precision. + + pts << QgsPoint( QgsWkbTypes::PointZ, _round_coord( v.x() ), _round_coord( v.y() ), _round_coord( v.z() ) ); } return new QgsLineString( pts ); } diff --git a/tests/src/3d/testqgstessellator.cpp b/tests/src/3d/testqgstessellator.cpp index 87271904d19a..d6fd8fceac8e 100644 --- a/tests/src/3d/testqgstessellator.cpp +++ b/tests/src/3d/testqgstessellator.cpp @@ -131,6 +131,7 @@ class TestQgsTessellator : public QObject void testWalls(); void asMultiPolygon(); void testBadCoordinates(); + void testIssue17745(); private: }; @@ -289,6 +290,18 @@ void TestQgsTessellator::testBadCoordinates() QVERIFY( checkTriangleOutput( t.data(), false, tc ) ); } +void TestQgsTessellator::testIssue17745() +{ + // this is a rectangular polygon with collinear points that would crash poly2tri if coordinates do not get rounded a bit + + QgsTessellator t( 0, 0, true ); + QgsPolygon p; + bool resWktRead = p.fromWkt( "Polygon((0 0, 1 1e-15, 4 0, 4 5, 1 5, 0 5, 0 0))" ); + QVERIFY( resWktRead ); + + t.addPolygon( p, 0 ); // must not crash - that's all we test here +} + QGSTEST_MAIN( TestQgsTessellator ) #include "testqgstessellator.moc"