compass.hpp

Go to the documentation of this file.
00001 
00028 #ifndef SGDK_MATH_GEOM_COMPASS_HPP
00029 #define SGDK_MATH_GEOM_COMPASS_HPP
00030 
00031 #include <vector>  // std::vector
00032 #include <swiss_gd_knife/math/zero_checker.hpp>  // sgdk::ZeroChecker
00033 #include <swiss_gd_knife/zref/tangent.hpp>  // sgdk::Tangent
00034 #include <swiss_gd_knife/zref/pi.hpp>  // sgdk::Pi
00035 
00036 namespace sgdk {
00037 
00039 
00055 class ZeroDirection
00056 {
00057  public:
00063     static const ZeroDirection NEGATIVE_X_AXIS;
00064 
00070     static const ZeroDirection NEGATIVE_Y_AXIS;
00071 
00077     static const ZeroDirection POSITIVE_X_AXIS;
00078 
00084     static const ZeroDirection POSITIVE_Y_AXIS;
00085 
00086  private:
00087     bool _is_y_axis;
00088     bool _is_positive;
00089 
00090     ZeroDirection(const bool is_y_axis, const bool is_positive)
00091       : _is_y_axis(is_y_axis)
00092       , _is_positive(is_positive)
00093     {
00094     }
00095 
00096  public:
00098 
00104     ZeroDirection(const ZeroDirection& copy)
00105       : _is_y_axis(copy._is_y_axis)
00106       , _is_positive(copy._is_positive)
00107     {
00108     }
00109 
00111 
00118     ZeroDirection& operator=(const ZeroDirection& copy)
00119     {
00120         _is_y_axis   = copy._is_y_axis;
00121         _is_positive = copy._is_positive;
00122         return *this;
00123     }
00124 
00134     inline bool isParallelToXAxis() const
00135     {
00136         return !_is_y_axis;
00137     }
00138 
00148     inline bool isParallelToYAxis() const
00149     {
00150         return _is_y_axis;
00151     }
00152 
00162     inline bool isNegative() const
00163     {
00164         return !_is_positive;
00165     }
00166 
00176     inline bool isPositive() const
00177     {
00178         return _is_positive;
00179     }
00180 };
00181 
00182 const ZeroDirection
00183       ZeroDirection::NEGATIVE_X_AXIS
00184     = ZeroDirection(false, false);
00185 const ZeroDirection
00186       ZeroDirection::NEGATIVE_Y_AXIS
00187     = ZeroDirection(true, false);
00188 const ZeroDirection
00189       ZeroDirection::POSITIVE_X_AXIS
00190     = ZeroDirection(false, true);
00191 const ZeroDirection
00192       ZeroDirection::POSITIVE_Y_AXIS
00193     = ZeroDirection(true, true);
00194 
00196 
00214 class PositiveRotation
00215 {
00216  public:
00222     static const PositiveRotation CLOCKWISE;
00223 
00229     static const PositiveRotation COUNTERCLOCKWISE;
00230 
00231  private:
00232     bool _is_clockwise;
00233 
00234     explicit PositiveRotation(const bool is_clockwise)
00235       : _is_clockwise(is_clockwise)
00236     {
00237     }
00238 
00239  public:
00241 
00247     PositiveRotation(const PositiveRotation& copy)
00248       : _is_clockwise(copy._is_clockwise)
00249     {
00250     }
00251 
00253 
00260     PositiveRotation& operator=(const PositiveRotation& copy)
00261     {
00262         _is_clockwise = copy._is_clockwise;
00263         return *this;
00264     }
00265 
00276     inline bool isClockwise() const
00277     {
00278         return _is_clockwise;
00279     }
00280 
00289     inline bool isCounterclockwise() const
00290     {
00291         return !_is_clockwise;
00292     }
00293 };
00294 
00295 const PositiveRotation
00296       PositiveRotation::CLOCKWISE
00297     = PositiveRotation(true);
00298 const PositiveRotation
00299       PositiveRotation::COUNTERCLOCKWISE
00300     = PositiveRotation(false);
00301 
00303 
00371 template <
00372 #ifndef SGDK_DOX
00373     typename Direction = unsigned int
00374   , typename DirectionChange = int
00375   , typename PiConstant = sgdk::Pi
00376   , typename TangentFunction = sgdk::Tangent
00377 #endif  /* SGDK_DOX */
00378 >
00379 class Compass
00380 {
00381  public:
00385 #ifdef SGDK_DOX
00386     typedef implementation_defined RealNumber;
00387 #else
00388     typedef typename TangentFunction::result_type
00389             RealNumber;
00390 #endif  /* SGDK_DOX */
00391 
00392  private:
00393     typedef std::vector<RealNumber>
00394             RealNumberContainer;
00395 
00396     static const TangentFunction  _tan;
00397 
00398     Direction                     _direction_count;
00399     RealNumberContainer           _neg_cot_thetas;
00400     ZeroDirection                 _zero_direction;
00401     PositiveRotation              _positive_rotation;
00402     const ZeroChecker<RealNumber> _zero_checker;
00403 
00404  public:
00406 
00420     explicit Compass(
00421         const Direction direction_count = 4
00422       , const ZeroDirection& zero_direction = ZeroDirection::POSITIVE_Y_AXIS
00423       , const PositiveRotation& positive_rotation = PositiveRotation::CLOCKWISE
00424       , const RealNumber zero_tolerance = 0.00001f
00425     )
00426       : _direction_count(direction_count)
00427       , _neg_cot_thetas(
00428           (
00429               (direction_count & 1)
00430             ? (direction_count - 1)
00431             : (direction_count >> 1)
00432           )
00433           , RealNumber()
00434         )
00435       , _zero_direction(zero_direction)
00436       , _positive_rotation(positive_rotation)
00437       , _zero_checker(zero_tolerance)
00438     {
00439         _validate();
00440     }
00441 
00443 
00449     Compass(const Compass& copy)
00450       : _direction_count(copy._direction_count)
00451       , _neg_cot_thetas(copy._neg_cot_thetas)
00452       , _zero_direction(copy._zero_direction)
00453       , _positive_rotation(copy._positive_rotation)
00454       , _zero_checker(copy._zero_checker)
00455     {
00456     }
00457 
00459 
00466     Compass& operator=(const Compass& copy)
00467     {
00468         _direction_count   = copy._direction_count;
00469         _neg_cot_thetas    = copy._neg_cot_thetas;
00470         _zero_direction    = copy._zero_direction;
00471         _positive_rotation = copy._positive_rotation;
00472         _zero_checker      = copy._zero_checker;
00473         return *this;
00474     }
00475 
00483     inline Direction getDirectionCount() const
00484     {
00485         return _direction_count;
00486     }
00487 
00581     Direction getDirection(const RealNumber dx, const RealNumber dy) const;
00582 
00603     DirectionChange
00604         getDirectionChange(
00605             const RealNumber dx1
00606           , const RealNumber dy1
00607           , const RealNumber dx2
00608           , const RealNumber dy2
00609         ) const;
00610 
00611  private:
00612     void _validate();
00613 
00614  public:
00621     inline void setDirectionCount(const Direction direction_count)
00622     {
00623         _direction_count = direction_count;
00624         _validate();
00625     }
00626 
00634     inline void setZeroDirection(const ZeroDirection& zero_direction)
00635     {
00636         _zero_direction = zero_direction;
00637     }
00638 
00646     inline void setPositiveRotation(const PositiveRotation& positive_rotation)
00647     {
00648         _positive_rotation = positive_rotation;
00649     }
00650 };
00651 
00652 template <
00653     typename Direction
00654   , typename DirectionChange
00655   , typename PiConstant
00656   , typename TangentFunction
00657 >
00658 const TangentFunction
00659     Compass<
00660         Direction
00661       , DirectionChange
00662       , PiConstant
00663       , TangentFunction
00664     >::_tan
00665       = TangentFunction();
00666 
00667 #ifndef SGDK_DOX
00668 template <
00669     typename Direction
00670   , typename DirectionChange
00671   , typename PiConstant
00672   , typename TangentFunction
00673 >
00674 #endif  /* SGDK_DOX */
00675 Direction
00676     Compass<
00677 #ifndef SGDK_DOX
00678         Direction
00679       , DirectionChange
00680       , PiConstant
00681       , TangentFunction
00682 #endif  /* SGDK_DOX */
00683     >::getDirection(
00684         const RealNumber dx
00685       , const RealNumber dy
00686     ) const
00687 {
00688     if (_direction_count < 2)
00689     {
00690         return 0;
00691     }
00692 
00693     if (_zero_direction.isParallelToYAxis())
00694     {
00695         if (_zero_checker.isZero(dx))
00696         {
00697             if ((dy < 0) == _zero_direction.isPositive())
00698             {
00699                 if (_direction_count & 1)
00700                 {
00701                     if (
00702                         (dx < 0)
00703                      == (
00704                             _zero_direction.isPositive()
00705                          == _positive_rotation.isClockwise()
00706                         )
00707                     )
00708                     {
00709                         return (_direction_count + 1) >> 1;
00710                     }
00711                     else
00712                     {
00713                         return (_direction_count - 1) >> 1;
00714                     }
00715                 }
00716                 else
00717                 {
00718                     return _neg_cot_thetas.size();
00719                 }
00720             }
00721             else
00722             {
00723                 return 0;
00724             }
00725         }
00726     }
00727     else
00728     {
00729         if (_zero_checker.isZero(dy))
00730         {
00731             if ((dx < 0) == _zero_direction.isPositive())
00732             {
00733                 if (_direction_count & 1)
00734                 {
00735                     if (
00736                         (dy < 0)
00737                      == (
00738                             _zero_direction.isPositive()
00739                          == _positive_rotation.isClockwise()
00740                         )
00741                     )
00742                     {
00743                         return (_direction_count - 1) >> 1;
00744                     }
00745                     else
00746                     {
00747                         return (_direction_count + 1) >> 1;
00748                     }
00749                 }
00750                 else
00751                 {
00752                     return _neg_cot_thetas.size();
00753                 }
00754             }
00755             else
00756             {
00757                 return 0;
00758             }
00759         }
00760     }
00761 
00762     const RealNumber
00763         neg_cot_theta
00764           = _zero_direction.isParallelToYAxis()
00765           ? (
00766                 (
00767                     (
00768                         _positive_rotation.isClockwise()
00769                      == _zero_direction.isPositive()
00770                     )
00771                   ? dy
00772                   : -dy
00773                 )
00774               / dx
00775             )
00776           : (
00777                 (
00778                     (
00779                         _positive_rotation.isClockwise()
00780                      != _zero_direction.isPositive()
00781                     )
00782                   ? dx
00783                   : -dx
00784                 )
00785               / dy
00786             );
00787 
00788     if (_neg_cot_thetas[0] < neg_cot_theta)
00789     {
00790         if (
00791             _zero_direction.isParallelToYAxis()
00792          && ((dy < 0) == _zero_direction.isPositive())
00793          || _zero_direction.isParallelToXAxis()
00794          && ((dx < 0) == _zero_direction.isPositive())
00795         )
00796         {
00797             if (_direction_count & 1)
00798             {
00799                 if (_zero_direction.isPositive())
00800                 {
00801                     return (_direction_count + 1) >> 1;
00802                 }
00803                 else
00804                 {
00805                     return (_direction_count - 1) >> 1;
00806                 }
00807             }
00808             else
00809             {
00810                 return _neg_cot_thetas.size();
00811             }
00812         }
00813         else
00814         {
00815             return 0;
00816         }
00817     }
00818 
00819     Direction low  = 0;
00820     Direction high = _neg_cot_thetas.size();
00821     Direction mid  = ((low + high) & ~1) >> 1;
00822 
00823     while (low < high - 1)
00824     {
00825         if (_neg_cot_thetas[mid] < neg_cot_theta)
00826         {
00827                 high = mid;
00828         }
00829         else
00830         {
00831            low = mid;
00832         }
00833 
00834         mid = ((low + high) & ~1) >> 1;
00835     }
00836 
00837     // We could use mid the rest of the way, at the cost of readability.
00838     Direction direction = mid;
00839 
00840     if (neg_cot_theta < _neg_cot_thetas[direction])
00841     {
00842         ++direction;
00843     }
00844 
00845     if (_direction_count & 1)
00846     {
00847         if (direction & 1)
00848         {
00849             ++direction;
00850             direction >>= 1;
00851 
00852             if (
00853                 _zero_direction.isParallelToYAxis()
00854              && ((dx < 0) == _positive_rotation.isClockwise())
00855              || _zero_direction.isParallelToXAxis()
00856              && ((dy < 0) == _positive_rotation.isCounterclockwise())
00857             )
00858             {
00859                 direction += (_direction_count - 1) >> 1;
00860             }
00861         }
00862         else
00863         {
00864             direction >>= 1;
00865 
00866             if (
00867                 _zero_direction.isParallelToYAxis()
00868              && ((dx < 0) == _positive_rotation.isClockwise())
00869              || _zero_direction.isParallelToXAxis()
00870              && ((dy < 0) == _positive_rotation.isCounterclockwise())
00871             )
00872             {
00873                 direction += (_direction_count + 1) >> 1;
00874 
00875                 if (direction == _direction_count)
00876                 {
00877                     direction = 0;
00878                 }
00879             }
00880         }
00881     }
00882     else
00883     {
00884         if (
00885             _zero_direction.isParallelToYAxis()
00886          && ((dx < 0) == _positive_rotation.isClockwise())
00887          || _zero_direction.isParallelToXAxis()
00888          && ((dy < 0) == _positive_rotation.isCounterclockwise())
00889         )
00890         {
00891             direction += _neg_cot_thetas.size();
00892 
00893             if (direction == _direction_count)
00894             {
00895                 direction = 0;
00896             }
00897         }
00898     }
00899 
00900     if (direction && !_zero_direction.isPositive())
00901     {
00902         direction = _direction_count - direction;
00903     }
00904 
00905     return direction;
00906 }
00907 
00908 #ifndef SGDK_DOX
00909 template <
00910     typename Direction
00911   , typename DirectionChange
00912   , typename PiConstant
00913   , typename TangentFunction
00914 >
00915 #endif  /* SGDK_DOX */
00916 DirectionChange
00917     Compass<
00918 #ifndef SGDK_DOX
00919         Direction
00920       , DirectionChange
00921       , PiConstant
00922       , TangentFunction
00923 #endif  /* SGDK_DOX */
00924     >::getDirectionChange(
00925         const RealNumber dx1
00926       , const RealNumber dy1
00927       , const RealNumber dx2
00928       , const RealNumber dy2
00929     ) const
00930 {
00931     const DirectionChange
00932         back_dir1
00933           = _direction_count >> 1;
00934     const DirectionChange
00935         back_dir2
00936           = back_dir1 - _direction_count;
00937     const DirectionChange
00938         yaw
00939           = _zero_direction.isParallelToYAxis()
00940           ? (getDirection(dx2, dy2) - getDirection(dx1, dy1))
00941           : (getDirection(dy2, dx2) - getDirection(dy1, dx1));
00942 
00943     return
00944         (back_dir1 < yaw)
00945       ? (yaw - _direction_count)
00946       : (back_dir2 < yaw)
00947       ? yaw
00948       : (yaw + _direction_count);
00949 }
00950 
00951 #ifndef SGDK_DOX
00952 template <
00953     typename Direction
00954   , typename DirectionChange
00955   , typename PiConstant
00956   , typename TangentFunction
00957 >
00958 #endif  /* SGDK_DOX */
00959 void
00960     Compass<
00961 #ifndef SGDK_DOX
00962         Direction
00963       , DirectionChange
00964       , PiConstant
00965       , TangentFunction
00966 #endif  /* SGDK_DOX */
00967     >::_validate()
00968 {
00969     if (_direction_count < 2)
00970     {
00971         return;
00972     }
00973     else if (_direction_count & 1)
00974     {
00975         Direction direction = _direction_count - 1;
00976         const Direction max_direction = direction << 1;
00977         DirectionChange factor = 2 - _direction_count;
00978 
00979         _neg_cot_thetas.resize(direction);
00980 
00981         while (direction > 0)
00982         {
00983             _neg_cot_thetas[--direction]
00984               = _tan(factor * PiConstant() / max_direction);
00985             factor += 2;
00986         }
00987     }
00988     else
00989     {
00990         const Direction neg_cot_thetas_size = _direction_count >> 1;
00991 
00992         _neg_cot_thetas.resize(neg_cot_thetas_size);
00993 
00994         if (neg_cot_thetas_size & 1)
00995         {
00996             DirectionChange factor = -((neg_cot_thetas_size + 1) >> 1);
00997             Direction direction = neg_cot_thetas_size;
00998 
00999             while (direction > 0)
01000             {
01001                 _neg_cot_thetas[--direction]
01002                   = _tan(++factor * PiConstant() / neg_cot_thetas_size);
01003             }
01004         }
01005         else
01006         {
01007             Direction direction = neg_cot_thetas_size;
01008             DirectionChange factor = 1 - neg_cot_thetas_size;
01009 
01010             while (direction > 0)
01011             {
01012                 _neg_cot_thetas[--direction]
01013                   = _tan(factor * PiConstant() / _direction_count);
01014                 factor += 2;
01015             }
01016         }
01017     }
01018 }
01019 }  // namespace sgdk
01020 
01021 #endif  /* SGDK_MATH_GEOM_COMPASS_HPP */

Swiss GD Knife is hosted by SourceForge.net.