package Wx::Polygon;

use Wx qw(:everything);
use strict;

our $VERSION='0.01';

my $pi2_360=(2.0*3.1459265)/360.0;

sub new {
  my $class=shift;
  my $args = {
	      POLYGON => undef,
	      ELLIPSE => undef,
	      @_
	      };
  my $self;
  my $def=0;

  $self->{'defined'}=0;

  bless $self,$class;

  if (defined $args->{'POLYGON'}) {
    $self->{'polygon'}=$args->{'POLYGON'};
    $def=1;
  }
  if (defined $args->{'ELLIPSE'}) {
    $self->{'polygon'}=$self->generate_ellipse(@{$args->{'ELLIPSE'}});
    $def=1;
  }

  if (not $def) {
    die "You need to specify one of the possible initializations (POLYGON, ELLIPSE, etc)";
  }

  $self->{'degrees'}=0;
  $self->{'x-off'}=0;
  $self->{'y-off'}=0;
  $self->{'scale'}=1.0;
  my @save=@{$self->{'polygon'}};
  $self->{'saved'}=\@save;

  $self->{'color'}=new Wx::Brush(new Wx::Colour(255,255,255),wxSOLID);

return $self;
}

sub draw {
  my ($self,$dc)=@_;
  my $xoff=$self->{'x-off'};
  my $yoff=$self->{'y-off'};

  $dc->SetBrush($self->{'color'});
  $dc->DrawPolygon($self->{'polygon'},$xoff,$yoff);
  $dc->SetBrush(wxNullBrush);
}

sub copy {
  my ($self)=@_;
  my $pol=$self->{'polygon'};
  my @p;

  for my $point (@{$pol}) {
    push @p,$point;
  }

  my $npol=new Wx::Graphics( 'POLYGON' => \@p );
  if ($self->{'scale'}!=1.0) {
    $npol->scale($self->{'scale'});
  }
  if ($self->{'degrees'}!=0) {
    $npol->rotate($self->{'degrees'});
  }
  if (defined $self->{'rgb.r'}) {
    $npol->set_color($self->{'rgb.r'},
		     $self->{'rgb.g'},
		     $self->{'rgb.b'}
		    );
  }

  return $npol;
}

sub scale {
  my ($self,$scale)=@_;
  my @n;

  $self->{'scale'}=$scale;
  for my $p (@{$self->{'saved'}}) {
    my $np=new Wx::Point($p->x*$scale,$p->y*$scale);
    push @n,$np;
  }
  $self->{'polygon'}=\@n;
}

sub rotate {
  my ($self,$deg)=@_;
  my $C=cos($pi2_360*$deg);
  my $S=sin($pi2_360*$deg);
  my @n;

  $self->{'rotate'}=$deg;

  for my $p (@{$self->{'saved'}}) {
    my $x=$p->x;
    my $y=$p->y;
    my $np=new Wx::Point($x*$C-$y*$S,$x*$S+$y*$C);
    push @n,$np;
  }

  $self->{'polygon'}=\@n;
}

sub offset {
  my ($self,$x,$y)=@_;
  $self->{'x-off'}=$x;
  $self->{'y-off'}=$y;
}

sub generate_ellipse {
  my ($self,$x,$y,$w,$h)=@_;
  my $r=0;
  my $step=1;
  my $d;
  my @pol;

  for($d=0;$d<360;$d+=$step) {
    my $px=$w*cos($pi2_360*$d);
    my $py=$h*sin($pi2_360*$d);
    my $p=new Wx::Point($x+$px,$y+$py);
    push @pol,$p;
  }

return \@pol;
}

sub add_point {
  my ($self,$x,$y,$recalc)=@_;
  if (not defined $recalc) { $recalc=1; }
  my $p=new Wx::Point($x,$y);
  push @{$self->{'polygon'}},$p;
  push @{$self->{'saved'}},$p;
  push @{$self->{'planar_polygon'}},[$x,$y];
  if ($recalc) { $self->recalc(); }
}

sub recalc {
  my $self=shift;
  if ($self->{'scale'}!=1.0) {
    $self->scale($self->{'scale'});
  }
  if ($self->{'degrees'}!=0) {
    $self->rotate($self->{'degrees'});
  }
}

