# The GIMP -- an image manipulation program
# Copyright (C) 1995 Spencer Kimball and Peter Mattis

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

# "Perlized" from C source by Manish Singh <yosh@gimp.org>
# Derived  from old transform_tools by 
# Joao S. O. Bueno Calligaris <gwidion@mpc.com.br>

# Drawable Transformations

# shortcuts

sub drawable_arg () {{
    name => 'drawable',
    type => 'drawable',
    desc => 'The affected drawable',
}}

sub clip_result_arg () {{ 
          name => 'clip_result',
          type => 'boolean',
          desc => 'Whether to clip results',
          init => False  
}}

sub transform_options_args () {
      { name => 'transform_direction', type => 'enum GimpTransformDirection',
        desc => 'Direction of Transformation: { %%desc%% } ' ,
        init => GIMP_TRANSFORM_BACKWARDS
      },
      { name => 'supersample', type => 'boolean',
        desc => 'Whether to perform supersample',
        init => True 
      },
      { name => 'recursion_level', type => '0 < int32',
        desc => 'Level of recursion (3 is a nice default)' ,
        init => 3,
      }
}

sub drawable_out_arg {
    my $type = shift;
    my $arg = &drawable_arg;
    $arg->{desc} = "The $type drawable";
    $arg->{no_declare} = 1;
    $arg;
}


# The defs

sub drawable_transform_flip {
    $blurb = <<'BLURB';
Flip the specified drawable about its center either vertically or
horizontally, optionally specifying the flip axis.
BLURB

    $help = <<'HELP';
This tool flips the specified drawable if no selection exists. If a selection
exists, the portion of the drawable which lies under the selection is cut from
the drawable and made into a floating selection which is then flipped by the
specified amount. If auto_axis is set to true, the flip is about the middle of the width or height. If not, one can specify the axis coordinates, i.e.two coordinates in the line that will be used as axis for fliping. GIMP_ORIENTATION_UNKNOWN should be used
for an arbitrary flip, else, only the x1 or y1 value will be used for a non-centered
horizontal or vertical flip. Transform_direction, supersample and recursion_level are
only used in such arbitrary flips.
The return value is the ID of the flipped drawable. If there
was no selection, this will be equal to the drawable ID supplied as input.
Otherwise, this will be the newly created and flipped drawable. The flip type
parameter indicates whether the flip will be applied horizontally or
vertically. The clip results parameter specifies wheter current selection will
affect the transform.
HELP

    $author = $copyright = 'João S. O. Bueno Calligaris';
    $date = '2004';

    @inargs = (
        &drawable_arg,
        { name => 'flip_type', type => 'enum GimpOrientationType',
          desc => 'Type of flip: %%desc%%' },
        { name => 'auto_axis', type => 'boolean',
          desc => 'Whether to authomaticaly position the Axis at width or height / 2',
          init => True  },
        { name => 'x1', type => 'float',
          desc => 'horz. coord. of one end of axis',
          init => 0.0     },
        { name => 'y1', type => 'float',
          desc => 'vert. coord. of one end of axis',
          init => 0.0     },
        { name => 'x2', type => 'float',
          desc => 'horz. coord. of other end of axis',
          init => 0.0     },
        { name => 'y2', type => 'float',
          desc => 'vert. coord. of other end of axis',
          init => 0.0     },
        { name => 'interpolation', type => 'enum GimpInterpolationType',
          desc => 'Type of interpolation: { %%desc%% } ' },
          
         &transform_options_args,   
         &clip_result_arg,
    );

    @outargs = ( &drawable_out_arg('flipped') );

    %invoke = (
        code => <<'CODE'
{
  success = gimp_item_is_attached (GIMP_ITEM (drawable));

  if (success)
    { 
      if (transform_direction != GIMP_ORIENTATION_UNKNOWN || y2 == y1 || x2 == x1)
        {
          if (x2 == x1)
            {
              transform_direction = GIMP_ORIENTATION_HORIZONTAL;
            }
          success = gimp_drawable_transform_flip_extended 
                         (drawable, context,
                          flip_type, auto_axis,
                          (transform_direction == GIMP_ORIENTATION_HORIZONTAL)? x1 : y1,
                          clip_result);
        }
      else
        { 
          gint                  bx0, by0, bx1, by1; 
          gdouble               angle;
          gdouble               dx, dy;
          GimpMatrix3           matrix;
          

          /* x0, y0 and  x1, y1 are the axis defining coordinates */
          gimp_drawable_mask_bounds (drawable, &bx0, &by0, &bx1, &by1);   
          
          angle = atan2  ((x2 - x1),  (y2 - y1));           
          dx = bx0  - x1;
          dy = (x1 + ( (gdouble) (y2 - y1) / (x2 - x1) ) * bx0 ) - y1;
          
          
          gimp_matrix3_identity  (&matrix);
          gimp_matrix3_translate (&matrix, dx, dy);
          gimp_matrix3_rotate    (&matrix, angle);
          gimp_matrix3_scale     (&matrix, 1.0, -1.0);
          gimp_matrix3_rotate    (&matrix, -angle);
          gimp_matrix3_translate (&matrix, -dx, -dy);
      

          if (progress)
            gimp_progress_start (progress, _("Flip..."), FALSE);
        
          
          /* Transform the selection */
          success = gimp_drawable_transform_affine (drawable, context,
                                                    &matrix, transform_direction,
                                                    interpolation, supersample,
                                                    recursion_level,
                                                    clip_result, progress);
          
          if (progress)
                gimp_progress_end (progress);
        }
    }
}
CODE
    );
}



