/* This file is part of the OpenLB library
*
* Copyright (C) 2014-2016 Cyril Masquelier, Albert Mink, Mathias J. Krause, Benjamin Förster
* E-mail contact: info@openlb.net
* The most recent release of OpenLB can be downloaded at
*
*
* This program 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 2
* of the License, or (at your option) any later version.
*
* This program 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 this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef INDICATOR_BASE_F_3D_HH
#define INDICATOR_BASE_F_3D_HH
#include
#include "indicatorBaseF3D.h"
#include "utilities/vectorHelpers.h"
#include "math.h"
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#define M_PI2 1.57079632679489661923
namespace olb {
template
IndicatorF3D::IndicatorF3D() : GenericF(1, 3)
{}
template
Vector& IndicatorF3D::getMin()
{
return _myMin;
}
template
Vector& IndicatorF3D::getMax()
{
return _myMax;
}
template
bool IndicatorF3D::distance(S& distance, const Vector& origin, const Vector& direction, int iC)
{
bool originValue;
bool currentValue;
S precision = .0001;
S pitch = 0.5;
// start at origin and move into given direction
Vector currentPoint(origin);
(*this)(&originValue, origin.data);
(*this)(¤tValue, currentPoint.data);
while (currentValue == originValue && isInsideBox(currentPoint)) {
currentPoint += direction;
// update currentValue until the first point on the other side (inside/outside) is found
(*this)(¤tValue, currentPoint.data);
}
// return false if no point was found in given direction
if (!isInsideBox(currentPoint) && !originValue) {
return false;
}
while (pitch >= precision) {
if (!isInsideBox(currentPoint) && originValue) {
currentPoint -= pitch * direction;
pitch /= 2.;
} else {
(*this)(¤tValue, currentPoint.data);
if (currentValue == originValue) {
currentPoint += pitch * direction;
pitch /= 2.;
} else {
currentPoint -= pitch * direction;
pitch /= 2.;
}
}
}
distance = (currentPoint - origin).norm();
return true;
}
template
bool IndicatorF3D::distance(S& distance, const Vector& origin)
{
std::cout << "you should not be here!" << std::endl;
return true;
}
template
bool IndicatorF3D::rotOnAxis(Vector& vec_rot, const Vector& vec, const Vector& axis, S& theta)
{
/// http://mathworld.wolfram.com/RodriguesRotationFormula.html https://en.wikipedia.org/wiki/Rodrigues%27_rotation_formula
//Vector axisN(axis*(1/(axis).norm())); // normalize rotation axis
Vector axisN(axis*(1/const_cast&> (axis).norm())); // normalize rotation axis
vec_rot = vec ;
Vector crossProd;
crossProd[0] = axisN[1]*vec[2] - axisN[2]*vec[1];
crossProd[1] = axisN[2]*vec[0] - axisN[0]*vec[2];
crossProd[2] = axisN[0]*vec[1] - axisN[1]*vec[0];
S dotProd = axisN[0]*vec[0] + axisN[1]*vec[1] + axisN[2]*vec[2];
//v_rot = std::cos(theta)*vec + (crossProd)*std::sin(theta) + axisN*(dotProduct3D(axisN,vec))*(1 - std::cos(theta));
vec_rot = std::cos(theta)*vec + (crossProd)*std::sin(theta) + axisN*(dotProd)*(1 - std::cos(theta));
return true;
}
template
bool IndicatorF3D::normal(Vector& normal, const Vector& origin, const Vector& direction, int iC)
{
//OstreamManager clout(std::cout,"IndicatorF3D");
#ifdef OLB_DEBUG
std::cout << "Calculating IndicatorF3D Normal" << std::endl;
#endif
bool originValue;
(*this)(&originValue, origin.data);
Vector currentPoint(origin);
S precision = .0001;
S dist;
distance(dist, origin, direction, iC);
S dirMag = const_cast&> (direction).norm();
#ifdef OLB_DEBUG
std::cout << "magnitude = " << dirMag << std::endl;
#endif
//Vector POS(origin + dist*direction*(1/const_cast&> (direction).norm())); //Point on Surface
//direction = direction*(1/const_cast&> (direction).norm());
Vector directionN(direction*(1/dirMag));
Vector POS(origin + dist*directionN); //Point on Surface
/// find perpendicular vector to direction
Vector directionPerp;
if ( (util::nearZero(directionN[0]) && util::nearZero(directionN[1]) && !util::nearZero(directionN[2]))
|| (util::nearZero(directionN[0]) && !util::nearZero(directionN[1]) && util::nearZero(directionN[2])) ) {
directionPerp[0] = 1;
directionPerp[1] = 0;
directionPerp[2] = 0;
} else if ( !util::nearZero(directionN[0]) && util::nearZero(directionN[1]) && util::nearZero(directionN[2]) ) {
directionPerp[0] = 0;
directionPerp[1] = 0;
directionPerp[2] = 1;
} else if ( ( !util::nearZero(directionN[0]) || !util::nearZero(directionN[1]) ) && !util::nearZero(directionN[2]) ) {
directionPerp[0] = directionN[0];
directionPerp[1] = directionN[1];
directionPerp[2] = -(directionN[0] + directionN[1])/directionN[2];
} else {
std::cout << "Error: unknown case for perpendicular check" << std::endl;
return false;
}
Vector directionPerpN(directionPerp*(dirMag/(directionPerp).norm()));
Vector point1;
Vector point2;
Vector point3;
bool currentValue;
/// Loop 3 times to find three points on the surface to use for normal calc.
/// orthogonal to direction vector 120 degree to each other
for (int n: {
0,120,240
}) {
S thetaMain = n*M_PI/180.;
/// rotate directionPerpN through 3 angles {0,120,240}
Vector perp;
rotOnAxis(perp, directionPerpN, directionN, thetaMain);
Vector perpPoint(POS + perp);
//std::cout << "perp = [" << perp[0] << "," << perp[1] << "," << perp[2] << "]" << std::endl;
//S rotate = 90.;
S rotate = 179.;
S pitch = rotate/2.;
Vector vec(perp);
Vector rotAxis;
//rotAxis = cross(perp,directionN);
rotAxis[0] = perp[1]*directionN[2] - perp[2]*directionN[1];
rotAxis[1] = perp[2]*directionN[0] - perp[0]*directionN[2];
rotAxis[2] = perp[0]*directionN[1] - perp[1]*directionN[0];
//normal = rotAxis;
//return true;
//std::cout << "rotAxis = [" << rotAxis[0] << "," << rotAxis[1] << "," << rotAxis[2] << "]" << std::endl;
/// Find 'positive' angle
Vector testPOS;
S testAngle(45.*M_PI/180.);
rotOnAxis(testPOS, perp, rotAxis, testAngle);
Vector testPoint( POS + testPOS);
//std::cout << "testPOS = [" << testPOS[0] << "," << testPOS[1] << "," << testPOS[2] << "]" << std::endl;
//std::cout << "testPoint = [" << testPoint[0] << "," << testPoint[1] << "," << testPoint[2] << "]" << std::endl;
S distTestPoint = (testPoint - origin).norm();
S distPerpPoint = (perpPoint - origin).norm();
S mod = 0;
if (distTestPoint < distPerpPoint) { // pos. angle rotates towards
mod = -1;
} else {
mod = 1;
}
while (std::abs(pitch) >= precision) {
S theta(pitch*M_PI/180);
currentPoint = POS + vec;
(*this)(¤tValue, currentPoint.data);
S temp;
if (currentValue == originValue) {
temp = mod*theta;
rotOnAxis(vec, vec, rotAxis, temp);
} else {
temp = -mod*theta;
rotOnAxis(vec, vec, rotAxis, temp);
}
pitch /= 2.;
}
if (n == 0) {
point1 = currentPoint;
} else if (n == 120) {
point2 = currentPoint;
} else if (n == 240) {
point3 = currentPoint;
} else {
std::cout << "Something broke" << std::endl;
return false;
}
}
/// Calculate Normal
Vector vec1 (point1 - point2);
Vector vec2 (point1 - point3);
normal[0] = -(vec1[1]*vec2[2] - vec1[2]*vec2[1]);
normal[1] = -(vec1[2]*vec2[0] - vec1[0]*vec2[2]);
normal[2] = -(vec1[0]*vec2[1] - vec1[1]*vec2[0]);
normalize(normal);
//S dist;
//Vector dist;
//distance(dist, origin, direction, iC);
//normal = Vector(dist);
//normal = POS;
//normal = directionPerpN;
//normal = directionN;
return true;
}
template
bool IndicatorF3D::isInsideBox(Vector point)
{
return point >= _myMin && point <= _myMax;
}
template
IndicatorIdentity3D::IndicatorIdentity3D(std::shared_ptr> f)
: _f(f)
{
this->_myMin = _f->getMin();
this->_myMax = _f->getMax();
}
template
bool IndicatorIdentity3D::operator() (bool output[1], const S input[3])
{
return (_f)->operator()(output, input);
}
} // namespace olb
#endif