QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
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.isShortCircuited() )
291  {
292  try
293  {
294  QgsCoordinateTransform extentTransform = mCoordinateTransform;
295  extentTransform.setBallparkTransformsAreAppropriate( true );
296  nativeExtent = extentTransform.transformBoundingBox( extent(), Qgis::TransformDirection::Reverse );
297  }
298  catch ( QgsCsException &cse )
299  {
300  Q_UNUSED( cse )
301  QgsDebugMsg( QStringLiteral( "Caught CRS exception %1" ).arg( cse.what() ) );
302  }
303  }
304  else
305  nativeExtent = extent();
306 
307  return nativeExtent;
308 }
309 
311 {
312  if ( !mIsExtentValid )
313  {
314  mExtent.setMinimal();
315  for ( int i = 0; i < mTriangularMesh.vertices.size(); ++i )
316  if ( !mTriangularMesh.vertices.at( i ).isEmpty() )
317  mExtent.include( mTriangularMesh.vertices.at( i ) );
318 
319  mIsExtentValid = true;
320  }
321  return mExtent;
322 }
323 
325 {
326  return mLod;
327 }
328 
330 {
331  switch ( type )
332  {
333  case QgsMesh::ElementType::Vertex:
334  return mTriangularMesh.vertexCount() != 0;
335  case QgsMesh::ElementType::Edge:
336  return mTriangularMesh.edgeCount() != 0;
337  case QgsMesh::ElementType::Face:
338  return mTriangularMesh.faceCount() != 0;
339  }
340 
341  return false;
342 }
343 
344 void QgsTriangularMesh::addVertex( const QgsMeshVertex &vertex )
345 {
346  QgsMeshVertex vertexInTriangularCoordinates = nativeToTriangularCoordinates( vertex );
347  mTriangularMesh.vertices.append( vertexInTriangularCoordinates );
348  if ( !vertexInTriangularCoordinates.isEmpty() )
349  mExtent.include( vertexInTriangularCoordinates );
350 }
351 
352 const QVector<QgsMeshVertex> &QgsTriangularMesh::vertices() const
353 {
354  return mTriangularMesh.vertices;
355 }
356 
357 const QVector<QgsMeshFace> &QgsTriangularMesh::triangles() const
358 {
359  return mTriangularMesh.faces;
360 }
361 
362 const QVector<QgsMeshEdge> &QgsTriangularMesh::edges() const
363 {
364  return mTriangularMesh.edges;
365 }
366 
367 const QVector<QgsMeshVertex> &QgsTriangularMesh::centroids() const
368 {
369  return faceCentroids();
370 }
371 
372 const QVector<QgsMeshVertex> &QgsTriangularMesh::faceCentroids() const
373 {
374  return mNativeMeshFaceCentroids;
375 }
376 
377 const QVector<QgsMeshVertex> &QgsTriangularMesh::edgeCentroids() const
378 {
379  return mNativeMeshEdgeCentroids;
380 }
381 
382 const QVector<int> &QgsTriangularMesh::trianglesToNativeFaces() const
383 {
384  return mTrianglesToNativeFaces;
385 }
386 
387 const QVector<int> &QgsTriangularMesh::edgesToNativeEdges() const
388 {
389  return mEdgesToNativeEdges;
390 }
391 
393 {
394  QgsPointXY mapPoint;
395  if ( mCoordinateTransform.isValid() )
396  {
397  try
398  {
399  mapPoint = mCoordinateTransform.transform( point );
400  }
401  catch ( QgsCsException &cse )
402  {
403  Q_UNUSED( cse )
404  QgsDebugMsg( QStringLiteral( "Caught CRS exception %1" ).arg( cse.what() ) );
405  mapPoint = point;
406  }
407  }
408  else
409  mapPoint = point;
410 
411  return point;
412 }
413 
415 {
416  const QList<int> faceIndexes = mSpatialFaceIndex.intersects( QgsRectangle( point, point ) );
417  for ( const int faceIndex : faceIndexes )
418  {
419  const QgsMeshFace &face = mTriangularMesh.faces.at( faceIndex );
420  const QgsGeometry geom = QgsMeshUtils::toGeometry( face, mTriangularMesh.vertices );
421  if ( geom.contains( &point ) )
422  return faceIndex;
423  }
424  return -1;
425 }
426 
428 {
429  int triangleIndex = faceIndexForPoint_v2( point );
430  if ( triangleIndex == -1 )
431  return -1;
432 
433  if ( triangleIndex < mTrianglesToNativeFaces.count() )
434  return mTrianglesToNativeFaces.at( triangleIndex );
435 
436  return -1;
437 }
438 
440 {
441  QSet<int> concernedFaceIndex = QgsMeshUtils::nativeFacesFromTriangles(
442  faceIndexesForRectangle( rectangle ),
444  return concernedFaceIndex.values();
445 }
446 
448 {
449  const QList<int> faceIndexes = mSpatialFaceIndex.intersects( QgsRectangle( point, point ) );
450 
451  for ( const int faceIndex : faceIndexes )
452  {
453  const QgsMeshFace &face = mTriangularMesh.faces.at( faceIndex );
454  if ( QgsMeshUtils::isInTriangleFace( point, face, mTriangularMesh.vertices ) )
455  return faceIndex;
456  }
457  return -1;
458 }
459 
460 QList<int> QgsTriangularMesh::faceIndexesForRectangle( const QgsRectangle &rectangle ) const
461 {
462  return mSpatialFaceIndex.intersects( rectangle );
463 }
464 
465 QList<int> QgsTriangularMesh::edgeIndexesForRectangle( const QgsRectangle &rectangle ) const
466 {
467  return mSpatialEdgeIndex.intersects( rectangle );
468 }
469 
470 QVector<QVector3D> QgsTriangularMesh::vertexNormals( float vertScale ) const
471 {
472  QVector<QVector3D> normales( vertices().count(), QVector3D( 0, 0, 0 ) );
473 
474  for ( const auto &face : triangles() )
475  {
476  if ( face.isEmpty() )
477  continue;
478 
479  for ( int i = 0; i < 3; i++ )
480  {
481  int index1( face.at( i ) );
482  int index2( face.at( ( i + 1 ) % 3 ) );
483  int index3( face.at( ( i + 2 ) % 3 ) );
484 
485  const QgsMeshVertex &vert( vertices().at( index1 ) );
486  const QgsMeshVertex &otherVert1( vertices().at( index2 ) );
487  const QgsMeshVertex &otherVert2( vertices().at( index3 ) );
488 
489  QVector3D v1( float( otherVert1.x() - vert.x() ), float( otherVert1.y() - vert.y() ), vertScale * float( otherVert1.z() - vert.z() ) );
490  QVector3D v2( float( otherVert2.x() - vert.x() ), float( otherVert2.y() - vert.y() ), vertScale * float( otherVert2.z() - vert.z() ) );
491 
492  normales[index1] += QVector3D::crossProduct( v1, v2 );
493  }
494  }
495  return normales;
496 }
497 
498 QVector<QgsTriangularMesh *> QgsTriangularMesh::simplifyMesh( double reductionFactor, int minimumTrianglesCount ) const
499 {
500  QVector<QgsTriangularMesh *> simplifiedMeshes;
501 
502  if ( mTriangularMesh.edgeCount() != 0 )
503  return simplifiedMeshes;
504 
505  if ( !( reductionFactor > 1 ) )
506  return simplifiedMeshes;
507 
508  size_t verticesCount = size_t( mTriangularMesh.vertices.count() );
509 
510  unsigned int baseIndexCount = mTriangularMesh.faceCount() * 3;
511 
512  QVector<unsigned int> indexes( mTriangularMesh.faces.count() * 3 );
513  for ( int i = 0; i < mTriangularMesh.faceCount(); ++i )
514  {
515  const QgsMeshFace &f = mTriangularMesh.face( i );
516  for ( int j = 0; j < 3; ++j )
517  indexes[i * 3 + j] = f.at( j );
518  }
519 
520  QVector<float> vertices( mTriangularMesh.vertices.count() * 3 );
521  for ( int i = 0; i < mTriangularMesh.vertices.count(); ++i )
522  {
523  const QgsMeshVertex &v = mTriangularMesh.vertex( i );
524  vertices[i * 3] = v.x() ;
525  vertices[i * 3 + 1] = v.y() ;
526  vertices[i * 3 + 2] = v.z() ;
527  }
528 
529  int path = 0;
530  while ( true )
531  {
532  QgsTriangularMesh *simplifiedMesh = new QgsTriangularMesh( *this );
533  size_t maxNumberOfIndexes = baseIndexCount / pow( reductionFactor, path + 1 );
534 
535  if ( indexes.size() <= int( maxNumberOfIndexes ) )
536  break;
537 
538  QVector<unsigned int> returnIndexes( indexes.size() );
539  //returned size could be different than goal size but not than the input indexes count
540  size_t size = meshopt_simplifySloppy(
541  returnIndexes.data(),
542  indexes.data(),
543  indexes.size(),
544  vertices.data(),
545  verticesCount,
546  sizeof( float ) * 3,
547  maxNumberOfIndexes );
548 
549 
550  returnIndexes.resize( size );
551 
552  if ( size == 0 || int( size ) >= indexes.size() )
553  {
554  QgsDebugMsg( QStringLiteral( "Mesh simplification failed after %1 path" ).arg( path + 1 ) );
555  break;
556  }
557 
558  QgsMesh newMesh;
559  newMesh.vertices = mTriangularMesh.vertices;
560 
561  newMesh.faces.resize( returnIndexes.size() / 3 );
562  for ( int i = 0; i < newMesh.faces.size(); ++i )
563  {
564  QgsMeshFace f( 3 );
565  for ( size_t j = 0; j < 3 ; ++j )
566  f[j] = returnIndexes.at( i * 3 + j ) ;
567  newMesh.faces[i ] = f;
568  }
569 
570  simplifiedMesh->mTriangularMesh = newMesh;
571  simplifiedMesh->mSpatialFaceIndex = QgsMeshSpatialIndex( simplifiedMesh->mTriangularMesh );
572  simplifiedMesh->finalizeTriangles();
573  simplifiedMeshes.push_back( simplifiedMesh );
574 
575  QgsDebugMsg( QStringLiteral( "Simplified mesh created with %1 triangles" ).arg( newMesh.faceCount() ) );
576 
577  simplifiedMesh->mTrianglesToNativeFaces = QVector<int>( simplifiedMesh->triangles().count(), 0 );
578  for ( int i = 0; i < simplifiedMesh->mTrianglesToNativeFaces.count(); ++i )
579  {
580  QgsMeshFace triangle = simplifiedMesh->triangles().at( i );
581  double x = 0;
582  double y = 0;
583  for ( size_t j = 0; j < 3 ; ++j )
584  {
585  x += mTriangularMesh.vertex( triangle[j] ).x();
586  y += mTriangularMesh.vertex( triangle[j] ).y();
587  }
588  x /= 3;
589  y /= 3;
590  QgsPoint centroid( x, y );
591  int indexInBaseMesh = faceIndexForPoint_v2( centroid );
592 
593  if ( indexInBaseMesh == -1 )
594  {
595  // sometime the centroid of simplified mesh could be outside the base mesh,
596  // so try with vertices of the simplified triangle
597  int j = 0;
598  while ( indexInBaseMesh == -1 && j < 3 )
599  indexInBaseMesh = faceIndexForPoint_v2( mTriangularMesh.vertex( triangle[j++] ) );
600  }
601 
602  if ( indexInBaseMesh > -1 && indexInBaseMesh < mTrianglesToNativeFaces.count() )
603  simplifiedMesh->mTrianglesToNativeFaces[i] = mTrianglesToNativeFaces[indexInBaseMesh];
604  }
605 
606  simplifiedMesh->mLod = path + 1;
607  simplifiedMesh->mBaseTriangularMesh = this;
608 
609  if ( simplifiedMesh->triangles().count() < minimumTrianglesCount )
610  break;
611 
612  indexes = returnIndexes;
613  ++path;
614  }
615 
616  return simplifiedMeshes;
617 }
618 
619 std::unique_ptr< QgsPolygon > QgsMeshUtils::toPolygon( const QgsMeshFace &face, const QVector<QgsMeshVertex> &vertices )
620 {
621  QVector<QgsPoint> ring;
622  for ( int j = 0; j < face.size(); ++j )
623  {
624  int vertexId = face[j];
625  Q_ASSERT( vertexId < vertices.size() );
626  const QgsPoint &vertex = vertices[vertexId];
627  ring.append( vertex );
628  }
629  std::unique_ptr< QgsPolygon > polygon = std::make_unique< QgsPolygon >();
630  polygon->setExteriorRing( new QgsLineString( ring ) );
631  return polygon;
632 }
633 
634 QgsGeometry QgsMeshUtils::toGeometry( const QgsMeshFace &face, const QVector<QgsMeshVertex> &vertices )
635 {
636  return QgsGeometry( QgsMeshUtils::toPolygon( face, vertices ) );
637 }
638 
639 static QSet<int> _nativeElementsFromElements( const QList<int> &indexes, const QVector<int> &elementToNativeElements )
640 {
641  QSet<int> nativeElements;
642  for ( const int index : indexes )
643  {
644  if ( index < elementToNativeElements.count() )
645  {
646  const int nativeIndex = elementToNativeElements[index];
647  nativeElements.insert( nativeIndex );
648  }
649  }
650  return nativeElements;
651 }
652 
653 QSet<int> QgsMeshUtils::nativeFacesFromTriangles( const QList<int> &triangleIndexes, const QVector<int> &trianglesToNativeFaces )
654 {
655  return _nativeElementsFromElements( triangleIndexes, trianglesToNativeFaces );
656 }
657 
658 QSet<int> QgsMeshUtils::nativeEdgesFromEdges( const QList<int> &edgesIndexes, const QVector<int> &edgesToNativeEdges )
659 {
660  return _nativeElementsFromElements( edgesIndexes, edgesToNativeEdges );
661 }
662 
663 
664 static double _isLeft2D( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p )
665 {
666  return ( p2.x() - p1.x() ) * ( p.y() - p1.y() ) - ( p.x() - p1.x() ) * ( p2.y() - p1.y() );
667 }
668 
669 static bool _isInTriangle2D( const QgsPoint &p, const QVector<QgsMeshVertex> &triangle )
670 {
671  return ( ( _isLeft2D( triangle[2], triangle[0], p ) * _isLeft2D( triangle[2], triangle[0], triangle[1] ) >= 0 )
672  && ( _isLeft2D( triangle[0], triangle[1], p ) * _isLeft2D( triangle[0], triangle[1], triangle[2] ) >= 0 )
673  && ( _isLeft2D( triangle[2], triangle[1], p ) * _isLeft2D( triangle[2], triangle[1], triangle[0] ) >= 0 ) );
674 }
675 
676 bool QgsMeshUtils::isInTriangleFace( const QgsPointXY point, const QgsMeshFace &face, const QVector<QgsMeshVertex> &vertices )
677 {
678  if ( face.count() != 3 )
679  return false;
680 
681  QVector<QgsMeshVertex> triangle( 3 );
682  for ( int i = 0; i < 3; ++i )
683  {
684  if ( face[i] > vertices.count() )
685  return false;
686  triangle[i] = vertices[face[i]];
687  }
688 
689  const QgsPoint p( point.x(), point.y() );
690 
691  return _isInTriangle2D( p, triangle );
692 }
693 
694 QSet<int> QgsMeshUtils::nativeVerticesFromTriangles( const QList<int> &triangleIndexes, const QVector<QgsMeshFace> &triangles )
695 {
696  QSet<int> uniqueVertices;
697  for ( int triangleIndex : triangleIndexes )
698  {
699  const QgsMeshFace triangle = triangles[triangleIndex];
700  for ( int i : triangle )
701  {
702  uniqueVertices.insert( i );
703  }
704  }
705  return uniqueVertices;
706 }
707 
708 QSet<int> QgsMeshUtils::nativeVerticesFromEdges( const QList<int> &edgesIndexes, const QVector<QgsMeshEdge> &edges )
709 {
710  QSet<int> uniqueVertices;
711  for ( int edgeIndex : edgesIndexes )
712  {
713  const QgsMeshEdge edge = edges[edgeIndex];
714  uniqueVertices.insert( edge.first );
715  uniqueVertices.insert( edge.second );
716  }
717  return uniqueVertices;
718 }
719 
721 {
722  //if necessary defined removes triangles index
723  if ( changes.mRemovedTriangleIndexes.isEmpty() && !changes.mNativeFaceIndexesToRemove.isEmpty() )
724  {
725  for ( int nf = 0; nf < changes.mNativeFaceIndexesToRemove.count(); ++nf )
726  {
727  int nativeIndex = changes.mNativeFaceIndexesToRemove.at( nf );
728  const QgsMeshFace &nativeFace = changes.mNativeFacesToRemove.at( nf );
729  Q_ASSERT( !nativeFace.isEmpty() );
730 
731  QgsRectangle nativeFaceExtent( mTriangularMesh.vertex( nativeFace.at( 0 ) ), mTriangularMesh.vertex( nativeFace.at( 0 ) ) );
732  for ( int i = 1; i < nativeFace.count(); ++i )
733  {
734  const QgsMeshVertex &triangularVertex = mTriangularMesh.vertex( nativeFace.at( i ) );
735  nativeFaceExtent.include( triangularVertex );
736  }
737 
738  QList<int> concernedTriangle = faceIndexesForRectangle( nativeFaceExtent );
739  //Remove only those corresponding to the native face
740  for ( int i = 0; i < concernedTriangle.count(); ++i )
741  {
742  int triangleIndex = concernedTriangle.at( i );
743  if ( mTrianglesToNativeFaces.at( triangleIndex ) == nativeIndex )
744  changes.mRemovedTriangleIndexes.append( triangleIndex );
745  }
746  }
747  }
748 
749  if ( changes.mOldZValue.isEmpty() && !changes.mNewZValue.isEmpty() )
750  {
751  changes.mOldZValue.reserve( changes.mNewZValue.count() );
752  for ( int i = 0; i < changes.mNewZValue.count(); ++i )
753  changes.mOldZValue.append( mTriangularMesh.vertices.at( changes.mChangedVerticesCoordinates.at( i ) ).z() );
754  }
755 
756  if ( changes.mTriangleIndexesGeometryChanged.isEmpty() && !changes.mNativeFaceIndexesGeometryChanged.isEmpty() )
757  {
758  for ( int i = 0; i < changes.mNativeFaceIndexesGeometryChanged.count(); ++i )
759  {
760  const QgsMeshFace &nativeFace = changes.mNativeFacesGeometryChanged.at( i );
761  if ( nativeFace.count() < 2 )
762  continue;
763  QgsRectangle bbox( mTriangularMesh.vertices.at( nativeFace.at( 0 ) ), mTriangularMesh.vertices.at( nativeFace.at( 1 ) ) );
764 
765  for ( int i = 2; i < nativeFace.count(); ++i )
766  bbox.include( mTriangularMesh.vertices.at( nativeFace.at( i ) ) );
767 
768  QList<int> triangleIndexes = faceIndexesForRectangle( bbox );
769  int pos = 0;
770  while ( pos < triangleIndexes.count() )
771  {
772  if ( trianglesToNativeFaces().at( triangleIndexes.at( pos ) ) !=
773  changes.mNativeFaceIndexesGeometryChanged.at( i ) )
774  triangleIndexes.removeAt( pos );
775  else
776  ++pos;
777  }
778  changes.mTriangleIndexesGeometryChanged.append( triangleIndexes );
779  }
780  }
781 
782  // add vertices
783  for ( const QgsMeshVertex &vertex : std::as_const( changes.mAddedVertices ) )
784  addVertex( vertex );
785 
786  // add faces
787  if ( !changes.mNativeFacesToAdd.isEmpty() )
788  {
789  changes.mTrianglesAddedStartIndex = mTriangularMesh.faceCount();
790  int firstNewNativeFacesIndex = mNativeMeshFaceCentroids.count();
791  for ( int i = 0; i < changes.mNativeFacesToAdd.count(); ++i )
792  {
793  const QgsMeshFace &nativeFace = changes.mNativeFacesToAdd.at( i );
794  triangulate( nativeFace, firstNewNativeFacesIndex + i );
795  mNativeMeshFaceCentroids.append( calculateCentroid( nativeFace ) );
796  }
797 
798  for ( int i = changes.mTrianglesAddedStartIndex; i < mTriangularMesh.faceCount(); ++i )
799  mSpatialFaceIndex.addFace( i, mTriangularMesh );
800  }
801 
802  // remove faces
803  for ( int i = 0; i < changes.mRemovedTriangleIndexes.count(); ++i )
804  {
805  int triangleIndex = changes.mRemovedTriangleIndexes.at( i );
806  mTrianglesToNativeFaces[triangleIndex] = -1;
807  mSpatialFaceIndex.removeFace( triangleIndex, mTriangularMesh );
808  mTriangularMesh.faces[triangleIndex] = QgsMeshFace();
809  }
810 
811  for ( int i = 0; i < changes.mNativeFaceIndexesToRemove.count(); ++i )
812  mNativeMeshFaceCentroids[changes.mNativeFaceIndexesToRemove.at( i )] = QgsMeshVertex();
813 
814  // remove vertices
815  for ( int i = 0; i < changes.mVerticesIndexesToRemove.count(); ++i )
816  mTriangularMesh.vertices[changes.mVerticesIndexesToRemove.at( i )] = QgsMeshVertex();
817 
818  if ( !changes.mVerticesIndexesToRemove.isEmpty() )
819  mIsExtentValid = false;
820 
821  // change Z value
822  for ( int i = 0; i < changes.mNewZValue.count(); ++i )
823  {
824  int vertexIndex = changes.mChangedVerticesCoordinates.at( i );
825  mTriangularMesh.vertices[vertexIndex].setZ( changes.mNewZValue.at( i ) );
826  }
827 
828  //remove outdated spatial index
829  for ( const int triangleIndex : std::as_const( changes.mTriangleIndexesGeometryChanged ) )
830  mSpatialFaceIndex.removeFace( triangleIndex, mTriangularMesh );
831 
832  // change (X,Y) of vertices
833  for ( int i = 0; i < changes.mNewXYValue.count(); ++i )
834  {
835  const QgsPointXY &nativeCoordinates = changes.mNewXYValue.at( i );
836  const QgsMeshVertex nativeVertex( nativeCoordinates.x(),
837  nativeCoordinates.y(),
838  mTriangularMesh.vertices.at( changes.mChangedVerticesCoordinates.at( i ) ).z() );
839 
840  mTriangularMesh.vertices[changes.mChangedVerticesCoordinates.at( i )] = nativeToTriangularCoordinates( nativeVertex );
841  }
842 
843  //restore spatial undex
844  for ( const int triangleIndex : std::as_const( changes.mTriangleIndexesGeometryChanged ) )
845  mSpatialFaceIndex.addFace( triangleIndex, mTriangularMesh );
846 
847  //update native faces
848  for ( int i = 0; i < changes.mNativeFaceIndexesGeometryChanged.count(); ++i )
849  mNativeMeshFaceCentroids[changes.mNativeFaceIndexesGeometryChanged.at( i )] = calculateCentroid( changes.mNativeFacesGeometryChanged.at( i ) );
850 }
851 
853 {
854  //reverse added faces and added vertices
855  if ( !changes.mNativeFacesToAdd.isEmpty() )
856  {
857  for ( int i = changes.mTrianglesAddedStartIndex; i < mTriangularMesh.faceCount(); ++i )
858  mSpatialFaceIndex.removeFace( i, mTriangularMesh );
859 
860  int initialNativeFacesCount = mNativeMeshFaceCentroids.count() - changes.mNativeFacesToAdd.count();
861 
862  mTriangularMesh.faces.resize( changes.mTrianglesAddedStartIndex );
863  mTrianglesToNativeFaces.resize( changes.mTrianglesAddedStartIndex );
864  mNativeMeshFaceCentroids.resize( initialNativeFacesCount );
865  }
866 
867  int initialVerticesCount = mTriangularMesh.vertices.count() - changes.mAddedVertices.count();
868  mTriangularMesh.vertices.resize( initialVerticesCount );
869 
870  if ( !changes.mAddedVertices.isEmpty() )
871  mIsExtentValid = false;
872 
873  // for each vertex to remove we need to update the vertices with the native vertex
874  for ( const int i : std::as_const( changes.mVerticesIndexesToRemove ) )
875  mTriangularMesh.vertices[i] = nativeToTriangularCoordinates( nativeMesh.vertex( i ) );
876 
877  if ( !changes.mVerticesIndexesToRemove.isEmpty() )
878  mIsExtentValid = false;
879 
880  // reverse removed faces
881  QVector<QgsMeshFace> restoredTriangles;
882  QVector<int> restoredTriangularToNative;
883  for ( int i = 0; i < changes.mNativeFacesToRemove.count(); ++i )
884  {
885  const QgsMeshFace &nativeFace = changes.mNativeFacesToRemove.at( i );
886  triangulateFaces( nativeFace,
887  changes.mNativeFaceIndexesToRemove.at( i ),
888  restoredTriangles,
889  restoredTriangularToNative,
890  mTriangularMesh );
891  mNativeMeshFaceCentroids[changes.mNativeFaceIndexesToRemove.at( i )] = calculateCentroid( nativeFace );
892  }
893  for ( int i = 0; i < changes.mRemovedTriangleIndexes.count(); ++i )
894  {
895  int triangleIndex = changes.mRemovedTriangleIndexes.at( i );
896  mTriangularMesh.faces[triangleIndex] = restoredTriangles.at( i );
897  mSpatialFaceIndex.addFace( triangleIndex, mTriangularMesh );
898  mTrianglesToNativeFaces[triangleIndex] = restoredTriangularToNative.at( i );
899  }
900 
901  // reverse Z value
902  for ( int i = 0; i < changes.mOldZValue.count(); ++i )
903  {
904  int vertexIndex = changes.mChangedVerticesCoordinates.at( i );
905  mTriangularMesh.vertices[vertexIndex].setZ( changes.mOldZValue.at( i ) );
906  }
907 
908  //remove outdated spatial index
909  for ( const int triangleIndex : std::as_const( changes.mTriangleIndexesGeometryChanged ) )
910  mSpatialFaceIndex.removeFace( triangleIndex, mTriangularMesh );
911 
912  // reverse (X,Y) of vertices
913  for ( int i = 0; i < changes.mOldXYValue.count(); ++i )
914  {
915  const QgsPointXY &nativeCoordinates = changes.mOldXYValue.at( i );
916  const QgsMeshVertex nativeVertex( nativeCoordinates.x(),
917  nativeCoordinates.y(),
918  mTriangularMesh.vertices.at( changes.mChangedVerticesCoordinates.at( i ) ).z() );
919 
920  mTriangularMesh.vertices[changes.mChangedVerticesCoordinates.at( i )] = nativeToTriangularCoordinates( nativeVertex );
921  }
922 
923  //restore spatial undex
924  for ( const int triangleIndex : std::as_const( changes.mTriangleIndexesGeometryChanged ) )
925  mSpatialFaceIndex.addFace( triangleIndex, mTriangularMesh );
926 
927  //update native faces
928  for ( int i = 0; i < changes.mNativeFaceIndexesGeometryChanged.count(); ++i )
929  mNativeMeshFaceCentroids[changes.mNativeFaceIndexesGeometryChanged.at( i )] = calculateCentroid( changes.mNativeFacesGeometryChanged.at( i ) );
930 }
931 
933  const QgsMesh &nativeMesh )
934 {
935  mAddedVertices = topologicalChanges.addedVertices();
936  mVerticesIndexesToRemove = topologicalChanges.verticesToRemoveIndexes();
937  mNativeFacesToAdd = topologicalChanges.addedFaces();
938  mNativeFacesToRemove = topologicalChanges.removedFaces();
939  mNativeFaceIndexesToRemove = topologicalChanges.removedFaceIndexes();
940  mChangedVerticesCoordinates = topologicalChanges.changedCoordinatesVerticesIndexes();
941  mNewZValue = topologicalChanges.newVerticesZValues();
942  mNewXYValue = topologicalChanges.newVerticesXYValues();
943  mOldXYValue = topologicalChanges.oldVerticesXYValues();
944 
945  mNativeFaceIndexesGeometryChanged = topologicalChanges.nativeFacesIndexesGeometryChanged();
946  mNativeFacesGeometryChanged.resize( mNativeFaceIndexesGeometryChanged.count() );
947  for ( int i = 0; i < mNativeFaceIndexesGeometryChanged.count(); ++i )
948  mNativeFacesGeometryChanged[i] = nativeMesh.face( mNativeFaceIndexesGeometryChanged.at( i ) );
949 }
950 
951 QgsMeshVertex QgsMeshUtils::centroid( const QgsMeshFace &face, const QVector<QgsMeshVertex> &vertices )
952 {
953  QVector<QPointF> points( face.size() );
954  for ( int j = 0; j < face.size(); ++j )
955  {
956  int index = face.at( j );
957  const QgsMeshVertex &vertex = vertices.at( index ); // we need vertices in map coordinate
958  points[j] = vertex.toQPointF();
959  }
960  QPolygonF poly( points );
961  double cx, cy;
962  ENP_centroid( poly, cx, cy );
963  return QgsMeshVertex( cx, cy );
964 }
965 
967 {
968  //To have consistent clock wise orientation of triangles which is necessary for 3D rendering
969  //Check the clock wise, and if it is not counter clock wise, swap indexes to make the oientation counter clock wise
970  double ux = v1.x() - v0.x();
971  double uy = v1.y() - v0.y();
972  double vx = v2.x() - v0.x();
973  double vy = v2.y() - v0.y();
974 
975  double crossProduct = ux * vy - uy * vx;
976  if ( crossProduct < 0 ) //CW -->change the orientation
977  {
978  std::swap( triangle[1], triangle[2] );
979  }
980 }
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.
void setBallparkTransformsAreAppropriate(bool appropriate)
Sets whether approximate "ballpark" results are appropriate for this coordinate transform.
bool isShortCircuited() const
Returns true if the transform short circuits because the source and destination are equivalent.
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...
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool handle180Crossover=false) const SIP_THROW(QgsCsException)
Transforms a rectangle from the source CRS to the destination CRS.
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:125
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