/* GIMP - The GNU Image Manipulation Program
 * Copyright (C) 1995-2001 Spencer Kimball, Peter Mattis, and others
 *
 * 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 3 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, see <https://www.gnu.org/licenses/>.
 */

#include "config.h"

#include <string.h>

#include <gio/gio.h>
#include <gegl.h>

#include "libgimpmath/gimpmath.h"

#include "core-types.h"

#include "gimp-transform-resize.h"
#include "gimp-transform-utils.h"
#include "gimp-utils.h"


#if defined (HAVE_FINITE)
#define FINITE(x) finite(x)
#elif defined (HAVE_ISFINITE)
#define FINITE(x) isfinite(x)
#elif defined (G_OS_WIN32)
#define FINITE(x) _finite(x)
#else
#error "no FINITE() implementation available?!"
#endif

#define EPSILON 0.00000001


typedef struct
{
  GimpVector2 a, b, c, d;
  gdouble     area;
  gdouble     aspect;
} Rectangle;


static void      gimp_transform_resize_adjust        (const GimpVector2 *points,
                                                      gint               n_points,
                                                      gint              *x1,
                                                      gint              *y1,
                                                      gint              *x2,
                                                      gint              *y2);
static void      gimp_transform_resize_crop          (const GimpVector2 *points,
                                                      gint               n_points,
                                                      gdouble            aspect,
                                                      gint              *x1,
                                                      gint              *y1,
                                                      gint              *x2,
                                                      gint              *y2);

static void      add_rectangle                       (const GimpVector2 *points,
                                                      gint               n_points,
                                                      Rectangle         *r,
                                                      GimpVector2        a,
                                                      GimpVector2        b,
                                                      GimpVector2        c,
                                                      GimpVector2        d);
static gboolean  intersect                           (GimpVector2        a,
                                                      GimpVector2        b,
                                                      GimpVector2        c,
                                                      GimpVector2        d,
                                                      GimpVector2       *i);
static gboolean  intersect_x                         (GimpVector2        a,
                                                      GimpVector2        b,
                                                      GimpVector2        c,
                                                      GimpVector2       *i);
static gboolean  intersect_y                         (GimpVector2        a,
                                                      GimpVector2        b,
                                                      GimpVector2        c,
                                                      GimpVector2       *i);
static gboolean  in_poly                             (const GimpVector2 *points,
                                                      gint               n_points,
                                                      GimpVector2        p);
static gboolean  point_on_border                     (const GimpVector2 *points,
                                                      gint               n_points,
                                                      GimpVector2        p);

static void      find_two_point_rectangle            (Rectangle         *r,
                                                      const GimpVector2 *points,
                                                      gint               n_points,
                                                      gint               p);
static void      find_three_point_rectangle_corner   (Rectangle         *r,
                                                      const GimpVector2 *points,
                                                      gint               n_points,
                                                      gint               p);
static void      find_three_point_rectangle          (Rectangle          *r,
                                                      const GimpVector2 *points,
                                                      gint               n_points,
                                                      gint               p);
static void      find_three_point_rectangle_triangle (Rectangle         *r,
                                                      const GimpVector2 *points,
                                                      gint               n_points,
                                                      gint               p);
static void      find_maximum_aspect_rectangle       (Rectangle          *r,
                                                      const GimpVector2 *points,
                                                      gint               n_points,
                                                      gint               p);


/*
 * This function wants to be passed the inverse transformation matrix!!
 */
