# # An object oriented module for working with vectors. # Note that 'scalar' refers to the mathmatical definition, not Perl's ;) # package Math::Vector; use strict; use Carp; use Math::Trig; use vars qw($VERSION); $VERSION = '0.01'; use overload "=" => \&assign, "+=" => \&add_assign, "-=" => \&subtract_assign, "+" => \&add, "-" => \&subtract, "x" => \&cross_product, "." => \&dot_product, "*" => \&multiply_scalar, "/" => \÷_scalar, "*=" => \&multiply_scalar_assign, "/=" => \÷_scalar_assign; sub new { my ($package, %args) = @_; return bless { _i => $args{i} || 0, _j => $args{j} || 0, _k => $args{k} || 0, }, $package; } # $v_1 = $v_2; sub assign { my ($self, $other) = @_; carp ('Operation only permitted on Math::Vector objects') && return undef unless (ref $other eq 'Math::Vector'); $self->set_i($other->i); $self->set_j($other->j); $self->set_k($other->k); return $self; } # $v_1 += $v_2; sub add_assign { my ($self, $other) = @_; carp ('Operation only permitted on Math::Vector objects') && return undef unless (ref $other eq 'Math::Vector'); $self->set_i($other->i + $self->i); $self->set_j($other->j + $self->j); $self->set_k($other->k + $self->k); return $self; } # $v_1 -= $v_2; sub subtract_assign { my ($self, $other) = @_; carp ('Operation only permitted on Math::Vector objects') && return undef unless (ref $other eq 'Math::Vector'); $self->set_i($other->i - $self->i); $self->set_j($other->j - $self->j); $self->set_k($other->k - $self->k); return $self; } # $v_new = $v_1 + $v_2; # $v_new = $v_1->add($v_2); sub add { my ($self, $other) = @_; carp ('Operation only permitted on Math::Vector objects') && return undef unless (ref $other eq 'Math::Vector'); return new Math::Vector ( i => $self->i + $other->i, j => $self->j + $other->j, k => $self->k + $other->k, ); } # $v_new = $v_1 - $v_2; # $v_new = $v_1->subtract($v_2); sub subtract { my ($self, $other) = @_; carp ('Operation only permitted on Math::Vector objects') && return undef unless (ref $other eq 'Math::Vector'); return new Math::Vector ( i => $self->i - $other->i, j => $self->j - $other->j, k => $self->k - $other->k, ); } # $v *= $scalar; sub multiply_scalar_assign { my ($self, $scalar) = @_; $self->set_i($self->i * $scalar); $self->set_j($self->j * $scalar); $self->set_k($self->k * $scalar); return $self; } # $v /= $scalar; sub divide_scalar_assign { my ($self, $scalar) = @_; carp ('Cannot divide by zero') && return undef unless ($scalar); $self->set_i($self->i / $scalar); $self->set_j($self->j / $scalar); $self->set_k($self->k / $scalar); return $self; } # $v_new = $v_1 * $scalar; # $v_new = $v_1->multiply_scalar($scalar); sub multiply_scalar { my ($self, $scalar) = @_; return new Math::Vector ( i => $self->i * $scalar, j => $self->j * $scalar, k => $self->k * $scalar, ); } # $v_new = $v_1 / $scalar; # $v_new = $v_1->divide_scalar($scalar); sub divide_scalar { my ($self, $scalar) = @_; carp ('Cannot divide by zero') && return undef unless ($scalar); return new Math::Vector ( i => $self->i / $scalar, j => $self->j / $scalar, k => $self->k / $scalar, ); } # $v_new = $v_1 x $v_2; # $v_new = $v_1->cross_product($v_2); sub cross_product { my ($self, $other) = @_; carp ('Operation only permitted on Math::Vector objects') && return undef unless (ref $other eq 'Math::Vector'); return new Math::Vector ( i => $self->j * $other->k - $self->k * $other->j, j => $self->k * $other->i - $self->i * $other->k, z => $self->i * $other->j - $self->j * $other->i, ); } # $dot_product = $v_1 . $v_2; # $v_new = $v_1->dot_product($v_2); sub dot_product { my ($self, $other) = @_; carp ('Operation only permitted on Math::Vector objects') && return undef unless (ref $other eq 'Math::Vector'); return $self->i * $other->i + $self->j * $other->j + $self->k * $other->k; } # $magnitude = $v->magnitude(); sub magnitude { my $self = shift; return sqrt($self->i * $self->i + $self->j * $self->j + $self->k * $self->k); } # Converts the vector to its normal # $v->normalize(); sub normalize { my $self = shift; my $m = $self->magnitude; carp('Cannot normalize a zero vector') && return undef unless ($m); $self->set_i( $self->i / $m ); $self->set_j( $self->j / $m ); $self->set_k( $self->k / $m ); } # $v_normalized = $v->normal(); sub normal { my $self = shift; my $normal = new Math::Vector (i=>$self->i, j=>$self->j, k=>$self->k); $normal->normalize(); return $normal; } # $angle_in_radians = $v_1->angle($v_2); sub angle { my ($self, $other) = @_; carp ('Operation only permitted on Math::Vector objects') && return undef unless (ref $other eq 'Math::Vector'); my $self_m = $self->magnitude; my $other_m = $other->magnitude; carp('Vectors must be non-zero') && return undef unless ($self_m && $other_m); return (acos ( $self->dot_product($other) / ($self_m * $other_m) )); } sub orthogonal { my ($self, $other) = @_; carp ('Operation only permitted on Math::Vector objects') && return undef unless (ref $other eq 'Math::Vector'); carp('Vectors must be non-zero') && return undef unless ($self->magnitude && $other->magnitude); # v1 is perpendicular to v2 if (v1 . v2 == 0) return ($self->dot_product($other) ? 0 : 1); } sub i { return $_[0]->{_i}; } sub j { return $_[0]->{_j}; } sub k { return $_[0]->{_k}; } sub set_i { my $self = shift; $self->{_i} = shift || 0; } sub set_j { my $self = shift; $self->{_j} = shift || 0; } sub set_k { my $self = shift; $self->{_k} = shift || 0; } 1; __END__ =head1 NAME Math::Vector - Object oriented vectors =head1 SYNOPSIS use Math::Vector; my $vector = new Math::Vector (i => $i, j => $j, k => $k); =head1 DESCRIPTION C provides an object oriented approach to working with vectors. Vectors created with this module are three-dimentional, although two-dimentional vectors are effectively achieved by omitting the 'k' component (which simply sets it to zero). It is important to note that this module will carp() if operations involving division are attempted on zero values. A basic understanding of vectors is expected, and the programmer should check the magnitude of a vector before performing operations such as normalization or finding the angle. =head2 Conventions used in this document =over 10 The term 'scalar' is used in the mathmatical sense, and is a number representing the amount by which a vector is scaled. Vector objects will always begin with a 'v'. (Example: $v, $v_1, $v_new) =back =head1 OPERATORS C objects override many default operators. =item '=' $v_1 = $v_2; =item '+' $v_new = $v_1 + $v_2; =item '+=' $v_1 += $v_2 =item '-' $v_new = $v_1 - $v_2; =item '-=' $v_1 -= $v_2; =item 'x' $v_cross_product = $v_1 x $v_2; =item '.' $dot_product = $v_1 . $v_2; =item '*' $v_new = $v_1 * $scalar_multiple =item '*=' $v_1 *= $scalar_multiple; =item '/' $v_new = $v_1 / $scalar_multiple =item '/=' $v_1 /= $scalar_multiple; =head1 METHODS =head2 Creation =over 4 =item new Math::Vector (i => $i, j => $j, k => $k) Creates a new vector object with components ($i, $j, $k). =back =head2 Access =over 4 =item i Returns the value of the 'i' (first) component of the vector. =item j Returns the value of the 'j' (second) component of the vector. =item k Returns the value of the 'k' (third) component of the vector. =item ijk Returns a list of the three component values. This is included for convenience, and is the same as: ($v->i, $v->j, $v->k); =back =head2 Direct Modifications =over 4 =item set_i (I) Sets the value for the 'i' component of the vector. $v->set_i($new_i_value) =item set_j (J) Sets the value for the 'j' component of the vector. $v->set_j($new_j_value) =item set_k (K) Sets the value for the 'k' component of the vector. $v->set_k($new_k_value) =back =head2 Calculations =over 4 =item dot_product (VECTOR) Returns the dot product of two vectors. $dot_product = $v_1->dot_product( $v_2 ); # Note this is equivalent: $dot_product = $v_1 . $v_2; =item cross_product (VECTOR) Returns the cross product of two vectors as a vector object. Note: you cannot take the cross product of 2d vectors. $v_cross = $v_1->cross_product( $v_2 ); # Note this is equivalent: $v_cross = $v_1 x $v_2; =item magnitude Returns the magnitude of the vector. $magnitude = $v->magnitude; =item normalize Normalizes the vector. $v->normalize; =item normal Returns the normal of a vector as a new vector object. $v_normalized = $v->normal; # Note the following is similar, but does not preserve $v : $v->normalize; $v_normalized = $v; =item angle (VECTOR) Returns the angle in radians between two vectors. $angle = $v_1->angle( $v_2 ); =item orthogonal (VECTOR) Returns 1 if vectors are orthogonal (or perpendicular), 0 if they are not, and undef if either is a zero vector. $orthogonal = $v_1->orthogonal( $v_2 ); if ($orthogonal) { # $v_1 and $v_2 are perpendicular } elsif (defined $orthogonal) { # they are not } else { # you tried to test a zero vector } =back =head1 SEE ALSO Math::Trig, Carp =head1 BUGS Please email the author (Emark@sporkstorms.orgE) with any. =head1 FEEDBACK Feel free to email the author with any questions, comments, or requests. This module was initially written for personal use, and may be lacking some desired features. I (Mark) would be glad to add anything to it that you might need. =head1 AUTHOR Mark Stratman, Emark@sporkstorms.orgE =head1 COPYRIGHT Copyright (c) 2001, Mark Stratman. All Rights Reserved. This modules is free software. It may be used, redistributed and/or modified under the same terms as Perl itself. =cut #