# Copyrights 2004-2018 by [Mark Overmeer].
#  For other contributors see ChangeLog.
# See the manual pages for details on the licensing terms.
# Pod stripped from pm file by OODoc 2.02.
# This code is part of distribution Math::Polygon.  Meta-POD processed with
# OODoc into POD and HTML manual-pages.  See README.md
# Copyright Mark Overmeer.  Licensed under the same terms as Perl itself.

package Math::Polygon;
use vars '$VERSION';
$VERSION = '1.10';


use strict;
use warnings;

use Math::Polygon::Calc;
use Math::Polygon::Clip;
use Math::Polygon::Transform;


sub new(@)
{   my $thing = shift;
    my $class = ref $thing || $thing;

    my @points;
    my %options;
    if(ref $thing)
    {   $options{clockwise} = $thing->{MP_clockwise};
    }

    while(@_)
    {   if(ref $_[0] eq 'ARRAY') {push @points, shift}
        else { my $k = shift; $options{$k} = shift }
    }
    $options{_points} = \@points;

    (bless {}, $class)->init(\%options);
}

sub init($$)
{   my ($self, $args) = @_;
    $self->{MP_points}    = $args->{points} || $args->{_points};
    $self->{MP_clockwise} = $args->{clockwise};
    $self->{MP_bbox}      = $args->{bbox};
    $self;
}

#------------------


sub nrPoints() { scalar @{shift->{MP_points}} }


sub order() { @{shift->{MP_points}} -1 }


sub points(;$)
{   my ($self, $format) = @_;
	my $points = $self->{MP_points};
	$points    = [ polygon_format $format, @$points ] if $format;
    wantarray ? @$points : $points;
}


sub point(@)
{   my $points = shift->{MP_points};
    wantarray ? @{$points}[@_] : $points->[shift];
}

#------------------


sub bbox()
{   my $self = shift;
    return @{$self->{MP_bbox}} if $self->{MP_bbox};

    my @bbox = polygon_bbox $self->points;
    $self->{MP_bbox} = \@bbox;
    @bbox;
}


sub area()
{   my $self = shift;
    return $self->{MP_area} if defined $self->{MP_area};
    $self->{MP_area} = polygon_area $self->points;
}

sub centroid()
{   my $self = shift;
    return $self->{MP_centroid} if $self->{MP_centroid};
    $self->{MP_centroid} = polygon_centroid $self->points;
}


sub isClockwise()
{   my $self = shift;
    return $self->{MP_clockwise} if defined $self->{MP_clockwise};
    $self->{MP_clockwise} = polygon_is_clockwise $self->points;
}


sub clockwise()
{   my $self = shift;
    return $self if $self->isClockwise;

    $self->{MP_points}    = [ reverse $self->points ];
    $self->{MP_clockwise} = 1;
    $self;
}


sub counterClockwise()
{   my $self = shift;
    return $self unless $self->isClockwise;

    $self->{MP_points}    = [ reverse $self->points ];
    $self->{MP_clockwise} = 0;
    $self;
}


sub perimeter() { polygon_perimeter shift->points }


sub startMinXY()
{   my $self = shift;
    $self->new(polygon_start_minxy $self->points);
}


sub beautify(@)
{   my ($self, %opts) = @_;
    my @beauty = polygon_beautify \%opts, $self->points;
    @beauty > 2 ? $self->new(points => \@beauty) : ();
}


sub equal($;@)
{   my $self  = shift;
    my ($other, $tolerance);
    if(@_ > 2 || ref $_[1] eq 'ARRAY') { $other = \@_ }
    else
    {   $other     = ref $_[0] eq 'ARRAY' ? shift : shift->points;
        $tolerance = shift;
    }
    polygon_equal scalar($self->points), $other, $tolerance;
}


sub same($;@)
{   my $self = shift;
    my ($other, $tolerance);
    if(@_ > 2 || ref $_[1] eq 'ARRAY') { $other = \@_ }
    else
    {   $other     = ref $_[0] eq 'ARRAY' ? shift : shift->points;
        $tolerance = shift;
    }
    polygon_same scalar($self->points), $other, $tolerance;
}


sub contains($)
{   my ($self, $point) = @_;
    polygon_contains_point($point, $self->points);
}


sub distance($)
{   my ($self, $point) = @_;
    polygon_distance($point, $self->points);
}


sub isClosed() { polygon_is_closed(shift->points) }

#------------------


sub resize(@)
{   my $self = shift;

    my $clockwise = $self->{MP_clockwise};
    if(defined $clockwise)
    {   my %args   = @_;
        my $xscale = $args{xscale} || $args{scale} || 1;
        my $yscale = $args{yscale} || $args{scale} || 1;
        $clockwise = not $clockwise if $xscale * $yscale < 0;
    }

    (ref $self)->new
       ( points    => [ polygon_resize @_, $self->points ]
       , clockwise => $clockwise
       # we could save the bbox calculation as well
       );
}


sub move(@)
{   my $self = shift;

    (ref $self)->new
       ( points    => [ polygon_move @_, $self->points ]
       , clockwise => $self->{MP_clockwise}
       , bbox      => $self->{MP_bbox}
       );
}


sub rotate(@)
{   my $self = shift;

    (ref $self)->new
       ( points    => [ polygon_rotate @_, $self->points ]
       , clockwise => $self->{MP_clockwise}
       # we could save the bbox calculation as well
       );
}


sub grid(@)
{   my $self = shift;

    (ref $self)->new
       ( points    => [ polygon_grid @_, $self->points ]
       , clockwise => $self->{MP_clockwise}  # probably
       # we could save the bbox calculation as well
       );
}


sub mirror(@)
{   my $self = shift;

    my $clockwise = $self->{MP_clockwise};
    $clockwise    = not $clockwise if defined $clockwise;

    (ref $self)->new
       ( points    => [ polygon_mirror @_, $self->points ]
       , clockwise => $clockwise
       # we could save the bbox calculation as well
       );
}


sub simplify(@)
{   my $self = shift;

    (ref $self)->new
       ( points    => [ polygon_simplify @_, $self->points ]
       , clockwise => $self->{MP_clockwise}  # probably
       , bbox      => $self->{MP_bbox}       # protect bounds
       );
}

#------------------


sub lineClip($$$$)
{   my ($self, @bbox) = @_;
    polygon_line_clip \@bbox, $self->points;
}


sub fillClip1($$$$)
{   my ($self, @bbox) = @_;
    my @clip = polygon_fill_clip1 \@bbox, $self->points;
    @clip or return undef;
    $self->new(points => \@clip);
}

#-------------


sub string(;$)
{   my ($self, $format) = @_;
    polygon_string($self->points($format));
}

1;