1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright held by original author
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 the
13 Free Software Foundation; either version 2 of the License, or (at your
14 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, write to the Free Software Foundation,
23 Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 \*---------------------------------------------------------------------------*/
27 #include "triSurfaceMesh.H"
29 #include "addToRunTimeSelectionTable.H"
31 #include "triSurfaceFields.H"
33 #include "PackedBoolList.H"
35 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
40 defineTypeNameAndDebug(triSurfaceMesh, 0);
41 addToRunTimeSelectionTable(searchableSurface, triSurfaceMesh, dict);
45 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
47 //// Special version of Time::findInstance that does not check headerOk
48 //// to search for instances of raw files
49 //Foam::word Foam::triSurfaceMesh::findRawInstance
51 // const Time& runTime,
52 // const fileName& dir,
56 // // Check current time first
57 // if (isFile(runTime.path()/runTime.timeName()/dir/name))
59 // return runTime.timeName();
61 // instantList ts = runTime.times();
64 // for (instanceI = ts.size()-1; instanceI >= 0; --instanceI)
66 // if (ts[instanceI].value() <= runTime.timeOutputValue())
72 // // continue searching from here
73 // for (; instanceI >= 0; --instanceI)
75 // if (isFile(runTime.path()/ts[instanceI].name()/dir/name))
77 // return ts[instanceI].name();
82 // // not in any of the time directories, try constant
84 // // Note. This needs to be a hard-coded constant, rather than the
85 // // constant function of the time, because the latter points to
86 // // the case constant directory in parallel cases
88 // if (isFile(runTime.path()/runTime.constant()/dir/name))
90 // return runTime.constant();
95 // "searchableSurfaces::findRawInstance"
96 // "(const Time&, const fileName&, const word&)"
97 // ) << "Cannot find file \"" << name << "\" in directory "
98 // << runTime.constant()/dir
99 // << exit(FatalError);
101 // return runTime.constant();
105 //- Check file existence
106 const Foam::fileName& Foam::triSurfaceMesh::checkFile
108 const fileName& fName,
109 const fileName& objectName
116 "triSurfaceMesh::checkFile(const fileName&, const fileName&)"
117 ) << "Cannot find triSurfaceMesh starting from "
118 << objectName << exit(FatalError);
124 bool Foam::triSurfaceMesh::addFaceToEdge
127 EdgeMap<label>& facesPerEdge
130 EdgeMap<label>::iterator eFnd = facesPerEdge.find(e);
131 if (eFnd != facesPerEdge.end())
141 facesPerEdge.insert(e, 1);
147 bool Foam::triSurfaceMesh::isSurfaceClosed() const
149 // Construct pointFaces. Let's hope surface has compact point
151 labelListList pointFaces;
152 invertManyToMany(points().size(), *this, pointFaces);
154 // Loop over all faces surrounding point. Count edges emanating from point.
155 // Every edge should be used by two faces exactly.
156 // To prevent doing work twice per edge only look at edges to higher
158 EdgeMap<label> facesPerEdge(100);
159 forAll(pointFaces, pointI)
161 const labelList& pFaces = pointFaces[pointI];
163 facesPerEdge.clear();
166 const labelledTri& f = triSurface::operator[](pFaces[i]);
167 label fp = findIndex(f, pointI);
169 // Something weird: if I expand the code of addFaceToEdge in both
170 // below instances it gives a segmentation violation on some
171 // surfaces. Compiler (4.3.2) problem?
175 label nextPointI = f[f.fcIndex(fp)];
177 if (nextPointI > pointI)
179 bool okFace = addFaceToEdge
181 edge(pointI, nextPointI),
191 label prevPointI = f[f.rcIndex(fp)];
193 if (prevPointI > pointI)
195 bool okFace = addFaceToEdge
197 edge(pointI, prevPointI),
208 // Check for any edges used only once.
209 forAllConstIter(EdgeMap<label>, facesPerEdge, iter)
222 // Gets all intersections after initial one. Adds smallVec and starts tracking
224 void Foam::triSurfaceMesh::getNextIntersections
226 const indexedOctree<treeDataTriSurface>& octree,
229 const vector& smallVec,
230 DynamicList<pointIndexHit, 1, 1>& hits
233 const vector dirVec(end-start);
234 const scalar magSqrDirVec(magSqr(dirVec));
236 // Initial perturbation amount
237 vector perturbVec(smallVec);
241 // Start tracking from last hit.
242 point pt = hits[hits.size()-1].hitPoint() + perturbVec;
244 if (((pt-start)&dirVec) > magSqrDirVec)
249 // See if any intersection between pt and end
250 pointIndexHit inter = octree.findLine(pt, end);
257 // Check if already found this intersection
258 bool duplicateHit = false;
259 forAllReverse(hits, i)
261 if (hits[i].index() == inter.index())
271 // Hit same triangle again. Increase perturbVec and try again.
278 // Restore perturbVec
279 perturbVec = smallVec;
285 void Foam::triSurfaceMesh::calcBounds(boundBox& bb, label& nPoints) const
287 // Unfortunately nPoints constructs meshPoints() so do compact version
290 const triSurface& s = static_cast<const triSurface&>(*this);
292 PackedBoolList pointIsUsed(points().size());
295 bb = boundBox::invertedBox;
299 const labelledTri& f = s[triI];
303 label pointI = f[fp];
304 if (pointIsUsed.set(pointI, 1u))
306 bb.min() = ::Foam::min(bb.min(), points()[pointI]);
307 bb.max() = ::Foam::max(bb.max(), points()[pointI]);
315 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
317 Foam::triSurfaceMesh::triSurfaceMesh(const IOobject& io, const triSurface& s)
319 searchableSurface(io),
330 false // searchableSurface already registered under name
334 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()),
384 Foam::triSurfaceMesh::triSurfaceMesh
387 const dictionary& dict
390 searchableSurface(io),
395 // io.time().findInstance(io.local(), word::null),
400 // io.registerObject()
403 // Reused found instance in objectRegistry
409 static_cast<const searchableSurface&>(*this).instance(),
414 false // searchableSurface already registered under name
421 searchableSurface::filePath(),
422 searchableSurface::objectPath()
425 tolerance_(indexedOctree<treeDataTriSurface>::perturbTol()),
429 scalar scaleFactor = 0;
431 // allow rescaling of the surface points
432 // eg, CAD geometries are often done in millimeters
433 if (dict.readIfPresent("scale", scaleFactor) && scaleFactor > 0)
435 Info<< searchableSurface::name() << " : using scale " << scaleFactor
437 triSurface::scalePoints(scaleFactor);
441 // Have optional non-standard search tolerance for gappy surfaces.
442 if (dict.readIfPresent("tolerance", tolerance_) && tolerance_ > 0)
444 Info<< searchableSurface::name() << " : using intersection tolerance "
445 << tolerance_ << endl;
449 // Have optional non-standard tree-depth to limit storage.
450 if (dict.readIfPresent("maxTreeDepth", maxTreeDepth_) && maxTreeDepth_ > 0)
452 Info<< searchableSurface::name() << " : using maximum tree depth "
453 << maxTreeDepth_ << endl;
458 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
460 Foam::triSurfaceMesh::~triSurfaceMesh()
466 void Foam::triSurfaceMesh::clearOut()
470 triSurface::clearOut();
474 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
476 Foam::pointField Foam::triSurfaceMesh::coordinates() const
478 // Use copy to calculate face centres so they don't get stored
479 return PrimitivePatch<labelledTri, SubList, const pointField&>
481 SubList<labelledTri>(*this, triSurface::size()),
487 void Foam::triSurfaceMesh::movePoints(const pointField& newPoints)
491 triSurface::movePoints(newPoints);
495 const Foam::indexedOctree<Foam::treeDataTriSurface>&
496 Foam::triSurfaceMesh::tree() const
500 // Calculate bb without constructing local point numbering.
503 calcBounds(bb, nPoints);
505 if (nPoints != points().size())
507 WarningIn("triSurfaceMesh::tree() const")
508 << "Surface " << searchableSurface::name()
509 << " does not have compact point numbering."
510 << " Of " << points().size() << " only " << nPoints
511 << " are used. This might give problems in some routines."
516 // Random number generator. Bit dodgy since not exactly random ;-)
517 Random rndGen(65431);
519 // Slightly extended bb. Slightly off-centred just so on symmetric
520 // geometry there are less face/edge aligned items.
521 bb = bb.extend(rndGen, 1E-4);
522 bb.min() -= point(ROOTVSMALL, ROOTVSMALL, ROOTVSMALL);
523 bb.max() += point(ROOTVSMALL, ROOTVSMALL, ROOTVSMALL);
525 scalar oldTol = indexedOctree<treeDataTriSurface>::perturbTol();
526 indexedOctree<treeDataTriSurface>::perturbTol() = tolerance_;
530 new indexedOctree<treeDataTriSurface>
532 treeDataTriSurface(*this),
534 maxTreeDepth_, // maxLevel
540 indexedOctree<treeDataTriSurface>::perturbTol() = oldTol;
547 const Foam::indexedOctree<Foam::treeDataEdge>&
548 Foam::triSurfaceMesh::edgeTree() const
550 if (edgeTree_.empty())
565 calcBounds(bb, nPoints);
567 // Random number generator. Bit dodgy since not exactly random ;-)
568 Random rndGen(65431);
570 // Slightly extended bb. Slightly off-centred just so on symmetric
571 // geometry there are less face/edge aligned items.
573 bb = bb.extend(rndGen, 1E-4);
574 bb.min() -= point(ROOTVSMALL, ROOTVSMALL, ROOTVSMALL);
575 bb.max() += point(ROOTVSMALL, ROOTVSMALL, ROOTVSMALL);
579 new indexedOctree<treeDataEdge>
585 localPoints(), // points
586 bEdges // selected edges
589 maxTreeDepth_, // maxLevel
599 const Foam::wordList& Foam::triSurfaceMesh::regions() const
601 if (regions_.empty())
603 regions_.setSize(patches().size());
604 forAll(regions_, regionI)
606 regions_[regionI] = patches()[regionI].name();
613 // Find out if surface is closed.
614 bool Foam::triSurfaceMesh::hasVolumeType() const
616 if (surfaceClosed_ == -1)
618 if (isSurfaceClosed())
628 return surfaceClosed_ == 1;
632 void Foam::triSurfaceMesh::findNearest
634 const pointField& samples,
635 const scalarField& nearestDistSqr,
636 List<pointIndexHit>& info
639 const indexedOctree<treeDataTriSurface>& octree = tree();
641 info.setSize(samples.size());
643 scalar oldTol = indexedOctree<treeDataTriSurface>::perturbTol();
644 indexedOctree<treeDataTriSurface>::perturbTol() = tolerance_;
648 static_cast<pointIndexHit&>(info[i]) = octree.findNearest
655 indexedOctree<treeDataTriSurface>::perturbTol() = oldTol;
659 void Foam::triSurfaceMesh::findLine
661 const pointField& start,
662 const pointField& end,
663 List<pointIndexHit>& info
666 const indexedOctree<treeDataTriSurface>& octree = tree();
668 info.setSize(start.size());
670 scalar oldTol = indexedOctree<treeDataTriSurface>::perturbTol();
671 indexedOctree<treeDataTriSurface>::perturbTol() = tolerance_;
675 static_cast<pointIndexHit&>(info[i]) = octree.findLine
682 indexedOctree<treeDataTriSurface>::perturbTol() = oldTol;
686 void Foam::triSurfaceMesh::findLineAny
688 const pointField& start,
689 const pointField& end,
690 List<pointIndexHit>& info
693 const indexedOctree<treeDataTriSurface>& octree = tree();
695 info.setSize(start.size());
697 scalar oldTol = indexedOctree<treeDataTriSurface>::perturbTol();
698 indexedOctree<treeDataTriSurface>::perturbTol() = tolerance_;
702 static_cast<pointIndexHit&>(info[i]) = octree.findLineAny
709 indexedOctree<treeDataTriSurface>::perturbTol() = oldTol;
713 void Foam::triSurfaceMesh::findLineAll
715 const pointField& start,
716 const pointField& end,
717 List<List<pointIndexHit> >& info
720 const indexedOctree<treeDataTriSurface>& octree = tree();
722 info.setSize(start.size());
724 scalar oldTol = indexedOctree<treeDataTriSurface>::perturbTol();
725 indexedOctree<treeDataTriSurface>::perturbTol() = tolerance_;
728 DynamicList<pointIndexHit, 1, 1> hits;
731 // To find all intersections we add a small vector to the last intersection
732 // This is chosen such that
733 // - it is significant (SMALL is smallest representative relative tolerance;
734 // we need something bigger since we're doing calculations)
735 // - if the start-end vector is zero we still progress
736 const vectorField dirVec(end-start);
737 const scalarField magSqrDirVec(magSqr(dirVec));
738 const vectorField smallVec
740 indexedOctree<treeDataTriSurface>::perturbTol()*dirVec
741 + vector(ROOTVSMALL,ROOTVSMALL,ROOTVSMALL)
744 forAll(start, pointI)
746 // See if any intersection between pt and end
747 pointIndexHit inter = octree.findLine(start[pointI], end[pointI]);
763 info[pointI].transfer(hits);
767 info[pointI].clear();
771 indexedOctree<treeDataTriSurface>::perturbTol() = oldTol;
775 void Foam::triSurfaceMesh::getRegion
777 const List<pointIndexHit>& info,
781 region.setSize(info.size());
786 region[i] = triSurface::operator[](info[i].index()).region();
796 void Foam::triSurfaceMesh::getNormal
798 const List<pointIndexHit>& info,
802 normal.setSize(info.size());
808 label triI = info[i].index();
810 //normal[i] = faceNormals()[triI];
813 normal[i] = triSurface::operator[](triI).normal(points());
814 normal[i] /= mag(normal[i]) + VSMALL;
819 normal[i] = vector::zero;
825 void Foam::triSurfaceMesh::setField(const labelList& values)
827 autoPtr<triSurfaceLabelField> fldPtr
829 new triSurfaceLabelField
834 objectRegistry::time().timeName(), // instance
835 "triSurface", // local
846 // Store field on triMesh
847 fldPtr.ptr()->store();
851 void Foam::triSurfaceMesh::getField
853 const List<pointIndexHit>& info,
857 if (foundObject<triSurfaceLabelField>("values"))
859 values.setSize(info.size());
861 const triSurfaceLabelField& fld = lookupObject<triSurfaceLabelField>
870 values[i] = fld[info[i].index()];
877 void Foam::triSurfaceMesh::getVolumeType
879 const pointField& points,
880 List<volumeType>& volType
883 volType.setSize(points.size());
885 scalar oldTol = indexedOctree<treeDataTriSurface>::perturbTol();
886 indexedOctree<treeDataTriSurface>::perturbTol() = tolerance_;
888 forAll(points, pointI)
890 const point& pt = points[pointI];
892 // - use cached volume type per each tree node
893 // - cheat conversion since same values
894 volType[pointI] = static_cast<volumeType>(tree().getVolumeType(pt));
897 indexedOctree<treeDataTriSurface>::perturbTol() = oldTol;
901 //- Write using given format, version and compression
902 bool Foam::triSurfaceMesh::writeObject
904 IOstream::streamFormat fmt,
905 IOstream::versionNumber ver,
906 IOstream::compressionType cmp
909 fileName fullPath(searchableSurface::objectPath());
911 if (!mkDir(fullPath.path()))
916 triSurface::write(fullPath);
918 if (!isFile(fullPath))
923 //return objectRegistry::writeObject(fmt, ver, cmp);
928 // ************************************************************************* //