sub drawable_transform_perspective {
    $blurb = <<'BLURB';
Perform a possibly non-affine transformation on the specified drawable, with extra parameters.
BLURB

    $help = <<'HELP';
This tool performs a possibly non-affine transformation on the specified
drawable by allowing the corners of the original bounding box to be arbitrarily
remapped to any values. The specified drawable is remapped if no selection
exists. However, if a selection exists, the portion of the drawable which lies
under the selection is cut from the drawable and made into a floating selection
which is then remapped as specified. The return value is the ID of the
remapped drawable. If there was no selection, this will be equal to the
drawable ID supplied as input. Otherwise, this will be the newly created and
remapped drawable. The 4 coordinates specify the new locations of each corner
of the original bounding box. By specifying these values, any affine
transformation (rotation, scaling, translation) can be affected. Additionally,
these values can be specified such that the resulting transformed drawable will
appear to have been projected via a perspective transform.
HELP

    &std_pdb_misc;

    @inargs = (
        &drawable_arg,
        { name => 'interpolation', type => 'enum GimpInterpolationType',
          desc => 'Type of interpolation: { %%desc%% } ' }
    );

    my $pos = 0;
    foreach $where (qw(upper-left upper-right lower-left lower-right)) {
        foreach (qw(x y)) {
            push @inargs,
                     { name => "$_$pos", type => 'float',
                   desc => "The new $_ coordinate of $where corner of original
                            bounding box",
                   alias => "trans_info[\U$_\E$pos]", no_declare => 1 }
        }
        $pos++;
    }
    @inargs = (@inargs,  
      &transform_options_args,
      &clip_result_arg  
    );
          
    @outargs = ( &drawable_out_arg('newly mapped') );

    %invoke = (
        vars => [ 'gdouble trans_info[8]' ],
        code => <<'CODE'
{
  success = gimp_item_is_attached (GIMP_ITEM (drawable));

  if (success)
    {
      gint                  x1, y1, x2, y2;
      GimpMatrix3           matrix;

      gimp_drawable_mask_bounds (drawable, &x1, &y1, &x2, &y2);

      /* Assemble the transformation matrix */
      gimp_transform_matrix_perspective (x1, y1, x2, y2,
                                         trans_info[X0], trans_info[Y0],
                                         trans_info[X1], trans_info[Y1],
                                         trans_info[X2], trans_info[Y2],
                                         trans_info[X3], trans_info[Y3],
                                         &matrix);

      if (progress)
        gimp_progress_start (progress, _("Perspective..."), FALSE);

      /* Perspective the selection */
      success = gimp_drawable_transform_affine (drawable, context,
                                                &matrix, transform_direction,
                                                interpolation, supersample,
                                                recursion_level, clip_result,
                                                progress);

      if (progress)
        gimp_progress_end (progress);
    }
}
CODE
    );
}  

