Chris@102
|
1 // Boost.Geometry (aka GGL, Generic Geometry Library)
|
Chris@102
|
2
|
Chris@102
|
3 // Copyright (c) 2013 Barend Gehrels, Amsterdam, the Netherlands.
|
Chris@102
|
4
|
Chris@102
|
5 // Use, modification and distribution is subject to the Boost Software License,
|
Chris@102
|
6 // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
|
Chris@102
|
7 // http://www.boost.org/LICENSE_1_0.txt)
|
Chris@102
|
8
|
Chris@102
|
9 #ifndef BOOST_GEOMETRY_POLICIES_ROBUSTNESS_SEGMENT_RATIO_HPP
|
Chris@102
|
10 #define BOOST_GEOMETRY_POLICIES_ROBUSTNESS_SEGMENT_RATIO_HPP
|
Chris@102
|
11
|
Chris@102
|
12 #include <boost/assert.hpp>
|
Chris@102
|
13 #include <boost/config.hpp>
|
Chris@102
|
14 #include <boost/rational.hpp>
|
Chris@102
|
15
|
Chris@102
|
16 #include <boost/geometry/util/math.hpp>
|
Chris@102
|
17 #include <boost/geometry/util/promote_floating_point.hpp>
|
Chris@102
|
18
|
Chris@102
|
19 namespace boost { namespace geometry
|
Chris@102
|
20 {
|
Chris@102
|
21
|
Chris@102
|
22
|
Chris@102
|
23 namespace detail { namespace segment_ratio
|
Chris@102
|
24 {
|
Chris@102
|
25
|
Chris@102
|
26 template
|
Chris@102
|
27 <
|
Chris@102
|
28 typename Type,
|
Chris@102
|
29 bool IsIntegral = boost::is_integral<Type>::type::value
|
Chris@102
|
30 >
|
Chris@102
|
31 struct less {};
|
Chris@102
|
32
|
Chris@102
|
33 template <typename Type>
|
Chris@102
|
34 struct less<Type, true>
|
Chris@102
|
35 {
|
Chris@102
|
36 template <typename Ratio>
|
Chris@102
|
37 static inline bool apply(Ratio const& lhs, Ratio const& rhs)
|
Chris@102
|
38 {
|
Chris@102
|
39 return boost::rational<Type>(lhs.numerator(), lhs.denominator())
|
Chris@102
|
40 < boost::rational<Type>(rhs.numerator(), rhs.denominator());
|
Chris@102
|
41 }
|
Chris@102
|
42 };
|
Chris@102
|
43
|
Chris@102
|
44 template <typename Type>
|
Chris@102
|
45 struct less<Type, false>
|
Chris@102
|
46 {
|
Chris@102
|
47 template <typename Ratio>
|
Chris@102
|
48 static inline bool apply(Ratio const& lhs, Ratio const& rhs)
|
Chris@102
|
49 {
|
Chris@102
|
50 BOOST_ASSERT(lhs.denominator() != 0);
|
Chris@102
|
51 BOOST_ASSERT(rhs.denominator() != 0);
|
Chris@102
|
52 return lhs.numerator() * rhs.denominator()
|
Chris@102
|
53 < rhs.numerator() * lhs.denominator();
|
Chris@102
|
54 }
|
Chris@102
|
55 };
|
Chris@102
|
56
|
Chris@102
|
57 template
|
Chris@102
|
58 <
|
Chris@102
|
59 typename Type,
|
Chris@102
|
60 bool IsIntegral = boost::is_integral<Type>::type::value
|
Chris@102
|
61 >
|
Chris@102
|
62 struct equal {};
|
Chris@102
|
63
|
Chris@102
|
64 template <typename Type>
|
Chris@102
|
65 struct equal<Type, true>
|
Chris@102
|
66 {
|
Chris@102
|
67 template <typename Ratio>
|
Chris@102
|
68 static inline bool apply(Ratio const& lhs, Ratio const& rhs)
|
Chris@102
|
69 {
|
Chris@102
|
70 return boost::rational<Type>(lhs.numerator(), lhs.denominator())
|
Chris@102
|
71 == boost::rational<Type>(rhs.numerator(), rhs.denominator());
|
Chris@102
|
72 }
|
Chris@102
|
73 };
|
Chris@102
|
74
|
Chris@102
|
75 template <typename Type>
|
Chris@102
|
76 struct equal<Type, false>
|
Chris@102
|
77 {
|
Chris@102
|
78 template <typename Ratio>
|
Chris@102
|
79 static inline bool apply(Ratio const& lhs, Ratio const& rhs)
|
Chris@102
|
80 {
|
Chris@102
|
81 BOOST_ASSERT(lhs.denominator() != 0);
|
Chris@102
|
82 BOOST_ASSERT(rhs.denominator() != 0);
|
Chris@102
|
83 return geometry::math::equals
|
Chris@102
|
84 (
|
Chris@102
|
85 lhs.numerator() * rhs.denominator(),
|
Chris@102
|
86 rhs.numerator() * lhs.denominator()
|
Chris@102
|
87 );
|
Chris@102
|
88 }
|
Chris@102
|
89 };
|
Chris@102
|
90
|
Chris@102
|
91 }}
|
Chris@102
|
92
|
Chris@102
|
93 //! Small class to keep a ratio (e.g. 1/4)
|
Chris@102
|
94 //! Main purpose is intersections and checking on 0, 1, and smaller/larger
|
Chris@102
|
95 //! The prototype used Boost.Rational. However, we also want to store FP ratios,
|
Chris@102
|
96 //! (so numerator/denominator both in float)
|
Chris@102
|
97 //! and Boost.Rational starts with GCD which we prefer to avoid if not necessary
|
Chris@102
|
98 //! On a segment means: this ratio is between 0 and 1 (both inclusive)
|
Chris@102
|
99 //!
|
Chris@102
|
100 template <typename Type>
|
Chris@102
|
101 class segment_ratio
|
Chris@102
|
102 {
|
Chris@102
|
103 public :
|
Chris@102
|
104 typedef Type numeric_type;
|
Chris@102
|
105
|
Chris@102
|
106 // Type-alias for the type itself
|
Chris@102
|
107 typedef segment_ratio<Type> thistype;
|
Chris@102
|
108
|
Chris@102
|
109 inline segment_ratio()
|
Chris@102
|
110 : m_numerator(0)
|
Chris@102
|
111 , m_denominator(1)
|
Chris@102
|
112 , m_approximation(0)
|
Chris@102
|
113 {}
|
Chris@102
|
114
|
Chris@102
|
115 inline segment_ratio(const Type& nominator, const Type& denominator)
|
Chris@102
|
116 : m_numerator(nominator)
|
Chris@102
|
117 , m_denominator(denominator)
|
Chris@102
|
118 {
|
Chris@102
|
119 initialize();
|
Chris@102
|
120 }
|
Chris@102
|
121
|
Chris@102
|
122 inline Type const& numerator() const { return m_numerator; }
|
Chris@102
|
123 inline Type const& denominator() const { return m_denominator; }
|
Chris@102
|
124
|
Chris@102
|
125 inline void assign(const Type& nominator, const Type& denominator)
|
Chris@102
|
126 {
|
Chris@102
|
127 m_numerator = nominator;
|
Chris@102
|
128 m_denominator = denominator;
|
Chris@102
|
129 initialize();
|
Chris@102
|
130 }
|
Chris@102
|
131
|
Chris@102
|
132 inline void initialize()
|
Chris@102
|
133 {
|
Chris@102
|
134 // Minimal normalization
|
Chris@102
|
135 // 1/-4 => -1/4, -1/-4 => 1/4
|
Chris@102
|
136 if (m_denominator < 0)
|
Chris@102
|
137 {
|
Chris@102
|
138 m_numerator = -m_numerator;
|
Chris@102
|
139 m_denominator = -m_denominator;
|
Chris@102
|
140 }
|
Chris@102
|
141
|
Chris@102
|
142 typedef typename promote_floating_point<Type>::type num_type;
|
Chris@102
|
143 static const num_type scale = 1000000.0;
|
Chris@102
|
144 m_approximation =
|
Chris@102
|
145 m_denominator == 0 ? 0
|
Chris@102
|
146 : boost::numeric_cast<double>
|
Chris@102
|
147 (
|
Chris@102
|
148 boost::numeric_cast<num_type>(m_numerator) * scale
|
Chris@102
|
149 / boost::numeric_cast<num_type>(m_denominator)
|
Chris@102
|
150 );
|
Chris@102
|
151 }
|
Chris@102
|
152
|
Chris@102
|
153 inline bool is_zero() const { return math::equals(m_numerator, 0); }
|
Chris@102
|
154 inline bool is_one() const { return math::equals(m_numerator, m_denominator); }
|
Chris@102
|
155 inline bool on_segment() const
|
Chris@102
|
156 {
|
Chris@102
|
157 // e.g. 0/4 or 4/4 or 2/4
|
Chris@102
|
158 return m_numerator >= 0 && m_numerator <= m_denominator;
|
Chris@102
|
159 }
|
Chris@102
|
160 inline bool in_segment() const
|
Chris@102
|
161 {
|
Chris@102
|
162 // e.g. 1/4
|
Chris@102
|
163 return m_numerator > 0 && m_numerator < m_denominator;
|
Chris@102
|
164 }
|
Chris@102
|
165 inline bool on_end() const
|
Chris@102
|
166 {
|
Chris@102
|
167 // e.g. 0/4 or 4/4
|
Chris@102
|
168 return is_zero() || is_one();
|
Chris@102
|
169 }
|
Chris@102
|
170 inline bool left() const
|
Chris@102
|
171 {
|
Chris@102
|
172 // e.g. -1/4
|
Chris@102
|
173 return m_numerator < 0;
|
Chris@102
|
174 }
|
Chris@102
|
175 inline bool right() const
|
Chris@102
|
176 {
|
Chris@102
|
177 // e.g. 5/4
|
Chris@102
|
178 return m_numerator > m_denominator;
|
Chris@102
|
179 }
|
Chris@102
|
180
|
Chris@102
|
181 inline bool close_to(thistype const& other) const
|
Chris@102
|
182 {
|
Chris@102
|
183 return geometry::math::abs(m_approximation - other.m_approximation) < 2;
|
Chris@102
|
184 }
|
Chris@102
|
185
|
Chris@102
|
186 inline bool operator< (thistype const& other) const
|
Chris@102
|
187 {
|
Chris@102
|
188 return close_to(other)
|
Chris@102
|
189 ? detail::segment_ratio::less<Type>::apply(*this, other)
|
Chris@102
|
190 : m_approximation < other.m_approximation;
|
Chris@102
|
191 }
|
Chris@102
|
192
|
Chris@102
|
193 inline bool operator== (thistype const& other) const
|
Chris@102
|
194 {
|
Chris@102
|
195 return close_to(other)
|
Chris@102
|
196 && detail::segment_ratio::equal<Type>::apply(*this, other);
|
Chris@102
|
197 }
|
Chris@102
|
198
|
Chris@102
|
199 static inline thistype zero()
|
Chris@102
|
200 {
|
Chris@102
|
201 static thistype result(0, 1);
|
Chris@102
|
202 return result;
|
Chris@102
|
203 }
|
Chris@102
|
204
|
Chris@102
|
205 static inline thistype one()
|
Chris@102
|
206 {
|
Chris@102
|
207 static thistype result(1, 1);
|
Chris@102
|
208 return result;
|
Chris@102
|
209 }
|
Chris@102
|
210
|
Chris@102
|
211 #if defined(BOOST_GEOMETRY_DEFINE_STREAM_OPERATOR_SEGMENT_RATIO)
|
Chris@102
|
212 friend std::ostream& operator<<(std::ostream &os, segment_ratio const& ratio)
|
Chris@102
|
213 {
|
Chris@102
|
214 os << ratio.m_numerator << "/" << ratio.m_denominator
|
Chris@102
|
215 << " (" << (static_cast<double>(ratio.m_numerator)
|
Chris@102
|
216 / static_cast<double>(ratio.m_denominator))
|
Chris@102
|
217 << ")";
|
Chris@102
|
218 return os;
|
Chris@102
|
219 }
|
Chris@102
|
220 #endif
|
Chris@102
|
221
|
Chris@102
|
222
|
Chris@102
|
223
|
Chris@102
|
224 private :
|
Chris@102
|
225 Type m_numerator;
|
Chris@102
|
226 Type m_denominator;
|
Chris@102
|
227
|
Chris@102
|
228 // Contains ratio on scale 0..1000000 (for 0..1)
|
Chris@102
|
229 // This is an approximation for fast and rough comparisons
|
Chris@102
|
230 // Boost.Rational is used if the approximations are close.
|
Chris@102
|
231 // Reason: performance, Boost.Rational does a GCD by default and also the
|
Chris@102
|
232 // comparisons contain while-loops.
|
Chris@102
|
233 double m_approximation;
|
Chris@102
|
234 };
|
Chris@102
|
235
|
Chris@102
|
236
|
Chris@102
|
237 }} // namespace boost::geometry
|
Chris@102
|
238
|
Chris@102
|
239 #endif // BOOST_GEOMETRY_POLICIES_ROBUSTNESS_SEGMENT_RATIO_HPP
|