Graphics-Grid

 view release on metacpan or  search on metacpan

lib/Graphics/Grid/UnitArithmetic.pm  view on Meta::CPAN

package Graphics::Grid::UnitArithmetic;

# ABSTRACT: Expression created from Graphics::Grid::Unit objects

use Graphics::Grid::Class;

our $VERSION = '0.0001'; # VERSION

use Scalar::Util qw(looks_like_number);
use Type::Params ();
use Types::Standard qw(Str ArrayRef Any Num);
use namespace::autoclean;

use Graphics::Grid::Util qw(points_to_cm);
use Graphics::Grid::Types qw(:all);

extends 'Forest::Tree';

use overload
  "+"        => 'plus',
  "-"        => 'minus',
  "*"        => 'multiply',
  "fallback" => 1;


has '+node' => (
    isa => (
        ( ArrayRef [Num] )->plus_coercions( Num, sub { [$_] } ) |
          Str->where( sub { $_ =~ /^[\*\+\-]$/ } ) | Unit
    ),
    coerce => 1,
);


has '+children' => (
    isa => ArrayRef [
        UnitArithmetic->plus_coercions( Any,
            sub {
                if ( $_->$_isa('Graphics::Grid::UnitArithmetic') ) {
                    return $_;
                }
                my $node = $_;
                unless ( $_->$_isa('Graphics::Grid::Unit')
                    or Ref::Util::is_arrayref($node) )
                {
                    $node = [$node];
                }
                return Graphics::Grid::UnitArithmetic->new( node => $node );
            }
        )
    ],
    coerce => 1,
);

with qw(
  Graphics::Grid::UnitLike
);


method at($idx) {
    if ( $self->is_unit ) {
        return __PACKAGE__->new( node => $self->node->at($idx) );
    }
    elsif ( $self->is_number ) {
        return __PACKAGE__->new(
            node => [ $self->node->[ $idx % $self->elems ] ] );
    }
    else {
        return __PACKAGE__->new(
            node     => $self->node,
            children => [ map { $_->at($idx) } @{ $self->children } ]
        );
    }
}


method elems() {
    if ( $self->is_unit ) {
        return $self->node->elems;
    }
    elsif ( $self->is_number ) {
        return scalar( @{ $self->node } );
    }
    else {
        return List::AllUtils::max( map { $_->elems } @{ $self->children } );
    }
};


method is_unit() {
    return $self->node->$_isa('Graphics::Grid::Unit');
}


method is_number() {
    return Ref::Util::is_arrayref( $self->node );
}


method is_arithmetic() {
    return !( $self->is_unit() or $self->is_number() );
}


method stringify() {
    if ( $self->is_unit ) {
        return $self->node->stringify;
    }
    elsif ( $self->is_number ) {
        return join( ', ', @{ $self->node } );
    }
    else {
        return join(
            ', ',
            map {
                my $arg0 = $self->children->[0]->at($_);
                my $arg1 = $self->children->[1]->at($_);
                my $format;
                if ( $self->node eq '*' ) {
                    if ( $arg0->is_arithmetic ) {
                        $format = "(%s)%s%s";
                    }
                    elsif ( $arg1->is_arithmetic ) {
                        $format = "%s%s(%s)";
                    }
                }
                $format //= "%s%s%s";
                sprintf( $format,
                    $arg0->stringify, $self->node, $arg1->stringify );
            } ( 0 .. $self->elems - 1 )
        );
    }
}

method _make_operation( $op, $other, $swap = undef ) {
    return __PACKAGE__->new(
        node     => $op,
        children => ( $swap ? [ $other, $self ] : [ $self, $other ] )
    );
}

method plus( UnitLike $other, $swap = undef ) {
    return $self->_make_operation( '+', $other, $swap );
}

method minus( UnitLike $other, $swap = undef ) {
    return $self->_make_operation( '-', $other, $swap );
}

method multiply( ( ArrayRef [Num] | Num ) $other, $swap = undef ) {
    return $self->_make_operation( '*', $other, $swap );
}

__PACKAGE__->meta->make_immutable;

1;



( run in 2.299 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )