Add the OpenFOAM source tree

This commit is contained in:
Henry
2014-12-10 22:40:10 +00:00
parent ee487c860d
commit 446e5777f0
13379 changed files with 3983377 additions and 0 deletions

View File

@ -0,0 +1,46 @@
2008-10-23
Contents:
surfaceAdd
- adds to surface files. (but does not intersect or anything)
surfaceBooleanOp
- Boolean operations (add, or, xor) on closed surfaces. Probably not working.
surfaceCheck
- checks surface for incorrect topology. Checks normals of neighbouring faces.
surfaceCoarsen
- Stan Melax coarsening algorithm
surfaceConvert
- Converts surfaces to/from various formats
surfaceFind
- Finds nearest vertex and face to given point.
surfaceMeshTriangulate
- Triangulate external faces of mesh and write as surface.
surfacePointMerge
- Explicit point merge of surface.
surfaceSetOutside
- Orient normals on (closed) surface.
surfaceSmooth
- Laplacian smoothing on surface vertices
surfaceSubset
- Subsets surface
surfaceToPatch
- Applies region information of surfaces to mesh.
Each external face of mesh gets region number of nearest surface triangle.
-------------------------------------------------------------------------------
surfaceMeshConvert
- Similar to surfaceConvert, but uses surfMesh library

View File

@ -0,0 +1,3 @@
surfaceAdd.C
EXE = $(FOAM_APPBIN)/surfaceAdd

View File

@ -0,0 +1,6 @@
EXE_INC = \
-I$(LIB_SRC)/triSurface/lnInclude
EXE_LIBS = \
-ltriSurface \
-lmeshTools

View File

@ -0,0 +1,293 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2013 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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 <http://www.gnu.org/licenses/>.
Application
surfaceAdd
Description
Add two surfaces. Does geometric merge on points. Does not check for
overlapping/intersecting triangles.
Keeps patches separate by renumbering.
\*---------------------------------------------------------------------------*/
#include "argList.H"
#include "fileName.H"
#include "triSurface.H"
#include "OFstream.H"
#include "IFstream.H"
#include "triFace.H"
#include "triFaceList.H"
using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
argList::addNote
(
"add two surfaces via a geometric merge on points."
);
argList::noParallel();
argList::validArgs.append("surfaceFile");
argList::validArgs.append("surfaceFile");
argList::validArgs.append("output surfaceFile");
argList::addOption
(
"points",
"file",
"provide additional points"
);
argList::addBoolOption
(
"mergeRegions",
"combine regions from both surfaces"
);
argList args(argc, argv);
const fileName inFileName1 = args[1];
const fileName inFileName2 = args[2];
const fileName outFileName = args[3];
const bool addPoint = args.optionFound("points");
const bool mergeRegions = args.optionFound("mergeRegions");
if (addPoint)
{
Info<< "Reading a surface and adding points from a file"
<< "; merging the points and writing the surface to another file"
<< nl << endl;
Info<< "Surface : " << inFileName1<< nl
<< "Points : " << args["points"] << nl
<< "Writing : " << outFileName << nl << endl;
}
else
{
Info<< "Reading two surfaces"
<< "; merging points and writing the surface to another file"
<< nl << endl;
if (mergeRegions)
{
Info<< "Regions from the two files will get merged" << nl
<< "Do not use this option if you want to keep the regions"
<< " separate" << nl << endl;
}
else
{
Info<< "Regions from the two files will not get merged" << nl
<< "Regions from " << inFileName2 << " will get offset so"
<< " as not to overlap with the regions in " << inFileName1
<< nl << endl;
}
Info<< "Surface1 : " << inFileName1<< nl
<< "Surface2 : " << inFileName2<< nl
<< "Writing : " << outFileName << nl << endl;
}
const triSurface surface1(inFileName1);
Info<< "Surface1:" << endl;
surface1.writeStats(Info);
Info<< endl;
const pointField& points1 = surface1.points();
// Final surface
triSurface combinedSurf;
if (addPoint)
{
IFstream pointsFile(args["points"]);
pointField extraPoints(pointsFile);
Info<< "Additional Points:" << extraPoints.size() << endl;
vectorField pointsAll(points1);
label pointI = pointsAll.size();
pointsAll.setSize(pointsAll.size() + extraPoints.size());
forAll(extraPoints, i)
{
pointsAll[pointI++] = extraPoints[i];
}
combinedSurf = triSurface(surface1, surface1.patches(), pointsAll);
}
else
{
const triSurface surface2(inFileName2);
Info<< "Surface2:" << endl;
surface2.writeStats(Info);
Info<< endl;
// Make new storage
List<labelledTri> facesAll(surface1.size() + surface2.size());
const pointField& points2 = surface2.points();
vectorField pointsAll(points1.size() + points2.size());
label pointi = 0;
// Copy points1 into pointsAll
forAll(points1, point1i)
{
pointsAll[pointi++] = points1[point1i];
}
// Add surface2 points
forAll(points2, point2i)
{
pointsAll[pointi++] = points2[point2i];
}
label trianglei = 0;
// Copy triangles1 into trianglesAll
forAll(surface1, faceI)
{
facesAll[trianglei++] = surface1[faceI];
}
label nRegions1 = surface1.patches().size();
if (!mergeRegions)
{
Info<< "Surface " << inFileName1 << " has " << nRegions1
<< " regions"
<< nl
<< "All region numbers in " << inFileName2 << " will be offset"
<< " by this amount" << nl << endl;
}
// Add (renumbered) surface2 triangles
forAll(surface2, faceI)
{
const labelledTri& tri = surface2[faceI];
labelledTri& destTri = facesAll[trianglei++];
destTri[0] = tri[0] + points1.size();
destTri[1] = tri[1] + points1.size();
destTri[2] = tri[2] + points1.size();
if (mergeRegions)
{
destTri.region() = tri.region();
}
else
{
destTri.region() = tri.region() + nRegions1;
}
}
label nRegions2 = surface2.patches().size();
geometricSurfacePatchList newPatches;
if (mergeRegions)
{
// Overwrite
newPatches.setSize(max(nRegions1, nRegions2));
forAll(surface1.patches(), patchI)
{
newPatches[patchI] = surface1.patches()[patchI];
}
forAll(surface2.patches(), patchI)
{
newPatches[patchI] = surface2.patches()[patchI];
}
}
else
{
Info<< "Regions from " << inFileName2 << " have been renumbered:"
<< nl
<< " old\tnew" << nl;
for (label regionI = 0; regionI < nRegions2; regionI++)
{
Info<< " " << regionI << '\t' << regionI+nRegions1
<< nl;
}
Info<< nl;
newPatches.setSize(nRegions1 + nRegions2);
label newPatchI = 0;
forAll(surface1.patches(), patchI)
{
newPatches[newPatchI++] = surface1.patches()[patchI];
}
forAll(surface2.patches(), patchI)
{
newPatches[newPatchI++] = surface2.patches()[patchI];
}
}
Info<< "New patches:" << nl;
forAll(newPatches, patchI)
{
Info<< " " << patchI << '\t' << newPatches[patchI].name() << nl;
}
Info<< endl;
// Construct new surface mesh
combinedSurf = triSurface(facesAll, newPatches, pointsAll);
}
// Merge all common points and do some checks
combinedSurf.cleanup(true);
Info<< "Merged surface:" << endl;
combinedSurf.writeStats(Info);
Info<< endl;
Info<< "Writing : " << outFileName << endl;
// No need to 'group' while writing since all in correct order anyway.
combinedSurf.write(outFileName);
Info<< "End\n" << endl;
return 0;
}
// ************************************************************************* //

View File

@ -0,0 +1,3 @@
surfaceAutoPatch.C
EXE = $(FOAM_APPBIN)/surfaceAutoPatch

View File

@ -0,0 +1,7 @@
EXE_INC = \
-I$(LIB_SRC)/meshTools/lnInclude \
-I$(LIB_SRC)/triSurface/lnInclude
EXE_LIBS = \
-lmeshTools \
-ltriSurface

View File

@ -0,0 +1,125 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2013 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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 <http://www.gnu.org/licenses/>.
Application
surfaceAutoPatch
Description
Patches surface according to feature angle. Like autoPatch.
\*---------------------------------------------------------------------------*/
#include "triangle.H"
#include "triSurface.H"
#include "argList.H"
#include "surfaceFeatures.H"
#include "treeBoundBox.H"
#include "meshTools.H"
#include "OFstream.H"
using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
argList::noParallel();
argList::validArgs.append("input surfaceFile");
argList::validArgs.append("output surfaceFile");
argList::validArgs.append("includedAngle [0..180]");
argList args(argc, argv);
const fileName inFileName = args[1];
const fileName outFileName = args[2];
const scalar includedAngle = args.argRead<scalar>(3);
Info<< "Surface : " << inFileName << nl << endl;
// Read
// ~~~~
Info<< "Reading : " << inFileName << endl;
triSurface surf(inFileName);
Info<< "Read surface:" << endl;
surf.writeStats(Info);
Info<< endl;
// Construct features from surface&featureangle
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Info<< "Constructing feature set from included angle " << includedAngle
<< endl;
surfaceFeatures set(surf, includedAngle);
Info<< nl
<< "Feature set:" << nl
<< " feature points : " << set.featurePoints().size() << nl
<< " feature edges : " << set.featureEdges().size() << nl
<< " of which" << nl
<< " region edges : " << set.nRegionEdges() << nl
<< " external edges : " << set.nExternalEdges() << nl
<< " internal edges : " << set.nInternalEdges() << nl
<< endl;
// Get per-edge status.
boolList borderEdge(surf.nEdges(), false);
forAll(set.featureEdges(), i)
{
borderEdge[set.featureEdges()[i]] = true;
}
labelList faceRegion(surf.size());
label nRegions = surf.markZones(borderEdge, faceRegion);
// Reregion triangles.
forAll(surf, i)
{
surf[i].region() = faceRegion[i];
}
// Create some patches
surf.patches().setSize(nRegions);
forAll(surf.patches(), patchI)
{
surf.patches()[patchI].name() = "patch" + Foam::name(patchI);
surf.patches()[patchI].geometricType() = "empty";
}
Info<< "Writing : " << outFileName << endl;
surf.write(outFileName, true);
Info<< "End\n" << endl;
return 0;
}
// ************************************************************************* //

View File

@ -0,0 +1,3 @@
surfaceBooleanFeatures.C
EXE = $(FOAM_APPBIN)/surfaceBooleanFeatures

View File

@ -0,0 +1,9 @@
EXE_INC = \
-I$(LIB_SRC)/triSurface/lnInclude \
-I$(LIB_SRC)/edgeMesh/lnInclude \
-I$(LIB_SRC)/meshTools/lnInclude
EXE_LIBS = \
-ltriSurface \
-ledgeMesh \
-lmeshTools

View File

@ -0,0 +1,821 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2013 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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 <http://www.gnu.org/licenses/>.
Application
surfaceBooleanFeatures
Description
Generates the extendedFeatureEdgeMesh for the interface between a boolean
operation on two surfaces. Assumes that the orientation of the surfaces is
correct:
+ if the operation is union or intersection, that both surface's normals
(n) have the same orientation with respect to a point, i.e. surfaces and b
are orientated the same with respect to point x:
@verbatim
_______
| |--> n
| ___|___ x
|a | | |--> n
|___|___| b|
| |
|_______|
@endverbatim
+ if the operation is a subtraction, the surfaces should be oppositely
oriented with respect to a point, i.e. for (a - b), then b's orientation
should be such that x is "inside", and a's orientation such that x is
"outside"
@verbatim
_______
| |--> n
| ___|___ x
|a | | |
|___|___| b|
| n <--|
|_______|
@endverbatim
When the operation is peformed - for union, all of the edges generates where
one surfaces cuts another are all "internal" for union, and "external" for
intersection, b - a and a - b. This has been assumed, formal (dis)proof is
invited.
\*---------------------------------------------------------------------------*/
#include "triSurface.H"
#include "argList.H"
#include "Time.H"
#include "featureEdgeMesh.H"
#include "extendedFeatureEdgeMesh.H"
#include "triSurfaceSearch.H"
#include "OFstream.H"
#include "booleanSurface.H"
#include "edgeIntersections.H"
#include "meshTools.H"
#include "labelPair.H"
using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
bool intersectSurfaces
(
triSurface& surf,
edgeIntersections& edgeCuts
)
{
bool hasMoved = false;
for (label iter = 0; iter < 10; iter++)
{
Info<< "Determining intersections of surface edges with itself" << endl;
// Determine surface edge intersections. Allow surface to be moved.
// Number of iterations needed to resolve degenerates
label nIters = 0;
{
triSurfaceSearch querySurf(surf);
scalarField surfPointTol
(
1e-3*edgeIntersections::minEdgeLength(surf)
);
// Determine raw intersections
edgeCuts = edgeIntersections
(
surf,
querySurf,
surfPointTol
);
// Shuffle a bit to resolve degenerate edge-face hits
{
pointField points(surf.points());
nIters =
edgeCuts.removeDegenerates
(
5, // max iterations
surf,
querySurf,
surfPointTol,
points // work array
);
if (nIters != 0)
{
// Update geometric quantities
surf.movePoints(points);
hasMoved = true;
}
}
}
}
if (hasMoved)
{
fileName newFile("surf.obj");
Info<< "Surface has been moved. Writing to " << newFile << endl;
surf.write(newFile);
}
return hasMoved;
}
// Keep on shuffling surface points until no more degenerate intersections.
// Moves both surfaces and updates set of edge cuts.
bool intersectSurfaces
(
triSurface& surf1,
edgeIntersections& edgeCuts1,
triSurface& surf2,
edgeIntersections& edgeCuts2
)
{
bool hasMoved1 = false;
bool hasMoved2 = false;
for (label iter = 0; iter < 10; iter++)
{
Info<< "Determining intersections of surf1 edges with surf2"
<< " faces" << endl;
// Determine surface1 edge intersections. Allow surface to be moved.
// Number of iterations needed to resolve degenerates
label nIters1 = 0;
{
triSurfaceSearch querySurf2(surf2);
scalarField surf1PointTol
(
1e-3*edgeIntersections::minEdgeLength(surf1)
);
// Determine raw intersections
edgeCuts1 = edgeIntersections
(
surf1,
querySurf2,
surf1PointTol
);
// Shuffle a bit to resolve degenerate edge-face hits
{
pointField points1(surf1.points());
nIters1 =
edgeCuts1.removeDegenerates
(
5, // max iterations
surf1,
querySurf2,
surf1PointTol,
points1 // work array
);
if (nIters1 != 0)
{
// Update geometric quantities
surf1.movePoints(points1);
hasMoved1 = true;
}
}
}
Info<< "Determining intersections of surf2 edges with surf1"
<< " faces" << endl;
label nIters2 = 0;
{
triSurfaceSearch querySurf1(surf1);
scalarField surf2PointTol
(
1e-3*edgeIntersections::minEdgeLength(surf2)
);
// Determine raw intersections
edgeCuts2 = edgeIntersections
(
surf2,
querySurf1,
surf2PointTol
);
// Shuffle a bit to resolve degenerate edge-face hits
{
pointField points2(surf2.points());
nIters2 =
edgeCuts2.removeDegenerates
(
5, // max iterations
surf2,
querySurf1,
surf2PointTol,
points2 // work array
);
if (nIters2 != 0)
{
// Update geometric quantities
surf2.movePoints(points2);
hasMoved2 = true;
}
}
}
if (nIters1 == 0 && nIters2 == 0)
{
Info<< "** Resolved all intersections to be proper edge-face pierce"
<< endl;
break;
}
}
if (hasMoved1)
{
fileName newFile("surf1.obj");
Info<< "Surface 1 has been moved. Writing to " << newFile
<< endl;
surf1.write(newFile);
}
if (hasMoved2)
{
fileName newFile("surf2.obj");
Info<< "Surface 2 has been moved. Writing to " << newFile
<< endl;
surf2.write(newFile);
}
return hasMoved1 || hasMoved2;
}
label calcNormalDirection
(
const vector& normal,
const vector& otherNormal,
const vector& edgeDir,
const vector& faceCentre,
const vector& pointOnEdge
)
{
vector cross = (normal ^ edgeDir);
cross /= mag(cross);
vector fC0tofE0 = faceCentre - pointOnEdge;
fC0tofE0 /= mag(fC0tofE0);
label nDir = ((cross & fC0tofE0) > 0.0 ? 1 : -1);
nDir *= ((otherNormal & fC0tofE0) > 0.0 ? -1 : 1);
return nDir;
}
void calcEdgeCuts
(
triSurface& surf1,
triSurface& surf2,
const bool perturb,
edgeIntersections& edge1Cuts,
edgeIntersections& edge2Cuts
)
{
if (perturb)
{
intersectSurfaces
(
surf1,
edge1Cuts,
surf2,
edge2Cuts
);
}
else
{
triSurfaceSearch querySurf2(surf2);
Info<< "Determining intersections of surf1 edges with surf2 faces"
<< endl;
edge1Cuts = edgeIntersections
(
surf1,
querySurf2,
1e-3*edgeIntersections::minEdgeLength(surf1)
);
triSurfaceSearch querySurf1(surf1);
Info<< "Determining intersections of surf2 edges with surf1 faces"
<< endl;
edge2Cuts = edgeIntersections
(
surf2,
querySurf1,
1e-3*edgeIntersections::minEdgeLength(surf2)
);
}
}
void calcFeaturePoints(const pointField& points, const edgeList& edges)
{
edgeMesh eMesh(points, edges);
const labelListList& pointEdges = eMesh.pointEdges();
// Get total number of feature points
label nFeaturePoints = 0;
forAll(pointEdges, pI)
{
const labelList& pEdges = pointEdges[pI];
if (pEdges.size() == 1)
{
nFeaturePoints++;
}
}
// Calculate addressing from feature point to cut point and cut edge
labelList featurePointToCutPoint(nFeaturePoints);
labelList featurePointToCutEdge(nFeaturePoints);
label nFeatPts = 0;
forAll(pointEdges, pI)
{
const labelList& pEdges = pointEdges[pI];
if (pEdges.size() == 1)
{
featurePointToCutPoint[nFeatPts] = pI;
featurePointToCutEdge[nFeatPts] = pEdges[0];
nFeatPts++;
}
}
label concaveStart = 0;
label mixedStart = 0;
label nonFeatureStart = nFeaturePoints;
labelListList featurePointNormals(nFeaturePoints);
labelListList featurePointEdges(nFeaturePoints);
labelList regionEdges;
}
int main(int argc, char *argv[])
{
argList::noParallel();
argList::validArgs.append("action");
argList::validArgs.append("surface file");
argList::validArgs.append("surface file");
argList::addBoolOption
(
"surf1Baffle",
"Mark surface 1 as a baffle"
);
argList::addBoolOption
(
"surf2Baffle",
"Mark surface 2 as a baffle"
);
argList::addBoolOption
(
"perturb",
"Perturb surface points to escape degenerate intersections"
);
argList::addBoolOption
(
"invertedSpace",
"do the surfaces have inverted space orientation, "
"i.e. a point at infinity is considered inside. "
"This is only sensible for union and intersection."
);
# include "setRootCase.H"
# include "createTime.H"
word action(args.args()[1]);
HashTable<booleanSurface::booleanOpType> validActions;
validActions.insert("intersection", booleanSurface::INTERSECTION);
validActions.insert("union", booleanSurface::UNION);
validActions.insert("difference", booleanSurface::DIFFERENCE);
if (!validActions.found(action))
{
FatalErrorIn(args.executable())
<< "Unsupported action " << action << endl
<< "Supported actions:" << validActions.toc() << abort(FatalError);
}
fileName surf1Name(args.args()[2]);
Info<< "Reading surface " << surf1Name << endl;
triSurface surf1(surf1Name);
Info<< surf1Name << " statistics:" << endl;
surf1.writeStats(Info);
Info<< endl;
fileName surf2Name(args[3]);
Info<< "Reading surface " << surf2Name << endl;
triSurface surf2(surf2Name);
Info<< surf2Name << " statistics:" << endl;
surf2.writeStats(Info);
Info<< endl;
const bool surf1Baffle = args.optionFound("surf1Baffle");
const bool surf2Baffle = args.optionFound("surf2Baffle");
edgeIntersections edge1Cuts;
edgeIntersections edge2Cuts;
bool invertedSpace = args.optionFound("invertedSpace");
if (invertedSpace && validActions[action] == booleanSurface::DIFFERENCE)
{
FatalErrorIn(args.executable())
<< "Inverted space only makes sense for union or intersection."
<< exit(FatalError);
}
// Calculate the points where the edges are cut by the other surface
calcEdgeCuts
(
surf1,
surf2,
args.optionFound("perturb"),
edge1Cuts,
edge2Cuts
);
// Determine intersection edges from the edge cuts
surfaceIntersection inter
(
surf1,
edge1Cuts,
surf2,
edge2Cuts
);
fileName sFeatFileName
(
surf1Name.lessExt().name()
+ "_"
+ surf2Name.lessExt().name()
+ "_"
+ action
);
label nFeatEds = inter.cutEdges().size();
DynamicList<vector> normals(2*nFeatEds);
vectorField edgeDirections(nFeatEds, vector::zero);
DynamicList<extendedFeatureEdgeMesh::sideVolumeType> normalVolumeTypes
(
2*nFeatEds
);
List<DynamicList<label> > edgeNormals(nFeatEds);
List<DynamicList<label> > normalDirections(nFeatEds);
forAllConstIter(labelPairLookup, inter.facePairToEdge(), iter)
{
const label& cutEdgeI = iter();
const labelPair& facePair = iter.key();
const edge& fE = inter.cutEdges()[cutEdgeI];
const vector& norm1 = surf1.faceNormals()[facePair.first()];
const vector& norm2 = surf2.faceNormals()[facePair.second()];
DynamicList<label>& eNormals = edgeNormals[cutEdgeI];
DynamicList<label>& nDirections = normalDirections[cutEdgeI];
edgeDirections[cutEdgeI] = fE.vec(inter.cutPoints());
normals.append(norm1);
eNormals.append(normals.size() - 1);
if (surf1Baffle)
{
normalVolumeTypes.append(extendedFeatureEdgeMesh::BOTH);
nDirections.append(1);
}
else
{
normalVolumeTypes.append(extendedFeatureEdgeMesh::INSIDE);
nDirections.append
(
calcNormalDirection
(
norm1,
norm2,
edgeDirections[cutEdgeI],
surf1[facePair.first()].centre(surf1.points()),
inter.cutPoints()[fE.start()]
)
);
}
normals.append(norm2);
eNormals.append(normals.size() - 1);
if (surf2Baffle)
{
normalVolumeTypes.append(extendedFeatureEdgeMesh::BOTH);
nDirections.append(1);
}
else
{
normalVolumeTypes.append(extendedFeatureEdgeMesh::INSIDE);
nDirections.append
(
calcNormalDirection
(
norm2,
norm1,
edgeDirections[cutEdgeI],
surf2[facePair.second()].centre(surf2.points()),
inter.cutPoints()[fE.start()]
)
);
}
if (surf1Baffle)
{
normals.append(norm2);
if (surf2Baffle)
{
normalVolumeTypes.append(extendedFeatureEdgeMesh::BOTH);
nDirections.append(1);
}
else
{
normalVolumeTypes.append(extendedFeatureEdgeMesh::INSIDE);
nDirections.append
(
calcNormalDirection
(
norm2,
norm1,
edgeDirections[cutEdgeI],
surf2[facePair.second()].centre(surf2.points()),
inter.cutPoints()[fE.start()]
)
);
}
eNormals.append(normals.size() - 1);
}
if (surf2Baffle)
{
normals.append(norm1);
if (surf1Baffle)
{
normalVolumeTypes.append(extendedFeatureEdgeMesh::BOTH);
nDirections.append(1);
}
else
{
normalVolumeTypes.append(extendedFeatureEdgeMesh::INSIDE);
nDirections.append
(
calcNormalDirection
(
norm1,
norm2,
edgeDirections[cutEdgeI],
surf1[facePair.first()].centre(surf1.points()),
inter.cutPoints()[fE.start()]
)
);
}
eNormals.append(normals.size() - 1);
}
}
label internalStart = -1;
label nIntOrExt = 0;
label nFlat = 0;
label nOpen = 0;
label nMultiple = 0;
forAll(edgeNormals, eI)
{
label nEdNorms = edgeNormals[eI].size();
if (nEdNorms == 1)
{
nOpen++;
}
else if (nEdNorms == 2)
{
const vector& n0(normals[edgeNormals[eI][0]]);
const vector& n1(normals[edgeNormals[eI][1]]);
if ((n0 & n1) > extendedFeatureEdgeMesh::cosNormalAngleTol_)
{
nFlat++;
}
else
{
nIntOrExt++;
}
}
else if (nEdNorms > 2)
{
nMultiple++;
}
}
if (validActions[action] == booleanSurface::UNION)
{
if (!invertedSpace)
{
// All edges are internal
internalStart = 0;
}
else
{
// All edges are external
internalStart = nIntOrExt;
}
}
else if (validActions[action] == booleanSurface::INTERSECTION)
{
if (!invertedSpace)
{
// All edges are external
internalStart = nIntOrExt;
}
else
{
// All edges are internal
internalStart = 0;
}
}
else if (validActions[action] == booleanSurface::DIFFERENCE)
{
// All edges are external
internalStart = nIntOrExt;
}
else
{
FatalErrorIn(args.executable())
<< "Unsupported booleanSurface:booleanOpType and space "
<< action << " " << invertedSpace
<< abort(FatalError);
}
// There are no feature points supported by surfaceIntersection
// Flat, open or multiple edges are assumed to be impossible
// Region edges are not explicitly supported by surfaceIntersection
vectorField normalsTmp(normals);
List<extendedFeatureEdgeMesh::sideVolumeType> normalVolumeTypesTmp
(
normalVolumeTypes
);
labelListList edgeNormalsTmp(edgeNormals.size());
forAll(edgeNormalsTmp, i)
{
edgeNormalsTmp[i] = edgeNormals[i];
}
labelListList normalDirectionsTmp(normalDirections.size());
forAll(normalDirectionsTmp, i)
{
normalDirectionsTmp[i] = normalDirections[i];
}
calcFeaturePoints(inter.cutPoints(), inter.cutEdges());
extendedFeatureEdgeMesh feMesh
(
IOobject
(
sFeatFileName + ".extendedFeatureEdgeMesh",
runTime.constant(),
"extendedFeatureEdgeMesh",
runTime,
IOobject::NO_READ,
IOobject::NO_WRITE
),
inter.cutPoints(),
inter.cutEdges(),
0, // concaveStart,
0, // mixedStart,
0, // nonFeatureStart,
internalStart, // internalStart,
nIntOrExt, // flatStart,
nIntOrExt + nFlat, // openStart,
nIntOrExt + nFlat + nOpen, // multipleStart,
normalsTmp,
normalVolumeTypesTmp,
edgeDirections,
normalDirectionsTmp,
edgeNormalsTmp,
labelListList(0), // featurePointNormals,
labelListList(0), // featurePointEdges,
labelList(0) // regionEdges
);
feMesh.write();
feMesh.writeObj(feMesh.path()/sFeatFileName);
{
// Write a featureEdgeMesh for backwards compatibility
featureEdgeMesh bfeMesh
(
IOobject
(
sFeatFileName + ".eMesh", // name
runTime.constant(), // instance
"triSurface",
runTime, // registry
IOobject::NO_READ,
IOobject::NO_WRITE,
false
),
feMesh.points(),
feMesh.edges()
);
Info<< nl << "Writing featureEdgeMesh to "
<< bfeMesh.objectPath() << endl;
bfeMesh.regIOobject::write();
}
Info << "End\n" << endl;
return 0;
}
// ************************************************************************* //

View File

@ -0,0 +1,3 @@
surfaceCheck.C
EXE = $(FOAM_APPBIN)/surfaceCheck

View File

@ -0,0 +1,11 @@
EXE_INC = \
-I$(LIB_SRC)/sampling/lnInclude \
-I$(LIB_SRC)/meshTools/lnInclude \
-I$(LIB_SRC)/surfMesh/lnInclude \
-I$(LIB_SRC)/triSurface/lnInclude
EXE_LIBS = \
-lsampling \
-ltriSurface \
-lsurfMesh \
-lmeshTools

View File

@ -0,0 +1,793 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2014 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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 <http://www.gnu.org/licenses/>.
Application
surfaceCheck
Description
Checks geometric and topological quality of a surface.
\*---------------------------------------------------------------------------*/
#include "triangle.H"
#include "triSurface.H"
#include "triSurfaceSearch.H"
#include "argList.H"
#include "OFstream.H"
#include "OBJstream.H"
#include "SortableList.H"
#include "PatchTools.H"
#include "vtkSurfaceWriter.H"
using namespace Foam;
// Does face use valid vertices?
bool validTri
(
const bool verbose,
const triSurface& surf,
const label faceI
)
{
// Simple check on indices ok.
const labelledTri& f = surf[faceI];
forAll(f, fp)
{
if (f[fp] < 0 || f[fp] >= surf.points().size())
{
WarningIn("validTri(const triSurface&, const label)")
<< "triangle " << faceI << " vertices " << f
<< " uses point indices outside point range 0.."
<< surf.points().size()-1 << endl;
return false;
}
}
if ((f[0] == f[1]) || (f[0] == f[2]) || (f[1] == f[2]))
{
WarningIn("validTri(const triSurface&, const label)")
<< "triangle " << faceI
<< " uses non-unique vertices " << f
<< " coords:" << f.points(surf.points())
<< endl;
return false;
}
// duplicate triangle check
const labelList& fFaces = surf.faceFaces()[faceI];
// Check if faceNeighbours use same points as this face.
// Note: discards normal information - sides of baffle are merged.
forAll(fFaces, i)
{
label nbrFaceI = fFaces[i];
if (nbrFaceI <= faceI)
{
// lower numbered faces already checked
continue;
}
const labelledTri& nbrF = surf[nbrFaceI];
if
(
((f[0] == nbrF[0]) || (f[0] == nbrF[1]) || (f[0] == nbrF[2]))
&& ((f[1] == nbrF[0]) || (f[1] == nbrF[1]) || (f[1] == nbrF[2]))
&& ((f[2] == nbrF[0]) || (f[2] == nbrF[1]) || (f[2] == nbrF[2]))
)
{
WarningIn("validTri(const triSurface&, const label)")
<< "triangle " << faceI << " vertices " << f
<< " has the same vertices as triangle " << nbrFaceI
<< " vertices " << nbrF
<< " coords:" << f.points(surf.points())
<< endl;
return false;
}
}
return true;
}
labelList countBins
(
const scalar min,
const scalar max,
const label nBins,
const scalarField& vals
)
{
scalar dist = nBins/(max - min);
labelList binCount(nBins, 0);
forAll(vals, i)
{
scalar val = vals[i];
label index = -1;
if (Foam::mag(val - min) < SMALL)
{
index = 0;
}
else if (val >= max - SMALL)
{
index = nBins - 1;
}
else
{
index = label((val - min)*dist);
if ((index < 0) || (index >= nBins))
{
WarningIn
(
"countBins(const scalar, const scalar, const label"
", const scalarField&)"
) << "value " << val << " at index " << i
<< " outside range " << min << " .. " << max << endl;
if (index < 0)
{
index = 0;
}
else
{
index = nBins - 1;
}
}
}
binCount[index]++;
}
return binCount;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
argList::noParallel();
argList::validArgs.append("surfaceFile");
argList::addBoolOption
(
"checkSelfIntersection",
"also check for self-intersection"
);
argList::addBoolOption
(
"splitNonManifold",
"split surface along non-manifold edges"
" (default split is fully disconnected)"
);
argList::addBoolOption
(
"verbose",
"verbose operation"
);
argList::addBoolOption
(
"blockMesh",
"write vertices/blocks for blockMeshDict"
);
argList args(argc, argv);
const fileName surfFileName = args[1];
const bool checkSelfIntersect = args.optionFound("checkSelfIntersection");
const bool verbose = args.optionFound("verbose");
const bool splitNonManifold = args.optionFound("splitNonManifold");
Info<< "Reading surface from " << surfFileName << " ..." << nl << endl;
// Read
// ~~~~
triSurface surf(surfFileName);
Info<< "Statistics:" << endl;
surf.writeStats(Info);
Info<< endl;
// write bounding box corners
if (args.optionFound("blockMesh"))
{
pointField cornerPts(boundBox(surf.points(), false).points());
Info<<"// blockMeshDict info" << nl
<<"vertices\n(" << nl;
forAll(cornerPts, ptI)
{
Info << " " << cornerPts[ptI] << nl;
}
// number of divisions needs adjustment later
Info<<");\n" << nl
<<"blocks\n"
<<"(\n"
<<" hex (0 1 2 3 4 5 6 7) (10 10 10) simpleGrading (1 1 1)\n"
<<");\n" << nl;
Info<<"edges\n();" << nl
<<"patches\n();" << endl;
}
// Region sizes
// ~~~~~~~~~~~~
{
labelList regionSize(surf.patches().size(), 0);
forAll(surf, faceI)
{
label region = surf[faceI].region();
if (region < 0 || region >= regionSize.size())
{
WarningIn(args.executable())
<< "Triangle " << faceI << " vertices " << surf[faceI]
<< " has region " << region << " which is outside the range"
<< " of regions 0.." << surf.patches().size()-1
<< endl;
}
else
{
regionSize[region]++;
}
}
Info<< "Region\tSize" << nl
<< "------\t----" << nl;
forAll(surf.patches(), patchI)
{
Info<< surf.patches()[patchI].name() << '\t'
<< regionSize[patchI] << nl;
}
Info<< nl << endl;
}
// Check triangles
// ~~~~~~~~~~~~~~~
{
DynamicList<label> illegalFaces(surf.size()/100 + 1);
forAll(surf, faceI)
{
if (!validTri(verbose, surf, faceI))
{
illegalFaces.append(faceI);
}
}
if (illegalFaces.size())
{
Info<< "Surface has " << illegalFaces.size()
<< " illegal triangles." << endl;
OFstream str("illegalFaces");
Info<< "Dumping conflicting face labels to " << str.name() << endl
<< "Paste this into the input for surfaceSubset" << endl;
str << illegalFaces;
}
else
{
Info<< "Surface has no illegal triangles." << endl;
}
Info<< endl;
}
// Triangle quality
// ~~~~~~~~~~~~~~~~
{
scalarField triQ(surf.size(), 0);
forAll(surf, faceI)
{
const labelledTri& f = surf[faceI];
if (f[0] == f[1] || f[0] == f[2] || f[1] == f[2])
{
//WarningIn(args.executable())
// << "Illegal triangle " << faceI << " vertices " << f
// << " coords " << f.points(surf.points()) << endl;
}
else
{
triQ[faceI] = triPointRef
(
surf.points()[f[0]],
surf.points()[f[1]],
surf.points()[f[2]]
).quality();
}
}
labelList binCount = countBins(0, 1, 20, triQ);
Info<< "Triangle quality (equilateral=1, collapsed=0):"
<< endl;
OSstream& os = Info;
os.width(4);
scalar dist = (1.0 - 0.0)/20.0;
scalar min = 0;
forAll(binCount, binI)
{
Info<< " " << min << " .. " << min+dist << " : "
<< 1.0/surf.size() * binCount[binI]
<< endl;
min += dist;
}
Info<< endl;
label minIndex = findMin(triQ);
label maxIndex = findMax(triQ);
Info<< " min " << triQ[minIndex] << " for triangle " << minIndex
<< nl
<< " max " << triQ[maxIndex] << " for triangle " << maxIndex
<< nl
<< endl;
if (triQ[minIndex] < SMALL)
{
WarningIn(args.executable()) << "Minimum triangle quality is "
<< triQ[minIndex] << ". This might give problems in"
<< " self-intersection testing later on." << endl;
}
// Dump for subsetting
{
DynamicList<label> problemFaces(surf.size()/100+1);
forAll(triQ, faceI)
{
if (triQ[faceI] < 1e-11)
{
problemFaces.append(faceI);
}
}
if (!problemFaces.empty())
{
OFstream str("badFaces");
Info<< "Dumping bad quality faces to " << str.name() << endl
<< "Paste this into the input for surfaceSubset" << nl
<< nl << endl;
str << problemFaces;
}
}
}
// Edges
// ~~~~~
{
const edgeList& edges = surf.edges();
const pointField& localPoints = surf.localPoints();
scalarField edgeMag(edges.size());
forAll(edges, edgeI)
{
edgeMag[edgeI] = edges[edgeI].mag(localPoints);
}
label minEdgeI = findMin(edgeMag);
label maxEdgeI = findMax(edgeMag);
const edge& minE = edges[minEdgeI];
const edge& maxE = edges[maxEdgeI];
Info<< "Edges:" << nl
<< " min " << edgeMag[minEdgeI] << " for edge " << minEdgeI
<< " points " << localPoints[minE[0]] << localPoints[minE[1]]
<< nl
<< " max " << edgeMag[maxEdgeI] << " for edge " << maxEdgeI
<< " points " << localPoints[maxE[0]] << localPoints[maxE[1]]
<< nl
<< endl;
}
// Close points
// ~~~~~~~~~~~~
{
const edgeList& edges = surf.edges();
const pointField& localPoints = surf.localPoints();
const boundBox bb(localPoints);
scalar smallDim = 1e-6 * bb.mag();
Info<< "Checking for points less than 1e-6 of bounding box ("
<< bb.span() << " metre) apart."
<< endl;
// Sort points
SortableList<scalar> sortedMag(mag(localPoints));
label nClose = 0;
for (label i = 1; i < sortedMag.size(); i++)
{
label ptI = sortedMag.indices()[i];
label prevPtI = sortedMag.indices()[i-1];
if (mag(localPoints[ptI] - localPoints[prevPtI]) < smallDim)
{
// Check if neighbours.
const labelList& pEdges = surf.pointEdges()[ptI];
label edgeI = -1;
forAll(pEdges, i)
{
const edge& e = edges[pEdges[i]];
if (e[0] == prevPtI || e[1] == prevPtI)
{
// point1 and point0 are connected through edge.
edgeI = pEdges[i];
break;
}
}
nClose++;
if (edgeI == -1)
{
Info<< " close unconnected points "
<< ptI << ' ' << localPoints[ptI]
<< " and " << prevPtI << ' '
<< localPoints[prevPtI]
<< " distance:"
<< mag(localPoints[ptI] - localPoints[prevPtI])
<< endl;
}
else
{
Info<< " small edge between points "
<< ptI << ' ' << localPoints[ptI]
<< " and " << prevPtI << ' '
<< localPoints[prevPtI]
<< " distance:"
<< mag(localPoints[ptI] - localPoints[prevPtI])
<< endl;
}
}
}
Info<< "Found " << nClose << " nearby points." << nl
<< endl;
}
// Check manifold
// ~~~~~~~~~~~~~~
DynamicList<label> problemFaces(surf.size()/100 + 1);
const labelListList& eFaces = surf.edgeFaces();
label nSingleEdges = 0;
forAll(eFaces, edgeI)
{
const labelList& myFaces = eFaces[edgeI];
if (myFaces.size() == 1)
{
problemFaces.append(myFaces[0]);
nSingleEdges++;
}
}
label nMultEdges = 0;
forAll(eFaces, edgeI)
{
const labelList& myFaces = eFaces[edgeI];
if (myFaces.size() > 2)
{
forAll(myFaces, myFaceI)
{
problemFaces.append(myFaces[myFaceI]);
}
nMultEdges++;
}
}
problemFaces.shrink();
if ((nSingleEdges != 0) || (nMultEdges != 0))
{
Info<< "Surface is not closed since not all edges connected to "
<< "two faces:" << endl
<< " connected to one face : " << nSingleEdges << endl
<< " connected to >2 faces : " << nMultEdges << endl;
Info<< "Conflicting face labels:" << problemFaces.size() << endl;
OFstream str("problemFaces");
Info<< "Dumping conflicting face labels to " << str.name() << endl
<< "Paste this into the input for surfaceSubset" << endl;
str << problemFaces;
}
else
{
Info<< "Surface is closed. All edges connected to two faces." << endl;
}
Info<< endl;
// Check singly connected domain
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{
boolList borderEdge(surf.nEdges(), false);
if (splitNonManifold)
{
const labelListList& eFaces = surf.edgeFaces();
forAll(eFaces, edgeI)
{
if (eFaces[edgeI].size() > 2)
{
borderEdge[edgeI] = true;
}
}
}
labelList faceZone;
label numZones = surf.markZones(borderEdge, faceZone);
Info<< "Number of unconnected parts : " << numZones << endl;
if (numZones > 1)
{
Info<< "Splitting surface into parts ..." << endl << endl;
fileName surfFileNameBase(surfFileName.name());
const word fileType = surfFileNameBase.ext();
// Strip extension
surfFileNameBase = surfFileNameBase.lessExt();
// If extension was .gz strip original extension
if (fileType == "gz")
{
surfFileNameBase = surfFileNameBase.lessExt();
}
{
Info<< "Writing zoning to "
<< fileName
(
"zone_"
+ surfFileNameBase
+ '.'
+ vtkSurfaceWriter::typeName
)
<< "..." << endl << endl;
// Convert data
scalarField scalarFaceZone(faceZone.size());
forAll(faceZone, i)
{
scalarFaceZone[i] = faceZone[i];
}
faceList faces(surf.size());
forAll(surf, i)
{
faces[i] = surf[i].triFaceFace();
}
vtkSurfaceWriter().write
(
surfFileName.path(),
surfFileNameBase,
surf.points(),
faces,
"zone",
scalarFaceZone,
false // face based data
);
}
for (label zone = 0; zone < numZones; zone++)
{
boolList includeMap(surf.size(), false);
forAll(faceZone, faceI)
{
if (faceZone[faceI] == zone)
{
includeMap[faceI] = true;
}
}
labelList pointMap;
labelList faceMap;
triSurface subSurf
(
surf.subsetMesh
(
includeMap,
pointMap,
faceMap
)
);
fileName subName(surfFileNameBase + "_" + name(zone) + ".obj");
Info<< "writing part " << zone << " size " << subSurf.size()
<< " to " << subName << endl;
subSurf.write(subName);
}
}
}
// Check orientation
// ~~~~~~~~~~~~~~~~~
labelHashSet borderEdge(surf.size()/1000);
PatchTools::checkOrientation(surf, false, &borderEdge);
//
// Colour all faces into zones using borderEdge
//
labelList normalZone;
label numNormalZones = PatchTools::markZones(surf, borderEdge, normalZone);
Info<< endl
<< "Number of zones (connected area with consistent normal) : "
<< numNormalZones << endl;
if (numNormalZones > 1)
{
Info<< "More than one normal orientation." << endl;
}
Info<< endl;
// Check self-intersection
// ~~~~~~~~~~~~~~~~~~~~~~~
if (checkSelfIntersect)
{
Info<< "Checking self-intersection." << endl;
triSurfaceSearch querySurf(surf);
const indexedOctree<treeDataTriSurface>& tree = querySurf.tree();
OBJstream intStream("selfInterPoints.obj");
label nInt = 0;
forAll(surf.edges(), edgeI)
{
const edge& e = surf.edges()[edgeI];
pointIndexHit hitInfo
(
tree.findLine
(
surf.points()[surf.meshPoints()[e[0]]],
surf.points()[surf.meshPoints()[e[1]]],
treeDataTriSurface::findSelfIntersectOp
(
tree,
edgeI
)
)
);
if (hitInfo.hit())
{
intStream.write(hitInfo.hitPoint());
nInt++;
}
}
if (nInt == 0)
{
Info<< "Surface is not self-intersecting" << endl;
}
else
{
Info<< "Surface is self-intersecting at " << nInt
<< " locations." << endl;
Info<< "Writing intersection points to " << intStream.name()
<< endl;
}
//surfaceIntersection inter(querySurf);
//
//if (inter.cutEdges().empty() && inter.cutPoints().empty())
//{
// Info<< "Surface is not self-intersecting" << endl;
//}
//else
//{
// Info<< "Surface is self-intersecting" << endl;
// Info<< "Writing edges of intersection to selfInter.obj" << endl;
//
// OFstream intStream("selfInter.obj");
// forAll(inter.cutPoints(), cutPointI)
// {
// const point& pt = inter.cutPoints()[cutPointI];
//
// intStream << "v " << pt.x() << ' ' << pt.y() << ' ' << pt.z()
// << endl;
// }
// forAll(inter.cutEdges(), cutEdgeI)
// {
// const edge& e = inter.cutEdges()[cutEdgeI];
//
// intStream << "l " << e.start()+1 << ' ' << e.end()+1 << endl;
// }
//}
Info<< endl;
}
Info<< "\nEnd\n" << endl;
return 0;
}
// ************************************************************************* //

View File

@ -0,0 +1,6 @@
collapseBase.C
collapseEdge.C
surfaceClean.C
EXE = $(FOAM_APPBIN)/surfaceClean

View File

@ -0,0 +1,7 @@
EXE_INC = \
-I$(LIB_SRC)/triSurface/lnInclude \
-I$(LIB_SRC)/meshTools/lnInclude
EXE_LIBS = \
-lmeshTools \
-ltriSurface

View File

@ -0,0 +1,994 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2012 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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 <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
#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;
// }
// }
//
// labelList pointMap, faceMap;
//
// triSurface regionSurf(surf.subsetMesh(include, pointMap, faceMap));
//
// 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<labelledTri>& tris
)
{
//label oldNTris = tris.size();
label fp = findIndex(f, 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
{
FatalErrorIn("splitTri(..)")
<< "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 (findIndex(sortedVerts, vertI) != -1)
{
FatalErrorIn("insertSorted(..)") << "Trying to insert vertex " << vertI
<< " which is already in list of sorted vertices "
<< sortedVerts << abort(FatalError);
}
if (weight <= 0 || weight >= 1)
{
FatalErrorIn("insertSorted(..)") << "Trying to insert vertex " << vertI
<< " 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)
{
WarningIn("insertSorted(..)")
<< "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;
}
else
{
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)
{
FatalErrorIn("markCollapsedFaces(..)")
<< "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)
{
FatalErrorIn("markRegion(..)")
<< "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)
{
FatalErrorIn("markRegion(..)")
<< "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)
{
FatalErrorIn("edgeRegion") << 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)
{
FatalErrorIn("edgeRegion") << 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 (findIndex(regionVerts, v) == -1)
{
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())
{
FatalErrorIn("projectNonSpanPoints")
<< 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 = findIndex(orderedVerts, e[1]);
splitVerts = SubList<label>(orderedVerts, i1, 0);
splitWeights = SubList<scalar>(orderedWeights, i1, 0);
}
}
else if (e[0] == spanPoints[1])
{
// Reverse.
if (e[1] == spanPoints[0])
{
// Copy all.
splitVerts = orderedVerts;
reverse(splitVerts);
splitWeights = orderedWeights;
reverse(splitWeights);
}
else
{
// Copy downto (but not including) e[1]
label i1 = findIndex(orderedVerts, e[1]);
splitVerts = SubList<label>(orderedVerts, sz-(i1+1), i1+1);
reverse(splitVerts);
splitWeights = SubList<scalar>(orderedWeights, sz-(i1+1), i1+1);
reverse(splitWeights);
}
}
else if (e[1] == spanPoints[0])
{
// Reverse.
// Copy upto (but not including) e[0]
label i0 = findIndex(orderedVerts, e[0]);
splitVerts = SubList<label>(orderedVerts, i0, 0);
reverse(splitVerts);
splitWeights = SubList<scalar>(orderedWeights, i0, 0);
reverse(splitWeights);
}
else if (e[1] == spanPoints[1])
{
// Copy from (but not including) e[0] to end
label i0 = findIndex(orderedVerts, e[0]);
splitVerts = SubList<label>(orderedVerts, sz-(i0+1), i0+1);
splitWeights = SubList<scalar>(orderedWeights, sz-(i0+1), i0+1);
}
else
{
label i0 = findIndex(orderedVerts, e[0]);
label i1 = findIndex(orderedVerts, e[1]);
if (i0 == -1 || i1 == -1)
{
FatalErrorIn("getSplitVerts")
<< "Did not find edge in projected vertices." << nl
<< "region:" << regionI << nl
<< "spanPoints:" << spanPoints
<< " coords:" << surf.localPoints()[spanPoints[0]]
<< surf.localPoints()[spanPoints[1]] << nl
<< "edge:" << edgeI
<< " verts:" << e
<< " coords:" << surf.localPoints()[e[0]]
<< surf.localPoints()[e[1]] << nl
<< "orderedVerts:" << orderedVerts << nl
<< abort(FatalError);
}
if (i0 < i1)
{
splitVerts = SubList<label>(orderedVerts, i1-i0-1, i0+1);
splitWeights = SubList<scalar>(orderedWeights, i1-i0-1, i0+1);
}
else
{
splitVerts = SubList<label>(orderedVerts, i0-i1-1, i1+1);
reverse(splitVerts);
splitWeights = SubList<scalar>(orderedWeights, i0-i1-1, i1+1);
reverse(splitWeights);
}
}
}
label collapseBase
(
triSurface& surf,
const scalar minLen,
const scalar minQuality
)
{
label nTotalSplit = 0;
label iter = 0;
while (true)
{
// Detect faces to collapse
// ~~~~~~~~~~~~~~~~~~~~~~~~
// -1 or edge the face is collapsed onto.
labelList faceToEdge(surf.size(), -1);
// Calculate faceToEdge (face collapses)
markCollapsedFaces(surf, minLen, minQuality, faceToEdge);
// Find regions of connected collapsed faces
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// per face -1 or region
labelList collapseRegion(surf.size(), -1);
label nRegions = markRegions(surf, faceToEdge, collapseRegion);
//Pout<< "Detected " << nRegions << " regions of faces to be collapsed"
// << nl << endl;
// Pick up all vertices on outside of region
labelListList outsideVerts
(
getOutsideVerts(surf, collapseRegion, nRegions)
);
// For all regions determine maximum distance between points
List<labelPair> spanPoints(nRegions);
labelListList orderedVertices(nRegions);
List<scalarField> orderedWeights(nRegions);
forAll(spanPoints, regionI)
{
spanPoints[regionI] = getSpanPoints(surf, outsideVerts[regionI]);
//Pout<< "For region " << regionI << " found extrema at points "
// << surf.localPoints()[spanPoints[regionI][0]]
// << surf.localPoints()[spanPoints[regionI][1]]
// << endl;
// Project all non-span points onto the span edge.
projectNonSpanPoints
(
surf,
outsideVerts[regionI],
spanPoints[regionI],
orderedVertices[regionI],
orderedWeights[regionI]
);
//Pout<< "For region:" << regionI
// << " span:" << spanPoints[regionI]
// << " orderedVerts:" << orderedVertices[regionI]
// << " orderedWeights:" << orderedWeights[regionI]
// << endl;
//writeRegionOBJ
//(
// surf,
// regionI,
// collapseRegion,
// outsideVerts[regionI]
//);
//Pout<< endl;
}
// Actually split the edges
// ~~~~~~~~~~~~~~~~~~~~~~~~
const List<labelledTri>& localFaces = surf.localFaces();
const edgeList& edges = surf.edges();
label nSplit = 0;
// Storage for new triangles.
DynamicList<labelledTri> newTris(surf.size());
// Whether face has been dealt with (either copied/split or deleted)
boolList faceHandled(surf.size(), false);
forAll(edges, edgeI)
{
const edge& e = edges[edgeI];
// Detect if edge is inbetween collapseRegion and non-collapse face
label regionI = edgeType(surf, collapseRegion, edgeI);
if (regionI == -2)
{
// inbetween collapsed faces. nothing needs to be done.
}
else if (regionI == -1)
{
// edge inbetween uncollapsed faces. Handle these later on.
}
else
{
// some faces around edge are collapsed.
// Find additional set of points on edge to be used to split
// the remaining faces.
labelList splitVerts;
scalarField splitWeights;
getSplitVerts
(
surf,
regionI,
spanPoints[regionI],
orderedVertices[regionI],
orderedWeights[regionI],
edgeI,
splitVerts,
splitWeights
);
if (splitVerts.size())
{
// Split edge using splitVerts. All non-collapsed triangles
// using edge will get split.
//{
// const pointField& localPoints = surf.localPoints();
// Pout<< "edge " << edgeI << ' ' << e
// << " points "
// << localPoints[e[0]] << ' ' << localPoints[e[1]]
// << " split into edges with extra points:"
// << endl;
// forAll(splitVerts, i)
// {
// Pout<< " " << splitVerts[i] << " weight "
// << splitWeights[i] << nl;
// }
//}
const labelList& eFaces = surf.edgeFaces()[edgeI];
forAll(eFaces, i)
{
label faceI = eFaces[i];
if (!faceHandled[faceI] && faceToEdge[faceI] == -1)
{
// Split face to use vertices.
splitTri
(
localFaces[faceI],
e,
splitVerts,
newTris
);
faceHandled[faceI] = true;
nSplit++;
}
}
}
}
}
// Copy all unsplit faces
forAll(faceHandled, faceI)
{
if (!faceHandled[faceI] && faceToEdge[faceI] == -1)
{
newTris.append(localFaces[faceI]);
}
}
Info<< "collapseBase : collapsing " << nSplit
<< " triangles by splitting their base edge."
<< endl;
nTotalSplit += nSplit;
if (nSplit == 0)
{
break;
}
// Pack the triangles
newTris.shrink();
//Pout<< "Resetting surface from " << surf.size() << " to "
// << newTris.size() << " triangles" << endl;
surf = triSurface(newTris, surf.patches(), surf.localPoints());
//{
// fileName fName("bla" + name(iter) + ".obj");
// Pout<< "Writing surf to " << fName << endl;
// surf.write(fName);
//}
iter++;
}
// Remove any unused vertices
surf = triSurface(surf.localFaces(), surf.patches(), surf.localPoints());
return nTotalSplit;
}
// ************************************************************************* //

View File

@ -0,0 +1,58 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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 <http://www.gnu.org/licenses/>.
InClass
Foam::collapseBase
Description
Routines collapse sliver triangles by splitting the base edge.
SourceFiles
collapseBase.C
\*---------------------------------------------------------------------------*/
#ifndef collapseBase_H
#define collapseBase_H
#include "triSurface.H"
using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
//- Keep collapsing all triangles whose height is < minLen or quality < minQ.
// Returns number of triangles collapsed.
label collapseBase
(
triSurface& surf,
const scalar minLen,
const scalar minQuality
);
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -0,0 +1,161 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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 <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
#include "collapseEdge.H"
//- Sets point neighbours of face to val
static void markPointNbrs
(
const triSurface& surf,
const label faceI,
const bool val,
boolList& okToCollapse
)
{
const triSurface::FaceType& f = surf.localFaces()[faceI];
forAll(f, fp)
{
const labelList& pFaces = surf.pointFaces()[f[fp]];
forAll(pFaces, i)
{
okToCollapse[pFaces[i]] = false;
}
}
}
static triSurface pack
(
const triSurface& surf,
const pointField& localPoints,
const labelList& pointMap
)
{
List<labelledTri> newTriangles(surf.size());
label newTriangleI = 0;
forAll(surf, faceI)
{
const labelledTri& f = surf.localFaces()[faceI];
label newA = pointMap[f[0]];
label newB = pointMap[f[1]];
label newC = pointMap[f[2]];
if ((newA != newB) && (newA != newC) && (newB != newC))
{
newTriangles[newTriangleI++] =
labelledTri(newA, newB, newC, f.region());
}
}
newTriangles.setSize(newTriangleI);
return triSurface(newTriangles, surf.patches(), localPoints);
}
// Collapses small edge to point, thus removing triangle.
label collapseEdge(triSurface& surf, const scalar minLen)
{
label nTotalCollapsed = 0;
while (true)
{
const pointField& localPoints = surf.localPoints();
const List<labelledTri>& localFaces = surf.localFaces();
// Mapping from old to new points
labelList pointMap(surf.nPoints());
forAll(pointMap, i)
{
pointMap[i] = i;
}
// Storage for new points.
pointField newPoints(localPoints);
// To protect neighbours of collapsed faces.
boolList okToCollapse(surf.size(), true);
label nCollapsed = 0;
forAll(localFaces, faceI)
{
if (okToCollapse[faceI])
{
// Check edge lengths.
const triSurface::FaceType& f = localFaces[faceI];
forAll(f, fp)
{
label v = f[fp];
label v1 = f[f.fcIndex(fp)];
if (mag(localPoints[v1] - localPoints[v]) < minLen)
{
// Collapse f[fp1] onto f[fp].
pointMap[v1] = v;
newPoints[v] = 0.5*(localPoints[v1] + localPoints[v]);
//Pout<< "Collapsing triange " << faceI
// << " to edge mid " << newPoints[v] << endl;
nCollapsed++;
okToCollapse[faceI] = false;
// Protect point neighbours from collapsing.
markPointNbrs(surf, faceI, false, okToCollapse);
break;
}
}
}
}
Info<< "collapseEdge : collapsing " << nCollapsed
<< " triangles to a single edge."
<< endl;
nTotalCollapsed += nCollapsed;
if (nCollapsed == 0)
{
break;
}
// Pack the triangles
surf = pack(surf, newPoints, pointMap);
}
// Remove any unused vertices
surf = triSurface(surf.localFaces(), surf.patches(), surf.localPoints());
return nTotalCollapsed;
}
// ************************************************************************* //

View File

@ -0,0 +1,53 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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 <http://www.gnu.org/licenses/>.
InClass
Foam::collapseEdge
Description
Routines to collapse small edges.
SourceFiles
collapseEdge.C
\*---------------------------------------------------------------------------*/
#ifndef collapseEdge_H
#define collapseEdge_H
#include "triSurface.H"
using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
//- Keep collapsing all edges < minLen.
// Returns number of triangles collapsed.
label collapseEdge(triSurface& surf, const scalar minLen);
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -0,0 +1,101 @@
solid patchEE80EE
facet normal 0 0 -1
outer loop
vertex -22 1.19209e-07 0
vertex 22 1.19209e-07 0
vertex 6.25849e-07 -1 0
endloop
endfacet
endsolid patchEE80EE
solid patchFFFF00
facet normal 0 0 -1
outer loop
vertex -22 1.19209e-07 0
vertex -6.4 1 0
vertex 22 1.19209e-07 0
endloop
endfacet
endsolid patchFFFF00
solid patch458674
facet normal 0 0 1
outer loop
vertex -22 1.19209e-07 0
vertex -8.8 -1.1 0
vertex 6.25849e-07 -1 0
endloop
endfacet
endsolid patch458674
solid patchFF0000
facet normal 0 0 -1
outer loop
vertex -6.4 1 0
vertex 11.2 0.9 0
vertex 22 1.19209e-07 0
endloop
endfacet
endsolid patchFF0000
solid patchFF
facet normal 0 0 -1
outer loop
vertex -22 1.19209e-07 0
vertex -7.2 17.8 0
vertex -6.4 1 0
endloop
endfacet
facet normal 0 0 -1
outer loop
vertex -7.2 17.8 0
vertex 11.2 0.9 0
vertex -6.4 1 0
endloop
endfacet
facet normal 0 0 -1
outer loop
vertex -7.2 17.8 0
vertex 18.7 18.2 0
vertex 11.2 0.9 0
endloop
endfacet
facet normal 0 -0 -1
outer loop
vertex 18.7 18.2 0
vertex 22 1.19209e-07 0
vertex 11.2 0.9 0
endloop
endfacet
facet normal -0 0 1
outer loop
vertex 6.25849e-07 -1 0
vertex 11.6 -21.1 0
vertex 22 1.19209e-07 0
endloop
endfacet
facet normal 0 0 1
outer loop
vertex -8.8 -1.1 0
vertex -8.4 -20.9 0
vertex 11.6 -21.1 0
endloop
endfacet
facet normal -0 0 1
outer loop
vertex -8.8 -1.1 0
vertex 11.6 -21.1 0
vertex 6.25849e-07 -1 0
endloop
endfacet
facet normal 0 0 1
outer loop
vertex -22 1.19209e-07 0
vertex -25 -25.3 0
vertex -8.4 -20.9 0
endloop
endfacet
facet normal 0 0 1
outer loop
vertex -22 1.19209e-07 0
vertex -8.4 -20.9 0
vertex -8.8 -1.1 0
endloop
endfacet
endsolid patchFF

View File

@ -0,0 +1,118 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2013 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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 <http://www.gnu.org/licenses/>.
Application
surfaceClean
Description
- removes baffles
- collapses small edges, removing triangles.
- converts sliver triangles into split edges by projecting point onto
base of triangle.
\*---------------------------------------------------------------------------*/
#include "triSurface.H"
#include "argList.H"
#include "OFstream.H"
#include "collapseBase.H"
#include "collapseEdge.H"
using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
argList::noParallel();
argList::validArgs.append("surfaceFile");
argList::validArgs.append("min length");
argList::validArgs.append("min quality");
argList::validArgs.append("output surfaceFile");
argList::addBoolOption
(
"noClean",
"perform some surface checking/cleanup on the input surface"
);
argList args(argc, argv);
const fileName inFileName = args[1];
const scalar minLen = args.argRead<scalar>(2);
const scalar minQuality = args.argRead<scalar>(3);
const fileName outFileName = args[4];
Info<< "Reading surface " << inFileName << nl
<< "Collapsing all triangles with" << nl
<< " edges or heights < " << minLen << nl
<< " quality < " << minQuality << nl
<< "Writing result to " << outFileName << nl << endl;
Info<< "Reading surface from " << inFileName << " ..." << nl << endl;
triSurface surf(inFileName);
surf.writeStats(Info);
if (!args.optionFound("noClean"))
{
Info<< "Removing duplicate and illegal triangles ..." << nl << endl;
surf.cleanup(true);
}
Info<< "Collapsing triangles to edges ..." << nl << endl;
while (true)
{
label nEdgeCollapse = collapseEdge(surf, minLen);
if (nEdgeCollapse == 0)
{
break;
}
}
while (true)
{
label nSplitEdge = collapseBase(surf, minLen, minQuality);
if (nSplitEdge == 0)
{
break;
}
}
Info<< nl
<< "Resulting surface:" << endl;
surf.writeStats(Info);
Info<< nl
<< "Writing refined surface to " << outFileName << " ..." << endl;
surf.write(outFileName);
Info<< "\nEnd\n" << endl;
return 0;
}
// ************************************************************************* //

View File

@ -0,0 +1,5 @@
bunnylod/progmesh.C
bunnylod/vector.C
surfaceCoarsen.C
EXE = $(FOAM_APPBIN)/surfaceCoarsen

View File

@ -0,0 +1,7 @@
EXE_INC = \
-Ibunnylod \
-I$(LIB_SRC)/triSurface/lnInclude
EXE_LIBS = \
-ltriSurface \
-lmeshTools

View File

@ -0,0 +1,12 @@
Polygon Reduction Demo
By Stan Melax (c) 1998
mailto:melax@cs.ualberta.ca
http://www.cs.ualberta.ca/~melax
The PC executable bunnylod.exe should run
on a standard PC.
Just run it and enjoy.
Mouse dragging spins the rabbit.

View File

@ -0,0 +1,282 @@
/*
* Polygon Reduction Demo by Stan Melax (c) 1998
* Permission to use any of this code wherever you want is granted..
* Although, please do acknowledge authorship if appropriate.
*
* This module initializes the bunny model data and calls
* the polygon reduction routine. At each frame the RenderModel()
* routine is called to draw the model. This module also
* animates the parameters (such as number of vertices to
* use) to show the model at various levels of detail.
*/
#include <windows.h>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <GL/gl.h>
#pragma warning(disable : 4244)
#include "vector.h"
#include "font.h"
#include "progmesh.h"
#include "rabdata.h"
extern float DeltaT; // change in time since last frame
int render_num; // number of vertices to draw with
float lodbase=0.5f; // the fraction of vertices used to morph toward
float morph=1.0f; // where to render between 2 levels of detail
List<Vector> vert; // global list of vertices
List<tridata> tri; // global list of triangles
List<int> collapse_map; // to which neighbor each vertex collapses
int renderpolycount=0; // polygons rendered in the current frame
Vector model_position; // position of bunny
Quaternion model_orientation; // orientation of bunny
// Note that the use of the Map() function and the collapse_map
// list isn't part of the polygon reduction algorithm.
// We just set up this system here in this module
// so that we could retrieve the model at any desired vertex count.
// Therefore if this part of the program confuses you, then
// dont worry about it. It might help to look over the progmesh.cpp
// module first.
// Map()
//
// When the model is rendered using a maximum of mx vertices
// then it is vertices 0 through mx-1 that are used.
// We are able to do this because the vertex list
// gets sorted according to the collapse order.
// The Map() routine takes a vertex number 'a' and the
// maximum number of vertices 'mx' and returns the
// appropriate vertex in the range 0 to mx-1.
// When 'a' is greater than 'mx' the Map() routine
// follows the chain of edge collapses until a vertex
// within the limit is reached.
// An example to make this clear: assume there is
// a triangle with vertices 1, 3 and 12. But when
// rendering the model we limit ourselves to 10 vertices.
// In that case we find out how vertex 12 was removed
// by the polygon reduction algorithm. i.e. which
// edge was collapsed. Lets say that vertex 12 was collapsed
// to vertex number 7. This number would have been stored
// in the collapse_map array (i.e. collapse_map[12]==7).
// Since vertex 7 is in range (less than max of 10) we
// will want to render the triangle 1,3,7.
// Pretend now that we want to limit ourselves to 5 vertices.
// and vertex 7 was collapsed to vertex 3
// (i.e. collapse_map[7]==3). Then triangle 1,3,12 would now be
// triangle 1,3,3. i.e. this polygon was removed by the
// progressive mesh polygon reduction algorithm by the time
// it had gotten down to 5 vertices.
// No need to draw a one dimensional polygon. :-)
int Map(int a,int mx) {
if(mx<=0) return 0;
while(a>=mx) {
a=collapse_map[a];
}
return a;
}
void DrawModelTriangles() {
assert(collapse_map.num);
renderpolycount=0;
int i=0;
for(i=0;i<tri.num;i++) {
int p0= Map(tri[i].v[0],render_num);
int p1= Map(tri[i].v[1],render_num);
int p2= Map(tri[i].v[2],render_num);
// note: serious optimization opportunity here,
// by sorting the triangles the following "continue"
// could have been made into a "break" statement.
if(p0==p1 || p1==p2 || p2==p0) continue;
renderpolycount++;
// if we are not currenly morphing between 2 levels of detail
// (i.e. if morph=1.0) then q0,q1, and q2 are not necessary.
int q0= Map(p0,(int)(render_num*lodbase));
int q1= Map(p1,(int)(render_num*lodbase));
int q2= Map(p2,(int)(render_num*lodbase));
Vector v0,v1,v2;
v0 = vert[p0]*morph + vert[q0]*(1-morph);
v1 = vert[p1]*morph + vert[q1]*(1-morph);
v2 = vert[p2]*morph + vert[q2]*(1-morph);
glBegin(GL_POLYGON);
// the purpose of the demo is to show polygons
// therefore just use 1 face normal (flat shading)
Vector nrml = (v1-v0) * (v2-v1); // cross product
if(0<magnitude(nrml)) {
glNormal3fv(normalize(nrml));
}
glVertex3fv(v0);
glVertex3fv(v1);
glVertex3fv(v2);
glEnd();
}
}
void PermuteVertices(List<int> &permutation) {
// rearrange the vertex list
List<Vector> temp_list;
int i;
assert(permutation.num==vert.num);
for(i=0;i<vert.num;i++) {
temp_list.Add(vert[i]);
}
for(i=0;i<vert.num;i++) {
vert[permutation[i]]=temp_list[i];
}
// update the changes in the entries in the triangle list
for(i=0;i<tri.num;i++) {
for(int j=0;j<3;j++) {
tri[i].v[j] = permutation[tri[i].v[j]];
}
}
}
void GetRabbitData(){
// Copy the geometry from the arrays of data in rabdata.cpp into
// the vert and tri lists which we send to the reduction routine
int i;
for(i=0;i<RABBIT_VERTEX_NUM;i++) {
float *vp=rabbit_vertices[i];
vert.Add(Vector(vp[0],vp[1],vp[2]));
}
for(i=0;i<RABBIT_TRIANGLE_NUM;i++) {
tridata td;
td.v[0]=rabbit_triangles[i][0];
td.v[1]=rabbit_triangles[i][1];
td.v[2]=rabbit_triangles[i][2];
tri.Add(td);
}
render_num=vert.num; // by default lets use all the model to render
}
void InitModel() {
List<int> permutation;
GetRabbitData();
ProgressiveMesh(vert,tri,collapse_map,permutation);
PermuteVertices(permutation);
model_position = Vector(0,0,-3);
Quaternion yaw(Vector(0,1,0),-3.14f/4); // 45 degrees
Quaternion pitch(Vector(1,0,0),3.14f/12); // 15 degrees
model_orientation = pitch*yaw;
}
void StatusDraw() {
// Draw a slider type widget looking thing
// to show portion of vertices being used
float b = (float)render_num/(float)vert.num;
float a = b*(lodbase );
glDisable(GL_LIGHTING);
glMatrixMode( GL_PROJECTION );
glPushMatrix();
glLoadIdentity();
glOrtho(-0.15,15,-0.1,1.1,-0.1,100);
glMatrixMode( GL_MODELVIEW );
glPushMatrix();
glLoadIdentity();
glBegin(GL_POLYGON);
glColor3f(1,0,0);
glVertex2f(0,0);
glVertex2f(1,0);
glVertex2f(1,a);
glVertex2f(0,a);
glEnd();
glBegin(GL_POLYGON);
glColor3f(1,0,0);
glVertex2f(0,a);
glVertex2f(morph,a);
glVertex2f(morph,b);
glVertex2f(0,b);
glEnd();
glBegin(GL_POLYGON);
glColor3f(0,0,1);
glVertex2f(morph,a);
glVertex2f(1,a);
glVertex2f(1,b);
glVertex2f(morph,b);
glEnd();
glBegin(GL_POLYGON);
glColor3f(0,0,1);
glVertex2f(0,b);
glVertex2f(1,b);
glVertex2f(1,1);
glVertex2f(0,1);
glEnd();
glPopMatrix();
glMatrixMode( GL_PROJECTION );
glPopMatrix();
glMatrixMode( GL_MODELVIEW );
}
/*
* The following is just a quick hack to animate
* the object through various polygon reduced versions.
*/
struct keyframethings {
float t; // timestamp
float n; // portion of vertices used to start
float dn; // rate of change in "n"
float m; // morph value
float dm; // rate of change in "m"
} keys[]={
{0 ,1 ,0 ,1, 0},
{2 ,1 ,-1,1, 0},
{10,0 ,1 ,1, 0},
{18,1 ,0 ,1, 0},
{20,1 ,0 ,1,-1},
{24,0.5 ,0 ,1, 0},
{26,0.5 ,0 ,1,-1},
{30,0.25,0 ,1, 0},
{32,0.25,0 ,1,-1},
{36,0.125,0,1, 0},
{38,0.25,0 ,0, 1},
{42,0.5 ,0 ,0, 1},
{46,1 ,0 ,0, 1},
{50,1 ,0 ,1, 0},
};
void AnimateParameters() {
static float time=0; // global time - used for animation
time+=DeltaT;
if(time>=50) time=0; // repeat cycle every so many seconds
int k=0;
while(time>keys[k+1].t) {
k++;
}
float interp = (time-keys[k].t)/(keys[k+1].t-keys[k].t);
render_num = vert.num*(keys[k].n + interp*keys[k].dn);
morph = keys[k].m + interp*keys[k].dm;
morph = (morph>1.0f) ? 1.0f : morph; // clamp value
if(render_num>vert.num) render_num=vert.num;
if(render_num<0 ) render_num=0;
}
void RenderModel() {
AnimateParameters();
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glColor3f(1,1,1);
glPushMatrix();
glTranslatef(model_position.x,model_position.y,model_position.z);
// Rotate by quaternion: model_orientation
Vector axis=model_orientation.axis();
float angle=model_orientation.angle()*180.0f/3.14f;
glRotatef(angle,axis.x,axis.y,axis.z);
DrawModelTriangles();
StatusDraw();
glPopMatrix();
char buf[256];
sprintf(buf,"Polys: %d Vertices: %d ",renderpolycount,render_num);
if(morph<1.0) {
sprintf(buf+strlen(buf),"<-> %d morph: %4.2f ",
(int)(lodbase *render_num),morph);
}
PostString(buf,0,-2,5);
}

View File

@ -0,0 +1,114 @@
# Microsoft Developer Studio Project File - Name="bunnylod" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 5.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Application" 0x0101
CFG=bunnylod - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE
!MESSAGE NMAKE /f "bunnylod.mak".
!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f "bunnylod.mak" CFG="bunnylod - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "bunnylod - Win32 Release" (based on "Win32 (x86) Application")
!MESSAGE "bunnylod - Win32 Debug" (based on "Win32 (x86) Application")
!MESSAGE
# Begin Project
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
MTL=midl.exe
RSC=rc.exe
!IF "$(CFG)" == "bunnylod - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Release"
# PROP Intermediate_Dir "Release"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c
# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o NUL /win32
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o NUL /win32
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib opengl32.lib glu32.lib winmm.lib /nologo /subsystem:windows /machine:I386
!ELSEIF "$(CFG)" == "bunnylod - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "Debug"
# PROP BASE Intermediate_Dir "Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "Debug"
# PROP Intermediate_Dir "Debug"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c
# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR /YX /FD /c
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o NUL /win32
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o NUL /win32
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib opengl32.lib glu32.lib winmm.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
!ENDIF
# Begin Target
# Name "bunnylod - Win32 Release"
# Name "bunnylod - Win32 Debug"
# Begin Source File
SOURCE=.\bunnygut.cpp
# End Source File
# Begin Source File
SOURCE=.\font.cpp
# End Source File
# Begin Source File
SOURCE=.\progmesh.cpp
# End Source File
# Begin Source File
SOURCE=.\rabdata.cpp
# End Source File
# Begin Source File
SOURCE=.\vector.cpp
# End Source File
# Begin Source File
SOURCE=.\winmain.cpp
# End Source File
# End Target
# End Project

View File

@ -0,0 +1,29 @@
Microsoft Developer Studio Workspace File, Format Version 5.00
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
###############################################################################
Project: "bunnylod"=.\bunnylod.dsp - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
}}}
###############################################################################
Global:
Package=<5>
{{{
}}}
Package=<3>
{{{
}}}
###############################################################################

View File

@ -0,0 +1,8 @@
#ifndef FONT_H
#define FONT_H
void PrintString(char *s,int x=0,int y=-1);
void PostString(char *_s,int _x,int _y,float _life=5.0);
void RenderStrings();
#endif

View File

@ -0,0 +1,130 @@
/*
* A generic template list class.
* Fairly typical of the list example you would
* find in any c++ book.
*/
#ifndef GENERIC_LIST_H
#define GENERIC_LIST_H
#include <assert.h>
#include <stdio.h>
template <class Type> class List {
public:
List(int s=0);
~List();
void allocate(int s);
void SetSize(int s);
void Pack();
void Add(Type);
void AddUnique(Type);
int Contains(Type);
void Remove(Type);
void DelIndex(int i);
Type * element;
int num;
int array_size;
Type &operator[](int i){
assert(i>=0 && i<num);
return element[i];}
};
template <class Type>
List<Type>::List(int s){
num=0;
array_size = 0;
element = NULL;
if(s) {
allocate(s);
}
}
template <class Type>
List<Type>::~List(){
delete element;
}
template <class Type>
void List<Type>::allocate(int s){
assert(s>0);
assert(s>=num);
Type *old = element;
array_size =s;
element = new Type[array_size];
assert(element);
for(int i=0;i<num;i++){
element[i]=old[i];
}
if(old) delete old;
}
template <class Type>
void List<Type>::SetSize(int s){
if(s==0) { if(element) delete element;}
else { allocate(s); }
num=s;
}
template <class Type>
void List<Type>::Pack(){
allocate(num);
}
template <class Type>
void List<Type>::Add(Type t){
assert(num<=array_size);
if(num==array_size) {
allocate((array_size)?array_size *2:16);
}
//int i;
//for(i=0;i<num;i++) {
// dissallow duplicates
// assert(element[i] != t);
//}
element[num++] = t;
}
template <class Type>
int List<Type>::Contains(Type t){
int i;
int count=0;
for(i=0;i<num;i++) {
if(element[i] == t) count++;
}
return count;
}
template <class Type>
void List<Type>::AddUnique(Type t){
if(!Contains(t)) Add(t);
}
template <class Type>
void List<Type>::DelIndex(int i){
assert(i<num);
num--;
while(i<num){
element[i] = element[i+1];
i++;
}
}
template <class Type>
void List<Type>::Remove(Type t){
int i;
for(i=0;i<num;i++) {
if(element[i] == t) {
break;
}
}
DelIndex(i);
for(i=0;i<num;i++) {
assert(element[i] != t);
}
}
#endif

View File

@ -0,0 +1,315 @@
/*
* Progressive Mesh type Polygon Reduction Algorithm
* by Stan Melax (c) 1998
* Permission to use any of this code wherever you want is granted..
* Although, please do acknowledge authorship if appropriate.
*
* See the header file progmesh.h for a description of this module
*/
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <assert.h>
//#include <windows.h>
#include "vector.h"
#include "list.h"
#include "progmesh.h"
#define min(x,y) (((x) <= (y)) ? (x) : (y))
#define max(x,y) (((x) >= (y)) ? (x) : (y))
/*
* For the polygon reduction algorithm we use data structures
* that contain a little bit more information than the usual
* indexed face set type of data structure.
* From a vertex we wish to be able to quickly get the
* neighboring faces and vertices.
*/
class Triangle;
class Vertex;
class Triangle {
public:
Vertex * vertex[3]; // the 3 points that make this tri
Vector normal; // unit vector othogonal to this face
Triangle(Vertex *v0,Vertex *v1,Vertex *v2);
~Triangle();
void ComputeNormal();
void ReplaceVertex(Vertex *vold,Vertex *vnew);
int HasVertex(Vertex *v);
};
class Vertex {
public:
Vector position; // location of point in euclidean space
int id; // place of vertex in original list
List<Vertex *> neighbor; // adjacent vertices
List<Triangle *> face; // adjacent triangles
float objdist; // cached cost of collapsing edge
Vertex * collapse; // candidate vertex for collapse
Vertex(Vector v,int _id);
~Vertex();
void RemoveIfNonNeighbor(Vertex *n);
};
List<Vertex *> vertices;
List<Triangle *> triangles;
Triangle::Triangle(Vertex *v0,Vertex *v1,Vertex *v2){
assert(v0!=v1 && v1!=v2 && v2!=v0);
vertex[0]=v0;
vertex[1]=v1;
vertex[2]=v2;
ComputeNormal();
triangles.Add(this);
for(int i=0;i<3;i++) {
vertex[i]->face.Add(this);
for(int j=0;j<3;j++) if(i!=j) {
vertex[i]->neighbor.AddUnique(vertex[j]);
}
}
}
Triangle::~Triangle(){
int i;
triangles.Remove(this);
for(i=0;i<3;i++) {
if(vertex[i]) vertex[i]->face.Remove(this);
}
for(i=0;i<3;i++) {
int i2 = (i+1)%3;
if(!vertex[i] || !vertex[i2]) continue;
vertex[i ]->RemoveIfNonNeighbor(vertex[i2]);
vertex[i2]->RemoveIfNonNeighbor(vertex[i ]);
}
}
int Triangle::HasVertex(Vertex *v) {
return (v==vertex[0] ||v==vertex[1] || v==vertex[2]);
}
void Triangle::ComputeNormal(){
Vector v0=vertex[0]->position;
Vector v1=vertex[1]->position;
Vector v2=vertex[2]->position;
normal = (v1-v0)*(v2-v1);
if(magnitude(normal)==0)return;
normal = normalize(normal);
}
void Triangle::ReplaceVertex(Vertex *vold,Vertex *vnew) {
assert(vold && vnew);
assert(vold==vertex[0] || vold==vertex[1] || vold==vertex[2]);
assert(vnew!=vertex[0] && vnew!=vertex[1] && vnew!=vertex[2]);
if(vold==vertex[0]){
vertex[0]=vnew;
}
else if(vold==vertex[1]){
vertex[1]=vnew;
}
else {
assert(vold==vertex[2]);
vertex[2]=vnew;
}
int i;
vold->face.Remove(this);
assert(!vnew->face.Contains(this));
vnew->face.Add(this);
for(i=0;i<3;i++) {
vold->RemoveIfNonNeighbor(vertex[i]);
vertex[i]->RemoveIfNonNeighbor(vold);
}
for(i=0;i<3;i++) {
assert(vertex[i]->face.Contains(this)==1);
for(int j=0;j<3;j++) if(i!=j) {
vertex[i]->neighbor.AddUnique(vertex[j]);
}
}
ComputeNormal();
}
Vertex::Vertex(Vector v,int _id) {
position =v;
id=_id;
vertices.Add(this);
}
Vertex::~Vertex(){
assert(face.num==0);
while(neighbor.num) {
neighbor[0]->neighbor.Remove(this);
neighbor.Remove(neighbor[0]);
}
vertices.Remove(this);
}
void Vertex::RemoveIfNonNeighbor(Vertex *n) {
// removes n from neighbor list if n isn't a neighbor.
if(!neighbor.Contains(n)) return;
for(int i=0;i<face.num;i++) {
if(face[i]->HasVertex(n)) return;
}
neighbor.Remove(n);
}
float ComputeEdgeCollapseCost(Vertex *u,Vertex *v) {
// if we collapse edge uv by moving u to v then how
// much different will the model change, i.e. how much "error".
// Texture, vertex normal, and border vertex code was removed
// to keep this demo as simple as possible.
// The method of determining cost was designed in order
// to exploit small and coplanar regions for
// effective polygon reduction.
// Is is possible to add some checks here to see if "folds"
// would be generated. i.e. normal of a remaining face gets
// flipped. I never seemed to run into this problem and
// therefore never added code to detect this case.
int i;
float edgelength = magnitude(v->position - u->position);
float curvature=0;
// find the "sides" triangles that are on the edge uv
List<Triangle *> sides;
for(i=0;i<u->face.num;i++) {
if(u->face[i]->HasVertex(v)){
sides.Add(u->face[i]);
}
}
// use the triangle facing most away from the sides
// to determine our curvature term
for(i=0;i<u->face.num;i++) {
float mincurv=1; // curve for face i and closer side to it
for(int j=0;j<sides.num;j++) {
// use dot product of face normals. '^'
// defined in vector
float dotprod = u->face[i]->normal ^ sides[j]->normal;
mincurv = min(mincurv,(1-dotprod)/2.0f);
}
curvature = max(curvature,mincurv);
}
// the more coplanar the lower the curvature term
return edgelength * curvature;
}
void ComputeEdgeCostAtVertex(Vertex *v) {
// compute the edge collapse cost for all edges that start
// from vertex v. Since we are only interested in reducing
// the object by selecting the min cost edge at each step, we
// only cache the cost of the least cost edge at this vertex
// (in member variable collapse) as well as the value of the
// cost (in member variable objdist).
if(v->neighbor.num==0) {
// v doesn't have neighbors so it costs nothing to collapse
v->collapse=NULL;
v->objdist=-0.01f;
return;
}
v->objdist = 1000000;
v->collapse=NULL;
// search all neighboring edges for "least cost" edge
for(int i=0;i<v->neighbor.num;i++) {
float dist;
dist = ComputeEdgeCollapseCost(v,v->neighbor[i]);
if(dist<v->objdist) {
// candidate for edge collapse
v->collapse=v->neighbor[i];
// cost of the collapse
v->objdist=dist;
}
}
}
void ComputeAllEdgeCollapseCosts() {
// For all the edges, compute the difference it would make
// to the model if it was collapsed. The least of these
// per vertex is cached in each vertex object.
for(int i=0;i<vertices.num;i++) {
ComputeEdgeCostAtVertex(vertices[i]);
}
}
void Collapse(Vertex *u,Vertex *v){
// Collapse the edge uv by moving vertex u onto v
// Actually remove tris on uv, then update tris that
// have u to have v, and then remove u.
if(!v) {
// u is a vertex all by itself so just delete it
delete u;
return;
}
int i;
List<Vertex *>tmp;
// make tmp a list of all the neighbors of u
for(i=0;i<u->neighbor.num;i++) {
tmp.Add(u->neighbor[i]);
}
// delete triangles on edge uv:
for(i=u->face.num-1;i>=0;i--) {
if(u->face[i]->HasVertex(v)) {
delete(u->face[i]);
}
}
// update remaining triangles to have v instead of u
for(i=u->face.num-1;i>=0;i--) {
u->face[i]->ReplaceVertex(u,v);
}
delete u;
// recompute the edge collapse costs for neighboring vertices
for(i=0;i<tmp.num;i++) {
ComputeEdgeCostAtVertex(tmp[i]);
}
}
void AddVertex(List<Vector> &vert){
for(int i=0;i<vert.num;i++) {
new Vertex(vert[i],i);
}
}
void AddFaces(List<tridata> &tri){
for(int i=0;i<tri.num;i++) {
new Triangle(
vertices[tri[i].v[0]],
vertices[tri[i].v[1]],
vertices[tri[i].v[2]] );
}
}
Vertex *MinimumCostEdge(){
// Find the edge that when collapsed will affect model the least.
// This funtion actually returns a Vertex, the second vertex
// of the edge (collapse candidate) is stored in the vertex data.
// Serious optimization opportunity here: this function currently
// does a sequential search through an unsorted list :-(
// Our algorithm could be O(n*lg(n)) instead of O(n*n)
Vertex *mn=vertices[0];
for(int i=0;i<vertices.num;i++) {
if(vertices[i]->objdist < mn->objdist) {
mn = vertices[i];
}
}
return mn;
}
void ProgressiveMesh(List<Vector> &vert, List<tridata> &tri,
List<int> &map, List<int> &permutation)
{
AddVertex(vert); // put input data into our data structures
AddFaces(tri);
ComputeAllEdgeCollapseCosts(); // cache all edge collapse costs
permutation.SetSize(vertices.num); // allocate space
map.SetSize(vertices.num); // allocate space
// reduce the object down to nothing:
while(vertices.num > 0) {
// get the next vertex to collapse
Vertex *mn = MinimumCostEdge();
// keep track of this vertex, i.e. the collapse ordering
permutation[mn->id]=vertices.num-1;
// keep track of vertex to which we collapse to
map[vertices.num-1] = (mn->collapse)?mn->collapse->id:-1;
// Collapse this edge
Collapse(mn,mn->collapse);
}
// reorder the map list based on the collapse ordering
for(int i=0;i<map.num;i++) {
map[i] = (map[i]==-1)?0:permutation[map[i]];
}
// The caller of this function should reorder their vertices
// according to the returned "permutation".
}

View File

@ -0,0 +1,33 @@
/*
* Progressive Mesh type Polygon Reduction Algorithm
* by Stan Melax (c) 1998
*
* The function ProgressiveMesh() takes a model in an "indexed face
* set" sort of way. i.e. list of vertices and list of triangles.
* The function then does the polygon reduction algorithm
* internally and reduces the model all the way down to 0
* vertices and then returns the order in which the
* vertices are collapsed and to which neighbor each vertex
* is collapsed to. More specifically the returned "permutation"
* indicates how to reorder your vertices so you can render
* an object by using the first n vertices (for the n
* vertex version). After permuting your vertices, the
* map list indicates to which vertex each vertex is collapsed to.
*/
#ifndef PROGRESSIVE_MESH_H
#define PROGRESSIVE_MESH_H
#include "vector.h"
#include "list.h"
class tridata {
public:
int v[3]; // indices to vertex list
// texture and vertex normal info removed for this demo
};
void ProgressiveMesh(List<Vector> &vert, List<tridata> &tri,
List<int> &map, List<int> &permutation );
#endif

View File

@ -0,0 +1,117 @@
#include <stdio.h>
#include <math.h>
#include <assert.h>
#include "vector.h"
float sqr(float a) {return a*a;}
// vector (floating point) implementation
float magnitude(Vector v) {
return float(sqrt(sqr(v.x) + sqr( v.y)+ sqr(v.z)));
}
Vector normalize(Vector v) {
float d=magnitude(v);
if (d==0) {
printf("Cant normalize ZERO vector\n");
assert(0);
d=0.1f;
}
v.x/=d;
v.y/=d;
v.z/=d;
return v;
}
Vector operator+(Vector v1,Vector v2)
{
return Vector(v1.x+v2.x,v1.y+v2.y,v1.z+v2.z);
}
Vector operator-(Vector v1,Vector v2)
{
return Vector(v1.x-v2.x,v1.y-v2.y,v1.z-v2.z);
}
Vector operator-(Vector v) {return Vector(-v.x,-v.y,-v.z);}
Vector operator*(Vector v1,float s) {return Vector(v1.x*s,v1.y*s,v1.z*s);}
Vector operator*(float s, Vector v1) {return Vector(v1.x*s,v1.y*s,v1.z*s);}
Vector operator/(Vector v1,float s) {return v1*(1.0f/s);}
float operator^(Vector v1,Vector v2)
{
return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z;
}
Vector operator*(Vector v1,Vector v2) {
return Vector(
v1.y * v2.z - v1.z*v2.y,
v1.z * v2.x - v1.x*v2.z,
v1.x * v2.y - v1.y*v2.x);
}
Vector planelineintersection(Vector n,float d,Vector p1,Vector p2){
// returns the point where the line p1-p2 intersects the plane n&d
Vector dif = p2-p1;
float dn= n^dif;
float t = -(d+(n^p1) )/dn;
return p1 + (dif*t);
}
int concurrent(Vector a,Vector b) {
return(a.x==b.x && a.y==b.y && a.z==b.z);
}
// Matrix Implementation
matrix transpose(matrix m) {
return matrix( Vector(m.x.x,m.y.x,m.z.x),
Vector(m.x.y,m.y.y,m.z.y),
Vector(m.x.z,m.y.z,m.z.z));
}
Vector operator*(matrix m,Vector v){
m=transpose(m); // since column ordered
return Vector(m.x^v,m.y^v,m.z^v);
}
matrix operator*(matrix m1,matrix m2){
m1=transpose(m1);
return matrix(m1*m2.x,m1*m2.y,m1*m2.z);
}
//Quaternion Implementation
Quaternion operator*(Quaternion a,Quaternion b) {
Quaternion c;
c.r = a.r*b.r - a.x*b.x - a.y*b.y - a.z*b.z;
c.x = a.r*b.x + a.x*b.r + a.y*b.z - a.z*b.y;
c.y = a.r*b.y - a.x*b.z + a.y*b.r + a.z*b.x;
c.z = a.r*b.z + a.x*b.y - a.y*b.x + a.z*b.r;
return c;
}
Quaternion operator-(Quaternion q) {
return Quaternion(q.r*-1,q.x,q.y,q.z);
}
Quaternion operator*(Quaternion a,float b) {
return Quaternion(a.r*b, a.x*b, a.y*b, a.z*b);
}
Vector operator*(Quaternion q,Vector v) {
return q.getmatrix() * v;
}
Vector operator*(Vector v,Quaternion q){
assert(0); // must multiply with the quat on the left
return Vector(0.0f,0.0f,0.0f);
}
Quaternion operator+(Quaternion a,Quaternion b) {
return Quaternion(a.r+b.r, a.x+b.x, a.y+b.y, a.z+b.z);
}
float operator^(Quaternion a,Quaternion b) {
return (a.r*b.r + a.x*b.x + a.y*b.y + a.z*b.z);
}
Quaternion slerp(Quaternion a,Quaternion b,float interp){
if((a^b) <0.0) {
a.r=-a.r;
a.x=-a.x;
a.y=-a.y;
a.z=-a.z;
}
float theta = float(acos(a^b));
if(theta==0.0f) { return(a);}
return
a*float(sin(theta-interp*theta)/sin(theta))
+ b*float(sin(interp*theta)/sin(theta));
}

View File

@ -0,0 +1,79 @@
//
// This module contains a bunch of well understood functions
// I apologise if the conventions used here are slightly
// different than what you are used to.
//
#ifndef GENERIC_VECTOR_H
#define GENERIC_VECTOR_H
#include <stdio.h>
#include <math.h>
class Vector {
public:
float x,y,z;
Vector(float _x=0.0,float _y=0.0,float _z=0.0){x=_x;y=_y;z=_z;};
operator float *() { return &x;};
};
float magnitude(Vector v);
Vector normalize(Vector v);
Vector operator+(Vector v1,Vector v2);
Vector operator-(Vector v);
Vector operator-(Vector v1,Vector v2);
Vector operator*(Vector v1,float s) ;
Vector operator*(float s,Vector v1) ;
Vector operator/(Vector v1,float s) ;
float operator^(Vector v1,Vector v2); // DOT product
Vector operator*(Vector v1,Vector v2); // CROSS product
Vector planelineintersection(Vector n,float d,Vector p1,Vector p2);
class matrix{
public:
Vector x,y,z;
matrix(){x=Vector(1.0f,0.0f,0.0f);
y=Vector(0.0f,1.0f,0.0f);
z=Vector(0.0f,0.0f,1.0f);};
matrix(Vector _x,Vector _y,Vector _z){x=_x;y=_y;z=_z;};
};
matrix transpose(matrix m);
Vector operator*(matrix m,Vector v);
matrix operator*(matrix m1,matrix m2);
class Quaternion{
public:
float r,x,y,z;
Quaternion(){x=y=z=0.0f;r=1.0f;};
Quaternion(Vector v,float t){
v=normalize(v);
r=float(cos(t/2.0));
v=v*float(sin(t/2.0));
x=v.x;
y=v.y;
z=v.z;
};
Quaternion(float _r,float _x,float _y,float _z){r=_r;x=_x;y=_y;z=_z;};
float angle(){return float(acos(r)*2.0);}
Vector axis(){Vector a(x,y,z); return a*float(1/sin(angle()/2.0));}
Vector xdir(){
return Vector(1-2*(y*y+z*z), 2*(x*y+r*z), 2*(x*z-r*y));
}
Vector ydir(){
return Vector( 2*(x*y-r*z),1-2*(x*x+z*z), 2*(y*z+r*x));
}
Vector zdir(){
return Vector( 2*(x*z+r*y), 2*(y*z-r*x),1-2*(x*x+y*y));
}
matrix getmatrix(){return matrix(xdir(),ydir(),zdir());}
//operator matrix(){return getmatrix();}
};
Quaternion operator-(Quaternion q);
Quaternion operator*(Quaternion a,Quaternion b);
Vector operator*(Quaternion q,Vector v);
Vector operator*(Vector v,Quaternion q);
Quaternion slerp(Quaternion a,Quaternion b,float interp);
#endif

View File

@ -0,0 +1,453 @@
/*
* Polygon Reduction Demo by Stan Melax (c) 1998
* Permission to use any of this code wherever you want is granted..
* Although, please do acknowledge authorship if appropriate.
*
* This module contains the window setup code, mouse input, timing
* routines, and that sort of stuff. The interesting modules
* to see are bunnygut.cpp and progmesh.cpp.
*
* The windows 95 specific code for this application was taken from
* an example of processing mouse events in an OpenGL program using
* the Win32 API from the www.opengl.org web site.
*
* Under Project->Settings, Link Options, General Category
* Add:
* Opengl32.lib glu32.lib winmm.lib
* to the Object/Library Modules
*
* You will need have OpenGL libs and include files to compile this
* Go to the www.opengl.org web site if you need help with this.
*/
#include <windows.h> /* must include this before GL/gl.h */
#include <GL/gl.h> /* OpenGL header file */
#include <GL/glu.h> /* OpenGL utilities header file */
#include <stdio.h>
#include <sys/types.h>
#include <sys/timeb.h>
#include <time.h>
#include "vector.h"
#include "font.h"
// Functions and Variables from bunny module
extern void InitModel();
extern void RenderModel();
extern Vector model_position; // position of bunny
extern Quaternion model_orientation; // orientation of bunny
// Global Variables
float DeltaT = 0.1f;
float FPS;
int Width = 512;
int Height = 512;
int MouseX = 0;
int MouseY = 0;
Vector MouseVector; // 3D direction mouse points
Vector OldMouseVector;
int MouseState=0; // true iff left button down
float ViewAngle=45.0f;
HDC hDC; /* device context */
HPALETTE hPalette = 0; /* custom palette (if needed) */
void CalcFPSDeltaT(){
static int timeinit=0;
static int start,start2,current,last;
static int frame=0, frame2=0;
if(!timeinit){
frame=0;
start=timeGetTime();
timeinit=1;
}
frame++;
frame2++;
current=timeGetTime(); // found in winmm.lib
double dif=(double)(current-start)/CLOCKS_PER_SEC;
double rv = (dif)? (double)frame/(double)dif:-1.0;
if(dif>2.0 && frame >10) {
start = start2;
frame = frame2;
start2 = timeGetTime();
frame2 = 0;
}
DeltaT = (float)(current-last)/CLOCKS_PER_SEC;
if(current==last) {
DeltaT = 0.1f/CLOCKS_PER_SEC; // it just cant be 0
}
// if(DeltaT>1.0) DeltaT=1.0;
FPS = (float)rv;
last = current;
}
void ComputeMouseVector(){
OldMouseVector=MouseVector;
float spread = (float)tan(ViewAngle/2*3.14/180);
float y = spread * ((Height-MouseY)-Height/2.0f) /(Height/2.0f);
float x = spread * (MouseX-Width/2.0f) /(Height/2.0f);
Vector v(x ,y,-1);
// v=UserOrientation *v;
v=normalize(v);
MouseVector = v;
}
Quaternion VirtualTrackBall(Vector cop,Vector cor,Vector dir1,Vector dir2) {
// Implement track ball functionality to spin stuf on the screen
// cop center of projection
// cor center of rotation
// dir1 old mouse direction
// dir2 new mouse direction
// pretend there is a sphere around cor. Then find the points
// where dir1 and dir2 intersect that sphere. Find the
// rotation that takes the first point to the second.
float m;
// compute plane
Vector nrml = cor - cop;
// since trackball proportional to distance from cop
float fudgefactor = 1.0f/(magnitude(nrml) * 0.25f);
nrml = normalize(nrml);
float dist = -(nrml^cor);
Vector u= planelineintersection(nrml,dist,cop,cop+dir1);
u=u-cor;
u=u*fudgefactor;
m= magnitude(u);
if(m>1) {u=u*1.0f/m;}
else {
u=u - (nrml * (float)sqrt(1-m*m));
}
Vector v= planelineintersection(nrml,dist,cop,cop+dir2);
v=v-cor;
v=v*fudgefactor;
m= magnitude(v);
if(m>1) {v=v*1.0f/m;}
else {
v=v - (nrml * (float)sqrt(1-m*m));
}
Vector axis = u*v;
float angle;
m=magnitude(axis);
if(m>1)m=1; // avoid potential floating point error
Quaternion q(Vector(1.0f,0.0f,0.0f),0.0f);
if(m>0 && (angle=(float)asin(m))>3.14/180) {
axis = normalize(axis);
q=Quaternion(axis,angle);
}
return q;
}
void SpinIt(){
// Change the orientation of the bunny according to mouse drag
Quaternion q=VirtualTrackBall(Vector(0,0,0),model_position,
OldMouseVector,MouseVector);
model_orientation=q*model_orientation;
}
void Reshape(int width, int height){
// called initially and when the window changes size
Width=width;
Height=height;
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(ViewAngle, (float)width/height, 0.1, 50.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void PrintStats(){
char buf[1024];buf[0]='\0';
sprintf(buf,"FPS: %5.2f ",FPS);
PostString(buf,0,-1,0);
}
void Display(){
// main drawing routine - called every frame
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
glLoadIdentity();
// camera at default (zero) position and orientation
RenderModel();
PrintStats();
glLoadIdentity();
RenderStrings();
glPopMatrix();
glFlush();
SwapBuffers(hDC); /* nop if singlebuffered */
}
LONG WINAPI WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static PAINTSTRUCT ps;
static GLboolean left = GL_FALSE; /* left button currently down? */
static GLboolean right = GL_FALSE; /* right button currently down? */
static int omx, omy, mx, my;
switch(uMsg) {
case WM_PAINT:
BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
return 0;
case WM_SIZE:
Reshape(LOWORD(lParam), HIWORD(lParam));
PostMessage(hWnd, WM_PAINT, 0, 0);
return 0;
case WM_CHAR:
switch (wParam) {
case 27: /* ESC key */
PostQuitMessage(0);
break;
}
return 0;
case WM_LBUTTONDOWN:
/* if we don't set the capture we won't get mouse move
messages when the mouse moves outside the window. */
SetCapture(hWnd);
MouseX = LOWORD(lParam);
MouseY = HIWORD(lParam);
ComputeMouseVector();
MouseState = 1;
return 0;
case WM_LBUTTONUP:
MouseX = LOWORD(lParam);
MouseY = HIWORD(lParam);
if(MouseX & 1 << 15) MouseX -= (1 << 16);
if(MouseY & 1 << 15) MouseY -= (1 << 16);
ComputeMouseVector();
if(MouseState) SpinIt();
MouseState=0;
/* remember to release the capture when we are finished. */
ReleaseCapture();
return 0;
case WM_MOUSEMOVE:
MouseX = LOWORD(lParam);
MouseY = HIWORD(lParam);
/* Win32 is pretty braindead about the x, y position that
it returns when the mouse is off the left or top edge
of the window (due to them being unsigned). therefore,
roll the Win32's 0..2^16 pointer co-ord range to the
more amenable (and useful) 0..+/-2^15. */
if(MouseX & 1 << 15) MouseX -= (1 << 16);
if(MouseY & 1 << 15) MouseY -= (1 << 16);
ComputeMouseVector();
if(MouseState) SpinIt();
return 0;
case WM_PALETTECHANGED:
if (hWnd == (HWND)wParam) break;
/* fall through to WM_QUERYNEWPALETTE */
case WM_QUERYNEWPALETTE:
if (hPalette) {
UnrealizeObject(hPalette);
SelectPalette(hDC, hPalette, FALSE);
RealizePalette(hDC);
return TRUE;
}
return FALSE;
case WM_CLOSE:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
HWND CreateOpenGLWindow(char* title)
{
// make a double-buffered, rgba, opengl window
int n, pf;
HWND hWnd;
WNDCLASS wc;
LOGPALETTE* lpPal;
PIXELFORMATDESCRIPTOR pfd;
static HINSTANCE hInstance = 0;
/* only register the window class once - use hInstance as a flag. */
if (!hInstance) {
hInstance = GetModuleHandle(NULL);
wc.style = CS_OWNDC;
wc.lpfnWndProc = (WNDPROC)WindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = "OpenGL";
if (!RegisterClass(&wc)) {
MessageBox(NULL, "RegisterClass() failed: "
"Cannot register window class.",
"Error", MB_OK);
return NULL;
}
}
hWnd = CreateWindow("OpenGL", title, WS_OVERLAPPEDWINDOW |
WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
0,0,Width,Height, NULL, NULL, hInstance, NULL);
if (hWnd == NULL) {
MessageBox(NULL,
"CreateWindow() failed: Cannot create a window.",
"Error", MB_OK);
return NULL;
}
hDC = GetDC(hWnd);
/* there is no guarantee that the contents of the stack that become
the pfd are zeroed, therefore _make sure_ to clear these bits. */
memset(&pfd, 0, sizeof(pfd));
pfd.nSize = sizeof(pfd);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW
| PFD_SUPPORT_OPENGL
| PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cDepthBits = 32;
pfd.cColorBits = 32;
pf = ChoosePixelFormat(hDC, &pfd);
if (pf == 0) {
MessageBox(NULL, "ChoosePixelFormat() failed: "
"Cannot find a suitable pixel format.",
"Error", MB_OK);
return 0;
}
if (SetPixelFormat(hDC, pf, &pfd) == FALSE) {
MessageBox(NULL, "SetPixelFormat() failed: "
"Cannot set format specified.", "Error", MB_OK);
return 0;
}
DescribePixelFormat(hDC, pf, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
if (pfd.dwFlags & PFD_NEED_PALETTE ||
pfd.iPixelType == PFD_TYPE_COLORINDEX) {
n = 1 << pfd.cColorBits;
if (n > 256) n = 256;
lpPal = (LOGPALETTE*)malloc(sizeof(LOGPALETTE) +
sizeof(PALETTEENTRY) * n);
memset(lpPal, 0, sizeof(LOGPALETTE) + sizeof(PALETTEENTRY) * n);
lpPal->palVersion = 0x300;
lpPal->palNumEntries = n;
GetSystemPaletteEntries(hDC, 0, n, &lpPal->palPalEntry[0]);
/* if the pixel type is RGBA, then we want to make an RGB ramp,
otherwise (color index) set individual colors. */
if (pfd.iPixelType == PFD_TYPE_RGBA) {
int redMask = (1 << pfd.cRedBits) - 1;
int greenMask = (1 << pfd.cGreenBits) - 1;
int blueMask = (1 << pfd.cBlueBits) - 1;
int i;
/* fill in the entries with an RGB color ramp. */
for (i = 0; i < n; ++i) {
lpPal->palPalEntry[i].peRed =
(((i >> pfd.cRedShift) & redMask) * 255)
/redMask;
lpPal->palPalEntry[i].peGreen =
(((i >> pfd.cGreenShift) & greenMask) * 255)
/greenMask;
lpPal->palPalEntry[i].peBlue =
(((i >> pfd.cBlueShift) & blueMask) * 255)
/blueMask;
lpPal->palPalEntry[i].peFlags = 0;
}
} else {
lpPal->palPalEntry[0].peRed = 0;
lpPal->palPalEntry[0].peGreen = 0;
lpPal->palPalEntry[0].peBlue = 0;
lpPal->palPalEntry[0].peFlags = PC_NOCOLLAPSE;
lpPal->palPalEntry[1].peRed = 255;
lpPal->palPalEntry[1].peGreen = 0;
lpPal->palPalEntry[1].peBlue = 0;
lpPal->palPalEntry[1].peFlags = PC_NOCOLLAPSE;
lpPal->palPalEntry[2].peRed = 0;
lpPal->palPalEntry[2].peGreen = 255;
lpPal->palPalEntry[2].peBlue = 0;
lpPal->palPalEntry[2].peFlags = PC_NOCOLLAPSE;
lpPal->palPalEntry[3].peRed = 0;
lpPal->palPalEntry[3].peGreen = 0;
lpPal->palPalEntry[3].peBlue = 255;
lpPal->palPalEntry[3].peFlags = PC_NOCOLLAPSE;
}
hPalette = CreatePalette(lpPal);
if (hPalette) {
SelectPalette(hDC, hPalette, FALSE);
RealizePalette(hDC);
}
free(lpPal);
}
ReleaseDC(hDC, hWnd);
return hWnd;
}
int APIENTRY WinMain(HINSTANCE hCurrentInst, HINSTANCE hPreviousInst,
LPSTR lpszCmdLine, int nCmdShow)
{
HGLRC hRC; /* opengl context */
HWND hWnd; /* window */
MSG msg; /* message */
// InitModel() initializes some data structures and
// does the progressive mesh polygon reduction algorithm
// on the model.
CalcFPSDeltaT(); // to time the algorithm
InitModel();
CalcFPSDeltaT();
hWnd = CreateOpenGLWindow("bunnylod by Stan Melax");
if (hWnd == NULL) exit(1);
hDC = GetDC(hWnd);
hRC = wglCreateContext(hDC);
wglMakeCurrent(hDC, hRC);
ShowWindow(hWnd, nCmdShow);
glEnable(GL_DEPTH_TEST);
PostString("Demo by Stan Melax (c)1998",5,-5,20);
PostString("Model by Viewpoint Datalabs (c)1996",5,-4,20);
char buf[128];
PostString("Mesh Reduction Algorithm (non-optimized)",1,0,5);
sprintf(buf,"was executed in %5.3f seconds",DeltaT);
PostString(buf,2,1,6);
while (1) {
while(PeekMessage(&msg, hWnd, 0, 0, PM_NOREMOVE)) {
if(GetMessage(&msg, hWnd, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
} else {
// This 'goto' was in the sample code
goto quit;
}
}
CalcFPSDeltaT();
Display();
}
quit:
wglMakeCurrent(NULL, NULL);
ReleaseDC(hDC, hWnd);
wglDeleteContext(hRC);
DestroyWindow(hWnd);
if (hPalette) DeleteObject(hPalette);
return msg.wParam;
}

View File

@ -0,0 +1,211 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2013 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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 <http://www.gnu.org/licenses/>.
Application
surfaceCoarsen
Description
Surface coarsening using 'bunnylod':
Polygon Reduction Demo
By Stan Melax (c) 1998
mailto:melax@cs.ualberta.ca
http://www.cs.ualberta.ca/~melax
\*---------------------------------------------------------------------------*/
#include "argList.H"
#include "fileName.H"
#include "triSurface.H"
#include "OFstream.H"
#include "triFace.H"
#include "triFaceList.H"
// From bunnylod
#include "progmesh.h"
using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int mapVertex(::List<int>& collapse_map, int a, int mx)
{
if (mx <= 0)
{
return 0;
}
while (a >= mx)
{
a = collapse_map[a];
}
return a;
}
int main(int argc, char *argv[])
{
argList::noParallel();
argList::validArgs.append("surfaceFile");
argList::validArgs.append("reductionFactor");
argList::validArgs.append("output surfaceFile");
argList args(argc, argv);
const fileName inFileName = args[1];
const scalar reduction = args.argRead<scalar>(2);
const fileName outFileName = args[3];
if (reduction <= 0 || reduction > 1)
{
FatalErrorIn(args.executable())
<< "Reduction factor " << reduction
<< " should be within 0..1" << endl
<< "(it is the reduction in number of vertices)"
<< exit(FatalError);
}
Info<< "Input surface :" << inFileName << endl
<< "Reduction factor:" << reduction << endl
<< "Output surface :" << outFileName << endl << endl;
const triSurface surf(inFileName);
Info<< "Surface:" << endl;
surf.writeStats(Info);
Info<< endl;
::List< ::Vector> vert; // global list of vertices
::List< ::tridata> tri; // global list of triangles
// Convert triSurface to progmesh format. Note: can use global point
// numbering since surface read in from file.
const pointField& pts = surf.points();
forAll(pts, ptI)
{
const point& pt = pts[ptI];
vert.Add( ::Vector(pt.x(), pt.y(), pt.z()));
}
forAll(surf, faceI)
{
const labelledTri& f = surf[faceI];
tridata td;
td.v[0]=f[0];
td.v[1]=f[1];
td.v[2]=f[2];
tri.Add(td);
}
::List<int> collapse_map; // to which neighbor each vertex collapses
::List<int> permutation;
::ProgressiveMesh(vert,tri,collapse_map,permutation);
// rearrange the vertex list
::List< ::Vector> temp_list;
for (int i=0;i<vert.num;i++)
{
temp_list.Add(vert[i]);
}
for (int i=0;i<vert.num;i++)
{
vert[permutation[i]]=temp_list[i];
}
// update the changes in the entries in the triangle list
for (int i=0;i<tri.num;i++)
{
for (int j=0;j<3;j++)
{
tri[i].v[j] = permutation[tri[i].v[j]];
}
}
// Only get triangles with non-collapsed edges.
int render_num = int(reduction * surf.nPoints());
Info<< "Reducing to " << render_num << " vertices" << endl;
// Storage for new surface.
Foam::List<labelledTri> newTris(surf.size());
label newI = 0;
for (int i=0; i<tri.num; i++)
{
int p0 = mapVertex(collapse_map, tri[i].v[0], render_num);
int p1 = mapVertex(collapse_map, tri[i].v[1], render_num);
int p2 = mapVertex(collapse_map, tri[i].v[2], render_num);
// note: serious optimization opportunity here,
// by sorting the triangles the following "continue"
// could have been made into a "break" statement.
if (p0 == p1 || p1 == p2 || p2 == p0)
{
continue;
}
newTris[newI++] = labelledTri(p0, p1, p2, 0);
}
newTris.setSize(newI);
// Convert vert into pointField.
pointField newPoints(vert.num);
for (int i=0; i<vert.num; i++)
{
const ::Vector & v = vert[i];
newPoints[i] = point(v.x, v.y, v.z);
}
triSurface surf2(newTris, newPoints);
triSurface outSurf
(
surf2.localFaces(),
surf2.patches(),
surf2.localPoints()
);
Info<< "Coarsened surface:" << endl;
surf2.writeStats(Info);
Info<< endl;
Info<< "Writing to file " << outFileName << endl << endl;
surf2.write(outFileName);
Info<< "End\n" << endl;
return 0;
}
// ************************************************************************* //

View File

@ -0,0 +1,3 @@
surfaceConvert.C
EXE = $(FOAM_APPBIN)/surfaceConvert

View File

@ -0,0 +1,6 @@
EXE_INC = \
-I$(LIB_SRC)/triSurface/lnInclude
EXE_LIBS = \
-lmeshTools \
-ltriSurface

View File

@ -0,0 +1,159 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2013 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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 <http://www.gnu.org/licenses/>.
Application
surfaceConvert
Description
Converts from one surface mesh format to another.
Usage
- surfaceConvert inputFile outputFile [OPTION]
\param -clean \n
Perform some surface checking/cleanup on the input surface
\param -scale \<scale\> \n
Specify a scaling factor for writing the files
\param -group \n
Orders faces by region
Note
The filename extensions are used to determine the file format type.
\*---------------------------------------------------------------------------*/
#include "argList.H"
#include "fileName.H"
#include "triSurface.H"
#include "OFstream.H"
#include "OSspecific.H"
#include "Time.H"
using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
argList::addNote
(
"convert between surface formats"
);
argList::noParallel();
argList::validArgs.append("inputFile");
argList::validArgs.append("outputFile");
argList::addBoolOption
(
"clean",
"perform some surface checking/cleanup on the input surface"
);
argList::addBoolOption
(
"group",
"reorder faces into groups; one per region"
);
argList::addOption
(
"scale",
"factor",
"geometry scaling factor - default is 1"
);
argList::addOption
(
"writePrecision",
"label",
"write to output with the specified precision"
);
argList args(argc, argv);
if (args.optionFound("writePrecision"))
{
label writePrecision = args.optionRead<label>("writePrecision");
IOstream::defaultPrecision(writePrecision);
Sout.precision(writePrecision);
Info<< "Output write precision set to " << writePrecision << endl;
}
const fileName importName = args[1];
const fileName exportName = args[2];
if (importName == exportName)
{
FatalErrorIn(args.executable())
<< "Output file " << exportName << " would overwrite input file."
<< exit(FatalError);
}
Info<< "Reading : " << importName << endl;
triSurface surf(importName);
Info<< "Read surface:" << endl;
surf.writeStats(Info);
Info<< endl;
if (args.optionFound("clean"))
{
Info<< "Cleaning up surface" << endl;
surf.cleanup(true);
Info<< "After cleaning up surface:" << endl;
surf.writeStats(Info);
Info<< endl;
}
const bool sortByRegion = args.optionFound("group");
if (sortByRegion)
{
Info<< "Reordering faces into groups; one per region." << endl;
}
else
{
Info<< "Maintaining face ordering" << endl;
}
Info<< "writing " << exportName;
scalar scaleFactor = 0;
if (args.optionReadIfPresent("scale", scaleFactor) && scaleFactor > 0)
{
Info<< " with scaling " << scaleFactor;
surf.scalePoints(scaleFactor);
}
Info<< endl;
surf.write(exportName, sortByRegion);
Info<< "\nEnd\n" << endl;
return 0;
}
// ************************************************************************* //

View File

@ -0,0 +1,3 @@
surfaceFeatureConvert.C
EXE = $(FOAM_APPBIN)/surfaceFeatureConvert

View File

@ -0,0 +1,7 @@
EXE_INC = \
-I$(LIB_SRC)/meshTools/lnInclude \
-I$(LIB_SRC)/edgeMesh/lnInclude
EXE_LIBS = \
-lmeshTools \
-ledgeMesh

View File

@ -0,0 +1,110 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2013 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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 <http://www.gnu.org/licenses/>.
Application
surfaceFeatureConvert
Description
Convert between edgeMesh formats.
\*---------------------------------------------------------------------------*/
#include "argList.H"
#include "Time.H"
#include "edgeMesh.H"
using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
argList::addNote
(
"Convert between edgeMesh formats"
);
argList::noParallel();
argList::validArgs.append("inputFile");
argList::validArgs.append("outputFile");
argList::addOption
(
"scale",
"factor",
"geometry scaling factor - default is 1"
);
argList args(argc, argv);
Time runTime(args.rootPath(), args.caseName());
const fileName importName = args[1];
const fileName exportName = args[2];
// disable inplace editing
if (importName == exportName)
{
FatalErrorIn(args.executable())
<< "Output file " << exportName << " would overwrite input file."
<< exit(FatalError);
}
// check that reading/writing is supported
if
(
!edgeMesh::canReadType(importName.ext(), true)
|| !edgeMesh::canWriteType(exportName.ext(), true)
)
{
return 1;
}
edgeMesh mesh(importName);
Info<< "\nRead edgeMesh " << importName << nl;
mesh.writeStats(Info);
Info<< nl
<< "\nwriting " << exportName;
scalar scaleFactor = 0;
if (args.optionReadIfPresent("scale", scaleFactor) && scaleFactor > 0)
{
Info<< " with scaling " << scaleFactor << endl;
mesh.scalePoints(scaleFactor);
}
else
{
Info<< " without scaling" << endl;
}
mesh.write(exportName);
mesh.writeStats(Info);
Info<< endl;
Info<< "\nEnd\n" << endl;
return 0;
}
// ************************************************************************* //

View File

@ -0,0 +1,3 @@
surfaceFeatureExtract.C
EXE = $(FOAM_APPBIN)/surfaceFeatureExtract

View File

@ -0,0 +1,13 @@
EXE_INC = \
-I$(LIB_SRC)/finiteVolume/lnInclude \
-I$(LIB_SRC)/meshTools/lnInclude \
-I$(LIB_SRC)/edgeMesh/lnInclude \
-I$(LIB_SRC)/triSurface/lnInclude \
-I$(LIB_SRC)/surfMesh/lnInclude \
-I$(LIB_SRC)/sampling/lnInclude
EXE_LIBS = \
-lmeshTools \
-ledgeMesh \
-ltriSurface \
-lsampling

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,117 @@
/*--------------------------------*- C++ -*----------------------------------*\
| ========= | |
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
| \\ / O peration | Version: dev |
| \\ / A nd | Web: www.OpenFOAM.org |
| \\/ M anipulation | |
\*---------------------------------------------------------------------------*/
FoamFile
{
version 2.0;
format ascii;
class dictionary;
object surfaceFeatureExtractDict;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
surface1.stl
{
// How to obtain raw features (extractFromFile || extractFromSurface)
extractionMethod extractFromSurface;
extractFromSurfaceCoeffs
{
// Mark edges whose adjacent surface normals are at an angle less
// than includedAngle as features
// - 0 : selects no edges
// - 180: selects all edges
includedAngle 120;
// Do not mark region edges
geometricTestOnly yes;
}
// Write options
// Write features to obj format for postprocessing
writeObj yes;
}
surface2.nas
{
// How to obtain raw features (extractFromFile || extractFromSurface)
extractionMethod extractFromFile;
extractFromFileCoeffs
{
// Load from an existing feature edge file
featureEdgeFile "constant/triSurface/featureEdges.nas";
}
trimFeatures
{
// Remove features with fewer than the specified number of edges
minElem 0;
// Remove features shorter than the specified cumulative length
minLen 0.0;
}
subsetFeatures
{
// Use a plane to select feature edges
// (normal)(basePoint)
// Keep only edges that intersect the plane will be included
plane (1 0 0)(0 0 0);
// Select feature edges using a box
// (minPt)(maxPt)
// Keep edges inside the box:
insideBox (0 0 0)(1 1 1);
// Keep edges outside the box:
outsideBox (0 0 0)(1 1 1);
// Keep nonManifold edges (edges with >2 connected faces where
// the faces form more than two different normal planes)
nonManifoldEdges yes;
// Keep open edges (edges with 1 connected face)
openEdges yes;
}
addFeatures
{
// Add (without merging) another extendedFeatureEdgeMesh
name axZ.extendedFeatureEdgeMesh;
// Optionally flip features (invert all normals, making
// convex<->concave etc)
//flip false;
}
// Output the curvature of the surface
curvature no;
// Output the proximity of feature points and edges to each other
featureProximity no;
// The maximum search distance to use when looking for other feature
// points and edges
maxFeatureProximity 1;
// Out put the closeness of surface elements to other surface elements.
closeness no;
// Write options
// Write features to obj format for postprocessing
writeObj yes;
// Write surface proximity and curvature fields to vtk format
// for postprocessing
writeVTK no;
}
// ************************************************************************* //

View File

@ -0,0 +1,3 @@
surfaceFind.C
EXE = $(FOAM_APPBIN)/surfaceFind

View File

@ -0,0 +1,6 @@
EXE_INC = \
-I$(LIB_SRC)/surfMesh/lnInclude
EXE_LIBS = \
-lmeshTools \
-lsurfMesh

View File

@ -0,0 +1,131 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2013 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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 <http://www.gnu.org/licenses/>.
Application
surfaceFind
Description
Finds nearest face and vertex.
\*---------------------------------------------------------------------------*/
#include "argList.H"
#include "OFstream.H"
#include "MeshedSurfaces.H"
using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
argList::noParallel();
argList::validArgs.append("surfaceFile");
argList::addOption("x", "X", "The point x-coordinate (if non-zero)");
argList::addOption("y", "Y", "The point y-coordinate (if non-zero)");
argList::addOption("z", "Z", "The point y-coordinate (if non-zero)");
argList args(argc, argv);
const point samplePt
(
args.optionLookupOrDefault<scalar>("x", 0),
args.optionLookupOrDefault<scalar>("y", 0),
args.optionLookupOrDefault<scalar>("z", 0)
);
Info<< "Looking for nearest face/vertex to " << samplePt << endl;
Info<< "Reading surf ..." << endl;
meshedSurface surf1(args[1]);
//
// Nearest vertex
//
const pointField& localPoints = surf1.localPoints();
label minIndex = -1;
scalar minDist = GREAT;
forAll(localPoints, pointI)
{
const scalar dist = mag(localPoints[pointI] - samplePt);
if (dist < minDist)
{
minDist = dist;
minIndex = pointI;
}
}
Info<< "Nearest vertex:" << nl
<< " index :" << minIndex << " (in localPoints)" << nl
<< " index :" << surf1.meshPoints()[minIndex]
<< " (in points)" << nl
<< " coordinates:" << localPoints[minIndex] << nl
<< endl;
//
// Nearest face
//
const pointField& points = surf1.points();
minIndex = -1;
minDist = GREAT;
forAll(surf1, faceI)
{
const point centre = surf1[faceI].centre(points);
const scalar dist = mag(centre - samplePt);
if (dist < minDist)
{
minDist = dist;
minIndex = faceI;
}
}
const face& f = surf1[minIndex];
Info<< "Face with nearest centre:" << nl
<< " index :" << minIndex << nl
<< " centre :" << f.centre(points) << nl
<< " face :" << f << nl
<< " vertex coords:\n";
forAll(f, fp)
{
Info<< " " << points[f[fp]] << "\n";
}
Info<< endl;
Info<< "End\n" << endl;
return 0;
}
// ************************************************************************* //

View File

@ -0,0 +1,3 @@
surfaceHookUp.C
EXE = $(FOAM_APPBIN)/surfaceHookUp

View File

@ -0,0 +1,9 @@
EXE_INC = \
-I$(LIB_SRC)/meshTools/lnInclude \
-I$(LIB_SRC)/fileFormats/lnInclude \
-I$(LIB_SRC)/triSurface/lnInclude
EXE_LIBS = \
-lmeshTools \
-lfileFormats \
-ltriSurface

View File

@ -0,0 +1,597 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2014 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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 <http://www.gnu.org/licenses/>.
Application
surfaceHookUp
Description
Find close open edges and stitches the surface along them
Usage
- surfaceHookUp hookDistance [OPTION]
\*---------------------------------------------------------------------------*/
#include "argList.H"
#include "Time.H"
#include "triSurfaceMesh.H"
#include "indexedOctree.H"
#include "treeBoundBox.H"
#include "PackedBoolList.H"
#include "unitConversion.H"
#include "searchableSurfaces.H"
using namespace Foam;
// Split faceI along edgeI at position newPointI
void greenRefine
(
const triSurface& surf,
const label faceI,
const label edgeI,
const label newPointI,
DynamicList<labelledTri>& newFaces
)
{
const labelledTri& f = surf.localFaces()[faceI];
const edge& e = surf.edges()[edgeI];
// Find index of edge in face.
label fp0 = findIndex(f, e[0]);
label fp1 = f.fcIndex(fp0);
label fp2 = f.fcIndex(fp1);
if (f[fp1] == e[1])
{
// Edge oriented like face
newFaces.append
(
labelledTri
(
f[fp0],
newPointI,
f[fp2],
f.region()
)
);
newFaces.append
(
labelledTri
(
newPointI,
f[fp1],
f[fp2],
f.region()
)
);
}
else
{
newFaces.append
(
labelledTri
(
f[fp2],
newPointI,
f[fp1],
f.region()
)
);
newFaces.append
(
labelledTri
(
newPointI,
f[fp0],
f[fp1],
f.region()
)
);
}
}
//scalar checkEdgeAngle
//(
// const triSurface& surf,
// const label edgeIndex,
// const label pointIndex,
// const scalar& angle
//)
//{
// const edge& e = surf.edges()[edgeIndex];
// vector eVec = e.vec(surf.localPoints());
// eVec /= mag(eVec) + SMALL;
// const labelList& pEdges = surf.pointEdges()[pointIndex];
//
// forAll(pEdges, eI)
// {
// const edge& nearE = surf.edges()[pEdges[eI]];
// vector nearEVec = nearE.vec(surf.localPoints());
// nearEVec /= mag(nearEVec) + SMALL;
// const scalar dot = eVec & nearEVec;
// const scalar minCos = degToRad(angle);
// if (mag(dot) > minCos)
// {
// return false;
// }
// }
// return true;
//}
void createBoundaryEdgeTrees
(
const PtrList<triSurfaceMesh>& surfs,
PtrList<indexedOctree<treeDataEdge> >& bEdgeTrees,
labelListList& treeBoundaryEdges
)
{
forAll(surfs, surfI)
{
const triSurface& surf = surfs[surfI];
// Boundary edges
treeBoundaryEdges[surfI] =
labelList
(
identity(surf.nEdges() - surf.nInternalEdges())
+ surf.nInternalEdges()
);
Random rndGen(17301893);
// Slightly extended bb. Slightly off-centred just so on symmetric
// geometry there are less face/edge aligned items.
treeBoundBox bb
(
treeBoundBox(UList<point>(surf.localPoints())).extend(rndGen, 1e-4)
);
bb.min() -= point(ROOTVSMALL, ROOTVSMALL, ROOTVSMALL);
bb.max() += point(ROOTVSMALL, ROOTVSMALL, ROOTVSMALL);
bEdgeTrees.set
(
surfI,
new indexedOctree<treeDataEdge>
(
treeDataEdge
(
false, // cachebb
surf.edges(), // edges
surf.localPoints(), // points
treeBoundaryEdges[surfI] // selected edges
),
bb, // bb
8, // maxLevel
10, // leafsize
3.0 // duplicity
)
);
}
}
class findNearestOpSubset
{
const indexedOctree<treeDataEdge>& tree_;
DynamicList<label>& shapeMask_;
public:
findNearestOpSubset
(
const indexedOctree<treeDataEdge>& tree,
DynamicList<label>& shapeMask
)
:
tree_(tree),
shapeMask_(shapeMask)
{}
void operator()
(
const labelUList& indices,
const point& sample,
scalar& nearestDistSqr,
label& minIndex,
point& nearestPoint
) const
{
const treeDataEdge& shape = tree_.shapes();
forAll(indices, i)
{
const label index = indices[i];
const label edgeIndex = shape.edgeLabels()[index];
if
(
!shapeMask_.empty()
&& findIndex(shapeMask_, edgeIndex) != -1
)
{
continue;
}
const edge& e = shape.edges()[edgeIndex];
pointHit nearHit = e.line(shape.points()).nearestDist(sample);
// Only register hit if closest point is not an edge point
if (nearHit.hit())
{
scalar distSqr = sqr(nearHit.distance());
if (distSqr < nearestDistSqr)
{
nearestDistSqr = distSqr;
minIndex = index;
nearestPoint = nearHit.rawPoint();
}
}
}
}
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
argList::addNote
(
"hook surfaces to other surfaces by moving and retriangulating their"
"boundary edges to match other surface boundary edges"
);
argList::noParallel();
argList::validArgs.append("hookTolerance");
# include "addDictOption.H"
# include "setRootCase.H"
# include "createTime.H"
const word dictName("surfaceHookUpDict");
# include "setSystemRunTimeDictionaryIO.H"
Info<< "Reading " << dictName << nl << endl;
const IOdictionary dict(dictIO);
const scalar dist(args.argRead<scalar>(1));
const scalar matchTolerance(max(1e-6*dist, SMALL));
const label maxIters = 100;
Info<< "Hooking distance = " << dist << endl;
searchableSurfaces surfs
(
IOobject
(
"surfacesToHook",
runTime.constant(),
"triSurface",
runTime
),
dict,
true // assume single-region names get surface name
);
Info<< nl << "Reading surfaces: " << endl;
forAll(surfs, surfI)
{
Info<< incrIndent;
Info<< nl << indent << "Surface = " << surfs.names()[surfI] << endl;
const wordList& regions = surfs[surfI].regions();
forAll(regions, surfRegionI)
{
Info<< incrIndent;
Info<< indent << "Regions = " << regions[surfRegionI] << endl;
Info<< decrIndent;
}
Info<< decrIndent;
}
PtrList<indexedOctree<treeDataEdge> > bEdgeTrees(surfs.size());
labelListList treeBoundaryEdges(surfs.size());
List<DynamicList<labelledTri> > newFaces(surfs.size());
List<DynamicList<point> > newPoints(surfs.size());
List<PackedBoolList> visitedFace(surfs.size());
PtrList<triSurfaceMesh> newSurfaces(surfs.size());
forAll(surfs, surfI)
{
const triSurfaceMesh& surf =
refCast<const triSurfaceMesh>(surfs[surfI]);
newSurfaces.set
(
surfI,
new triSurfaceMesh
(
IOobject
(
"hookedSurface_" + surfs.names()[surfI],
runTime.constant(),
"triSurface",
runTime
),
surf
)
);
}
label nChanged = 0;
label nIters = 1;
do
{
Info<< nl << "Iteration = " << nIters++ << endl;
nChanged = 0;
createBoundaryEdgeTrees(newSurfaces, bEdgeTrees, treeBoundaryEdges);
forAll(newSurfaces, surfI)
{
const triSurface& newSurf = newSurfaces[surfI];
newFaces[surfI] = newSurf.localFaces();
newPoints[surfI] = newSurf.localPoints();
visitedFace[surfI] = PackedBoolList(newSurf.size(), false);
}
forAll(newSurfaces, surfI)
{
const triSurface& surf = newSurfaces[surfI];
List<pointIndexHit> bPointsTobEdges(surf.boundaryPoints().size());
labelList bPointsHitTree(surf.boundaryPoints().size(), -1);
const labelListList& pointEdges = surf.pointEdges();
forAll(bPointsTobEdges, bPointI)
{
pointIndexHit& nearestHit = bPointsTobEdges[bPointI];
const label pointI = surf.boundaryPoints()[bPointI];
const point& samplePt = surf.localPoints()[pointI];
const labelList& pEdges = pointEdges[pointI];
// Add edges connected to the edge to the shapeMask
DynamicList<label> shapeMask;
shapeMask.append(pEdges);
forAll(bEdgeTrees, treeI)
{
const indexedOctree<treeDataEdge>& bEdgeTree =
bEdgeTrees[treeI];
pointIndexHit currentHit =
bEdgeTree.findNearest
(
samplePt,
sqr(dist),
findNearestOpSubset
(
bEdgeTree,
shapeMask
)
);
if
(
currentHit.hit()
&&
(
!nearestHit.hit()
||
(
magSqr(currentHit.hitPoint() - samplePt)
< magSqr(nearestHit.hitPoint() - samplePt)
)
)
)
{
nearestHit = currentHit;
bPointsHitTree[bPointI] = treeI;
}
}
scalar dist2 = magSqr(nearestHit.rawPoint() - samplePt);
if (nearestHit.hit())
{
// bool rejectEdge =
// checkEdgeAngle
// (
// surf,
// nearestHit.index(),
// pointI,
// 30
// );
if (dist2 > Foam::sqr(dist))
{
nearestHit.setMiss();
}
}
}
forAll(bPointsTobEdges, bPointI)
{
const pointIndexHit& eHit = bPointsTobEdges[bPointI];
if (eHit.hit())
{
const label hitSurfI = bPointsHitTree[bPointI];
const triSurface& hitSurf = newSurfaces[hitSurfI];
const label eIndex =
treeBoundaryEdges[hitSurfI][eHit.index()];
const edge& e = hitSurf.edges()[eIndex];
const label pointI = surf.boundaryPoints()[bPointI];
const labelList& eFaces = hitSurf.edgeFaces()[eIndex];
if (eFaces.size() != 1)
{
WarningIn(args.executable())
<< "Edge is attached to " << eFaces.size()
<< " faces." << endl;
continue;
}
const label faceI = eFaces[0];
if (visitedFace[hitSurfI][faceI])
{
continue;
}
DynamicList<labelledTri> newFacesFromSplit(2);
const point& pt = surf.localPoints()[pointI];
if
(
(
magSqr(pt - hitSurf.localPoints()[e.start()])
< matchTolerance
)
|| (
magSqr(pt - hitSurf.localPoints()[e.end()])
< matchTolerance
)
)
{
continue;
}
nChanged++;
label newPointI = -1;
// Keep the points in the same place and move the edge
if (hitSurfI == surfI)
{
newPointI = pointI;
}
else
{
newPoints[hitSurfI].append(newPoints[surfI][pointI]);
newPointI = newPoints[hitSurfI].size() - 1;
}
// Split the other face.
greenRefine
(
hitSurf,
faceI,
eIndex,
newPointI,
newFacesFromSplit
);
visitedFace[hitSurfI][faceI] = true;
forAll(newFacesFromSplit, newFaceI)
{
const labelledTri& fN = newFacesFromSplit[newFaceI];
if (newFaceI == 0)
{
newFaces[hitSurfI][faceI] = fN;
}
else
{
newFaces[hitSurfI].append(fN);
}
}
}
}
}
Info<< " Number of edges split = " << nChanged << endl;
forAll(newSurfaces, surfI)
{
newSurfaces.set
(
surfI,
new triSurfaceMesh
(
IOobject
(
"hookedSurface_" + surfs.names()[surfI],
runTime.constant(),
"triSurface",
runTime
),
triSurface
(
newFaces[surfI],
newSurfaces[surfI].patches(),
pointField(newPoints[surfI])
)
)
);
}
} while (nChanged > 0 && nIters <= maxIters);
Info<< endl;
forAll(newSurfaces, surfI)
{
const triSurfaceMesh& newSurf = newSurfaces[surfI];
Info<< "Writing hooked surface " << newSurf.searchableSurface::name()
<< endl;
newSurf.searchableSurface::write();
}
Info<< "\nEnd\n" << endl;
return 0;
}
// ************************************************************************* //

View File

@ -0,0 +1,22 @@
/*--------------------------------*- C++ -*----------------------------------*\
| ========= | |
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
| \\ / O peration | Version: dev |
| \\ / A nd | Web: www.OpenFOAM.org |
| \\/ M anipulation | |
\*---------------------------------------------------------------------------*/
FoamFile
{
version 2.0;
format ascii;
class dictionary;
location "system";
object surfaceHookUpDict;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
surface1.stl {type triSurfaceMesh;}
surface2.stl {type triSurfaceMesh;}
// ************************************************************************* //

View File

@ -0,0 +1,3 @@
surfaceInertia.C
EXE = $(FOAM_APPBIN)/surfaceInertia

View File

@ -0,0 +1,7 @@
EXE_INC = \
-I$(LIB_SRC)/meshTools/lnInclude \
-I$(LIB_SRC)/triSurface/lnInclude
EXE_LIBS = \
-lmeshTools \
-ltriSurface

View File

@ -0,0 +1,415 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2013 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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 <http://www.gnu.org/licenses/>.
Application
surfaceInertia
Description
Calculates the inertia tensor, principal axes and moments of a
command line specified triSurface. Inertia can either be of the
solid body or of a thin shell.
\*---------------------------------------------------------------------------*/
#include "argList.H"
#include "ListOps.H"
#include "triSurface.H"
#include "OFstream.H"
#include "meshTools.H"
#include "Random.H"
#include "transform.H"
#include "IOmanip.H"
#include "Pair.H"
#include "momentOfInertia.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
using namespace Foam;
int main(int argc, char *argv[])
{
argList::addNote
(
"Calculates the inertia tensor and principal axes and moments "
"of the specified surface.\n"
"Inertia can either be of the solid body or of a thin shell."
);
argList::noParallel();
argList::validArgs.append("surfaceFile");
argList::addBoolOption
(
"shellProperties",
"inertia of a thin shell"
);
argList::addOption
(
"density",
"scalar",
"Specify density, "
"kg/m3 for solid properties, kg/m2 for shell properties"
);
argList::addOption
(
"referencePoint",
"vector",
"Inertia relative to this point, not the centre of mass"
);
argList args(argc, argv);
const fileName surfFileName = args[1];
const scalar density = args.optionLookupOrDefault("density", 1.0);
vector refPt = vector::zero;
bool calcAroundRefPt = args.optionReadIfPresent("referencePoint", refPt);
triSurface surf(surfFileName);
scalar m = 0.0;
vector cM = vector::zero;
tensor J = tensor::zero;
if (args.optionFound("shellProperties"))
{
momentOfInertia::massPropertiesShell(surf, density, m, cM, J);
}
else
{
momentOfInertia::massPropertiesSolid(surf, density, m, cM, J);
}
if (m < 0)
{
WarningIn(args.executable() + "::main")
<< "Negative mass detected, the surface may be inside-out." << endl;
}
vector eVal = eigenValues(J);
tensor eVec = eigenVectors(J);
label pertI = 0;
Random rand(57373);
while ((magSqr(eVal) < VSMALL) && pertI < 10)
{
WarningIn(args.executable() + "::main")
<< "No eigenValues found, shape may have symmetry, "
<< "perturbing inertia tensor diagonal" << endl;
J.xx() *= 1.0 + SMALL*rand.scalar01();
J.yy() *= 1.0 + SMALL*rand.scalar01();
J.zz() *= 1.0 + SMALL*rand.scalar01();
eVal = eigenValues(J);
eVec = eigenVectors(J);
pertI++;
}
bool showTransform = true;
if
(
(mag(eVec.x() ^ eVec.y()) > (1.0 - SMALL))
&& (mag(eVec.y() ^ eVec.z()) > (1.0 - SMALL))
&& (mag(eVec.z() ^ eVec.x()) > (1.0 - SMALL))
)
{
// Make the eigenvectors a right handed orthogonal triplet
eVec = tensor
(
eVec.x(),
eVec.y(),
eVec.z() * sign((eVec.x() ^ eVec.y()) & eVec.z())
);
// Finding the most natural transformation. Using Lists
// rather than tensors to allow indexed permutation.
// Cartesian basis vectors - right handed orthogonal triplet
List<vector> cartesian(3);
cartesian[0] = vector(1, 0, 0);
cartesian[1] = vector(0, 1, 0);
cartesian[2] = vector(0, 0, 1);
// Principal axis basis vectors - right handed orthogonal
// triplet
List<vector> principal(3);
principal[0] = eVec.x();
principal[1] = eVec.y();
principal[2] = eVec.z();
scalar maxMagDotProduct = -GREAT;
// Matching axis indices, first: cartesian, second:principal
Pair<label> match(-1, -1);
forAll(cartesian, cI)
{
forAll(principal, pI)
{
scalar magDotProduct = mag(cartesian[cI] & principal[pI]);
if (magDotProduct > maxMagDotProduct)
{
maxMagDotProduct = magDotProduct;
match.first() = cI;
match.second() = pI;
}
}
}
scalar sense = sign
(
cartesian[match.first()] & principal[match.second()]
);
if (sense < 0)
{
// Invert the best match direction and swap the order of
// the other two vectors
List<vector> tPrincipal = principal;
tPrincipal[match.second()] *= -1;
tPrincipal[(match.second() + 1) % 3] =
principal[(match.second() + 2) % 3];
tPrincipal[(match.second() + 2) % 3] =
principal[(match.second() + 1) % 3];
principal = tPrincipal;
vector tEVal = eVal;
tEVal[(match.second() + 1) % 3] = eVal[(match.second() + 2) % 3];
tEVal[(match.second() + 2) % 3] = eVal[(match.second() + 1) % 3];
eVal = tEVal;
}
label permutationDelta = match.second() - match.first();
if (permutationDelta != 0)
{
// Add 3 to the permutationDelta to avoid negative indices
permutationDelta += 3;
List<vector> tPrincipal = principal;
vector tEVal = eVal;
for (label i = 0; i < 3; i++)
{
tPrincipal[i] = principal[(i + permutationDelta) % 3];
tEVal[i] = eVal[(i + permutationDelta) % 3];
}
principal = tPrincipal;
eVal = tEVal;
}
label matchedAlready = match.first();
match =Pair<label>(-1, -1);
maxMagDotProduct = -GREAT;
forAll(cartesian, cI)
{
if (cI == matchedAlready)
{
continue;
}
forAll(principal, pI)
{
if (pI == matchedAlready)
{
continue;
}
scalar magDotProduct = mag(cartesian[cI] & principal[pI]);
if (magDotProduct > maxMagDotProduct)
{
maxMagDotProduct = magDotProduct;
match.first() = cI;
match.second() = pI;
}
}
}
sense = sign
(
cartesian[match.first()] & principal[match.second()]
);
if (sense < 0 || (match.second() - match.first()) != 0)
{
principal[match.second()] *= -1;
List<vector> tPrincipal = principal;
tPrincipal[(matchedAlready + 1) % 3] =
principal[(matchedAlready + 2) % 3]*-sense;
tPrincipal[(matchedAlready + 2) % 3] =
principal[(matchedAlready + 1) % 3]*-sense;
principal = tPrincipal;
vector tEVal = eVal;
tEVal[(matchedAlready + 1) % 3] = eVal[(matchedAlready + 2) % 3];
tEVal[(matchedAlready + 2) % 3] = eVal[(matchedAlready + 1) % 3];
eVal = tEVal;
}
eVec = tensor(principal[0], principal[1], principal[2]);
// {
// tensor R = rotationTensor(vector(1, 0, 0), eVec.x());
// R = rotationTensor(R & vector(0, 1, 0), eVec.y()) & R;
// Info<< "R = " << nl << R << endl;
// Info<< "R - eVec.T() " << R - eVec.T() << endl;
// }
}
else
{
WarningIn(args.executable() + "::main")
<< "Non-unique eigenvectors, cannot compute transformation "
<< "from Cartesian axes" << endl;
showTransform = false;
}
// calculate the total surface area
scalar surfaceArea = 0;
forAll(surf, faceI)
{
const labelledTri& f = surf[faceI];
if (f[0] == f[1] || f[0] == f[2] || f[1] == f[2])
{
WarningIn(args.executable())
<< "Illegal triangle " << faceI << " vertices " << f
<< " coords " << f.points(surf.points()) << endl;
}
else
{
surfaceArea += triPointRef
(
surf.points()[f[0]],
surf.points()[f[1]],
surf.points()[f[2]]
).mag();
}
}
Info<< nl << setprecision(12)
<< "Density: " << density << nl
<< "Mass: " << m << nl
<< "Centre of mass: " << cM << nl
<< "Surface area: " << surfaceArea << nl
<< "Inertia tensor around centre of mass: " << nl << J << nl
<< "eigenValues (principal moments): " << eVal << nl
<< "eigenVectors (principal axes): " << nl
<< eVec.x() << nl << eVec.y() << nl << eVec.z() << endl;
if (showTransform)
{
Info<< "Transform tensor from reference state (orientation):" << nl
<< eVec.T() << nl
<< "Rotation tensor required to transform "
"from the body reference frame to the global "
"reference frame, i.e.:" << nl
<< "globalVector = orientation & bodyLocalVector"
<< endl;
Info<< nl
<< "Entries for sixDoFRigidBodyDisplacement boundary condition:"
<< nl
<< " mass " << m << token::END_STATEMENT << nl
<< " centreOfMass " << cM << token::END_STATEMENT << nl
<< " momentOfInertia " << eVal << token::END_STATEMENT << nl
<< " orientation " << eVec.T() << token::END_STATEMENT
<< endl;
}
if (calcAroundRefPt)
{
Info<< nl << "Inertia tensor relative to " << refPt << ": " << nl
<< momentOfInertia::applyParallelAxisTheorem(m, cM, J, refPt)
<< endl;
}
OFstream str("axes.obj");
Info<< nl << "Writing scaled principal axes at centre of mass of "
<< surfFileName << " to " << str.name() << endl;
scalar scale = mag(cM - surf.points()[0])/eVal.component(findMin(eVal));
meshTools::writeOBJ(str, cM);
meshTools::writeOBJ(str, cM + scale*eVal.x()*eVec.x());
meshTools::writeOBJ(str, cM + scale*eVal.y()*eVec.y());
meshTools::writeOBJ(str, cM + scale*eVal.z()*eVec.z());
for (label i = 1; i < 4; i++)
{
str << "l " << 1 << ' ' << i + 1 << endl;
}
Info<< nl << "End" << nl << endl;
return 0;
}
// ************************************************************************* //

View File

@ -0,0 +1,3 @@
surfaceLambdaMuSmooth.C
EXE = $(FOAM_APPBIN)/surfaceLambdaMuSmooth

View File

@ -0,0 +1,7 @@
EXE_INC = \
-I$(LIB_SRC)/surfMesh/lnInclude \
-I$(LIB_SRC)/edgeMesh/lnInclude
EXE_LIBS = \
-ledgeMesh \
-lsurfMesh

View File

@ -0,0 +1,237 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2013 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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 <http://www.gnu.org/licenses/>.
Application
surfaceLambdaMuSmooth
Description
Smooths a surface using lambda/mu smoothing.
To get laplacian smoothing, set lambda to the relaxation factor and mu to
zero.
Provide an edgeMesh file containing points that are not to be moved during
smoothing in order to preserve features.
lambda/mu smoothing: G. Taubin, IBM Research report Rc-19923 (02/01/95)
"A signal processing approach to fair surface design"
\*---------------------------------------------------------------------------*/
#include "argList.H"
#include "boundBox.H"
#include "edgeMesh.H"
#include "matchPoints.H"
#include "MeshedSurfaces.H"
using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
tmp<pointField> avg
(
const meshedSurface& s,
const PackedBoolList& fixedPoints
)
{
const labelListList& pointEdges = s.pointEdges();
tmp<pointField> tavg(new pointField(s.nPoints(), vector::zero));
pointField& avg = tavg();
forAll(pointEdges, vertI)
{
vector& avgPos = avg[vertI];
if (fixedPoints[vertI])
{
avgPos = s.localPoints()[vertI];
}
else
{
const labelList& pEdges = pointEdges[vertI];
forAll(pEdges, myEdgeI)
{
const edge& e = s.edges()[pEdges[myEdgeI]];
label otherVertI = e.otherVertex(vertI);
avgPos += s.localPoints()[otherVertI];
}
avgPos /= pEdges.size();
}
}
return tavg;
}
void getFixedPoints
(
const edgeMesh& feMesh,
const pointField& points,
PackedBoolList& fixedPoints
)
{
scalarList matchDistance(feMesh.points().size(), 1e-1);
labelList from0To1;
bool matchedAll = matchPoints
(
feMesh.points(),
points,
matchDistance,
false,
from0To1
);
if (!matchedAll)
{
WarningIn("getFixedPoints(const edgeMesh&, const pointField&)")
<< "Did not match all feature points to points on the surface"
<< endl;
}
forAll(from0To1, fpI)
{
if (from0To1[fpI] != -1)
{
fixedPoints[from0To1[fpI]] = true;
}
}
}
// Main program:
int main(int argc, char *argv[])
{
argList::noParallel();
argList::validOptions.clear();
argList::validArgs.append("surfaceFile");
argList::validArgs.append("lambda (0..1)");
argList::validArgs.append("mu (0..1)");
argList::validArgs.append("iterations");
argList::validArgs.append("output surfaceFile");
argList::addOption
(
"featureFile",
"fix points from a file containing feature points and edges"
);
argList args(argc, argv);
const fileName surfFileName = args[1];
const scalar lambda = args.argRead<scalar>(2);
const scalar mu = args.argRead<scalar>(3);
const label iters = args.argRead<label>(4);
const fileName outFileName = args[5];
if (lambda < 0 || lambda > 1)
{
FatalErrorIn(args.executable()) << "Illegal relaxation factor "
<< lambda << endl
<< "0: no change 1: move vertices to average of neighbours"
<< exit(FatalError);
}
if (mu < 0 || mu > 1)
{
FatalErrorIn(args.executable()) << "Illegal relaxation factor "
<< mu << endl
<< "0: no change 1: move vertices to average of neighbours"
<< exit(FatalError);
}
Info<< "lambda : " << lambda << nl
<< "mu : " << mu << nl
<< "Iters : " << iters << nl
<< "Reading surface from " << surfFileName << " ..." << endl;
meshedSurface surf1(surfFileName);
Info<< "Faces : " << surf1.size() << nl
<< "Vertices : " << surf1.nPoints() << nl
<< "Bounding Box: " << boundBox(surf1.localPoints()) << endl;
PackedBoolList fixedPoints(surf1.localPoints().size(), false);
if (args.optionFound("featureFile"))
{
const fileName featureFileName(args.option("featureFile"));
Info<< "Reading features from " << featureFileName << " ..." << endl;
edgeMesh feMesh(featureFileName);
getFixedPoints(feMesh, surf1.localPoints(), fixedPoints);
Info<< "Number of fixed points on surface = " << fixedPoints.count()
<< endl;
}
pointField newPoints(surf1.localPoints());
for (label iter = 0; iter < iters; iter++)
{
// Lambda
{
pointField newLocalPoints
(
(1 - lambda)*surf1.localPoints()
+ lambda*avg(surf1, fixedPoints)
);
pointField newPoints(surf1.points());
UIndirectList<point>(newPoints, surf1.meshPoints()) =
newLocalPoints;
surf1.movePoints(newPoints);
}
// Mu
if (mu != 0)
{
pointField newLocalPoints
(
(1 + mu)*surf1.localPoints()
- mu*avg(surf1, fixedPoints)
);
pointField newPoints(surf1.points());
UIndirectList<point>(newPoints, surf1.meshPoints()) =
newLocalPoints;
surf1.movePoints(newPoints);
}
}
Info<< "Writing surface to " << outFileName << " ..." << endl;
surf1.write(outFileName);
Info<< "End\n" << endl;
return 0;
}
// ************************************************************************* //

View File

@ -0,0 +1,3 @@
surfaceMeshConvert.C
EXE = $(FOAM_APPBIN)/surfaceMeshConvert

View File

@ -0,0 +1,5 @@
EXE_INC = \
-I$(LIB_SRC)/meshTools/lnInclude \
-I$(LIB_SRC)/surfMesh/lnInclude
EXE_LIBS = -lmeshTools -lsurfMesh

View File

@ -0,0 +1,83 @@
/*--------------------------------*- C++ -*----------------------------------*\
| ========= | |
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
| \\ / O peration | Version: dev |
| \\ / A nd | Web: www.OpenFOAM.org |
| \\/ M anipulation | |
\*---------------------------------------------------------------------------*/
FoamFile
{
version 2.0;
format ascii;
class IOPtrList<coordinateSystem>;
object coordinateSystems;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
7
(
system_9
{
type cartesian;
origin (1.03291515 -0.114391257 -0.0826236662);
e3 (1 0 0);
e1 (0 1 0);
// STARCDRotation (0 90 90);
}
system_10
{
type cartesian;
origin (0.623151719 -0.286472935 -0.113933262);
e3 (0.99508851 0.09829095 0.01173645);
e1 (0.01179356 0 -0.99993045);
// STARCDRotation (5.6403745 -0.0664172952 89.3275351);
}
system_15
{
type cartesian;
origin (0.644772231 -0.240036493 0.155972187);
e3 (-0.01346388 -0.90616979 -0.42269969);
e1 (0.00627978 0.42265304 -0.90626981);
// STARCDRotation (-90.8512386 0 115.005148);
}
system_16
{
type cartesian;
origin (0.540824938 -0.240036415 0.15928296);
e3 (-0.01346388 -0.90616979 -0.42269969);
e1 (0.00627978 0.42265304 -0.90626981);
// STARCDRotation (-90.8512386 0 115.005148);
}
system_17
{
type cartesian;
origin (0.436877646 -0.240036339 0.162593737);
e3 (-0.01346388 -0.90616979 -0.42269969);
e1 (0.00627978 0.42265304 -0.90626981);
// STARCDRotation (-90.8512386 0 115.005148);
}
system_18
{
type cartesian;
origin (0.332930354 -0.240036261 0.16590451);
e3 (-0.01346388 -0.90616979 -0.42269969);
e1 (0.00627978 0.42265304 -0.90626981);
// STARCDRotation (-90.8512386 0 115.005148);
}
system_21
{
type cartesian;
origin (0.55863733 -0.300866705 0.00317260982);
e3 (0.42110287 0.02470132 -0.90667647);
e1 (0.90646036 0.02342535 0.42164069);
// STARCDRotation (-178.185897 -0.71772221 -155.059695);
}
)
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

View File

@ -0,0 +1,302 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2013 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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 <http://www.gnu.org/licenses/>.
Application
surfaceMeshConvert
Description
Converts between surface formats with optional scaling or
transformations (rotate/translate) on a coordinateSystem.
Usage
- surfaceMeshConvert inputFile outputFile [OPTION]
\param -clean \n
Perform some surface checking/cleanup on the input surface.
\param -scaleIn \<scale\> \n
Specify a scaling factor when reading files.
\param -scaleOut \<scale\> \n
Specify a scaling factor when writing files.
\param -dict \<dictionary\> \n
Specify an alternative dictionary for constant/coordinateSystems.
\param -from \<coordinateSystem\> \n
Specify a coordinate System when reading files.
\param -to \<coordinateSystem\> \n
Specify a coordinate System when writing files.
\param -tri \n
Triangulate surface.
Note
The filename extensions are used to determine the file format type.
\*---------------------------------------------------------------------------*/
#include "argList.H"
#include "Time.H"
#include "MeshedSurfaces.H"
#include "coordinateSystems.H"
using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
argList::addNote
(
"convert between surface formats"
);
argList::noParallel();
argList::validArgs.append("inputFile");
argList::validArgs.append("outputFile");
argList::addBoolOption
(
"clean",
"perform some surface checking/cleanup on the input surface"
);
argList::addOption
(
"scaleIn",
"factor",
"geometry scaling factor on input"
);
argList::addOption
(
"scaleOut",
"factor",
"geometry scaling factor on output"
);
#include "addDictOption.H"
argList::addOption
(
"from",
"system",
"specify the source coordinate system, applied after '-scaleIn'"
);
argList::addOption
(
"to",
"system",
"specify the target coordinate system, applied before '-scaleOut'"
);
argList::addBoolOption
(
"tri",
"triangulate surface"
);
argList args(argc, argv);
Time runTime(args.rootPath(), args.caseName());
const fileName importName = args[1];
const fileName exportName = args[2];
// disable inplace editing
if (importName == exportName)
{
FatalErrorIn(args.executable())
<< "Output file " << exportName << " would overwrite input file."
<< exit(FatalError);
}
// check that reading/writing is supported
if
(
!MeshedSurface<face>::canRead(importName, true)
|| !MeshedSurface<face>::canWriteType(exportName.ext(), true)
)
{
return 1;
}
// get the coordinate transformations
autoPtr<coordinateSystem> fromCsys;
autoPtr<coordinateSystem> toCsys;
if (args.optionFound("from") || args.optionFound("to"))
{
autoPtr<IOobject> csDictIoPtr;
const word dictName("coordinateSystems::typeName");
// Note: cannot use setSystemRunTimeDictionaryIO.H since dictionary
// is in constant
fileName dictPath = "";
if (args.optionFound("dict"))
{
dictPath = args["dict"];
if (isDir(dictPath))
{
dictPath = dictPath / dictName;
}
}
if (dictPath.size())
{
csDictIoPtr.set
(
new IOobject
(
dictPath,
runTime,
IOobject::MUST_READ,
IOobject::NO_WRITE,
false
)
);
}
else
{
csDictIoPtr.set
(
new IOobject
(
dictName,
runTime.constant(),
runTime,
IOobject::MUST_READ,
IOobject::NO_WRITE,
false
)
);
}
if (!csDictIoPtr->headerOk())
{
FatalErrorIn(args.executable())
<< "Cannot open coordinateSystems file\n "
<< csDictIoPtr->objectPath() << nl
<< exit(FatalError);
}
coordinateSystems csLst(csDictIoPtr());
if (args.optionFound("from"))
{
const word csName = args["from"];
const label csIndex = csLst.findIndex(csName);
if (csIndex < 0)
{
FatalErrorIn(args.executable())
<< "Cannot find -from " << csName << nl
<< "available coordinateSystems: " << csLst.toc() << nl
<< exit(FatalError);
}
fromCsys.reset(new coordinateSystem(csLst[csIndex]));
}
if (args.optionFound("to"))
{
const word csName = args["to"];
const label csIndex = csLst.findIndex(csName);
if (csIndex < 0)
{
FatalErrorIn(args.executable())
<< "Cannot find -to " << csName << nl
<< "available coordinateSystems: " << csLst.toc() << nl
<< exit(FatalError);
}
toCsys.reset(new coordinateSystem(csLst[csIndex]));
}
// maybe fix this later
if (fromCsys.valid() && toCsys.valid())
{
FatalErrorIn(args.executable())
<< "Only allowed '-from' or '-to' option at the moment."
<< exit(FatalError);
}
}
{
MeshedSurface<face> surf(importName);
if (args.optionFound("clean"))
{
surf.cleanup(true);
}
scalar scaleIn = 0;
if (args.optionReadIfPresent("scaleIn", scaleIn) && scaleIn > 0)
{
Info<< " -scaleIn " << scaleIn << endl;
surf.scalePoints(scaleIn);
}
if (fromCsys.valid())
{
Info<< " -from " << fromCsys().name() << endl;
tmp<pointField> tpf = fromCsys().localPosition(surf.points());
surf.movePoints(tpf());
}
if (toCsys.valid())
{
Info<< " -to " << toCsys().name() << endl;
tmp<pointField> tpf = toCsys().globalPosition(surf.points());
surf.movePoints(tpf());
}
scalar scaleOut = 0;
if (args.optionReadIfPresent("scaleOut", scaleOut) && scaleOut > 0)
{
Info<< " -scaleOut " << scaleOut << endl;
surf.scalePoints(scaleOut);
}
if (args.optionFound("tri"))
{
Info<< "triangulate" << endl;
surf.triangulate();
}
Info<< "writing " << exportName;
surf.write(exportName);
}
Info<< "\nEnd\n" << endl;
return 0;
}
// ************************************************************************* //

View File

@ -0,0 +1,3 @@
surfaceMeshConvertTesting.C
EXE = $(FOAM_APPBIN)/surfaceMeshConvertTesting

View File

@ -0,0 +1,5 @@
EXE_INC = \
-I$(LIB_SRC)/triSurface/lnInclude \
-I$(LIB_SRC)/surfMesh/lnInclude
EXE_LIBS = -ltriSurface -lsurfMesh

View File

@ -0,0 +1,424 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2013 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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 <http://www.gnu.org/licenses/>.
Application
surfaceMeshConvertTesting
Description
Converts from one surface mesh format to another, but primarily
used for testing functionality.
Usage
- surfaceMeshConvertTesting inputFile outputFile [OPTION]
\param -clean \n
Perform some surface checking/cleanup on the input surface
\param -orient \n
Check face orientation on the input surface
\param -scale \<scale\> \n
Specify a scaling factor for writing the files
\param -triSurface \n
Use triSurface library for input/output
\param -keyed \n
Use keyedSurface for input/output
Note
The filename extensions are used to determine the file format type.
\*---------------------------------------------------------------------------*/
#include "argList.H"
#include "Time.H"
#include "polyMesh.H"
#include "triSurface.H"
#include "surfMesh.H"
#include "surfFields.H"
#include "surfPointFields.H"
#include "PackedBoolList.H"
#include "MeshedSurfaces.H"
#include "UnsortedMeshedSurfaces.H"
using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
argList::addNote
(
"convert between surface formats, "
"but primarily for testing functionality\n"
"Normally use surfaceMeshConvert instead."
);
argList::noParallel();
argList::validArgs.append("inputFile");
argList::validArgs.append("outputFile");
argList::addBoolOption("clean");
argList::addBoolOption("orient");
argList::addBoolOption("surfMesh");
argList::addBoolOption("triSurface");
argList::addBoolOption("unsorted");
argList::addBoolOption("triFace");
argList::addOption
(
"scale",
"factor",
"geometry scaling factor - default is 1"
);
# include "setRootCase.H"
const scalar scaleFactor = args.optionLookupOrDefault("scale", 0.0);
const fileName importName = args[1];
const fileName exportName = args[2];
if (importName == exportName)
{
FatalErrorIn(args.executable())
<< "Output file " << exportName << " would overwrite input file."
<< exit(FatalError);
}
if
(
!MeshedSurface<face>::canRead(importName, true)
|| !MeshedSurface<face>::canWriteType(exportName.ext(), true)
)
{
return 1;
}
if (args.optionFound("triSurface"))
{
triSurface surf(importName);
Info<< "Read surface:" << endl;
surf.writeStats(Info);
Info<< endl;
if (args.optionFound("orient"))
{
Info<< "Checking surface orientation" << endl;
PatchTools::checkOrientation(surf, true);
Info<< endl;
}
if (args.optionFound("clean"))
{
Info<< "Cleaning up surface" << endl;
surf.cleanup(true);
surf.writeStats(Info);
Info<< endl;
}
Info<< "writing " << exportName;
if (scaleFactor <= 0)
{
Info<< " without scaling" << endl;
}
else
{
Info<< " with scaling " << scaleFactor << endl;
surf.scalePoints(scaleFactor);
surf.writeStats(Info);
Info<< endl;
}
// write sorted by region
surf.write(exportName, true);
}
else if (args.optionFound("unsorted"))
{
UnsortedMeshedSurface<face> surf(importName);
Info<< "Read surface:" << endl;
surf.writeStats(Info);
Info<< endl;
if (args.optionFound("orient"))
{
Info<< "Checking surface orientation" << endl;
PatchTools::checkOrientation(surf, true);
Info<< endl;
}
if (args.optionFound("clean"))
{
Info<< "Cleaning up surface" << endl;
surf.cleanup(true);
surf.writeStats(Info);
Info<< endl;
}
Info<< "writing " << exportName;
if (scaleFactor <= 0)
{
Info<< " without scaling" << endl;
}
else
{
Info<< " with scaling " << scaleFactor << endl;
surf.scalePoints(scaleFactor);
surf.writeStats(Info);
Info<< endl;
}
surf.write(exportName);
}
#if 1
else if (args.optionFound("triFace"))
{
MeshedSurface<triFace> surf(importName);
Info<< "Read surface:" << endl;
surf.writeStats(Info);
Info<< endl;
if (args.optionFound("orient"))
{
Info<< "Checking surface orientation" << endl;
PatchTools::checkOrientation(surf, true);
Info<< endl;
}
if (args.optionFound("clean"))
{
Info<< "Cleaning up surface" << endl;
surf.cleanup(true);
surf.writeStats(Info);
Info<< endl;
}
Info<< "writing " << exportName;
if (scaleFactor <= 0)
{
Info<< " without scaling" << endl;
}
else
{
Info<< " with scaling " << scaleFactor << endl;
surf.scalePoints(scaleFactor);
surf.writeStats(Info);
Info<< endl;
}
surf.write(exportName);
}
#endif
else
{
MeshedSurface<face> surf(importName);
Info<< "Read surface:" << endl;
surf.writeStats(Info);
Info<< endl;
if (args.optionFound("orient"))
{
Info<< "Checking surface orientation" << endl;
PatchTools::checkOrientation(surf, true);
Info<< endl;
}
if (args.optionFound("clean"))
{
Info<< "Cleaning up surface" << endl;
surf.cleanup(true);
surf.writeStats(Info);
Info<< endl;
}
Info<< "writing " << exportName;
if (scaleFactor <= 0)
{
Info<< " without scaling" << endl;
}
else
{
Info<< " with scaling " << scaleFactor << endl;
surf.scalePoints(scaleFactor);
surf.writeStats(Info);
Info<< endl;
}
surf.write(exportName);
if (args.optionFound("surfMesh"))
{
Foam::Time runTime
(
args.rootPath(),
args.caseName()
);
// start with "constant"
runTime.setTime(instant(0, runTime.constant()), 0);
Info<< "runTime.instance() = " << runTime.instance() << endl;
Info<< "runTime.timeName() = " << runTime.timeName() << endl;
Info<< "write MeshedSurface 'yetAnother' via proxy as surfMesh"
<< endl;
surf.write
(
runTime,
"yetAnother"
);
surfMesh surfIn
(
IOobject
(
"default",
runTime.timeName(),
runTime,
IOobject::MUST_READ,
IOobject::NO_WRITE
)
);
MeshedSurface<face> surfIn2(runTime, "foobar");
Info<<"surfIn2 = " << surfIn2.size() << endl;
Info<< "surfIn = " << surfIn.size() << endl;
Info<< "writing surfMesh as obj = oldSurfIn.obj" << endl;
surfIn.write("oldSurfIn.obj");
Info<< "runTime.instance() = " << runTime.instance() << endl;
surfMesh surfOut
(
IOobject
(
"mySurf",
runTime.instance(),
runTime,
IOobject::NO_READ,
IOobject::NO_WRITE,
false
),
surf.xfer()
);
Info<< "writing surfMesh as well: " << surfOut.objectPath() << endl;
surfOut.write();
surfLabelField zoneIds
(
IOobject
(
"zoneIds",
surfOut.instance(),
surfOut,
IOobject::NO_READ,
IOobject::NO_WRITE
),
surfOut,
dimless
);
Info<<" surf name= " << surfOut.name() <<nl;
Info<< "rename to anotherSurf" << endl;
surfOut.rename("anotherSurf");
Info<<" surf name= " << surfOut.name() <<nl;
// advance time to 1
runTime.setTime(instant(1), 1);
surfOut.setInstance(runTime.timeName());
Info<< "writing surfMesh again well: " << surfOut.objectPath()
<< endl;
surfOut.write();
// write directly
surfOut.write("someName.ofs");
#if 1
const surfZoneList& zones = surfOut.surfZones();
forAll(zones, zoneI)
{
SubList<label>
(
zoneIds,
zones[zoneI].size(),
zones[zoneI].start()
) = zoneI;
}
Info<< "write zoneIds (for testing only): "
<< zoneIds.objectPath() << endl;
zoneIds.write();
surfPointLabelField pointIds
(
IOobject
(
"zoneIds.",
// "pointIds",
surfOut.instance(),
// "pointFields",
surfOut,
IOobject::NO_READ,
IOobject::NO_WRITE
),
surfOut,
dimless
);
forAll(pointIds, i)
{
pointIds[i] = i;
}
Info<< "write pointIds (for testing only): "
<< pointIds.objectPath() << endl;
pointIds.write();
Info<<"surfMesh with these names: " << surfOut.names() << endl;
#endif
}
}
Info<< "\nEnd\n" << endl;
return 0;
}
// ************************************************************************* //

View File

@ -0,0 +1,3 @@
surfaceMeshExport.C
EXE = $(FOAM_APPBIN)/surfaceMeshExport

View File

@ -0,0 +1,5 @@
EXE_INC = \
-I$(LIB_SRC)/meshTools/lnInclude \
-I$(LIB_SRC)/surfMesh/lnInclude
EXE_LIBS = -lmeshTools -lsurfMesh

View File

@ -0,0 +1,294 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2013 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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 <http://www.gnu.org/licenses/>.
Application
surfaceMeshExport
Description
Export from surfMesh to various third-party surface formats with
optional scaling or transformations (rotate/translate) on a
coordinateSystem.
Usage
- surfaceMeshExport outputFile [OPTION]
\param -clean \n
Perform some surface checking/cleanup on the input surface.
\param -name \<name\> \n
Specify an alternative surface name when writing.
\param -scaleIn \<scale\> \n
Specify a scaling factor when reading files.
\param -scaleOut \<scale\> \n
Specify a scaling factor when writing files.
\param -dict \<dictionary\> \n
Specify an alternative dictionary for constant/coordinateSystems.
\param -from \<coordinateSystem\> \n
Specify a coordinate system when reading files.
\param -to \<coordinateSystem\> \n
Specify a coordinate system when writing files.
Note
The filename extensions are used to determine the file format type.
\*---------------------------------------------------------------------------*/
#include "argList.H"
#include "Time.H"
#include "MeshedSurfaces.H"
#include "coordinateSystems.H"
using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
argList::addNote
(
"export from surfMesh to various third-party surface formats"
);
argList::noParallel();
argList::validArgs.append("outputFile");
argList::addBoolOption
(
"clean",
"perform some surface checking/cleanup on the input surface"
);
argList::addOption
(
"name",
"name",
"specify an alternative surface name when reading - "
"default is 'default'"
);
argList::addOption
(
"scaleIn",
"factor",
"geometry scaling factor on input - default is 1"
);
argList::addOption
(
"scaleOut",
"factor",
"geometry scaling factor on output - default is 1"
);
#include "addDictOption.H"
argList::addOption
(
"from",
"coordinateSystem",
"specify the source coordinate system, applied after '-scaleIn'"
);
argList::addOption
(
"to",
"coordinateSystem",
"specify the target coordinate system, applied before '-scaleOut'"
);
argList args(argc, argv);
Time runTime(args.rootPath(), args.caseName());
const fileName exportName = args[1];
const word importName = args.optionLookupOrDefault<word>("name", "default");
// check that writing is supported
if (!MeshedSurface<face>::canWriteType(exportName.ext(), true))
{
return 1;
}
// get the coordinate transformations
autoPtr<coordinateSystem> fromCsys;
autoPtr<coordinateSystem> toCsys;
if (args.optionFound("from") || args.optionFound("to"))
{
autoPtr<IOobject> ioPtr;
if (args.optionFound("dict"))
{
const fileName dictPath = args["dict"];
ioPtr.set
(
new IOobject
(
(
isDir(dictPath)
? dictPath/coordinateSystems::typeName
: dictPath
),
runTime,
IOobject::MUST_READ,
IOobject::NO_WRITE,
false
)
);
}
else
{
ioPtr.set
(
new IOobject
(
coordinateSystems::typeName,
runTime.constant(),
runTime,
IOobject::MUST_READ,
IOobject::NO_WRITE,
false
)
);
}
if (!ioPtr->headerOk())
{
FatalErrorIn(args.executable())
<< "Cannot open coordinateSystems file\n "
<< ioPtr->objectPath() << nl
<< exit(FatalError);
}
coordinateSystems csLst(ioPtr());
if (args.optionFound("from"))
{
const word csName = args["from"];
const label csIndex = csLst.findIndex(csName);
if (csIndex < 0)
{
FatalErrorIn(args.executable())
<< "Cannot find -from " << csName << nl
<< "available coordinateSystems: " << csLst.toc() << nl
<< exit(FatalError);
}
fromCsys.reset(new coordinateSystem(csLst[csIndex]));
}
if (args.optionFound("to"))
{
const word csName = args["to"];
const label csIndex = csLst.findIndex(csName);
if (csIndex < 0)
{
FatalErrorIn(args.executable())
<< "Cannot find -to " << csName << nl
<< "available coordinateSystems: " << csLst.toc() << nl
<< exit(FatalError);
}
toCsys.reset(new coordinateSystem(csLst[csIndex]));
}
// maybe fix this later
if (fromCsys.valid() && toCsys.valid())
{
FatalErrorIn(args.executable())
<< "Only allowed '-from' or '-to' option at the moment."
<< exit(FatalError);
}
}
surfMesh smesh
(
IOobject
(
importName,
runTime.constant(),
runTime,
IOobject::MUST_READ_IF_MODIFIED,
IOobject::NO_WRITE
)
);
Info<< "read surfMesh:\n " << smesh.objectPath() << endl;
// Simply copy for now, but really should have a separate write method
MeshedSurface<face> surf(smesh);
if (args.optionFound("clean"))
{
surf.cleanup(true);
}
scalar scaleIn = 0;
if (args.optionReadIfPresent("scaleIn", scaleIn) && scaleIn > 0)
{
Info<< " -scaleIn " << scaleIn << endl;
surf.scalePoints(scaleIn);
}
if (fromCsys.valid())
{
Info<< " -from " << fromCsys().name() << endl;
tmp<pointField> tpf = fromCsys().localPosition(surf.points());
surf.movePoints(tpf());
}
if (toCsys.valid())
{
Info<< " -to " << toCsys().name() << endl;
tmp<pointField> tpf = toCsys().globalPosition(surf.points());
surf.movePoints(tpf());
}
scalar scaleOut = 0;
if (args.optionReadIfPresent("scaleOut", scaleOut) && scaleOut > 0)
{
Info<< " -scaleOut " << scaleOut << endl;
surf.scalePoints(scaleOut);
}
surf.writeStats(Info);
Info<< endl;
Info<< "writing " << exportName << endl;
surf.write(exportName);
Info<< "\nEnd\n" << endl;
return 0;
}
// ************************************************************************* //

View File

@ -0,0 +1,3 @@
surfaceMeshImport.C
EXE = $(FOAM_APPBIN)/surfaceMeshImport

View File

@ -0,0 +1,5 @@
EXE_INC = \
-I$(LIB_SRC)/meshTools/lnInclude \
-I$(LIB_SRC)/surfMesh/lnInclude
EXE_LIBS = -lmeshTools -lsurfMesh

View File

@ -0,0 +1,300 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2013 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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 <http://www.gnu.org/licenses/>.
Application
surfaceMeshImport
Description
Import from various third-party surface formats into surfMesh
with optional scaling or transformations (rotate/translate)
on a coordinateSystem.
Usage
- surfaceMeshImport inputFile [OPTION]
\param -clean \n
Perform some surface checking/cleanup on the input surface.
\param -name \<name\> \n
Specify an alternative surface name when writing.
\param -scaleIn \<scale\> \n
Specify a scaling factor when reading files.
\param -scaleOut \<scale\> \n
Specify a scaling factor when writing files.
\param -dict \<dictionary\> \n
Specify an alternative dictionary for constant/coordinateSystems.
\param -from \<coordinateSystem\> \n
Specify a coordinate system when reading files.
\param -to \<coordinateSystem\> \n
Specify a coordinate system when writing files.
Note
The filename extensions are used to determine the file format type.
\*---------------------------------------------------------------------------*/
#include "argList.H"
#include "Time.H"
#include "MeshedSurfaces.H"
#include "coordinateSystems.H"
using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
argList::addNote
(
"import from various third-party surface formats into surfMesh"
);
argList::noParallel();
argList::validArgs.append("inputFile");
argList::addBoolOption
(
"clean",
"perform some surface checking/cleanup on the input surface"
);
argList::addOption
(
"name",
"name",
"specify an alternative surface name when writing - "
"default is 'default'"
);
argList::addOption
(
"scaleIn",
"factor",
"geometry scaling factor on input - default is 1"
);
argList::addOption
(
"scaleOut",
"factor",
"geometry scaling factor on output - default is 1"
);
#include "addDictOption.H"
argList::addOption
(
"from",
"coordinateSystem",
"specify a local coordinate system when reading files."
);
argList::addOption
(
"to",
"coordinateSystem",
"specify a local coordinate system when writing files."
);
#include "setRootCase.H"
#include "createTime.H"
// try for the latestTime, but create "constant" as needed
instantList Times = runTime.times();
if (Times.size())
{
label startTime = Times.size()-1;
runTime.setTime(Times[startTime], startTime);
}
else
{
runTime.setTime(instant(0, runTime.constant()), 0);
}
const fileName importName = args[1];
const word exportName = args.optionLookupOrDefault<word>("name", "default");
// check that reading is supported
if (!MeshedSurface<face>::canRead(importName, true))
{
return 1;
}
// get the coordinate transformations
autoPtr<coordinateSystem> fromCsys;
autoPtr<coordinateSystem> toCsys;
if (args.optionFound("from") || args.optionFound("to"))
{
autoPtr<IOobject> ioPtr;
if (args.optionFound("dict"))
{
const fileName dictPath = args["dict"];
ioPtr.set
(
new IOobject
(
(
isDir(dictPath)
? dictPath/coordinateSystems::typeName
: dictPath
),
runTime,
IOobject::MUST_READ,
IOobject::NO_WRITE,
false
)
);
}
else
{
ioPtr.set
(
new IOobject
(
coordinateSystems::typeName,
runTime.constant(),
runTime,
IOobject::MUST_READ,
IOobject::NO_WRITE,
false
)
);
}
if (!ioPtr->headerOk())
{
FatalErrorIn(args.executable())
<< "Cannot open coordinateSystems file\n "
<< ioPtr->objectPath() << nl
<< exit(FatalError);
}
coordinateSystems csLst(ioPtr());
if (args.optionFound("from"))
{
const word csName = args["from"];
const label csIndex = csLst.findIndex(csName);
if (csIndex < 0)
{
FatalErrorIn(args.executable())
<< "Cannot find -from " << csName << nl
<< "available coordinateSystems: " << csLst.toc() << nl
<< exit(FatalError);
}
fromCsys.reset(new coordinateSystem(csLst[csIndex]));
}
if (args.optionFound("to"))
{
const word csName = args["to"];
const label csIndex = csLst.findIndex(csName);
if (csIndex < 0)
{
FatalErrorIn(args.executable())
<< "Cannot find -to " << csName << nl
<< "available coordinateSystems: " << csLst.toc() << nl
<< exit(FatalError);
}
toCsys.reset(new coordinateSystem(csLst[csIndex]));
}
// maybe fix this later
if (fromCsys.valid() && toCsys.valid())
{
FatalErrorIn(args.executable())
<< "Only allowed '-from' or '-to' option at the moment."
<< exit(FatalError);
}
}
MeshedSurface<face> surf(importName);
if (args.optionFound("clean"))
{
surf.cleanup(true);
}
scalar scaleIn = 0;
if (args.optionReadIfPresent("scaleIn", scaleIn) && scaleIn > 0)
{
Info<< " -scaleIn " << scaleIn << endl;
surf.scalePoints(scaleIn);
}
if (fromCsys.valid())
{
Info<< " -from " << fromCsys().name() << endl;
tmp<pointField> tpf = fromCsys().localPosition(surf.points());
surf.movePoints(tpf());
}
if (toCsys.valid())
{
Info<< " -to " << toCsys().name() << endl;
tmp<pointField> tpf = toCsys().globalPosition(surf.points());
surf.movePoints(tpf());
}
scalar scaleOut = 0;
if (args.optionReadIfPresent("scaleOut", scaleOut) && scaleOut > 0)
{
Info<< " -scaleOut " << scaleOut << endl;
surf.scalePoints(scaleOut);
}
surfMesh smesh
(
IOobject
(
exportName,
runTime.constant(),
runTime
),
surf.xfer()
);
Info<< "writing surfMesh:\n " << smesh.objectPath() << endl;
smesh.write();
Info<< "\nEnd\n" << endl;
return 0;
}
// ************************************************************************* //

View File

@ -0,0 +1,3 @@
surfaceMeshInfo.C
EXE = $(FOAM_APPBIN)/surfaceMeshInfo

View File

@ -0,0 +1,5 @@
EXE_INC = \
-I$(LIB_SRC)/meshTools/lnInclude \
-I$(LIB_SRC)/surfMesh/lnInclude
EXE_LIBS = -lmeshTools -lsurfMesh

View File

@ -0,0 +1,175 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2013 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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 <http://www.gnu.org/licenses/>.
Application
surfaceMeshInfo
Description
Miscellaneous information about surface meshes.
Usage
- surfaceMeshInfo surfaceFile [OPTION]
\param -areas \n
Report area for each face.
\param -scale \<scale\> \n
Specify a scaling factor when reading files.
\param -xml \n
Write output in XML format.
Note
The filename extensions are used to determine the file format type.
The XML-like output can be useful for extraction with other tools,
but either output format can be easily extracted with a simple sed
command:
\verbatim
surfaceMeshInfo surfaceFile -areas | \
sed -ne '/areas/,/:/{ /:/!p }'
surfaceMeshInfo surfaceFile -areas -xml | \
sed -ne '/<areas/,/</{ /</!p }'
\endverbatim
\*---------------------------------------------------------------------------*/
#include "argList.H"
#include "Time.H"
#include "UnsortedMeshedSurfaces.H"
using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
argList::addNote
(
"information about surface meshes"
);
argList::noBanner();
argList::noParallel();
argList::validArgs.append("surfaceFile");
argList::addOption
(
"scale",
"factor",
"geometry scaling factor - default is 1"
);
argList::addBoolOption
(
"areas",
"display area of each face"
);
argList::addBoolOption
(
"xml",
"write output in XML format"
);
argList args(argc, argv);
Time runTime(args.rootPath(), args.caseName());
const fileName importName = args[1];
// check that reading is supported
if (!UnsortedMeshedSurface<face>::canRead(importName, true))
{
return 1;
}
const bool writeXML = args.optionFound("xml");
const bool writeAreas = args.optionFound("areas");
// use UnsortedMeshedSurface, not MeshedSurface to maintain ordering
UnsortedMeshedSurface<face> surf(importName);
scalar scaling = 0;
if (args.optionReadIfPresent("scale", scaling) && scaling > 0)
{
Info<< " -scale " << scaling << endl;
surf.scalePoints(scaling);
}
scalar areaTotal = 0;
if (writeXML)
{
Info<<"<?xml version='1.0' encoding='utf-8'?>" << nl
<<"<surfaceMeshInfo>" << nl
<< "<npoints>" << surf.nPoints() << "</npoints>" << nl
<< "<nfaces>" << surf.size() << "</nfaces>" << nl;
if (writeAreas)
{
Info<<"<areas size='" << surf.size() << "'>" << nl;
}
}
else
{
Info<< "nPoints : " << surf.nPoints() << nl
<< "nFaces : " << surf.size() << nl;
if (writeAreas)
{
Info<< "areas : " << nl;
}
}
forAll(surf, faceI)
{
const scalar fArea(surf[faceI].mag(surf.points()));
areaTotal += fArea;
if (writeAreas)
{
Info<< fArea << nl;
}
}
if (writeXML)
{
if (writeAreas)
{
Info<<"</areas>" << nl;
}
Info<< "<area>" << areaTotal << "</area>" << nl
<< "</surfaceMeshInfo>" << nl;
}
else
{
Info<< "area : " << areaTotal << nl;
}
return 0;
}
// ************************************************************************* //

View File

@ -0,0 +1,3 @@
surfaceMeshTriangulate.C
EXE = $(FOAM_APPBIN)/surfaceMeshTriangulate

View File

@ -0,0 +1,8 @@
EXE_INC = \
-I$(LIB_SRC)/meshTools/lnInclude \
-I$(LIB_SRC)/surfMesh/lnInclude
EXE_LIBS = \
-lmeshTools \
-lsurfMesh

View File

@ -0,0 +1,377 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2013 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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 <http://www.gnu.org/licenses/>.
Application
surfaceMeshTriangulate
Description
Extracts surface from a polyMesh. Depending on output surface format
triangulates faces.
Region numbers on faces cannot be guaranteed to be the same as the patch
indices.
Optionally only triangulates named patches.
If run in parallel the processor patches get filtered out by default and
the mesh gets merged (based on topology).
\*---------------------------------------------------------------------------*/
#include "MeshedSurface.H"
#include "UnsortedMeshedSurface.H"
#include "argList.H"
#include "Time.H"
#include "polyMesh.H"
#include "processorPolyPatch.H"
#include "ListListOps.H"
#include "uindirectPrimitivePatch.H"
#include "globalMeshData.H"
#include "globalIndex.H"
using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
argList::addNote
(
"extract surface from a polyMesh"
);
argList::validArgs.append("output file");
#include "addRegionOption.H"
argList::addBoolOption
(
"excludeProcPatches",
"exclude processor patches"
);
argList::addOption
(
"patches",
"(patch0 .. patchN)",
"only triangulate selected patches (wildcards supported)"
);
argList::addOption
(
"faceZones",
"(fz0 .. fzN)",
"triangulate selected faceZones (wildcards supported)"
);
#include "setRootCase.H"
#include "createTime.H"
const fileName outFileName(args[1]);
Info<< "Extracting surface from boundaryMesh ..."
<< endl << endl;
const bool includeProcPatches =
!(
args.optionFound("excludeProcPatches")
|| Pstream::parRun()
);
if (includeProcPatches)
{
Info<< "Including all processor patches." << nl << endl;
}
else if (Pstream::parRun())
{
Info<< "Excluding all processor patches." << nl << endl;
}
Info<< "Reading mesh from time " << runTime.value() << endl;
#include "createNamedPolyMesh.H"
// Create local surface from:
// - explicitly named patches only (-patches (at your option)
// - all patches (default in sequential mode)
// - all non-processor patches (default in parallel mode)
// - all non-processor patches (sequential mode, -excludeProcPatches
// (at your option)
// Construct table of patches to include.
const polyBoundaryMesh& bMesh = mesh.boundaryMesh();
labelHashSet includePatches(bMesh.size());
if (args.optionFound("patches"))
{
includePatches = bMesh.patchSet
(
wordReList(args.optionLookup("patches")())
);
}
else
{
forAll(bMesh, patchI)
{
const polyPatch& patch = bMesh[patchI];
if (includeProcPatches || !isA<processorPolyPatch>(patch))
{
includePatches.insert(patchI);
}
}
}
const faceZoneMesh& fzm = mesh.faceZones();
labelHashSet includeFaceZones(fzm.size());
if (args.optionFound("faceZones"))
{
wordReList zoneNames(args.optionLookup("faceZones")());
const wordList allZoneNames(fzm.names());
forAll(zoneNames, i)
{
const wordRe& zoneName = zoneNames[i];
labelList zoneIDs = findStrings(zoneName, allZoneNames);
forAll(zoneIDs, j)
{
includeFaceZones.insert(zoneIDs[j]);
}
if (zoneIDs.empty())
{
WarningIn(args.executable())
<< "Cannot find any faceZone name matching "
<< zoneName << endl;
}
}
Info<< "Additionally triangulating faceZones "
<< UIndirectList<word>(allZoneNames, includeFaceZones.sortedToc())
<< endl;
}
// From (name of) patch to compact 'zone' index
HashTable<label> compactZoneID(1000);
// Mesh face and compact zone indx
DynamicList<label> faceLabels;
DynamicList<label> compactZones;
{
// Collect sizes. Hash on names to handle local-only patches (e.g.
// processor patches)
HashTable<label> patchSize(1000);
label nFaces = 0;
forAllConstIter(labelHashSet, includePatches, iter)
{
const polyPatch& pp = bMesh[iter.key()];
patchSize.insert(pp.name(), pp.size());
nFaces += pp.size();
}
HashTable<label> zoneSize(1000);
forAllConstIter(labelHashSet, includeFaceZones, iter)
{
const faceZone& pp = fzm[iter.key()];
zoneSize.insert(pp.name(), pp.size());
nFaces += pp.size();
}
Pstream::mapCombineGather(patchSize, plusEqOp<label>());
Pstream::mapCombineGather(zoneSize, plusEqOp<label>());
// Allocate compact numbering for all patches/faceZones
forAllConstIter(HashTable<label>, patchSize, iter)
{
label sz = compactZoneID.size();
compactZoneID.insert(iter.key(), sz);
}
forAllConstIter(HashTable<label>, zoneSize, iter)
{
label sz = compactZoneID.size();
//Info<< "For faceZone " << iter.key() << " allocating zoneID "
// << sz << endl;
compactZoneID.insert(iter.key(), sz);
}
Pstream::mapCombineScatter(compactZoneID);
// Rework HashTable into labelList just for speed of conversion
labelList patchToCompactZone(bMesh.size(), -1);
labelList faceZoneToCompactZone(bMesh.size(), -1);
forAllConstIter(HashTable<label>, compactZoneID, iter)
{
label patchI = bMesh.findPatchID(iter.key());
if (patchI != -1)
{
patchToCompactZone[patchI] = iter();
}
else
{
label zoneI = fzm.findZoneID(iter.key());
faceZoneToCompactZone[zoneI] = iter();
}
}
faceLabels.setCapacity(nFaces);
compactZones.setCapacity(nFaces);
// Collect faces on patches
forAllConstIter(labelHashSet, includePatches, iter)
{
const polyPatch& pp = bMesh[iter.key()];
forAll(pp, i)
{
faceLabels.append(pp.start()+i);
compactZones.append(patchToCompactZone[pp.index()]);
}
}
// Collect faces on faceZones
forAllConstIter(labelHashSet, includeFaceZones, iter)
{
const faceZone& pp = fzm[iter.key()];
forAll(pp, i)
{
faceLabels.append(pp[i]);
compactZones.append(faceZoneToCompactZone[pp.index()]);
}
}
}
// Addressing engine for all faces
uindirectPrimitivePatch allBoundary
(
UIndirectList<face>(mesh.faces(), faceLabels),
mesh.points()
);
// Find correspondence to master points
labelList pointToGlobal;
labelList uniqueMeshPoints;
autoPtr<globalIndex> globalNumbers = mesh.globalData().mergePoints
(
allBoundary.meshPoints(),
allBoundary.meshPointMap(),
pointToGlobal,
uniqueMeshPoints
);
// Gather all unique points on master
List<pointField> gatheredPoints(Pstream::nProcs());
gatheredPoints[Pstream::myProcNo()] = pointField
(
mesh.points(),
uniqueMeshPoints
);
Pstream::gatherList(gatheredPoints);
// Gather all faces
List<faceList> gatheredFaces(Pstream::nProcs());
gatheredFaces[Pstream::myProcNo()] = allBoundary.localFaces();
forAll(gatheredFaces[Pstream::myProcNo()], i)
{
inplaceRenumber(pointToGlobal, gatheredFaces[Pstream::myProcNo()][i]);
}
Pstream::gatherList(gatheredFaces);
// Gather all ZoneIDs
List<labelList> gatheredZones(Pstream::nProcs());
gatheredZones[Pstream::myProcNo()] = compactZones.xfer();
Pstream::gatherList(gatheredZones);
// On master combine all points, faces, zones
if (Pstream::master())
{
pointField allPoints = ListListOps::combine<pointField>
(
gatheredPoints,
accessOp<pointField>()
);
gatheredPoints.clear();
faceList allFaces = ListListOps::combine<faceList>
(
gatheredFaces,
accessOp<faceList>()
);
gatheredFaces.clear();
labelList allZones = ListListOps::combine<labelList>
(
gatheredZones,
accessOp<labelList>()
);
gatheredZones.clear();
// Zones
surfZoneIdentifierList surfZones(compactZoneID.size());
forAllConstIter(HashTable<label>, compactZoneID, iter)
{
surfZones[iter()] = surfZoneIdentifier(iter.key(), iter());
Info<< "surfZone " << iter() << " : " << surfZones[iter()].name()
<< endl;
}
UnsortedMeshedSurface<face> unsortedFace
(
xferMove(allPoints),
xferMove(allFaces),
xferMove(allZones),
xferMove(surfZones)
);
MeshedSurface<face> sortedFace(unsortedFace);
fileName globalCasePath
(
runTime.processorCase()
? runTime.path()/".."/outFileName
: runTime.path()/outFileName
);
Info<< "Writing merged surface to " << globalCasePath << endl;
sortedFace.write(globalCasePath);
}
Info<< "End\n" << endl;
return 0;
}
// ************************************************************************* //

View File

@ -0,0 +1,3 @@
surfaceOrient.C
EXE = $(FOAM_APPBIN)/surfaceOrient

View File

@ -0,0 +1,7 @@
EXE_INC = \
-I$(LIB_SRC)/triSurface/lnInclude \
-I$(LIB_SRC)/meshTools/lnInclude
EXE_LIBS = \
-lmeshTools \
-ltriSurface

View File

@ -0,0 +1,135 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2013 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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 <http://www.gnu.org/licenses/>.
Application
surfaceOrient
Description
Set normal consistent with respect to a user provided 'outside' point.
If the -inside option is used the point is considered inside.
\*---------------------------------------------------------------------------*/
#include "argList.H"
#include "triSurfaceSearch.H"
#include "orientedSurface.H"
using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
argList::addNote
(
"set face normals consistent with a user-provided 'outside' point"
);
argList::noParallel();
argList::validArgs.append("surfaceFile");
argList::validArgs.append("visiblePoint");
argList::validArgs.append("output surfaceFile");
argList::addBoolOption
(
"inside",
"treat provided point as being inside"
);
argList::addBoolOption
(
"usePierceTest",
"determine orientation by counting number of intersections"
);
argList args(argc, argv);
const fileName surfFileName = args[1];
const point visiblePoint = args.argRead<point>(2);
const fileName outFileName = args[3];
const bool orientInside = args.optionFound("inside");
const bool usePierceTest = args.optionFound("usePierceTest");
Info<< "Reading surface from " << surfFileName << nl
<< "Orienting surface such that visiblePoint " << visiblePoint
<< " is ";
if (orientInside)
{
Info<< "inside" << endl;
}
else
{
Info<< "outside" << endl;
}
// Load surface
triSurface surf(surfFileName);
bool anyFlipped = false;
if (usePierceTest)
{
triSurfaceSearch surfSearches(surf);
anyFlipped = orientedSurface::orient
(
surf,
surfSearches,
visiblePoint,
!orientInside
);
}
else
{
anyFlipped = orientedSurface::orient
(
surf,
visiblePoint,
!orientInside
);
}
if (anyFlipped)
{
Info<< "Flipped orientation of (part of) surface." << endl;
}
else
{
Info<< "Did not flip orientation of any triangle of surface." << endl;
}
Info<< "Writing new surface to " << outFileName << endl;
surf.write(outFileName);
Info<< "End\n" << endl;
return 0;
}
// ************************************************************************* //

