/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2016-2017 OpenFOAM Foundation Copyright (C) 2019-2022 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. OpenFOAM is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. OpenFOAM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenFOAM. If not, see . \*---------------------------------------------------------------------------*/ #include "searchableExtrudedCircle.H" #include "addToRunTimeSelectionTable.H" #include "Time.H" #include "edgeMesh.H" #include "indexedOctree.H" #include "treeDataEdge.H" #include "linearInterpolationWeights.H" #include "quaternion.H" // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // namespace Foam { defineTypeNameAndDebug(searchableExtrudedCircle, 0); addToRunTimeSelectionTable ( searchableSurface, searchableExtrudedCircle, dict ); addNamedToRunTimeSelectionTable ( searchableSurface, searchableExtrudedCircle, dict, extrudedCircle ); } // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // Foam::searchableExtrudedCircle::searchableExtrudedCircle ( const IOobject& io, const dictionary& dict ) : searchableSurface(io), eMeshPtr_ ( edgeMesh::New ( IOobject ( dict.get("file"), // name io.time().constant(), // instance "geometry", // local io.time(), // registry IOobject::MUST_READ, IOobject::NO_WRITE, IOobject::NO_REGISTER ).objectPath() ) ), radius_(dict.get("radius")) { const edgeMesh& eMesh = eMeshPtr_(); const pointField& points = eMesh.points(); const edgeList& edges = eMesh.edges(); bounds() = boundBox(points, false); // Make the boundBox into a perfect cube around its centre const scalar halfWidth = mag(0.5*bounds().span()); bounds().reset(bounds().centre()); bounds().grow(halfWidth); // Slightly extended bb. Slightly off-centred just so on symmetric // geometry there are less face/edge aligned items. treeBoundBox bb(bounds()); bb.grow(ROOTVSMALL); edgeTree_.reset ( new indexedOctree ( treeDataEdge(edges, points), // All edges bb, // overall search domain 8, // maxLevel 10, // leafsize 3.0 // duplicity ) ); } // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * // Foam::searchableExtrudedCircle::~searchableExtrudedCircle() {} // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // const Foam::wordList& Foam::searchableExtrudedCircle::regions() const { if (regions_.empty()) { regions_.setSize(1); regions_[0] = "region0"; } return regions_; } Foam::label Foam::searchableExtrudedCircle::size() const { return eMeshPtr_().points().size(); } Foam::tmp Foam::searchableExtrudedCircle::coordinates() const { return eMeshPtr_().points(); } void Foam::searchableExtrudedCircle::boundingSpheres ( pointField& centres, scalarField& radiusSqr ) const { centres = eMeshPtr_().points(); radiusSqr.setSize(centres.size()); radiusSqr = Foam::sqr(radius_); // Add a bit to make sure all points are tested inside radiusSqr += Foam::sqr(SMALL); } void Foam::searchableExtrudedCircle::findNearest ( const pointField& samples, const scalarField& nearestDistSqr, List& info ) const { const indexedOctree& tree = edgeTree_(); info.setSize(samples.size()); forAll(samples, i) { const scalar nearestDist = Foam::sqrt(nearestDistSqr[i]); const scalar searchDistSqr = Foam::sqr(nearestDist+radius_); // Find nearest on central edge info[i] = tree.findNearest(samples[i], searchDistSqr); if (info[i].hit()) { // Derive distance to nearest surface from distance to nearest edge const vector d(samples[i] - info[i].point()); const scalar s(mag(d)); if (s < ROOTVSMALL) { // Point is on edge. TBD. info[i].setMiss(); } else { const scalar distToSurface = radius_-s; if (mag(distToSurface) > nearestDist) { info[i].setMiss(); } else { info[i].setPoint(info[i].point() + d/s*radius_); } } } } } void Foam::searchableExtrudedCircle::findParametricNearest ( const point& start, const point& end, const scalarField& rawLambdas, const scalarField& nearestDistSqr, List& info ) const { const edgeMesh& mesh = eMeshPtr_(); const indexedOctree& tree = edgeTree_(); const auto& treeData = tree.shapes(); const edgeList& edges = mesh.edges(); const pointField& points = mesh.points(); const labelListList& pointEdges = mesh.pointEdges(); const scalar maxDistSqr = bounds().magSqr(); // Normalise lambdas const scalarField lambdas ( (rawLambdas-rawLambdas[0]) /(rawLambdas.last()-rawLambdas[0]) ); // Nearest point on curve and local axis direction pointField curvePoints(lambdas.size()); vectorField axialVecs(lambdas.size()); const pointIndexHit startInfo = tree.findNearest(start, maxDistSqr); curvePoints[0] = startInfo.hitPoint(); axialVecs[0] = treeData.line(startInfo.index()).vec(); const pointIndexHit endInfo = tree.findNearest(end, maxDistSqr); curvePoints.last() = endInfo.hitPoint(); axialVecs.last() = treeData.line(endInfo.index()).vec(); scalarField curveLambdas(points.size(), -1.0); { scalar endDistance = -1.0; // Determine edge lengths walking from start to end. const point& start = curvePoints[0]; const point& end = curvePoints.last(); label edgei = startInfo.index(); const edge& startE = edges[edgei]; label pointi = startE[0]; if ((startE.vec(points)&(end-start)) > 0) { pointi = startE[1]; } curveLambdas[pointi] = mag(points[pointi]-curvePoints[0]); label otherPointi = startE.otherVertex(pointi); curveLambdas[otherPointi] = -mag(points[otherPointi]-curvePoints[0]); //Pout<< "for point:" << points[pointi] << " have distance " // << curveLambdas[pointi] << endl; while (true) { const labelList& pEdges = pointEdges[pointi]; if (pEdges.size() == 1) { break; } else if (pEdges.size() != 2) { FatalErrorInFunction << "Curve " << name() << " is not a single path. This is not supported" << exit(FatalError); break; } label oldEdgei = edgei; if (pEdges[0] == oldEdgei) { edgei = pEdges[1]; } else { edgei = pEdges[0]; } if (edgei == endInfo.index()) { endDistance = curveLambdas[pointi] + mag(end-points[pointi]); //Pout<< "Found end edge:" << edges[edgei].centre(points) // << " endPt:" << end // << " point before:" << points[pointi] // << " accumulated length:" << endDistance << endl; } label oldPointi = pointi; pointi = edges[edgei].otherVertex(oldPointi); if (curveLambdas[pointi] >= 0) { break; } curveLambdas[pointi] = curveLambdas[oldPointi] + edges[edgei].mag(points); } // Normalise curveLambdas forAll(curveLambdas, i) { if (curveLambdas[i] >= 0) { curveLambdas[i] /= endDistance; } } } // Interpolation engine linearInterpolationWeights interpolator(curveLambdas); // Find wanted location along curve labelList indices; scalarField weights; for (label i = 1; i < curvePoints.size()-1; i++) { interpolator.valueWeights(lambdas[i], indices, weights); if (indices.size() == 1) { // On outside of curve. Choose one of the connected edges. label pointi = indices[0]; const point& p0 = points[pointi]; label edge0 = pointEdges[pointi][0]; const edge& e0 = edges[edge0]; axialVecs[i] = e0.vec(points); curvePoints[i] = weights[0]*p0; } else if (indices.size() == 2) { const point& p0 = points[indices[0]]; const point& p1 = points[indices[1]]; axialVecs[i] = p1-p0; curvePoints[i] = weights[0]*p0+weights[1]*p1; } } axialVecs /= mag(axialVecs); // Now we have the lambdas, curvePoints and axialVecs. info.setSize(lambdas.size()); info = pointIndexHit(); // Given the current lambdas interpolate radial direction inbetween // endpoints (all projected onto the starting coordinate system) quaternion qStart; vector radialStart; { radialStart = start-curvePoints[0]; radialStart.removeCollinear(axialVecs[0]); radialStart.normalise(); qStart = quaternion(radialStart, 0.0); info[0] = pointIndexHit(true, start, 0); } quaternion qProjectedEnd; { vector radialEnd(end-curvePoints.last()); radialEnd.removeCollinear(axialVecs.last()); radialEnd.normalise(); vector projectedEnd = radialEnd; projectedEnd.removeCollinear(axialVecs[0]); projectedEnd.normalise(); qProjectedEnd = quaternion(projectedEnd, 0.0); info.last() = pointIndexHit(true, end, 0); } for (label i = 1; i < lambdas.size()-1; i++) { quaternion q(slerp(qStart, qProjectedEnd, lambdas[i])); vector radialDir(q.transform(radialStart)); radialDir.removeCollinear(axialVecs[i]); radialDir.normalise(); info[i] = pointIndexHit(true, curvePoints[i]+radius_*radialDir, 0); } } void Foam::searchableExtrudedCircle::getRegion ( const List& info, labelList& region ) const { region.setSize(info.size()); region = 0; } void Foam::searchableExtrudedCircle::getNormal ( const List& info, vectorField& normal ) const { const edgeMesh& mesh = eMeshPtr_(); const indexedOctree& tree = edgeTree_(); const edgeList& edges = mesh.edges(); const pointField& points = mesh.points(); normal.setSize(info.size()); normal = Zero; const scalar distSqr = bounds().magSqr(); forAll(info, i) { if (info[i].hit()) { // Find nearest on curve pointIndexHit curvePt = tree.findNearest(info[i].point(), distSqr); normal[i] = info[i].hitPoint()-curvePt.hitPoint(); // Subtract axial direction const vector axialVec = edges[curvePt.index()].unitVec(points); normal[i].removeCollinear(axialVec); normal[i].normalise(); } } } // ************************************************************************* //