sub drawable_transform_rotate {
    $blurb = <<'BLURB';
Rotate the specified drawable about given coordinates through the specified angle.
BLURB

    $help = <<'HELP';
This tool rotates the specified drawable if no selection exists. If a selection
exists, the portion of the drawable which lies under the selection is cut from
the drawable and made into a floating selection which is then rotated by the
specified amount. The return value is the ID of the rotated drawable.
If there was no selection, this will be equal to the drawable ID supplied as
input. Otherwise, this will be the newly created and rotated drawable.
HELP

    &std_pdb_misc;

    @inargs = (
        &drawable_arg,        
        { name => 'interpolation', type => 'enum GimpInterpolationType',
          desc => 'Type of interpolation: { %%desc%% } ' },
        { name => 'angle', type => 'float',
          desc => 'The angle of rotation (radians)' },
        { name => 'cx', type => 'int32',
          desc => 'The hor. coordinate of the center of rotation' },
        { name => 'cy', type => 'int32',
          desc => 'The vert. coordinate of the center of rotation' },
        &transform_options_args,
        &clip_result_arg
    );

    @outargs = ( &drawable_out_arg('rotated') );

    %invoke = (
        code => <<'CODE'
{
  success = gimp_item_is_attached (GIMP_ITEM (drawable));

  if (success)
    {
      GimpMatrix3           matrix;

      /* Assemble the transformation matrix */
      gimp_transform_matrix_rotate_center (cx, cy, angle, &matrix);

      if (progress)
        gimp_progress_start (progress, _("Rotating..."), FALSE);

      /* Rotate the selection */
      success = gimp_drawable_transform_affine (drawable, context,
                                                &matrix, transform_direction,
                                                interpolation, supersample, 
                                                recursion_level,
                                                clip_result, progress);

      if (progress)
        gimp_progress_end (progress);
    }
}
CODE
    );
}  

sub drawable_transform_scale {
    $blurb = 'Scale the specified drawable with extra parameters';

    $help = <<'HELP';
This tool scales the specified drawable if no selection exists. If a selection
exists, the portion of the drawable which lies under the selection is cut from
the drawable and made into a floating selection which is then scaled by the
specified amount. The return value is the ID of the scaled drawable.
If there was no selection, this will be equal to the drawable ID supplied as
input. Otherwise, this will be the newly created and scaled drawable.
HELP

    &std_pdb_misc;

    @inargs = (
        &drawable_arg,
        { name => 'interpolation', type => 'enum GimpInterpolationType',
          desc => 'Type of interpolation: { %%desc%% } ' }
    );

    my $pos = 0;
    foreach $where (qw(upper-left lower-right)) {
        foreach (qw(x y)) {
            push @inargs,
                     { name => "$_$pos", type => 'float',
                   desc => "The new $_ coordinate of $where corner of newly
                            scaled region",
                   alias => "trans_info[\U$_\E$pos]", no_declare => 1 }
        }
        $pos++;
    }
  
    @inargs = (@inargs,  
      &transform_options_args,
      &clip_result_arg
    );
    
    
    @outargs = ( &drawable_out_arg('scaled') );

    %invoke = (
        vars => [ 'gdouble trans_info[4]' ],
        code => <<'CODE'
{
  success = (gimp_item_is_attached (GIMP_ITEM (drawable)) &&
             trans_info[X0] < trans_info[X1] &&
             trans_info[Y0] < trans_info[X1]);

  if (success)
    {
      gint                  x1, y1, x2, y2;
      GimpMatrix3           matrix;


      gimp_drawable_mask_bounds (drawable, &x1, &y1, &x2, &y2);

      /* Assemble the transformation matrix */
      gimp_transform_matrix_scale (x1, y1, x2, y2,
                                   trans_info[X0], trans_info[Y0],
                                   trans_info[X1], trans_info[Y1],
                                   &matrix);

      if (progress)
        gimp_progress_start (progress, _("Scaling..."), FALSE);

      /* Scale the selection */
      success = gimp_drawable_transform_affine (drawable, context,
                                                &matrix, transform_direction,
                                                interpolation, supersample,
                                                recursion_level,
                                                clip_result, progress);

      if (progress)
        gimp_progress_end (progress);
    }
}
CODE
    );
}
  