View File

@ -0,0 +1,3 @@
surfacePointMerge.C
EXE = $(FOAM_APPBIN)/surfacePointMerge

View File

@ -0,0 +1,7 @@
EXE_INC = \
-I$(LIB_SRC)/meshTools/lnInclude \
-I$(LIB_SRC)/triSurface/lnInclude
EXE_LIBS = \
-lmeshTools \
-ltriSurface

View File

@ -0,0 +1,92 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2013 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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 <http://www.gnu.org/licenses/>.
Application
surfacePointMerge
Description
Merges points on surface if they are within absolute distance.
Since absolute distance use with care!
\*---------------------------------------------------------------------------*/
#include "triSurface.H"
#include "triSurfaceTools.H"
#include "argList.H"
#include "OFstream.H"
#include "boundBox.H"
using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
argList::noParallel();
argList::validArgs.append("surfaceFile");
argList::validArgs.append("merge distance");
argList::validArgs.append("output surfaceFile");
argList args(argc, argv);
const fileName surfFileName = args[1];
const scalar mergeTol = args.argRead<scalar>(2);
const fileName outFileName = args[3];
Info<< "Reading surface from " << surfFileName << " ..." << endl;
Info<< "Merging points within " << mergeTol << " metre." << endl;
triSurface surf1(surfFileName);
Info<< "Original surface:" << endl;
surf1.writeStats(Info);
triSurface cleanSurf(surf1);
while (true)
{
label nOldVert = cleanSurf.nPoints();
cleanSurf = triSurfaceTools::mergePoints(cleanSurf, mergeTol);
Info<< "After merging points:" << endl;
cleanSurf.writeStats(Info);
if (nOldVert == cleanSurf.nPoints())
{
break;
}
}
cleanSurf.write(outFileName);
Info<< "End\n" << endl;
return 0;
}
// ************************************************************************* //

View File

@ -0,0 +1,3 @@
surfaceRedistributePar.C
EXE = $(FOAM_APPBIN)/surfaceRedistributePar

View File

@ -0,0 +1,9 @@
EXE_INC = \
-I$(LIB_SRC)/triSurface/lnInclude \
-I$(LIB_SRC)/meshTools/lnInclude \
-I$(LIB_SRC)/parallel/distributed/lnInclude
EXE_LIBS = \
-ldistributed \
-lmeshTools \
-ltriSurface

View File

@ -0,0 +1,301 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2013 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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 <http://www.gnu.org/licenses/>.
Application
surfaceRedistributePar
Description
(Re)distribution of triSurface. Either takes an undecomposed surface
or an already decomposed surface and redistributes it so that each
processor has all triangles that overlap its mesh.
Note
- best decomposition option is hierarchGeomDecomp since
guarantees square decompositions.
- triangles might be present on multiple processors.
- merging uses geometric tolerance so take care with writing precision.
\*---------------------------------------------------------------------------*/
#include "treeBoundBox.H"
#include "FixedList.H"
#include "argList.H"
#include "Time.H"
#include "polyMesh.H"
#include "distributedTriSurfaceMesh.H"
#include "mapDistribute.H"
#include "triSurfaceFields.H"
#include "Pair.H"
using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Print on master all the per-processor surface stats.
void writeProcStats
(
const triSurface& s,
const List<List<treeBoundBox> >& meshBb
)
{
// Determine surface bounding boxes, faces, points
List<treeBoundBox> surfBb(Pstream::nProcs());
{
surfBb[Pstream::myProcNo()] = treeBoundBox(s.points());
Pstream::gatherList(surfBb);
Pstream::scatterList(surfBb);
}
labelList nPoints(Pstream::nProcs());
nPoints[Pstream::myProcNo()] = s.points().size();
Pstream::gatherList(nPoints);
Pstream::scatterList(nPoints);
labelList nFaces(Pstream::nProcs());
nFaces[Pstream::myProcNo()] = s.size();
Pstream::gatherList(nFaces);
Pstream::scatterList(nFaces);
forAll(surfBb, procI)
{
const List<treeBoundBox>& bbs = meshBb[procI];
Info<< "processor" << procI << nl
<< "\tMesh bounds : " << bbs[0] << nl;
for (label i = 1; i < bbs.size(); i++)
{
Info<< "\t " << bbs[i]<< nl;
}
Info<< "\tSurface bounding box : " << surfBb[procI] << nl
<< "\tTriangles : " << nFaces[procI] << nl
<< "\tVertices : " << nPoints[procI]
<< endl;
}
Info<< endl;
}
int main(int argc, char *argv[])
{
argList::addNote
(
"redistribute a triSurface"
);
argList::validArgs.append("triSurfaceMesh");
argList::validArgs.append("distributionType");
argList::addBoolOption
(
"keepNonMapped",
"preserve surface outside of mesh bounds"
);
#include "setRootCase.H"
#include "createTime.H"
runTime.functionObjects().off();
const fileName surfFileName = args[1];
const word distType = args[2];
Info<< "Reading surface from " << surfFileName << nl << nl
<< "Using distribution method "
<< distributedTriSurfaceMesh::distributionTypeNames_[distType]
<< " " << distType << nl << endl;
const bool keepNonMapped = args.options().found("keepNonMapped");
if (keepNonMapped)
{
Info<< "Preserving surface outside of mesh bounds." << nl << endl;
}
else
{
Info<< "Removing surface outside of mesh bounds." << nl << endl;
}
if (!Pstream::parRun())
{
FatalErrorIn(args.executable())
<< "Please run this program on the decomposed case."
<< " It will read surface " << surfFileName
<< " and decompose it such that it overlaps the mesh bounding box."
<< exit(FatalError);
}
#include "createPolyMesh.H"
Random rndGen(653213);
// Determine mesh bounding boxes:
List<List<treeBoundBox> > meshBb(Pstream::nProcs());
{
meshBb[Pstream::myProcNo()] = List<treeBoundBox>
(
1,
treeBoundBox
(
boundBox(mesh.points(), false)
).extend(rndGen, 1e-3)
);
Pstream::gatherList(meshBb);
Pstream::scatterList(meshBb);
}
IOobject io
(
surfFileName, // name
//runTime.findInstance("triSurface", surfFileName), // instance
runTime.constant(), // instance
"triSurface", // local
runTime, // registry
IOobject::MUST_READ,
IOobject::NO_WRITE
);
const fileName actualPath(io.filePath());
fileName localPath(actualPath);
localPath.replace(runTime.rootPath() + '/', "");
if (actualPath == io.objectPath())
{
Info<< "Loading local (decomposed) surface " << localPath << nl <<endl;
}
else
{
Info<< "Loading undecomposed surface " << localPath << nl << endl;
}
// Create dummy dictionary for bounding boxes if does not exist.
if (!isFile(actualPath / "Dict"))
{
dictionary dict;
dict.add("bounds", meshBb[Pstream::myProcNo()]);
dict.add("distributionType", distType);
dict.add("mergeDistance", SMALL);
IOdictionary ioDict
(
IOobject
(
io.name() + "Dict",
io.instance(),
io.local(),
io.db(),
IOobject::NO_READ,
IOobject::NO_WRITE,
false
),
dict
);
Info<< "Writing dummy bounds dictionary to " << ioDict.name()
<< nl << endl;
ioDict.regIOobject::writeObject
(
IOstream::ASCII,
IOstream::currentVersion,
ioDict.time().writeCompression()
);
}
// Load surface
distributedTriSurfaceMesh surfMesh(io);
Info<< "Loaded surface" << nl << endl;
// Generate a test field
{
const triSurface& s = static_cast<const triSurface&>(surfMesh);
autoPtr<triSurfaceVectorField> fcPtr
(
new triSurfaceVectorField
(
IOobject
(
surfMesh.searchableSurface::name(), // name
surfMesh.searchableSurface::instance(), // instance
surfMesh.searchableSurface::local(), // local
surfMesh,
IOobject::NO_READ,
IOobject::AUTO_WRITE
),
surfMesh,
dimLength
)
);
triSurfaceVectorField& fc = fcPtr();
forAll(fc, triI)
{
fc[triI] = s[triI].centre(s.points());
}
// Steal pointer and store object on surfMesh
fcPtr.ptr()->store();
}
// Write per-processor stats
Info<< "Before redistribution:" << endl;
writeProcStats(surfMesh, meshBb);
// Do redistribution
Info<< "Redistributing surface" << nl << endl;
autoPtr<mapDistribute> faceMap;
autoPtr<mapDistribute> pointMap;
surfMesh.distribute
(
meshBb[Pstream::myProcNo()],
keepNonMapped,
faceMap,
pointMap
);
faceMap.clear();
pointMap.clear();
Info<< endl;
// Write per-processor stats
Info<< "After redistribution:" << endl;
writeProcStats(surfMesh, meshBb);
Info<< "Writing surface." << nl << endl;
surfMesh.searchableSurface::write();
Info<< "End\n" << endl;
return 0;
}
// ************************************************************************* //

