amanatidesWoo2D.h

Go to the documentation of this file.
00001 
00015 #ifndef _DLR_NUMERIC_AMANATIDESWOO2D_H_
00016 #define _DLR_NUMERIC_AMANATIDESWOO2D_H_
00017 
00018 #include <iostream>
00019 #include <limits>
00020 #include <dlrCommon/exception.h>
00021 #include <dlrNumeric/vector2D.h>
00022 #include <dlrNumeric/transform2D.h>
00023 #include <dlrNumeric/utilities.h>
00024 #include <dlrNumeric/amanatidesWoo2DIterator.h>
00025 
00026 namespace dlr {
00027 
00028   namespace numeric {
00029     
00079     template <class ARRAY2D>
00080     class AmanatidesWoo2D {
00081     public:
00082       /* ================== Public typedefs ================== */
00083 
00091       typedef AmanatidesWoo2DIterator<ARRAY2D> iterator;
00092 
00093       // Iteration over const arrays will be done using a different
00094       // class.
00095       // typedef AmanatidesWoo2DConstIterator<ARRAY2D> const_iterator;
00096     
00097       /* ================== Public methods ================== */
00098 
00133       AmanatidesWoo2D(ARRAY2D& data,
00134                       const Transform2D& pixelTworld,
00135                       const Vector2D& rayOrigin,
00136                       const Vector2D& rayDirection,
00137                       bool downstreamOnly=true);
00138 
00139     
00147       AmanatidesWoo2D(const AmanatidesWoo2D& source);
00148 
00153       ~AmanatidesWoo2D();
00154 
00173       iterator
00174       begin();
00175 
00186       iterator
00187       end();
00188 
00195       ARRAY2D&
00196       getData() {return m_data;}
00197     
00212       inline bool
00213       validIntersection();
00214 
00223       AmanatidesWoo2D&
00224       operator=(const AmanatidesWoo2D& source);
00225   
00226     private:
00227 
00244       std::pair<double, double>
00245       findEntryAndExitPoints(const Vector2D& rayOriginPixel,
00246                              const Vector2D& rayDirectionPixel,
00247                              const ARRAY2D& data);
00248 
00260       double
00261       findIntersection(const Vector2D& rayOrigin,
00262                        const Vector2D& rayDirection,
00263                        const Vector2D& bVector,
00264                        double cConstant,
00265                        double defaultValue);
00266 
00269       ARRAY2D& m_data;
00270 
00273       int m_initialU;
00274 
00277       int m_initialV;
00278 
00282       int m_stepU;
00283 
00287       int m_stepV;
00288     
00291       double m_tDeltaU;
00292 
00295       double m_tDeltaV;
00296     
00300       double m_tMaxU;
00301 
00305       double m_tMaxV;
00306 
00313       double m_tStart;
00314 
00317       bool m_validIntersection;
00318     };
00319 
00320   } // namespace numeric
00321 
00322 } // namespace dlr
00323 
00324 
00325 /* ======= Declarations to maintain compatibility with legacy code. ======= */
00326 
00327 namespace dlr {
00328 
00329   using numeric::AmanatidesWoo2D;
00330 
00331 } // namespace dlr
00332 
00333 
00334 /* ========================================================= */
00335 /* Implementation follows                                    */
00336 /* ========================================================= */
00337 
00338 namespace dlr {
00339 
00340   namespace numeric {
00341     
00342     template <class ARRAY2D>
00343     AmanatidesWoo2D<ARRAY2D>::
00344     AmanatidesWoo2D(ARRAY2D& data,
00345                     const Transform2D& pixelTworld,
00346                     const Vector2D& rayOrigin,
00347                     const Vector2D& rayDirection,
00348                     bool downstreamOnly)
00349       : m_data(data),
00350         m_initialU(-1),  // Initialize to illegal values.  These will be 
00351         m_initialV(-1),  // replaced with legal ones if there's a valid
00352         // intersection between the ray and the pixel
00353         // array.
00354         m_stepU(),
00355         m_stepV(),
00356         m_tDeltaU(),
00357         m_tDeltaV(),
00358         m_tMaxU(),
00359         m_tMaxV(),
00360         m_tStart(),
00361         m_validIntersection(true)    
00362     {
00363       // First convert everything into pixel coordinates.
00364       Vector2D rayOriginPixel = pixelTworld * rayOrigin;
00365       Vector2D rayDirectionPixel =
00366         (pixelTworld * (rayOrigin + rayDirection)) - rayOriginPixel;
00367 
00368       // Now find points entry and exit from the CT volume.  These are
00369       // expressed in as parameter values tEntry and tExit such that
00370       // (rayOriginPixel + tEntry * rayDirectionPixel) is the entry
00371       // point and (rayOriginPixel + tExit * rayDirectionPixel) is the
00372       // exit point.
00373       std::pair<double, double> tEntry_tExit =
00374         this->findEntryAndExitPoints(rayOriginPixel, rayDirectionPixel, m_data);
00375 
00376       // Sometimes we want to disallow any pixels which are "behind" the
00377       // ray origin.  That is, sometimes we want to only trace those
00378       // parts of the ray which correspond to positive values of the
00379       // parameter t.
00380       if(downstreamOnly && (tEntry_tExit.first < 0.0)) {
00381         tEntry_tExit.first = 0.0;
00382       }
00383 
00384       // Sometimes the there's no intersection with the pixel array.  In
00385       // this case, tExit will be less than tEntry, and we don't bother
00386       // doing any more calculation.
00387       if(tEntry_tExit.second <= tEntry_tExit.first) {
00388         m_validIntersection = false;
00389         return;
00390       }
00391 
00392       // Now make the point of entry explicit.
00393       Vector2D entryPoint =
00394         rayOriginPixel + tEntry_tExit.first * rayDirectionPixel;
00395 
00396       // Correct for rounding error which sometimes makes entryPoint
00397       // have values like -1.0e-14.
00398       if(entryPoint.x() < 0.0) {
00399         entryPoint.x() = 0.0;
00400       }
00401       if(entryPoint.y() < 0.0) {
00402         entryPoint.y() = 0.0;
00403       }
00404     
00405       // Finally, assign the variables described in the Amanatides' and
00406       // Woo's paper.
00407 
00408       // Since we've already converted to pixel coords, finding the
00409       // first pixel on the path is trivial.
00410       m_initialU = static_cast<int>(entryPoint.x());
00411       m_initialV = static_cast<int>(entryPoint.y());
00412 
00413       // There's an similar case to the one handled by the rounding
00414       // error test above.  Member function findEntryAndExitPoints()
00415       // uses m_data.columns() and m_data.rows() to define the maximum U
00416       // and V coordinates, respectively.  This means that it's very
00417       // possible for m_initialU to be equal to m_data.columns(), which
00418       // will cause an out-of-bounds index. Correct for this now.
00419       if(m_initialU == static_cast<int>(m_data.columns())) {
00420         --m_initialU;
00421       }
00422       if(m_initialV == static_cast<int>(m_data.rows())) {
00423         --m_initialV;
00424       }
00425 
00426       // Sanity check.
00427       if((m_initialU >= static_cast<int>(m_data.columns()))
00428          || (m_initialV >= static_cast<int>(m_data.rows()))) {
00429         DLR_THROW3(LogicException,
00430                    "AmanatidesWoo2D::AmanatidesWoo2D(ARRAY2D&, ...)",
00431                    "Illegal value for m_initialU or m_initialV.");
00432       }
00433     
00434       // m_tStart is just the same as tEntry.
00435       m_tStart = tEntry_tExit.first;
00436     
00437       // The remaining variables depend on whether U & V will be
00438       // increasing or decreasing as we travel along the ray, so we need
00439       // if clauses.  Please see the declaration for documentation on
00440       // what each of these member variables means.
00441       if(rayDirectionPixel.x() > 0.0) {
00442         m_stepU = 1;
00443         m_tDeltaU = 1.0 / rayDirectionPixel.x();
00444         m_tMaxU = m_tStart + (((m_initialU + 1) - entryPoint.x())
00445                               / rayDirectionPixel.x());
00446       } else if(rayDirectionPixel.x() < 0.0) {
00447         m_stepU = -1;
00448         m_tDeltaU = -(1.0 / rayDirectionPixel.x());
00449         m_tMaxU = m_tStart + ((m_initialU - entryPoint.x())
00450                               / rayDirectionPixel.x());
00451       } else { // rayDirectionPixel.x() == 0.0;
00452         m_stepU = 0;
00453         m_tDeltaU = std::numeric_limits<double>::max();
00454         m_tMaxU = std::numeric_limits<double>::max();
00455       }
00456       if(rayDirectionPixel.y() > 0.0) {
00457         m_stepV = 1;
00458         m_tDeltaV = 1.0 / rayDirectionPixel.y();
00459         m_tMaxV = m_tStart + (((m_initialV + 1) - entryPoint.y())
00460                               / rayDirectionPixel.y());
00461       } else if(rayDirectionPixel.y() < 0.0) {
00462         m_stepV = -1;
00463         m_tDeltaV = -(1.0 / rayDirectionPixel.y());
00464         m_tMaxV = m_tStart + ((m_initialV - entryPoint.y())
00465                               / rayDirectionPixel.y());
00466       } else { // rayDirectionPixel.y() == 0.0;
00467         m_stepV = 0;
00468         m_tDeltaV = std::numeric_limits<double>::max();
00469         m_tMaxV = std::numeric_limits<double>::max();
00470       }
00471     }
00472 
00473 
00474     // The copy constructor deep copies its argument.
00475     template <class ARRAY2D>
00476     AmanatidesWoo2D<ARRAY2D>::
00477     AmanatidesWoo2D(const AmanatidesWoo2D& source)
00478       : m_data(source.m_data),
00479         m_initialU(source.m_initialU),
00480         m_initialV(source.m_initialV),
00481         m_stepU(source.m_stepU),
00482         m_stepV(source.m_stepV),
00483         m_tDeltaU(source.m_tDeltaU),
00484         m_tDeltaV(source.m_tDeltaV),
00485         m_tMaxU(source.m_tMaxU),
00486         m_tMaxV(source.m_tMaxV),
00487         m_tStart(source.m_tStart),
00488         m_validIntersection(source.m_validIntersection)
00489     {
00490       // Empty
00491     }
00492 
00493     template <class ARRAY2D>
00494     AmanatidesWoo2D<ARRAY2D>::
00495     ~AmanatidesWoo2D() {};
00496 
00497     template <class ARRAY2D>
00498     typename AmanatidesWoo2D<ARRAY2D>::iterator
00499     AmanatidesWoo2D<ARRAY2D>::
00500     begin()
00501     {
00502       return AmanatidesWoo2DIterator<ARRAY2D>(
00503         m_data, m_initialU, m_initialV, m_stepU, m_stepV, m_tMaxU, m_tMaxV,
00504         m_tDeltaU, m_tDeltaV, m_tStart);
00505     }
00506 
00507     template <class ARRAY2D>
00508     typename AmanatidesWoo2D<ARRAY2D>::iterator
00509     AmanatidesWoo2D<ARRAY2D>::
00510     end()
00511     {
00512       // AmanatidesWoo2DIterator<ARRAY2D>::operator==(...) considers all
00513       // iterators with illegal pixel coordinates to be equal, so all we
00514       // have to do here is return an iterator which references an
00515       // illegal pixel.
00516       return AmanatidesWoo2DIterator<ARRAY2D>(
00517         m_data, -1, -1, m_stepU, m_stepV, m_tMaxU, m_tMaxV,
00518         m_tDeltaU, m_tDeltaV, m_tStart);
00519     }
00520 
00521     template <class ARRAY2D>
00522     inline bool
00523     AmanatidesWoo2D<ARRAY2D>::
00524     validIntersection()
00525     {
00526       return m_validIntersection;
00527     }
00528 
00529     template <class ARRAY2D>
00530     AmanatidesWoo2D<ARRAY2D>&
00531     AmanatidesWoo2D<ARRAY2D>::
00532     operator=(const AmanatidesWoo2D& source)
00533     {
00534       m_data = source.m_data;
00535       m_initialU = source.m_initialU;
00536       m_initialV = source.m_initialV;
00537       m_stepU = source.m_stepU;
00538       m_stepV = source.m_stepV;
00539       m_tDeltaU = source.m_tDeltaU;
00540       m_tDeltaV = source.m_tDeltaV;
00541       m_tMaxU = source.m_tMaxU;
00542       m_tMaxV = source.m_tMaxV;
00543       m_tStart = source.m_tStart;
00544       m_validIntersection = source.m_validIntersection;
00545       return *this;
00546     }
00547     
00548     template <class ARRAY2D>
00549     std::pair<double, double>
00550     AmanatidesWoo2D<ARRAY2D>::
00551     findEntryAndExitPoints(const Vector2D& rayOriginPixel,
00552                            const Vector2D& rayDirectionPixel,
00553                            const ARRAY2D& data)
00554     {
00555       // First find intersection with each boundary line.
00556 
00557       // ... Find the intersection with the line U = 0, or else a really
00558       // small number if rayDirection is parallel to the rows of the
00559       // array.
00560       double tIntersectU0 = findIntersection(
00561         rayOriginPixel, rayDirectionPixel, Vector2D(1.0, 0.0), 0.0,
00562         std::numeric_limits<double>::min());
00563 
00564       // ... Find the intersection with the line U = data.columns(),
00565       // or else a really big number if rayDirection is parallel to the
00566       // rows of the array.
00567       double tIntersectU1 = findIntersection(
00568         rayOriginPixel, rayDirectionPixel, Vector2D(1.0, 0.0), data.columns(),
00569         std::numeric_limits<double>::max());
00570 
00571       // ... Find the intersection with the line V = 0, or else a really
00572       // small number if rayDirection is parallel to the columns of the
00573       // array.
00574       double tIntersectV0 = findIntersection(
00575         rayOriginPixel, rayDirectionPixel, Vector2D(0.0, 1.0), 0.0,
00576         std::numeric_limits<double>::min());
00577     
00578       // ... Find the intersection with the line V = data.rows(), or
00579       // else a really big number if rayDirection is parallel to the
00580       // columns of the array.
00581       double tIntersectV1 = findIntersection(
00582         rayOriginPixel, rayDirectionPixel, Vector2D(0.0, 1.0), data.rows(),
00583         std::numeric_limits<double>::max());
00584 
00585       // Now find the closer and farther of each pair of intersections.
00586       double tMinU = std::min(tIntersectU0, tIntersectU1);
00587       double tMinV = std::min(tIntersectV0, tIntersectV1);
00588       double tMaxU = std::max(tIntersectU0, tIntersectU1);
00589       double tMaxV = std::max(tIntersectV0, tIntersectV1);
00590 
00591       // Compute closest point which could possibly intersect with the volume.
00592       double tEntry = std::max(tMinU, tMinV);
00593       // Compute farthest point which could possibly intersect with the volume.
00594       double tExit = std::min(tMaxU, tMaxV);
00595 
00596       // Return our findings.
00597       return std::make_pair(tEntry, tExit);
00598     }
00599 
00600     template <class ARRAY2D>
00601     double
00602     AmanatidesWoo2D<ARRAY2D>::
00603     findIntersection(const Vector2D& rayOrigin,
00604                      const Vector2D& rayDirection,
00605                      const Vector2D& bVector,
00606                      double cConstant,
00607                      double defaultValue)
00608     {
00609       // bVector and cConstant describe the desired plane:
00610       //   dot(x, bVector) = cConstant.
00611       // Now, x is constrained to lie on the specified line:
00612       //   x = rayOrigin + t * rayDirection.
00613       // Substituting, we have:
00614       //   dot(rayOrigin + t * rayDirection, bVector) = cConstant.
00615       // Which we rewrite:
00616       //   dot(rayOrigin, bVector) + t * dot(rayDirection, bVector)
00617       //     = cConstant.
00618       // Solving for t, we have:
00619       //   t = (cConstant - dot(rayOrigin, bVector))
00620       //        / dot(rayDirection, bVector).
00621       double denominator = dot(rayDirection, bVector);
00622       if(denominator == 0.0) {
00623         return defaultValue;
00624       }
00625       // else
00626       return (cConstant - dot(rayOrigin, bVector)) / denominator;
00627     }      
00628 
00629   } // namespace numeric
00630 
00631 } // namespace dlr
00632 
00633 #endif // #ifndef _DLR_NUMERIC_AMANATIDESWOO2D_H_

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