``````

Manipulate 3D vectors

PhilipRBrenan@yahoo.com, 2004, Perl licence

Example t/vector.t

#_ Vector _____________________________________________________________
# Test 3d vectors
#______________________________________________________________________

use Math::Zap::Vector vector=>'v', units=>'u';
use Test::Simple tests=>7;

my (\$x, \$y, \$z) = u();

ok(!\$x                            == 1);
ok(2*\$x+3*\$y+4*\$z                 == v( 2,  3,   4));
ok(-\$x-\$y-\$z                      == v(-1, -1,  -1));
ok((2*\$x+3*\$y+4*\$z) + (-\$x-\$y-\$z) == v( 1,  2,   3));
ok((2*\$x+3*\$y+4*\$z) * (-\$x-\$y-\$z) == -9);
ok(\$x*2                           == v( 2,  0,   0));
ok(\$y/2                           == v( 0,  0.5, 0));

Manipulate 3 dimensional vectors via operators.

=cut

package Math::Zap::Vector;
\$VERSION=1.07;
use Math::Trig;
use Carp;
use constant debug => 0; # Debugging level

Create a vector from numbers

=cut

sub new(\$\$\$)
{return round(bless({x=>\$_[0], y=>\$_[1], z=>\$_[2]})) if debug;
bless {x=>\$_[0], y=>\$_[1], z=>\$_[2]};
}

sub vector(\$\$\$) {new(\$_[0], \$_[1], \$_[2])}

Unit vectors

=cut

sub units() {(\$x, \$y, \$z)}

Check its a vector

=cut

sub check(@)
{if (debug)
{for my \$v(@_)
{confess "\$v is not a vector" unless ref(\$v) eq __PACKAGE__;
}
}
return (@_)
}

Test its a vector

=cut

sub is(@)
{for my \$v(@_)
{return 0 unless ref(\$v) eq __PACKAGE__;
}
'vector';
}

Get/Set accuracy for comparisons

=cut

my \$accuracy = 1e-10;

sub accuracy
{return \$accuracy unless scalar(@_);
\$accuracy = shift();
}

Round: round to nearest integer if within accuracy of that integer

=cut

sub round(\$)
{unless (debug)
{return \$_[0];
}
else
{my (\$a) = @_;
for my \$k(keys(%\$a))
{my \$n = \$a->{\$k};
my \$N = int(\$n);
\$a->{\$k} = \$N if abs(\$n-\$N) < \$accuracy;
}
return \$a;
}
}

x,y,z components of vector

=cut

sub x(\$) {check(@_) if debug; \$_[0]->{x}}
sub y(\$) {check(@_) if debug; \$_[0]->{y}}
sub z(\$) {check(@_) if debug; \$_[0]->{z}}

\$x = new(1,0,0);
\$y = new(0,1,0);
\$z = new(0,0,1);

Create a vector from another vector

=cut

sub clone(\$)
{my (\$v) = check(@_); # Vectors
round bless {x=>\$v->x, y=>\$v->y, z=>\$v->z};
}

Length of a vector

=cut

sub length(\$)
{my (\$v) = check(@_[0..0]); # Vectors
sqrt(\$v->x**2+\$v->y**2+\$v->z**2);
}

Print vector

=cut

sub print(\$)
{my (\$v) = check(@_); # Vectors
my (\$x, \$y, \$z) = (\$v->x, \$v->y, \$v->z);

"vector(\$x, \$y, \$z)";
}

Normalize vector

=cut

sub norm(\$)
{my (\$v) = check(@_); # Vectors
\$v = \$v->clone;   # Copy vector
my \$l = \$v->length;

\$l > 0 or confess "Cannot normalize zero length vector \$v";

\$v->{x} /= \$l;
\$v->{y} /= \$l;
\$v->{z} /= \$l;
\$v;
}

Dot product

=cut

sub dot(\$\$)
{my (\$a, \$b) = check(@_); # Vectors
\$a->x*\$b->x+\$a->y*\$b->y+\$a->z*\$b->z;
}

Angle between two vectors

=cut

