QGIS API Documentation  3.20.0-Odense (decaadbb31)
qgstriangularmesh.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgstriangularmesh.cpp
3  ---------------------
4  begin : April 2018
5  copyright : (C) 2018 by Peter Petrik
6  email : zilolv at gmail dot com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include <memory>
19 #include <QList>
20 #include "qgspolygon.h"
21 #include "qgslinestring.h"
22 #include "qgstriangularmesh.h"
23 #include "qgsrendercontext.h"
24 #include "qgscoordinatetransform.h"
25 #include "qgsgeometry.h"
26 #include "qgsrectangle.h"
27 #include "qgslogger.h"
28 #include "qgsmeshspatialindex.h"
29 #include "qgsmeshlayerutils.h"
30 #include "meshOptimizer/meshoptimizer.h"
31 
32 static void ENP_centroid_step( const QPolygonF &pX, double &cx, double &cy, double &signedArea, int i, int i1 )
33 {
34  double x0 = 0.0; // Current vertex X
35  double y0 = 0.0; // Current vertex Y
36  double x1 = 0.0; // Next vertex X
37  double y1 = 0.0; // Next vertex Y
38  double a = 0.0; // Partial signed area
39 
40  x0 = pX[i].x();
41  y0 = pX[i].y();
42  x1 = pX[i1].x();
43  y1 = pX[i1].y();
44  a = x0 * y1 - x1 * y0;
45  signedArea += a;
46  cx += ( x0 + x1 ) * a;
47  cy += ( y0 + y1 ) * a;
48 }
49 
50 static void ENP_centroid( const QPolygonF &pX, double &cx, double &cy )
51 {
52  // http://stackoverflow.com/questions/2792443/finding-the-centroid-of-a-polygon/2792459#2792459
53  cx = 0;
54  cy = 0;
55 
56  if ( pX.isEmpty() )
57  return;
58 
59  double signedArea = 0.0;
60 
61  const QPointF &pt0 = pX.first();
62  QPolygonF localPolygon( pX.count() );
63  for ( int i = 0; i < pX.count(); ++i )
64  localPolygon[i] = pX.at( i ) - pt0;
65 
66  // For all vertices except last
67  int i = 0;
68  for ( ; i < localPolygon.size() - 1; ++i )
69  {
70  ENP_centroid_step( localPolygon, cx, cy, signedArea, i, i + 1 );
71  }
72  // Do last vertex separately to avoid performing an expensive
73  // modulus operation in each iteration.
74  ENP_centroid_step( localPolygon, cx, cy, signedArea, i, 0 );
75 
76  signedArea *= 0.5;
77  cx /= ( 6.0 * signedArea );
78  cy /= ( 6.0 * signedArea );
79 
80  cx = cx + pt0.x();
81  cy = cy + pt0.y();
82 }
83 
84 void QgsTriangularMesh::triangulate( const QgsMeshFace &face, int nativeIndex )
85 {
86  int vertexCount = face.size();
87  if ( vertexCount < 3 )
88  return;
89 
90  while ( vertexCount > 3 )
91  {
92  // clip one ear from last 2 and first vertex
93  const QgsMeshFace ear = { face[vertexCount - 2], face[vertexCount - 1], face[0] };
94  if ( !( std::isnan( mTriangularMesh.vertex( ear[0] ).x() ) ||
95  std::isnan( mTriangularMesh.vertex( ear[1] ).x() ) ||
96  std::isnan( mTriangularMesh.vertex( ear[2] ).x() ) ) )
97  {
98  mTriangularMesh.faces.push_back( ear );
99  mTrianglesToNativeFaces.push_back( nativeIndex );
100  }
101  --vertexCount;
102  }
103 
104  const QgsMeshFace triangle = { face[1], face[2], face[0] };
105  if ( !( std::isnan( mTriangularMesh.vertex( triangle[0] ).x() ) ||
106  std::isnan( mTriangularMesh.vertex( triangle[1] ).x() ) ||
107  std::isnan( mTriangularMesh.vertex( triangle[2] ).x() ) ) )
108  {
109  mTriangularMesh.faces.push_back( triangle );
110  mTrianglesToNativeFaces.push_back( nativeIndex );
111  }
112 }
113 
115 {
116  return mAverageTriangleSize;
117 }
118 
121 
122 bool QgsTriangularMesh::update( QgsMesh *nativeMesh, const QgsCoordinateTransform &transform )
123 {
124  Q_ASSERT( nativeMesh );
125 
126  // FIND OUT IF UPDATE IS NEEDED
127  if ( mTriangularMesh.vertices.size() >= nativeMesh->vertices.size() &&
128  mTriangularMesh.faces.size() >= nativeMesh->faces.size() &&
129  mTriangularMesh.edges.size() == nativeMesh->edges.size() &&
130  ( ( !mCoordinateTransform.isValid() && !transform.isValid() ) ||
131  ( mCoordinateTransform.sourceCrs() == transform.sourceCrs() &&
132  mCoordinateTransform.destinationCrs() == transform.destinationCrs() &&
133  mCoordinateTransform.isValid() == transform.isValid() ) ) )
134  return false;
135 
136  // CLEAN-UP
137  mTriangularMesh.vertices.clear();
138  mTriangularMesh.faces.clear();
139  mTriangularMesh.edges.clear();
140  mTrianglesToNativeFaces.clear();
141  mEdgesToNativeEdges.clear();
142  mNativeMeshFaceCentroids.clear();
143  mNativeMeshEdgeCentroids.clear();
144 
145  // TRANSFORM VERTICES
146  mCoordinateTransform = transform;
147  mTriangularMesh.vertices.resize( nativeMesh->vertices.size() );
148  mExtent.setMinimal();
149  for ( int i = 0; i < nativeMesh->vertices.size(); ++i )
150  {
151  const QgsMeshVertex &vertex = nativeMesh->vertices.at( i );
152  if ( mCoordinateTransform.isValid() )
153  {
154  try
155  {
156  QgsPointXY mapPoint = mCoordinateTransform.transform( QgsPointXY( vertex.x(), vertex.y() ) );
157  QgsMeshVertex mapVertex( mapPoint );
158  mapVertex.addZValue( vertex.z() );
159  mapVertex.setM( vertex.m() );
160  mTriangularMesh.vertices[i] = mapVertex;
161  mExtent.include( mapPoint );
162  }
163  catch ( QgsCsException &cse )
164  {
165  Q_UNUSED( cse )
166  QgsDebugMsg( QStringLiteral( "Caught CRS exception %1" ).arg( cse.what() ) );
167  mTriangularMesh.vertices[i] = QgsMeshVertex();
168  }
169  }
170  else
171  {
172  mTriangularMesh.vertices[i] = vertex;
173  mExtent.include( vertex );
174  }
175  }
176 
177  // CREATE TRIANGULAR MESH
178  for ( int i = 0; i < nativeMesh->faces.size(); ++i )
179  {
180  const QgsMeshFace &face = nativeMesh->faces.at( i ) ;
181  triangulate( face, i );
182  }
183 
184  // CALCULATE CENTROIDS
185  mNativeMeshFaceCentroids.resize( nativeMesh->faces.size() );
186  for ( int i = 0; i < nativeMesh->faces.size(); ++i )
187  {
188  const QgsMeshFace &face = nativeMesh->faces.at( i ) ;
189  QVector<QPointF> points;
190  points.reserve( face.size() );
191  for ( int j = 0; j < face.size(); ++j )
192  {
193  int index = face.at( j );
194  const QgsMeshVertex &vertex = mTriangularMesh.vertices[index]; // we need projected vertices
195  points.push_back( vertex.toQPointF() );
196  }
197  QPolygonF poly( points );
198  double cx, cy;
199  ENP_centroid( poly, cx, cy );
200  mNativeMeshFaceCentroids[i] = QgsMeshVertex( cx, cy );
201  }
202 
203  // CALCULATE SPATIAL INDEX
204  mSpatialFaceIndex = QgsMeshSpatialIndex( mTriangularMesh, nullptr, QgsMesh::ElementType::Face );
205 
206  // SET ALL TRIANGLE CCW AND COMPUTE AVERAGE SIZE
207  finalizeTriangles();
208 
209  // CREATE EDGES
210  // remove all edges with invalid vertices
211  const QVector<QgsMeshEdge> edges = nativeMesh->edges;
212  for ( int nativeIndex = 0; nativeIndex < edges.size(); ++nativeIndex )
213  {
214  const QgsMeshEdge &edge = edges.at( nativeIndex );
215  if ( !( std::isnan( mTriangularMesh.vertex( edge.first ).x() ) ||
216  std::isnan( mTriangularMesh.vertex( edge.second ).x() ) ) )
217  {
218  mTriangularMesh.edges.push_back( edge );
219  mEdgesToNativeEdges.push_back( nativeIndex );
220  }
221  }
222 
223  // CALCULATE CENTROIDS
224  mNativeMeshEdgeCentroids.resize( nativeMesh->edgeCount() );
225  for ( int i = 0; i < nativeMesh->edgeCount(); ++i )
226  {
227  const QgsMeshEdge &edge = nativeMesh->edges.at( i ) ;
228  const QgsPoint &a = mTriangularMesh.vertices[edge.first];
229  const QgsPoint &b = mTriangularMesh.vertices[edge.second];
230  mNativeMeshEdgeCentroids[i] = QgsMeshVertex( ( a.x() + b.x() ) / 2.0, ( a.y() + b.y() ) / 2.0, ( a.z() + b.z() ) / 2.0 );
231  }
232 
233  // CALCULATE SPATIAL INDEX
234  mSpatialEdgeIndex = QgsMeshSpatialIndex( mTriangularMesh, nullptr, QgsMesh::ElementType::Edge );
235 
236  return true;
237 }
238 
239 void QgsTriangularMesh::finalizeTriangles()
240 {
241  mAverageTriangleSize = 0;
242  for ( int i = 0; i < mTriangularMesh.faceCount(); ++i )
243  {
244  QgsMeshFace &face = mTriangularMesh.faces[i];
245 
246  const QgsMeshVertex &v0 = mTriangularMesh.vertex( face[0] );
247  const QgsMeshVertex &v1 = mTriangularMesh.vertex( face[1] );
248  const QgsMeshVertex &v2 = mTriangularMesh.vertex( face[2] );
249 
250  QgsRectangle bbox = QgsMeshLayerUtils::triangleBoundingBox( v0, v1, v2 );
251 
252  mAverageTriangleSize += std::fmax( bbox.width(), bbox.height() );
253 
254  //To have consistent clock wise orientation of triangles which is necessary for 3D rendering
255  //Check the clock wise, and if it is not counter clock wise, swap indexes to make the oientation counter clock wise
256  double ux = v1.x() - v0.x();
257  double uy = v1.y() - v0.y();
258  double vx = v2.x() - v0.x();
259  double vy = v2.y() - v0.y();
260 
261  double crossProduct = ux * vy - uy * vx;
262  if ( crossProduct < 0 ) //CW -->change the orientation
263  {
264  std::swap( face[1], face[2] );
265  }
266  }
267  mAverageTriangleSize /= mTriangularMesh.faceCount();
268 }
269 
271 {
272  return mExtent;
273 }
274 
276 {
277  return mLod;
278 }
279 
281 {
282  switch ( type )
283  {
284  case QgsMesh::ElementType::Vertex:
285  return mTriangularMesh.vertexCount() != 0;
286  case QgsMesh::ElementType::Edge:
287  return mTriangularMesh.edgeCount() != 0;
288  case QgsMesh::ElementType::Face:
289  return mTriangularMesh.faceCount() != 0;
290  }
291 
292  return false;
293 }
294 
295 const QVector<QgsMeshVertex> &QgsTriangularMesh::vertices() const
296 {
297  return mTriangularMesh.vertices;
298 }
299 
300 const QVector<QgsMeshFace> &QgsTriangularMesh::triangles() const
301 {
302  return mTriangularMesh.faces;
303 }
304 
305 const QVector<QgsMeshEdge> &QgsTriangularMesh::edges() const
306 {
307  return mTriangularMesh.edges;
308 }
309 
310 const QVector<QgsMeshVertex> &QgsTriangularMesh::centroids() const
311 {
312  return faceCentroids();
313 }
314 
315 const QVector<QgsMeshVertex> &QgsTriangularMesh::faceCentroids() const
316 {
317  return mNativeMeshFaceCentroids;
318 }
319 
320 const QVector<QgsMeshVertex> &QgsTriangularMesh::edgeCentroids() const
321 {
322  return mNativeMeshEdgeCentroids;
323 }
324 
325 const QVector<int> &QgsTriangularMesh::trianglesToNativeFaces() const
326 {
327  return mTrianglesToNativeFaces;
328 }
329 
330 const QVector<int> &QgsTriangularMesh::edgesToNativeEdges() const
331 {
332  return mEdgesToNativeEdges;
333 }
334 
336 {
337  const QList<int> faceIndexes = mSpatialFaceIndex.intersects( QgsRectangle( point, point ) );
338  for ( const int faceIndex : faceIndexes )
339  {
340  const QgsMeshFace &face = mTriangularMesh.faces.at( faceIndex );
341  const QgsGeometry geom = QgsMeshUtils::toGeometry( face, mTriangularMesh.vertices );
342  if ( geom.contains( &point ) )
343  return faceIndex;
344  }
345  return -1;
346 }
347 
349 {
350  const QList<int> faceIndexes = mSpatialFaceIndex.intersects( QgsRectangle( point, point ) );
351 
352  for ( const int faceIndex : faceIndexes )
353  {
354  const QgsMeshFace &face = mTriangularMesh.faces.at( faceIndex );
355  if ( QgsMeshUtils::isInTriangleFace( point, face, mTriangularMesh.vertices ) )
356  return faceIndex;
357  }
358  return -1;
359 }
360 
361 QList<int> QgsTriangularMesh::faceIndexesForRectangle( const QgsRectangle &rectangle ) const
362 {
363  return mSpatialFaceIndex.intersects( rectangle );
364 }
365 
366 QList<int> QgsTriangularMesh::edgeIndexesForRectangle( const QgsRectangle &rectangle ) const
367 {
368  return mSpatialEdgeIndex.intersects( rectangle );
369 }
370 
371 QVector<QVector3D> QgsTriangularMesh::vertexNormals( float vertScale ) const
372 {
373  QVector<QVector3D> normales( vertices().count(), QVector3D( 0, 0, 0 ) );
374 
375  for ( const auto &face : triangles() )
376  {
377  for ( int i = 0; i < 3; i++ )
378  {
379  int index1( face.at( i ) );
380  int index2( face.at( ( i + 1 ) % 3 ) );
381  int index3( face.at( ( i + 2 ) % 3 ) );
382 
383  const QgsMeshVertex &vert( vertices().at( index1 ) );
384  const QgsMeshVertex &otherVert1( vertices().at( index2 ) );
385  const QgsMeshVertex &otherVert2( vertices().at( index3 ) );
386 
387  QVector3D v1( float( otherVert1.x() - vert.x() ), float( otherVert1.y() - vert.y() ), vertScale * float( otherVert1.z() - vert.z() ) );
388  QVector3D v2( float( otherVert2.x() - vert.x() ), float( otherVert2.y() - vert.y() ), vertScale * float( otherVert2.z() - vert.z() ) );
389 
390  normales[index1] += QVector3D::crossProduct( v1, v2 );
391  }
392  }
393  return normales;
394 }
395 
396 QVector<QgsTriangularMesh *> QgsTriangularMesh::simplifyMesh( double reductionFactor, int minimumTrianglesCount ) const
397 {
398  QVector<QgsTriangularMesh *> simplifiedMeshes;
399 
400  if ( mTriangularMesh.edgeCount() != 0 )
401  return simplifiedMeshes;
402 
403  if ( !( reductionFactor > 1 ) )
404  return simplifiedMeshes;
405 
406  size_t verticesCount = size_t( mTriangularMesh.vertices.count() );
407 
408  unsigned int baseIndexCount = mTriangularMesh.faceCount() * 3;
409 
410  QVector<unsigned int> indexes( mTriangularMesh.faces.count() * 3 );
411  for ( int i = 0; i < mTriangularMesh.faceCount(); ++i )
412  {
413  const QgsMeshFace &f = mTriangularMesh.face( i );
414  for ( int j = 0; j < 3; ++j )
415  indexes[i * 3 + j] = f.at( j );
416  }
417 
418  QVector<float> vertices( mTriangularMesh.vertices.count() * 3 );
419  for ( int i = 0; i < mTriangularMesh.vertices.count(); ++i )
420  {
421  const QgsMeshVertex &v = mTriangularMesh.vertex( i );
422  vertices[i * 3] = v.x() ;
423  vertices[i * 3 + 1] = v.y() ;
424  vertices[i * 3 + 2] = v.z() ;
425  }
426 
427  int path = 0;
428  while ( true )
429  {
430  QgsTriangularMesh *simplifiedMesh = new QgsTriangularMesh( *this );
431  size_t maxNumberOfIndexes = baseIndexCount / pow( reductionFactor, path + 1 );
432 
433  if ( indexes.size() <= int( maxNumberOfIndexes ) )
434  break;
435 
436  QVector<unsigned int> returnIndexes( indexes.size() );
437  //returned size could be different than goal size but not than the input indexes count
438  size_t size = meshopt_simplifySloppy(
439  returnIndexes.data(),
440  indexes.data(),
441  indexes.size(),
442  vertices.data(),
443  verticesCount,
444  sizeof( float ) * 3,
445  maxNumberOfIndexes );
446 
447 
448  returnIndexes.resize( size );
449 
450  if ( size == 0 || int( size ) >= indexes.size() )
451  {
452  QgsDebugMsg( QStringLiteral( "Mesh simplification failed after %1 path" ).arg( path + 1 ) );
453  break;
454  }
455 
456  QgsMesh newMesh;
457  newMesh.vertices = mTriangularMesh.vertices;
458 
459  newMesh.faces.resize( returnIndexes.size() / 3 );
460  for ( int i = 0; i < newMesh.faces.size(); ++i )
461  {
462  QgsMeshFace f( 3 );
463  for ( size_t j = 0; j < 3 ; ++j )
464  f[j] = returnIndexes.at( i * 3 + j ) ;
465  newMesh.faces[i ] = f;
466  }
467 
468  simplifiedMesh->mTriangularMesh = newMesh;
469  simplifiedMesh->mSpatialFaceIndex = QgsMeshSpatialIndex( simplifiedMesh->mTriangularMesh );
470  simplifiedMesh->finalizeTriangles();
471  simplifiedMeshes.push_back( simplifiedMesh );
472 
473  QgsDebugMsg( QStringLiteral( "Simplified mesh created with %1 triangles" ).arg( newMesh.faceCount() ) );
474 
475  simplifiedMesh->mTrianglesToNativeFaces = QVector<int>( simplifiedMesh->triangles().count(), 0 );
476  for ( int i = 0; i < simplifiedMesh->mTrianglesToNativeFaces.count(); ++i )
477  {
478  QgsMeshFace triangle = simplifiedMesh->triangles().at( i );
479  double x = 0;
480  double y = 0;
481  for ( size_t j = 0; j < 3 ; ++j )
482  {
483  x += mTriangularMesh.vertex( triangle[j] ).x();
484  y += mTriangularMesh.vertex( triangle[j] ).y();
485  }
486  x /= 3;
487  y /= 3;
488  QgsPoint centroid( x, y );
489  int indexInBaseMesh = faceIndexForPoint_v2( centroid );
490 
491  if ( indexInBaseMesh == -1 )
492  {
493  // sometime the centroid of simplified mesh could be outside the base mesh,
494  // so try with vertices of the simplified triangle
495  int j = 0;
496  while ( indexInBaseMesh == -1 && j < 3 )
497  indexInBaseMesh = faceIndexForPoint_v2( mTriangularMesh.vertex( triangle[j++] ) );
498  }
499 
500  if ( indexInBaseMesh > -1 && indexInBaseMesh < mTrianglesToNativeFaces.count() )
501  simplifiedMesh->mTrianglesToNativeFaces[i] = mTrianglesToNativeFaces[indexInBaseMesh];
502  }
503 
504  simplifiedMesh->mLod = path + 1;
505  simplifiedMesh->mBaseTriangularMesh = this;
506 
507  if ( simplifiedMesh->triangles().count() < minimumTrianglesCount )
508  break;
509 
510  indexes = returnIndexes;
511  ++path;
512  }
513 
514  return simplifiedMeshes;
515 }
516 
517 std::unique_ptr< QgsPolygon > QgsMeshUtils::toPolygon( const QgsMeshFace &face, const QVector<QgsMeshVertex> &vertices )
518 {
519  QVector<QgsPoint> ring;
520  for ( int j = 0; j < face.size(); ++j )
521  {
522  int vertexId = face[j];
523  Q_ASSERT( vertexId < vertices.size() );
524  const QgsPoint &vertex = vertices[vertexId];
525  ring.append( vertex );
526  }
527  std::unique_ptr< QgsPolygon > polygon = std::make_unique< QgsPolygon >();
528  polygon->setExteriorRing( new QgsLineString( ring ) );
529  return polygon;
530 }
531 
532 QgsGeometry QgsMeshUtils::toGeometry( const QgsMeshFace &face, const QVector<QgsMeshVertex> &vertices )
533 {
534  return QgsGeometry( QgsMeshUtils::toPolygon( face, vertices ) );
535 }
536 
537 static QSet<int> _nativeElementsFromElements( const QList<int> &indexes, const QVector<int> &elementToNativeElements )
538 {
539  QSet<int> nativeElements;
540  for ( const int index : indexes )
541  {
542  const int nativeIndex = elementToNativeElements[index];
543  nativeElements.insert( nativeIndex );
544  }
545  return nativeElements;
546 }
547 
548 QSet<int> QgsMeshUtils::nativeFacesFromTriangles( const QList<int> &triangleIndexes, const QVector<int> &trianglesToNativeFaces )
549 {
550  return _nativeElementsFromElements( triangleIndexes, trianglesToNativeFaces );
551 }
552 
553 QSet<int> QgsMeshUtils::nativeEdgesFromEdges( const QList<int> &edgesIndexes, const QVector<int> &edgesToNativeEdges )
554 {
555  return _nativeElementsFromElements( edgesIndexes, edgesToNativeEdges );
556 }
557 
558 
559 static double _isLeft2D( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p )
560 {
561  return ( p2.x() - p1.x() ) * ( p.y() - p1.y() ) - ( p.x() - p1.x() ) * ( p2.y() - p1.y() );
562 }
563 
564 static bool _isInTriangle2D( const QgsPoint &p, const QVector<QgsMeshVertex> &triangle )
565 {
566  return ( ( _isLeft2D( triangle[2], triangle[0], p ) * _isLeft2D( triangle[2], triangle[0], triangle[1] ) >= 0 )
567  && ( _isLeft2D( triangle[0], triangle[1], p ) * _isLeft2D( triangle[0], triangle[1], triangle[2] ) >= 0 )
568  && ( _isLeft2D( triangle[2], triangle[1], p ) * _isLeft2D( triangle[2], triangle[1], triangle[0] ) >= 0 ) );
569 }
570 
571 bool QgsMeshUtils::isInTriangleFace( const QgsPointXY point, const QgsMeshFace &face, const QVector<QgsMeshVertex> &vertices )
572 {
573  if ( face.count() != 3 )
574  return false;
575 
576  QVector<QgsMeshVertex> triangle( 3 );
577  for ( int i = 0; i < 3; ++i )
578  {
579  if ( face[i] > vertices.count() )
580  return false;
581  triangle[i] = vertices[face[i]];
582  }
583 
584  const QgsPoint p( point.x(), point.y() );
585 
586  return _isInTriangle2D( p, triangle );
587 }
588 
589 QSet<int> QgsMeshUtils::nativeVerticesFromTriangles( const QList<int> &triangleIndexes, const QVector<QgsMeshFace> &triangles )
590 {
591  QSet<int> uniqueVertices;
592  for ( int triangleIndex : triangleIndexes )
593  {
594  const QgsMeshFace triangle = triangles[triangleIndex];
595  for ( int i : triangle )
596  {
597  uniqueVertices.insert( i );
598  }
599  }
600  return uniqueVertices;
601 }
602 
603 QSet<int> QgsMeshUtils::nativeVerticesFromEdges( const QList<int> &edgesIndexes, const QVector<QgsMeshEdge> &edges )
604 {
605  QSet<int> uniqueVertices;
606  for ( int edgeIndex : edgesIndexes )
607  {
608  const QgsMeshEdge edge = edges[edgeIndex];
609  uniqueVertices.insert( edge.first );
610  uniqueVertices.insert( edge.second );
611  }
612  return uniqueVertices;
613 }
Class for doing transforms between two map coordinate systems.
QgsCoordinateReferenceSystem sourceCrs() const
Returns the source coordinate reference system, which the transform will transform coordinates from.
QgsPointXY transform(const QgsPointXY &point, TransformDirection direction=ForwardTransform) const SIP_THROW(QgsCsException)
Transform the point from the source CRS to the destination CRS.
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system, which the transform will transform coordinates t...
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:66
QString what() const
Definition: qgsexception.h:48
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:124
bool contains(const QgsPointXY *p) const
Returns true if the geometry contains the point p.
Line string geometry type, with support for z-dimension and m-values.
Definition: qgslinestring.h:44
A spatial index for QgsMeshFace or QgsMeshEdge objects.
QList< int > intersects(const QgsRectangle &rectangle) const
Returns a list of face ids with a bounding box which intersects the specified rectangle.
A class to represent a 2D point.
Definition: qgspointxy.h:59
double y
Definition: qgspointxy.h:63
Q_GADGET double x
Definition: qgspointxy.h:62
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:49
QPointF toQPointF() const SIP_HOLDGIL
Returns the point as a QPointF.
Definition: qgspoint.h:331
bool addZValue(double zValue=0) override
Adds a z-dimension to the geometry, initialized to a preset value.
Definition: qgspoint.cpp:551
Q_GADGET double x
Definition: qgspoint.h:52
double z
Definition: qgspoint.h:54
double m
Definition: qgspoint.h:55
double y
Definition: qgspoint.h:53
void setM(double m) SIP_HOLDGIL
Sets the point's m-value.
Definition: qgspoint.h:319
A rectangle specified with double values.
Definition: qgsrectangle.h:42
void include(const QgsPointXY &p)
Updates the rectangle to include the specified point.
Definition: qgsrectangle.h:307
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
Definition: qgsrectangle.h:230
void setMinimal() SIP_HOLDGIL
Set a rectangle so that min corner is at max and max corner is at min.
Definition: qgsrectangle.h:172
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:223
Triangular/Derived Mesh is mesh with vertices in map coordinates.
bool update(QgsMesh *nativeMesh, const QgsCoordinateTransform &transform=QgsCoordinateTransform())
Constructs triangular mesh from layer's native mesh and transform to destination CRS.
const QVector< QgsMeshVertex > & edgeCentroids() const
Returns centroids of the native edges in map CRS.
const QVector< QgsMeshFace > & triangles() const
Returns triangles.
QVector< QgsTriangularMesh * > simplifyMesh(double reductionFactor, int minimumTrianglesCount=10) const
Returns simplified meshes.
QgsTriangularMesh()
Ctor.
int levelOfDetail() const
Returns the corresponding index of level of detail on which this mesh is associated.
QgsRectangle extent() const
Returns the extent of the triangular mesh in map coordinates.
int faceIndexForPoint(const QgsPointXY &point) const
Finds index of triangle at given point It uses spatial indexing.
double averageTriangleSize() const
Returns the average size of triangles in map unit.
QList< int > edgeIndexesForRectangle(const QgsRectangle &rectangle) const
Finds indexes of edges intersecting given bounding box It uses spatial indexing.
Q_DECL_DEPRECATED const QVector< QgsMeshVertex > & centroids() const
Returns centroids of the native faces in map CRS.
const QVector< QgsMeshVertex > & vertices() const
Returns vertices in map coordinate system.
const QVector< QgsMeshEdge > & edges() const
Returns edges.
bool contains(const QgsMesh::ElementType &type) const
Returns whether the mesh contains at mesh elements of given type.
QVector< QVector3D > vertexNormals(float vertScale) const
Calculates and returns normale vector on each vertex that is part of any face.
~QgsTriangularMesh()
Dtor.
const QVector< QgsMeshVertex > & faceCentroids() const
Returns centroids of the native faces in map CRS.
const QVector< int > & trianglesToNativeFaces() const
Returns mapping between triangles and original faces.
const QVector< int > & edgesToNativeEdges() const
Returns mapping between edges and original edges.
QList< int > faceIndexesForRectangle(const QgsRectangle &rectangle) const
Finds indexes of triangles intersecting given bounding box It uses spatial indexing.
int faceIndexForPoint_v2(const QgsPointXY &point) const
Finds index of triangle at given point It uses spatial indexing and don't use geos to be faster.
bool isInTriangleFace(const QgsPointXY point, const QgsMeshFace &face, const QVector< QgsMeshVertex > &vertices)
Tests if point p is on the face defined with vertices.
CORE_EXPORT QSet< int > nativeEdgesFromEdges(const QList< int > &edgesIndexes, const QVector< int > &edgesToNativeEdges)
Returns unique native faces indexes from list of triangle indexes.
CORE_EXPORT std::unique_ptr< QgsPolygon > toPolygon(const QgsMeshFace &face, const QVector< QgsMeshVertex > &vertices)
Returns face as polygon geometry, caller is responsible for delete.
CORE_EXPORT QgsGeometry toGeometry(const QgsMeshFace &face, const QVector< QgsMeshVertex > &vertices)
Returns face as polygon geometry.
CORE_EXPORT QSet< int > nativeVerticesFromEdges(const QList< int > &edgesIndexes, const QVector< QgsMeshEdge > &edges)
Returns unique native faces indexes from list of vertices of triangles.
CORE_EXPORT QSet< int > nativeVerticesFromTriangles(const QList< int > &triangleIndexes, const QVector< QgsMeshFace > &triangles)
Returns unique native vertex indexes from list of vertices of triangles.
CORE_EXPORT QSet< int > nativeFacesFromTriangles(const QList< int > &triangleIndexes, const QVector< int > &trianglesToNativeFaces)
Returns unique native faces indexes from list of triangle indexes.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QVector< int > QgsMeshFace
List of vertex indexes.
QPair< int, int > QgsMeshEdge
Edge is a straight line seqment between 2 points.
QgsPoint QgsMeshVertex
xyz coords of vertex
Mesh - vertices, edges and faces.
int vertexCount() const
Returns number of vertices.
QVector< QgsMeshVertex > vertices
QgsMeshFace face(int index) const
Returns a face at the index.
QVector< QgsMeshFace > faces
int faceCount() const
Returns number of faces.
ElementType
Defines type of mesh elements.
QgsMeshVertex vertex(int index) const
Returns a vertex at the index.
int edgeCount() const
Returns number of edge.
QVector< QgsMeshEdge > edges