QGIS API Documentation  3.21.0-Master (5b68dc587e)
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 static void triangulateFaces( const QgsMeshFace &face,
85  int nativeIndex,
86  QVector<QgsMeshFace> &destinationFaces,
87  QVector<int> &triangularToNative,
88  const QgsMesh &verticesMeshSource )
89 {
90  int vertexCount = face.size();
91  if ( vertexCount < 3 )
92  return;
93 
94  while ( vertexCount > 3 )
95  {
96  // clip one ear from last 2 and first vertex
97  const QgsMeshFace ear = { face[vertexCount - 2], face[vertexCount - 1], face[0] };
98  if ( !( std::isnan( verticesMeshSource.vertex( ear[0] ).x() ) ||
99  std::isnan( verticesMeshSource.vertex( ear[1] ).x() ) ||
100  std::isnan( verticesMeshSource.vertex( ear[2] ).x() ) ) )
101  {
102  destinationFaces.push_back( ear );
103  triangularToNative.push_back( nativeIndex );
104  }
105  --vertexCount;
106  }
107 
108  const QgsMeshFace triangle = { face[1], face[2], face[0] };
109  if ( !( std::isnan( verticesMeshSource.vertex( triangle[0] ).x() ) ||
110  std::isnan( verticesMeshSource.vertex( triangle[1] ).x() ) ||
111  std::isnan( verticesMeshSource.vertex( triangle[2] ).x() ) ) )
112  {
113  destinationFaces.push_back( triangle );
114  triangularToNative.push_back( nativeIndex );
115  }
116 }
117 
118 void QgsTriangularMesh::triangulate( const QgsMeshFace &face, int nativeIndex )
119 {
120  triangulateFaces( face, nativeIndex, mTriangularMesh.faces, mTrianglesToNativeFaces, mTriangularMesh );
121 }
122 
123 QgsMeshVertex QgsTriangularMesh::transformVertex( const QgsMeshVertex &vertex, Qgis::TransformDirection direction ) const
124 {
125  QgsMeshVertex transformedVertex = vertex;
126 
127  if ( mCoordinateTransform.isValid() )
128  {
129  try
130  {
131  transformedVertex.transform( mCoordinateTransform, direction );
132  }
133  catch ( QgsCsException &cse )
134  {
135  Q_UNUSED( cse )
136  QgsDebugMsg( QStringLiteral( "Caught CRS exception %1" ).arg( cse.what() ) );
137  transformedVertex = QgsMeshVertex();
138  }
139  }
140 
141  return transformedVertex;
142 }
143 
144 QgsMeshVertex QgsTriangularMesh::calculateCentroid( const QgsMeshFace &nativeFace )
145 {
146  return QgsMeshUtils::centroid( nativeFace, mTriangularMesh.vertices );
147 }
148 
150 {
151  return mAverageTriangleSize;
152 }
153 
156 
157 bool QgsTriangularMesh::update( QgsMesh *nativeMesh, const QgsCoordinateTransform &transform )
158 {
159  Q_ASSERT( nativeMesh );
160 
161  bool needUpdateVerticesCoordinates = mTriangularMesh.vertices.size() != nativeMesh->vertices.size() ||
162  ( ( mCoordinateTransform.isValid() || transform.isValid() ) &&
163  ( mCoordinateTransform.sourceCrs() != transform.sourceCrs() ||
164  mCoordinateTransform.destinationCrs() != transform.destinationCrs() ||
165  mCoordinateTransform.isValid() != transform.isValid() ) ) ;
166 
167  bool needUpdateFrame = mTriangularMesh.vertices.size() != nativeMesh->vertices.size() ||
168  mNativeMeshFaceCentroids.size() != nativeMesh->faces.size() ||
169  mTriangularMesh.faces.size() < nativeMesh->faces.size() ||
170  mTriangularMesh.edges.size() != nativeMesh->edges.size();
171 
172 
173  // FIND OUT IF UPDATE IS NEEDED
174  if ( ! needUpdateVerticesCoordinates && !needUpdateFrame )
175  return false;
176 
177  // CLEAN-UP
178  mTriangularMesh.vertices.clear();
179  if ( needUpdateFrame )
180  {
181  mTriangularMesh.faces.clear();
182  mTriangularMesh.edges.clear();
183  mEdgesToNativeEdges.clear();
184  mTrianglesToNativeFaces.clear();
185  }
186 
187  // TRANSFORM VERTICES
188  mCoordinateTransform = transform;
189  mTriangularMesh.vertices.resize( nativeMesh->vertices.size() );
190  mExtent.setMinimal();
191  for ( int i = 0; i < nativeMesh->vertices.size(); ++i )
192  {
193  mTriangularMesh.vertices[i] = nativeToTriangularCoordinates( nativeMesh->vertices.at( i ) );
194  mExtent.include( mTriangularMesh.vertices.at( i ) );
195  }
196 
197  if ( needUpdateFrame )
198  {
199  // CREATE TRIANGULAR MESH
200  for ( int i = 0; i < nativeMesh->faces.size(); ++i )
201  {
202  const QgsMeshFace &face = nativeMesh->faces.at( i ) ;
203  triangulate( face, i );
204  }
205  }
206 
207  // CALCULATE CENTROIDS
208  mNativeMeshFaceCentroids.resize( nativeMesh->faces.size() );
209  for ( int i = 0; i < nativeMesh->faces.size(); ++i )
210  {
211  const QgsMeshFace &face = nativeMesh->faces.at( i ) ;
212  mNativeMeshFaceCentroids[i] = calculateCentroid( face );
213  }
214 
215  // CALCULATE SPATIAL INDEX
216  mSpatialFaceIndex = QgsMeshSpatialIndex( mTriangularMesh, nullptr, QgsMesh::ElementType::Face );
217 
218  if ( needUpdateFrame )
219  {
220  // SET ALL TRIANGLE CCW AND COMPUTE AVERAGE SIZE
221  finalizeTriangles();
222  }
223 
224  // CREATE EDGES
225  // remove all edges with invalid vertices
226  if ( needUpdateFrame )
227  {
228  const QVector<QgsMeshEdge> edges = nativeMesh->edges;
229  for ( int nativeIndex = 0; nativeIndex < edges.size(); ++nativeIndex )
230  {
231  const QgsMeshEdge &edge = edges.at( nativeIndex );
232  if ( !( std::isnan( mTriangularMesh.vertex( edge.first ).x() ) ||
233  std::isnan( mTriangularMesh.vertex( edge.second ).x() ) ) )
234  {
235  mTriangularMesh.edges.push_back( edge );
236  mEdgesToNativeEdges.push_back( nativeIndex );
237  }
238  }
239  }
240 
241  // CALCULATE CENTROIDS
242  mNativeMeshEdgeCentroids.resize( nativeMesh->edgeCount() );
243  for ( int i = 0; i < nativeMesh->edgeCount(); ++i )
244  {
245  const QgsMeshEdge &edge = nativeMesh->edges.at( i ) ;
246  const QgsPoint &a = mTriangularMesh.vertices[edge.first];
247  const QgsPoint &b = mTriangularMesh.vertices[edge.second];
248  mNativeMeshEdgeCentroids[i] = QgsMeshVertex( ( a.x() + b.x() ) / 2.0, ( a.y() + b.y() ) / 2.0, ( a.z() + b.z() ) / 2.0 );
249  }
250 
251  // CALCULATE SPATIAL INDEX
252  mSpatialEdgeIndex = QgsMeshSpatialIndex( mTriangularMesh, nullptr, QgsMesh::ElementType::Edge );
253 
254  return true;
255 }
256 
257 void QgsTriangularMesh::finalizeTriangles()
258 {
259  mAverageTriangleSize = 0;
260  for ( int i = 0; i < mTriangularMesh.faceCount(); ++i )
261  {
262  QgsMeshFace &face = mTriangularMesh.faces[i];
263 
264  const QgsMeshVertex &v0 = mTriangularMesh.vertex( face[0] );
265  const QgsMeshVertex &v1 = mTriangularMesh.vertex( face[1] );
266  const QgsMeshVertex &v2 = mTriangularMesh.vertex( face[2] );
267 
268  QgsRectangle bbox = QgsMeshLayerUtils::triangleBoundingBox( v0, v1, v2 );
269 
270  mAverageTriangleSize += std::fmax( bbox.width(), bbox.height() );
271 
272  QgsMeshUtils::setCounterClockwise( face, v0, v1, v2 );
273  }
274  mAverageTriangleSize /= mTriangularMesh.faceCount();
275 }
276 
278 {
279  return transformVertex( vertex, Qgis::TransformDirection::Forward );
280 }
281 
283 {
284  return transformVertex( vertex, Qgis::TransformDirection::Reverse );
285 }
286 
288 {
290  if ( mCoordinateTransform.isValid() )
291  {
292  try
293  {
294  nativeExtent = mCoordinateTransform.transform( extent(), Qgis::TransformDirection::Reverse );
295  }
296  catch ( QgsCsException &cse )
297  {
298  Q_UNUSED( cse )
299  QgsDebugMsg( QStringLiteral( "Caught CRS exception %1" ).arg( cse.what() ) );
300  }
301  }
302  else
303  nativeExtent = extent();
304 
305  return nativeExtent;
306 }
307 
309 {
310  if ( !mIsExtentValid )
311  {
312  mExtent.setMinimal();
313  for ( int i = 0; i < mTriangularMesh.vertices.size(); ++i )
314  if ( !mTriangularMesh.vertices.at( i ).isEmpty() )
315  mExtent.include( mTriangularMesh.vertices.at( i ) );
316 
317  mIsExtentValid = true;
318  }
319  return mExtent;
320 }
321 
323 {
324  return mLod;
325 }
326 
328 {
329  switch ( type )
330  {
331  case QgsMesh::ElementType::Vertex:
332  return mTriangularMesh.vertexCount() != 0;
333  case QgsMesh::ElementType::Edge:
334  return mTriangularMesh.edgeCount() != 0;
335  case QgsMesh::ElementType::Face:
336  return mTriangularMesh.faceCount() != 0;
337  }
338 
339  return false;
340 }
341 
342 void QgsTriangularMesh::addVertex( const QgsMeshVertex &vertex )
343 {
344  QgsMeshVertex vertexInTriangularCoordinates = nativeToTriangularCoordinates( vertex );
345  mTriangularMesh.vertices.append( vertexInTriangularCoordinates );
346  if ( !vertexInTriangularCoordinates.isEmpty() )
347  mExtent.include( vertexInTriangularCoordinates );
348 }
349 
350 const QVector<QgsMeshVertex> &QgsTriangularMesh::vertices() const
351 {
352  return mTriangularMesh.vertices;
353 }
354 
355 const QVector<QgsMeshFace> &QgsTriangularMesh::triangles() const
356 {
357  return mTriangularMesh.faces;
358 }
359 
360 const QVector<QgsMeshEdge> &QgsTriangularMesh::edges() const
361 {
362  return mTriangularMesh.edges;
363 }
364 
365 const QVector<QgsMeshVertex> &QgsTriangularMesh::centroids() const
366 {
367  return faceCentroids();
368 }
369 
370 const QVector<QgsMeshVertex> &QgsTriangularMesh::faceCentroids() const
371 {
372  return mNativeMeshFaceCentroids;
373 }
374 
375 const QVector<QgsMeshVertex> &QgsTriangularMesh::edgeCentroids() const
376 {
377  return mNativeMeshEdgeCentroids;
378 }
379 
380 const QVector<int> &QgsTriangularMesh::trianglesToNativeFaces() const
381 {
382  return mTrianglesToNativeFaces;
383 }
384 
385 const QVector<int> &QgsTriangularMesh::edgesToNativeEdges() const
386 {
387  return mEdgesToNativeEdges;
388 }
389 
391 {
392  QgsPointXY mapPoint;
393  if ( mCoordinateTransform.isValid() )
394  {
395  try
396  {
397  mapPoint = mCoordinateTransform.transform( point );
398  }
399  catch ( QgsCsException &cse )
400  {
401  Q_UNUSED( cse )
402  QgsDebugMsg( QStringLiteral( "Caught CRS exception %1" ).arg( cse.what() ) );
403  mapPoint = point;
404  }
405  }
406  else
407  mapPoint = point;
408 
409  return point;
410 }
411 
413 {
414  const QList<int> faceIndexes = mSpatialFaceIndex.intersects( QgsRectangle( point, point ) );
415  for ( const int faceIndex : faceIndexes )
416  {
417  const QgsMeshFace &face = mTriangularMesh.faces.at( faceIndex );
418  const QgsGeometry geom = QgsMeshUtils::toGeometry( face, mTriangularMesh.vertices );
419  if ( geom.contains( &point ) )
420  return faceIndex;
421  }
422  return -1;
423 }
424 
426 {
427  int triangleIndex = faceIndexForPoint_v2( point );
428  if ( triangleIndex == -1 )
429  return -1;
430 
431  if ( triangleIndex < mTrianglesToNativeFaces.count() )
432  return mTrianglesToNativeFaces.at( triangleIndex );
433 
434  return -1;
435 }
436 
438 {
439  QSet<int> concernedFaceIndex = QgsMeshUtils::nativeFacesFromTriangles(
440  faceIndexesForRectangle( rectangle ),
442  return concernedFaceIndex.values();
443 }
444 
446 {
447  const QList<int> faceIndexes = mSpatialFaceIndex.intersects( QgsRectangle( point, point ) );
448 
449  for ( const int faceIndex : faceIndexes )
450  {
451  const QgsMeshFace &face = mTriangularMesh.faces.at( faceIndex );
452  if ( QgsMeshUtils::isInTriangleFace( point, face, mTriangularMesh.vertices ) )
453  return faceIndex;
454  }
455  return -1;
456 }
457 
458 QList<int> QgsTriangularMesh::faceIndexesForRectangle( const QgsRectangle &rectangle ) const
459 {
460  return mSpatialFaceIndex.intersects( rectangle );
461 }
462 
463 QList<int> QgsTriangularMesh::edgeIndexesForRectangle( const QgsRectangle &rectangle ) const
464 {
465  return mSpatialEdgeIndex.intersects( rectangle );
466 }
467 
468 QVector<QVector3D> QgsTriangularMesh::vertexNormals( float vertScale ) const
469 {
470  QVector<QVector3D> normales( vertices().count(), QVector3D( 0, 0, 0 ) );
471 
472  for ( const auto &face : triangles() )
473  {
474  if ( face.isEmpty() )
475  continue;
476 
477  for ( int i = 0; i < 3; i++ )
478  {
479  int index1( face.at( i ) );
480  int index2( face.at( ( i + 1 ) % 3 ) );
481  int index3( face.at( ( i + 2 ) % 3 ) );
482 
483  const QgsMeshVertex &vert( vertices().at( index1 ) );
484  const QgsMeshVertex &otherVert1( vertices().at( index2 ) );
485  const QgsMeshVertex &otherVert2( vertices().at( index3 ) );
486 
487  QVector3D v1( float( otherVert1.x() - vert.x() ), float( otherVert1.y() - vert.y() ), vertScale * float( otherVert1.z() - vert.z() ) );
488  QVector3D v2( float( otherVert2.x() - vert.x() ), float( otherVert2.y() - vert.y() ), vertScale * float( otherVert2.z() - vert.z() ) );
489 
490  normales[index1] += QVector3D::crossProduct( v1, v2 );
491  }
492  }
493  return normales;
494 }
495 
496 QVector<QgsTriangularMesh *> QgsTriangularMesh::simplifyMesh( double reductionFactor, int minimumTrianglesCount ) const
497 {
498  QVector<QgsTriangularMesh *> simplifiedMeshes;
499 
500  if ( mTriangularMesh.edgeCount() != 0 )
501  return simplifiedMeshes;
502 
503  if ( !( reductionFactor > 1 ) )
504  return simplifiedMeshes;
505 
506  size_t verticesCount = size_t( mTriangularMesh.vertices.count() );
507 
508  unsigned int baseIndexCount = mTriangularMesh.faceCount() * 3;
509 
510  QVector<unsigned int> indexes( mTriangularMesh.faces.count() * 3 );
511  for ( int i = 0; i < mTriangularMesh.faceCount(); ++i )
512  {
513  const QgsMeshFace &f = mTriangularMesh.face( i );
514  for ( int j = 0; j < 3; ++j )
515  indexes[i * 3 + j] = f.at( j );
516  }
517 
518  QVector<float> vertices( mTriangularMesh.vertices.count() * 3 );
519  for ( int i = 0; i < mTriangularMesh.vertices.count(); ++i )
520  {
521  const QgsMeshVertex &v = mTriangularMesh.vertex( i );
522  vertices[i * 3] = v.x() ;
523  vertices[i * 3 + 1] = v.y() ;
524  vertices[i * 3 + 2] = v.z() ;
525  }
526 
527  int path = 0;
528  while ( true )
529  {
530  QgsTriangularMesh *simplifiedMesh = new QgsTriangularMesh( *this );
531  size_t maxNumberOfIndexes = baseIndexCount / pow( reductionFactor, path + 1 );
532 
533  if ( indexes.size() <= int( maxNumberOfIndexes ) )
534  break;
535 
536  QVector<unsigned int> returnIndexes( indexes.size() );
537  //returned size could be different than goal size but not than the input indexes count
538  size_t size = meshopt_simplifySloppy(
539  returnIndexes.data(),
540  indexes.data(),
541  indexes.size(),
542  vertices.data(),
543  verticesCount,
544  sizeof( float ) * 3,
545  maxNumberOfIndexes );
546 
547 
548  returnIndexes.resize( size );
549 
550  if ( size == 0 || int( size ) >= indexes.size() )
551  {
552  QgsDebugMsg( QStringLiteral( "Mesh simplification failed after %1 path" ).arg( path + 1 ) );
553  break;
554  }
555 
556  QgsMesh newMesh;
557  newMesh.vertices = mTriangularMesh.vertices;
558 
559  newMesh.faces.resize( returnIndexes.size() / 3 );
560  for ( int i = 0; i < newMesh.faces.size(); ++i )
561  {
562  QgsMeshFace f( 3 );
563  for ( size_t j = 0; j < 3 ; ++j )
564  f[j] = returnIndexes.at( i * 3 + j ) ;
565  newMesh.faces[i ] = f;
566  }
567 
568  simplifiedMesh->mTriangularMesh = newMesh;
569  simplifiedMesh->mSpatialFaceIndex = QgsMeshSpatialIndex( simplifiedMesh->mTriangularMesh );
570  simplifiedMesh->finalizeTriangles();
571  simplifiedMeshes.push_back( simplifiedMesh );
572 
573  QgsDebugMsg( QStringLiteral( "Simplified mesh created with %1 triangles" ).arg( newMesh.faceCount() ) );
574 
575  simplifiedMesh->mTrianglesToNativeFaces = QVector<int>( simplifiedMesh->triangles().count(), 0 );
576  for ( int i = 0; i < simplifiedMesh->mTrianglesToNativeFaces.count(); ++i )
577  {
578  QgsMeshFace triangle = simplifiedMesh->triangles().at( i );
579  double x = 0;
580  double y = 0;
581  for ( size_t j = 0; j < 3 ; ++j )
582  {
583  x += mTriangularMesh.vertex( triangle[j] ).x();
584  y += mTriangularMesh.vertex( triangle[j] ).y();
585  }
586  x /= 3;
587  y /= 3;
588  QgsPoint centroid( x, y );
589  int indexInBaseMesh = faceIndexForPoint_v2( centroid );
590 
591  if ( indexInBaseMesh == -1 )
592  {
593  // sometime the centroid of simplified mesh could be outside the base mesh,
594  // so try with vertices of the simplified triangle
595  int j = 0;
596  while ( indexInBaseMesh == -1 && j < 3 )
597  indexInBaseMesh = faceIndexForPoint_v2( mTriangularMesh.vertex( triangle[j++] ) );
598  }
599 
600  if ( indexInBaseMesh > -1 && indexInBaseMesh < mTrianglesToNativeFaces.count() )
601  simplifiedMesh->mTrianglesToNativeFaces[i] = mTrianglesToNativeFaces[indexInBaseMesh];
602  }
603 
604  simplifiedMesh->mLod = path + 1;
605  simplifiedMesh->mBaseTriangularMesh = this;
606 
607  if ( simplifiedMesh->triangles().count() < minimumTrianglesCount )
608  break;
609 
610  indexes = returnIndexes;
611  ++path;
612  }
613 
614  return simplifiedMeshes;
615 }
616 
617 std::unique_ptr< QgsPolygon > QgsMeshUtils::toPolygon( const QgsMeshFace &face, const QVector<QgsMeshVertex> &vertices )
618 {
619  QVector<QgsPoint> ring;
620  for ( int j = 0; j < face.size(); ++j )
621  {
622  int vertexId = face[j];
623  Q_ASSERT( vertexId < vertices.size() );
624  const QgsPoint &vertex = vertices[vertexId];
625  ring.append( vertex );
626  }
627  std::unique_ptr< QgsPolygon > polygon = std::make_unique< QgsPolygon >();
628  polygon->setExteriorRing( new QgsLineString( ring ) );
629  return polygon;
630 }
631 
632 QgsGeometry QgsMeshUtils::toGeometry( const QgsMeshFace &face, const QVector<QgsMeshVertex> &vertices )
633 {
634  return QgsGeometry( QgsMeshUtils::toPolygon( face, vertices ) );
635 }
636 
637 static QSet<int> _nativeElementsFromElements( const QList<int> &indexes, const QVector<int> &elementToNativeElements )
638 {
639  QSet<int> nativeElements;
640  for ( const int index : indexes )
641  {
642  const int nativeIndex = elementToNativeElements[index];
643  nativeElements.insert( nativeIndex );
644  }
645  return nativeElements;
646 }
647 
648 QSet<int> QgsMeshUtils::nativeFacesFromTriangles( const QList<int> &triangleIndexes, const QVector<int> &trianglesToNativeFaces )
649 {
650  return _nativeElementsFromElements( triangleIndexes, trianglesToNativeFaces );
651 }
652 
653 QSet<int> QgsMeshUtils::nativeEdgesFromEdges( const QList<int> &edgesIndexes, const QVector<int> &edgesToNativeEdges )
654 {
655  return _nativeElementsFromElements( edgesIndexes, edgesToNativeEdges );
656 }
657 
658 
659 static double _isLeft2D( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p )
660 {
661  return ( p2.x() - p1.x() ) * ( p.y() - p1.y() ) - ( p.x() - p1.x() ) * ( p2.y() - p1.y() );
662 }
663 
664 static bool _isInTriangle2D( const QgsPoint &p, const QVector<QgsMeshVertex> &triangle )
665 {
666  return ( ( _isLeft2D( triangle[2], triangle[0], p ) * _isLeft2D( triangle[2], triangle[0], triangle[1] ) >= 0 )
667  && ( _isLeft2D( triangle[0], triangle[1], p ) * _isLeft2D( triangle[0], triangle[1], triangle[2] ) >= 0 )
668  && ( _isLeft2D( triangle[2], triangle[1], p ) * _isLeft2D( triangle[2], triangle[1], triangle[0] ) >= 0 ) );
669 }
670 
671 bool QgsMeshUtils::isInTriangleFace( const QgsPointXY point, const QgsMeshFace &face, const QVector<QgsMeshVertex> &vertices )
672 {
673  if ( face.count() != 3 )
674  return false;
675 
676  QVector<QgsMeshVertex> triangle( 3 );
677  for ( int i = 0; i < 3; ++i )
678  {
679  if ( face[i] > vertices.count() )
680  return false;
681  triangle[i] = vertices[face[i]];
682  }
683 
684  const QgsPoint p( point.x(), point.y() );
685 
686  return _isInTriangle2D( p, triangle );
687 }
688 
689 QSet<int> QgsMeshUtils::nativeVerticesFromTriangles( const QList<int> &triangleIndexes, const QVector<QgsMeshFace> &triangles )
690 {
691  QSet<int> uniqueVertices;
692  for ( int triangleIndex : triangleIndexes )
693  {
694  const QgsMeshFace triangle = triangles[triangleIndex];
695  for ( int i : triangle )
696  {
697  uniqueVertices.insert( i );
698  }
699  }
700  return uniqueVertices;
701 }
702 
703 QSet<int> QgsMeshUtils::nativeVerticesFromEdges( const QList<int> &edgesIndexes, const QVector<QgsMeshEdge> &edges )
704 {
705  QSet<int> uniqueVertices;
706  for ( int edgeIndex : edgesIndexes )
707  {
708  const QgsMeshEdge edge = edges[edgeIndex];
709  uniqueVertices.insert( edge.first );
710  uniqueVertices.insert( edge.second );
711  }
712  return uniqueVertices;
713 }
714 
716 {
717  //if necessary defined removes triangles index
718  if ( changes.mRemovedTriangleIndexes.isEmpty() && !changes.mNativeFaceIndexesToRemove.isEmpty() )
719  {
720  for ( int nf = 0; nf < changes.mNativeFaceIndexesToRemove.count(); ++nf )
721  {
722  int nativeIndex = changes.mNativeFaceIndexesToRemove.at( nf );
723  const QgsMeshFace &nativeFace = changes.mNativeFacesToRemove.at( nf );
724  Q_ASSERT( !nativeFace.isEmpty() );
725 
726  QgsRectangle nativeFaceExtent( mTriangularMesh.vertex( nativeFace.at( 0 ) ), mTriangularMesh.vertex( nativeFace.at( 0 ) ) );
727  for ( int i = 1; i < nativeFace.count(); ++i )
728  {
729  const QgsMeshVertex &triangularVertex = mTriangularMesh.vertex( nativeFace.at( i ) );
730  nativeFaceExtent.include( triangularVertex );
731  }
732 
733  QList<int> concernedTriangle = faceIndexesForRectangle( nativeFaceExtent );
734  //Remove only those corresponding to the native face
735  for ( int i = 0; i < concernedTriangle.count(); ++i )
736  {
737  int triangleIndex = concernedTriangle.at( i );
738  if ( mTrianglesToNativeFaces.at( triangleIndex ) == nativeIndex )
739  changes.mRemovedTriangleIndexes.append( triangleIndex );
740  }
741  }
742  }
743 
744  if ( changes.mOldZValue.isEmpty() && !changes.mNewZValue.isEmpty() )
745  {
746  changes.mOldZValue.reserve( changes.mNewZValue.count() );
747  for ( int i = 0; i < changes.mNewZValue.count(); ++i )
748  changes.mOldZValue.append( mTriangularMesh.vertices.at( changes.mChangedVerticesCoordinates.at( i ) ).z() );
749  }
750 
751  if ( changes.mTriangleIndexesGeometryChanged.isEmpty() && !changes.mNativeFaceIndexesGeometryChanged.isEmpty() )
752  {
753  for ( int i = 0; i < changes.mNativeFaceIndexesGeometryChanged.count(); ++i )
754  {
755  const QgsMeshFace &nativeFace = changes.mNativeFacesGeometryChanged.at( i );
756  if ( nativeFace.count() < 2 )
757  continue;
758  QgsRectangle bbox( mTriangularMesh.vertices.at( nativeFace.at( 0 ) ), mTriangularMesh.vertices.at( nativeFace.at( 1 ) ) );
759 
760  for ( int i = 2; i < nativeFace.count(); ++i )
761  bbox.include( mTriangularMesh.vertices.at( nativeFace.at( i ) ) );
762 
763  QList<int> triangleIndexes = faceIndexesForRectangle( bbox );
764  int pos = 0;
765  while ( pos < triangleIndexes.count() )
766  {
767  if ( trianglesToNativeFaces().at( triangleIndexes.at( pos ) ) !=
768  changes.mNativeFaceIndexesGeometryChanged.at( i ) )
769  triangleIndexes.removeAt( pos );
770  else
771  ++pos;
772  }
773  changes.mTriangleIndexesGeometryChanged.append( triangleIndexes );
774  }
775  }
776 
777  // add vertices
778  for ( const QgsMeshVertex &vertex : std::as_const( changes.mAddedVertices ) )
779  addVertex( vertex );
780 
781  // add faces
782  if ( !changes.mNativeFacesToAdd.isEmpty() )
783  {
784  changes.mTrianglesAddedStartIndex = mTriangularMesh.faceCount();
785  int firstNewNativeFacesIndex = mNativeMeshFaceCentroids.count();
786  for ( int i = 0; i < changes.mNativeFacesToAdd.count(); ++i )
787  {
788  const QgsMeshFace &nativeFace = changes.mNativeFacesToAdd.at( i );
789  triangulate( nativeFace, firstNewNativeFacesIndex + i );
790  mNativeMeshFaceCentroids.append( calculateCentroid( nativeFace ) );
791  }
792 
793  for ( int i = changes.mTrianglesAddedStartIndex; i < mTriangularMesh.faceCount(); ++i )
794  mSpatialFaceIndex.addFace( i, mTriangularMesh );
795  }
796 
797  // remove faces
798  for ( int i = 0; i < changes.mRemovedTriangleIndexes.count(); ++i )
799  {
800  int triangleIndex = changes.mRemovedTriangleIndexes.at( i );
801  mTrianglesToNativeFaces[triangleIndex] = -1;
802  mSpatialFaceIndex.removeFace( triangleIndex, mTriangularMesh );
803  mTriangularMesh.faces[triangleIndex] = QgsMeshFace();
804  }
805 
806  for ( int i = 0; i < changes.mNativeFaceIndexesToRemove.count(); ++i )
807  mNativeMeshFaceCentroids[changes.mNativeFaceIndexesToRemove.at( i )] = QgsMeshVertex();
808 
809  // remove vertices
810  for ( int i = 0; i < changes.mVerticesIndexesToRemove.count(); ++i )
811  mTriangularMesh.vertices[changes.mVerticesIndexesToRemove.at( i )] = QgsMeshVertex();
812 
813  if ( !changes.mVerticesIndexesToRemove.isEmpty() )
814  mIsExtentValid = false;
815 
816  // change Z value
817  for ( int i = 0; i < changes.mNewZValue.count(); ++i )
818  {
819  int vertexIndex = changes.mChangedVerticesCoordinates.at( i );
820  mTriangularMesh.vertices[vertexIndex].setZ( changes.mNewZValue.at( i ) );
821  }
822 
823  //remove outdated spatial index
824  for ( const int triangleIndex : std::as_const( changes.mTriangleIndexesGeometryChanged ) )
825  mSpatialFaceIndex.removeFace( triangleIndex, mTriangularMesh );
826 
827  // change (X,Y) of vertices
828  for ( int i = 0; i < changes.mNewXYValue.count(); ++i )
829  {
830  const QgsPointXY &nativeCoordinates = changes.mNewXYValue.at( i );
831  const QgsMeshVertex nativeVertex( nativeCoordinates.x(),
832  nativeCoordinates.y(),
833  mTriangularMesh.vertices.at( changes.mChangedVerticesCoordinates.at( i ) ).z() );
834 
835  mTriangularMesh.vertices[changes.mChangedVerticesCoordinates.at( i )] = nativeToTriangularCoordinates( nativeVertex );
836  }
837 
838  //restore spatial undex
839  for ( const int triangleIndex : std::as_const( changes.mTriangleIndexesGeometryChanged ) )
840  mSpatialFaceIndex.addFace( triangleIndex, mTriangularMesh );
841 
842  //update native faces
843  for ( int i = 0; i < changes.mNativeFaceIndexesGeometryChanged.count(); ++i )
844  mNativeMeshFaceCentroids[changes.mNativeFaceIndexesGeometryChanged.at( i )] = calculateCentroid( changes.mNativeFacesGeometryChanged.at( i ) );
845 }
846 
848 {
849  //reverse added faces and added vertices
850  if ( !changes.mNativeFacesToAdd.isEmpty() )
851  {
852  for ( int i = changes.mTrianglesAddedStartIndex; i < mTriangularMesh.faceCount(); ++i )
853  mSpatialFaceIndex.removeFace( i, mTriangularMesh );
854 
855  int initialNativeFacesCount = mNativeMeshFaceCentroids.count() - changes.mNativeFacesToAdd.count();
856 
857  mTriangularMesh.faces.resize( changes.mTrianglesAddedStartIndex );
858  mTrianglesToNativeFaces.resize( changes.mTrianglesAddedStartIndex );
859  mNativeMeshFaceCentroids.resize( initialNativeFacesCount );
860  }
861 
862  int initialVerticesCount = mTriangularMesh.vertices.count() - changes.mAddedVertices.count();
863  mTriangularMesh.vertices.resize( initialVerticesCount );
864 
865  if ( !changes.mAddedVertices.isEmpty() )
866  mIsExtentValid = false;
867 
868  // for each vertex to remove we need to update the vertices with the native vertex
869  for ( const int i : std::as_const( changes.mVerticesIndexesToRemove ) )
870  mTriangularMesh.vertices[i] = nativeToTriangularCoordinates( nativeMesh.vertex( i ) );
871 
872  if ( !changes.mVerticesIndexesToRemove.isEmpty() )
873  mIsExtentValid = false;
874 
875  // reverse removed faces
876  QVector<QgsMeshFace> restoredTriangles;
877  QVector<int> restoredTriangularToNative;
878  for ( int i = 0; i < changes.mNativeFacesToRemove.count(); ++i )
879  {
880  const QgsMeshFace &nativeFace = changes.mNativeFacesToRemove.at( i );
881  triangulateFaces( nativeFace,
882  changes.mNativeFaceIndexesToRemove.at( i ),
883  restoredTriangles,
884  restoredTriangularToNative,
885  mTriangularMesh );
886  mNativeMeshFaceCentroids[changes.mNativeFaceIndexesToRemove.at( i )] = calculateCentroid( nativeFace );
887  }
888  for ( int i = 0; i < changes.mRemovedTriangleIndexes.count(); ++i )
889  {
890  int triangleIndex = changes.mRemovedTriangleIndexes.at( i );
891  mTriangularMesh.faces[triangleIndex] = restoredTriangles.at( i );
892  mSpatialFaceIndex.addFace( triangleIndex, mTriangularMesh );
893  mTrianglesToNativeFaces[triangleIndex] = restoredTriangularToNative.at( i );
894  }
895 
896  // reverse Z value
897  for ( int i = 0; i < changes.mOldZValue.count(); ++i )
898  {
899  int vertexIndex = changes.mChangedVerticesCoordinates.at( i );
900  mTriangularMesh.vertices[vertexIndex].setZ( changes.mOldZValue.at( i ) );
901  }
902 
903  //remove outdated spatial index
904  for ( const int triangleIndex : std::as_const( changes.mTriangleIndexesGeometryChanged ) )
905  mSpatialFaceIndex.removeFace( triangleIndex, mTriangularMesh );
906 
907  // reverse (X,Y) of vertices
908  for ( int i = 0; i < changes.mOldXYValue.count(); ++i )
909  {
910  const QgsPointXY &nativeCoordinates = changes.mOldXYValue.at( i );
911  const QgsMeshVertex nativeVertex( nativeCoordinates.x(),
912  nativeCoordinates.y(),
913  mTriangularMesh.vertices.at( changes.mChangedVerticesCoordinates.at( i ) ).z() );
914 
915  mTriangularMesh.vertices[changes.mChangedVerticesCoordinates.at( i )] = nativeToTriangularCoordinates( nativeVertex );
916  }
917 
918  //restore spatial undex
919  for ( const int triangleIndex : std::as_const( changes.mTriangleIndexesGeometryChanged ) )
920  mSpatialFaceIndex.addFace( triangleIndex, mTriangularMesh );
921 
922  //update native faces
923  for ( int i = 0; i < changes.mNativeFaceIndexesGeometryChanged.count(); ++i )
924  mNativeMeshFaceCentroids[changes.mNativeFaceIndexesGeometryChanged.at( i )] = calculateCentroid( changes.mNativeFacesGeometryChanged.at( i ) );
925 }
926 
928  const QgsMesh &nativeMesh )
929 {
930  mAddedVertices = topologicalChanges.addedVertices();
931  mVerticesIndexesToRemove = topologicalChanges.verticesToRemoveIndexes();
932  mNativeFacesToAdd = topologicalChanges.addedFaces();
933  mNativeFacesToRemove = topologicalChanges.removedFaces();
934  mNativeFaceIndexesToRemove = topologicalChanges.removedFaceIndexes();
935  mChangedVerticesCoordinates = topologicalChanges.changedCoordinatesVerticesIndexes();
936  mNewZValue = topologicalChanges.newVerticesZValues();
937  mNewXYValue = topologicalChanges.newVerticesXYValues();
938  mOldXYValue = topologicalChanges.oldVerticesXYValues();
939 
940  mNativeFaceIndexesGeometryChanged = topologicalChanges.nativeFacesIndexesGeometryChanged();
941  mNativeFacesGeometryChanged.resize( mNativeFaceIndexesGeometryChanged.count() );
942  for ( int i = 0; i < mNativeFaceIndexesGeometryChanged.count(); ++i )
943  mNativeFacesGeometryChanged[i] = nativeMesh.face( mNativeFaceIndexesGeometryChanged.at( i ) );
944 }
945 
946 QgsMeshVertex QgsMeshUtils::centroid( const QgsMeshFace &face, const QVector<QgsMeshVertex> &vertices )
947 {
948  QVector<QPointF> points( face.size() );
949  for ( int j = 0; j < face.size(); ++j )
950  {
951  int index = face.at( j );
952  const QgsMeshVertex &vertex = vertices.at( index ); // we need vertices in map coordinate
953  points[j] = vertex.toQPointF();
954  }
955  QPolygonF poly( points );
956  double cx, cy;
957  ENP_centroid( poly, cx, cy );
958  return QgsMeshVertex( cx, cy );
959 }
960 
962 {
963  //To have consistent clock wise orientation of triangles which is necessary for 3D rendering
964  //Check the clock wise, and if it is not counter clock wise, swap indexes to make the oientation counter clock wise
965  double ux = v1.x() - v0.x();
966  double uy = v1.y() - v0.y();
967  double vx = v2.x() - v0.x();
968  double vy = v2.y() - v0.y();
969 
970  double crossProduct = ux * vy - uy * vx;
971  if ( crossProduct < 0 ) //CW -->change the orientation
972  {
973  std::swap( triangle[1], triangle[2] );
974  }
975 }
TransformDirection
Indicates the direction (forward or inverse) of a transform.
Definition: qgis.h:896
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.
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...
QgsPointXY transform(const QgsPointXY &point, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const SIP_THROW(QgsCsException)
Transform the point from the source CRS to the destination CRS.
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.
void addFace(int faceIndex, const QgsMesh &mesh)
Adds a face with faceIndex from the mesh in the spatial index.
void removeFace(int faceIndex, const QgsMesh &mesh)
Removes a face with faceIndex from the mesh in the spatial index.
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
void transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection d=Qgis::TransformDirection::Forward, bool transformZ=false) override SIP_THROW(QgsCsException)
Transforms the geometry using a coordinate transform.
Definition: qgspoint.cpp:382
QPointF toQPointF() const SIP_HOLDGIL
Returns the point as a QPointF.
Definition: qgspoint.h:331
bool isEmpty() const override SIP_HOLDGIL
Returns true if the geometry is empty.
Definition: qgspoint.cpp:767
Q_GADGET double x
Definition: qgspoint.h:52
double z
Definition: qgspoint.h:54
double y
Definition: qgspoint.h:53
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
Class that contains topological differences between two states of a topological mesh,...
QVector< QgsMeshFace > removedFaces() const
Returns the faces that are removed with this changes.
QVector< QgsMeshVertex > addedVertices() const
Returns the added vertices with this changes.
QList< int > changedCoordinatesVerticesIndexes() const
Returns the indexes of vertices that have changed coordinates.
QList< int > removedFaceIndexes() const
Returns the indexes of the faces that are removed with this changes.
QList< double > newVerticesZValues() const
Returns the new Z values of vertices that have changed their coordinates.
QVector< QgsMeshFace > addedFaces() const
Returns the face that are added with this changes.
QList< QgsPointXY > oldVerticesXYValues() const
Returns the old (X,Y) values of vertices that have changed their coordinates.
QList< QgsPointXY > newVerticesXYValues() const
Returns the new (X,Y) values of vertices that have changed their coordinates.
QList< int > nativeFacesIndexesGeometryChanged() const
Returns a list of the native face indexes that have a geometry changed.
QList< int > verticesToRemoveIndexes() const
Returns the indexes of vertices to remove.
The Changes class is used to make changes of the triangular and to keep traces of this changes If a C...
Changes()=default
Default constructor, no changes.
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.
QgsRectangle nativeExtent()
Returns the extent of the mesh in the native mesh coordinates system, returns empty extent if the tra...
QgsPointXY transformFromLayerToTrianglesCoordinates(const QgsPointXY &point) const
Transforms a point from layer coordinates system to triangular Mesh coordinates system.
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.
int nativeFaceIndexForPoint(const QgsPointXY &point) const
Finds index of native face at given point It uses spatial indexing.
double averageTriangleSize() const
Returns the average size of triangles in map unit.
void reverseChanges(const Changes &changes, const QgsMesh &nativeMesh)
Reverses the changes on the triangular mesh (see Changes)
void applyChanges(const Changes &changes)
Applies the changes on the triangular mesh (see Changes)
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 mesh elements of given type.
QgsMeshVertex triangularToNativeCoordinates(const QgsMeshVertex &vertex) const
Transforms the vertex from triangular mesh coordinates system to native coordinates system.
QVector< QVector3D > vertexNormals(float vertScale) const
Calculates and returns normale vector on each vertex that is part of any face.
QgsMeshVertex nativeToTriangularCoordinates(const QgsMeshVertex &vertex) const
Transforms the vertex from native coordinates system to triangular mesh coordinates system.
~QgsTriangularMesh()
Dtor.
const QVector< QgsMeshVertex > & faceCentroids() const
Returns centroids of the native faces in map CRS.
QList< int > nativeFaceIndexForRectangle(const QgsRectangle &rectangle) const
Finds indexes of native faces which bounding boxes intersect given bounding box It uses spatial index...
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.
void setCounterClockwise(QgsMeshFace &triangle, const QgsMeshVertex &v0, const QgsMeshVertex &v1, const QgsMeshVertex &v2)
Checks if the triangle is counter clockwise, if not sets it counter clockwise.
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 QgsMeshVertex centroid(const QgsMeshFace &face, const QVector< QgsMeshVertex > &vertices)
Returns the centroid of the face.
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