Skip to content

Commit

Permalink
[3d] Fix tessellator crash with nearly collinear points (fixes #17745)
Browse files Browse the repository at this point in the history
  • Loading branch information
wonder-sk committed Jan 1, 2018
1 parent 32504c4 commit 6508543
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 1 deletion.
22 changes: 21 additions & 1 deletion src/3d/qgstessellator.cpp
Expand Up @@ -231,6 +231,14 @@ static void _ringToPoly2tri( const QgsCurve *ring, std::vector<p2t::Point *> &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();
Expand All @@ -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 );
}
Expand Down
13 changes: 13 additions & 0 deletions tests/src/3d/testqgstessellator.cpp
Expand Up @@ -131,6 +131,7 @@ class TestQgsTessellator : public QObject
void testWalls();
void asMultiPolygon();
void testBadCoordinates();
void testIssue17745();

private:
};
Expand Down Expand Up @@ -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"

0 comments on commit 6508543

Please sign in to comment.