/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2007-2019 PCOpt/NTUA
Copyright (C) 2013-2019 FOSS GP
Copyright (C) 2019 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 "NURBS3DCurve.H"
#include "vectorField.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
label NURBS3DCurve::sgn(const scalar val) const
{
return val>=scalar(0) ? 1 : -1;
}
scalar NURBS3DCurve::abs(const scalar val) const
{
return (sgn(val) == 1) ? val : -val;
}
label NURBS3DCurve::mod(const label x, const label interval) const
{
label ratio(x%interval);
return ratio < 0 ? ratio + interval : ratio;
}
void NURBS3DCurve::setUniformU()
{
const vectorField& curve(*this);
label nPts(curve.size());
forAll(curve, ptI)
{
u_[ptI] = scalar(ptI)/scalar(nPts - 1);
}
}
bool NURBS3DCurve::bound
(
scalar& u,
const scalar minVal = 1e-7,
const scalar maxVal = 0.999999
) const
{
// lower value bounding
if (u < scalar(0))
{
u = minVal;
return true;
//Info<< "Lower bound hit." << endl;
}
// upper value bounding
if (u > scalar(1))
{
u = maxVal;
return true;
//Info<< "Upper bound hit." << endl;
}
return false;
}
void NURBS3DCurve::setEquidistantU
(
scalarList& U,
const label lenAcc = 25,
const label maxIter = 10,
const label spacingCorrInterval=-1,
const scalar tolerance = 1.e-5
) const
{
const label nPts(U.size());
const scalar xLength(length() /(nPts - 1));
const scalar uLength(scalar(1) / scalar(nPts - 1));
U[0] = Zero;
U[nPts - 1] = scalar(1);
for (label ptI = 1; ptI<(nPts - 1); ptI++)
{
const scalar UPrev(U[ptI - 1]);
scalar& UCurr(U[ptI]);
scalar direc(scalar(1));
scalar xDiff(scalar(0));
scalar delta(scalar(0));
bool overReached(false);
UCurr = UPrev + uLength;
// Find the starting U value to ensure target is within 1 uLength.
while (true)
{
bool bounded(bound(UCurr));
delta = length(UPrev, UCurr, lenAcc);
xDiff = xLength - delta;
// Found the point.
if (abs(xDiff) < tolerance)
{
break;
}
else
{
direc = sgn(xDiff);
if (bounded && (direc == 1))
{
// uLength addition makes U exceed 1 so it becomes bounded.
// However, the desired x location still exceeds how far the
// bounded uLength can move (~e-5 error).
// Must force U to be u=0.999999.
overReached = true;
break;
}
else if (direc == scalar(1))
{
UCurr += uLength;
}
else
{
break;
}
}
}
if (!overReached)
{
label iter(0);
while (iter < maxIter)
{
// Set the new search length to ensure convergence and next v.
direc /= scalar(2);
UCurr += direc * uLength;
bound(UCurr);
// Can employ an occasional tolerance check from beg of curve.
if (
(spacingCorrInterval != -1)
&& (mod(ptI, spacingCorrInterval) == 0)
)
{
delta = length(scalar(0), UCurr, ptI*lenAcc);
xDiff = (ptI * xLength) - delta;
}
else
{
delta = length(UPrev, UCurr, lenAcc);
xDiff = xLength - delta;
}
// Break if found point or find the new search direction.
if (abs(xDiff) < tolerance)
{
break;
}
else
{
const scalar oldDirec(direc);
direc = sgn(xDiff) * abs(oldDirec);
}
iter++;
}
}
}
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
NURBS3DCurve::NURBS3DCurve
(
const NURBSbasis& basis,
const List& CPs,
const List& weights,
const scalarField& u,
const label nPts,
const word name
)
:
vectorField(nPts, Zero),
CPs_(CPs),
weights_(weights),
u_(u),
name_(name),
basis_(basis),
givenInitNrm_(Zero),
nrmOrientation_(ALIGNED)
{
buildCurve();
}
NURBS3DCurve::NURBS3DCurve
(
const NURBSbasis& basis,
const List& CPs,
const List& weights,
const label nPts,
const word name
)
:
vectorField(nPts, Zero),
CPs_(CPs),
weights_(weights),
u_(nPts, Zero),
name_(name),
basis_(basis),
givenInitNrm_(Zero),
nrmOrientation_(ALIGNED)
{
setUniformU();
buildCurve();
}
NURBS3DCurve::NURBS3DCurve
(
const NURBSbasis& basis,
const List& CPs,
const label nPts,
const word name
)
:
vectorField(nPts, Zero),
CPs_(CPs),
weights_(CPs.size(), scalar(1)),
u_(nPts, Zero),
name_(name),
basis_(basis),
givenInitNrm_(Zero),
nrmOrientation_(1)
{
setUniformU();
buildCurve();
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
// * * * * * * * * * * * * * * * * Set Functions * * * * * * * * * * * * * * //
void NURBS3DCurve::setNrm3DOrientation
(
const vector& givenNrm,
const vector& givenTan
)
{
givenInitNrm_ = givenNrm;
const vector tan(curveDerivativeU(Zero));
vector curveNrm(tan ^ givenTan);
if ((givenNrm & curveNrm) >= scalar(0))
{
nrmOrientation_ = ALIGNED;
}
else
{
nrmOrientation_ = OPPOSED;
}
Info<< "Initial nrmOrientation after comparison to NURBS u = 0 nrm : "
<< nrmOrientation_
<< endl;
}
void NURBS3DCurve::setNrm2DOrientation
(
const vector& givenNrm,
const scalar zVal
)
{
givenInitNrm_ = givenNrm;
const vector tan(curveDerivativeU(Zero));
vector curveNrm(Zero);
curveNrm.x() = -tan.y();
curveNrm.y() = tan.x();
curveNrm.z() = zVal;
if ((givenNrm & curveNrm) >= scalar(0))
{
nrmOrientation_ = ALIGNED;
}
else
{
nrmOrientation_ = OPPOSED;
}
Info<< "Initial nrmOrientation after comparison to NURBS u = 0 nrm : "
<< nrmOrientation_
<< endl;
}
void NURBS3DCurve::flipNrmOrientation()
{
if (nrmOrientation_ == ALIGNED)
{
nrmOrientation_ = OPPOSED;
}
else
{
nrmOrientation_ = ALIGNED;
}
}
void NURBS3DCurve::setCPs(const List& CPs)
{
CPs_ = CPs;
}
void NURBS3DCurve::setWeights(const List& weights)
{
weights_ = weights;
}
void NURBS3DCurve::setName(const word& name)
{
name_ = name;
}
void NURBS3DCurve::buildCurve()
{
const label degree(basis_.degree());
// Iterate over the CPs of this curve.
forAll(*this, ptI)
{
this->operator[](ptI) = vector::zero;
const scalar u(u_[ptI]);
// Compute denominator.
scalar denom(Zero);
forAll(CPs_, CPJ)
{
denom += basis_.basisValue(CPJ, degree, u)*weights_[CPJ];
}
// Compute curve point.
forAll(CPs_, CPI)
{
this->operator[](ptI)
+= CPs_[CPI]
* basis_.basisValue(CPI, degree, u)
* weights_[CPI]/denom;
}
}
}
void NURBS3DCurve::invert()
{
Info<< "Inverting NURBS curve " << name_ << endl;
const label nCPs(CPs_.size());
List invertedCPs(nCPs, Zero);
List invertedWeights(nCPs, Zero);
for (label CPI = 0; CPIsize()-1));
vector xu(curvePoint(u));
scalar res(GREAT);
do
{
vector dxdu(curveDerivativeU(u));
vector d2xdu2(curveDerivativeUU(u));
scalar lhs((dxdu&dxdu) + ((xu - targetPoint) & d2xdu2));
scalar rhs(-((xu - targetPoint) & dxdu));
// Update parametric coordinate and compute new point and
// bound param coordinates if needed.
// Compute residual.
u += rhs/lhs;
bound(u);
xu = curvePoint(u);
res = mag((xu - targetPoint) & curveDerivativeU(u));
//Info<< "dxdu" << dxdu << endl;
//Info<< "d2xdu2" << d2xdu2 << endl;
//Info<< "Iteration " << iter << ", Residual " << res << endl;
}
while ((iter++< maxIter) && (res > tolerance));
/*
// debug info
Info<< "Residual after " << iter << " iterations : " << res
<< "\nparametric coordinate " << u
<< "\ncurve point closest to target " << xu
<< "\ntarget being " << targetPoint
<< endl;
*/
// warning if method did not reach threshold
if (iter > maxIter)
{
WarningInFunction
<< "Finding curve point closest to " << targetPoint << " failed."
<< endl;
}
return u;
}
scalar NURBS3DCurve::findClosestCurvePoint
(
const vector& targetPoint,
const scalar initGuess,
const label maxIter,
const scalar tolerance
)
{
// Loop through curve points to find the closest one for initialization.
label iter(0);
scalar u(initGuess);
vector xu(curvePoint(u));
scalar res(GREAT);
do
{
vector dxdu(curveDerivativeU(u));
vector d2xdu2(curveDerivativeUU(u));
scalar lhs((dxdu&dxdu) + ((xu - targetPoint) & d2xdu2));
scalar rhs(-((xu - targetPoint) & dxdu));
// Update parametric coordinate and compute new point and
// bound param coordinates if needed.
// Compute residual.
u += rhs/lhs;
bound(u);
xu = curvePoint(u);
res = mag((xu - targetPoint) & curveDerivativeU(u));
//Info<< "dxdu" << dxdu << endl;
//Info<< "d2xdu2" << d2xdu2 << endl;
//Info<< "bound u " << u << endl;
//Info<< "u " << u << endl;
//Info<< "Iteration " << iter << ", Residual " << res << endl;
}
while ((iter++< maxIter) && (res > tolerance));
/*
// debug info
Info<< "Residual after " << iter << " iterations : " << res
<< "\nparametric coordinate " << u
<< "\ncurve point closest to target " << xu
<< "\ntarget being " << targetPoint
<< endl;
*/
// warning if method did not reach threshold
if (iter > maxIter)
{
WarningInFunction
<< "Finding curve point closest to " << targetPoint
<< " failed."
<< endl;
}
return u;
}
const vector NURBS3DCurve::nrm3D(const vector& refTan, const scalar u) const
{
vector curveNrm(Zero);
if (nrmOrientation_ == ALIGNED)
{
curveNrm = curveDerivativeU(u) ^ refTan;
}
else
{
curveNrm = refTan ^ curveDerivativeU(u);
}
curveNrm.normalise();
return curveNrm;
}
const vector NURBS3DCurve::nrm2D(const scalar zVal, const scalar u) const
{
const vector tan(curveDerivativeU(u));
vector curveNrm(Zero);
curveNrm.x() = -nrmOrientation_*tan.y();
curveNrm.y() = nrmOrientation_*tan.x();
curveNrm.z() = zVal;
curveNrm /= mag(curveNrm);
return curveNrm;
}
void NURBS3DCurve::insertKnot
(
const scalarField& oldKnots,
const scalar uBar,
const label kInsert
)
{
// Get the req ref info.
// Insertion into curve of non-uniform weight is not currently supported.
const label degree(basis_.degree());
const label nCPs(basis_.nCPs());
List newCPs(nCPs, Zero);
List newWeights(nCPs, scalar(1));
// Compute the new CPs and their weights.
for (label CPI = 0; CPI < (kInsert - degree + 1); CPI++)
{
newCPs[CPI] = CPs_[CPI];
}
for (label CPI = (kInsert - degree + 1); CPI < (kInsert + 1); CPI++)
{
const scalar uIOld(oldKnots[CPI]);
const scalar uIDOld(oldKnots[CPI + degree]);
const scalar ratio((uBar - uIOld) /(uIDOld - uIOld));
newCPs[CPI] = (ratio*CPs_[CPI] + (1 - ratio)*CPs_[CPI - 1]);
}
for (label CPI= (kInsert + 1); CPI