QGIS API Documentation 3.43.0-Master (c67cf405802)
qgsmeshadvancedediting.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsmeshadvancedediting.cpp - QgsMeshAdvancedEditing
3
4 ---------------------
5 begin : 9.7.2021
6 copyright : (C) 2021 by Vincent Cloarec
7 email : vcloarec at gmail dot com
8 ***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
17
18#include "qgis.h"
19#include "qgsmesheditor.h"
20#include "poly2tri.h"
21
22#include "qgsmeshlayer.h"
23#include "qgsexpression.h"
25#include "qgsproject.h"
27#include "qgsterrainprovider.h"
28
30
32
33void QgsMeshAdvancedEditing::setInputVertices( const QList<int> verticesIndexes )
34{
35 mInputVertices = verticesIndexes;
36}
37
38void QgsMeshAdvancedEditing::setInputFaces( const QList<int> faceIndexes )
39{
40 mInputFaces = faceIndexes;
41}
42
44{
45 return mMessage;
46}
47
49{
50 mInputVertices.clear();
51 mInputFaces.clear();
52 mMessage.clear();
53 mIsFinished = false;
55}
56
58{
59 return mIsFinished;
60}
61
63{
64 return QString();
65}
66
68
69QgsTopologicalMesh::Changes QgsMeshEditRefineFaces::apply( QgsMeshEditor *meshEditor )
70{
71 QSet<int> facesToRefine = qgis::listToSet( mInputFaces );
72 QHash<int, FaceRefinement> facesRefinement;
73 QHash<int, BorderFace> borderFaces;
74
75 createNewVerticesAndRefinedFaces( meshEditor, facesToRefine, facesRefinement );
76 if ( !createNewBorderFaces( meshEditor, facesToRefine, facesRefinement, borderFaces ) )
78
79 // create new vertices
80 const QgsMesh &nativeMesh = *meshEditor->topologicalMesh().mesh();
81 mAddedFacesFirstIndex = nativeMesh.faceCount();
82
83 mFaceIndexesToRemove = facesRefinement.keys();
84 mFaceIndexesToRemove.append( borderFaces.keys() );
85 mFacesToRemove.resize( mFaceIndexesToRemove.size() );
87 for ( int i = 0; i < mFaceIndexesToRemove.count(); ++i )
88 {
89 int faceIndexToRemove = mFaceIndexesToRemove.at( i );
90 mFacesToRemove[i] = nativeMesh.face( faceIndexToRemove );
91 mFacesNeighborhoodToRemove[i] = meshEditor->topologicalMesh().neighborsOfFace( faceIndexToRemove );
92 }
93
94 meshEditor->topologicalMesh().applyChanges( *this );
95
96 mIsFinished = true;
97
98 return *this;
99}
100
101void QgsMeshEditRefineFaces::createNewVerticesAndRefinedFaces( QgsMeshEditor *meshEditor,
102 QSet<int> &facesToRefine,
103 QHash<int, FaceRefinement> &facesRefinement )
104{
105 const QgsTopologicalMesh &topology = meshEditor->topologicalMesh();
106 const QgsMesh &mesh = *meshEditor->topologicalMesh().mesh();
107
108 int startingVertexIndex = mesh.vertexCount();
109 int startingGlobalFaceIndex = mesh.faceCount();
110
111 auto canBeRefined = [ & ]( int fi )->bool
112 {
113 if ( fi < 0 || fi > mesh.faceCount() )
114 return false;
115 int fs = mesh.face( fi ).size();
116 return fs == 3 || fs == 4;
117
118 };
119
120 for ( const int faceIndex : std::as_const( mInputFaces ) )
121 {
122 FaceRefinement refinement;
123
124 const QgsMeshFace &face = mesh.face( faceIndex );
125 int faceSize = face.size();
126
127 QVector<int> addedVerticesIndex( faceSize, -1 );
128
129 if ( canBeRefined( faceIndex ) )
130 {
131 refinement.newVerticesLocalIndex.reserve( faceSize );
132 refinement.refinedFaceNeighbor.reserve( faceSize );
133 refinement.borderFaceNeighbor.reserve( faceSize );
134 const QVector<int> &neighbors = topology.neighborsOfFace( faceIndex );
135
136 double zValueSum = 0;
137
138 for ( int positionInFace = 0; positionInFace < faceSize; ++positionInFace )
139 {
140 refinement.refinedFaceNeighbor.append( false );
141 refinement.borderFaceNeighbor.append( false );
142
143 int neighborFaceIndex = neighbors.at( positionInFace );
144 bool needCreateVertex = true;
145 if ( neighborFaceIndex != -1 && facesToRefine.contains( neighborFaceIndex ) && canBeRefined( neighborFaceIndex ) )
146 {
147 int neighborFaceSize = mesh.face( neighborFaceIndex ).size();
148 int positionVertexInNeighbor = QgsTopologicalMesh::vertexPositionInFace( mesh, face.at( positionInFace ), neighborFaceIndex );
149 positionVertexInNeighbor = ( positionVertexInNeighbor + neighborFaceSize - 1 ) % neighborFaceSize;
150 refinement.refinedFaceNeighbor[positionInFace] = true;
151 QHash<int, FaceRefinement>::iterator it = facesRefinement.find( neighborFaceIndex );
152 if ( it != facesRefinement.end() )
153 {
154 FaceRefinement &neighborRefinement = it.value();
155 int existingVertexLocalIndex = neighborRefinement.newVerticesLocalIndex.at( positionVertexInNeighbor );
156 refinement.newVerticesLocalIndex.append( existingVertexLocalIndex );
157 needCreateVertex = false;
158 zValueSum += mVerticesToAdd.at( existingVertexLocalIndex ).z();
159 }
160 }
161
162 if ( needCreateVertex )
163 {
164 const QgsMeshVertex &vertex1 = mesh.vertex( face.at( positionInFace ) );
165 const QgsMeshVertex &vertex2 = mesh.vertex( face.at( ( positionInFace + 1 ) % faceSize ) );
166
167 refinement.newVerticesLocalIndex.append( mVerticesToAdd.count() );
168 addedVerticesIndex[positionInFace] = mVerticesToAdd.count();
169
170 mVerticesToAdd.append( QgsMeshVertex( ( vertex1.x() + vertex2.x() ) / 2,
171 ( vertex1.y() + vertex2.y() ) / 2,
172 ( vertex1.z() + vertex2.z() ) / 2 ) );
173
174 zValueSum += mVerticesToAdd.last().z();
175 mVertexToFaceToAdd.append( -1 );
176
177 }
178 }
179
180 int faceStartIndex = startingGlobalFaceIndex + mFacesToAdd.count();
181
182 if ( faceSize == 3 )
183 {
184 for ( int i = 0; i < faceSize; ++i )
185 {
186 QgsMeshFace newFace( {face.at( i ),
187 refinement.newVerticesLocalIndex.at( i ) + startingVertexIndex,
188 refinement.newVerticesLocalIndex.at( ( i + faceSize - 1 ) % faceSize ) + startingVertexIndex} );
189 refinement.newFacesChangesIndex.append( mFacesToAdd.count() );
190 mFacesToAdd.append( newFace );
191 mFacesNeighborhoodToAdd.append( {-1, faceStartIndex + 3, -1} );
192
193 }
194 QgsMeshFace newFace( {refinement.newVerticesLocalIndex.at( 0 ) + startingVertexIndex,
195 refinement.newVerticesLocalIndex.at( 1 ) + startingVertexIndex,
196 refinement.newVerticesLocalIndex.at( ( 2 ) % faceSize ) + startingVertexIndex} );
197 refinement.newFacesChangesIndex.append( mFacesToAdd.count() );
198 mFacesToAdd.append( newFace );
199 mFacesNeighborhoodToAdd.append( {faceStartIndex + 1, faceStartIndex + 2, faceStartIndex} );
200 }
201
202 if ( faceSize == 4 )
203 {
204 int centerVertexIndex = mVerticesToAdd.count() + startingVertexIndex;
205 refinement.newCenterVertexIndex = mVerticesToAdd.count();
206 QgsMeshVertex centerVertex = QgsMeshUtils::centroid( face, mesh.vertices );
207 mVerticesToAdd.append( QgsMeshVertex( centerVertex.x(), centerVertex.y(), zValueSum / 4 ) );
208
209 for ( int i = 0; i < faceSize; ++i )
210 {
211 QgsMeshFace newFace( {face.at( i ),
212 refinement.newVerticesLocalIndex.at( i ) + startingVertexIndex,
213 centerVertexIndex,
214 refinement.newVerticesLocalIndex.at( ( i + faceSize - 1 ) % faceSize ) + startingVertexIndex} );
215 refinement.newFacesChangesIndex.append( mFacesToAdd.count() );
216 mFacesToAdd.append( newFace );
217 mFacesNeighborhoodToAdd.append( {-1, faceStartIndex + ( i + 1 ) % 4, faceStartIndex + ( i + 3 ) % 4, -1} );
218 }
219
220 mVertexToFaceToAdd.append( mFacesToAdd.count() + startingGlobalFaceIndex - 1 );
221 }
222 else
223 refinement.newCenterVertexIndex = -1;
224
225 facesRefinement.insert( faceIndex, refinement );
226
227 //look for vertexToFace
228 for ( int positionInFace = 0; positionInFace < faceSize; ++positionInFace )
229 {
230 if ( addedVerticesIndex.at( positionInFace ) != -1 )
231 {
232 mVertexToFaceToAdd[addedVerticesIndex.at( positionInFace )] =
233 refinement.newFacesChangesIndex.at( positionInFace ) + startingGlobalFaceIndex;
234 }
235
236 int vertexIndex = face.at( positionInFace );
237 if ( topology.firstFaceLinked( vertexIndex ) == faceIndex )
238 mVerticesToFaceChanges.append( {vertexIndex, faceIndex, refinement.newFacesChangesIndex.at( positionInFace ) + startingGlobalFaceIndex} );
239 }
240 }
241 else
242 {
243 //not 3 or 4 vertices, we do not refine this face
244 facesToRefine.remove( faceIndex );
245 }
246 }
247
248 //all new refined faces are in place, we can build their neighborhood with other new refined faces
249 for ( QHash<int, FaceRefinement>::iterator it = facesRefinement.begin(); it != facesRefinement.end(); ++it )
250 {
251 int faceIndex = it.key();
252 FaceRefinement &faceRefinement = it.value();
253 const QgsMeshFace &face = mesh.face( faceIndex );
254 int faceSize = face.size();
255
256 const QVector<int> &neighbors = topology.neighborsOfFace( faceIndex );
257
258 for ( int positionInFace = 0; positionInFace < faceSize; ++positionInFace )
259 {
260 if ( faceRefinement.refinedFaceNeighbor.at( positionInFace ) )
261 {
262 int neighborIndex = neighbors.at( positionInFace );
263 int firstVertexIndex = face.at( positionInFace );
264 int secondVertexIndex = faceRefinement.newVerticesLocalIndex.at( positionInFace ) + startingVertexIndex;
265
266 const FaceRefinement &otherRefinement = facesRefinement.value( neighborIndex );
267 const QgsMeshFace &otherFace = mesh.face( neighborIndex );
268 int otherFaceSize = otherFace.size();
269 int otherPositionInface = ( QgsTopologicalMesh::vertexPositionInFace( firstVertexIndex, otherFace ) + otherFaceSize - 1 ) % otherFaceSize;
270
271 int newFace1ChangesIndex = faceRefinement.newFacesChangesIndex.at( ( positionInFace ) );
272 const QgsMeshFace &newFace1 = mFacesToAdd.at( newFace1ChangesIndex );
273 int positionInNewface1Index = QgsTopologicalMesh::vertexPositionInFace( firstVertexIndex, newFace1 );
274
275 int newFace2ChangesIndex = faceRefinement.newFacesChangesIndex.at( ( positionInFace + 1 ) % faceSize );
276 const QgsMeshFace &newFace2 = mFacesToAdd.at( newFace2ChangesIndex );
277 int positionInNewface2Index = QgsTopologicalMesh::vertexPositionInFace( secondVertexIndex, newFace2 );
278
279 int otherNewFace1ChangesIndex = otherRefinement.newFacesChangesIndex.at( ( otherPositionInface ) % otherFaceSize );
280 int otherNewFace2ChangesIndex = otherRefinement.newFacesChangesIndex.at( ( otherPositionInface + 1 ) % otherFaceSize );
281
282 mFacesNeighborhoodToAdd[newFace1ChangesIndex][positionInNewface1Index] = otherNewFace2ChangesIndex + startingGlobalFaceIndex;
283 mFacesNeighborhoodToAdd[newFace2ChangesIndex][positionInNewface2Index] = otherNewFace1ChangesIndex + startingGlobalFaceIndex;
284 }
285 }
286 }
287}
288
289bool QgsMeshEditRefineFaces::createNewBorderFaces( QgsMeshEditor *meshEditor,
290 const QSet<int> &facesToRefine,
291 QHash<int, FaceRefinement> &facesRefinement,
292 QHash<int, BorderFace> &borderFaces )
293{
294 const QgsTopologicalMesh &topology = meshEditor->topologicalMesh();
295 const QgsMesh &mesh = *meshEditor->topologicalMesh().mesh();
296
297 int startingVertexIndex = mesh.vertexCount();
298 int startingFaceChangesGlobalIndex = mesh.faceCount();
299
300 // first create the border faces
301 for ( int faceIndexToRefine : facesToRefine )
302 {
303 const QgsMeshFace &faceToRefine = mesh.face( faceIndexToRefine );
304 int faceToRefineSize = faceToRefine.size();
305
306 const QVector<int> &neighbors = topology.neighborsOfFace( faceIndexToRefine );
307
308 QHash<int, FaceRefinement>::iterator itFace = facesRefinement.find( faceIndexToRefine );
309
310 if ( itFace == facesRefinement.end() )
311 Q_ASSERT( false ); // That could not happen
312
313 FaceRefinement &refinement = itFace.value();
314
315 for ( int posInFaceToRefine = 0; posInFaceToRefine < faceToRefineSize; ++posInFaceToRefine )
316 {
317 int neighborFaceIndex = neighbors.at( posInFaceToRefine );
318 if ( neighborFaceIndex != -1 && !facesToRefine.contains( neighborFaceIndex ) )
319 {
320 const QgsMeshFace &neighborFace = mesh.face( neighborFaceIndex );
321 int neighborFaceSize = neighborFace.size();
322 int positionInNeighbor = QgsTopologicalMesh::vertexPositionInFace( mesh, faceToRefine.at( posInFaceToRefine ), neighborFaceIndex );
323 positionInNeighbor = ( positionInNeighbor + neighborFaceSize - 1 ) % neighborFaceSize;
324
325 QHash<int, BorderFace>::iterator it = borderFaces.find( neighborFaceIndex );
326 if ( it == borderFaces.end() ) //not present for now--> create a border face
327 {
328 BorderFace borderFace;
329 for ( int i = 0; i < neighborFaceSize; ++i )
330 {
331 borderFace.unchangeFacesNeighbor.append( false );
332 borderFace.borderFacesNeighbor.append( false );
333 if ( i == positionInNeighbor )
334 {
335 borderFace.refinedFacesNeighbor.append( true );
336 borderFace.newVerticesLocalIndex.append( refinement.newVerticesLocalIndex.at( posInFaceToRefine ) );
337 }
338 else
339 {
340 borderFace.refinedFacesNeighbor.append( false );
341 borderFace.newVerticesLocalIndex.append( -1 );
342 }
343 }
344 borderFaces.insert( neighborFaceIndex, borderFace );
345 }
346 else
347 {
348 BorderFace &borderFace = it.value();
349 for ( int i = 0; i < neighborFaceSize; ++i )
350 {
351 if ( i == positionInNeighbor )
352 {
353 borderFace.unchangeFacesNeighbor[i] = false;
354 borderFace.borderFacesNeighbor[i] = false;
355 borderFace.refinedFacesNeighbor[i] = true;
356 borderFace.newVerticesLocalIndex[i] = refinement.newVerticesLocalIndex.at( posInFaceToRefine );
357 }
358 }
359 }
360 }
361 }
362 }
363
364 // now build information about neighbors
365 for ( QHash<int, BorderFace>::iterator it = borderFaces.begin(); it != borderFaces.end(); ++it )
366 {
367 int faceIndex = it.key();
368 BorderFace &borderFace = it.value();
369
370 const QgsMeshFace &face = mesh.face( faceIndex );
371 int faceSize = face.size();
372
373 const QVector<int> &neighbors = topology.neighborsOfFace( faceIndex );
374 for ( int posInFace = 0; posInFace < faceSize; ++posInFace )
375 {
376 int neighborIndex = neighbors.at( posInFace );
377
378 if ( neighborIndex != -1 )
379 {
380 const QgsMeshFace &neighborFace = mesh.face( neighborIndex );
381 int neighborFaceSize = neighborFace.size();
382 int posInNeighbor = QgsTopologicalMesh::vertexPositionInFace( mesh, face.at( posInFace ), neighborIndex );
383 posInNeighbor = ( posInNeighbor - 1 + neighborFaceSize ) % neighborFaceSize;
384
385 QHash<int, FaceRefinement>::iterator itRefinement = facesRefinement.find( neighborIndex );
386 if ( itRefinement != facesRefinement.end() )
387 {
388 FaceRefinement &neighborRefinement = itRefinement.value();
389 neighborRefinement.borderFaceNeighbor[posInNeighbor] = true;
390 borderFace.refinedFacesNeighbor[posInFace] = true;
391 continue;
392 }
393
394 QHash<int, BorderFace>::iterator itNeighborBorder = borderFaces.find( neighborIndex );
395 if ( itNeighborBorder == borderFaces.end() )
396 borderFace.unchangeFacesNeighbor[posInFace] = true;
397 else
398 {
399 BorderFace &neighborBorderFace = itNeighborBorder.value();
400 neighborBorderFace.borderFacesNeighbor[posInNeighbor] = true;
401 borderFace.borderFacesNeighbor[posInFace] = true;
402 continue;
403 }
404 }
405
406 borderFace.unchangeFacesNeighbor[posInFace] = true;
407
408 }
409 }
410
411// create new faces for each border faces
412 for ( QHash<int, BorderFace>::iterator it = borderFaces.begin(); it != borderFaces.end(); ++it )
413 {
414 int faceIndex = it.key();
415 BorderFace &borderFace = it.value();
416
417 const QgsMeshFace &face = mesh.face( faceIndex );
418 int faceSize = face.size();
419
420 QHash<p2t::Point *, int> mapPoly2TriPointToVertex;
421 std::vector<p2t::Point *> points;
422 for ( int i = 0; i < faceSize; ++i )
423 {
424 const QgsMeshVertex &vert = mesh.vertex( face.at( i ) );
425 points.push_back( new p2t::Point( vert.x(), vert.y() ) );
426 mapPoly2TriPointToVertex.insert( points.back(), face.at( i ) );
427 if ( borderFace.refinedFacesNeighbor.at( i ) )
428 {
429 int localVertexIndex = borderFace.newVerticesLocalIndex.at( i );
430 const QgsMeshVertex &newVert = mVerticesToAdd.at( localVertexIndex );
431 points.push_back( new p2t::Point( newVert.x(), newVert.y() ) );
432 mapPoly2TriPointToVertex.insert( points.back(), localVertexIndex + startingVertexIndex );
433 }
434 }
435
436 try
437 {
438 auto cdt = std::make_unique<p2t::CDT>( points );
439 cdt->Triangulate();
440 std::vector<p2t::Triangle *> triangles = cdt->GetTriangles();
441 QVector<QgsMeshFace> faces( triangles.size() );
442 for ( size_t i = 0; i < triangles.size(); ++i )
443 {
444 QgsMeshFace &triangle = faces[i];
445 triangle.resize( 3 );
446 QVector<QgsMeshVertex> vertices( 3 );
447 for ( int j = 0; j < 3; j++ )
448 {
449 int vertInd = mapPoly2TriPointToVertex.value( triangles.at( i )->GetPoint( j ), -1 );
450 if ( vertInd == -1 )
451 throw std::exception();;
452 triangle[j] = vertInd;
453 if ( vertInd >= startingVertexIndex )
454 vertices[j] = mVerticesToAdd.at( vertInd - startingVertexIndex );
455 else
456 vertices[j] = mesh.vertex( vertInd );
457 }
458 QgsMeshUtils::setCounterClockwise( triangle, vertices[0], vertices[1], vertices[2] );
459 }
460
461 int startingFaceIndex = mesh.faceCount() + mFacesToAdd.count();
462
465 QVector<QgsTopologicalMesh::FaceNeighbors> neighborhood = topologicalFaces.facesNeighborhood();
466
467 // reindex internal neighborhood
468 for ( int i = 0; i < neighborhood.count(); ++i )
469 {
470 QgsTopologicalMesh::FaceNeighbors &neighbors = neighborhood[i];
471 for ( int j = 0; j < neighbors.count(); ++j )
472 {
473 if ( neighbors[j] != -1 ) //internal neighborhood
474 neighbors[j] = neighbors[j] + startingFaceIndex;
475 }
476 }
477
478 QVector<int> neighborOfFace = topology.neighborsOfFace( faceIndex );
479
480 // connect neighboring with refined faces, other border face and unchanged faces
481 for ( int positionInFace = 0; positionInFace < faceSize; ++positionInFace )
482 {
483 if ( borderFace.refinedFacesNeighbor.at( positionInFace ) )
484 {
485 //here we have two edges to treat
486 QVector<int> vertexIndexes( 2 );
487 QVector<int> localFaceIndex( 2 );
488 vertexIndexes[0] = face.at( positionInFace );
489 vertexIndexes[1] = borderFace.newVerticesLocalIndex.at( positionInFace ) + startingVertexIndex;
490
491 int refinedFaceIndex = neighborOfFace.at( positionInFace );
492 const FaceRefinement &faceRefinement = facesRefinement.value( refinedFaceIndex );
493 const QgsMeshFace &refinedFace = mesh.face( refinedFaceIndex );
494 int refinedFaceSize = refinedFace.size();
495 int positionInRefinedFace = ( QgsTopologicalMesh::vertexPositionInFace( vertexIndexes[0], refinedFace ) + refinedFaceSize - 1 ) % refinedFaceSize;
496
497 for ( int i = 0; i < 2; ++i )
498 {
499 QgsMeshVertexCirculator circulator( topologicalFaces, vertexIndexes.at( i ) );
500 circulator.goBoundaryClockwise();
501 localFaceIndex[i] = circulator.currentFaceIndex();
502
503 // new refined faces are in place, so we can link neighborhood
504 QgsTopologicalMesh::FaceNeighbors &neighborsNewFace = neighborhood[localFaceIndex.at( i )];
505 const QgsMeshFace newFace = faces.at( localFaceIndex.at( i ) );
506 int positionInNewFace = QgsTopologicalMesh::vertexPositionInFace( vertexIndexes.at( i ), newFace );
507 int newFaceRefinedIndexInChanges = faceRefinement.newFacesChangesIndex.at( ( positionInRefinedFace + ( 1 - i ) ) % refinedFaceSize ) ;
508 neighborsNewFace[positionInNewFace] = newFaceRefinedIndexInChanges + startingFaceChangesGlobalIndex;
509
510 QgsTopologicalMesh::FaceNeighbors &neighborsRefinedFace = mFacesNeighborhoodToAdd[newFaceRefinedIndexInChanges];
511 const QgsMeshFace &newRefinedFace = mFacesToAdd.at( newFaceRefinedIndexInChanges );
512 int newRefinedFaceSize = newRefinedFace.size();
513 int positionInNewRefinedChange = ( QgsTopologicalMesh::vertexPositionInFace( vertexIndexes.at( i ), newRefinedFace ) + newRefinedFaceSize - 1 ) % newRefinedFaceSize;
514 neighborsRefinedFace[positionInNewRefinedChange] = localFaceIndex.at( i ) + startingFaceIndex;
515 }
516
517 borderFace.edgeFace.append( localFaceIndex.at( 0 ) + startingFaceIndex );
518 }
519
520 if ( borderFace.borderFacesNeighbor.at( positionInFace ) )
521 {
522 int vertexIndex = face.at( positionInFace );
523 QgsMeshVertexCirculator circulator( topologicalFaces, vertexIndex );
524 circulator.goBoundaryClockwise();
525 int localFaceIndex = circulator.currentFaceIndex();
526
527 // all new border faces are not in place, so store information for later
528 borderFace.edgeFace.append( localFaceIndex + startingFaceIndex );
529 }
530
531 if ( borderFace.unchangeFacesNeighbor.at( positionInFace ) )
532 {
533 int vertexIndex = face.at( positionInFace );
534 QgsMeshVertexCirculator circulator( topologicalFaces, vertexIndex );
535 circulator.goBoundaryClockwise();
536 int localFaceIndex = circulator.currentFaceIndex();
537
538 const QgsMeshFace &newFace = faces.at( localFaceIndex );
539 int positionInNewface = QgsTopologicalMesh::vertexPositionInFace( vertexIndex, newFace );
540 QgsTopologicalMesh::FaceNeighbors &neighborsNewFace = neighborhood[localFaceIndex];
541 int unchangedFaceIndex = neighborOfFace.at( positionInFace );
542 neighborsNewFace[positionInNewface] = unchangedFaceIndex;
543
544 if ( unchangedFaceIndex != -1 )
545 {
546 const QgsMeshFace &unchangedFace = mesh.face( unchangedFaceIndex );
547 int unchangedFaceSize = unchangedFace.size();
548 int positionInUnchangedFace = ( QgsTopologicalMesh::vertexPositionInFace( vertexIndex, unchangedFace ) + unchangedFaceSize - 1 ) % unchangedFaceSize;
549 mNeighborhoodChanges.append( {unchangedFaceIndex, positionInUnchangedFace, faceIndex, localFaceIndex + startingFaceIndex} );
550 }
551
552 borderFace.edgeFace.append( localFaceIndex + startingFaceIndex );
553 }
554 }
555
556 mFacesToAdd.append( faces );
557 mFacesNeighborhoodToAdd.append( neighborhood );
558
559 for ( p2t::Point *pt : points )
560 delete pt;
561
562 }
563 catch ( ... )
564 {
565 return false;
566 }
567 }
568
569 //all border faces are in place, now it is possible to finalize with completing their nieghborhood with other border faces
570 for ( QHash<int, BorderFace>::iterator it = borderFaces.begin(); it != borderFaces.end(); ++it )
571 {
572 int faceIndex = it.key();
573 BorderFace &borderFace = it.value();
574 const QgsMeshFace &face = mesh.face( faceIndex );
575 int faceSize = face.size();
576
577 const QVector<int> neighbors = topology.neighborsOfFace( faceIndex );
578
579 for ( int positionInFace = 0; positionInFace < faceSize; ++positionInFace )
580 {
581 if ( borderFace.borderFacesNeighbor.at( positionInFace ) )
582 {
583 int otherIndex = neighbors.at( positionInFace );
584 const QgsMeshFace &otherFace = mesh.face( otherIndex );
585 int otherFaceSize = otherFace.size();
586 int otherPositionInface = ( QgsTopologicalMesh::vertexPositionInFace( face.at( positionInFace ), otherFace ) + otherFaceSize - 1 ) % otherFaceSize;
587 const BorderFace &otherBorderFace = borderFaces.value( otherIndex );
588 int otherNewFaceIndex = otherBorderFace.edgeFace.at( otherPositionInface );
589
590 int newFaceChangesIndex = borderFace.edgeFace.at( positionInFace ) - startingFaceChangesGlobalIndex;
591 const QgsMeshFace &newFace = mFacesToAdd.at( newFaceChangesIndex );
592 int newFacePositionInFace = QgsTopologicalMesh::vertexPositionInFace( face.at( positionInFace ), newFace );
593
594 mFacesNeighborhoodToAdd[newFaceChangesIndex][newFacePositionInFace] = otherNewFaceIndex;
595 }
596
597 // ... and look for vertexToFace
598 for ( int positionInFace = 0; positionInFace < faceSize; ++positionInFace )
599 {
600 int vertexIndex = face.at( positionInFace );
601 if ( topology.firstFaceLinked( vertexIndex ) == faceIndex )
602 mVerticesToFaceChanges.append( {vertexIndex, faceIndex, borderFace.edgeFace.at( positionInFace ) } );
603 }
604 }
605 }
606
607 return true;
608}
609
611{
612 return QObject::tr( "Refine %n face(s)", nullptr, mInputFaces.count() );
613}
614
616{
617 if ( !layer || !layer->meshEditor() || !layer->nativeMesh() )
618 return false;
619
620 if ( mInputVertices.isEmpty() )
621 return false;
622
623 const QgsMesh mesh = *layer->nativeMesh();
624 QSet<int> concernedFaces;
625 mChangingVertexMap = QHash<int, int>();
626
627 std::unique_ptr<QgsExpressionContextScope> expScope( QgsExpressionContextUtils::meshExpressionScope( QgsMesh::Vertex ) );
628 QgsExpressionContext context;
629 context.appendScope( expScope.release() );
630 context.lastScope()->setVariable( QStringLiteral( "_native_mesh" ), QVariant::fromValue( mesh ) );
631
632 QVector<QgsMeshVertex> newVertices;
633 newVertices.reserve( mInputVertices.count() );
634
635 int inputCount = mInputVertices.count();
637
638 bool calcX = !mExpressionX.isEmpty();
639 bool calcY = !mExpressionY.isEmpty();
640 bool calcZ = !mExpressionZ.isEmpty();
641 QgsExpression expressionX;
642 if ( calcX )
643 {
644 expressionX = QgsExpression( mExpressionX );
645 expressionX.prepare( &context );
646 }
647
648 QgsExpression expressionY;
649 if ( calcY )
650 {
651 expressionY = QgsExpression( mExpressionY );
652 expressionY.prepare( &context );
653 }
654
655 if ( calcX || calcY )
656 {
657 mNewXYValues.reserve( inputCount );
658 mOldXYValues.reserve( inputCount );
659 }
660
661 QgsExpression expressionZ;
662 if ( calcZ || mZFromTerrain )
663 {
664 expressionZ = QgsExpression( mExpressionZ );
665 expressionZ.prepare( &context );
666 mNewZValues.reserve( inputCount );
667 mOldZValues.reserve( inputCount );
668 }
669
670 QgsCoordinateTransform transformation;
671 const QgsAbstractTerrainProvider *terrainProvider = nullptr;
672
673 if ( mZFromTerrain && project )
674 {
675 terrainProvider = project->elevationProperties()->terrainProvider();
676 if ( terrainProvider )
677 {
678 transformation = QgsCoordinateTransform( layer->crs(), terrainProvider->crs(), project );
679 }
680 }
681
682
683 for ( int i = 0; i < mInputVertices.count(); ++i )
684 {
685 const int vertexIndex = mInputVertices.at( i );
686 context.lastScope()->setVariable( QStringLiteral( "_mesh_vertex_index" ), vertexIndex, false );
687
688 mChangingVertexMap[vertexIndex] = i;
689 const QVariant xvar = expressionX.evaluate( &context );
690 const QVariant yvar = expressionY.evaluate( &context );
691 const QVariant zvar = expressionZ.evaluate( &context );
692
693 const QgsMeshVertex &vert = mesh.vertex( vertexIndex );
694
695 if ( calcX || calcY )
696 {
697 mOldXYValues.append( QgsPointXY( vert ) );
698 mNewXYValues.append( QgsPointXY( vert ) );
699
700 const QList<int> facesAround = layer->meshEditor()->topologicalMesh().facesAroundVertex( vertexIndex );
701 concernedFaces.unite( qgis::listToSet( facesAround ) );
702 }
703
704 bool ok = false;
705 if ( calcX )
706 {
707 if ( xvar.isValid() )
708 {
709 double x = xvar.toDouble( &ok );
710 if ( ok )
711 {
712 mNewXYValues.last().setX( x );
713 }
714 else
715 return false;
716 }
717 else
718 {
719 return false;
720 }
721 }
722
723 if ( calcY )
724 {
725 if ( yvar.isValid() )
726 {
727 double y = yvar.toDouble( &ok );
728 if ( ok )
729 {
730 mNewXYValues.last().setY( y );
731 }
732 else
733 return false;
734 }
735 else
736 return false;
737 }
738
739 if ( calcZ && !mZFromTerrain )
740 {
741 double z = std::numeric_limits<double>::quiet_NaN();
742 if ( zvar.isValid() )
743 {
744 z = zvar.toDouble( &ok );
745 if ( !ok )
746 z = std::numeric_limits<double>::quiet_NaN();
747 }
748
749 mNewZValues.append( z );
750 mOldZValues.append( vert.z() );
751 }
752
753 if ( mZFromTerrain && terrainProvider )
754 {
755 QgsPointXY point;
756 bool vertexTransformed;
757 double elevation;
758
759 try
760 {
761 if ( calcX || calcY )
762 {
763 point = transformation.transform( mNewXYValues.last().x(), mNewXYValues.last().y() );
764 }
765 else
766 {
767 point = transformation.transform( vert.x(), vert.y() );
768 }
769 vertexTransformed = true;
770 }
771 catch ( const QgsCsException & )
772 {
773 vertexTransformed = false;
774 }
775
776 // default elevation is the previous Z
777 elevation = vert.z();
778
779 if ( vertexTransformed )
780 {
781 double terrainElevation = terrainProvider->heightAt( point.x(), point.y() );
782 // if elevation at terrain provider is NaN, use the original vertex Z value
783 if ( ! std::isnan( terrainElevation ) )
784 {
785 elevation = terrainElevation;
786 }
787 }
788
789 mNewZValues.append( elevation );
790 mOldZValues.append( vert.z() );
791 }
792 }
793
794 auto transformFunction = [this, layer ]( int vi )-> const QgsMeshVertex
795 {
796 return transformedVertex( layer, vi );
797 };
798
799 mNativeFacesIndexesGeometryChanged = qgis::setToList( concernedFaces );
800 return ( !calcX && !calcY ) || layer->meshEditor()->canBeTransformed( mNativeFacesIndexesGeometryChanged, transformFunction );
801}
802
804{
805 return QObject::tr( "Transform %n vertices by expression", nullptr, mInputVertices.count() );
806}
807
808void QgsMeshTransformVerticesByExpression::setExpressions( const QString &expressionX, const QString &expressionY, const QString &expressionZ )
809{
810 mExpressionX = expressionX;
811 mExpressionY = expressionY;
812 mExpressionZ = expressionZ;
813
814 mChangingVertexMap.clear();
815}
816
817QgsTopologicalMesh::Changes QgsMeshTransformVerticesByExpression::apply( QgsMeshEditor *meshEditor )
818{
819 meshEditor->topologicalMesh().applyChanges( *this );
820 mIsFinished = true;
821 return *this;
822}
823
825{
826 int pos = mChangingVertexMap.value( vertexIndex, -1 );
827 if ( pos > -1 )
828 {
829 QgsPointXY pointXY;
830 double z;
831
832 if ( mNewXYValues.isEmpty() )
833 pointXY = layer->nativeMesh()->vertex( vertexIndex );
834 else
835 pointXY = mNewXYValues.at( pos );
836
837 if ( mNewZValues.isEmpty() )
838 z = layer->nativeMesh()->vertex( vertexIndex ).z();
839 else
840 z = mNewZValues.at( pos );
841
842 return QgsMeshVertex( pointXY.x(), pointXY.y(), z );
843 }
844 else
845 return layer->nativeMesh()->vertex( vertexIndex );
846}
847
849{
850 mZFromTerrain = enable;
851}
Abstract base class for terrain providers.
virtual double heightAt(double x, double y) const =0
Returns the height at the point (x,y) in the terrain provider's native crs().
virtual QgsCoordinateReferenceSystem crs() const =0
Returns the native coordinate reference system of the terrain provider.
Handles coordinate transforms between two coordinate systems.
QgsPointXY transform(const QgsPointXY &point, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const
Transform the point from the source CRS to the destination CRS.
Custom exception class for Coordinate Reference System related exceptions.
void setVariable(const QString &name, const QVariant &value, bool isStatic=false)
Convenience method for setting a variable in the context scope by name name and value.
static QgsExpressionContextScope * meshExpressionScope(QgsMesh::ElementType elementType)
Creates a new scope which contains functions relating to mesh layer element elementType.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QgsExpressionContextScope * lastScope()
Returns the last scope added to the context.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
Handles parsing and evaluation of expressions (formerly called "search strings").
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
QVariant evaluate()
Evaluate the feature and return the result.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:84
QString message() const
Returns a message that can be provided by the advanced editing when applying is done.
void setInputVertices(const QList< int > verticesIndexes)
Sets the input vertices indexes that will be used for the editing.
virtual ~QgsMeshAdvancedEditing()
void clear()
Removes all data provided to the editing or created by the editing.
virtual bool isFinished() const
Returns whether the advanced edit is finished, if not, this edit has to be applied again with QgsMesh...
virtual QString text() const
Returns a short text string describing what this advanced edit does. Default implementation return a ...
void setInputFaces(const QList< int > faceIndexes)
Sets the input faces indexes that will be used for the editing.
QString text() const override
Returns a short text string describing what this advanced edit does. Default implementation return a ...
Represents an error which occurred during mesh editing.
Handles edit operations on a mesh layer.
bool canBeTransformed(const QList< int > &facesToCheck, const std::function< const QgsMeshVertex(int)> &transformFunction) const
Returns true if faces with index in transformedFaces can be transformed without obtaining topologic o...
QgsTopologicalMesh & topologicalMesh()
Returns a reference to the topological mesh.
Represents a mesh layer supporting display of data on structured or unstructured meshes.
QgsMeshEditor * meshEditor()
Returns a pointer to the mesh editor own by the mesh layer.
QgsMesh * nativeMesh()
Returns native mesh (nullptr before rendering or calling to updateMesh)
void setExpressions(const QString &expressionX, const QString &expressionY, const QString &expressionZ)
Sets the expressions for the coordinates transformation.
QgsMeshVertex transformedVertex(QgsMeshLayer *layer, int vertexIndex) const
Returns the transformed vertex from its index vertexIndex for the mesh layer.
bool calculate(QgsMeshLayer *layer, QgsProject *project=nullptr)
Calculates the transformed vertices of the mesh layer, returns false if this leads to topological or ...
void setZFromTerrain(bool enable)
Sets if Z values for vertices should be obtained from project terrain, instead of expression.
QString text() const override
Returns a short text string describing what this advanced edit does. Default implementation return a ...
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.
Convenience class that turns around a vertex and provides information about faces and vertices.
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
double y
Definition qgspoint.h:53
QgsAbstractTerrainProvider * terrainProvider()
Returns the project's terrain provider.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:107
const QgsProjectElevationProperties * elevationProperties() const
Returns the project's elevation properties, which contains the project's elevation related settings.
Contains topological differences between two states of a topological mesh, only accessible from the Q...
void clearChanges()
Clears all changes.
QVector< FaceNeighbors > mFacesNeighborhoodToRemove
QList< std::array< int, 4 > > mNeighborhoodChanges
QList< int > mNativeFacesIndexesGeometryChanged
QVector< QgsMeshFace > mFacesToAdd
QVector< FaceNeighbors > mFacesNeighborhoodToAdd
QList< std::array< int, 3 > > mVerticesToFaceChanges
QVector< QgsMeshVertex > mVerticesToAdd
QVector< QgsMeshFace > mFacesToRemove
Contains independent faces and topological information about these faces.
QVector< FaceNeighbors > facesNeighborhood() const
Returns the face neighborhood of the faces, indexing is local.
Wraps a QgsMesh to ensure the consistency of the mesh during editing and helps to access elements fro...
static int vertexPositionInFace(int vertexIndex, const QgsMeshFace &face)
Returns vertex position in face.
void applyChanges(const Changes &changes)
Applies the changes.
int firstFaceLinked(int vertexIndex) const
Returns the index of the first face linked, returns -1 if it is a free vertex or out of range index.
QVector< int > neighborsOfFace(int faceIndex) const
Returns the indexes of neighbor faces of the face with index faceIndex.
QVector< int > FaceNeighbors
QList< int > facesAroundVertex(int vertexIndex) const
Returns the indexes of faces that are around the vertex with index vertexIndex.
QgsMesh * mesh() const
Returns a pointer to the wrapped mesh.
static TopologicalFaces createNewTopologicalFaces(const QVector< QgsMeshFace > &faces, bool uniqueSharedVertexAllowed, QgsMeshEditingError &error)
Creates new topological faces that are not yet included in the mesh.
QVector< int > QgsMeshFace
List of vertex indexes.
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.
int faceCount() const
Returns number of faces.
QgsMeshVertex vertex(int index) const
Returns a vertex at the index.