1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | foam-extend: Open Source CFD
4 \\ / O peration | Version: 3.2
5 \\ / A nd | Web: http://www.foam-extend.org
6 \\/ M anipulation | For copyright notice see file Copyright
7 -------------------------------------------------------------------------------
9 This file is part of foam-extend.
11 foam-extend is free software: you can redistribute it and/or modify it
12 under the terms of the GNU General Public License as published by the
13 Free Software Foundation, either version 3 of the License, or (at your
14 option) any later version.
16 foam-extend is distributed in the hope that it will be useful, but
17 WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with foam-extend. If not, see <http://www.gnu.org/licenses/>.
25 Collapse short edges and combines edges that are in line.
27 - collapse short edges. Length of edges to collapse provided as argument.
28 - merge two edges if they are in line. Maximum angle provided as argument.
29 - remove unused points.
31 Cannot remove cells. Can remove faces and points but does not check
32 for nonsense resulting topology.
34 When collapsing an edge with one point on the boundary it will leave
35 the boundary point intact. When both points inside it chooses random. When
36 both points on boundary random again. Note: it should in fact use features
37 where if one point is on a feature it collapses to that one. Alas we don't
38 have features on a polyMesh.
40 \*---------------------------------------------------------------------------*/
43 #include "objectRegistry.H"
45 #include "edgeCollapser.H"
46 #include "directTopoChange.H"
48 #include "mapPolyMesh.H"
49 #include "mathematicalConstants.H"
50 #include "PackedBoolList.H"
51 #include "SortableList.H"
55 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
57 // Get faceEdges in order of face points, i.e. faceEdges[0] is between
59 labelList getSortedEdges
61 const edgeList& edges,
63 const labelList& edgeLabels
66 labelList faceEdges(edgeLabels.size(), -1);
68 // Find starting pos in f for every edgeLabels
71 label edgeI = edgeLabels[i];
73 const edge& e = edges[edgeI];
75 label fp = findIndex(f, e[0]);
76 label fp1 = f.fcIndex(fp);
80 // EdgeI between fp -> fp1
81 faceEdges[fp] = edgeI;
85 // EdgeI between fp-1 -> fp
86 faceEdges[f.rcIndex(fp)] = edgeI;
94 // Merges edges which are in straight line. I.e. edge split by point.
99 edgeCollapser& collapser
102 const pointField& points = mesh.points();
103 const edgeList& edges = mesh.edges();
104 const labelListList& pointEdges = mesh.pointEdges();
105 const labelList& region = collapser.pointRegion();
106 const labelList& master = collapser.pointRegionMaster();
108 label nCollapsed = 0;
110 forAll(pointEdges, pointI)
112 const labelList& pEdges = pointEdges[pointI];
114 if (pEdges.size() == 2)
116 const edge& leftE = edges[pEdges[0]];
117 const edge& rightE = edges[pEdges[1]];
119 // Get the two vertices on both sides of the point
120 label leftV = leftE.otherVertex(pointI);
121 label rightV = rightE.otherVertex(pointI);
123 // Collapse only if none of the points part of merge network
124 // or all of networks with different masters.
125 label midMaster = -1;
126 if (region[pointI] != -1)
128 midMaster = master[region[pointI]];
131 label leftMaster = -2;
132 if (region[leftV] != -1)
134 leftMaster = master[region[leftV]];
137 label rightMaster = -3;
138 if (region[rightV] != -1)
140 rightMaster = master[region[rightV]];
145 midMaster != leftMaster
146 && midMaster != rightMaster
147 && leftMaster != rightMaster
150 // Check if the two edge are in line
151 vector leftVec = points[pointI] - points[leftV];
152 leftVec /= mag(leftVec) + VSMALL;
154 vector rightVec = points[rightV] - points[pointI];
155 rightVec /= mag(rightVec) + VSMALL;
157 if ((leftVec & rightVec) > maxCos)
159 // Collapse one (left) side of the edge. Make left vertex
161 //if (collapser.unaffectedEdge(pEdges[0]))
163 collapser.collapseEdge(pEdges[0], leftV);
175 // Return master point edge needs to be collapsed to (or -1)
176 label edgeMaster(const PackedBoolList& boundaryPoint, const edge& e)
178 label masterPoint = -1;
180 // Collapse edge to boundary point.
181 if (boundaryPoint.get(e[0]))
183 if (boundaryPoint.get(e[1]))
185 // Both points on boundary. Choose one to collapse to.
186 // Note: should look at feature edges/points!
196 if (boundaryPoint.get(e[1]))
202 // None on boundary. Choose arbitrary.
203 // Note: should look at geometry?
211 label collapseSmallEdges
213 const polyMesh& mesh,
214 const PackedBoolList& boundaryPoint,
216 edgeCollapser& collapser
219 const pointField& points = mesh.points();
220 const edgeList& edges = mesh.edges();
222 // Collapse all edges that are too small. Choose intelligently which
223 // point to collapse edge to.
225 label nCollapsed = 0;
229 const edge& e = edges[edgeI];
231 if (e.mag(points) < minLen)
233 label master = edgeMaster(boundaryPoint, e);
235 if (master != -1) // && collapser.unaffectedEdge(edgeI))
237 collapser.collapseEdge(edgeI, master);
246 // Faces which have edges just larger than collapse length but faces which
247 // are very small. This one tries to collapse them if it can be done with
248 // edge collapse. For faces where a face gets replace by two edges use
250 label collapseHighAspectFaces
252 const polyMesh& mesh,
253 const PackedBoolList& boundaryPoint,
254 const scalar areaFac,
255 const scalar edgeRatio,
256 edgeCollapser& collapser
259 const pointField& points = mesh.points();
260 const edgeList& edges = mesh.edges();
261 const faceList& faces = mesh.faces();
262 const labelListList& faceEdges = mesh.faceEdges();
264 scalarField magArea(mag(mesh.faceAreas()));
266 label maxIndex = findMax(magArea);
268 scalar minArea = areaFac * magArea[maxIndex];
270 Info<< "Max face area:" << magArea[maxIndex] << endl
271 << "Collapse area factor:" << areaFac << endl
272 << "Collapse area:" << minArea << endl;
274 label nCollapsed = 0;
278 if (magArea[faceI] < minArea)
280 const face& f = faces[faceI];
282 // Get the edges in face point order
283 labelList fEdges(getSortedEdges(edges, f, faceEdges[faceI]));
285 SortableList<scalar> lengths(fEdges.size());
288 lengths[i] = edges[fEdges[i]].mag(points);
297 // Compare second largest to smallest
298 if (lengths[2] > edgeRatio*lengths[0])
300 // Collapse smallest only. Triangle should be cleared
302 edgeI = fEdges[lengths.indices()[0]];
305 else if (f.size() == 3)
307 // Compare second largest to smallest
308 if (lengths[1] > edgeRatio*lengths[0])
310 edgeI = fEdges[lengths.indices()[0]];
317 label master = edgeMaster(boundaryPoint, edges[edgeI]);
319 if (master != -1)// && collapser.unaffectedEdge(edgeI))
321 collapser.collapseEdge(edgeI, master);
332 void set(const labelList& elems, const bool val, boolList& status)
336 status[elems[i]] = val;
341 // Tries to simplify polygons to face of minSize (4=quad, 3=triangle)
344 const polyMesh& mesh,
345 const PackedBoolList& boundaryPoint,
348 edgeCollapser& collapser
351 const pointField& points = mesh.points();
352 const edgeList& edges = mesh.edges();
353 const faceList& faces = mesh.faces();
354 const cellList& cells = mesh.cells();
355 const labelListList& faceEdges = mesh.faceEdges();
356 const labelList& faceOwner = mesh.faceOwner();
357 const labelList& faceNeighbour = mesh.faceNeighbour();
358 const labelListList& pointCells = mesh.pointCells();
359 const labelListList& cellEdges = mesh.cellEdges();
361 label nCollapsed = 0;
363 boolList protectedEdge(mesh.nEdges(), false);
367 const face& f = faces[faceI];
372 && cells[faceOwner[faceI]].size() >= 6
374 mesh.isInternalFace(faceI)
375 && cells[faceNeighbour[faceI]].size() >= 6
379 // Get the edges in face point order
380 labelList fEdges(getSortedEdges(edges, f, faceEdges[faceI]));
382 SortableList<scalar> lengths(fEdges.size());
385 lengths[i] = edges[fEdges[i]].mag(points);
390 // Now find a gap in length between consecutive elements greater
395 for (label i = f.size()-1-minSize; i >= 0; --i)
397 if (lengths[i+1] > lenGap*lengths[i])
407 //for (label i = gapPos; i >= 0; --i)
408 label i = 0; // Hack: collapse smallest edge only.
410 label edgeI = fEdges[lengths.indices()[i]];
412 if (!protectedEdge[edgeI])
414 const edge& e = edges[edgeI];
416 label master = edgeMaster(boundaryPoint, e);
420 collapser.collapseEdge(edgeI, master);
422 // Protect all other edges on all cells using edge
425 const labelList& pCells0 = pointCells[e[0]];
429 set(cellEdges[pCells0[i]], true, protectedEdge);
431 const labelList& pCells1 = pointCells[e[1]];
435 set(cellEdges[pCells1[i]], true, protectedEdge);
452 int main(int argc, char *argv[])
454 argList::noParallel();
455 argList::validOptions.insert("overwrite", "");
456 argList::validArgs.append("edge length [m]");
457 argList::validArgs.append("merge angle (degrees)");
459 # include "setRootCase.H"
460 # include "createTime.H"
461 runTime.functionObjects().off();
462 # include "createPolyMesh.H"
463 const word oldInstance = mesh.pointsInstance();
465 scalar minLen(readScalar(IStringStream(args.additionalArgs()[0])()));
466 scalar angle(readScalar(IStringStream(args.additionalArgs()[1])()));
467 bool overwrite = args.optionFound("overwrite");
469 scalar maxCos = Foam::cos(angle*mathematicalConstant::pi/180.0);
471 Info<< "Merging:" << nl
472 << " edges with length less than " << minLen << " meters" << nl
473 << " edges split by a point with edges in line to within " << angle
478 bool meshChanged = false;
482 const faceList& faces = mesh.faces();
484 // Get all points on the boundary
485 PackedBoolList boundaryPoint(mesh.nPoints());
487 label nIntFaces = mesh.nInternalFaces();
488 for (label faceI = nIntFaces; faceI < mesh.nFaces(); faceI++)
490 const face& f = faces[faceI];
494 boundaryPoint.set(f[fp], 1);
498 // Edge collapsing engine
499 edgeCollapser collapser(mesh);
502 // Collapse all edges that are too small.
511 Info<< "Collapsing " << nCollapsed << " small edges" << endl;
514 // Remove midpoints on straight edges.
517 nCollapsed = mergeEdges(mesh, maxCos, collapser);
518 Info<< "Collapsing " << nCollapsed << " in line edges" << endl;
522 // Remove small sliver faces that can be collapsed to single edge
526 collapseHighAspectFaces
530 1E-9, // factor of largest face area
531 5, // factor between smallest and largest edge on
535 Info<< "Collapsing " << nCollapsed
536 << " small high aspect ratio faces" << endl;
539 // Simplify faces to quads wherever possible
540 //if (nCollapsed == 0)
547 // 4, // minimum size of face
548 // 0.2, // gap in edge lengths on face
551 // Info<< "Collapsing " << nCollapsed << " polygonal faces" << endl;
560 directTopoChange meshMod(mesh);
562 // Insert mesh refinement into directTopoChange.
563 collapser.setRefinement(meshMod);
566 Info<< "Morphing ..." << endl;
568 autoPtr<mapPolyMesh> morphMap = meshMod.changeMesh(mesh, false);
570 collapser.updateMesh(morphMap());
572 if (morphMap().hasMotionPoints())
574 mesh.movePoints(morphMap().preMotionPoints());
582 // Write resulting mesh
589 mesh.setInstance(oldInstance);
592 Info<< "Writing collapsed mesh to time " << runTime.timeName() << endl;
597 Info << "End\n" << endl;
603 // ************************************************************************* //