sub angle(\$\$)
{my (\$a, \$b) = check(@_); # Vectors
acos(\$a->norm->dot(\$b->norm));
}

Cross product

=cut

sub cross(\$\$)
{my (\$a, \$b) = check(@_); # Vectors

new
(((\$a->y * \$b->z) - (\$a->z * \$b->y)),
((\$a->z * \$b->x) - (\$a->x * \$b->z)),
((\$a->x * \$b->y) - (\$a->y * \$b->x))
);
}

=cut

{my (\$a, \$b) = check(@_); # Vectors
new(\$a->x+\$b->x, \$a->y+\$b->y, \$a->z+\$b->z);
}

Subtract vectors

=cut

sub subtract(\$\$)
{check(@_) if debug; # Vectors
new(\$_[0]->{x}-\$_[1]->{x}, \$_[0]->{y}-\$_[1]->{y}, \$_[0]->{z}-\$_[1]->{z});
}

Vector times a scalar

=cut

sub multiply(\$\$)
{my (\$a) = check(@_[0..0]); # Vector
my (\$b) =       @_[1..1];  # Scalar

confess "\$b is not a scalar" if ref(\$b);
new(\$a->x*\$b, \$a->y*\$b, \$a->z*\$b);
}

Vector divided by a non zero scalar

=cut

sub divide(\$\$)
{my (\$a) = check(@_[0..0]); # Vector
my (\$b) =       @_[1..1];  # Scalar

confess "\$b is not a scalar" if ref(\$b);
confess "\$b is zero"         if \$b == 0;
new(\$a->x/\$b, \$a->y/\$b, \$a->z/\$b);
}

Equals to within accuracy

=cut

sub equals(\$\$)
{my (\$a, \$b) = check(@_); # Vectors
abs(\$a->x-\$b->x) < \$accuracy and
abs(\$a->y-\$b->y) < \$accuracy and
abs(\$a->z-\$b->z) < \$accuracy;
}

=cut

'-'        => \&subtract3, # Subtract one vector from another
'*'        => \&multiply3, # Times by a scalar, or vector dot product
'/'        => \&divide3,   # Divide by a scalar
'x'        => \&cross3,    # BEWARE LOW PRIORITY! Cross product
'<'        => \&angle3,    # Angle in radians between two vectors
'>'        => \&angle3,    # Angle in radians between two vectors
'=='       => \&equals3,   # Equals
'""'       => \&print3,    # Print
'!'        => \&length,    # Length
'fallback' => FALSE;

=cut

{my (\$a, \$b) = @_;
}

Subtract operator.

=cut

sub subtract3
{return new(\$_[0]->{x}-\$_[1]->{x}, \$_[0]->{y}-\$_[1]->{y}, \$_[0]->{z}-\$_[1]->{z}) if ref(\$_[1]);
new(-\$_[0]->{x}, -\$_[0]->{y}, -\$_[0]->{z});
}

Multiply operator, see L</multiply>

=cut

sub multiply3
{my (\$a, \$b) = @_;
return \$a->dot     (\$b) if ref(\$b);
return \$a->multiply(\$b);
}

Divide operator, see L</divide>

=cut

sub divide3
{my (\$a, \$b, \$c) = @_;
return \$a->divide(\$b);
}

Cross operator, see L</cross>

=cut

sub cross3
{my (\$a, \$b, \$c) = @_;
return \$a->cross(\$b);
}

Angle operator, see L</angle>

=cut

sub angle3
{my (\$a, \$b, \$c) = @_;
return \$a->angle(\$b);
}

Equals operator, see L</equals>

=cut

sub equals3
{my (\$a, \$b, \$c) = @_;
return \$a->equals(\$b);
}

Print a vector, see L</print>

=cut

sub print3
{my (\$a) = @_;
return \$a->print;
}

Export L</vector>, L</units>, L</check>, L</is>

=cut

use Math::Zap::Exports qw(
vector (\$\$\$)
units  ()
check  (@)
is     (@)
);

#______________________________________________________________________
#______________________________________________________________________

1;

philiprbrenan@yahoo.com