File Coverage

blib/lib/Math/VectorXYZ.pm
Criterion Covered Total %
statement 57 62 91.9
branch 5 10 50.0
condition 2 6 33.3
subroutine 21 21 100.0
pod 0 14 0.0
total 85 113 75.2


line stmt bran cond sub pod time code
1             package Math::VectorXYZ;
2              
3             our $VERSION = '1.01';
4              
5 3     3   53069 use 5.006;
  3         13  
6 3     3   12 use strict;
  3         4  
  3         65  
7 3     3   11 use warnings;
  3         6  
  3         67  
8              
9 3     3   18 use Carp;
  3         5  
  3         154  
10 3     3   1243 use Math::Trig;
  3         36525  
  3         335  
11              
12 3     3   17 use Exporter 'import';
  3         5  
  3         170  
13             our @EXPORT = ('Vec');
14              
15             use overload (
16 3         12 '+' => 'vec_add',
17             '-' => 'vec_subtract',
18             '*' => 'vec_scalar_mult',
19             '/' => 'vec_scalar_div',
20             'x' => 'vec_cross',
21             '.' => 'vec_dot',
22              
23             '==' => 'vec_equality',
24             'eq' => 'vec_equality',
25             q/""/ => 'as_string',
26 3     3   23 );
  3         6  
27              
28             #-------------- Additional methods available beyond overloaded operators above are: -----------------
29             #
30             # $vec->mag Returns vector magnitude (scalar)
31             # $vec->uvec Returns a new unit vector in same direction
32             # $vec->proj( $vec2 ) Returns the projection of $vec onto $vec2 (scalar)
33             # $vec->angle( $vec2 ) Returns the angle between two vectors (scalar)
34             #
35             #----------------------------------------------------------------------------------------------------
36              
37              
38              
39             #----------------------------------------- object constructors ---------------------------------------
40             #
41             # Instructions: Provide a list of three numbers (x,y,z) to the constructor
42             #
43             #----------------------------------------------------------------------------------------------------
44              
45             sub new {
46              
47 1     1 0 5 my $class = shift;
48            
49 1 50       3 if ( @_ != 3 ) {
50 0         0 croak '*** Error; syntax is "$vec = VectorXYZ->new(x,y,z)" ***';
51             }
52              
53 1         3 return bless [ @_ ], $class;
54             }
55              
56             sub Vec {
57              
58 13 50   13 0 686 if ( @_ != 3 ) {
59 0         0 croak '*** Error; syntax is "$vec = Vec(x,y,z)" ***';
60             }
61              
62 13         47 return bless [ @_ ], __PACKAGE__;
63             }
64              
65              
66             #----------------------------------------------------------------------------------------------------
67             #-------------------------------------- vector subs returning a vector ------------------------------
68             #----------------------------------------------------------------------------------------------------
69              
70             sub vec_add {
71 4     4 0 79 my ($a, $b) = @_;
72 4         27 return bless [ $a->[0] + $b->[0], $a->[1] + $b->[1], $a->[2] + $b->[2] ], ref($a);
73             }
74              
75             sub vec_subtract {
76 4     4 0 17 my ($a, $b) = @_;
77 4         22 return bless [ $a->[0] - $b->[0], $a->[1] - $b->[1], $a->[2] - $b->[2] ], ref($a);
78             }
79              
80             sub vec_scalar_mult { # vec_a, const
81 6     6 0 15 my ($a, $b, $swap) = @_;
82            
83             #Note: overload ('*' => 'vec_scalar_mult') magically swaps the arguments $vec * const --or-- const * $vec
84             #so that the vector object is always the first argument "$a"
85 6         28 return bless [ $a->[0]*$b, $a->[1]*$b, $a->[2]*$b ], ref($a);
86             }
87              
88             sub vec_scalar_div { # vec_a, const
89 6     6 0 13 my ($a, $b, $swap) = @_;
90            
91             #Note: overload ('/' => 'vec_scalar_div') magically swaps the arguments $vec / const --or-- const / $vec
92             #so that the vector object is always the first argument "$a".
93 6         30 return bless [ $a->[0] / $b, $a->[1] / $b, $a->[2] / $b ], ref($a);
94             }
95              
96             sub vec_cross { # a x b per "Advanced Engineering Mathematics", Kreyszig, 7th ed.
97              
98 2     2 0 4 my ($a, $b) = @_;
99              
100 2         11 my $res = [
101            
102             $a->[1]*$b->[2] - $a->[2]*$b->[1], #i
103              
104             $a->[2]*$b->[0] - $a->[0]*$b->[2], #j
105              
106             $a->[0]*$b->[1] - $a->[1]*$b->[0], #k
107            
108             ];
109              
110 2         9 return bless $res; #cross product result is always 3d, so don't bless into ref($a)
111             }
112              
113             sub uvec {
114 2     2 0 5 my $self = shift;
115 2         6 my $u_vec = ( $self / mag($self) );
116 2         8 return bless $u_vec, ref($self);
117             }
118              
119              
120             #----------------------------------------------------------------------------------------------------
121             #-------------------------------------- vector subs returning a scalar ------------------------------
122             #----------------------------------------------------------------------------------------------------
123             sub vec_dot {
124 16     16 0 24 my ($a, $b) = @_;
125 16         60 return $a->[0]*$b->[0] + $a->[1]*$b->[1] + $a->[2]*$b->[2]; #scalar value
126             }
127              
128             sub mag {
129 10     10 0 14 my $self = shift;
130 10         14 return sqrt($self.$self);
131             }
132              
133             sub proj {
134 2     2 0 6 my ($self, $b) = @_;
135              
136 2 50       13 unless ( $b->isa(__PACKAGE__) ) {
137 0         0 croak "Argument is not a vector object";
138             }
139              
140 2         4 my $p = ($self.$b) / mag($b);
141 2         17 return $p;
142             }
143              
144             sub angle {
145 2     2 0 6 my ($self, $b) = @_;
146              
147 2 50       10 unless ( $b->isa(__PACKAGE__) ) {
148 0         0 croak "Argument is not a vector object";
149             }
150              
151 2         5 my $cos_theta = ($self.$b) / ( mag($self) * mag($b) );
152 2         9 my $theta = acos($cos_theta);
153 2         27 return rad2deg($theta);
154             }
155              
156             my $tol = 1e-5; #floating point equality tolerance for testing
157             sub vec_equality {
158 20     20 0 3584 my ($a, $b) = @_;
159              
160 20 50 33     141 if (abs($a->[0] - $b->[0]) < $tol and
      33        
161             abs($a->[1] - $b->[1]) < $tol and
162             abs($a->[2] - $b->[2]) < $tol)
163 20         185 { return 1 }
164              
165             else
166 0         0 { return 0 }
167             }
168              
169             sub as_string {
170 1     1 0 426 my $self = shift;
171 1         6 return "<" . join(",", @$self) . ">"; #
172             }
173              
174             1;
175              
176             __END__