QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
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 "qgstriangularmesh.h"
19
20#include "meshoptimizer.h"
22#include "qgsgeometry.h"
23#include "qgslogger.h"
24#include "qgsmeshlayerutils.h"
25#include "qgsmeshspatialindex.h"
26#include "qgsmeshutils.h"
27#include "qgsrectangle.h"
28
29#include <QList>
30
31static void triangulateFaces( const QgsMeshFace &face,
32 int nativeIndex,
33 QVector<QgsMeshFace> &destinationFaces,
34 QVector<int> &triangularToNative,
35 const QgsMesh &verticesMeshSource )
36{
37 int vertexCount = face.size();
38 if ( vertexCount < 3 )
39 return;
40
41 while ( vertexCount > 3 )
42 {
43 // clip one ear from last 2 and first vertex
44 const QgsMeshFace ear = { face[vertexCount - 2], face[vertexCount - 1], face[0] };
45 if ( !( std::isnan( verticesMeshSource.vertex( ear[0] ).x() ) ||
46 std::isnan( verticesMeshSource.vertex( ear[1] ).x() ) ||
47 std::isnan( verticesMeshSource.vertex( ear[2] ).x() ) ) )
48 {
49 destinationFaces.push_back( ear );
50 triangularToNative.push_back( nativeIndex );
51 }
52 --vertexCount;
53 }
54
55 const QgsMeshFace triangle = { face[1], face[2], face[0] };
56 if ( !( std::isnan( verticesMeshSource.vertex( triangle[0] ).x() ) ||
57 std::isnan( verticesMeshSource.vertex( triangle[1] ).x() ) ||
58 std::isnan( verticesMeshSource.vertex( triangle[2] ).x() ) ) )
59 {
60 destinationFaces.push_back( triangle );
61 triangularToNative.push_back( nativeIndex );
62 }
63}
64
65void QgsTriangularMesh::triangulate( const QgsMeshFace &face, int nativeIndex )
66{
67 triangulateFaces( face, nativeIndex, mTriangularMesh.faces, mTrianglesToNativeFaces, mTriangularMesh );
68}
69
70QgsMeshVertex QgsTriangularMesh::transformVertex( const QgsMeshVertex &vertex, Qgis::TransformDirection direction ) const
71{
72 QgsMeshVertex transformedVertex = vertex;
73
74 if ( mCoordinateTransform.isValid() )
75 {
76 try
77 {
78 transformedVertex.transform( mCoordinateTransform, direction );
79 }
80 catch ( QgsCsException &cse )
81 {
82 Q_UNUSED( cse )
83 QgsDebugError( QStringLiteral( "Caught CRS exception %1" ).arg( cse.what() ) );
84 transformedVertex = QgsMeshVertex();
85 }
86 }
87
88 return transformedVertex;
89}
90
91QgsMeshVertex QgsTriangularMesh::calculateCentroid( const QgsMeshFace &nativeFace ) const
92{
93 return QgsMeshUtils::centroid( nativeFace, mTriangularMesh.vertices );
94}
95
97{
98 return mAverageTriangleSize;
99}
100
103
104bool QgsTriangularMesh::update( QgsMesh *nativeMesh, const QgsCoordinateTransform &transform )
105{
106 Q_ASSERT( nativeMesh );
107
108 bool needUpdateVerticesCoordinates = mTriangularMesh.vertices.size() != nativeMesh->vertices.size() ||
109 ( ( mCoordinateTransform.isValid() || transform.isValid() ) &&
110 ( mCoordinateTransform.sourceCrs() != transform.sourceCrs() ||
111 mCoordinateTransform.destinationCrs() != transform.destinationCrs() ||
112 mCoordinateTransform.isValid() != transform.isValid() ) ) ;
113
114 bool needUpdateFrame = mTriangularMesh.vertices.size() != nativeMesh->vertices.size() ||
115 mNativeMeshFaceCentroids.size() != nativeMesh->faces.size() ||
116 mTriangularMesh.faces.size() < nativeMesh->faces.size() ||
117 mTriangularMesh.edges.size() != nativeMesh->edges.size();
118
119
120 // FIND OUT IF UPDATE IS NEEDED
121 if ( ! needUpdateVerticesCoordinates && !needUpdateFrame )
122 return false;
123
124 // CLEAN-UP
125 mTriangularMesh.vertices.clear();
126 if ( needUpdateFrame )
127 {
128 mTriangularMesh.faces.clear();
129 mTriangularMesh.edges.clear();
130 mEdgesToNativeEdges.clear();
131 mTrianglesToNativeFaces.clear();
132 }
133
134 // TRANSFORM VERTICES
135 mCoordinateTransform = transform;
136 mTriangularMesh.vertices.resize( nativeMesh->vertices.size() );
137 mExtent.setNull();
138 for ( int i = 0; i < nativeMesh->vertices.size(); ++i )
139 {
140 mTriangularMesh.vertices[i] = nativeToTriangularCoordinates( nativeMesh->vertices.at( i ) );
141 mExtent.include( mTriangularMesh.vertices.at( i ) );
142 }
143
144 if ( needUpdateFrame )
145 {
146 // CREATE TRIANGULAR MESH
147 for ( int i = 0; i < nativeMesh->faces.size(); ++i )
148 {
149 const QgsMeshFace &face = nativeMesh->faces.at( i ) ;
150 triangulate( face, i );
151 }
152 }
153
154 // CALCULATE CENTROIDS
155 mNativeMeshFaceCentroids.resize( nativeMesh->faces.size() );
156 for ( int i = 0; i < nativeMesh->faces.size(); ++i )
157 {
158 const QgsMeshFace &face = nativeMesh->faces.at( i ) ;
159 mNativeMeshFaceCentroids[i] = calculateCentroid( face );
160 }
161
162 // CALCULATE SPATIAL INDEX
163 mSpatialFaceIndex = QgsMeshSpatialIndex( mTriangularMesh, nullptr, QgsMesh::ElementType::Face );
164
165 if ( needUpdateFrame )
166 {
167 // SET ALL TRIANGLE CCW AND COMPUTE AVERAGE SIZE
168 finalizeTriangles();
169 }
170
171 // CREATE EDGES
172 // remove all edges with invalid vertices
173 if ( needUpdateFrame )
174 {
175 const QVector<QgsMeshEdge> edges = nativeMesh->edges;
176 for ( int nativeIndex = 0; nativeIndex < edges.size(); ++nativeIndex )
177 {
178 const QgsMeshEdge &edge = edges.at( nativeIndex );
179 if ( !( std::isnan( mTriangularMesh.vertex( edge.first ).x() ) ||
180 std::isnan( mTriangularMesh.vertex( edge.second ).x() ) ) )
181 {
182 mTriangularMesh.edges.push_back( edge );
183 mEdgesToNativeEdges.push_back( nativeIndex );
184 }
185 }
186 }
187
188 // CALCULATE CENTROIDS
189 mNativeMeshEdgeCentroids.resize( nativeMesh->edgeCount() );
190 for ( int i = 0; i < nativeMesh->edgeCount(); ++i )
191 {
192 const QgsMeshEdge &edge = nativeMesh->edges.at( i ) ;
193 const QgsPoint &a = mTriangularMesh.vertices[edge.first];
194 const QgsPoint &b = mTriangularMesh.vertices[edge.second];
195 mNativeMeshEdgeCentroids[i] = QgsMeshVertex( ( a.x() + b.x() ) / 2.0, ( a.y() + b.y() ) / 2.0, ( a.z() + b.z() ) / 2.0 );
196 }
197
198 // CALCULATE SPATIAL INDEX
199 mSpatialEdgeIndex = QgsMeshSpatialIndex( mTriangularMesh, nullptr, QgsMesh::ElementType::Edge );
200
201 return true;
202}
203
205{
206 return update( nativeMesh, mCoordinateTransform );
207}
208
209void QgsTriangularMesh::finalizeTriangles()
210{
211 mAverageTriangleSize = 0;
212 for ( int i = 0; i < mTriangularMesh.faceCount(); ++i )
213 {
214 QgsMeshFace &face = mTriangularMesh.faces[i];
215
216 const QgsMeshVertex &v0 = mTriangularMesh.vertex( face[0] );
217 const QgsMeshVertex &v1 = mTriangularMesh.vertex( face[1] );
218 const QgsMeshVertex &v2 = mTriangularMesh.vertex( face[2] );
219
220 QgsRectangle bbox = QgsMeshLayerUtils::triangleBoundingBox( v0, v1, v2 );
221
222 mAverageTriangleSize += std::fmax( bbox.width(), bbox.height() );
223
224 QgsMeshUtils::setCounterClockwise( face, v0, v1, v2 );
225 }
226 mAverageTriangleSize /= mTriangularMesh.faceCount();
227}
228
230{
231 return transformVertex( vertex, Qgis::TransformDirection::Forward );
232}
233
235{
236 return transformVertex( vertex, Qgis::TransformDirection::Reverse );
237}
238
240{
242 if ( !mCoordinateTransform.isShortCircuited() )
243 {
244 try
245 {
246 QgsCoordinateTransform extentTransform = mCoordinateTransform;
247 extentTransform.setBallparkTransformsAreAppropriate( true );
249 }
250 catch ( QgsCsException &cse )
251 {
252 Q_UNUSED( cse )
253 QgsDebugError( QStringLiteral( "Caught CRS exception %1" ).arg( cse.what() ) );
254 }
255 }
256 else
258
259 return nativeExtent;
260}
261
263{
264 if ( !mIsExtentValid )
265 {
266 mExtent.setNull();
267 for ( int i = 0; i < mTriangularMesh.vertices.size(); ++i )
268 if ( !mTriangularMesh.vertices.at( i ).isEmpty() )
269 mExtent.include( mTriangularMesh.vertices.at( i ) );
270
271 mIsExtentValid = true;
272 }
273 return mExtent;
274}
275
277{
278 return mLod;
279}
280
282{
283 switch ( type )
284 {
286 return mTriangularMesh.vertexCount() != 0;
288 return mTriangularMesh.edgeCount() != 0;
290 return mTriangularMesh.faceCount() != 0;
291 }
292
293 return false;
294}
295
296void QgsTriangularMesh::addVertex( const QgsMeshVertex &vertex )
297{
298 QgsMeshVertex vertexInTriangularCoordinates = nativeToTriangularCoordinates( vertex );
299 mTriangularMesh.vertices.append( vertexInTriangularCoordinates );
300 if ( !vertexInTriangularCoordinates.isEmpty() )
301 mExtent.include( vertexInTriangularCoordinates );
302}
303
304const QVector<QgsMeshVertex> &QgsTriangularMesh::vertices() const
305{
306 return mTriangularMesh.vertices;
307}
308
309const QVector<QgsMeshFace> &QgsTriangularMesh::triangles() const
310{
311 return mTriangularMesh.faces;
312}
313
314const QVector<QgsMeshEdge> &QgsTriangularMesh::edges() const
315{
316 return mTriangularMesh.edges;
317}
318
319const QVector<QgsMeshVertex> &QgsTriangularMesh::centroids() const
320{
321 return faceCentroids();
322}
323
324const QVector<QgsMeshVertex> &QgsTriangularMesh::faceCentroids() const
325{
326 return mNativeMeshFaceCentroids;
327}
328
329const QVector<QgsMeshVertex> &QgsTriangularMesh::edgeCentroids() const
330{
331 return mNativeMeshEdgeCentroids;
332}
333
335{
336 return mTrianglesToNativeFaces;
337}
338
339const QVector<int> &QgsTriangularMesh::edgesToNativeEdges() const
340{
341 return mEdgesToNativeEdges;
342}
343
345{
346 QgsPointXY mapPoint;
347 if ( mCoordinateTransform.isValid() )
348 {
349 try
350 {
351 mapPoint = mCoordinateTransform.transform( point );
352 }
353 catch ( QgsCsException &cse )
354 {
355 Q_UNUSED( cse )
356 QgsDebugError( QStringLiteral( "Caught CRS exception %1" ).arg( cse.what() ) );
357 mapPoint = point;
358 }
359 }
360 else
361 mapPoint = point;
362
363 return point;
364}
365
367{
368 const QList<int> faceIndexes = mSpatialFaceIndex.intersects( QgsRectangle( point, point ) );
369 for ( const int faceIndex : faceIndexes )
370 {
371 const QgsMeshFace &face = mTriangularMesh.faces.at( faceIndex );
372 const QgsGeometry geom = QgsMeshUtils::toGeometry( face, mTriangularMesh.vertices );
373 if ( geom.contains( &point ) )
374 return faceIndex;
375 }
376 return -1;
377}
378
380{
381 int triangleIndex = faceIndexForPoint_v2( point );
382 if ( triangleIndex == -1 )
383 return -1;
384
385 if ( triangleIndex < mTrianglesToNativeFaces.count() )
386 return mTrianglesToNativeFaces.at( triangleIndex );
387
388 return -1;
389}
390
392{
393 QSet<int> concernedFaceIndex = QgsMeshUtils::nativeFacesFromTriangles(
394 faceIndexesForRectangle( rectangle ),
396 return concernedFaceIndex.values();
397}
398
400{
401 const QList<int> faceIndexes = mSpatialFaceIndex.intersects( QgsRectangle( point, point ) );
402
403 for ( const int faceIndex : faceIndexes )
404 {
405 const QgsMeshFace &face = mTriangularMesh.faces.at( faceIndex );
406 if ( QgsMeshUtils::isInTriangleFace( point, face, mTriangularMesh.vertices ) )
407 return faceIndex;
408 }
409 return -1;
410}
411
413{
414 return mSpatialFaceIndex.intersects( rectangle );
415}
416
418{
419 return mSpatialEdgeIndex.intersects( rectangle );
420}
421
422QVector<QVector3D> QgsTriangularMesh::vertexNormals( float vertScale ) const
423{
424 QVector<QVector3D> normals( vertices().count(), QVector3D( 0, 0, 0 ) );
425
426 for ( const auto &face : triangles() )
427 {
428 if ( face.isEmpty() )
429 continue;
430
431 for ( int i = 0; i < 3; i++ )
432 {
433 int index1( face.at( i ) );
434 int index2( face.at( ( i + 1 ) % 3 ) );
435 int index3( face.at( ( i + 2 ) % 3 ) );
436
437 const QgsMeshVertex &vert( vertices().at( index1 ) );
438 const QgsMeshVertex &otherVert1( vertices().at( index2 ) );
439 const QgsMeshVertex &otherVert2( vertices().at( index3 ) );
440
441 QVector3D v1( float( otherVert1.x() - vert.x() ), float( otherVert1.y() - vert.y() ), vertScale * float( otherVert1.z() - vert.z() ) );
442 QVector3D v2( float( otherVert2.x() - vert.x() ), float( otherVert2.y() - vert.y() ), vertScale * float( otherVert2.z() - vert.z() ) );
443
444 normals[index1] += QVector3D::crossProduct( v1, v2 );
445 }
446 }
447 return normals;
448}
449
450QVector<QgsTriangularMesh *> QgsTriangularMesh::simplifyMesh( double reductionFactor, int minimumTrianglesCount ) const
451{
452 QVector<QgsTriangularMesh *> simplifiedMeshes;
453
454 if ( mTriangularMesh.edgeCount() != 0 )
455 return simplifiedMeshes;
456
457 if ( !( reductionFactor > 1 ) )
458 return simplifiedMeshes;
459
460 size_t verticesCount = size_t( mTriangularMesh.vertices.count() );
461
462 unsigned int baseIndexCount = mTriangularMesh.faceCount() * 3;
463
464 QVector<unsigned int> indexes( mTriangularMesh.faces.count() * 3 );
465 for ( int i = 0; i < mTriangularMesh.faceCount(); ++i )
466 {
467 const QgsMeshFace &f = mTriangularMesh.face( i );
468 for ( int j = 0; j < 3; ++j )
469 indexes[i * 3 + j] = f.at( j );
470 }
471
472 QVector<float> vertices( mTriangularMesh.vertices.count() * 3 );
473 for ( int i = 0; i < mTriangularMesh.vertices.count(); ++i )
474 {
475 const QgsMeshVertex &v = mTriangularMesh.vertex( i );
476 vertices[i * 3] = v.x() ;
477 vertices[i * 3 + 1] = v.y() ;
478 vertices[i * 3 + 2] = v.z() ;
479 }
480
481 int path = 0;
482 while ( true )
483 {
484 QgsTriangularMesh *simplifiedMesh = new QgsTriangularMesh( *this );
485 size_t maxNumberOfIndexes = baseIndexCount / pow( reductionFactor, path + 1 );
486
487 if ( indexes.size() <= int( maxNumberOfIndexes ) )
488 {
489 delete simplifiedMesh;
490 break;
491 }
492
493 QVector<unsigned int> returnIndexes( indexes.size() );
494 //returned size could be different than goal size but not than the input indexes count
495 size_t size = meshopt_simplifySloppy(
496 returnIndexes.data(),
497 indexes.data(),
498 indexes.size(),
499 vertices.data(),
500 verticesCount,
501 sizeof( float ) * 3,
502 maxNumberOfIndexes
503#if MESHOPTIMIZER_VERSION > 150
504 , 0
505 , nullptr
506#endif
507 );
508
509
510 returnIndexes.resize( size );
511
512 if ( size == 0 || int( size ) >= indexes.size() )
513 {
514 QgsDebugError( QStringLiteral( "Mesh simplification failed after %1 path" ).arg( path + 1 ) );
515 delete simplifiedMesh;
516 break;
517 }
518
519 QgsMesh newMesh;
520 newMesh.vertices = mTriangularMesh.vertices;
521
522 newMesh.faces.resize( returnIndexes.size() / 3 );
523 for ( int i = 0; i < newMesh.faces.size(); ++i )
524 {
525 QgsMeshFace f( 3 );
526 for ( size_t j = 0; j < 3 ; ++j )
527 f[j] = returnIndexes.at( i * 3 + j ) ;
528 newMesh.faces[i ] = f;
529 }
530
531 simplifiedMesh->mTriangularMesh = newMesh;
532 simplifiedMesh->mSpatialFaceIndex = QgsMeshSpatialIndex( simplifiedMesh->mTriangularMesh );
533 simplifiedMesh->finalizeTriangles();
534 simplifiedMeshes.push_back( simplifiedMesh );
535
536 QgsDebugMsgLevel( QStringLiteral( "Simplified mesh created with %1 triangles" ).arg( newMesh.faceCount() ), 2 );
537
538 simplifiedMesh->mTrianglesToNativeFaces = QVector<int>( simplifiedMesh->triangles().count(), 0 );
539 for ( int i = 0; i < simplifiedMesh->mTrianglesToNativeFaces.count(); ++i )
540 {
541 QgsMeshFace triangle = simplifiedMesh->triangles().at( i );
542 double x = 0;
543 double y = 0;
544 for ( size_t j = 0; j < 3 ; ++j )
545 {
546 x += mTriangularMesh.vertex( triangle[j] ).x();
547 y += mTriangularMesh.vertex( triangle[j] ).y();
548 }
549 x /= 3;
550 y /= 3;
551 QgsPoint centroid( x, y );
552 int indexInBaseMesh = faceIndexForPoint_v2( centroid );
553
554 if ( indexInBaseMesh == -1 )
555 {
556 // sometime the centroid of simplified mesh could be outside the base mesh,
557 // so try with vertices of the simplified triangle
558 int j = 0;
559 while ( indexInBaseMesh == -1 && j < 3 )
560 indexInBaseMesh = faceIndexForPoint_v2( mTriangularMesh.vertex( triangle[j++] ) );
561 }
562
563 if ( indexInBaseMesh > -1 && indexInBaseMesh < mTrianglesToNativeFaces.count() )
564 simplifiedMesh->mTrianglesToNativeFaces[i] = mTrianglesToNativeFaces[indexInBaseMesh];
565 }
566
567 simplifiedMesh->mLod = path + 1;
568 simplifiedMesh->mBaseTriangularMesh = this;
569
570 if ( simplifiedMesh->triangles().count() < minimumTrianglesCount )
571 break;
572
573 indexes = returnIndexes;
574 ++path;
575 }
576
577 return simplifiedMeshes;
578}
579
581{
582 //if necessary defined removes triangles index
583 if ( changes.mRemovedTriangleIndexes.isEmpty() && !changes.mNativeFaceIndexesToRemove.isEmpty() )
584 {
585 for ( int nf = 0; nf < changes.mNativeFaceIndexesToRemove.count(); ++nf )
586 {
587 int nativeIndex = changes.mNativeFaceIndexesToRemove.at( nf );
588 const QgsMeshFace &nativeFace = changes.mNativeFacesToRemove.at( nf );
589 Q_ASSERT( !nativeFace.isEmpty() );
590
591 QgsRectangle nativeFaceExtent( mTriangularMesh.vertex( nativeFace.at( 0 ) ), mTriangularMesh.vertex( nativeFace.at( 0 ) ) );
592 for ( int i = 1; i < nativeFace.count(); ++i )
593 {
594 const QgsMeshVertex &triangularVertex = mTriangularMesh.vertex( nativeFace.at( i ) );
595 nativeFaceExtent.include( triangularVertex );
596 }
597
598 QList<int> concernedTriangle = faceIndexesForRectangle( nativeFaceExtent );
599 //Remove only those corresponding to the native face
600 for ( int i = 0; i < concernedTriangle.count(); ++i )
601 {
602 int triangleIndex = concernedTriangle.at( i );
603 if ( mTrianglesToNativeFaces.at( triangleIndex ) == nativeIndex )
604 changes.mRemovedTriangleIndexes.append( triangleIndex );
605 }
606 }
607 }
608
609 if ( changes.mOldZValue.isEmpty() && !changes.mNewZValue.isEmpty() )
610 {
611 changes.mOldZValue.reserve( changes.mNewZValue.count() );
612 for ( int i = 0; i < changes.mNewZValue.count(); ++i )
613 changes.mOldZValue.append( mTriangularMesh.vertices.at( changes.mChangedVerticesCoordinates.at( i ) ).z() );
614 }
615
616 if ( changes.mTriangleIndexesGeometryChanged.isEmpty() && !changes.mNativeFaceIndexesGeometryChanged.isEmpty() )
617 {
618 for ( int i = 0; i < changes.mNativeFaceIndexesGeometryChanged.count(); ++i )
619 {
620 const QgsMeshFace &nativeFace = changes.mNativeFacesGeometryChanged.at( i );
621 if ( nativeFace.count() < 2 )
622 continue;
623 QgsRectangle bbox( mTriangularMesh.vertices.at( nativeFace.at( 0 ) ), mTriangularMesh.vertices.at( nativeFace.at( 1 ) ) );
624
625 for ( int i = 2; i < nativeFace.count(); ++i )
626 bbox.include( mTriangularMesh.vertices.at( nativeFace.at( i ) ) );
627
628 QList<int> triangleIndexes = faceIndexesForRectangle( bbox );
629 int pos = 0;
630 while ( pos < triangleIndexes.count() )
631 {
632 if ( trianglesToNativeFaces().at( triangleIndexes.at( pos ) ) !=
633 changes.mNativeFaceIndexesGeometryChanged.at( i ) )
634 triangleIndexes.removeAt( pos );
635 else
636 ++pos;
637 }
638 changes.mTriangleIndexesGeometryChanged.append( triangleIndexes );
639 }
640 }
641
642 // add vertices
643 for ( const QgsMeshVertex &vertex : std::as_const( changes.mAddedVertices ) )
644 addVertex( vertex );
645
646 // add faces
647 if ( !changes.mNativeFacesToAdd.isEmpty() )
648 {
649 changes.mTrianglesAddedStartIndex = mTriangularMesh.faceCount();
650 int firstNewNativeFacesIndex = mNativeMeshFaceCentroids.count();
651 for ( int i = 0; i < changes.mNativeFacesToAdd.count(); ++i )
652 {
653 const QgsMeshFace &nativeFace = changes.mNativeFacesToAdd.at( i );
654 triangulate( nativeFace, firstNewNativeFacesIndex + i );
655 mNativeMeshFaceCentroids.append( calculateCentroid( nativeFace ) );
656 }
657
658 for ( int i = changes.mTrianglesAddedStartIndex; i < mTriangularMesh.faceCount(); ++i )
659 mSpatialFaceIndex.addFace( i, mTriangularMesh );
660 }
661
662 // remove faces
663 for ( int i = 0; i < changes.mRemovedTriangleIndexes.count(); ++i )
664 {
665 int triangleIndex = changes.mRemovedTriangleIndexes.at( i );
666 mTrianglesToNativeFaces[triangleIndex] = -1;
667 mSpatialFaceIndex.removeFace( triangleIndex, mTriangularMesh );
668 mTriangularMesh.faces[triangleIndex] = QgsMeshFace();
669 }
670
671 for ( int i = 0; i < changes.mNativeFaceIndexesToRemove.count(); ++i )
672 mNativeMeshFaceCentroids[changes.mNativeFaceIndexesToRemove.at( i )] = QgsMeshVertex();
673
674 // remove vertices
675 for ( int i = 0; i < changes.mVerticesIndexesToRemove.count(); ++i )
676 mTriangularMesh.vertices[changes.mVerticesIndexesToRemove.at( i )] = QgsMeshVertex();
677
678 if ( !changes.mVerticesIndexesToRemove.isEmpty() )
679 mIsExtentValid = false;
680
681 // change Z value
682 for ( int i = 0; i < changes.mNewZValue.count(); ++i )
683 {
684 int vertexIndex = changes.mChangedVerticesCoordinates.at( i );
685 mTriangularMesh.vertices[vertexIndex].setZ( changes.mNewZValue.at( i ) );
686 }
687
688 //remove outdated spatial index
689 for ( const int triangleIndex : std::as_const( changes.mTriangleIndexesGeometryChanged ) )
690 mSpatialFaceIndex.removeFace( triangleIndex, mTriangularMesh );
691
692 // change (X,Y) of vertices
693 for ( int i = 0; i < changes.mNewXYValue.count(); ++i )
694 {
695 const QgsPointXY &nativeCoordinates = changes.mNewXYValue.at( i );
696 const QgsMeshVertex nativeVertex( nativeCoordinates.x(),
697 nativeCoordinates.y(),
698 mTriangularMesh.vertices.at( changes.mChangedVerticesCoordinates.at( i ) ).z() );
699
700 mTriangularMesh.vertices[changes.mChangedVerticesCoordinates.at( i )] = nativeToTriangularCoordinates( nativeVertex );
701 }
702
703 //restore spatial undex
704 for ( const int triangleIndex : std::as_const( changes.mTriangleIndexesGeometryChanged ) )
705 mSpatialFaceIndex.addFace( triangleIndex, mTriangularMesh );
706
707 //update native faces
708 for ( int i = 0; i < changes.mNativeFaceIndexesGeometryChanged.count(); ++i )
709 mNativeMeshFaceCentroids[changes.mNativeFaceIndexesGeometryChanged.at( i )] = calculateCentroid( changes.mNativeFacesGeometryChanged.at( i ) );
710}
711
713{
714 //reverse added faces and added vertices
715 if ( !changes.mNativeFacesToAdd.isEmpty() )
716 {
717 for ( int i = changes.mTrianglesAddedStartIndex; i < mTriangularMesh.faceCount(); ++i )
718 mSpatialFaceIndex.removeFace( i, mTriangularMesh );
719
720 int initialNativeFacesCount = mNativeMeshFaceCentroids.count() - changes.mNativeFacesToAdd.count();
721
722 mTriangularMesh.faces.resize( changes.mTrianglesAddedStartIndex );
723 mTrianglesToNativeFaces.resize( changes.mTrianglesAddedStartIndex );
724 mNativeMeshFaceCentroids.resize( initialNativeFacesCount );
725 }
726
727 int initialVerticesCount = mTriangularMesh.vertices.count() - changes.mAddedVertices.count();
728 mTriangularMesh.vertices.resize( initialVerticesCount );
729
730 if ( !changes.mAddedVertices.isEmpty() )
731 mIsExtentValid = false;
732
733 // for each vertex to remove we need to update the vertices with the native vertex
734 for ( const int i : std::as_const( changes.mVerticesIndexesToRemove ) )
735 mTriangularMesh.vertices[i] = nativeToTriangularCoordinates( nativeMesh.vertex( i ) );
736
737 if ( !changes.mVerticesIndexesToRemove.isEmpty() )
738 mIsExtentValid = false;
739
740 // reverse removed faces
741 QVector<QgsMeshFace> restoredTriangles;
742 QVector<int> restoredTriangularToNative;
743 for ( int i = 0; i < changes.mNativeFacesToRemove.count(); ++i )
744 {
745 const QgsMeshFace &nativeFace = changes.mNativeFacesToRemove.at( i );
746 triangulateFaces( nativeFace,
747 changes.mNativeFaceIndexesToRemove.at( i ),
748 restoredTriangles,
749 restoredTriangularToNative,
750 mTriangularMesh );
751 mNativeMeshFaceCentroids[changes.mNativeFaceIndexesToRemove.at( i )] = calculateCentroid( nativeFace );
752 }
753 for ( int i = 0; i < changes.mRemovedTriangleIndexes.count(); ++i )
754 {
755 int triangleIndex = changes.mRemovedTriangleIndexes.at( i );
756 mTriangularMesh.faces[triangleIndex] = restoredTriangles.at( i );
757 mSpatialFaceIndex.addFace( triangleIndex, mTriangularMesh );
758 mTrianglesToNativeFaces[triangleIndex] = restoredTriangularToNative.at( i );
759 }
760
761 // reverse Z value
762 for ( int i = 0; i < changes.mOldZValue.count(); ++i )
763 {
764 int vertexIndex = changes.mChangedVerticesCoordinates.at( i );
765 mTriangularMesh.vertices[vertexIndex].setZ( changes.mOldZValue.at( i ) );
766 }
767
768 //remove outdated spatial index
769 for ( const int triangleIndex : std::as_const( changes.mTriangleIndexesGeometryChanged ) )
770 mSpatialFaceIndex.removeFace( triangleIndex, mTriangularMesh );
771
772 // reverse (X,Y) of vertices
773 for ( int i = 0; i < changes.mOldXYValue.count(); ++i )
774 {
775 const QgsPointXY &nativeCoordinates = changes.mOldXYValue.at( i );
776 const QgsMeshVertex nativeVertex( nativeCoordinates.x(),
777 nativeCoordinates.y(),
778 mTriangularMesh.vertices.at( changes.mChangedVerticesCoordinates.at( i ) ).z() );
779
780 mTriangularMesh.vertices[changes.mChangedVerticesCoordinates.at( i )] = nativeToTriangularCoordinates( nativeVertex );
781 }
782
783 //restore spatial undex
784 for ( const int triangleIndex : std::as_const( changes.mTriangleIndexesGeometryChanged ) )
785 mSpatialFaceIndex.addFace( triangleIndex, mTriangularMesh );
786
787 //update native faces
788 for ( int i = 0; i < changes.mNativeFaceIndexesGeometryChanged.count(); ++i )
789 mNativeMeshFaceCentroids[changes.mNativeFaceIndexesGeometryChanged.at( i )] = calculateCentroid( changes.mNativeFacesGeometryChanged.at( i ) );
790}
791
793 const QgsMesh &nativeMesh )
794{
795 mAddedVertices = topologicalChanges.addedVertices();
796 mVerticesIndexesToRemove = topologicalChanges.verticesToRemoveIndexes();
797 mNativeFacesToAdd = topologicalChanges.addedFaces();
798 mNativeFacesToRemove = topologicalChanges.removedFaces();
799 mNativeFaceIndexesToRemove = topologicalChanges.removedFaceIndexes();
800 mChangedVerticesCoordinates = topologicalChanges.changedCoordinatesVerticesIndexes();
801 mNewZValue = topologicalChanges.newVerticesZValues();
802 mNewXYValue = topologicalChanges.newVerticesXYValues();
803 mOldXYValue = topologicalChanges.oldVerticesXYValues();
804
805 mNativeFaceIndexesGeometryChanged = topologicalChanges.nativeFacesIndexesGeometryChanged();
806 mNativeFacesGeometryChanged.resize( mNativeFaceIndexesGeometryChanged.count() );
807 for ( int i = 0; i < mNativeFaceIndexesGeometryChanged.count(); ++i )
808 mNativeFacesGeometryChanged[i] = nativeMesh.face( mNativeFaceIndexesGeometryChanged.at( i ) );
809}
TransformDirection
Indicates the direction (forward or inverse) of a transform.
Definition qgis.h:2671
@ Forward
Forward transform (from source to destination).
Definition qgis.h:2672
@ Reverse
Reverse/inverse transform (from destination to source).
Definition qgis.h:2673
QgsVertexIterator vertices() const
Returns a read-only, Java-style iterator for traversal of vertices of all the geometry,...
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
Handles coordinate transforms between two 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.
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool handle180Crossover=false) const
Transforms a rectangle from the source CRS to the destination CRS.
bool isValid() const
Returns true if the coordinate transform is valid, ie both the source and destination CRS have been s...
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system, which the transform will transform coordinates t...
Custom exception class for Coordinate Reference System related exceptions.
QString what() const
A geometry is the spatial representation of a feature.
bool contains(const QgsPointXY *p) const
Returns true if the geometry contains the point p.
A spatial index for QgsMeshFace or QgsMeshEdge objects.
static bool isInTriangleFace(const QgsPointXY point, const QgsMeshFace &face, const QVector< QgsMeshVertex > &vertices)
Tests if point p is on the face defined with vertices.
static QgsGeometry toGeometry(const QgsMeshFace &face, const QVector< QgsMeshVertex > &vertices)
Returns face as polygon geometry.
static 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.
static QgsMeshVertex centroid(const QgsMeshFace &face, const QVector< QgsMeshVertex > &vertices)
Returns the centroid of the face.
static QSet< int > nativeFacesFromTriangles(const QList< int > &triangleIndexes, const QVector< int > &trianglesToNativeFaces)
Returns unique native faces indexes from list of triangle indexes.
Represents a 2D point.
Definition qgspointxy.h:60
double y
Definition qgspointxy.h:64
double x
Definition qgspointxy.h:63
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
double z
Definition qgspoint.h:54
double x
Definition qgspoint.h:52
bool isEmpty() const override
Returns true if the geometry is empty.
Definition qgspoint.cpp:739
void transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection d=Qgis::TransformDirection::Forward, bool transformZ=false) override
Transforms the geometry using a coordinate transform.
Definition qgspoint.cpp:387
double y
Definition qgspoint.h:53
A rectangle specified with double values.
void include(const QgsPointXY &p)
Updates the rectangle to include the specified point.
Contains topological differences between two states of a topological mesh, only accessible from the Q...
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.
Makes changes to a triangular mesh and keeps track of these changes.
Changes()=default
Default constructor, no changes.
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.
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 normal 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.
bool update(QgsMesh *nativeMesh, const QgsCoordinateTransform &transform)
Constructs triangular mesh from layer's native mesh and transform to destination CRS.
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.
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:61
#define QgsDebugError(str)
Definition qgslogger.h:57
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.
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