Skip to content

Commit 7bce7ea

Browse files
committed
[3d] Fix crash in tessellator with near coords (fixes #17286, fixes #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.
1 parent d147064 commit 7bce7ea

File tree

2 files changed

+62
-0
lines changed

2 files changed

+62
-0
lines changed

src/3d/qgstessellator.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,8 +265,51 @@ static bool _check_intersecting_rings( const QgsPolygon &polygon )
265265
}
266266

267267

268+
double _minimum_distance_between_coordinates( const QgsPolygon &polygon )
269+
{
270+
double min_d = 1e20;
271+
auto it = polygon.vertices_begin();
272+
273+
if ( it == polygon.vertices_end() )
274+
return min_d;
275+
276+
QgsPoint p0 = *it;
277+
++it;
278+
for ( ; it != polygon.vertices_end(); ++it )
279+
{
280+
QgsPoint p1 = *it;
281+
double d = p0.distance( p1 );
282+
if ( d < min_d )
283+
min_d = d;
284+
p0 = p1;
285+
}
286+
return min_d;
287+
}
288+
289+
268290
void QgsTessellator::addPolygon( const QgsPolygon &polygon, float extrusionHeight )
269291
{
292+
if ( _minimum_distance_between_coordinates( polygon ) < 0.001 )
293+
{
294+
// when the distances between coordinates of input points are very small,
295+
// the triangulation likes to crash on numerical errors - when the distances are ~ 1e-5
296+
// Assuming that the coordinates should be in a projected CRS, we should be able
297+
// to simplify geometries that may cause problems and avoid possible crashes
298+
QgsGeometry polygonSimplified = QgsGeometry( polygon.clone() ).simplify( 0.001 );
299+
const QgsPolygon *polygonSimplifiedData = qgsgeometry_cast<const QgsPolygon *>( polygonSimplified.constGet() );
300+
if ( _minimum_distance_between_coordinates( *polygonSimplifiedData ) < 0.001 )
301+
{
302+
// Failed to fix that. It could be a really tiny geometry... or maybe they gave us
303+
// geometry in unprojected lat/lon coordinates
304+
qDebug() << "geometry's coordinates are too close to each other and simplification failed - skipping";
305+
}
306+
else
307+
{
308+
addPolygon( *polygonSimplifiedData, extrusionHeight );
309+
}
310+
return;
311+
}
312+
270313
if ( !_check_intersecting_rings( polygon ) )
271314
{
272315
// skip the polygon - it would cause a crash inside poly2tri library

tests/src/3d/testqgstessellator.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,10 @@ bool checkTriangleOutput( const QVector<float> &data, bool withNormals, const QL
7878
{
7979
int valuesPerTriangle = withNormals ? 18 : 9;
8080
if ( data.count() != expected.count() * valuesPerTriangle )
81+
{
82+
qDebug() << "expected" << expected.count() << "triangles, got" << data.count() / valuesPerTriangle;
8183
return false;
84+
}
8285

8386
// TODO: allow arbitrary order of triangles in output
8487
const float *dataRaw = data.constData();
@@ -117,6 +120,7 @@ class TestQgsTessellator : public QObject
117120
void testBasic();
118121
void testWalls();
119122
void asMultiPolygon();
123+
void testBadCoordinates();
120124

121125
private:
122126
};
@@ -208,7 +212,22 @@ void TestQgsTessellator::asMultiPolygon()
208212
QgsTessellator t2( 0, 0, false );
209213
t2.addPolygon( polygonZ, 0 );
210214
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)))" ) );
215+
}
211216

217+
void TestQgsTessellator::testBadCoordinates()
218+
{
219+
// triangulation would crash for me with this polygon if there is no simplification
220+
// to remove the coordinates that are very close to each other
221+
QgsPolygon polygon;
222+
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))" );
223+
224+
QList<TriangleCoords> tc;
225+
tc << TriangleCoords( QVector3D( 1, 2, 0 ), QVector3D( 2, 1, 0 ), QVector3D( 3, 2, 0 ) );
226+
tc << TriangleCoords( QVector3D( 1, 2, 0 ), QVector3D( 1, 1, 0 ), QVector3D( 2, 1, 0 ) );
227+
228+
QgsTessellator t( 0, 0, false );
229+
t.addPolygon( polygon, 0 );
230+
QVERIFY( checkTriangleOutput( t.data(), false, tc ) );
212231
}
213232

214233

0 commit comments

Comments
 (0)