File Coverage

blib/lib/Math/Trig.pm
Criterion Covered Total %
statement 70 79 88.6
branch 20 32 62.5
condition n/a
subroutine 22 27 81.4
pod 22 22 100.0
total 134 160 83.7


line stmt bran cond sub pod time code
1             #
2             # Trigonometric functions, mostly inherited from Math::Complex.
3             # -- Jarkko Hietaniemi, since April 1997
4             # -- Raphael Manfredi, September 1996 (indirectly: because of Math::Complex)
5             #
6              
7             package Math::Trig;
8              
9 1     1   30875 { use 5.006; }
  1         3  
  1         32  
10 1     1   5 use strict;
  1         2  
  1         32  
11              
12 1     1   635 use Math::Complex 1.59;
  1         39  
  1         273  
13 1     1   5 use Math::Complex qw(:trig :pi);
  1         2  
  1         1722  
14             require Exporter;
15              
16             our @ISA = qw(Exporter);
17              
18             our $VERSION = 1.23;
19              
20             my @angcnv = qw(rad2deg rad2grad
21             deg2rad deg2grad
22             grad2rad grad2deg);
23              
24             my @areal = qw(asin_real acos_real);
25              
26             our @EXPORT = (@{$Math::Complex::EXPORT_TAGS{'trig'}},
27             @angcnv, @areal);
28              
29             my @rdlcnv = qw(cartesian_to_cylindrical
30             cartesian_to_spherical
31             cylindrical_to_cartesian
32             cylindrical_to_spherical
33             spherical_to_cartesian
34             spherical_to_cylindrical);
35              
36             my @greatcircle = qw(
37             great_circle_distance
38             great_circle_direction
39             great_circle_bearing
40             great_circle_waypoint
41             great_circle_midpoint
42             great_circle_destination
43             );
44              
45             my @pi = qw(pi pi2 pi4 pip2 pip4);
46              
47             our @EXPORT_OK = (@rdlcnv, @greatcircle, @pi, 'Inf');
48              
49             # See e.g. the following pages:
50             # http://www.movable-type.co.uk/scripts/LatLong.html
51             # http://williams.best.vwh.net/avform.htm
52              
53             our %EXPORT_TAGS = ('radial' => [ @rdlcnv ],
54             'great_circle' => [ @greatcircle ],
55             'pi' => [ @pi ]);
56              
57             sub _DR () { pi2/360 }
58             sub _RD () { 360/pi2 }
59             sub _DG () { 400/360 }
60             sub _GD () { 360/400 }
61             sub _RG () { 400/pi2 }
62             sub _GR () { pi2/400 }
63              
64             #
65             # Truncating remainder.
66             #
67              
68             sub _remt ($$) {
69             # Oh yes, POSIX::fmod() would be faster. Possibly. If it is available.
70 36     36   371 $_[0] - $_[1] * int($_[0] / $_[1]);
71             }
72              
73             #
74             # Angle conversions.
75             #
76              
77 30     30 1 76 sub rad2rad($) { _remt($_[0], pi2) }
78              
79 6     6 1 12 sub deg2deg($) { _remt($_[0], 360) }
80              
81 0     0 1 0 sub grad2grad($) { _remt($_[0], 400) }
82              
83 8 100   8 1 947 sub rad2deg ($;$) { my $d = _RD * $_[0]; $_[1] ? $d : deg2deg($d) }
  8         28  
84              
85 19 100   19 1 10118 sub deg2rad ($;$) { my $d = _DR * $_[0]; $_[1] ? $d : rad2rad($d) }
  19         1500  
86              
87 0 0   0 1 0 sub grad2deg ($;$) { my $d = _GD * $_[0]; $_[1] ? $d : deg2deg($d) }
  0         0  
88              
89 0 0   0 1 0 sub deg2grad ($;$) { my $d = _DG * $_[0]; $_[1] ? $d : grad2grad($d) }
  0         0  
90              
91 0 0   0 1 0 sub rad2grad ($;$) { my $d = _RG * $_[0]; $_[1] ? $d : grad2grad($d) }
  0         0  
92              
93 0 0   0 1 0 sub grad2rad ($;$) { my $d = _GR * $_[0]; $_[1] ? $d : rad2rad($d) }
  0         0  
