geometry: rework `Float::angle_lerp` in the style of `Float::lerp`
[nit.git] / lib / geometry / angles.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Angle related service using `Float` to represent an angle in radians
16 module angles
17
18 import points_and_lines
19
20 redef class Point[N]
21 # Arctangent function using the difference between `self` and `other` as trigonometric ratio
22 #
23 # Behave similarly to the toplevel `atan2` as it returns the angle in the appropriate quadrant.
24 #
25 # ~~~
26 # var p0 = new Point[Float](0.0, 0.0)
27 # var p1 = new Point[Float](1.0, 1.0)
28 # assert p0.atan2(p1).is_approx(0.25*pi, 0.0001)
29 # ~~~
30 fun atan2(other: Point[N]): Float
31 do
32 var dx = other.x.to_f - x.to_f
33 var dy = other.y.to_f - y.to_f
34 return sys.atan2(dy.to_f, dx.to_f)
35 end
36 end
37
38 redef universal Float
39 # Normalize the `self` angle in radians to be within `[-pi .. pi[`
40 #
41 # ~~~
42 # assert (1.5*pi).angle_normalize.is_approx(-0.5*pi, 0.0001)
43 # assert 8.0.angle_normalize.is_approx(1.7168, 0.0001)
44 # assert (-1.0).angle_normalize == -1.0
45 # ~~~
46 fun angle_normalize: Float
47 do
48 var s = self
49 while s < -pi do s += 2.0*pi
50 while s >= pi do s -= 2.0*pi
51 return s
52 end
53
54 # Linear interpolation of between the angles `a` and `b`, in radians
55 #
56 # The result is normalized with `angle_normalize`.
57 #
58 # ~~~
59 # assert 0.5.angle_lerp(0.0, pi).is_approx(0.5*pi, 0.0001)
60 # assert 8.5.angle_lerp(0.0, pi).is_approx(0.5*pi, 0.0001)
61 # assert 7.5.angle_lerp(0.0, pi).is_approx(-0.5*pi, 0.0001)
62 # assert 0.5.angle_lerp(0.2, 2.0*pi-0.1).is_approx(0.05, 0.0001)
63 # ~~~
64 fun angle_lerp(a, b: Float): Float
65 do
66 var d = b - a
67 while d > pi do d -= 2.0*pi
68 while d < -pi do d += 2.0*pi
69 return (a + d*self).angle_normalize
70 end
71 end