void
gimp_transform_resize_boundary (const GimpMatrix3   *inv,
                                GimpTransformResize  resize,
                                gint                 u1,
                                gint                 v1,
                                gint                 u2,
                                gint                 v2,
                                gint                *x1,
                                gint                *y1,
                                gint                *x2,
                                gint                *y2)
{
  GimpVector2 bounds[4];
  GimpVector2 points[5];
  gint        n_points;
  gboolean    valid;
  gint        i;

  g_return_if_fail (inv != NULL);

  /*  initialize with the original boundary  */
  *x1 = u1;
  *y1 = v1;
  *x2 = u2;
  *y2 = v2;

  /* if clipping then just return the original rectangle */
  if (resize == GIMP_TRANSFORM_RESIZE_CLIP)
    return;

  bounds[0] = (GimpVector2) { u1, v1 };
  bounds[1] = (GimpVector2) { u2, v1 };
  bounds[2] = (GimpVector2) { u2, v2 };
  bounds[3] = (GimpVector2) { u1, v2 };

  gimp_transform_polygon (inv, bounds, 4, TRUE,
                          points, &n_points);

  valid = (n_points >= 2);

  /*  check if the transformation matrix is valid at all  */
  for (i = 0; i < n_points && valid; i++)
    valid = (FINITE (points[i].x) && FINITE (points[i].y));

  if (! valid)
    {
      g_warning ("invalid transform matrix");
      /* since there is no sensible way to deal with this, just do the same as
       * with GIMP_TRANSFORM_RESIZE_CLIP: return
       */
      return;
    }

  switch (resize)
    {
    case GIMP_TRANSFORM_RESIZE_ADJUST:
      /* return smallest rectangle (with sides parallel to x- and y-axis)
       * that surrounds the new points */
      gimp_transform_resize_adjust (points, n_points,
                                    x1, y1, x2, y2);
      break;

    case GIMP_TRANSFORM_RESIZE_CROP:
      gimp_transform_resize_crop (points, n_points,
                                  0.0,
                                  x1, y1, x2, y2);
      break;

    case GIMP_TRANSFORM_RESIZE_CROP_WITH_ASPECT:
      gimp_transform_resize_crop (points, n_points,
                                  ((gdouble) u2 - u1) / (v2 - v1),
                                  x1, y1, x2, y2);
      break;

    case GIMP_TRANSFORM_RESIZE_CLIP:
      /* Remove warning about not handling all enum values. We handle
       * this case in the beginning of the function
       */
      break;
    }

  /* ensure that resulting rectangle has at least area 1 */
  if (*x1 == *x2)
    (*x2)++;

  if (*y1 == *y2)
    (*y2)++;
}

/* this calculates the smallest rectangle (with sides parallel to x- and
 * y-axis) that contains the points d1 to d4
 */
static void
gimp_transform_resize_adjust (const GimpVector2 *points,
                              gint               n_points,
                              gint              *x1,
                              gint              *y1,
                              gint              *x2,
                              gint              *y2)
{
  GimpVector2 top_left;
  GimpVector2 bottom_right;
  gint        i;

  top_left = bottom_right = points[0];

  for (i = 1; i < n_points; i++)
    {
      top_left.x     = MIN (top_left.x,     points[i].x);
      top_left.y     = MIN (top_left.y,     points[i].y);

      bottom_right.x = MAX (bottom_right.x, points[i].x);
      bottom_right.y = MAX (bottom_right.y, points[i].y);
    }

  *x1 = (gint) floor (top_left.x + EPSILON);
  *y1 = (gint) floor (top_left.y + EPSILON);

  *x2 = (gint) ceil (bottom_right.x - EPSILON);
  *y2 = (gint) ceil (bottom_right.y - EPSILON);
}

static void
gimp_transform_resize_crop (const GimpVector2 *orig_points,
                            gint               n_points,
                            gdouble            aspect,
                            gint              *x1,
                            gint              *y1,
                            gint              *x2,
                            gint              *y2)
{
  GimpVector2 points[5];
  Rectangle   r;
  GimpVector2 t,a;
  gint        i, j;
  gint        min;

  memcpy (points, orig_points, sizeof (GimpVector2) * n_points);

  /* find lowest, rightmost corner of surrounding rectangle */
  a.x = 0;
  a.y = 0;
  for (i = 0; i < 4; i++)
    {
      if (points[i].x < a.x)
        a.x = points[i].x;

      if (points[i].y < a.y)
        a.y = points[i].y;
    }

  /* and translate all the points to the first quadrant */
  for (i = 0; i < n_points; i++)
    {
      points[i].x += (-a.x) * 2;
      points[i].y += (-a.y) * 2;
    }

  /* find the convex hull using Jarvis's March as the points are passed
   * in different orders due to gimp_matrix3_transform_point()
   */
  min = 0;
  for (i = 0; i < n_points; i++)
    {
      if (points[i].y < points[min].y)
        min = i;
    }

  t = points[0];
  points[0] = points[min];
  points[min] = t;

  for (i = 1; i < n_points - 1; i++)
    {
      gdouble min_theta;
      gdouble min_mag;
      int next;

      next = n_points - 1;
      min_theta = 2.0 * G_PI;
      min_mag = DBL_MAX;

      for (j = i; j < n_points; j++)
        {
          gdouble theta;
          gdouble sy;
          gdouble sx;
          gdouble mag;

          sy = points[j].y - points[i - 1].y;
          sx = points[j].x - points[i - 1].x;

          if ((sx == 0.0) && (sy == 0.0))
            {
              next = j;
              break;
            }

          theta = atan2 (-sy, -sx);
          mag = (sx * sx) + (sy * sy);

          if ((theta < min_theta) ||
              ((theta == min_theta) && (mag < min_mag)))
            {
              min_theta = theta;
              min_mag = mag;
              next = j;
            }
        }

      t = points[i];
      points[i] = points[next];
      points[next] = t;
    }

  /* reverse the order of points */
  for (i = 0; i < n_points / 2; i++)
    {
      t                        = points[i];
      points[i]                = points[n_points - i - 1];
      points[n_points - i - 1] = t;
    }

  r.a.x = r.a.y = r.b.x = r.b.y = r.c.x = r.c.y = r.d.x = r.d.y = r.area = 0;
  r.aspect = aspect;

  if (aspect != 0)
    {
      for (i = 0; i < n_points; i++)
        find_maximum_aspect_rectangle (&r, points, n_points, i);
    }
  else
    {
      for (i = 0; i < n_points; i++)
        {
          find_three_point_rectangle          (&r, points, n_points, i);
          find_three_point_rectangle_corner   (&r, points, n_points, i);
          find_two_point_rectangle            (&r, points, n_points, i);
          find_three_point_rectangle_triangle (&r, points, n_points, i);
        }
    }

  if (r.area == 0)
    {
      /* saveguard if something went wrong, adjust and give warning */
      gimp_transform_resize_adjust (orig_points, n_points,
                                    x1, y1, x2, y2);
      g_warning ("no rectangle found by algorithm, no cropping done");
      return;
    }
  else
    {
      /* round and translate the calculated points back */
      *x1 = floor (r.a.x + 0.5);
      *y1 = floor (r.a.y + 0.5);
      *x2 = ceil  (r.c.x - 0.5);
      *y2 = ceil  (r.c.y - 0.5);

      *x1 = *x1 - ((-a.x) * 2);
      *y1 = *y1 - ((-a.y) * 2);
      *x2 = *x2 - ((-a.x) * 2);
      *y2 = *y2 - ((-a.y) * 2);
      return;
    }
}

static void
find_three_point_rectangle (Rectangle         *r,
                            const GimpVector2 *points,
                            gint               n_points,
                            gint               p)
{
  GimpVector2 a = points[p       % n_points];  /* 0 1 2 3 */
  GimpVector2 b = points[(p + 1) % n_points];  /* 1 2 3 0 */
  GimpVector2 c = points[(p + 2) % n_points];  /* 2 3 0 1 */
  GimpVector2 d = points[(p + 3) % n_points];  /* 3 0 1 2 */
  GimpVector2 i1;                              /* intersection point */
  GimpVector2 i2;                              /* intersection point */
  GimpVector2 i3;                              /* intersection point */

  if (intersect_x (b, c, a,  &i1) &&
      intersect_y (c, d, i1, &i2) &&
      intersect_x (d, a, i2, &i3))
    add_rectangle (points, n_points, r, i3, i3, i1, i1);

  if (intersect_y (b, c, a,  &i1) &&
      intersect_x (c, d, i1, &i2) &&
      intersect_y (d, a, i2, &i3))
    add_rectangle (points, n_points, r, i3, i3, i1, i1);

  if (intersect_x (d, c, a,  &i1) &&
      intersect_y (c, b, i1, &i2) &&
      intersect_x (b, a, i2, &i3))
    add_rectangle (points, n_points, r, i3, i3, i1, i1);

  if (intersect_y (d, c, a,  &i1) &&
      intersect_x (c, b, i1, &i2) &&
      intersect_y (b, a, i2, &i3))
    add_rectangle (points, n_points, r, i3, i3, i1, i1);
}

static void
find_three_point_rectangle_corner (Rectangle         *r,
                                   const GimpVector2 *points,
                                   gint               n_points,
                                   gint               p)
{
  GimpVector2 a = points[p       % n_points];  /* 0 1 2 3 */
  GimpVector2 b = points[(p + 1) % n_points];  /* 1 2 3 0 */
  GimpVector2 c = points[(p + 2) % n_points];  /* 2 3 0 2 */
  GimpVector2 d = points[(p + 3) % n_points];  /* 3 0 2 1 */
  GimpVector2 i1;                              /* intersection point */
  GimpVector2 i2;                              /* intersection point */

  if (intersect_x (b, c, a , &i1) &&
      intersect_y (c, d, i1, &i2))
    add_rectangle (points, n_points, r, a, a, i1, i2);

  if (intersect_y (b, c, a , &i1) &&
      intersect_x (c, d, i1, &i2))
    add_rectangle (points, n_points, r, a, a, i1, i2);

  if (intersect_x (c, d, a , &i1) &&
      intersect_y (b, c, i1, &i2))
    add_rectangle (points, n_points, r, a, a, i1, i2);

  if (intersect_y (c, d, a , &i1) &&
      intersect_x (b, c, i1, &i2))
    add_rectangle (points, n_points, r, a, a, i1, i2);
}

static void
find_two_point_rectangle (Rectangle         *r,
                          const GimpVector2 *points,
                          gint               n_points,
                          gint               p)
{
  GimpVector2 a = points[ p      % n_points];  /* 0 1 2 3 */
  GimpVector2 b = points[(p + 1) % n_points];  /* 1 2 3 0 */
  GimpVector2 c = points[(p + 2) % n_points];  /* 2 3 0 1 */
  GimpVector2 d = points[(p + 3) % n_points];  /* 3 0 1 2 */
  GimpVector2 i1;                              /* intersection point */
  GimpVector2 i2;                              /* intersection point */
  GimpVector2 mid;                             /* Mid point */

  add_rectangle (points, n_points, r, a, a, c, c);
  add_rectangle (points, n_points, r, b, b, d, d);

  if (intersect_x (c, b, a, &i1) &&
      intersect_y (c, b, a, &i2))
    {
      mid.x = ( i1.x + i2.x ) / 2.0;
      mid.y = ( i1.y + i2.y ) / 2.0;

      add_rectangle (points, n_points, r, a, a, mid, mid);
    }
}

static void
find_three_point_rectangle_triangle (Rectangle         *r,
                                     const GimpVector2 *points,
                                     gint               n_points,
                                     gint               p)
{
  GimpVector2 a = points[p       % n_points];  /* 0 1 2 3 */
  GimpVector2 b = points[(p + 1) % n_points];  /* 1 2 3 0 */
  GimpVector2 c = points[(p + 2) % n_points];  /* 2 3 0 1 */
  GimpVector2 d = points[(p + 3) % n_points];  /* 3 0 1 2 */
  GimpVector2 i1;                              /* intersection point */
  GimpVector2 i2;                              /* intersection point */
  GimpVector2 mid;

  mid.x = (a.x + b.x) / 2.0;
  mid.y = (a.y + b.y) / 2.0;

  if (intersect_x (b, c, mid, &i1) &&
      intersect_y (a, d, mid, &i2))
    add_rectangle (points, n_points, r, mid, mid, i1, i2);

  if (intersect_y (b, c, mid, &i1) &&
      intersect_x (a, d, mid, &i2))
    add_rectangle (points, n_points, r, mid, mid, i1, i2);

  if (intersect_x (a, d, mid, &i1) &&
      intersect_y (b, c, mid, &i2))
    add_rectangle (points, n_points, r, mid, mid, i1, i2);

  if (intersect_y (a, d, mid, &i1) &&
      intersect_x (b, c, mid, &i2))
    add_rectangle (points, n_points, r, mid, mid, i1, i2);
}

