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