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 .