1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 2011 OpenFOAM Foundation
7 -------------------------------------------------------------------------------
9 This file is part of OpenFOAM.
11 OpenFOAM is free software: you can redistribute it and/or modify it
12 under the terms of the GNU General Public License as published by
13 the Free Software Foundation, either version 3 of the License, or
14 (at your option) any later version.
16 OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 You should have received a copy of the GNU General Public License
22 along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
24 \*---------------------------------------------------------------------------*/
26 #include "triSurfaceMesh.H"
28 #include "addToRunTimeSelectionTable.H"
30 #include "triSurfaceFields.H"
32 #include "PackedBoolList.H"
34 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
39 defineTypeNameAndDebug(triSurfaceMesh, 0);
40 addToRunTimeSelectionTable(searchableSurface, triSurfaceMesh, dict);
44 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
46 //// Special version of Time::findInstance that does not check headerOk
47 //// to search for instances of raw files
48 //Foam::word Foam::triSurfaceMesh::findRawInstance
50 // const Time& runTime,
51 // const fileName& dir,
55 // // Check current time first
56 // if (isFile(runTime.path()/runTime.timeName()/dir/name))
58 // return runTime.timeName();
60 // instantList ts = runTime.times();
63 // for (instanceI = ts.size()-1; instanceI >= 0; --instanceI)
65 // if (ts[instanceI].value() <= runTime.timeOutputValue())
71 // // continue searching from here
72 // for (; instanceI >= 0; --instanceI)
74 // if (isFile(runTime.path()/ts[instanceI].name()/dir/name))
76 // return ts[instanceI].name();
81 // // not in any of the time directories, try constant
83 // // Note. This needs to be a hard-coded constant, rather than the
84 // // constant function of the time, because the latter points to
85 // // the case constant directory in parallel cases
87 // if (isFile(runTime.path()/runTime.constant()/dir/name))
89 // return runTime.constant();
94 // "searchableSurfaces::findRawInstance"
95 // "(const Time&, const fileName&, const word&)"
96 // ) << "Cannot find file \"" << name << "\" in directory "
97 // << runTime.constant()/dir
98 // << exit(FatalError);
100 // return runTime.constant();
104 //- Check file existence
105 const Foam::fileName& Foam::triSurfaceMesh::checkFile
107 const fileName& fName,
108 const fileName& objectName
115 "triSurfaceMesh::checkFile(const fileName&, const fileName&)"
116 ) << "Cannot find triSurfaceMesh starting from "
117 << objectName << exit(FatalError);
123 bool Foam::triSurfaceMesh::addFaceToEdge
126 EdgeMap<label>& facesPerEdge
129 EdgeMap<label>::iterator eFnd = facesPerEdge.find(e);
130 if (eFnd != facesPerEdge.end())
140 facesPerEdge.insert(e, 1);
146 bool Foam::triSurfaceMesh::isSurfaceClosed() const
148 // Construct pointFaces. Let's hope surface has compact point
150 labelListList pointFaces;
151 invertManyToMany(points().size(), *this, pointFaces);
153 // Loop over all faces surrounding point. Count edges emanating from point.
154 // Every edge should be used by two faces exactly.
155 // To prevent doing work twice per edge only look at edges to higher
157 EdgeMap<label> facesPerEdge(100);
158 forAll(pointFaces, pointI)
160 const labelList& pFaces = pointFaces[pointI];
162 facesPerEdge.clear();
165 const triSurface::FaceType& f = triSurface::operator[](pFaces[i]);
166 label fp = findIndex(f, pointI);
168 // Something weird: if I expand the code of addFaceToEdge in both
169 // below instances it gives a segmentation violation on some
170 // surfaces. Compiler (4.3.2) problem?
174 label nextPointI = f[f.fcIndex(fp)];
176 if (nextPointI > pointI)
178 bool okFace = addFaceToEdge
180 edge(pointI, nextPointI),
190 label prevPointI = f[f.rcIndex(fp)];
192 if (prevPointI > pointI)
194 bool okFace = addFaceToEdge
196 edge(pointI, prevPointI),
207 // Check for any edges used only once.
208 forAllConstIter(EdgeMap<label>, facesPerEdge, iter)
221 // Gets all intersections after initial one. Adds smallVec and starts tracking
223 void Foam::triSurfaceMesh::getNextIntersections
225 const indexedOctree<treeDataTriSurface>& octree,
228 const vector& smallVec,
229 DynamicList<pointIndexHit, 1, 1>& hits
232 const vector dirVec(end-start);
233 const scalar magSqrDirVec(magSqr(dirVec));
235 // Initial perturbation amount
236 vector perturbVec(smallVec);
240 // Start tracking from last hit.
241 point pt = hits.last().hitPoint() + perturbVec;
243 if (((pt-start)&dirVec) > magSqrDirVec)
248 // See if any intersection between pt and end
249 pointIndexHit inter = octree.findLine(pt, end);
256 // Check if already found this intersection
257 bool duplicateHit = false;
258 forAllReverse(hits, i)
260 if (hits[i].index() == inter.index())
270 // Hit same triangle again. Increase perturbVec and try again.
277 // Restore perturbVec
278 perturbVec = smallVec;
284 void Foam::triSurfaceMesh::calcBounds(boundBox& bb, label& nPoints) const
286 // Unfortunately nPoints constructs meshPoints() so do compact version
289 const triSurface& s = static_cast<const triSurface&>(*this);
291 PackedBoolList pointIsUsed(points().size());
294 bb = boundBox::invertedBox;
298 const triSurface::FaceType& f = s[faceI];
302 label pointI = f[fp];
303 if (pointIsUsed.set(pointI, 1u))
305 bb.min() = ::Foam::min(bb.min(), points()[pointI]);
306 bb.max() = ::Foam::max(bb.max(), points()[pointI]);
314 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
316 Foam::triSurfaceMesh::triSurfaceMesh(const IOobject& io, const triSurface& s)
318 searchableSurface(io),
329 false // searchableSurface already registered under name
333 tolerance_(indexedOctree<treeDataTriSurface>::perturbTol()),
340 Foam::triSurfaceMesh::triSurfaceMesh(const IOobject& io)
342 // Find instance for triSurfaceMesh
343 searchableSurface(io),
348 // io.time().findInstance(io.local(), word::null),
353 // io.registerObject()
356 // Reused found instance in objectRegistry
362 static_cast<const searchableSurface&>(*this).instance(),
367 false // searchableSurface already registered under name
374 searchableSurface::filePath(),
375 searchableSurface::objectPath()
378 tolerance_(indexedOctree<treeDataTriSurface>::perturbTol()),
385 Foam::triSurfaceMesh::triSurfaceMesh
388 const dictionary& dict
391 searchableSurface(io),
396 // io.time().findInstance(io.local(), word::null),
401 // io.registerObject()
404 // Reused found instance in objectRegistry
410 static_cast<const searchableSurface&>(*this).instance(),
415 false // searchableSurface already registered under name
422 searchableSurface::filePath(),
423 searchableSurface::objectPath()
426 tolerance_(indexedOctree<treeDataTriSurface>::perturbTol()),
431 scalar scaleFactor = 0;
433 // allow rescaling of the surface points
434 // eg, CAD geometries are often done in millimeters
435 if (dict.readIfPresent("scale", scaleFactor) && scaleFactor > 0)
437 Info<< searchableSurface::name() << " : using scale " << scaleFactor
439 triSurface::scalePoints(scaleFactor);
443 // Have optional non-standard search tolerance for gappy surfaces.
444 if (dict.readIfPresent("tolerance", tolerance_) && tolerance_ > 0)
446 Info<< searchableSurface::name() << " : using intersection tolerance "
447 << tolerance_ << endl;
450 // Have optional minimum quality for normal calculation
451 if (dict.readIfPresent("minQuality", minQuality_) && minQuality_ > 0)
453 Info<< searchableSurface::name()
454 << " : ignoring triangles with quality < "
455 << minQuality_ << " for normals calculation." << endl;
458 // Have optional non-standard tree-depth to limit storage.
459 if (dict.readIfPresent("maxTreeDepth", maxTreeDepth_) && maxTreeDepth_ > 0)
461 Info<< searchableSurface::name() << " : using maximum tree depth "
462 << maxTreeDepth_ << endl;
467 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
469 Foam::triSurfaceMesh::~triSurfaceMesh()
475 void Foam::triSurfaceMesh::clearOut()
479 triSurface::clearOut();
483 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
485 Foam::pointField Foam::triSurfaceMesh::coordinates() const
487 // Use copy to calculate face centres so they don't get stored
488 return PrimitivePatch<triSurface::FaceType, SubList, const pointField&>
490 SubList<triSurface::FaceType>(*this, triSurface::size()),
496 void Foam::triSurfaceMesh::movePoints(const pointField& newPoints)
500 triSurface::movePoints(newPoints);
504 const Foam::indexedOctree<Foam::treeDataTriSurface>&
505 Foam::triSurfaceMesh::tree() const
509 // Calculate bb without constructing local point numbering.
512 calcBounds(bb, nPoints);
514 if (nPoints != points().size())
516 WarningIn("triSurfaceMesh::tree() const")
517 << "Surface " << searchableSurface::name()
518 << " does not have compact point numbering."
519 << " Of " << points().size() << " only " << nPoints
520 << " are used. This might give problems in some routines."
525 // Random number generator. Bit dodgy since not exactly random ;-)
526 Random rndGen(65431);
528 // Slightly extended bb. Slightly off-centred just so on symmetric
529 // geometry there are less face/edge aligned items.
530 bb = bb.extend(rndGen, 1E-4);
531 bb.min() -= point(ROOTVSMALL, ROOTVSMALL, ROOTVSMALL);
532 bb.max() += point(ROOTVSMALL, ROOTVSMALL, ROOTVSMALL);
534 scalar oldTol = indexedOctree<treeDataTriSurface>::perturbTol();
535 indexedOctree<treeDataTriSurface>::perturbTol() = tolerance_;
539 new indexedOctree<treeDataTriSurface>
541 treeDataTriSurface(*this, tolerance_),
543 maxTreeDepth_, // maxLevel
549 indexedOctree<treeDataTriSurface>::perturbTol() = oldTol;
556 const Foam::indexedOctree<Foam::treeDataEdge>&
557 Foam::triSurfaceMesh::edgeTree() const
559 if (edgeTree_.empty())
574 calcBounds(bb, nPoints);
576 // Random number generator. Bit dodgy since not exactly random ;-)
577 Random rndGen(65431);
579 // Slightly extended bb. Slightly off-centred just so on symmetric
580 // geometry there are less face/edge aligned items.
582 bb = bb.extend(rndGen, 1E-4);
583 bb.min() -= point(ROOTVSMALL, ROOTVSMALL, ROOTVSMALL);
584 bb.max() += point(ROOTVSMALL, ROOTVSMALL, ROOTVSMALL);
586 scalar oldTol = indexedOctree<treeDataTriSurface>::perturbTol();
587 indexedOctree<treeDataEdge>::perturbTol() = tolerance_;
591 new indexedOctree<treeDataEdge>
597 localPoints(), // points
598 bEdges // selected edges
601 maxTreeDepth_, // maxLevel
607 indexedOctree<treeDataEdge>::perturbTol() = oldTol;
613 const Foam::wordList& Foam::triSurfaceMesh::regions() const
615 if (regions_.empty())
617 regions_.setSize(patches().size());
618 forAll(regions_, regionI)
620 regions_[regionI] = patches()[regionI].name();
627 // Find out if surface is closed.
628 bool Foam::triSurfaceMesh::hasVolumeType() const
630 if (surfaceClosed_ == -1)
632 if (isSurfaceClosed())
642 return surfaceClosed_ == 1;
646 void Foam::triSurfaceMesh::findNearest
648 const pointField& samples,
649 const scalarField& nearestDistSqr,
650 List<pointIndexHit>& info
653 const indexedOctree<treeDataTriSurface>& octree = tree();
655 info.setSize(samples.size());
657 scalar oldTol = indexedOctree<treeDataTriSurface>::perturbTol();
658 indexedOctree<treeDataTriSurface>::perturbTol() = tolerance_;
662 static_cast<pointIndexHit&>(info[i]) = octree.findNearest
669 indexedOctree<treeDataTriSurface>::perturbTol() = oldTol;
673 void Foam::triSurfaceMesh::findLine
675 const pointField& start,
676 const pointField& end,
677 List<pointIndexHit>& info
680 const indexedOctree<treeDataTriSurface>& octree = tree();
682 info.setSize(start.size());
684 scalar oldTol = indexedOctree<treeDataTriSurface>::perturbTol();
685 indexedOctree<treeDataTriSurface>::perturbTol() = tolerance_;
689 static_cast<pointIndexHit&>(info[i]) = octree.findLine
696 indexedOctree<treeDataTriSurface>::perturbTol() = oldTol;
700 void Foam::triSurfaceMesh::findLineAny
702 const pointField& start,
703 const pointField& end,
704 List<pointIndexHit>& info
707 const indexedOctree<treeDataTriSurface>& octree = tree();
709 info.setSize(start.size());
711 scalar oldTol = indexedOctree<treeDataTriSurface>::perturbTol();
712 indexedOctree<treeDataTriSurface>::perturbTol() = tolerance_;
716 static_cast<pointIndexHit&>(info[i]) = octree.findLineAny
723 indexedOctree<treeDataTriSurface>::perturbTol() = oldTol;
727 void Foam::triSurfaceMesh::findLineAll
729 const pointField& start,
730 const pointField& end,
731 List<List<pointIndexHit> >& info
734 const indexedOctree<treeDataTriSurface>& octree = tree();
736 info.setSize(start.size());
738 scalar oldTol = indexedOctree<treeDataTriSurface>::perturbTol();
739 indexedOctree<treeDataTriSurface>::perturbTol() = tolerance_;
742 DynamicList<pointIndexHit, 1, 1> hits;
745 // To find all intersections we add a small vector to the last intersection
746 // This is chosen such that
747 // - it is significant (SMALL is smallest representative relative tolerance;
748 // we need something bigger since we're doing calculations)
749 // - if the start-end vector is zero we still progress
750 const vectorField dirVec(end-start);
751 const vectorField smallVec
753 indexedOctree<treeDataTriSurface>::perturbTol()*dirVec
754 + vector(ROOTVSMALL,ROOTVSMALL,ROOTVSMALL)
757 forAll(start, pointI)
759 // See if any intersection between pt and end
760 pointIndexHit inter = octree.findLine(start[pointI], end[pointI]);
776 info[pointI].transfer(hits);
780 info[pointI].clear();
784 indexedOctree<treeDataTriSurface>::perturbTol() = oldTol;
788 void Foam::triSurfaceMesh::getRegion
790 const List<pointIndexHit>& info,
794 region.setSize(info.size());
799 region[i] = triSurface::operator[](info[i].index()).region();
809 void Foam::triSurfaceMesh::getNormal
811 const List<pointIndexHit>& info,
815 normal.setSize(info.size());
817 if (minQuality_ >= 0)
819 // Make sure we don't use triangles with low quality since
820 // normal is not reliable.
822 const triSurface& s = static_cast<const triSurface&>(*this);
823 const labelListList& faceFaces = s.faceFaces();
829 label faceI = info[i].index();
831 scalar qual = s[faceI].tri(points()).quality();
833 if (qual < minQuality_)
835 // Search neighbouring triangles
836 const labelList& fFaces = faceFaces[faceI];
840 label nbrI = fFaces[j];
841 scalar nbrQual = s[nbrI].tri(points()).quality();
845 normal[i] = s[nbrI].normal(points());
851 normal[i] = s[faceI].normal(points());
853 normal[i] /= mag(normal[i]);
858 normal[i] = vector::zero;
868 label faceI = info[i].index();
870 //normal[i] = faceNormals()[faceI];
873 normal[i] = triSurface::operator[](faceI).normal(points());
874 normal[i] /= mag(normal[i]) + VSMALL;
879 normal[i] = vector::zero;
886 void Foam::triSurfaceMesh::setField(const labelList& values)
888 autoPtr<triSurfaceLabelField> fldPtr
890 new triSurfaceLabelField
895 objectRegistry::time().timeName(), // instance
896 "triSurface", // local
907 // Store field on triMesh
908 fldPtr.ptr()->store();
912 void Foam::triSurfaceMesh::getField
914 const List<pointIndexHit>& info,
918 if (foundObject<triSurfaceLabelField>("values"))
920 values.setSize(info.size());
922 const triSurfaceLabelField& fld = lookupObject<triSurfaceLabelField>
931 values[i] = fld[info[i].index()];
938 void Foam::triSurfaceMesh::getVolumeType
940 const pointField& points,
941 List<volumeType>& volType
944 volType.setSize(points.size());
946 scalar oldTol = indexedOctree<treeDataTriSurface>::perturbTol();
947 indexedOctree<treeDataTriSurface>::perturbTol() = tolerance_;
949 forAll(points, pointI)
951 const point& pt = points[pointI];
953 // - use cached volume type per each tree node
954 // - cheat conversion since same values
955 volType[pointI] = static_cast<volumeType>(tree().getVolumeType(pt));
958 indexedOctree<treeDataTriSurface>::perturbTol() = oldTol;
962 //- Write using given format, version and compression
963 bool Foam::triSurfaceMesh::writeObject
965 IOstream::streamFormat fmt,
966 IOstream::versionNumber ver,
967 IOstream::compressionType cmp
970 fileName fullPath(searchableSurface::objectPath());
972 if (!mkDir(fullPath.path()))
977 triSurface::write(fullPath);
979 if (!isFile(fullPath))
984 //return objectRegistry::writeObject(fmt, ver, cmp);
989 // ************************************************************************* //