static void
find_maximum_aspect_rectangle (Rectangle         *r,
                               const GimpVector2 *points,
                               gint               n_points,
                               gint               p)
{
  GimpVector2 a = points[ p      % n_points];  /* 0 1 2 3 */
  GimpVector2 b = points[(p + 1) % n_points];  /* 1 2 3 0 */
  GimpVector2 c = points[(p + 2) % n_points];  /* 2 3 0 1 */
  GimpVector2 d = points[(p + 3) % n_points];  /* 3 0 1 2 */
  GimpVector2 i1;                              /* intersection point */
  GimpVector2 i2;                              /* intersection point */
  GimpVector2 i3;                              /* intersection point */

  if (intersect_x (b, c, a, &i1))
    {
      i2.x = i1.x + 1.0 * r->aspect;
      i2.y = i1.y + 1.0;

      if (intersect (d, a, i1, i2, &i3))
        add_rectangle (points, n_points, r, i1, i3, i1, i3);

      if (intersect (a, b, i1, i2, &i3))
        add_rectangle (points, n_points, r, i1, i3, i1, i3);

      if (intersect (c, d, i1, i2, &i3))
        add_rectangle (points, n_points, r, i1, i3, i1, i3);

      i2.x = i1.x - 1.0 * r->aspect;
      i2.y = i1.y + 1.0;

      if (intersect (d, a, i1, i2, &i3))
        add_rectangle (points, n_points, r, i1, i3, i1, i3);

      if (intersect (a, b, i1, i2, &i3))
        add_rectangle (points, n_points, r, i1, i3, i1, i3);

      if (intersect (c, d, i1, i2, &i3))
        add_rectangle (points, n_points, r, i1, i3, i1, i3);
    }

  if (intersect_y (b, c, a, &i1))
    {
      i2.x = i1.x + 1.0 * r->aspect;
      i2.y = i1.y + 1.0;

      if (intersect (d, a, i1, i2, &i3))
        add_rectangle (points, n_points, r, i1, i3, i1, i3);

      if (intersect (a, b, i1, i2, &i3))
        add_rectangle (points, n_points, r, i1, i3, i1, i3);

      if (intersect (c, d, i1, i2, &i3))
        add_rectangle (points, n_points, r, i1, i3, i1, i3);

      i2.x = i1.x - 1.0 * r->aspect;
      i2.y = i1.y + 1.0;

      if (intersect (d, a, i1, i2, &i3))
        add_rectangle (points, n_points, r, i1, i3, i1, i3);

      if (intersect (a, b, i1, i2, &i3))
        add_rectangle (points, n_points, r, i1, i3, i1, i3);

      if (intersect (c, d, i1, i2, &i3))
        add_rectangle (points, n_points, r, i1, i3, i1, i3);
    }

  if (intersect_x (c, d, a,  &i1))
    {
      i2.x = i1.x + 1.0 * r->aspect;
      i2.y = i1.y + 1.0;

      if (intersect (d, a, i1, i2, &i3))
        add_rectangle (points, n_points, r, i1, i3, i1, i3);

      if (intersect (a, b, i1, i2, &i3))
        add_rectangle (points, n_points, r, i1, i3, i1, i3);

      if (intersect (b, c, i1, i2, &i3))
        add_rectangle (points, n_points, r, i1, i3, i1, i3);

      i2.x = i1.x - 1.0 * r->aspect;
      i2.y = i1.y + 1.0;

      if (intersect (d, a, i1, i2, &i3))
        add_rectangle (points, n_points, r, i1, i3, i1, i3);

      if (intersect (a, b, i1, i2, &i3))
        add_rectangle (points, n_points, r, i1, i3, i1, i3);

      if (intersect (b, c, i1, i2, &i3))
        add_rectangle (points, n_points, r, i1, i3, i1, i3);
    }

  if (intersect_y (c, d, a,  &i1))
    {
      i2.x = i1.x + 1.0 * r->aspect;
      i2.y = i1.y + 1.0;

      if (intersect (d, a, i1, i2, &i3))
        add_rectangle (points, n_points, r, i1, i3, i1, i3);

      if (intersect (a, b, i1, i2, &i3))
        add_rectangle (points, n_points, r, i1, i3, i1, i3);

      if (intersect (b, c, i1, i2, &i3))
        add_rectangle (points, n_points, r, i1, i3, i1, i3);

      i2.x = i1.x - 1.0 * r->aspect;
      i2.y = i1.y + 1.0;

      if (intersect (d, a, i1, i2, &i3))
        add_rectangle (points, n_points, r, i1, i3, i1, i3);

      if (intersect (a, b, i1, i2, &i3))
        add_rectangle (points, n_points, r, i1, i3, i1, i3);

      if (intersect (b, c, i1, i2, &i3))
        add_rectangle (points, n_points, r, i1, i3, i1, i3);
    }
}

/* check if point is inside the polygon "points", if point is on border
 * its still inside.
 */
static gboolean
in_poly (const GimpVector2 *points,
         gint               n_points,
         GimpVector2        p)
{
  GimpVector2 p1, p2;
  gint  counter = 0;
  gint  i;

  p1 = points[0];

  for (i = 1; i <= n_points; i++)
    {
      p2 = points[i % n_points];

      if (p.y > MIN (p1.y, p2.y))
        {
          if (p.y <= MAX (p1.y, p2.y))
            {
              if (p.x <= MAX (p1.x, p2.x))
                {
                  if (p1.y != p2.y)
                    {
                      gdouble xinters = ((p.y - p1.y) * (p2.x - p1.x) /
                                         (p2.y - p1.y) + p1.x);

                      if (p1.x == p2.x || p.x <= xinters)
                        counter++;
                    }
                }
            }
        }

      p1 = p2;
    }

  /* border check */
  if (point_on_border (points, n_points, p))
    return TRUE;

  return (counter % 2 != 0);
}

