Manipulate 2D vectors

Example t/vector2.t

#_ Vector _____________________________________________________________
# Test 2d vectors
#______________________________________________________________________

use Math::Zap::Vector2 vector2=>v, units=>u;
use Test::Simple tests=>7;

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

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

Manipulate 2D vectors

=cut

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

Create a vector from numbers

=cut

sub new(\$\$)
{return bless {x=>\$_[0], y=>\$_[1]} unless debug;
my (\$x, \$y) = @_;
round(bless({x=>\$x, y=>\$y}));
}

Create a vector from numbers - synonym for L</new>

=cut

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

Unit vectors

=cut

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

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

Check its a vector

=cut

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

Test its a vector

=cut

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

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 components of vector

=cut

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

Create a vector from another vector

=cut

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

Length of a vector

=cut

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

Print vector

=cut

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

"vector2(\$x, \$y)";
}

Normalize vector

=cut

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

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

new(\$v->x / \$l, \$v->y / \$l);
}

At right angles

=cut

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

Dot product

=cut

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

Angle between two vectors

=cut

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

=cut

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

Subtract vectors

=cut

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

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);
}

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);
}

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;
}

=cut

'-'        => \&subtract3, # Subtract one vector from another
'*'        => \&multiply3, # Times by a scalar, or vector dot product
'/'        => \&divide3,   # Divide by a scalar
'<'        => \&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
{#my (\$a, \$b, \$c) = @_;
#return \$a->subtract(\$b) if ref(\$b);
return new(\$_[0]->{x}-\$_[1]->{x}, \$_[0]->{y}-\$_[1]->{y}) if ref(\$_[1]);
new(-\$_[0]->{x}, -\$_[0]->{y});
}

Multiply operator.

=cut

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

Divide operator.

=cut

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

Angle between two vectors.

=cut

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

Equals operator.

=cut

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

Print a vector.

=cut

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

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

=cut

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

#_ Vector2 ____________________________________________________________
#______________________________________________________________________

1;

philiprbrenan@yahoo.com

philiprbrenan@yahoo.com, 2004