sourcecode
count0
#<code>
#
# 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<Math::Vector> 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<Math::Vector> 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 (E<lt>mark@sporkstorms.orgE<gt>) 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, E<lt>mark@sporkstorms.orgE<gt>
=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
#</CODE>
This is a module I was using for basic vector operations. I cleaned it up a bit and documented it for (possible) CPAN submission.
I'd be most grateful =) if anyone would be so kind as to provide feedback.
Some of the areas I was questionable about were:
<UL>
<LI>The namespace.. perhaps Physics::Vector might be more appropriage?
<li>The extensive operator overloading.. and more specifically, overloading '.' and 'x' with operations that are completely different than the default '.' and 'x' operators.
<li>Anything you'd care to comment on! =)
</uL>
Math
Mark Stratman ([count0])
<a href="mailto:mark@sporkstorms.org">mark@sporkstorms.org</a>