/* check if the point p lies on the polygon "points"
 */
static gboolean
point_on_border (const GimpVector2 *points,
                 gint               n_points,
                 GimpVector2        p)
{
  gint i;

  for (i = 0; i <= n_points; i++)
    {
      GimpVector2   a  = points[i       % n_points];
      GimpVector2   b  = points[(i + 1) % n_points];
      gdouble a1 = (b.y - a.y);
      gdouble b1 = (a.x - b.x);
      gdouble c1 = a1 * a.x + b1 * a.y;
      gdouble c2 = a1 * p.x + b1 * p.y;

      if (ABS (c1 - c2) < EPSILON &&
          MIN (a.x, b.x) <= p.x   &&
          MAX (a.x, b.x) >= p.x   &&
          MIN (a.y, b.y) <= p.y   &&
          MAX (a.y, b.y) >= p.y)
        return TRUE;
    }

  return FALSE;
}

/* calculate the intersection point of the line a-b with the line c-d
 * and write it to i, if existing.
 */
static gboolean
intersect (GimpVector2  a,
           GimpVector2  b,
           GimpVector2  c,
           GimpVector2  d,
           GimpVector2 *i)
{
  gdouble a1  = (b.y - a.y);
  gdouble b1  = (a.x - b.x);
  gdouble c1  = a1 * a.x + b1 * a.y;

  gdouble a2  = (d.y - c.y);
  gdouble b2  = (c.x - d.x);
  gdouble c2  = a2 * c.x + b2 * c.y;
  gdouble det = a1 * b2 - a2 * b1;

  if (det == 0)
    return FALSE;

  i->x = (b2 * c1 - b1 * c2) / det;
  i->y = (a1 * c2 - a2 * c1) / det;

  return TRUE;
}

/* calculate the intersection point of the line a-b with the vertical line
 * through c and write it to i, if existing.
 */
static gboolean
intersect_x (GimpVector2  a,
             GimpVector2  b,
             GimpVector2  c,
             GimpVector2 *i)
{
  GimpVector2   d = c;
  d.y += 1;

  return intersect(a,b,c,d,i);
}

/* calculate the intersection point of the line a-b with the horizontal line
 * through c and write it to i, if existing.
 */
static gboolean
intersect_y (GimpVector2  a,
             GimpVector2  b,
             GimpVector2  c,
             GimpVector2 *i)
{
  GimpVector2   d = c;
  d.x += 1;

  return intersect(a,b,c,d,i);
}

/* this takes the smallest ortho-aligned (the sides of the rectangle are
 * parallel to the x- and y-axis) rectangle fitting around the points a to d,
 * checks if the whole rectangle is inside the polygon described by points and
 * writes it to r if the area is bigger than the rectangle already stored in r.
 */
static void
add_rectangle (const GimpVector2 *points,
               gint               n_points,
               Rectangle         *r,
               GimpVector2        a,
               GimpVector2        b,
               GimpVector2        c,
               GimpVector2        d)
{
  gdouble width;
  gdouble height;
  gdouble minx, maxx;
  gdouble miny, maxy;

  /* get the orthoaligned (the sides of the rectangle are parallel to the x-
   * and y-axis) rectangle surrounding the points a to d.
   */
  minx = MIN4 (a.x, b.x, c.x, d.x);
  maxx = MAX4 (a.x, b.x, c.x, d.x);
  miny = MIN4 (a.y, b.y, c.y, d.y);
  maxy = MAX4 (a.y, b.y, c.y, d.y);

  a.x = minx;
  a.y = miny;

  b.x = maxx;
  b.y = miny;

  c.x = maxx;
  c.y = maxy;

  d.x = minx;
  d.y = maxy;

  width  =  maxx - minx;
  height =  maxy - miny;

  /* check if this rectangle is inside the polygon "points" */
  if (in_poly (points, n_points, a) &&
      in_poly (points, n_points, b) &&
      in_poly (points, n_points, c) &&
      in_poly (points, n_points, d))
    {
      gdouble area = width * height;

      /* check if the new rectangle is larger (in terms of area)
       * than the currently stored rectangle in r, if yes store
       * new rectangle to r
       */
      if (r->area <= area)
        {
          r->a.x = a.x;
          r->a.y = a.y;

          r->b.x = b.x;
          r->b.y = b.y;

          r->c.x = c.x;
          r->c.y = c.y;

          r->d.x = d.x;
          r->d.y = d.y;

          r->area = area;
        }
    }
}
