$treeview $search $mathjax
Palabos  Version 1.1
$projectbrief
$projectbrief
$searchbox

voxelizer.hh

Go to the documentation of this file.
00001 /* This file is part of the Palabos library.
00002  *
00003  * Copyright (C) 2011 FlowKit Sarl
00004  * Avenue de Chailly 23
00005  * 1012 Lausanne, Switzerland
00006  * E-mail contact: contact@flowkit.com
00007  *
00008  * The most recent release of Palabos can be downloaded at 
00009  * <http://www.palabos.org/>
00010  *
00011  * The library Palabos is free software: you can redistribute it and/or
00012  * modify it under the terms of the GNU Affero General Public License as
00013  * published by the Free Software Foundation, either version 3 of the
00014  * License, or (at your option) any later version.
00015  *
00016  * The library is distributed in the hope that it will be useful,
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  * GNU Affero General Public License for more details.
00020  *
00021  * You should have received a copy of the GNU Affero General Public License
00022  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00023 */
00024 
00025 /* Main author: Orestis Malaspinas */
00026 
00027 #ifndef VOXELIZER_HH
00028 #define VOXELIZER_HH
00029 
00030 #include "core/globalDefs.h"
00031 #include "offLattice/voxelizer.h"
00032 #include "atomicBlock/dataField3D.h"
00033 #include "multiBlock/multiBlockGenerator3D.h"
00034 
00035 namespace plb {
00036 
00037 namespace voxelFlag {
00038     inline int invert(int arg) {
00039         switch(arg) {
00040             case inside: return outside;
00041             case outside: return inside;
00042             case innerBorder: return outerBorder;
00043             case outerBorder: return innerBorder;
00044             case undetermined: return undetermined;
00045             default:
00046                 PLB_ASSERT(false);
00047         }
00048         return undetermined;
00049     }
00050     inline int bulkFlag(int arg) {
00051         if (arg==innerBorder || arg==inside) {
00052             return inside;
00053         }
00054         else if (arg==outerBorder || arg==outside) {
00055             return outside;
00056         }
00057         else {
00058             return undetermined;
00059         }
00060     }
00061     inline int borderFlag(int arg) {
00062         if (arg==inside || arg==innerBorder) {
00063             return innerBorder;
00064         }
00065         else if (arg==outside || arg==outerBorder) {
00066             return outerBorder;
00067         }
00068         else {
00069             return undetermined;
00070         }
00071     }
00072     inline bool insideFlag(int arg) {
00073         return arg==inside || arg==innerBorder;
00074     }
00075     inline bool outsideFlag(int arg) {
00076         return arg==outside || arg==outerBorder;
00077     }
00078 
00079 }  // namespace voxelFlag
00080 
00081 template<typename T>
00082 std::auto_ptr<MultiScalarField3D<int> > voxelize (
00083         TriangularSurfaceMesh<T> const& mesh,
00084         plint symmetricLayer, plint borderWidth )
00085 {
00086     Array<T,2> xRange, yRange, zRange;
00087     mesh.computeBoundingBox(xRange, yRange, zRange);
00088     // Creation of the multi-scalar field. The +1 is because if the resolution is N,
00089     //   the number of nodes is N+1.
00090     plint nx = (plint)(xRange[1] - xRange[0]) + 1 + 2*symmetricLayer;
00091     plint ny = (plint)(yRange[1] - yRange[0]) + 1 + 2*symmetricLayer;
00092     plint nz = (plint)(zRange[1] - zRange[0]) + 1 + 2*symmetricLayer;
00093 
00094     return voxelize(mesh, Box3D(0,nx-1, 0,ny-1, 0,nz-1), borderWidth);
00095 }
00096 
00097 template<typename T>
00098 std::auto_ptr<MultiScalarField3D<int> > voxelize (
00099         TriangularSurfaceMesh<T> const& mesh,
00100         Box3D const& domain, plint borderWidth )
00101 {
00102     // As initial seed, a one-cell layer around the outer boundary is tagged
00103     //   as ouside cells.
00104     plint envelopeWidth=1;
00105     std::auto_ptr<MultiScalarField3D<int> > voxelMatrix
00106         = generateMultiScalarField<int>(domain, voxelFlag::outside, envelopeWidth);
00107     setToConstant(*voxelMatrix, voxelMatrix->getBoundingBox().enlarge(-1),
00108                   voxelFlag::undetermined);
00109 
00110     MultiContainerBlock3D hashContainer(*voxelMatrix);
00111     std::vector<MultiBlock3D*> container_arg;
00112     container_arg.push_back(&hashContainer);
00113     applyProcessingFunctional (
00114             new CreateTriangleHash<T>(mesh),
00115             hashContainer.getBoundingBox(), container_arg );
00116 
00117     std::vector<MultiBlock3D*> flag_hash_arg;
00118     flag_hash_arg.push_back(voxelMatrix.get());
00119     flag_hash_arg.push_back(&hashContainer);
00120 
00121     voxelMatrix->resetFlags(); // Flags are used internally by VoxelizeMeshFunctional3D.
00122     while (!allFlagsTrue(voxelMatrix.get())) {
00123         applyProcessingFunctional (
00124                 new VoxelizeMeshFunctional3D<T>(mesh),
00125                 voxelMatrix->getBoundingBox(), flag_hash_arg );
00126     }
00127 
00128     detectBorderLine(*voxelMatrix, voxelMatrix->getBoundingBox(), borderWidth);
00129 
00130     return std::auto_ptr<MultiScalarField3D<int> >(voxelMatrix);
00131 }
00132 
00133 
00134 template<typename T>
00135 std::auto_ptr<MultiScalarField3D<int> > revoxelize (
00136         TriangularSurfaceMesh<T> const& mesh,
00137         MultiScalarField3D<int>& oldVoxelMatrix,
00138         MultiContainerBlock3D& hashContainer, plint borderWidth )
00139 {
00140     // As initial seed, a one-cell layer around the outer boundary is tagged
00141     //   as ouside cells.
00142     plint envelopeWidth=1;
00143     Box3D domain(oldVoxelMatrix.getBoundingBox());
00144     std::auto_ptr<MultiScalarField3D<int> > voxelMatrix (
00145             new MultiScalarField3D<int>((MultiBlock3D&)oldVoxelMatrix) );
00146     setToConstant(*voxelMatrix, domain, voxelFlag::outside);
00147     setToConstant(*voxelMatrix, voxelMatrix->getBoundingBox().enlarge(-1),
00148                   voxelFlag::undetermined);
00149 
00150     std::vector<MultiBlock3D*> flag_hash_arg;
00151     flag_hash_arg.push_back(voxelMatrix.get());
00152     flag_hash_arg.push_back(&hashContainer);
00153 
00154     voxelMatrix->resetFlags(); // Flags are used internally by VoxelizeMeshFunctional3D.
00155     while (!allFlagsTrue(voxelMatrix.get())) {
00156         applyProcessingFunctional (
00157                 new VoxelizeMeshFunctional3D<T>(mesh),
00158                 voxelMatrix->getBoundingBox(), flag_hash_arg );
00159     }
00160 
00161     detectBorderLine(*voxelMatrix, voxelMatrix->getBoundingBox(), borderWidth);
00162 
00163     return std::auto_ptr<MultiScalarField3D<int> >(voxelMatrix);
00164 }
00165 
00166 
00167 /* ******** VoxelizeMeshFunctional3D ************************************* */
00168 
00169 template<typename T>
00170 VoxelizeMeshFunctional3D<T>::VoxelizeMeshFunctional3D (
00171         TriangularSurfaceMesh<T> const& mesh_)
00172     : mesh(mesh_)
00173 { }
00174 
00175 template<typename T>
00176 bool VoxelizeMeshFunctional3D<T>::distanceToSurface (
00177         AtomicContainerBlock3D& hashContainer,
00178         Array<T,3> const& point, T& distance, bool& isBehind ) const
00179 {
00180     T maxDistance = sqrt(3);
00181     Array<T,2> xRange(point[0]-maxDistance, point[0]+maxDistance);
00182     Array<T,2> yRange(point[1]-maxDistance, point[1]+maxDistance);
00183     Array<T,2> zRange(point[2]-maxDistance, point[2]+maxDistance);
00184     TriangleHash<T> triangleHash(hashContainer);
00185     std::vector<plint> possibleTriangles;
00186     triangleHash.getTriangles(xRange, yRange, zRange, possibleTriangles);
00187 
00188     T    tmpDistance;
00189     bool tmpIsBehind;
00190     bool triangleFound = false;
00191 
00192     for (pluint iPossible=0; iPossible<possibleTriangles.size(); ++iPossible) {
00193         plint iTriangle = possibleTriangles[iPossible];
00194         mesh.distanceToTriangle (
00195                     point, iTriangle, tmpDistance, tmpIsBehind );
00196         if( !triangleFound || tmpDistance<distance) {
00197             distance = tmpDistance;
00198             isBehind = tmpIsBehind;
00199             triangleFound = true;
00200         }
00201     }
00202     return triangleFound;
00203 }
00204 
00205 template<typename T>
00206 bool VoxelizeMeshFunctional3D<T>::checkIfFacetsCrossed (
00207         AtomicContainerBlock3D& hashContainer,
00208         Array<T,3> const& point1, Array<T,3> const& point2,
00209         T& distance, plint& whichTriangle )
00210 {
00211     Array<T,2> xRange (
00212                  std::min(point1[0], point2[0]),
00213                  std::max(point1[0], point2[0]) );
00214     Array<T,2> yRange (
00215                  std::min(point1[1], point2[1]),
00216                  std::max(point1[1], point2[1]) );
00217     Array<T,2> zRange (
00218                  std::min(point1[2], point2[2]),
00219                  std::max(point1[2], point2[2]) );
00220     TriangleHash<T> triangleHash(hashContainer);
00221     std::vector<plint> possibleTriangles;
00222     triangleHash.getTriangles(xRange, yRange, zRange, possibleTriangles);
00223 
00224     int flag = 0; // Check for crossings inside the point1-point2 segment.
00225     Array<T,3> intersection; // Dummy variable.
00226     Array<T,3> normal;       // Dummy variable.
00227     T tmpDistance;           // Dummy variable.
00228 
00229     if (global::counter("voxelizer-debug").getCount()==1) {
00230         std::cout << "{";
00231     }
00232     std::vector<T> crossings;
00233     for (pluint iPossible=0; iPossible<possibleTriangles.size(); ++iPossible) {
00234         plint iTriangle = possibleTriangles[iPossible];
00235         if (mesh.pointOnTriangle(point1, point2, flag, iTriangle, intersection, normal, tmpDistance)==1) {
00236             if (global::counter("voxelizer-debug").getCount()==1) {
00237                 std::cout << "(" << iTriangle << ";" << tmpDistance << ")";
00238             }
00239             crossings.push_back(tmpDistance);
00240             if (crossings.size()==1 || tmpDistance<distance) {
00241                 distance = tmpDistance;
00242                 whichTriangle = iTriangle;
00243             }
00244         }
00245     }
00246     if (global::counter("voxelizer-debug").getCount()==1) {
00247         std::cout << "}";
00248     }
00249 
00250     if (crossings.size()==0) {
00251         return false;
00252     }
00253     else {
00254         bool hasCrossed = true;
00255         for (pluint iCrossing=1; iCrossing<crossings.size(); ++iCrossing) {
00256             //const T eps1 = std::numeric_limits<double>::epsilon()*1.e2;
00257             //if ( !util::fpequal(crossings[iCrossing], crossings[iCrossing-1], eps1) )
00258             const T eps1 = std::numeric_limits<double>::epsilon()*1.e4;
00259             if ( fabs(crossings[iCrossing]-crossings[iCrossing-1])>eps1)
00260             {
00261                 hasCrossed = !hasCrossed;
00262             }
00263         }
00264         return hasCrossed;
00265     }
00266 }
00267 
00268 template<typename T>
00269 bool VoxelizeMeshFunctional3D<T>::createVoxelizationRange (
00270         Box3D const& domain, ScalarField3D<int>& voxels,
00271         Array<plint,2>& xRange, Array<plint,2>& yRange, Array<plint,2>& zRange )
00272 {
00273     // The purpose of the first three loops is to locate the eight
00274     //   corners of the cube. One voxel per corner would be insufficient
00275     //   because a potential seed is situated differently, depending on
00276     //   whether it is on the boundary of the multi-block or somewhere inside.
00277     for (plint dx=0; dx<=+1; ++dx) {
00278         plint xMin = domain.x0+dx*domain.getNx()-1;
00279         plint xMax = domain.x0+dx*domain.getNx();
00280         for (plint dy=0; dy<=+1; ++dy) {
00281             plint yMin = domain.y0+dy*domain.getNy()-1;
00282             plint yMax = domain.y0+dy*domain.getNy();
00283             for (plint dz=0; dz<=+1; ++dz) {
00284                 plint zMin = domain.z0+dz*domain.getNz()-1;
00285                 plint zMax = domain.z0+dz*domain.getNz();
00286 
00287                 // Locate a potential seed in one of the corners.
00288                 for (plint iX=xMin; iX<=xMax; ++iX) {
00289                     for (plint iY=yMin; iY<=yMax; ++iY) {
00290                         for (plint iZ=zMin; iZ<=zMax; ++iZ) {
00291                             if (voxels.get(iX,iY,iZ) != voxelFlag::undetermined) {
00292                                 xRange[0] = domain.x0+dx*(domain.getNx()-1);
00293                                 xRange[1] = domain.x0+(1-dx)*(domain.getNx()-1);
00294                                 yRange[0] = domain.y0+dy*(domain.getNy()-1);
00295                                 yRange[1] = domain.y0+(1-dy)*(domain.getNy()-1);
00296                                 zRange[0] = domain.z0+dz*(domain.getNz()-1);
00297                                 zRange[1] = domain.z0+(1-dz)*(domain.getNz()-1);
00298                                 return true;
00299                             }
00300                         }
00301                     }
00302                 }
00303 
00304             }
00305         }
00306     }
00307     return false;
00308 }
00309 
00310 template<typename T>
00311 void VoxelizeMeshFunctional3D<T>::printOffender (
00312         ScalarField3D<int> const& voxels,
00313         AtomicContainerBlock3D& hashContainer,
00314         Dot3D pos )
00315 {
00316     std::set<plint> triangles;
00317     Dot3D offset = voxels.getLocation();
00318     Dot3D pos_ = pos+offset;
00319     std::cout << "Position (" << pos_.x << "," << pos_.y << "," << pos_.z << ")" << std::endl;
00320     for (plint dx=-1; dx<=+1; ++dx) {
00321         for (plint dy=-1; dy<=+1; ++dy) {
00322             for (plint dz=-1; dz<=+1; ++dz) {
00323                 if (!(dx==0 && dy==0 && dz==0)) {
00324                     Dot3D neigh = pos+offset+Dot3D(dx,dy,dz);
00325                     int typeOfNeighbor = voxels.get(pos.x+dx,pos.y+dy,pos.z+dz);
00326                     if (typeOfNeighbor!=voxelFlag::undetermined) {
00327                         T distance;
00328                         plint whichTriangle;
00329                         Array<T,3> p1(pos_.x,pos_.y,pos_.z);
00330                         Array<T,3> p2(neigh.x,neigh.y,neigh.z);
00331                         global::counter("voxelizer-debug").increment(1);
00332                         bool crossed = checkIfFacetsCrossed (
00333                                     hashContainer, p1, p2, distance, whichTriangle);
00334                         global::counter("voxelizer-debug").reset();
00335                         std::cout << "Neighbor ("
00336                                   << dx << "," << dy << "," << dz
00337                                   << "); is "
00338                                   << (voxelFlag::insideFlag(typeOfNeighbor) ? "inside" : "outside");
00339                         if (crossed) {
00340                             triangles.insert(whichTriangle);
00341                             std::cout 
00342                                   << " inters. at distance " << distance
00343                                   << " with triangle " << whichTriangle << std::endl;
00344                         }
00345                         else {
00346                             std::cout << " no inters." << std::endl;
00347                         }
00348                     }
00349                 }
00350             }
00351         }
00352     }
00353     std::set<plint>::iterator it = triangles.begin();
00354     for (; it!=triangles.end(); ++it) {
00355         std::cout << "Triangle " << *it << " [" << std::flush;
00356         Array<T,3> p0 = mesh.getVertex(*it, 0);
00357         Array<T,3> p1 = mesh.getVertex(*it, 1);
00358         Array<T,3> p2 = mesh.getVertex(*it, 2);
00359         std::cout << p0[0] << " " << p1[0] << " " << p2[0] << " " << p0[0] << "], ["
00360                   << p0[1] << " " << p1[1] << " " << p2[1] << " " << p0[1] << "], ["
00361                   << p0[2] << " " << p1[2] << " " << p2[2] << " " << p0[2] << "]" << std::endl;
00362     }
00363 }
00364 
00365 template<typename T>
00366 bool VoxelizeMeshFunctional3D<T>::voxelizeFromNeighbor (
00367         ScalarField3D<int> const& voxels,
00368         AtomicContainerBlock3D& hashContainer,
00369         Dot3D pos, Dot3D neighbor, int& voxelType )
00370 {
00371     int verificationLevel = 0;
00372     Dot3D offset = voxels.getLocation();
00373     int typeOfNeighbor = voxels.get(neighbor.x,neighbor.y,neighbor.z);
00374     if (typeOfNeighbor==voxelFlag::undetermined) {
00375         return true;
00376     }
00377     // If there is no verification and the voxel has already been voxelized,
00378     //   it is not being re-voxelized here.
00379     if (verificationLevel==0) {
00380         if (voxelType!=voxelFlag::undetermined) {
00381             return true;
00382         }
00383     }
00384     Dot3D pos_ = pos+offset;
00385     Dot3D neighbor_ = neighbor+offset;
00386     Array<T,3> point1((T)pos_.x, (T)pos_.y, (T)pos_.z);
00387     Array<T,3> point2((T)neighbor_.x, (T)neighbor_.y, (T)neighbor_.z);
00388     int newVoxelType = voxelFlag::undetermined;
00389     T distance1, distance2, distance3, distance4;
00390     bool isBehind1, isBehind2;
00391     plint whichTriangle1, whichTriangle2;
00392     if (checkIfFacetsCrossed(hashContainer, point1, point2, distance1, whichTriangle1)) {
00393         newVoxelType = voxelFlag::invert(typeOfNeighbor);
00394         // Additional consistency checks only at the ultimate level of verification.
00395         if (verificationLevel==2) {
00396             PLB_ASSERT( distance1 < sqrt(3)+0.0001 );
00397             bool ok = checkIfFacetsCrossed(hashContainer, point2, point1, distance2, whichTriangle2);
00398             PLB_ASSERT( ok );
00399             PLB_ASSERT( distance2 < sqrt(3)+0.0001 );
00400 
00401             bool ok1 = distanceToSurface( hashContainer, point1,
00402                                           distance3, isBehind1 );
00403 
00404             PLB_ASSERT( ok1 );
00405             PLB_ASSERT( distance1 < sqrt(3)+0.0001 );
00406             // Attention: At this moment, the following consistency check fails sometimes,
00407             //   god knows why. It might be that there is a bug in the method
00408             //   mesh.distanceToSurface.
00409             PLB_ASSERT( (voxelFlag::insideFlag(newVoxelType) && isBehind1) ||
00410                         (voxelFlag::outsideFlag(newVoxelType) && !isBehind1) );
00411 
00412             bool ok2 = distanceToSurface( hashContainer, point2,
00413                                           distance4, isBehind2 );
00414             PLB_ASSERT( ok2 );
00415             PLB_ASSERT( distance2 < sqrt(3)+0.0001 );
00416             PLB_ASSERT ( (voxelFlag::insideFlag(typeOfNeighbor) && isBehind2) ||
00417                          (voxelFlag::outsideFlag(typeOfNeighbor) && !isBehind2) );
00418         }
00419     }
00420     else {
00421         newVoxelType = typeOfNeighbor;
00422     }
00423     int oldVoxelType = voxelType;
00424     voxelType = newVoxelType;
00425     if (oldVoxelType == voxelFlag::undetermined) {
00426         return true;
00427     }
00428     else {
00429         return oldVoxelType == newVoxelType;
00430     }
00431 }
00432 
00433 template<typename T>
00434 void VoxelizeMeshFunctional3D<T>::processGenericBlocks (
00435         Box3D domain, std::vector<AtomicBlock3D*> blocks )
00436 {
00437     PLB_PRECONDITION( blocks.size()==2 );
00438     ScalarField3D<int>* voxels =
00439         dynamic_cast<ScalarField3D<int>*>(blocks[0]);
00440     PLB_ASSERT( voxels );
00441     AtomicContainerBlock3D* container =
00442         dynamic_cast<AtomicContainerBlock3D*>(blocks[1]);
00443     PLB_ASSERT( container );
00444 
00445     // Return if this block is already voxelized.
00446     if (voxels->getFlag()) {
00447         return;
00448     }
00449 
00450     Array<plint,2> xRange, yRange, zRange;
00451     if (!createVoxelizationRange(domain, *voxels, xRange, yRange, zRange)) {
00452         // If no seed has been found in the envelope, just return and wait
00453         //   for the next round.
00454         return;
00455     }
00456 
00457     // Specify if the loops go in positive or negative direction.
00458     plint xIncr = xRange[1]>xRange[0] ? 1 : -1;
00459     plint yIncr = yRange[1]>yRange[0] ? 1 : -1;
00460     plint zIncr = zRange[1]>zRange[0] ? 1 : -1;
00461     // The ranges are closed on both ends. Here, the range[1] end
00462     //   is converted to an open one so we can use != checks in the loops.
00463     xRange[1] += xIncr; 
00464     yRange[1] += yIncr; 
00465     zRange[1] += zIncr; 
00466     for (plint iX=xRange[0]; iX!=xRange[1]; iX+=xIncr) {
00467         for (plint iY=yRange[0]; iY!=yRange[1]; iY+=yIncr) {
00468             for (plint iZ=zRange[0]; iZ!=zRange[1]; iZ+=zIncr) {
00469                 Dot3D pos(iX,iY,iZ);
00470                 int voxelType = voxels->get(iX,iY,iZ);
00471                 if (voxelType==voxelFlag::undetermined) {
00472                     for (plint dx=-1; dx<=+1; ++dx) {
00473                         for (plint dy=-1; dy<=+1; ++dy) {
00474                             for (plint dz=-1; dz<=+1; ++dz) {
00475                                 if (!(dx==0 && dy==0 && dz==0)) {
00476                                     Dot3D neighbor(iX+dx, iY+dy, iZ+dz);
00477                                     bool ok = voxelizeFromNeighbor (
00478                                                   *voxels, *container, 
00479                                                   pos, neighbor, voxelType );
00480                                     if (!ok) {
00481                                         printOffender(*voxels, *container, pos);
00482                                     }
00483                                     PLB_ASSERT( ok );
00484                                 }
00485                             }
00486                         }
00487                     }
00488                     voxels->get(iX,iY,iZ) = voxelType;
00489                 }
00490             }
00491         }
00492     }
00493     // Indicate that this atomic-block has been voxelized.
00494     voxels->setFlag(true);
00495 }
00496 
00497 template<typename T>
00498 VoxelizeMeshFunctional3D<T>* VoxelizeMeshFunctional3D<T>::clone() const {
00499     return new VoxelizeMeshFunctional3D<T>(*this);
00500 }
00501 
00502 template<typename T>
00503 void VoxelizeMeshFunctional3D<T>::getTypeOfModification(std::vector<modif::ModifT>& modified) const {
00504     modified[0] = modif::staticVariables;  // Voxels
00505     modified[1] = modif::nothing; // Hash Container
00506 }
00507 
00508 template<typename T>
00509 BlockDomain::DomainT VoxelizeMeshFunctional3D<T>::appliesTo() const {
00510     return BlockDomain::bulk;
00511 }
00512 
00513 
00514 
00515 /* ******** DetectBorderLineFunctional3D ************************************* */
00516 
00517 template<typename T>
00518 void detectBorderLine( MultiScalarField3D<T>& voxelMatrix,
00519                        Box3D const& domain, plint borderWidth )
00520 {
00521     applyProcessingFunctional( new DetectBorderLineFunctional3D<T>(borderWidth),
00522                                domain, voxelMatrix );
00523 }
00524 
00525 template<typename T>
00526 DetectBorderLineFunctional3D<T>::DetectBorderLineFunctional3D(plint borderWidth_)
00527     : borderWidth(borderWidth_)
00528 { }
00529 
00530 template<typename T>
00531 void DetectBorderLineFunctional3D<T>::process (
00532         Box3D domain, ScalarField3D<T>& voxels )
00533 {
00534     for (plint iX = domain.x0; iX <= domain.x1; ++iX) {
00535         for (plint iY = domain.y0; iY <= domain.y1; ++iY) {
00536             for (plint iZ = domain.z0; iZ <= domain.z1; ++iZ) {
00537                 for (plint dx=-borderWidth; dx<=borderWidth; ++dx)
00538                 for (plint dy=-borderWidth; dy<=borderWidth; ++dy)
00539                 for (plint dz=-borderWidth; dz<=borderWidth; ++dz)
00540                 if(!(dx==0 && dy==0 && dz==0)) {
00541                     plint nextX = iX + dx;
00542                     plint nextY = iY + dy;
00543                     plint nextZ = iZ + dz;
00544                     if (contained(Dot3D(nextX,nextY,nextZ),voxels.getBoundingBox())) {
00545                         if ( voxelFlag::outsideFlag(voxels.get(iX,iY,iZ)) &&
00546                              voxelFlag::insideFlag(voxels.get(nextX,nextY,nextZ)) )
00547                         {
00548                             voxels.get(iX,iY,iZ) = voxelFlag::outerBorder;
00549                         }
00550                         if ( voxelFlag::insideFlag(voxels.get(iX,iY,iZ)) &&
00551                              voxelFlag::outsideFlag(voxels.get(nextX,nextY,nextZ)) )
00552                         {
00553                             voxels.get(iX,iY,iZ) = voxelFlag::innerBorder;
00554                         }
00555                     }
00556                 }
00557             }
00558         }
00559     }
00560 }
00561 
00562 template<typename T>
00563 DetectBorderLineFunctional3D<T>* DetectBorderLineFunctional3D<T>::clone() const {
00564     return new DetectBorderLineFunctional3D<T>(*this);
00565 }
00566 
00567 template<typename T>
00568 void DetectBorderLineFunctional3D<T>::getTypeOfModification(std::vector<modif::ModifT>& modified) const {
00569     modified[0] = modif::staticVariables;
00570 }
00571 
00572 template<typename T>
00573 BlockDomain::DomainT DetectBorderLineFunctional3D<T>::appliesTo() const {
00574     return BlockDomain::bulk;
00575 }
00576 
00577 } // namespace plb
00578 
00579 #endif  // VOXELIZER_HH