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