amanatidesWoo3D.h

Go to the documentation of this file.
00001 
00015 #ifndef _DLR_NUMERIC_AMANATIDESWOO3D_H_
00016 #define _DLR_NUMERIC_AMANATIDESWOO3D_H_
00017 
00018 #include <iostream>
00019 #include <limits>
00020 #include <dlrCommon/exception.h>
00021 #include <dlrNumeric/vector3D.h>
00022 #include <dlrNumeric/transform3D.h>
00023 #include <dlrNumeric/amanatidesWoo3DIterator.h>
00024 
00025 namespace dlr {
00026 
00027   namespace numeric {
00028     
00083     template <class ARRAY3D>
00084     class AmanatidesWoo3D {
00085     public:
00086       /* ================== Public typedefs ================== */
00087 
00095       typedef AmanatidesWoo3DIterator<ARRAY3D> iterator;
00096 
00097       // Iteration over const arrays will be done using a different
00098       // class.
00099       // typedef AmanatidesWoo3DConstIterator<ARRAY3D> const_iterator;
00100     
00101       /* ================== Public methods ================== */
00102 
00137       AmanatidesWoo3D(ARRAY3D& data,
00138                       const Transform3D& voxelTworld,
00139                       const Vector3D& rayOrigin,
00140                       const Vector3D& rayDirection,
00141                       bool downstreamOnly=true);
00142 
00143     
00151       AmanatidesWoo3D(const AmanatidesWoo3D& source);
00152 
00157       ~AmanatidesWoo3D();
00158 
00177       iterator
00178       begin();
00179 
00190       iterator
00191       end();
00192 
00199       ARRAY3D&
00200       getData() {return m_data;}
00201 
00216       inline bool
00217       validIntersection();
00218     
00227       AmanatidesWoo3D&
00228       operator=(const AmanatidesWoo3D& source);
00229   
00230     private:
00231 
00248       std::pair<double, double>
00249       findEntryAndExitPoints(const Vector3D& rayOriginVoxel,
00250                              const Vector3D& rayDirectionVoxel,
00251                              const ARRAY3D& m_data);
00252 
00264       double
00265       findIntersection(const Vector3D& rayOrigin,
00266                        const Vector3D& rayDirection,
00267                        const Vector3D& bVector,
00268                        double cConstant,
00269                        double defaultValue);
00270 
00273       ARRAY3D& m_data;
00274 
00277       int m_initialU;
00278 
00281       int m_initialV;
00282 
00285       int m_initialW;
00286     
00290       int m_stepU;
00291 
00295       int m_stepV;
00296     
00300       int m_stepW;
00301     
00304       double m_tDeltaU;
00305 
00308       double m_tDeltaV;
00309     
00312       double m_tDeltaW;
00313     
00317       double m_tMaxU;
00318 
00322       double m_tMaxV;
00323 
00327       double m_tMaxW;
00328 
00335       double m_tStart;
00336 
00339       bool m_validIntersection;
00340     };
00341 
00342   } // namespace numeric
00343 
00344 } // namespace dlr
00345 
00346 
00347 /* ======= Declarations to maintain compatibility with legacy code. ======= */
00348 
00349 namespace dlr {
00350 
00351   using numeric::AmanatidesWoo3D;
00352 
00353 } // namespace dlr
00354 
00355 
00356 /* ========================================================= */
00357 /* Implementation follows                                    */
00358 /* ========================================================= */
00359 
00360 namespace dlr {
00361 
00362   namespace numeric {
00363     
00364     template <class ARRAY3D>
00365     AmanatidesWoo3D<ARRAY3D>::
00366     AmanatidesWoo3D(ARRAY3D& data,
00367                     const Transform3D& voxelTworld,
00368                     const Vector3D& rayOrigin,
00369                     const Vector3D& rayDirection,
00370                     bool downstreamOnly)
00371       : m_data(data),
00372         m_initialU(-1),  // Initialize to illegal values.  These will be 
00373         m_initialV(-1),  // replaced with legal ones if there's a valid
00374         m_initialW(-1),  // intersection between the ray and the voxel
00375         // array.
00376         m_stepU(),
00377         m_stepV(),
00378         m_stepW(),
00379         m_tDeltaU(),
00380         m_tDeltaV(),
00381         m_tDeltaW(),
00382         m_tMaxU(),
00383         m_tMaxV(),
00384         m_tMaxW(),
00385         m_tStart(),
00386         m_validIntersection(true)    
00387     {
00388       // First convert everything into voxel coordinates.
00389       Vector3D rayOriginVoxel = voxelTworld * rayOrigin;
00390       Vector3D rayDirectionVoxel =
00391         (voxelTworld * (rayOrigin + rayDirection)) - rayOriginVoxel;
00392 
00393       // Now find points entry and exit from the CT volume.  These are
00394       // expressed in as parameter values tEntry and tExit such that
00395       // (rayOriginVoxel + tEntry * rayDirectionVoxel) is the entry
00396       // point and (rayOriginVoxel + tExit * rayDirectionVoxel) is the
00397       // exit point.
00398       std::pair<double, double> tEntry_tExit =
00399         this->findEntryAndExitPoints(rayOriginVoxel, rayDirectionVoxel, m_data);
00400 
00401       // Sometimes we want to disallow any voxels which are "behind" the
00402       // ray origin.  That is, sometimes we want to only trace those
00403       // parts of the ray which correspond to positive values of the
00404       // parameter t.
00405       if(downstreamOnly && (tEntry_tExit.first < 0.0)) {
00406         tEntry_tExit.first = 0.0;
00407       }
00408 
00409       // Sometimes the there's no intersection with the voxel array.  In
00410       // this case, tExit will be less than tEntry, and we don't bother
00411       // doing any more calculation.
00412       if(tEntry_tExit.second <= tEntry_tExit.first) {
00413         m_validIntersection = false;
00414         return;
00415       }
00416 
00417       // Now make the point of entry explicit.
00418       Vector3D entryPoint =
00419         rayOriginVoxel + tEntry_tExit.first * rayDirectionVoxel;
00420 
00421       // Correct for rounding error which sometimes makes entryPoint
00422       // have values like -1.0e-14.
00423       if(entryPoint.x() < 0.0) {
00424         entryPoint.x() = 0.0;
00425       }
00426       if(entryPoint.y() < 0.0) {
00427         entryPoint.y() = 0.0;
00428       }
00429       if(entryPoint.z() < 0.0) {
00430         entryPoint.z() = 0.0;
00431       }
00432     
00433       // Finally, assign the variables described in the Amanatides' and
00434       // Woo's paper.
00435 
00436       // Since we've already converted to voxel coords, finding the
00437       // first voxel on the path is trivial.
00438       m_initialU = static_cast<int>(entryPoint.x());
00439       m_initialV = static_cast<int>(entryPoint.y());
00440       m_initialW = static_cast<int>(entryPoint.z());
00441 
00442       // There's an similar case to the one handled by the rounding
00443       // error test above.  Member function findEntryAndExitPoints()
00444       // uses m_data.shape to define the maximum U, V, and W
00445       // coordinates, respectively.  This means that it's very possible
00446       // for m_initialU to be equal to m_data.columns(), which will
00447       // cause an out-of-bounds index. Correct for this now.
00448       if(m_initialU == static_cast<int>(m_data.shape()[2])) {
00449         --m_initialU;
00450       }
00451       if(m_initialV == static_cast<int>(m_data.shape()[1])) {
00452         --m_initialV;
00453       }
00454       if(m_initialW == static_cast<int>(m_data.shape()[0])) {
00455         --m_initialW;
00456       }
00457 
00458       // Sanity check.
00459       if((m_initialU >= static_cast<int>(m_data.shape()[2]))
00460          || (m_initialV >= static_cast<int>(m_data.shape()[1]))
00461          || (m_initialW >= static_cast<int>(m_data.shape()[0]))) {
00462         DLR_THROW3(LogicException,
00463                    "AmanatidesWoo3D::AmanatidesWoo3D(ARRAY3D&, ...)",
00464                    "Illegal value for m_initialU, m_initialV, or m_initialW.");
00465       }
00466     
00467       // m_tStart is just the same as tEntry.
00468       m_tStart = tEntry_tExit.first;
00469     
00470       // The remaining variables depend on whether U & V will be
00471       // increasing or decreasing as we travel along the ray, so we need
00472       // if clauses.  Please see the declaration for documentation on
00473       // what each of these member variables means.
00474       if(rayDirectionVoxel.x() > 0.0) {
00475         m_stepU = 1;
00476         m_tDeltaU = 1.0 / rayDirectionVoxel.x();
00477         m_tMaxU = m_tStart + (((m_initialU + 1) - entryPoint.x())
00478                               / rayDirectionVoxel.x());
00479       } else if(rayDirectionVoxel.x() < 0.0) {
00480         m_stepU = -1;
00481         m_tDeltaU = -(1.0 / rayDirectionVoxel.x());
00482         m_tMaxU = m_tStart + ((m_initialU - entryPoint.x())
00483                               / rayDirectionVoxel.x());
00484       } else { // rayDirectionVoxel.x() == 0.0;
00485         m_stepU = 0;
00486         m_tDeltaU = std::numeric_limits<double>::max();
00487         m_tMaxU = std::numeric_limits<double>::max();
00488       }
00489       if(rayDirectionVoxel.y() > 0.0) {
00490         m_stepV = 1;
00491         m_tDeltaV = 1.0 / rayDirectionVoxel.y();
00492         m_tMaxV = m_tStart + (((m_initialV + 1) - entryPoint.y())
00493                               / rayDirectionVoxel.y());
00494       } else if(rayDirectionVoxel.y() < 0.0) {
00495         m_stepV = -1;
00496         m_tDeltaV = -(1.0 / rayDirectionVoxel.y());
00497         m_tMaxV = m_tStart + ((m_initialV - entryPoint.y())
00498                               / rayDirectionVoxel.y());
00499       } else { // rayDirectionVoxel.y() == 0.0;
00500         m_stepV = 0;
00501         m_tDeltaV = std::numeric_limits<double>::max();
00502         m_tMaxV = std::numeric_limits<double>::max();
00503       }
00504       if(rayDirectionVoxel.z() > 0.0) {
00505         m_stepW = 1;
00506         m_tDeltaW = 1.0 / rayDirectionVoxel.z();
00507         m_tMaxW = m_tStart + (((m_initialW + 1) - entryPoint.z())
00508                               / rayDirectionVoxel.z());
00509       } else if(rayDirectionVoxel.z() < 0.0) {
00510         m_stepW = -1;
00511         m_tDeltaW = -(1.0 / rayDirectionVoxel.z());
00512         m_tMaxW = m_tStart + ((m_initialW - entryPoint.z())
00513                               / rayDirectionVoxel.z());
00514       } else { // rayDirectionVoxel.z() == 0.0;
00515         m_stepW = 0;
00516         m_tDeltaW = std::numeric_limits<double>::max();
00517         m_tMaxW = std::numeric_limits<double>::max();
00518       }
00519     }
00520     
00521     template <class ARRAY3D>
00522     AmanatidesWoo3D<ARRAY3D>::
00523     AmanatidesWoo3D(const AmanatidesWoo3D& source)
00524       : m_data(source.m_data),
00525         m_initialU(source.m_initialU),
00526         m_initialV(source.m_initialV),
00527         m_initialW(source.m_initialW),
00528         m_stepU(source.m_stepU),
00529         m_stepV(source.m_stepV),
00530         m_stepW(source.m_stepW),
00531         m_tDeltaU(source.m_tDeltaU),
00532         m_tDeltaV(source.m_tDeltaV),
00533         m_tDeltaW(source.m_tDeltaW),
00534         m_tMaxU(source.m_tMaxU),
00535         m_tMaxV(source.m_tMaxV),
00536         m_tMaxW(source.m_tMaxW),
00537         m_tStart(source.m_tStart),
00538         m_validIntersection(source.m_validIntersection)
00539     {
00540       // Empty
00541     }
00542 
00543     template <class ARRAY3D>
00544     AmanatidesWoo3D<ARRAY3D>::
00545     ~AmanatidesWoo3D() {};
00546 
00547     template <class ARRAY3D>
00548     typename AmanatidesWoo3D<ARRAY3D>::iterator
00549     AmanatidesWoo3D<ARRAY3D>::
00550     begin()
00551     {
00552       return AmanatidesWoo3DIterator<ARRAY3D>(
00553         m_data, m_initialU, m_initialV, m_initialW, m_stepU, m_stepV, m_stepW,
00554         m_tMaxU, m_tMaxV, m_tMaxW, m_tDeltaU, m_tDeltaV, m_tDeltaW, m_tStart);
00555     }
00556 
00557     template <class ARRAY3D>
00558     typename AmanatidesWoo3D<ARRAY3D>::iterator
00559     AmanatidesWoo3D<ARRAY3D>::
00560     end()
00561     {
00562       // AmanatidesWoo3DIterator<ARRAY3D>::operator==(...) considers all
00563       // iterators with illegal voxel coordinates to be equal, so all we
00564       // have to do here is return an iterator which references an
00565       // illegal voxel.
00566       return AmanatidesWoo3DIterator<ARRAY3D>(
00567         m_data, -1, -1, -1, m_stepU, m_stepV, m_stepW,
00568         m_tMaxU, m_tMaxV, m_tMaxW, m_tDeltaU, m_tDeltaV, m_tDeltaW, m_tStart);
00569     }
00570 
00571     template <class ARRAY3D>
00572     inline bool
00573     AmanatidesWoo3D<ARRAY3D>::
00574     validIntersection()
00575     {
00576       return m_validIntersection;
00577     }
00578 
00579     template <class ARRAY3D>
00580     AmanatidesWoo3D<ARRAY3D>&
00581     AmanatidesWoo3D<ARRAY3D>::
00582     operator=(const AmanatidesWoo3D& source)
00583     {
00584       m_data = source.m_data;
00585       m_initialU = source.m_initialU;
00586       m_initialV = source.m_initialV;
00587       m_initialW = source.m_initialW;
00588       m_stepU = source.m_stepU;
00589       m_stepV = source.m_stepV;
00590       m_stepW = source.m_stepW;
00591       m_tDeltaU = source.m_tDeltaU;
00592       m_tDeltaV = source.m_tDeltaV;
00593       m_tDeltaW = source.m_tDeltaW;
00594       m_tMaxU = source.m_tMaxU;
00595       m_tMaxV = source.m_tMaxV;
00596       m_tMaxW = source.m_tMaxW;
00597       m_tStart = source.m_tStart;
00598       m_validIntersection = source.m_validIntersection;
00599       return *this;
00600     }
00601     
00602     template <class ARRAY3D>
00603     std::pair<double, double>
00604     AmanatidesWoo3D<ARRAY3D>::
00605     findEntryAndExitPoints(const Vector3D& rayOriginVoxel,
00606                            const Vector3D& rayDirectionVoxel,
00607                            const ARRAY3D& m_data)
00608     {
00609       // First find intersection with each boundary line.
00610 
00611       // ... Find the intersection with the plane U = 0, or else a
00612       // really small number if rayDirection is parallel to the U axis.
00613       double tIntersectU0 = findIntersection(
00614         rayOriginVoxel, rayDirectionVoxel, Vector3D(1.0, 0.0, 0.0),
00615         0.0, std::numeric_limits<double>::min());
00616     
00617       // ... Find the intersection with the plane U = m_data.shape()[2],
00618       // or else a really big number if rayDirection is parallel to the
00619       // U axis.
00620       double tIntersectU1 = findIntersection(
00621         rayOriginVoxel, rayDirectionVoxel, Vector3D(1.0, 0.0, 0.0),
00622         m_data.shape()[2], std::numeric_limits<double>::max());
00623     
00624       // ... Find the intersection with the plane V = 0, or else a
00625       // really small number if rayDirection is parallel to the V axis.
00626       double tIntersectV0 = findIntersection(
00627         rayOriginVoxel, rayDirectionVoxel, Vector3D(0.0, 1.0, 0.0),
00628         0.0, std::numeric_limits<double>::min());
00629     
00630       // ... Find the intersection with the plane V = m_data.shape()[1],
00631       // or else a really big number if rayDirection is parallel to the
00632       // V axis.
00633       double tIntersectV1 = findIntersection(
00634         rayOriginVoxel, rayDirectionVoxel, Vector3D(0.0, 1.0, 0.0),
00635         m_data.shape()[1], std::numeric_limits<double>::max());
00636     
00637       // ... Find the intersection with the plane W = 0, or else a
00638       // really small number if rayDirection is parallel to the W axis.
00639       double tIntersectW0 = findIntersection(
00640         rayOriginVoxel, rayDirectionVoxel, Vector3D(0.0, 0.0, 1.0),
00641         0.0, std::numeric_limits<double>::min());
00642     
00643       // ... Find the intersection with the plane W = m_data.shape()[0],
00644       // or else a really big number if rayDirection is parallel to the
00645       // W axis.
00646       double tIntersectW1 = findIntersection(
00647         rayOriginVoxel, rayDirectionVoxel, Vector3D(0.0, 0.0, 1.0),
00648         m_data.shape()[0], std::numeric_limits<double>::max());
00649 
00650       // Now find the closer and farther of each pair of intersections.
00651       double tMinU = std::min(tIntersectU0, tIntersectU1);
00652       double tMinV = std::min(tIntersectV0, tIntersectV1);
00653       double tMinW = std::min(tIntersectW0, tIntersectW1);
00654       double tMaxU = std::max(tIntersectU0, tIntersectU1);
00655       double tMaxV = std::max(tIntersectV0, tIntersectV1);
00656       double tMaxW = std::max(tIntersectW0, tIntersectW1);
00657 
00658       // Compute closest point which could possibly intersect with the volume.
00659       double tEntry = std::max(tMinU, std::max(tMinV, tMinW));
00660       // Compute farthest point which could possibly intersect with the volume.
00661       double tExit = std::min(tMaxU, std::min(tMaxV, tMaxW));
00662 
00663       // Return our findings.
00664       return std::make_pair(tEntry, tExit);
00665     }
00666 
00667     template <class ARRAY3D>
00668     double
00669     AmanatidesWoo3D<ARRAY3D>::
00670     findIntersection(const Vector3D& rayOrigin,
00671                      const Vector3D& rayDirection,
00672                      const Vector3D& bVector,
00673                      double cConstant,
00674                      double defaultValue)
00675     {
00676       // bVector and cConstant describe the desired plane:
00677       //   dot(x, bVector) = cConstant.
00678       // Now, x is constrained to lie on the specified line:
00679       //   x = rayOrigin + alpha * rayDirection.
00680       // Substituting, we have:
00681       //   dot(rayOrigin + alpha * rayDirection, bVector) = cConstant.
00682       // Which we rewrite:
00683       //   dot(rayOrigin, bVector) + alpha * dot(rayDirection, bVector)
00684       //     = cConstant.
00685       // Solving for alpha, we have:
00686       //   alpha = (cConstant - dot(rayOrigin, bVector))
00687       //            / dot(rayDirection, bVector).
00688       double denominator = dot(rayDirection, bVector);
00689       if(denominator == 0.0) {
00690         return defaultValue;
00691       }
00692       // else
00693       return (cConstant - dot(rayOrigin, bVector)) / denominator;
00694     }      
00695 
00696   } // namespace numeric
00697 
00698 } // namespace dlr
00699 
00700 #endif // #ifndef _DLR_NUMERIC_AMANATIDESWOO3D_H_

Generated on Wed Nov 25 00:42:40 2009 for dlrUtilities Utility Library by  doxygen 1.5.8