/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2016 OpenFOAM Foundation ------------------------------------------------------------------------------- License This file is part of OpenFOAM. OpenFOAM is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. OpenFOAM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenFOAM. If not, see . \*---------------------------------------------------------------------------*/ #include "collapseBase.H" #include "triSurfaceTools.H" #include "argList.H" #include "OFstream.H" #include "SubList.H" #include "labelPair.H" #include "meshTools.H" #include "OSspecific.H" using namespace Foam; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // //// Dump collapse region to .obj file //static void writeRegionOBJ //( // const triSurface& surf, // const label regionI, // const labelList& collapseRegion, // const labelList& outsideVerts //) //{ // fileName dir("regions"); // // mkDir(dir); // fileName regionName(dir / "region_" + name(regionI) + ".obj"); // // Pout<< "Dumping region " << regionI << " to file " << regionName << endl; // // boolList include(surf.size(), false); // // forAll(collapseRegion, facei) // { // if (collapseRegion[facei] == regionI) // { // include[facei] = true; // } // } // // triSurface regionSurf(surf.subsetMesh(include)); // // Pout<< "Region " << regionI << " surface:" << nl; // regionSurf.writeStats(Pout); // // regionSurf.write(regionName); // // // // Dump corresponding outside vertices. // fileName pointsName(dir / "regionPoints_" + name(regionI) + ".obj"); // // Pout<< "Dumping region " << regionI << " points to file " << pointsName // << endl; // // OFstream str(pointsName); // // forAll(outsideVerts, i) // { // meshTools::writeOBJ(str, surf.localPoints()[outsideVerts[i]]); // } //} // Split triangle into multiple triangles because edge e being split // into multiple edges. static void splitTri ( const labelledTri& f, const edge& e, const labelList& splitPoints, DynamicList& tris ) { //label oldNTris = tris.size(); label fp = f.find(e[0]); label fp1 = f.fcIndex(fp); label fp2 = f.fcIndex(fp1); if (f[fp1] == e[1]) { // Split triangle along fp to fp1 tris.append(labelledTri(f[fp2], f[fp], splitPoints[0], f.region())); for (label i = 1; i < splitPoints.size(); i++) { tris.append ( labelledTri ( f[fp2], splitPoints[i-1], splitPoints[i], f.region() ) ); } tris.append ( labelledTri ( f[fp2], splitPoints.last(), f[fp1], f.region() ) ); } else if (f[fp2] == e[1]) { // Split triangle along fp2 to fp. (Reverse order of splitPoints) tris.append ( labelledTri ( f[fp1], f[fp2], splitPoints.last(), f.region() ) ); for (label i = splitPoints.size()-1; i > 0; --i) { tris.append ( labelledTri ( f[fp1], splitPoints[i], splitPoints[i-1], f.region() ) ); } tris.append ( labelledTri ( f[fp1], splitPoints[0], f[fp], f.region() ) ); } else { FatalErrorInFunction << "Edge " << e << " not part of triangle " << f << " fp:" << fp << " fp1:" << fp1 << " fp2:" << fp2 << abort(FatalError); } //Pout<< "Split face " << f << " along edge " << e // << " into triangles:" << endl; // //for (label i = oldNTris; i < tris.size(); i++) //{ // Pout<< " " << tris[i] << nl; //} } // Insert scalar into sortedVerts/sortedWeights so the weights are in // incrementing order. static bool insertSorted ( const label vertI, const scalar weight, labelList& sortedVerts, scalarField& sortedWeights ) { if (sortedVerts.found(vertI)) { FatalErrorInFunction << " which is already in list of sorted vertices " << sortedVerts << abort(FatalError); } if (weight <= 0 || weight >= 1) { FatalErrorInFunction << " with illegal weight " << weight << " into list of sorted vertices " << sortedVerts << abort(FatalError); } label insertI = sortedVerts.size(); forAll(sortedVerts, sortedI) { scalar w = sortedWeights[sortedI]; if (mag(w - weight) < SMALL) { WarningInFunction << "Trying to insert weight " << weight << " which is close to" << " existing weight " << w << " in " << sortedWeights << endl; } if (w > weight) { // Start inserting before sortedI. insertI = sortedI; break; } } label sz = sortedWeights.size(); sortedWeights.setSize(sz + 1); sortedVerts.setSize(sz + 1); // Leave everything up to (not including) insertI intact. // Make space by copying from insertI up. for (label i = sz-1; i >= insertI; --i) { sortedWeights[i+1] = sortedWeights[i]; sortedVerts[i+1] = sortedVerts[i]; } sortedWeights[insertI] = weight; sortedVerts[insertI] = vertI; return true; } // Is triangle candidate for collapse? Small height or small quality bool isSliver ( const triSurface& surf, const scalar minLen, const scalar minQuality, const label facei, const label edgeI ) { const pointField& localPoints = surf.localPoints(); // Check // - opposite vertex projects onto base edge // - normal distance is small // - or triangle quality is small label opposite0 = triSurfaceTools::oppositeVertex ( surf, facei, edgeI ); const edge& e = surf.edges()[edgeI]; const labelledTri& f = surf[facei]; pointHit pHit = e.line(localPoints).nearestDist ( localPoints[opposite0] ); if ( pHit.hit() && ( pHit.distance() < minLen || f.tri(surf.points()).quality() < minQuality ) ) { // Remove facei and split all other faces using this // edge. This is done by 'replacing' the edgeI with the // opposite0 vertex //Pout<< "Splitting face " << facei << " since distance " // << pHit.distance() // << " from vertex " << opposite0 // << " to edge " << edgeI // << " points " // << localPoints[e[0]] // << localPoints[e[1]] // << " is too small or triangle quality " // << f.tri(surf.points()).quality() // << " too small." << endl; return true; } return false; } // Mark all faces that are going to be collapsed. // faceToEdge: per face -1 or the base edge of the face. static void markCollapsedFaces ( const triSurface& surf, const scalar minLen, const scalar minQuality, labelList& faceToEdge ) { faceToEdge.setSize(surf.size()); faceToEdge = -1; const labelListList& edgeFaces = surf.edgeFaces(); forAll(edgeFaces, edgeI) { const labelList& eFaces = surf.edgeFaces()[edgeI]; forAll(eFaces, i) { label facei = eFaces[i]; bool isCandidate = isSliver(surf, minLen, minQuality, facei, edgeI); if (isCandidate) { // Mark face as being collapsed if (faceToEdge[facei] != -1) { FatalErrorInFunction << "Cannot collapse face " << facei << " since " << " is marked to be collapsed both to edge " << faceToEdge[facei] << " and " << edgeI << abort(FatalError); } faceToEdge[facei] = edgeI; } } } } // Recurse through collapsed faces marking all of them with regionI (in // collapseRegion) static void markRegion ( const triSurface& surf, const labelList& faceToEdge, const label regionI, const label facei, labelList& collapseRegion ) { if (faceToEdge[facei] == -1 || collapseRegion[facei] != -1) { FatalErrorInFunction << "Problem : crossed into uncollapsed/regionized face" << abort(FatalError); } collapseRegion[facei] = regionI; // Recurse across edges to collapsed neighbours const labelList& fEdges = surf.faceEdges()[facei]; forAll(fEdges, fEdgeI) { label edgeI = fEdges[fEdgeI]; const labelList& eFaces = surf.edgeFaces()[edgeI]; forAll(eFaces, i) { label nbrFacei = eFaces[i]; if (faceToEdge[nbrFacei] != -1) { if (collapseRegion[nbrFacei] == -1) { markRegion ( surf, faceToEdge, regionI, nbrFacei, collapseRegion ); } else if (collapseRegion[nbrFacei] != regionI) { FatalErrorInFunction << "Edge:" << edgeI << " between face " << facei << " with region " << regionI << " and face " << nbrFacei << " with region " << collapseRegion[nbrFacei] << endl; } } } } } // Mark every face with region (in collapseRegion) (or -1). // Return number of regions. static label markRegions ( const triSurface& surf, const labelList& faceToEdge, labelList& collapseRegion ) { label regionI = 0; forAll(faceToEdge, facei) { if (collapseRegion[facei] == -1 && faceToEdge[facei] != -1) { //Pout<< "markRegions : Marking region:" << regionI // << " starting from face " << facei << endl; // Collapsed face. Mark connected region with current region number markRegion(surf, faceToEdge, regionI++, facei, collapseRegion); } } return regionI; } // Type of region. // -1 : edge inbetween uncollapsed faces. // -2 : edge inbetween collapsed faces // >=0 : edge inbetween uncollapsed and collapsed region. Returns region. static label edgeType ( const triSurface& surf, const labelList& collapseRegion, const label edgeI ) { const labelList& eFaces = surf.edgeFaces()[edgeI]; // Detect if edge is inbetween collapseRegion and non-collapse face bool usesUncollapsed = false; label usesRegion = -1; forAll(eFaces, i) { label facei = eFaces[i]; label region = collapseRegion[facei]; if (region == -1) { usesUncollapsed = true; } else if (usesRegion == -1) { usesRegion = region; } else if (usesRegion != region) { FatalErrorInFunction << abort(FatalError); } else { // Equal regions. } } if (usesUncollapsed) { if (usesRegion == -1) { // uncollapsed faces only. return -1; } else { // between uncollapsed and collapsed. return usesRegion; } } else { if (usesRegion == -1) { FatalErrorInFunction << abort(FatalError); return -2; } else { return -2; } } } // Get points on outside edge of region (= outside points) static labelListList getOutsideVerts ( const triSurface& surf, const labelList& collapseRegion, const label nRegions ) { const labelListList& edgeFaces = surf.edgeFaces(); // Per region all the outside vertices. labelListList outsideVerts(nRegions); forAll(edgeFaces, edgeI) { // Detect if edge is inbetween collapseRegion and non-collapse face label regionI = edgeType(surf, collapseRegion, edgeI); if (regionI >= 0) { // Edge borders both uncollapsed face and collapsed face on region // usesRegion. const edge& e = surf.edges()[edgeI]; labelList& regionVerts = outsideVerts[regionI]; // Add both edge points to regionVerts. forAll(e, eI) { label v = e[eI]; if (!regionVerts.found(v)) { label sz = regionVerts.size(); regionVerts.setSize(sz+1); regionVerts[sz] = v; } } } } return outsideVerts; } // n^2 search for furthest removed point pair. static labelPair getSpanPoints ( const triSurface& surf, const labelList& outsideVerts ) { const pointField& localPoints = surf.localPoints(); scalar maxDist = -GREAT; labelPair maxPair; forAll(outsideVerts, i) { label v0 = outsideVerts[i]; for (label j = i+1; j < outsideVerts.size(); j++) { label v1 = outsideVerts[j]; scalar d = mag(localPoints[v0] - localPoints[v1]); if (d > maxDist) { maxDist = d; maxPair[0] = v0; maxPair[1] = v1; } } } return maxPair; } // Project all non-span points onto the span edge. static void projectNonSpanPoints ( const triSurface& surf, const labelList& outsideVerts, const labelPair& spanPair, labelList& sortedVertices, scalarField& sortedWeights ) { const point& p0 = surf.localPoints()[spanPair[0]]; const point& p1 = surf.localPoints()[spanPair[1]]; forAll(outsideVerts, i) { label v = outsideVerts[i]; if (v != spanPair[0] && v != spanPair[1]) { // Is a non-span point. Project onto spanning edge. pointHit pHit = linePointRef(p0, p1).nearestDist ( surf.localPoints()[v] ); if (!pHit.hit()) { FatalErrorInFunction << abort(FatalError); } scalar w = mag(pHit.hitPoint() - p0) / mag(p1 - p0); insertSorted(v, w, sortedVertices, sortedWeights); } } } // Slice part of the orderVertices (and optionally reverse) for this edge. static void getSplitVerts ( const triSurface& surf, const label regionI, const labelPair& spanPoints, const labelList& orderedVerts, const scalarField& orderedWeights, const label edgeI, labelList& splitVerts, scalarField& splitWeights ) { const edge& e = surf.edges()[edgeI]; const label sz = orderedVerts.size(); if (e[0] == spanPoints[0]) { // Edge in same order as spanPoints&orderedVerts. Keep order. if (e[1] == spanPoints[1]) { // Copy all. splitVerts = orderedVerts; splitWeights = orderedWeights; } else { // Copy upto (but not including) e[1] label i1 = orderedVerts.find(e[1]); splitVerts = SubList