#!/usr/bin/perl -w

#############################################################################
# Angle - a angle coordinate in degress (0..359)
#
# Copyright (C) 1999-2000 by Tels. All rights reserved.
#############################################################################

package CoW::Angle;
use vars qw($VERSION);
$VERSION = 1.00;    # Current version of this package
require  5.005;     # requires this Perl version or later

use base CoW::Coord;
#@EXPORT_OK	= qw( );
#@EXPORT	= qw( );
use strict;
use vars qw($AUTOLOAD);

use overload 
	'+' => \&add,
	'-' => \&sub,
	'*' => \&scale,
	'""' => \&absolute,
	'+0' => \&absolute;

#############################################################################
# private, initialize self 

sub _init
  {
  my $self = shift;
  my $args = shift;

  $self->SUPER::_init($args);
  $self->SUPER::_default( {
   rel => '', val => 0, snap => 0,
   } );
  die "illegal rel field $self->{rel}" 
   unless $self->{rel} eq '' || $self->{rel} =~ /^((self|_parent)::|)[a-z]+/;
  $self->{val} = normalize( $self->{val} ) if $self->{rel} eq '';
  return $self;
  }

sub normalize
  {
  # make value between 0 and 359 if absolute value
  my $val = shift;
  $val -= 360 while $val >= 360;
  $val += 360 while $val < 0;
  return $val;
  }

#############################################################################
# public

sub absolute
  {
  my $self = shift;

  my $abs = $self->{val};
  if ($self->{rel} ne '')
    {
    $abs = 0 + $self->_rel() + $self->{val};    # works due to overload
    }
  $abs = $self->{min} if (defined $self->{min}) && ($abs < $self->{min});
  $abs = $self->{max} if (defined $self->{max}) && ($abs > $self->{max});
  return $abs;
  }
 
sub scale
  {
  my $self = shift;
  my $factor = shift || return $self;

  $self->{val} = normalize ($self->{val} * $factor);
  return $self;
  }

sub add
  {
  my $self = shift;
  my $ofs = shift || return $self;

  $self->{val} = normalize ($self->{val} + $ofs);
  return $self;
  }

sub sub
  {
  my $self = shift;
  my $ofs = shift || 0;

  $self->{val} = normalize ($self->{val} - $ofs);
  return $self;
  }

1; 

__END__

#############################################################################

=pod

=head1 NAME

CoW::Angle - an angle that can be absolute or relative to another Angle.

=head1 SYNOPSIS

	use CoW::Angle;

	$a1 = new CoW::Angle ( rel => '_parent::rx', val => 45 );
	$a2 = new CoW::Angle ( rel => '_parent::ry', val => -25 );

	print $a1->value();	# print angle absolute value based on rel

=head1 REQUIRES

perl5.005, overload, L<Item|Item>

=head1 EXPORTS

Exports nothing.

=head1 DESCRIPTION

This module defines an angle that can be relative to
another value. It overloads the stringify routine, so that you can use

	use CoW::Angle;

	$coord = new CoW::Angle ( rel => '', val => 45 );

	print $angle,"\n";		# prints "45\n";
	print $angle->absolute(),"\n";	# the same
	$simple = $angle->absolute();	# put 45 into simple

It also overloads add/sub, so that

	$angle += 90;
	print $angle,"\n";

works as expected, even with relative angles. 
L<Thing|CoW::Thing> uses this for rotating itself.

See also L<Item|Item>.

=head1 FIELDS

=over 2

=item rel

Specifies wether the L<value|val> is relative or absolut. If set to '',
the value is absolute, otherwise rel specifies the field where the value
is relativ to. Use '_parent:: to make it relative to the parent. Self means
the Thing the angle belongs to, not the Angle itself. Parent then means the
parent of the Thing the Angle belongs to. Examples:

	$angle->rel('_parent::rx');	# relative to parents width (aka to
					# $angle->{_parent}->{_parent}->{rx}
	$angle->rel('_self::ry);	# relative to own lenght (self is the
					# object the coord belongs to, aka 
					# $angle->{_parent}->{ry})
	$angle->rel('ry');		# dito as last example

=item val

The actual value.

=item snap

The actual value will always be snapped to the gridsize given by C<snap>. If
snap is 1, then C<absolute()> will always give you -1,0,1,2 etc, but never
0.5. The actual value will be rounded to the nearest snap value. This
implements the grid/gridsize in Dromed, think of it as a rounding factor.

A value of 0 (the default) means no snapping takes place.

Currently unimplemented.

=item min

The minimum absolute value. If the value after conversion to absolute is
smaller than C<min>, it will be set to C<max>.

=item max

The maximum absolute value. If the value after conversion to absolute is
greater than C<max>, it will be set to C<max>.

=back

=head1 METHODS

=head2 absolute

Return the absolute value of this angle based on it's rel value (this
might traverse the entire tree upwards up to the root).

	print $coord->absolute();

=head2 rel

Sets/gets the L<rel|rel> field, and does check wether the new C<rel> field
is valid.

=head2 val

Sets/gets the L<val|val> field.

=head2 scale

Multiply the given factor to self. After the multiply, the value is 
normalized to be between 0 and 359.

=head2 add & sub

Subtract/add the given offset to self. After the operation, the value is
normalized to be between 0 and 359.

=head2 normalize

Normalize the given argument to be between 0 and 359, then return the result.
Does not expext an object reference:

	print CoW::Angle::normalize(450);	# prints 90

=head1 BUGS

None known yet.

=head1 AUTHOR

Tels http://bloodgate.com in late 2000.

=cut

