QGIS API Documentation  3.23.0-Master (dd0cd13a00)
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 faces", 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 }
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:97
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:59
double y
Definition: qgspointxy.h:63
Q_GADGET double x
Definition: qgspointxy.h:62
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.