View File

@ -0,0 +1,3 @@
surfaceRefineRedGreen.C
EXE = $(FOAM_APPBIN)/surfaceRefineRedGreen

View File

@ -0,0 +1,7 @@
EXE_INC = \
-I$(LIB_SRC)/meshTools/lnInclude \
-I$(LIB_SRC)/triSurface/lnInclude
EXE_LIBS = \
-lmeshTools \
-ltriSurface

View File

@ -0,0 +1,85 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2013 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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 <http://www.gnu.org/licenses/>.
Application
surfaceRefineRedGreen
Description
Refine by splitting all three edges of triangle ('red' refinement).
Neighbouring triangles (which are not marked for refinement get split
in half ('green' refinement). (R. Verfuerth, "A review of a posteriori
error estimation and adaptive mesh refinement techniques",
Wiley-Teubner, 1996)
\*---------------------------------------------------------------------------*/
#include "triSurface.H"
#include "triSurfaceTools.H"
#include "argList.H"
#include "OFstream.H"
using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
argList::noParallel();
argList::validArgs.append("surfaceFile");
argList::validArgs.append("output surfaceFile");
argList args(argc, argv);
const fileName surfFileName = args[1];
const fileName outFileName = args[2];
Info<< "Reading surface from " << surfFileName << " ..." << endl;
triSurface surf1(surfFileName);
// Refine
triSurface surf2 = triSurfaceTools::redGreenRefine
(
surf1,
identity(surf1.size()) //Hack: refine all
);
Info<< "Original surface:" << endl
<< " triangles :" << surf1.size() << endl
<< " vertices(used):" << surf1.nPoints() << endl
<< "Refined surface:" << endl
<< " triangles :" << surf2.size() << endl
<< " vertices(used):" << surf2.nPoints() << endl << endl;
Info<< "Writing refined surface to " << outFileName << " ..." << endl;
surf2.write(outFileName);
Info<< "End\n" << endl;
return 0;
}
// ************************************************************************* //

