/* This file is part of the OpenLB library * * Copyright (C) 2015 Thomas Henn * 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. */ /** \file * Octree - adapted from http://www.flipcode.com/archives/Octree_Implementation.shtml */ #ifndef OCTREE_HH #define OCTREE_HH #include #include "core/singleton.h" using namespace olb::util; /// All OpenLB code is contained in this namespace. namespace olb { template class STLmesh; template struct STLtriangle; template class Octree; template Octree::Octree(Vector center, T rad, STLmesh* mesh, short maxDepth, T overlap, Octree* parent) : _center(center), _radius(rad), _mesh(mesh), _maxDepth(maxDepth),_isLeaf(false), _boundaryNode(false), _inside(false), _parent(parent), _child(nullptr) { findTriangles(overlap); // cout << _triangles.size() << std::endl; if (_triangles.size() > 0 && 0 < _maxDepth) { _child = new Octree*[8]; Vector tmpCenter = _center; T tmpRad = _radius/2.; tmpCenter[0] = _center[0] - tmpRad; tmpCenter[1] = _center[1] - tmpRad; tmpCenter[2] = _center[2] + tmpRad; _child[0] = new Octree(tmpCenter, tmpRad, _mesh, _maxDepth-1, overlap, this); tmpCenter[0] = _center[0] + tmpRad; tmpCenter[1] = _center[1] - tmpRad; tmpCenter[2] = _center[2] + tmpRad; _child[1] = new Octree(tmpCenter, tmpRad, _mesh, _maxDepth-1, overlap, this); tmpCenter[0] = _center[0] - tmpRad; tmpCenter[1] = _center[1] - tmpRad; tmpCenter[2] = _center[2] - tmpRad; _child[2] = new Octree(tmpCenter, tmpRad, _mesh, _maxDepth-1, overlap, this); tmpCenter[0] = _center[0] + tmpRad; tmpCenter[1] = _center[1] - tmpRad; tmpCenter[2] = _center[2] - tmpRad; _child[3] = new Octree(tmpCenter, tmpRad, _mesh, _maxDepth-1, overlap, this); tmpCenter[0] = _center[0] - tmpRad; tmpCenter[1] = _center[1] + tmpRad; tmpCenter[2] = _center[2] + tmpRad; _child[4] = new Octree(tmpCenter, tmpRad, _mesh, _maxDepth-1, overlap, this); tmpCenter[0] = _center[0] + tmpRad; tmpCenter[1] = _center[1] + tmpRad; tmpCenter[2] = _center[2] + tmpRad; _child[5] = new Octree(tmpCenter, tmpRad, _mesh, _maxDepth-1, overlap, this); tmpCenter[0] = _center[0] - tmpRad; tmpCenter[1] = _center[1] + tmpRad; tmpCenter[2] = _center[2] - tmpRad; _child[6] = new Octree(tmpCenter, tmpRad, _mesh, _maxDepth-1, overlap, this); tmpCenter[0] = _center[0] + tmpRad; tmpCenter[1] = _center[1] + tmpRad; tmpCenter[2] = _center[2] - tmpRad; _child[7] = new Octree(tmpCenter, tmpRad, _mesh, _maxDepth-1, overlap, this); } else { _isLeaf = true; if (_triangles.size() > 0 ) { _boundaryNode = true; } } } template Octree::~Octree() { if (_maxDepth!=0 && !_isLeaf) { for (int i=0; i<8; i++) { delete _child[i]; } delete[] _child; } } template void Octree::findTriangles(T overlap) { if (_parent == nullptr) { _triangles.reserve(_mesh->triangleSize()); for (unsigned int i=0; i<_mesh->triangleSize(); ++i) { if (AABBTri(_mesh->getTri(i))) { _triangles.push_back(i); } } } else { std::vector::iterator it; for (it = _parent->_triangles.begin(); it!=_parent->_triangles.end(); ++it) { if (AABBTri(_mesh->getTri(*it), overlap)) { _triangles.push_back(*it); } } } } template bool Octree::AABBTri(const STLtriangle& tri, T overlap) { std::vector v0(3,T()), v1(3,T()), v2(3,T()), f0(3,T()), f1(3,T()), f2(3,T()), e(3, T()); /* Test intersection cuboids - triangle * Intersection test after Christer Ericson - Real time Collision Detection p. * TestTriangleAABB p.171 */ Vector c(_center); T eps = 2e-16; for (int j=0; j<3; j++) { v0[j] = tri.point[0].r[j]-_center[j]; v1[j] = tri.point[1].r[j]-_center[j]; v2[j] = tri.point[2].r[j]-_center[j]; e[j] = _radius*1.01 + overlap; // + std::numeric_limits::epsilon(); // *1.01; } for (int j=0; j<3; j++) { f0[j] = v1[j] - v0[j]; f1[j] = v2[j] - v1[j]; f2[j] = v0[j] - v2[j]; } T p0=T(), p1=T(), r=T(); //test a00 p0 = v0[2]*v1[1]-v0[1]*v1[2]; p1 = v2[2]*v1[1]-v2[2]*v0[1]+v0[2]*v2[1]-v1[2]*v2[1]; r = e[1] * std::fabs(f0[2]) + e[2]*std::fabs(f0[1]); T mmm = std::max(-std::max(p0, p1), std::min(p0, p1)); if (mmm > r+eps) { return false; } // test a01 p0 = v0[1]*v1[2]-v0[1]*v2[2]-v0[2]*v1[1]+v0[2]*v2[1]; p1 = -v1[1]*v2[2]+v1[2]*v2[1]; r = e[1] * std::fabs(f1[2]) + e[2]*std::fabs(f1[1]); mmm = std::max(-std::max(p0, p1), std::min(p0, p1)); if (mmm > r+eps) { return false; } // test a02 p0 = v0[1]*v2[2]-v0[2]*v2[1]; p1 = v0[1]*v1[2]-v0[2]*v1[1]+v1[1]*v2[2]-v1[2]*v2[1]; r = e[1]*std::fabs(f2[2]) + e[2]*std::fabs(f2[1]); mmm = std::max(-std::max(p0, p1), std::min(p0, p1)); if (mmm > r+eps) { return false; } // test a10 p0 = v0[0]*v1[2]-v0[2]*v1[0]; p1 = v0[0]*v2[2]-v0[2]*v2[0]-v1[0]*v2[2]+v1[2]*v2[0]; r = e[0]*std::fabs(f0[2]) + e[2]*std::fabs(f0[0]); mmm = std::max(-std::max(p0, p1), std::min(p0, p1)); if (mmm > r+eps) { return false; } // test a11 p0 = -v0[0]*v1[2]+v0[0]*v2[2]+v0[2]*v1[0]-v0[2]*v2[0]; p1 = v1[0]*v2[2]-v1[2]*v2[0]; r = (T)(e[0]*std::fabs(f1[2])+e[2]*std::fabs(f1[0])); mmm = std::max(-std::max(p0, p1), std::min(p0, p1)); if (mmm > r+eps) { return false; } // test a12 p0 = -v0[0]*v2[2]+v0[2]*v2[0]; p1 = -v0[0]*v1[2]+v0[2]*v1[0]-v1[0]*v2[2]+v1[2]*v2[0]; r = e[0]*std::fabs(f2[2])+e[2]*std::fabs(f2[0]); mmm = std::max(-std::max(p0, p1), std::min(p0, p1)); if (mmm > r+eps) { return false; } // test a20 p0 = -v0[0]*v1[1]+v0[1]*v1[0]; p1 = -v0[0]*v2[1]+v0[1]*v2[0]+v1[0]*v2[1]-v1[1]*v2[0]; r = e[0]*std::fabs(f0[1])+e[1]*std::fabs(f0[0]); mmm = std::max(-std::max(p0, p1), std::min(p0, p1)); if (mmm > r+eps) { return false; } // test a21 p0 = v0[0]*v1[1]-v0[0]*v2[1]-v0[1]*v1[0]+v0[1]*v2[0]; p1 = -v1[0]*v2[1]+v1[1]*v2[0]; r = e[0]*std::fabs(f1[1])+e[1]*std::fabs(f1[0]); mmm = std::max(-std::max(p0, p1), std::min(p0, p1)); if (mmm > r+eps) { return false; } // test a22 p0 = v0[0]*v2[1]-v0[1]*v2[0]; p1 = v0[0]*v1[1]-v0[1]*v1[0]+v1[0]*v2[1]-v1[1]*v2[0]; r = e[0]*std::fabs(f2[1])+e[1]*std::fabs(f2[0]); mmm = std::max(-std::max(p0, p1), std::min(p0, p1)); if (mmm > r+eps) { return false; } if (std::max(std::max(v0[0], v1[0]), v2[0]) < -e[0] || std::min(std::min(v0[0], v1[0]), v2[0]) > e[0]) { return false; } if (std::max(std::max(v0[1], v1[1]), v2[1]) < -e[1] || std::min(std::min(v0[1], v1[1]), v2[1]) > e[1]) { return false; } if (std::max(std::max(v0[2], v1[2]), v2[2]) < -e[2] || std::min(std::min(v0[2], v1[2]), v2[2]) > e[2]) { return false; } /* Test intersection cuboids - triangle plane*/ r = e[0]*std::fabs(tri.normal[0]) + e[1]*std::fabs(tri.normal[1]) + e[2]*std::fabs(tri.normal[2]); T s = tri.normal[0]*c[0] + tri.normal[1]*c[1] + tri.normal[2]*c[2] - tri.d; return (fabs(s) <= r); } template Octree* Octree::find(const Vector& pt,const int& maxDepth) { // clout << pt[0] << " " << pt[1] << " " << pt[2] << std::endl; if (_isLeaf || maxDepth == _maxDepth) { if (std::abs(_center[0] - pt[0]) < _radius + std::numeric_limits::epsilon() && std::abs(_center[1] - pt[1]) < _radius + std::numeric_limits::epsilon() && std::abs(_center[2] - pt[2]) < _radius + std::numeric_limits::epsilon()) { // clout << pt[0] << " " << pt[1] << " " << pt[2] << std::endl; return this; } else { OstreamManager clout(std::cout, "Octree"); clout << "Point: " << std::setprecision(10) << pt[0]<< " " <find] Point outside of geometry."); return nullptr; } } else { if (pt[0] < _center[0]) { if (pt[1] < _center[1]) { if (pt[2] < _center[2]) { return _child[2]->find(pt, maxDepth); } else { return _child[0]->find(pt, maxDepth); } } else { if (pt[2] < _center[2]) { return _child[6]->find(pt, maxDepth); } else { return _child[4]->find(pt, maxDepth); } } } else { if (pt[1] < _center[1]) { if (pt[2] < _center[2]) { return _child[3]->find(pt, maxDepth); } else { return _child[1]->find(pt, maxDepth); } } else { if (pt[2] < _center[2]) { return _child[7]->find(pt, maxDepth); } else { return _child[5]->find(pt, maxDepth); } } } } } template int Octree::testIntersection(const Vector& pt,const Vector& dir, bool print) { int intersections = 0; Vector q; std::vector > qs; T a; #ifdef OLB_DEBUG if (print) { OstreamManager clout(std::cout, "Octree"); clout << "Center: " << _center[0] << " " << _center[1] << " "<< _center[2] << " Dir: " << dir[0] << " " << dir[1] << " "<< dir[2] << std::endl; clout << "Tris: "; for (unsigned k=0; k<_triangles.size(); ++k) { clout << _triangles[k] << " "; } clout << std::endl; } #endif for (unsigned k=0; k<_triangles.size(); ++k) { if (_mesh->getTri(_triangles[k]).testRayIntersect(pt, dir, q, a)) { if (std::fabs(_center[0]-q[0]) <= _radius + std::numeric_limits::epsilon() + 1/1000. * _radius && std::fabs(_center[1]-q[1]) <= _radius + std::numeric_limits::epsilon() + 1/1000. * _radius && std::fabs(_center[2]-q[2]) <= _radius + std::numeric_limits::epsilon() + 1/1000. * _radius) { bool newpoint=true; for (unsigned i=0; i void Octree::checkRay(const Vector& pt,const Vector& dir, unsigned short& rayInside) { unsigned short left=0, right=0; Vector dirNormed(dir); dirNormed.normalize(); dirNormed*= _radius * 2.; Vector q; std::vector > qs; T a = 1.; for (unsigned int k=0; k<_triangles.size(); ++k) { if (_mesh->getTri(_triangles[k]).testRayIntersect(pt, dirNormed, q, a, 0.) && a<1.) { bool newpoint=true; for (unsigned int i=0; i void Octree::getCenterpoints(std::vector >& pts) { if (_isLeaf) { pts.push_back(_center); } else { for (int i=0; i<8; i++) { _child[i]->getCenterpoints(pts); } } } template void Octree::getLeafs(std::vector* >& pts) { if (_isLeaf) { pts.push_back(this); } else { for (int i=0; i<8; i++) { _child[i]->getLeafs(pts); } } } template bool Octree::isLeaf() { return _isLeaf; } template void Octree::write(const Vector& pt,const std::string no) { if (_triangles.size()>0 && (std::fabs(pt[0]-_center[0]) < _radius && std::fabs(pt[1]-_center[1]) < _radius && std::fabs(pt[2]-_center[2]) < _radius)) { std::string fullName = singleton::directories().getVtkOutDir() + "Octree_" + no + ".stl"; std::ofstream f(fullName.c_str()); if (!f) { std::cerr << "[Octree] could not open file: " << fullName << std::endl; } f << "solid ascii" << std::endl << std::flush; std::vector::iterator it = _triangles.begin(); for (; it != _triangles.end(); ++it) { f << "facet normal" << _mesh->getTri(*it).normal[0] << " " << _mesh->getTri(*it).normal[1] << " " << _mesh->getTri(*it).normal[2] << " " <getTri(*it).point[0].r[0] << " " << _mesh->getTri(*it).point[0].r[1] << " " << _mesh->getTri(*it).point[0].r[2] << "\n"; f << " vertex " << _mesh->getTri(*it).point[1].r[0] << " " << _mesh->getTri(*it).point[1].r[1] << " " << _mesh->getTri(*it).point[1].r[2] << "\n"; f << " vertex " << _mesh->getTri(*it).point[2].r[0] << " " << _mesh->getTri(*it).point[2].r[1] << " " << _mesh->getTri(*it).point[2].r[2] << "\n"; f << " endloop\n"; f << "endfacet\n"; } f.close(); } if (!_isLeaf) { for (int i=0; i<8; i++) { std::stringstream istr; istr << i; _child[i]->write(pt, no+istr.str()); } } } template void Octree::write(const int depth,const std::string no) { if (_triangles.size()>0 && _maxDepth == depth) { std::string fullName = singleton::directories().getVtkOutDir() + "Octree_" + no + ".stl"; std::ofstream f(fullName.c_str()); if (!f) { std::cerr << "[Octree] could not open file: " << fullName << std::endl; } f << "solid ascii" << std::endl << std::flush; std::vector::iterator it = _triangles.begin(); for (; it != _triangles.end(); ++it) { f << "facet normal" << _mesh->getTri(*it).normal[0] << " " << _mesh->getTri(*it).normal[1] << " " << _mesh->getTri(*it).normal[2] << " " <getTri(*it).point[0].r[0] << " " << _mesh->getTri(*it).point[0].r[1] << " " << _mesh->getTri(*it).point[0].r[2] << "\n"; f << " vertex " << _mesh->getTri(*it).point[1].r[0] << " " << _mesh->getTri(*it).point[1].r[1] << " " << _mesh->getTri(*it).point[1].r[2] << "\n"; f << " vertex " << _mesh->getTri(*it).point[2].r[0] << " " << _mesh->getTri(*it).point[2].r[1] << " " << _mesh->getTri(*it).point[2].r[2] << "\n"; f << " endloop\n"; f << "endfacet\n"; } f.close(); } if (!_isLeaf) { for (int i=0; i<8; i++) { std::stringstream istr; istr << i; _child[i]->write(depth, no+istr.str()); } } } template void Octree::write(const std::string fName) { int rank = 0; #ifdef PARALLEL_MODE_MPI rank = singleton::mpi().getRank(); #endif if (rank==0) { std::vector* > leafs; getLeafs(leafs); typename std::vector* >::iterator it = leafs.begin(); std::string fullName = singleton::directories().getVtkOutDir() + fName + ".vtk"; std::ofstream f(fullName.c_str()); if (!f) { std::cerr << "[Particles3D] could not open file: " << fullName << std::endl; } f << "# vtk DataFile Version 2.0" << std::endl << std::flush; f << "Octree"<< std::endl << std::flush; f << "ASCII"<< std::endl << std::flush; f << "DATASET UNSTRUCTURED_GRID"<< std::endl << std::flush; std::stringstream points; std::stringstream cells; std::stringstream cell_types; // std::stringstream point_data; std::stringstream cell_data; std::stringstream cell_leaf; std::stringstream cell_boundary; points << "POINTS " << leafs.size()*8 << " float" << std::endl; cells << "CELLS " << leafs.size() << " " << leafs.size()*9 << std::endl; cell_types << "CELL_TYPES " << leafs.size() << std::endl; cell_data << "CELL_DATA " << leafs.size() << std::endl; cell_data << "SCALARS insideout int" << std::endl; cell_data << "LOOKUP_TABLE default" << std::endl; cell_leaf << "SCALARS leaf int" << std::endl; cell_leaf << "LOOKUP_TABLE default" << std::endl; cell_boundary << "SCALARS boundary int" << std::endl; cell_boundary << "LOOKUP_TABLE default" << std::endl; Vector center; Vector pt; T rad; int i=0; for (; it != leafs.end(); ++it) { center = (*it)->getCenter(); rad = (*it)->getRadius(); pt[0] = center[0] - rad; pt[1] = center[1] - rad; pt[2] = center[2] - rad; points << pt[0]<< " " << pt[1]<< " " << pt[2] << " "; pt[0] = center[0] + rad; pt[1] = center[1] - rad; pt[2] = center[2] - rad; points << pt[0]<< " " << pt[1]<< " " << pt[2] << " "; pt[0] = center[0] - rad; pt[1] = center[1] + rad; pt[2] = center[2] - rad; points << pt[0]<< " " << pt[1]<< " " << pt[2] << " "; pt[0] = center[0] + rad; pt[1] = center[1] + rad; pt[2] = center[2] - rad; points << pt[0]<< " " << pt[1]<< " " << pt[2] << " "; pt[0] = center[0] - rad; pt[1] = center[1] - rad; pt[2] = center[2] + rad; points << pt[0]<< " " << pt[1]<< " " << pt[2] << " "; pt[0] = center[0] + rad; pt[1] = center[1] - rad; pt[2] = center[2] + rad; points << pt[0]<< " " << pt[1]<< " " << pt[2] << " "; pt[0] = center[0] - rad; pt[1] = center[1] + rad; pt[2] = center[2] + rad; points << pt[0]<< " " << pt[1]<< " " << pt[2] << " "; pt[0] = center[0] + rad; pt[1] = center[1] + rad; pt[2] = center[2] + rad; points << pt[0]<< " " << pt[1]<< " " << pt[2] << " " << std::endl; cells << "8 "; for (int j=0; j<8; j++) { cells << i+j << " "; } i+=8; cells << std::endl; cell_types << 11 << std::endl; cell_data << (*it)->getInside() << " "<< std::endl; cell_leaf << (*it)->getMaxdepth() << " "<< std::endl; cell_boundary << (*it)->getBoundaryNode() << " " << std::endl; } f << points.str() << cells.str() << cell_types.str() << cell_data.str() << cell_leaf.str() << cell_boundary.str(); // f << "POINT_DATA 0\nCELL_DATA 0\n" << std::endl; f.close(); } OstreamManager clout(std::cout, "Octree"); /*if (_verbose)*/ clout << "Write ... OK" << std::endl; } template bool Octree::closestIntersectionSphere(const Vector& pt, const T& rad, const Vector& direction, Vector& q, T& a, STLtriangle& tri) { a = std::numeric_limits::infinity(); T alpha = T(); std::vector qtmp(3, T()); bool found = false; for (unsigned int k=0; k<_triangles.size(); ++k) { if (_mesh->getTri(_triangles[k]).testMovingSphereIntersect(pt, rad, direction, qtmp, alpha)) { if (alpha < a) { a = alpha; q = qtmp; found = true; tri = _mesh->getTri(_triangles[k]); } } } return found; } template bool Octree::closestIntersection(const Vector& pt, const Vector& direction, Vector& q, T& a, STLtriangle& tri, const T& rad, bool print) { a = std::numeric_limits::infinity(); T alpha = T(); Vector qtmp; bool found = false; #ifdef OLB_DEBUG if (print) { std::cout << "Tri Size: " << _triangles.size() << std::endl; } #endif for (unsigned int k=0; k<_triangles.size(); ++k) { if (_mesh->getTri(_triangles[k]).testRayIntersect(pt, direction, qtmp, alpha, rad)) { if (print) { std::cout << "Found intersection!" << std::endl; } if (alpha < a) { a = alpha; q = qtmp; found = true; tri = _mesh->getTri(_triangles[k]); } } } // std::cout << a << std::endl; return found; } template bool Octree::closestIntersection(const Vector& pt, const Vector& direction, Vector& q, T& a) { STLtriangle tri; return closestIntersection(pt, direction, q, a, tri, 0.); } template void Octree::intersectRayNode(const Vector& pt, const Vector& dir, Vector& s) { T t,d; s*=T(); //Plane Normals outside if (dir[0] > 0.) { // n = {1, 0, 0} d = _center[0] + _radius; t = (d - pt[0])/dir[0]; s = pt + t*dir; if (std::fabs(s[1]-_center[1]) < _radius && std::fabs(s[2]-_center[2]) < _radius) { return; } } else if (dir[0] < 0.) { // n = {-1, 0, 0} d = _center[0] - _radius; t = (d - pt[0])/dir[0]; s = pt + t*dir; if (std::fabs(s[1]-_center[1]) < _radius && std::fabs(s[2]-_center[2]) < _radius) { return; } } if (dir[1] > 0.) { d = _center[1] + _radius; t = (d - pt[1])/dir[1]; s = pt + t*dir; if (std::fabs(s[0]-_center[0]) < _radius && std::fabs(s[2]-_center[2]) < _radius) { return; } } else if (dir[1] < 0.) { // n = {0, 0, -1} d = _center[1] - _radius; t = (d - pt[1])/dir[1]; s = pt + t*dir; if (std::fabs(s[0]-_center[0]) < _radius && std::fabs(s[2]-_center[2]) < _radius) { return; } } if (dir[2] > 0.) { // n = {0, 0, 1} d = _center[2] + _radius; t = (d - pt[2])/dir[2]; s = pt + t*dir; if (std::fabs(s[0]-_center[0]) < _radius && std::fabs(s[1]-_center[1]) < _radius) { return; } } else if (dir[2] < 0.) { // n = {0, 0, -1} d = _center[2] - _radius; t = (d - pt[2])/dir[2]; s = pt + t*dir; if (std::fabs(s[0]-_center[0]) < _radius && std::fabs(s[1]-_center[1]) < _radius) { return; } } } template void Octree::print() { OstreamManager clout(std::cout, "Octree"); clout << "radius=" << _radius << "; center=(" << _center[0] << "," << _center[1] << "," << _center[2] << ")" << std::endl; } template void Octree::trianglesOnLine(const Vector& pt1, const Vector& pt2, std::set& tris) { tris.clear(); std::vector line = pt2-pt1; std::vector s = pt1; T lineNorm2 = line[0] * line[0] + line[1] * line[1] + line[2] * line[2]; T dist2 = T(); Octree* node = NULL; int it = 0; while (dist2 < lineNorm2 && it < 50) { node = find(s); tris.insert(node->_triangles.begin(), node->_triangles.end()); node->intersectRayNode(s, line, s); for (int i=0; i<3; i++) { s[i] = s[i] + line[i]*_radius*0.001 /* *node->getRadius()*/; } it++; dist2 = (pt1[0]-s[0])*(pt1[0]-s[0]) + (pt1[1]-s[1])*(pt1[1]-s[1]) + (pt1[2]-s[2])*(pt1[2]-s[2]); } } } #endif