94              
95             #
96             # acos and asin functions which always return a real number
97             #
98              
99             sub acos_real {
100 32 100   32 1 864 return 0 if $_[0] >= 1;
101 26 100       191 return pi if $_[0] <= -1;
102 23         69 return acos($_[0]);
103             }
104              
105             sub asin_real {
106 11 100   11 1 58 return &pip2 if $_[0] >= 1;
107 9 100       48 return -&pip2 if $_[0] <= -1;
108 7         29 return asin($_[0]);
109             }
110              
111             sub cartesian_to_spherical {
112 4     4 1 1689 my ( $x, $y, $z ) = @_;
113              
114 4         17 my $rho = sqrt( $x * $x + $y * $y + $z * $z );
115              
116 4 50       14 return ( $rho,
117             atan2( $y, $x ),
118             $rho ? acos_real( $z / $rho ) : 0 );
119             }
120              
121             sub spherical_to_cartesian {
122 4     4 1 873 my ( $rho, $theta, $phi ) = @_;
123              
124 4         42 return ( $rho * cos( $theta ) * sin( $phi ),
125             $rho * sin( $theta ) * sin( $phi ),
126             $rho * cos( $phi ) );
127             }
128              
129             sub spherical_to_cylindrical {
130 2     2 1 798 my ( $x, $y, $z ) = spherical_to_cartesian( @_ );
131              
132 2         8 return ( sqrt( $x * $x + $y * $y ), $_[1], $z );
133             }
134              
135             sub cartesian_to_cylindrical {
136 2     2 1 1295 my ( $x, $y, $z ) = @_;
137              
138 2         14 return ( sqrt( $x * $x + $y * $y ), atan2( $y, $x ), $z );
139             }
140              
141             sub cylindrical_to_cartesian {
142 4     4 1 1513 my ( $rho, $theta, $z ) = @_;
143              
144 4         22 return ( $rho * cos( $theta ), $rho * sin( $theta ), $z );
145             }
146              
147             sub cylindrical_to_spherical {
148 2     2 1 805 return ( cartesian_to_spherical( cylindrical_to_cartesian( @_ ) ) );
149             }
150              
151             sub great_circle_distance {
152 15     15 1 3638 my ( $theta0, $phi0, $theta1, $phi1, $rho ) = @_;
153              
154 15 100       39 $rho = 1 unless defined $rho; # Default to the unit sphere.
155              
156 15         30 my $lat0 = pip2 - $phi0;
157 15         22 my $lat1 = pip2 - $phi1;
158              
159 15         74 return $rho *
160             acos_real( cos( $lat0 ) * cos( $lat1 ) * cos( $theta0 - $theta1 ) +
161             sin( $lat0 ) * sin( $lat1 ) );
162             }
163              
164             sub great_circle_direction {
165 12     12 1 2326 my ( $theta0, $phi0, $theta1, $phi1 ) = @_;
166              
167 12         19 my $lat0 = pip2 - $phi0;
168 12         14 my $lat1 = pip2 - $phi1;
169              
170 12         72 return rad2rad(pi2 -
171             atan2(sin($theta0-$theta1) * cos($lat1),
172             cos($lat0) * sin($lat1) -
173             sin($lat0) * cos($lat1) * cos($theta0-$theta1)));
174             }
175              
176             *great_circle_bearing = \&great_circle_direction;
177              
178             sub great_circle_waypoint {
179 6     6 1 2314 my ( $theta0, $phi0, $theta1, $phi1, $point ) = @_;
180              
181 6 50       16 $point = 0.5 unless defined $point;
182              
183 6         27 my $d = great_circle_distance( $theta0, $phi0, $theta1, $phi1 );
184              
185 6 50       20 return undef if $d == pi;
186              
187 6         9 my $sd = sin($d);
188              
189 6 50       14 return ($theta0, $phi0) if $sd == 0;
190              
191 6         13 my $A = sin((1 - $point) * $d) / $sd;
192 6         10 my $B = sin( $point * $d) / $sd;
193              
194 6         7 my $lat0 = pip2 - $phi0;
195 6         7 my $lat1 = pip2 - $phi1;
196              
197 6         16 my $x = $A * cos($lat0) * cos($theta0) + $B * cos($lat1) * cos($theta1);
198 6         16 my $y = $A * cos($lat0) * sin($theta0) + $B * cos($lat1) * sin($theta1);
199 6         10 my $z = $A * sin($lat0) + $B * sin($lat1);
200              
201 6         18 my $theta = atan2($y, $x);
202 6         11 my $phi = acos_real($z);
203              
204 6         19 return ($theta, $phi);
205             }
206              
207             sub great_circle_midpoint {
208 1     1 1 520 great_circle_waypoint(@_[0..3], 0.5);
209             }
210              
211             sub great_circle_destination {
212 4     4 1 775 my ( $theta0, $phi0, $dir0, $dst ) = @_;
213              
214 4         6 my $lat0 = pip2 - $phi0;
215              
216 4         16 my $phi1 = asin_real(sin($lat0)*cos($dst) +
217             cos($lat0)*sin($dst)*cos($dir0));
218              
219 4         26 my $theta1 = $theta0 + atan2(sin($dir0)*sin($dst)*cos($lat0),
220             cos($dst)-sin($lat0)*sin($phi1));
221              
222 4         11 my $dir1 = great_circle_bearing($theta1, $phi1, $theta0, $phi0) + pi;
223              
224 4 100       14 $dir1 -= pi2 if $dir1 > pi2;
225              
226 4         19 return ($theta1, $phi1, $dir1);
227             }
228              
229             1;
230              
231             __END__