QGIS API Documentation 3.27.0-Master (9c08adf5ef)
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"
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
32static 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
50static 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
84static 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
118void QgsTriangularMesh::triangulate( const QgsMeshFace &face, int nativeIndex )
119{
120 triangulateFaces( face, nativeIndex, mTriangularMesh.faces, mTrianglesToNativeFaces, mTriangularMesh );
121}
122
123QgsMeshVertex 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
144QgsMeshVertex QgsTriangularMesh::calculateCentroid( const QgsMeshFace &nativeFace )
145{
146 return QgsMeshUtils::centroid( nativeFace, mTriangularMesh.vertices );
147}
148
150{
151 return mAverageTriangleSize;
152}
153
156
157bool 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
257void 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
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
344void 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
352const QVector<QgsMeshVertex> &QgsTriangularMesh::vertices() const
353{
354 return mTriangularMesh.vertices;
355}
356
357const QVector<QgsMeshFace> &QgsTriangularMesh::triangles() const
358{
359 return mTriangularMesh.faces;
360}
361
362const QVector<QgsMeshEdge> &QgsTriangularMesh::edges() const
363{
364 return mTriangularMesh.edges;
365}
366
367const QVector<QgsMeshVertex> &QgsTriangularMesh::centroids() const
368{
369 return faceCentroids();
370}
371
372const QVector<QgsMeshVertex> &QgsTriangularMesh::faceCentroids() const
373{
374 return mNativeMeshFaceCentroids;
375}
376
377const QVector<QgsMeshVertex> &QgsTriangularMesh::edgeCentroids() const
378{
379 return mNativeMeshEdgeCentroids;
380}
381
383{
384 return mTrianglesToNativeFaces;
385}
386
387const 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
461{
462 return mSpatialFaceIndex.intersects( rectangle );
463}
464
466{
467 return mSpatialEdgeIndex.intersects( rectangle );
468}
469
470QVector<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
498QVector<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 {
537 delete simplifiedMesh;
538 break;
539 }
540
541 QVector<unsigned int> returnIndexes( indexes.size() );
542 //returned size could be different than goal size but not than the input indexes count
543 size_t size = meshopt_simplifySloppy(
544 returnIndexes.data(),
545 indexes.data(),
546 indexes.size(),
547 vertices.data(),
548 verticesCount,
549 sizeof( float ) * 3,
550 maxNumberOfIndexes );
551
552
553 returnIndexes.resize( size );
554
555 if ( size == 0 || int( size ) >= indexes.size() )
556 {
557 QgsDebugMsg( QStringLiteral( "Mesh simplification failed after %1 path" ).arg( path + 1 ) );
558 delete simplifiedMesh;
559 break;
560 }
561
562 QgsMesh newMesh;
563 newMesh.vertices = mTriangularMesh.vertices;
564
565 newMesh.faces.resize( returnIndexes.size() / 3 );
566 for ( int i = 0; i < newMesh.faces.size(); ++i )
567 {
568 QgsMeshFace f( 3 );
569 for ( size_t j = 0; j < 3 ; ++j )
570 f[j] = returnIndexes.at( i * 3 + j ) ;
571 newMesh.faces[i ] = f;
572 }
573
574 simplifiedMesh->mTriangularMesh = newMesh;
575 simplifiedMesh->mSpatialFaceIndex = QgsMeshSpatialIndex( simplifiedMesh->mTriangularMesh );
576 simplifiedMesh->finalizeTriangles();
577 simplifiedMeshes.push_back( simplifiedMesh );
578
579 QgsDebugMsgLevel( QStringLiteral( "Simplified mesh created with %1 triangles" ).arg( newMesh.faceCount() ), 2 );
580
581 simplifiedMesh->mTrianglesToNativeFaces = QVector<int>( simplifiedMesh->triangles().count(), 0 );
582 for ( int i = 0; i < simplifiedMesh->mTrianglesToNativeFaces.count(); ++i )
583 {
584 QgsMeshFace triangle = simplifiedMesh->triangles().at( i );
585 double x = 0;
586 double y = 0;
587 for ( size_t j = 0; j < 3 ; ++j )
588 {
589 x += mTriangularMesh.vertex( triangle[j] ).x();
590 y += mTriangularMesh.vertex( triangle[j] ).y();
591 }
592 x /= 3;
593 y /= 3;
594 QgsPoint centroid( x, y );
595 int indexInBaseMesh = faceIndexForPoint_v2( centroid );
596
597 if ( indexInBaseMesh == -1 )
598 {
599 // sometime the centroid of simplified mesh could be outside the base mesh,
600 // so try with vertices of the simplified triangle
601 int j = 0;
602 while ( indexInBaseMesh == -1 && j < 3 )
603 indexInBaseMesh = faceIndexForPoint_v2( mTriangularMesh.vertex( triangle[j++] ) );
604 }
605
606 if ( indexInBaseMesh > -1 && indexInBaseMesh < mTrianglesToNativeFaces.count() )
607 simplifiedMesh->mTrianglesToNativeFaces[i] = mTrianglesToNativeFaces[indexInBaseMesh];
608 }
609
610 simplifiedMesh->mLod = path + 1;
611 simplifiedMesh->mBaseTriangularMesh = this;
612
613 if ( simplifiedMesh->triangles().count() < minimumTrianglesCount )
614 break;
615
616 indexes = returnIndexes;
617 ++path;
618 }
619
620 return simplifiedMeshes;
621}
622
623std::unique_ptr< QgsPolygon > QgsMeshUtils::toPolygon( const QgsMeshFace &face, const QVector<QgsMeshVertex> &vertices )
624{
625 QVector<QgsPoint> ring;
626 for ( int j = 0; j < face.size(); ++j )
627 {
628 int vertexId = face[j];
629 Q_ASSERT( vertexId < vertices.size() );
630 const QgsPoint &vertex = vertices[vertexId];
631 ring.append( vertex );
632 }
633 std::unique_ptr< QgsPolygon > polygon = std::make_unique< QgsPolygon >();
634 polygon->setExteriorRing( new QgsLineString( ring ) );
635 return polygon;
636}
637
638QgsGeometry QgsMeshUtils::toGeometry( const QgsMeshFace &face, const QVector<QgsMeshVertex> &vertices )
639{
640 return QgsGeometry( QgsMeshUtils::toPolygon( face, vertices ) );
641}
642
643static QSet<int> _nativeElementsFromElements( const QList<int> &indexes, const QVector<int> &elementToNativeElements )
644{
645 QSet<int> nativeElements;
646 for ( const int index : indexes )
647 {
648 if ( index < elementToNativeElements.count() )
649 {
650 const int nativeIndex = elementToNativeElements[index];
651 nativeElements.insert( nativeIndex );
652 }
653 }
654 return nativeElements;
655}
656
657QSet<int> QgsMeshUtils::nativeFacesFromTriangles( const QList<int> &triangleIndexes, const QVector<int> &trianglesToNativeFaces )
658{
659 return _nativeElementsFromElements( triangleIndexes, trianglesToNativeFaces );
660}
661
662QSet<int> QgsMeshUtils::nativeEdgesFromEdges( const QList<int> &edgesIndexes, const QVector<int> &edgesToNativeEdges )
663{
664 return _nativeElementsFromElements( edgesIndexes, edgesToNativeEdges );
665}
666
667
668static double _isLeft2D( const QgsPoint &p1, const QgsPoint &p2, const QgsPoint &p )
669{
670 return ( p2.x() - p1.x() ) * ( p.y() - p1.y() ) - ( p.x() - p1.x() ) * ( p2.y() - p1.y() );
671}
672
673static bool _isInTriangle2D( const QgsPoint &p, const QVector<QgsMeshVertex> &triangle )
674{
675 return ( ( _isLeft2D( triangle[2], triangle[0], p ) * _isLeft2D( triangle[2], triangle[0], triangle[1] ) >= 0 )
676 && ( _isLeft2D( triangle[0], triangle[1], p ) * _isLeft2D( triangle[0], triangle[1], triangle[2] ) >= 0 )
677 && ( _isLeft2D( triangle[2], triangle[1], p ) * _isLeft2D( triangle[2], triangle[1], triangle[0] ) >= 0 ) );
678}
679
680bool QgsMeshUtils::isInTriangleFace( const QgsPointXY point, const QgsMeshFace &face, const QVector<QgsMeshVertex> &vertices )
681{
682 if ( face.count() != 3 )
683 return false;
684
685 QVector<QgsMeshVertex> triangle( 3 );
686 for ( int i = 0; i < 3; ++i )
687 {
688 if ( face[i] > vertices.count() )
689 return false;
690 triangle[i] = vertices[face[i]];
691 }
692
693 const QgsPoint p( point.x(), point.y() );
694
695 return _isInTriangle2D( p, triangle );
696}
697
698QSet<int> QgsMeshUtils::nativeVerticesFromTriangles( const QList<int> &triangleIndexes, const QVector<QgsMeshFace> &triangles )
699{
700 QSet<int> uniqueVertices;
701 for ( int triangleIndex : triangleIndexes )
702 {
703 const QgsMeshFace triangle = triangles[triangleIndex];
704 for ( int i : triangle )
705 {
706 uniqueVertices.insert( i );
707 }
708 }
709 return uniqueVertices;
710}
711
712QSet<int> QgsMeshUtils::nativeVerticesFromEdges( const QList<int> &edgesIndexes, const QVector<QgsMeshEdge> &edges )
713{
714 QSet<int> uniqueVertices;
715 for ( int edgeIndex : edgesIndexes )
716 {
717 const QgsMeshEdge edge = edges[edgeIndex];
718 uniqueVertices.insert( edge.first );
719 uniqueVertices.insert( edge.second );
720 }
721 return uniqueVertices;
722}
723
725{
726 //if necessary defined removes triangles index
727 if ( changes.mRemovedTriangleIndexes.isEmpty() && !changes.mNativeFaceIndexesToRemove.isEmpty() )
728 {
729 for ( int nf = 0; nf < changes.mNativeFaceIndexesToRemove.count(); ++nf )
730 {
731 int nativeIndex = changes.mNativeFaceIndexesToRemove.at( nf );
732 const QgsMeshFace &nativeFace = changes.mNativeFacesToRemove.at( nf );
733 Q_ASSERT( !nativeFace.isEmpty() );
734
735 QgsRectangle nativeFaceExtent( mTriangularMesh.vertex( nativeFace.at( 0 ) ), mTriangularMesh.vertex( nativeFace.at( 0 ) ) );
736 for ( int i = 1; i < nativeFace.count(); ++i )
737 {
738 const QgsMeshVertex &triangularVertex = mTriangularMesh.vertex( nativeFace.at( i ) );
739 nativeFaceExtent.include( triangularVertex );
740 }
741
742 QList<int> concernedTriangle = faceIndexesForRectangle( nativeFaceExtent );
743 //Remove only those corresponding to the native face
744 for ( int i = 0; i < concernedTriangle.count(); ++i )
745 {
746 int triangleIndex = concernedTriangle.at( i );
747 if ( mTrianglesToNativeFaces.at( triangleIndex ) == nativeIndex )
748 changes.mRemovedTriangleIndexes.append( triangleIndex );
749 }
750 }
751 }
752
753 if ( changes.mOldZValue.isEmpty() && !changes.mNewZValue.isEmpty() )
754 {
755 changes.mOldZValue.reserve( changes.mNewZValue.count() );
756 for ( int i = 0; i < changes.mNewZValue.count(); ++i )
757 changes.mOldZValue.append( mTriangularMesh.vertices.at( changes.mChangedVerticesCoordinates.at( i ) ).z() );
758 }
759
760 if ( changes.mTriangleIndexesGeometryChanged.isEmpty() && !changes.mNativeFaceIndexesGeometryChanged.isEmpty() )
761 {
762 for ( int i = 0; i < changes.mNativeFaceIndexesGeometryChanged.count(); ++i )
763 {
764 const QgsMeshFace &nativeFace = changes.mNativeFacesGeometryChanged.at( i );
765 if ( nativeFace.count() < 2 )
766 continue;
767 QgsRectangle bbox( mTriangularMesh.vertices.at( nativeFace.at( 0 ) ), mTriangularMesh.vertices.at( nativeFace.at( 1 ) ) );
768
769 for ( int i = 2; i < nativeFace.count(); ++i )
770 bbox.include( mTriangularMesh.vertices.at( nativeFace.at( i ) ) );
771
772 QList<int> triangleIndexes = faceIndexesForRectangle( bbox );
773 int pos = 0;
774 while ( pos < triangleIndexes.count() )
775 {
776 if ( trianglesToNativeFaces().at( triangleIndexes.at( pos ) ) !=
777 changes.mNativeFaceIndexesGeometryChanged.at( i ) )
778 triangleIndexes.removeAt( pos );
779 else
780 ++pos;
781 }
782 changes.mTriangleIndexesGeometryChanged.append( triangleIndexes );
783 }
784 }
785
786 // add vertices
787 for ( const QgsMeshVertex &vertex : std::as_const( changes.mAddedVertices ) )
788 addVertex( vertex );
789
790 // add faces
791 if ( !changes.mNativeFacesToAdd.isEmpty() )
792 {
793 changes.mTrianglesAddedStartIndex = mTriangularMesh.faceCount();
794 int firstNewNativeFacesIndex = mNativeMeshFaceCentroids.count();
795 for ( int i = 0; i < changes.mNativeFacesToAdd.count(); ++i )
796 {
797 const QgsMeshFace &nativeFace = changes.mNativeFacesToAdd.at( i );
798 triangulate( nativeFace, firstNewNativeFacesIndex + i );
799 mNativeMeshFaceCentroids.append( calculateCentroid( nativeFace ) );
800 }
801
802 for ( int i = changes.mTrianglesAddedStartIndex; i < mTriangularMesh.faceCount(); ++i )
803 mSpatialFaceIndex.addFace( i, mTriangularMesh );
804 }
805
806 // remove faces
807 for ( int i = 0; i < changes.mRemovedTriangleIndexes.count(); ++i )
808 {
809 int triangleIndex = changes.mRemovedTriangleIndexes.at( i );
810 mTrianglesToNativeFaces[triangleIndex] = -1;
811 mSpatialFaceIndex.removeFace( triangleIndex, mTriangularMesh );
812 mTriangularMesh.faces[triangleIndex] = QgsMeshFace();
813 }
814
815 for ( int i = 0; i < changes.mNativeFaceIndexesToRemove.count(); ++i )
816 mNativeMeshFaceCentroids[changes.mNativeFaceIndexesToRemove.at( i )] = QgsMeshVertex();
817
818 // remove vertices
819 for ( int i = 0; i < changes.mVerticesIndexesToRemove.count(); ++i )
820 mTriangularMesh.vertices[changes.mVerticesIndexesToRemove.at( i )] = QgsMeshVertex();
821
822 if ( !changes.mVerticesIndexesToRemove.isEmpty() )
823 mIsExtentValid = false;
824
825 // change Z value
826 for ( int i = 0; i < changes.mNewZValue.count(); ++i )
827 {
828 int vertexIndex = changes.mChangedVerticesCoordinates.at( i );
829 mTriangularMesh.vertices[vertexIndex].setZ( changes.mNewZValue.at( i ) );
830 }
831
832 //remove outdated spatial index
833 for ( const int triangleIndex : std::as_const( changes.mTriangleIndexesGeometryChanged ) )
834 mSpatialFaceIndex.removeFace( triangleIndex, mTriangularMesh );
835
836 // change (X,Y) of vertices
837 for ( int i = 0; i < changes.mNewXYValue.count(); ++i )
838 {
839 const QgsPointXY &nativeCoordinates = changes.mNewXYValue.at( i );
840 const QgsMeshVertex nativeVertex( nativeCoordinates.x(),
841 nativeCoordinates.y(),
842 mTriangularMesh.vertices.at( changes.mChangedVerticesCoordinates.at( i ) ).z() );
843
844 mTriangularMesh.vertices[changes.mChangedVerticesCoordinates.at( i )] = nativeToTriangularCoordinates( nativeVertex );
845 }
846
847 //restore spatial undex
848 for ( const int triangleIndex : std::as_const( changes.mTriangleIndexesGeometryChanged ) )
849 mSpatialFaceIndex.addFace( triangleIndex, mTriangularMesh );
850
851 //update native faces
852 for ( int i = 0; i < changes.mNativeFaceIndexesGeometryChanged.count(); ++i )
853 mNativeMeshFaceCentroids[changes.mNativeFaceIndexesGeometryChanged.at( i )] = calculateCentroid( changes.mNativeFacesGeometryChanged.at( i ) );
854}
855
857{
858 //reverse added faces and added vertices
859 if ( !changes.mNativeFacesToAdd.isEmpty() )
860 {
861 for ( int i = changes.mTrianglesAddedStartIndex; i < mTriangularMesh.faceCount(); ++i )
862 mSpatialFaceIndex.removeFace( i, mTriangularMesh );
863
864 int initialNativeFacesCount = mNativeMeshFaceCentroids.count() - changes.mNativeFacesToAdd.count();
865
866 mTriangularMesh.faces.resize( changes.mTrianglesAddedStartIndex );
867 mTrianglesToNativeFaces.resize( changes.mTrianglesAddedStartIndex );
868 mNativeMeshFaceCentroids.resize( initialNativeFacesCount );
869 }
870
871 int initialVerticesCount = mTriangularMesh.vertices.count() - changes.mAddedVertices.count();
872 mTriangularMesh.vertices.resize( initialVerticesCount );
873
874 if ( !changes.mAddedVertices.isEmpty() )
875 mIsExtentValid = false;
876
877 // for each vertex to remove we need to update the vertices with the native vertex
878 for ( const int i : std::as_const( changes.mVerticesIndexesToRemove ) )
879 mTriangularMesh.vertices[i] = nativeToTriangularCoordinates( nativeMesh.vertex( i ) );
880
881 if ( !changes.mVerticesIndexesToRemove.isEmpty() )
882 mIsExtentValid = false;
883
884 // reverse removed faces
885 QVector<QgsMeshFace> restoredTriangles;
886 QVector<int> restoredTriangularToNative;
887 for ( int i = 0; i < changes.mNativeFacesToRemove.count(); ++i )
888 {
889 const QgsMeshFace &nativeFace = changes.mNativeFacesToRemove.at( i );
890 triangulateFaces( nativeFace,
891 changes.mNativeFaceIndexesToRemove.at( i ),
892 restoredTriangles,
893 restoredTriangularToNative,
894 mTriangularMesh );
895 mNativeMeshFaceCentroids[changes.mNativeFaceIndexesToRemove.at( i )] = calculateCentroid( nativeFace );
896 }
897 for ( int i = 0; i < changes.mRemovedTriangleIndexes.count(); ++i )
898 {
899 int triangleIndex = changes.mRemovedTriangleIndexes.at( i );
900 mTriangularMesh.faces[triangleIndex] = restoredTriangles.at( i );
901 mSpatialFaceIndex.addFace( triangleIndex, mTriangularMesh );
902 mTrianglesToNativeFaces[triangleIndex] = restoredTriangularToNative.at( i );
903 }
904
905 // reverse Z value
906 for ( int i = 0; i < changes.mOldZValue.count(); ++i )
907 {
908 int vertexIndex = changes.mChangedVerticesCoordinates.at( i );
909 mTriangularMesh.vertices[vertexIndex].setZ( changes.mOldZValue.at( i ) );
910 }
911
912 //remove outdated spatial index
913 for ( const int triangleIndex : std::as_const( changes.mTriangleIndexesGeometryChanged ) )
914 mSpatialFaceIndex.removeFace( triangleIndex, mTriangularMesh );
915
916 // reverse (X,Y) of vertices
917 for ( int i = 0; i < changes.mOldXYValue.count(); ++i )
918 {
919 const QgsPointXY &nativeCoordinates = changes.mOldXYValue.at( i );
920 const QgsMeshVertex nativeVertex( nativeCoordinates.x(),
921 nativeCoordinates.y(),
922 mTriangularMesh.vertices.at( changes.mChangedVerticesCoordinates.at( i ) ).z() );
923
924 mTriangularMesh.vertices[changes.mChangedVerticesCoordinates.at( i )] = nativeToTriangularCoordinates( nativeVertex );
925 }
926
927 //restore spatial undex
928 for ( const int triangleIndex : std::as_const( changes.mTriangleIndexesGeometryChanged ) )
929 mSpatialFaceIndex.addFace( triangleIndex, mTriangularMesh );
930
931 //update native faces
932 for ( int i = 0; i < changes.mNativeFaceIndexesGeometryChanged.count(); ++i )
933 mNativeMeshFaceCentroids[changes.mNativeFaceIndexesGeometryChanged.at( i )] = calculateCentroid( changes.mNativeFacesGeometryChanged.at( i ) );
934}
935
937 const QgsMesh &nativeMesh )
938{
939 mAddedVertices = topologicalChanges.addedVertices();
940 mVerticesIndexesToRemove = topologicalChanges.verticesToRemoveIndexes();
941 mNativeFacesToAdd = topologicalChanges.addedFaces();
942 mNativeFacesToRemove = topologicalChanges.removedFaces();
943 mNativeFaceIndexesToRemove = topologicalChanges.removedFaceIndexes();
944 mChangedVerticesCoordinates = topologicalChanges.changedCoordinatesVerticesIndexes();
945 mNewZValue = topologicalChanges.newVerticesZValues();
946 mNewXYValue = topologicalChanges.newVerticesXYValues();
947 mOldXYValue = topologicalChanges.oldVerticesXYValues();
948
949 mNativeFaceIndexesGeometryChanged = topologicalChanges.nativeFacesIndexesGeometryChanged();
950 mNativeFacesGeometryChanged.resize( mNativeFaceIndexesGeometryChanged.count() );
951 for ( int i = 0; i < mNativeFaceIndexesGeometryChanged.count(); ++i )
952 mNativeFacesGeometryChanged[i] = nativeMesh.face( mNativeFaceIndexesGeometryChanged.at( i ) );
953}
954
955QgsMeshVertex QgsMeshUtils::centroid( const QgsMeshFace &face, const QVector<QgsMeshVertex> &vertices )
956{
957 QVector<QPointF> points( face.size() );
958 for ( int j = 0; j < face.size(); ++j )
959 {
960 int index = face.at( j );
961 const QgsMeshVertex &vertex = vertices.at( index ); // we need vertices in map coordinate
962 points[j] = vertex.toQPointF();
963 }
964 QPolygonF poly( points );
965 double cx, cy;
966 ENP_centroid( poly, cx, cy );
967 return QgsMeshVertex( cx, cy );
968}
969
971{
972 //To have consistent clock wise orientation of triangles which is necessary for 3D rendering
973 //Check the clock wise, and if it is not counter clock wise, swap indexes to make the oientation counter clock wise
974 double ux = v1.x() - v0.x();
975 double uy = v1.y() - v0.y();
976 double vx = v2.x() - v0.x();
977 double vy = v2.y() - v0.y();
978
979 double crossProduct = ux * vy - uy * vx;
980 if ( crossProduct < 0 ) //CW -->change the orientation
981 {
982 std::swap( triangle[1], triangle[2] );
983 }
984}
TransformDirection
Flags for raster layer temporal capabilities.
Definition: qgis.h:1288
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:45
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 QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#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