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