sub set_color {
  my ($self,$r,$g,$b)=@_;
  $self->{'rbg.r'}=$r;
  $self->{'rgb.g'}=$g;
  $self->{'rgb.b'}=$b;
  $self->{'color'}=new Wx::Brush(new Wx::Colour($r,$g,$b),wxSOLID);
}

sub mid {
  my ($self)=@_;
  my ($minx,$miny,$maxx,$maxy)=(30000,30000,-30000,-30000);

  for my $p (@{$self->{'polygon'}}) {
    if ($minx>$p->x) { $minx=$p->x; }
    if ($maxx<$p->x) { $maxx=$p->x; }
    if ($miny>$p->y) { $miny=$p->y; }
    if ($maxy<$p->y) { $maxy=$p->y; }
  }

return ( $self->{'x-off'}+(($maxx-$minx)/2+$minx), $self->{'y-off'}+(($maxy-$miny)/2+$miny) );
}


sub in {
  my ($self,$x,$y)=@_;
  my $yes=0;

  my $i;
  my $j;

  $x-=$self->{'x-off'};
  $y-=$self->{'y-off'};

  my $N=scalar @{$self->{'polygon'}};
  my $pol=$self->{'polygon'};

  for ($i=0,$j=$N-1;$i<$N;$j=$i++) {
    my $ypi=$pol->[$i]->y;
    my $ypj=$pol->[$j]->y;
    my $xpi=$pol->[$i]->x;
    my $xpj=$pol->[$j]->x;

    if (((($ypi<=$y) and ($y<$ypj)) or
	 (($ypj<=$y) and ($y<$ypi))) and
	($x<($xpj-$xpi)*($y-$ypi)/($ypj-$ypi)+$xpi)) {
      $yes=!$yes;
    }
  }

  if ($yes) {
    return 1;
  }
  else {
    return 0;
  }

}


1;
__END__

=head1 NAME

Wx::Polygon - Draw and manipulate polygons for wxPerl

=head1 ABSTRACT

This module provides functions for manipulating polygons
in wxPerl.

=head1 Description

=head2 C<new( POLYGON => \(Wx::Point,...) | ELLIPSE => \(x_offset,y_offset,width,heigth) ) --E<gt> Wx::Polygon>

Instantiates a new Wx::Polygon with an array of Wx::Points for a given
C<POLYGON> or an ellipse with given parameters.

=head2 C<add_point(x,y,recalc=1) --E<gt> void>

Adds a point to the polygon. If scale and rotation have
been used, rescales and rerotates the polygon after adding
the point. If C<recalc> is set to C<0>, this will not
be done, which is better when a lot of points need added.

=head2 C<recalc() --E<gt> void>

Rescales and rerotates the polygon. This method should be
used when a lot of points need to be added. Recalculation
can than be done after adding the points.

=head2 C<set_color(r,g,b) --E<gt> void>

Sets the fill color to wxSOLID with the given C<r>, C<g>
and C<b> values.

=head2 C<mid() --E<gt> (midx:integer,midy:integer)>

Calculates the middle of the polygon (not point 'z') and 
returns a list with x and y position.

=head2 C<in(x,y) --E<gt> boolean>

Calculates if (x,y) falls within the edges of the polygon.
Returns true if so. Uses algorithm of Randolph Franklin,
L<http://astronomy.swin.edu.au/~pbourke/geometry/insidepoly>.

=head2 C<draw( dc:Wx::DC ) --E<gt> void>

Draws the polygon at given offsets and with given colour(s) to
the current DC.

=head2 C<copy() --E<gt> Wx::Polygon>

Returns a copy of the object.

=head2 C<scale(scale-factor) --E<gt> void>

Scales the polygon with C<scale-factor>.

=head2 C<rotate(degrees) --E<gt> void>

Rotates the polygon by C<degrees> degrees (0-360).

=head2 C<offset(x,y) --E<gt> void>

Sets the offset for drawing. This can be used to
transpose the polygon.





=head1 AUTHOR

Hans Oesterholt-Dijkema <oesterhol@cpan.org>

=head1 COPYRIGHT & LICENSE

(c)2004 Hans Oesterholt-Dijkema. This module can be
redistributed under artistic license.

=cut