sub drawable_transform_shear {
    $blurb = <<'BLURB';
Shear the specified drawable about its center by the specified magnitude, with extra parameters.
BLURB

    $help = <<'HELP';
This tool shears the specified drawable if no selection exists. If a selection
exists, the portion of the drawable which lies under the selection is cut from
the drawable and made into a floating selection which is then sheard by the
specified amount. The return value is the ID of the sheard drawable.
If there was no selection, this will be equal to the drawable ID supplied as
input. Otherwise, this will be the newly created and sheard drawable. The shear
type parameter indicates whether the shear will be applied horizontally or
vertically. The magnitude can be either positive or negative and indicates the
extent (in pixels) to shear by.
HELP

    &std_pdb_misc;

    @inargs = (
        &drawable_arg,
        { name => 'interpolation', type => 'enum GimpInterpolationType',
          desc => 'Type of interpolation: { %%desc%% } ' },
        { name => 'shear_type', type => &std_orientation_enum,
          desc => 'Type of shear: %%desc%%' },
        { name => 'magnitude', type => 'float',
          desc => 'The magnitude of the shear' },
        &transform_options_args,
        &clip_result_arg
    );

    @outargs = ( &drawable_out_arg('sheared') );

    %invoke = (
        code => <<'CODE'
{
  success = gimp_item_is_attached (GIMP_ITEM (drawable));

  if (success)
    {
      gint                  x1, y1, x2, y2;
      GimpMatrix3           matrix;


      gimp_drawable_mask_bounds (drawable, &x1, &y1, &x2, &y2);

      /* Assemble the transformation matrix */
      gimp_transform_matrix_shear (x1, y1, x2, y2,
                                   shear_type, magnitude,
                                   &matrix);

      if (progress)
        gimp_progress_start (progress, _("Shearing..."), FALSE);

      /* Shear the selection */
      success = gimp_drawable_transform_affine (drawable, context,
                                                &matrix, transform_direction,
                                                interpolation, supersample,
                                                recursion_level,
                                                clip_result, progress);

      if (progress)
        gimp_progress_end (progress);
    }
}
CODE
    );
}


sub drawable_transform_2d {
    $blurb = <<'BLURB';
Transform the specified drawable in 2d, with extra parameters.
BLURB

    $help = <<'HELP';
This tool transforms the specified drawable if no selection exists. If a 
selection exists, the portion of the drawable which lies under the 
selection is cut from the drawable and made into a floating selection which 
is then transformed. The transformation is done by scaling the image by 
the x and y scale factors about the point (source_x, source_y), then rotating 
around the same point, then translating that point to the new position 
(dest_x, dest_y). The return value is the ID of the rotated drawable. If 
there was no selection, this will be equal to the drawable ID supplied as 
input. Otherwise, this will be the newly created and transformed drawable.
HELP

    &std_pdb_misc;

    @inargs = (
        &drawable_arg,        
        { name => 'interpolation', type => 'enum GimpInterpolationType',
          desc => 'Type of interpolation: { %%desc%% } ' },
        { name => 'source_x', type => 'float',
          desc => 'X coordinate of the transformation center' },
        { name => 'source_y', type => 'float',
          desc => 'Y coordinate of the transformation center' },
        { name => 'scale_x', type => 'float',
          desc => 'Amount to scale in x direction' },
        { name => 'scale_y', type => 'float',
          desc => 'Amount to scale in y direction' },
        { name => 'angle', type => 'float',
          desc => 'The angle of rotation (radians)' },
        { name => 'dest_x', type => 'float',
          desc => 'X coordinate of where the center goes' },
        { name => 'dest_y', type => 'float',
          desc => 'Y coordinate of where the center goes' },
        &transform_options_args,
        &clip_result_arg
    );

    @outargs = ( &drawable_out_arg('transformed') );

    %invoke = (
        code => <<'CODE'
{
  success = gimp_item_is_attached (GIMP_ITEM (drawable));

  if (success)
    {
      GimpMatrix3           matrix;

      /* Assemble the transformation matrix */
      gimp_matrix3_identity  (&matrix);
      gimp_matrix3_translate (&matrix, -source_x, -source_y);
      gimp_matrix3_scale     (&matrix, scale_x, scale_y);
      gimp_matrix3_rotate    (&matrix, angle);
      gimp_matrix3_translate (&matrix, dest_x, dest_y);


      if (progress)
        gimp_progress_start (progress, _("2D Transform..."), FALSE);

      /* Transform the selection */
      success = gimp_drawable_transform_affine (drawable, context,
                                                &matrix, transform_direction,
                                                interpolation, supersample,
                                                recursion_level,
                                                clip_result, progress);

      if (progress)
        gimp_progress_end (progress);
    }
}
CODE
    );
}
  
@headers = qw("libgimpmath/gimpmath.h" "config/gimpcoreconfig.h"
              "core/gimp.h" "core/gimp-transform-utils.h" "core/gimpimage.h"
              "core/gimpdrawable.h" "core/gimpdrawable-transform.h"
              "core/gimpprogress.h"
              "gimp-intl.h");

@procs = qw(drawable_transform_flip drawable_transform_perspective 
            drawable_transform_rotate drawable_transform_scale 
            drawable_transform_shear   drawable_transform_2d);
  
%exports = (app => [@procs], lib => [@procs]);

$desc = 'Transform Tool procedures';

1;