View File

@ -0,0 +1,3 @@
surfaceSplitByPatch.C
EXE = $(FOAM_APPBIN)/surfaceSplitByPatch

View File

@ -0,0 +1,7 @@
EXE_INC = \
-I$(LIB_SRC)/triSurface/lnInclude
EXE_LIBS = \
-lmeshTools \
-ltriSurface

View File

@ -0,0 +1,119 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2013 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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 <http://www.gnu.org/licenses/>.
Application
surfaceSplitByPatch
Description
Writes regions of triSurface to separate files.
\*---------------------------------------------------------------------------*/
#include "argList.H"
#include "triSurface.H"
using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
argList::addNote
(
"write surface mesh regions to separate files"
);
argList::noParallel();
argList::validArgs.append("input surfaceFile");
argList args(argc, argv);
const fileName surfName = args[1];
Info<< "Reading surf from " << surfName << " ..." << nl << endl;
fileName surfBase = surfName.lessExt();
word extension = surfName.ext();
triSurface surf(surfName);
Info<< "Writing regions to separate files ..."
<< nl << endl;
const geometricSurfacePatchList& patches = surf.patches();
forAll(patches, patchI)
{
const geometricSurfacePatch& pp = patches[patchI];
word patchName = pp.name();
if (patchName.empty())
{
patchName = "patch" + Foam::name(patchI);
}
fileName outFile(surfBase + '_' + patchName + '.' + extension);
Info<< " Writing patch " << patchName << " to file " << outFile
<< endl;
// Collect faces of region
boolList includeMap(surf.size(), false);
forAll(surf, faceI)
{
const labelledTri& f = surf[faceI];
if (f.region() == patchI)
{
includeMap[faceI] = true;
}
}
// Subset triSurface
labelList pointMap;
labelList faceMap;
triSurface subSurf
(
surf.subsetMesh
(
includeMap,
pointMap,
faceMap
)
);
subSurf.write(outFile);
}
Info<< "End\n" << endl;
return 0;
}
// ************************************************************************* //

View File

@ -0,0 +1,4 @@
surfaceSplitByTopology.C
EXE = $(FOAM_APPBIN)/surfaceSplitByTopology

View File

@ -0,0 +1,7 @@
EXE_INC = \
-I$(LIB_SRC)/triSurface/lnInclude
EXE_LIBS = \
-lmeshTools \
-ltriSurface

View File

@ -0,0 +1,220 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2012 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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 <http://www.gnu.org/licenses/>.
Description
Strips any baffle parts of a surface. A baffle region is one which is
reached by walking from an open edge, and stopping when a multiply connected
edge is reached.
\*---------------------------------------------------------------------------*/
#include "argList.H"
#include "triSurface.H"
using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
int main(int argc, char *argv[])
{
argList::noParallel();
argList::validOptions.clear();
argList::validArgs.append("input surface file");
argList::validArgs.append("output surface file");
argList args(argc, argv);
fileName surfFileName(args.additionalArgs()[0]);
Info<< "Reading surface from " << surfFileName << endl;
fileName outFileName(args.additionalArgs()[1]);
fileName outFileBaseName = outFileName.lessExt();
word outExtension = outFileName.ext();
// Load surface
triSurface surf(surfFileName);
bool anyZoneRemoved = false;
label iterationNo = 0;
label iterationLimit = 10;
Info<< "Splitting off baffle parts " << endl;
do
{
anyZoneRemoved = false;
labelList faceZone;
const labelListList& edFaces = surf.edgeFaces();
const labelListList& faceEds = surf.faceEdges();
boolList multipleEdges(edFaces.size(), false);
forAll(multipleEdges, i)
{
if (edFaces[i].size() > 2)
{
multipleEdges[i] = true;
}
}
label nZones = surf.markZones(multipleEdges, faceZone);
if (nZones < 2)
{
break;
}
boolList nonBaffle(faceZone.size(), true);
boolList baffle(faceZone.size(), true);
labelList pointMap;
labelList faceMap;
for (label z = 0; z < nZones; z++)
{
bool keepZone = true;
forAll(faceZone, f)
{
if (faceZone[f] == z)
{
forAll (faceEds[f], fe)
{
if (edFaces[faceEds[f][fe]].size() < 2)
{
keepZone = false;
anyZoneRemoved = true;
break;
}
}
}
if (!keepZone)
{
break;
}
}
forAll(faceZone, f)
{
if (faceZone[f] == z)
{
nonBaffle[f] = keepZone;
baffle[f] = !keepZone;
}
}
}
Info<< " Iteration " << iterationNo << endl;
triSurface baffleSurf = surf.subsetMesh(baffle, pointMap, faceMap);
if (baffleSurf.size())
{
fileName bafflePartFileName =
outFileBaseName
+ "_bafflePart_"
+ name(iterationNo)
+ "." + outExtension;
Info<< " Writing baffle part to " << bafflePartFileName << endl;
baffleSurf.write(bafflePartFileName);
}
surf = surf.subsetMesh(nonBaffle, pointMap, faceMap);
if (iterationNo == iterationLimit)
{
WarningIn("surfaceSplitByTopology")
<< "Iteration limit of " << iterationLimit << "reached" << endl;
}
iterationNo++;
} while (anyZoneRemoved && iterationNo < iterationLimit);
Info<< "Writing new surface to " << outFileName << endl;
surf.write(outFileName);
labelList faceZone;
const labelListList& edFaces = surf.edgeFaces();
boolList multipleEdges(edFaces.size(), false);
forAll(multipleEdges, i)
{
if (edFaces[i].size() > 2)
{
multipleEdges[i] = true;
}
}
label nZones = surf.markZones(multipleEdges, faceZone);
Info<< "Splitting remaining multiply connected parts" << endl;
for (label z = 0; z < nZones; z++)
{
boolList include(faceZone.size(), false);
labelList pointMap;
labelList faceMap;
forAll(faceZone, f)
{
if (faceZone[f] == z)
{
include[f] = true;
}
}
triSurface zoneSurf = surf.subsetMesh(include, pointMap, faceMap);
fileName remainingPartFileName =
outFileBaseName
+ "_multiplePart_"
+ name(z)
+ "." + outExtension;
Info<< " Writing mulitple part "
<< z << " to " << remainingPartFileName << endl;
zoneSurf.write(remainingPartFileName);
}
Info << "End\n" << endl;
return 0;
}
// ************************************************************************* //

View File

@ -0,0 +1,3 @@
surfaceSplitNonManifolds.C
EXE = $(FOAM_APPBIN)/surfaceSplitNonManifolds

View File

@ -0,0 +1,7 @@
EXE_INC = \
-I$(LIB_SRC)/meshTools/lnInclude \
-I$(LIB_SRC)/triSurface/lnInclude
EXE_LIBS = \
-lmeshTools \
-ltriSurface

View File

@ -0,0 +1,970 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2011-2013 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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 <http://www.gnu.org/licenses/>.
Application
surfaceSplitNonManifolds
Description
Takes multiply connected surface and tries to split surface at
multiply connected edges by duplicating points. Introduces concept of
- borderEdge. Edge with 4 faces connected to it.
- borderPoint. Point connected to exactly 2 borderEdges.
- borderLine. Connected list of borderEdges.
By duplicating borderPoints this will split 'borderLines'. As a
preprocessing step it can detect borderEdges without any borderPoints
and explicitly split these triangles.
The problems in this algorithm are:
- determining which two (of the four) faces form a surface. Done by walking
face-edge-face while keeping and edge or point on the borderEdge
borderPoint.
- determining the outwards pointing normal to be used to slightly offset the
duplicated point.
Uses sortedEdgeFaces quite a bit.
Is tested on simple borderLines resulting from extracting a surface
from a hex mesh. Will quite possibly go wrong on more complicated border
lines (i.e. ones forming a loop).
Dumps surface every so often since might take a long time to complete.
\*---------------------------------------------------------------------------*/
#include "argList.H"
#include "triSurface.H"
#include "OFstream.H"
#include "ListOps.H"
#include "triSurfaceTools.H"
using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
void writeOBJ(Ostream& os, const pointField& pts)
{
forAll(pts, i)
{
const point& pt = pts[i];
os << "v " << pt.x() << ' ' << pt.y() << ' ' << pt.z() << endl;
}
}
void dumpPoints(const triSurface& surf, const labelList& borderPoint)
{
fileName fName("borderPoints.obj");
Info<< "Dumping borderPoints as Lightwave .obj file to " << fName
<< "\nThis can be visualized with e.g. javaview (www.javaview.de)\n\n";
OFstream os(fName);
forAll(borderPoint, pointI)
{
if (borderPoint[pointI] != -1)
{
const point& pt = surf.localPoints()[pointI];
os << "v " << pt.x() << ' ' << pt.y() << ' ' << pt.z() << endl;
}
}
}
void dumpEdges(const triSurface& surf, const boolList& borderEdge)
{
fileName fName("borderEdges.obj");
Info<< "Dumping borderEdges as Lightwave .obj file to " << fName
<< "\nThis can be visualized with e.g. javaview (www.javaview.de)\n\n";
OFstream os(fName);
writeOBJ(os, surf.localPoints());
forAll(borderEdge, edgeI)
{
if (borderEdge[edgeI])
{
const edge& e = surf.edges()[edgeI];
os << "l " << e.start()+1 << ' ' << e.end()+1 << endl;
}
}
}
void dumpFaces
(
const fileName& fName,
const triSurface& surf,
const Map<label>& connectedFaces
)
{
Info<< "Dumping connectedFaces as Lightwave .obj file to " << fName
<< "\nThis can be visualized with e.g. javaview (www.javaview.de)\n\n";
OFstream os(fName);
forAllConstIter(Map<label>, connectedFaces, iter)
{
point ctr = surf.localFaces()[iter.key()].centre(surf.localPoints());
os << "v " << ctr.x() << ' ' << ctr.y() << ' ' << ctr.z() << endl;
}
}
void testSortedEdgeFaces(const triSurface& surf)
{
const labelListList& edgeFaces = surf.edgeFaces();
const labelListList& sortedEdgeFaces = surf.sortedEdgeFaces();
forAll(edgeFaces, edgeI)
{
const labelList& myFaces = edgeFaces[edgeI];
const labelList& sortMyFaces = sortedEdgeFaces[edgeI];
forAll(myFaces, i)
{
if (findIndex(sortMyFaces, myFaces[i]) == -1)
{
FatalErrorIn("testSortedEdgeFaces(..)") << abort(FatalError);
}
}
forAll(sortMyFaces, i)
{
if (findIndex(myFaces, sortMyFaces[i]) == -1)
{
FatalErrorIn("testSortedEdgeFaces(..)") << abort(FatalError);
}
}
}
}
// Mark off all border edges. Return number of border edges.
label markBorderEdges
(
const bool debug,
const triSurface& surf,
boolList& borderEdge
)
{
label nBorderEdges = 0;
const labelListList& edgeFaces = surf.edgeFaces();
forAll(edgeFaces, edgeI)
{
if (edgeFaces[edgeI].size() == 4)
{
borderEdge[edgeI] = true;
nBorderEdges++;
}
}
if (debug)
{
dumpEdges(surf, borderEdge);
}
return nBorderEdges;
}
// Mark off all border points. Return number of border points. Border points
// marked by setting value to newly introduced point.
label markBorderPoints
(
const bool debug,
const triSurface& surf,
const boolList& borderEdge,
labelList& borderPoint
)
{
label nPoints = surf.nPoints();
const labelListList& pointEdges = surf.pointEdges();
forAll(pointEdges, pointI)
{
const labelList& pEdges = pointEdges[pointI];
label nBorderEdges = 0;
forAll(pEdges, i)
{
if (borderEdge[pEdges[i]])
{
nBorderEdges++;
}
}
if (nBorderEdges == 2 && borderPoint[pointI] == -1)
{
borderPoint[pointI] = nPoints++;
}
}
label nBorderPoints = nPoints - surf.nPoints();
if (debug)
{
dumpPoints(surf, borderPoint);
}
return nBorderPoints;
}
// Get minumum length of edges connected to pointI
// Serves to get some local length scale.
scalar minEdgeLen(const triSurface& surf, const label pointI)
{
const pointField& points = surf.localPoints();
const labelList& pEdges = surf.pointEdges()[pointI];
scalar minLen = GREAT;
forAll(pEdges, i)
{
label edgeI = pEdges[i];
scalar len = surf.edges()[edgeI].mag(points);
if (len < minLen)
{
minLen = len;
}
}
return minLen;
}
// Find edge among edgeLabels with endpoints v0,v1
label findEdge
(
const triSurface& surf,
const labelList& edgeLabels,
const label v0,
const label v1
)
{
forAll(edgeLabels, i)
{
label edgeI = edgeLabels[i];
const edge& e = surf.edges()[edgeI];
if
(
(
e.start() == v0
&& e.end() == v1
)
|| (
e.start() == v1
&& e.end() == v0
)
)
{
return edgeI;
}
}
FatalErrorIn("findEdge(..)") << "Cannot find edge with labels " << v0
<< ' ' << v1 << " in candidates " << edgeLabels
<< " with vertices:" << UIndirectList<edge>(surf.edges(), edgeLabels)()
<< abort(FatalError);
return -1;
}
// Get the other edge connected to pointI on faceI.
label otherEdge
(
const triSurface& surf,
const label faceI,
const label otherEdgeI,
const label pointI
)
{
const labelList& fEdges = surf.faceEdges()[faceI];
forAll(fEdges, i)
{
label edgeI = fEdges[i];
const edge& e = surf.edges()[edgeI];
if
(
edgeI != otherEdgeI
&& (
e.start() == pointI
|| e.end() == pointI
)
)
{
return edgeI;
}
}
FatalErrorIn("otherEdge(..)") << "Cannot find other edge on face " << faceI
<< " verts:" << surf.localPoints()[faceI]
<< " connected to point " << pointI
<< " faceEdges:" << UIndirectList<edge>(surf.edges(), fEdges)()
<< abort(FatalError);
return -1;
}
// Starting from startPoint on startEdge on startFace walk along border
// and insert faces along the way. Walk keeps always one point or one edge
// on the border line.
void walkSplitLine
(
const triSurface& surf,
const boolList& borderEdge,
const labelList& borderPoint,
const label startFaceI,
const label startEdgeI, // is border edge
const label startPointI, // is border point
Map<label>& faceToEdge,
Map<label>& faceToPoint
)
{
label faceI = startFaceI;
label edgeI = startEdgeI;
label pointI = startPointI;
do
{
//
// Stick to pointI and walk face-edge-face until back on border edge.
//
do
{
// Cross face to next edge.
edgeI = otherEdge(surf, faceI, edgeI, pointI);
if (borderEdge[edgeI])
{
if (!faceToEdge.insert(faceI, edgeI))
{
// Was already visited.
return;
}
else
{
// First visit to this borderEdge. We're back on borderline.
break;
}
}
else if (!faceToPoint.insert(faceI, pointI))
{
// Was already visited.
return;
}
// Cross edge to other face
const labelList& eFaces = surf.edgeFaces()[edgeI];
if (eFaces.size() != 2)
{
FatalErrorIn("walkSplitPoint(..)")
<< "Can only handle edges with 2 or 4 edges for now."
<< abort(FatalError);
}
if (eFaces[0] == faceI)
{
faceI = eFaces[1];
}
else if (eFaces[1] == faceI)
{
faceI = eFaces[0];
}
else
{
FatalErrorIn("walkSplitPoint(..)") << abort(FatalError);
}
}
while (true);
//
// Back on border edge. Cross to other point on edge.
//
pointI = surf.edges()[edgeI].otherVertex(pointI);
if (borderPoint[pointI] == -1)
{
return;
}
}
while (true);
}
// Find second face which is from same surface i.e. has points on the
// shared edge in reverse order.
label sharedFace
(
const triSurface& surf,
const label firstFaceI,
const label sharedEdgeI
)
{
// Find ordering of face points in edge.
const edge& e = surf.edges()[sharedEdgeI];
const triSurface::FaceType& f = surf.localFaces()[firstFaceI];
label startIndex = findIndex(f, e.start());
// points in face in same order as edge
bool edgeOrder = (f[f.fcIndex(startIndex)] == e.end());
// Get faces using edge in sorted order. (sorted such that walking
// around them in anti-clockwise order corresponds to edge vector
// acc. to right-hand rule)
const labelList& eFaces = surf.sortedEdgeFaces()[sharedEdgeI];
// Get position of face in sorted edge faces
label faceIndex = findIndex(eFaces, firstFaceI);
if (edgeOrder)
{
// Get face before firstFaceI
return eFaces[eFaces.rcIndex(faceIndex)];
}
else
{
// Get face after firstFaceI
return eFaces[eFaces.fcIndex(faceIndex)];
}
}
// Calculate (inward pointing) normals on edges shared by faces in
// faceToEdge and averages them to pointNormals.
void calcPointVecs
(
const triSurface& surf,
const Map<label>& faceToEdge,
const Map<label>& faceToPoint,
vectorField& borderPointVec
)
{
const labelListList& sortedEdgeFaces = surf.sortedEdgeFaces();
const edgeList& edges = surf.edges();
const pointField& points = surf.localPoints();
boolList edgeDone(surf.nEdges(), false);
forAllConstIter(Map<label>, faceToEdge, iter)
{
const label edgeI = iter();
if (!edgeDone[edgeI])
{
edgeDone[edgeI] = true;
// Get the two connected faces in sorted order
// Note: should have stored this when walking ...
label face0I = -1;
label face1I = -1;
const labelList& eFaces = sortedEdgeFaces[edgeI];
forAll(eFaces, i)
{
label faceI = eFaces[i];
if (faceToEdge.found(faceI))
{
if (face0I == -1)
{
face0I = faceI;
}
else if (face1I == -1)
{
face1I = faceI;
break;
}
}
}
if (face0I == -1 && face1I == -1)
{
Info<< "Writing surface to errorSurf.obj" << endl;
surf.write("errorSurf.obj");
FatalErrorIn("calcPointVecs(..)")
<< "Cannot find two faces using border edge " << edgeI
<< " verts:" << edges[edgeI]
<< " eFaces:" << eFaces << endl
<< "face0I:" << face0I
<< " face1I:" << face1I << nl
<< "faceToEdge:" << faceToEdge << nl
<< "faceToPoint:" << faceToPoint
<< "Written surface to errorSurf.obj"
<< abort(FatalError);
}
// Now we have edge and the two faces in counter-clockwise order
// as seen from edge vector. Calculate normal.
const edge& e = edges[edgeI];
vector eVec = e.vec(points);
// Determine vector as average of the vectors in the two faces.
// If there is only one face available use only one vector.
vector midVec(vector::zero);
if (face0I != -1)
{
label v0 = triSurfaceTools::oppositeVertex(surf, face0I, edgeI);
vector e0 = (points[v0] - points[e.start()]) ^ eVec;
e0 /= mag(e0);
midVec = e0;
}
if (face1I != -1)
{
label v1 = triSurfaceTools::oppositeVertex(surf, face1I, edgeI);
vector e1 = (points[e.start()] - points[v1]) ^ eVec;
e1 /= mag(e1);
midVec += e1;
}
scalar magMidVec = mag(midVec);
if (magMidVec > SMALL)
{
midVec /= magMidVec;
// Average to pointVec
borderPointVec[e.start()] += midVec;
borderPointVec[e.end()] += midVec;
}
}
}
}
// Renumbers vertices (of triangles in faceToEdge) of which the pointMap is
// not -1.
void renumberFaces
(
const triSurface& surf,
const labelList& pointMap,
const Map<label>& faceToEdge,
List<triSurface::FaceType>& newTris
)
{
forAllConstIter(Map<label>, faceToEdge, iter)
{
const label faceI = iter.key();
const triSurface::FaceType& f = surf.localFaces()[faceI];
forAll(f, fp)
{
if (pointMap[f[fp]] != -1)
{
newTris[faceI][fp] = pointMap[f[fp]];
}
}
}
}
// Split all borderEdges that don't have borderPoint. Return true if split
// anything.
bool splitBorderEdges
(
triSurface& surf,
const boolList& borderEdge,
const labelList& borderPoint
)
{
labelList edgesToBeSplit(surf.nEdges());
label nSplit = 0;
forAll(borderEdge, edgeI)
{
if (borderEdge[edgeI])
{
const edge& e = surf.edges()[edgeI];
if (borderPoint[e.start()] == -1 && borderPoint[e.end()] == -1)
{
// None of the points of the edge is borderPoint. Split edge
// to introduce border point.
edgesToBeSplit[nSplit++] = edgeI;
}
}
}
edgesToBeSplit.setSize(nSplit);
if (nSplit > 0)
{
Info<< "Splitting surface along " << nSplit << " borderEdges that don't"
<< " neighbour other borderEdges" << nl << endl;
surf = triSurfaceTools::greenRefine(surf, edgesToBeSplit);
return true;
}
else
{
Info<< "No edges to be split" <<nl << endl;
return false;
}
}
int main(int argc, char *argv[])
{
argList::addNote
(
"split multiply connected surface edges by duplicating points"
);
argList::noParallel();
argList::validArgs.append("surfaceFile");
argList::validArgs.append("output surfaceFile");
argList::addBoolOption
(
"debug",
"add debugging output"
);
argList args(argc, argv);
const fileName inSurfName = args[1];
const fileName outSurfName = args[2];
const bool debug = args.optionFound("debug");
Info<< "Reading surface from " << inSurfName << endl;
triSurface surf(inSurfName);
// Make sure sortedEdgeFaces is calculated correctly
testSortedEdgeFaces(surf);
// Get all quad connected edges. These are seen as borders when walking.
boolList borderEdge(surf.nEdges(), false);
markBorderEdges(debug, surf, borderEdge);
// Points on two sides connected to borderEdges are called
// borderPoints and will be duplicated. borderPoint contains label
// of newly introduced vertex.
labelList borderPoint(surf.nPoints(), -1);
markBorderPoints(debug, surf, borderEdge, borderPoint);
// Split edges where there would be no borderPoint to duplicate.
splitBorderEdges(surf, borderEdge, borderPoint);
Info<< "Writing split surface to " << outSurfName << nl << endl;
surf.write(outSurfName);
Info<< "Finished writing surface to " << outSurfName << nl << endl;
// Last iteration values.
label nOldBorderEdges = -1;
label nOldBorderPoints = -1;
label iteration = 0;
do
{
// Redo borderEdge/borderPoint calculation.
boolList borderEdge(surf.nEdges(), false);
label nBorderEdges = markBorderEdges(debug, surf, borderEdge);
if (nBorderEdges == 0)
{
Info<< "Found no border edges. Exiting." << nl << nl;
break;
}
// Label of newly introduced duplicate.
labelList borderPoint(surf.nPoints(), -1);
label nBorderPoints =
markBorderPoints
(
debug,
surf,
borderEdge,
borderPoint
);
if (nBorderPoints == 0)
{
Info<< "Found no border points. Exiting." << nl << nl;
break;
}
Info<< "Found:\n"
<< " border edges :" << nBorderEdges << nl
<< " border points:" << nBorderPoints << nl
<< endl;
if
(
nBorderPoints == nOldBorderPoints
&& nBorderEdges == nOldBorderEdges
)
{
Info<< "Stopping since number of border edges and point is same"
<< " as in previous iteration" << nl << endl;
break;
}
//
// Define splitLine as a series of connected borderEdges. Find start
// of one (as edge and point on edge)
//
label startEdgeI = -1;
label startPointI = -1;
forAll(borderEdge, edgeI)
{
if (borderEdge[edgeI])
{
const edge& e = surf.edges()[edgeI];
if ((borderPoint[e[0]] != -1) && (borderPoint[e[1]] == -1))
{
startEdgeI = edgeI;
startPointI = e[0];
break;
}
else if ((borderPoint[e[0]] == -1) && (borderPoint[e[1]] != -1))
{
startEdgeI = edgeI;
startPointI = e[1];
break;
}
}
}
if (startEdgeI == -1)
{
Info<< "Cannot find starting point of splitLine\n" << endl;
break;
}
// Pick any face using edge to start from.
const labelList& eFaces = surf.edgeFaces()[startEdgeI];
label firstFaceI = eFaces[0];
// Find second face which is from same surface i.e. has outwards
// pointing normal as well (actually bit more complex than this)
label secondFaceI = sharedFace(surf, firstFaceI, startEdgeI);
Info<< "Starting local walk from:" << endl
<< " edge :" << startEdgeI << endl
<< " point:" << startPointI << endl
<< " face0:" << firstFaceI << endl
<< " face1:" << secondFaceI << endl
<< endl;
// From face on border edge to edge.
Map<label> faceToEdge(2*nBorderEdges);
// From face connected to border point (but not border edge) to point.
Map<label> faceToPoint(2*nBorderPoints);
faceToEdge.insert(firstFaceI, startEdgeI);
walkSplitLine
(
surf,
borderEdge,
borderPoint,
firstFaceI,
startEdgeI,
startPointI,
faceToEdge,
faceToPoint
);
faceToEdge.insert(secondFaceI, startEdgeI);
walkSplitLine
(
surf,
borderEdge,
borderPoint,
secondFaceI,
startEdgeI,
startPointI,
faceToEdge,
faceToPoint
);
Info<< "Finished local walk and visited" << nl
<< " border edges :" << faceToEdge.size() << nl
<< " border points(but not edges):" << faceToPoint.size() << nl
<< endl;
if (debug)
{
dumpFaces("faceToEdge.obj", surf, faceToEdge);
dumpFaces("faceToPoint.obj", surf, faceToPoint);
}
//
// Create coordinates for borderPoints by duplicating the existing
// point and then slightly shifting it inwards. To determine the
// inwards direction get the average normal of both connectedFaces on
// the edge and then interpolate this to the (border)point.
//
vectorField borderPointVec(surf.nPoints(), vector(GREAT, GREAT, GREAT));
calcPointVecs(surf, faceToEdge, faceToPoint, borderPointVec);
// New position. Start off from copy of old points.
pointField newPoints(surf.localPoints());
newPoints.setSize(newPoints.size() + nBorderPoints);
forAll(borderPoint, pointI)
{
label newPointI = borderPoint[pointI];
if (newPointI != -1)
{
scalar minLen = minEdgeLen(surf, pointI);
vector n = borderPointVec[pointI];
n /= mag(n);
newPoints[newPointI] = newPoints[pointI] + 0.1 * minLen * n;
}
}
//
// Renumber all faces in connectedFaces
//
// Start off from copy of faces.
List<labelledTri> newTris(surf.size());
forAll(surf, faceI)
{
newTris[faceI] = surf.localFaces()[faceI];
newTris[faceI].region() = surf[faceI].region();
}
// Renumber all faces in faceToEdge
renumberFaces(surf, borderPoint, faceToEdge, newTris);
// Renumber all faces in faceToPoint
renumberFaces(surf, borderPoint, faceToPoint, newTris);
// Check if faces use unmoved points.
forAll(newTris, faceI)
{
const triSurface::FaceType& f = newTris[faceI];
forAll(f, fp)
{
const point& pt = newPoints[f[fp]];
if (mag(pt) >= GREAT/2)
{
Info<< "newTri:" << faceI << " verts:" << f
<< " vert:" << f[fp] << " point:" << pt << endl;
}
}
}
surf = triSurface(newTris, surf.patches(), newPoints);
if (debug || (iteration != 0 && (iteration % 20) == 0))
{
Info<< "Writing surface to " << outSurfName << nl << endl;
surf.write(outSurfName);
Info<< "Finished writing surface to " << outSurfName << nl << endl;
}
// Save prev iteration values.
nOldBorderEdges = nBorderEdges;
nOldBorderPoints = nBorderPoints;
iteration++;
}
while (true);
Info<< "Writing final surface to " << outSurfName << nl << endl;
surf.write(outSurfName);
Info<< "End\n" << endl;
return 0;
}
// ************************************************************************* //

View File

@ -0,0 +1,164 @@
1
(
patch0
empty
)
49
(
(0 1 1)
(0 0 1)
(1 0 1)
(1 1 1)
(0 1 0)
(0 0 0)
(1 0 0)
(1 1 0)
(0 0.5 1)
(0.5 0.5 1)
(0.5 1 1)
(0.5 0 1)
(1 0.5 1)
(1 0.5 0)
(0.5 0.5 0)
(0.5 1 0)
(0.5 0 0)
(0 0.5 0)
(1 0.5 0.5)
(1 1 0.5)
(1 0 0.5)
(0 1 0.5)
(0.5 1 0.5)
(0 0.5 0.5)
(0 0 0.5)
(0.5 0 0.5)
(1 2 1)
(2 1 1)
(2 2 1)
(1 2 0)
(2 1 0)
(2 2 0)
(1 1.5 1)
(1.5 1.5 1)
(1.5 2 1)
(1.5 1 1)
(2 1.5 1)
(2 1.5 0)
(1.5 1.5 0)
(1.5 2 0)
(1.5 1 0)
(1 1.5 0)
(2 1.5 0.5)
(2 2 0.5)
(2 1 0.5)
(1 2 0.5)
(1.5 2 0.5)
(1 1.5 0.5)
(1.5 1 0.5)
)
96
(
((8 1 9) 0)
((9 3 10) 0)
((10 0 8) 0)
((8 9 10) 0)
((9 1 11) 0)
((11 2 12) 0)
((12 3 9) 0)
((9 11 12) 0)
((13 6 14) 0)
((14 4 15) 0)
((15 7 13) 0)
((13 14 15) 0)
((14 6 16) 0)
((16 5 17) 0)
((17 4 14) 0)
((14 16 17) 0)
((12 2 18) 0)
((18 7 19) 0)
((19 3 12) 0)
((12 18 19) 0)
((13 7 18) 0)
((18 2 20) 0)
((20 6 13) 0)
((13 18 20) 0)
((15 4 21) 0)
((21 0 22) 0)
((22 7 15) 0)
((15 21 22) 0)
((10 3 19) 0)
((19 7 22) 0)
((22 0 10) 0)
((10 19 22) 0)
((4 17 21) 0)
((0 21 23) 0)
((5 23 17) 0)
((21 17 23) 0)
((8 0 23) 0)
((23 5 24) 0)
((24 1 8) 0)
((8 23 24) 0)
((11 1 24) 0)
((24 5 25) 0)
((25 2 11) 0)
((11 24 25) 0)
((16 6 20) 0)
((20 2 25) 0)
((25 5 16) 0)
((16 20 25) 0)
((32 3 33) 0)
((33 28 34) 0)
((34 26 32) 0)
((32 33 34) 0)
((33 3 35) 0)
((35 27 36) 0)
((36 28 33) 0)
((33 35 36) 0)
((37 30 38) 0)
((38 29 39) 0)
((39 31 37) 0)
((37 38 39) 0)
((38 30 40) 0)
((40 7 41) 0)
((41 29 38) 0)
((38 40 41) 0)
((36 27 42) 0)
((42 31 43) 0)
((43 28 36) 0)
((36 42 43) 0)
((37 31 42) 0)
((42 27 44) 0)
((44 30 37) 0)
((37 42 44) 0)
((39 29 45) 0)
((45 26 46) 0)
((46 31 39) 0)
((39 45 46) 0)
((34 28 43) 0)
((43 31 46) 0)
((46 26 34) 0)
((34 43 46) 0)
((29 41 45) 0)
((26 45 47) 0)
((7 47 41) 0)
((45 41 47) 0)
((32 26 47) 0)
((47 7 19) 0)
((19 3 32) 0)
((32 47 19) 0)
((35 3 19) 0)
((19 7 48) 0)
((48 27 35) 0)
((35 19 48) 0)
((40 30 44) 0)
((44 27 48) 0)
((48 7 40) 0)
((40 44 48) 0)
)

View File

@ -0,0 +1,3 @@
surfaceSubset.C
EXE = $(FOAM_APPBIN)/surfaceSubset

Some files were not shown because too many files have changed in this diff Show More