836 lines
26 KiB
C
836 lines
26 KiB
C
/* PIKA - Photo and Image Kooker Application
|
|
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
|
|
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
|
|
*
|
|
* Original copyright, applying to most contents (license remains unchanged):
|
|
* 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 "libpikamath/pikamath.h"
|
|
|
|
#include "core-types.h"
|
|
|
|
#include "pika-transform-resize.h"
|
|
#include "pika-transform-utils.h"
|
|
#include "pika-utils.h"
|
|
|
|
|
|
#define EPSILON 0.00000001
|
|
|
|
|
|
typedef struct
|
|
{
|
|
PikaVector2 a, b, c, d;
|
|
gdouble area;
|
|
gdouble aspect;
|
|
} Rectangle;
|
|
|
|
|
|
static void pika_transform_resize_adjust (const PikaVector2 *points,
|
|
gint n_points,
|
|
gint *x1,
|
|
gint *y1,
|
|
gint *x2,
|
|
gint *y2);
|
|
static void pika_transform_resize_crop (const PikaVector2 *points,
|
|
gint n_points,
|
|
gdouble aspect,
|
|
gint *x1,
|
|
gint *y1,
|
|
gint *x2,
|
|
gint *y2);
|
|
|
|
static void add_rectangle (const PikaVector2 *points,
|
|
gint n_points,
|
|
Rectangle *r,
|
|
PikaVector2 a,
|
|
PikaVector2 b,
|
|
PikaVector2 c,
|
|
PikaVector2 d);
|
|
static gboolean intersect (PikaVector2 a,
|
|
PikaVector2 b,
|
|
PikaVector2 c,
|
|
PikaVector2 d,
|
|
PikaVector2 *i);
|
|
static gboolean intersect_x (PikaVector2 a,
|
|
PikaVector2 b,
|
|
PikaVector2 c,
|
|
PikaVector2 *i);
|
|
static gboolean intersect_y (PikaVector2 a,
|
|
PikaVector2 b,
|
|
PikaVector2 c,
|
|
PikaVector2 *i);
|
|
static gboolean in_poly (const PikaVector2 *points,
|
|
gint n_points,
|
|
PikaVector2 p);
|
|
static gboolean point_on_border (const PikaVector2 *points,
|
|
gint n_points,
|
|
PikaVector2 p);
|
|
|
|
static void find_two_point_rectangle (Rectangle *r,
|
|
const PikaVector2 *points,
|
|
gint n_points,
|
|
gint p);
|
|
static void find_three_point_rectangle_corner (Rectangle *r,
|
|
const PikaVector2 *points,
|
|
gint n_points,
|
|
gint p);
|
|
static void find_three_point_rectangle (Rectangle *r,
|
|
const PikaVector2 *points,
|
|
gint n_points,
|
|
gint p);
|
|
static void find_three_point_rectangle_triangle (Rectangle *r,
|
|
const PikaVector2 *points,
|
|
gint n_points,
|
|
gint p);
|
|
static void find_maximum_aspect_rectangle (Rectangle *r,
|
|
const PikaVector2 *points,
|
|
gint n_points,
|
|
gint p);
|
|
|
|
|
|
/*
|
|
* This function wants to be passed the inverse transformation matrix!!
|
|
*/
|
|
gboolean
|
|
pika_transform_resize_boundary (const PikaMatrix3 *inv,
|
|
PikaTransformResize resize,
|
|
gdouble u1,
|
|
gdouble v1,
|
|
gdouble u2,
|
|
gdouble v2,
|
|
gint *x1,
|
|
gint *y1,
|
|
gint *x2,
|
|
gint *y2)
|
|
{
|
|
PikaVector2 bounds[4];
|
|
PikaVector2 points[5];
|
|
gint n_points;
|
|
gboolean valid;
|
|
gint i;
|
|
|
|
g_return_val_if_fail (inv != NULL, FALSE);
|
|
|
|
/* initialize with the original boundary */
|
|
*x1 = floor (u1);
|
|
*y1 = floor (v1);
|
|
*x2 = ceil (u2);
|
|
*y2 = ceil (v2);
|
|
|
|
/* if clipping then just return the original rectangle */
|
|
if (resize == PIKA_TRANSFORM_RESIZE_CLIP)
|
|
return TRUE;
|
|
|
|
bounds[0] = (PikaVector2) { u1, v1 };
|
|
bounds[1] = (PikaVector2) { u2, v1 };
|
|
bounds[2] = (PikaVector2) { u2, v2 };
|
|
bounds[3] = (PikaVector2) { u1, v2 };
|
|
|
|
pika_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 = (isfinite (points[i].x) && isfinite (points[i].y));
|
|
|
|
if (! valid)
|
|
{
|
|
/* since there is no sensible way to deal with this, just do the same as
|
|
* with PIKA_TRANSFORM_RESIZE_CLIP: return
|
|
*/
|
|
return FALSE;
|
|
}
|
|
|
|
switch (resize)
|
|
{
|
|
case PIKA_TRANSFORM_RESIZE_ADJUST:
|
|
/* return smallest rectangle (with sides parallel to x- and y-axis)
|
|
* that surrounds the new points */
|
|
pika_transform_resize_adjust (points, n_points,
|
|
x1, y1, x2, y2);
|
|
break;
|
|
|
|
case PIKA_TRANSFORM_RESIZE_CROP:
|
|
pika_transform_resize_crop (points, n_points,
|
|
0.0,
|
|
x1, y1, x2, y2);
|
|
break;
|
|
|
|
case PIKA_TRANSFORM_RESIZE_CROP_WITH_ASPECT:
|
|
pika_transform_resize_crop (points, n_points,
|
|
(u2 - u1) / (v2 - v1),
|
|
x1, y1, x2, y2);
|
|
break;
|
|
|
|
case PIKA_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)++;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* this calculates the smallest rectangle (with sides parallel to x- and
|
|
* y-axis) that contains the points d1 to d4
|
|
*/
|
|
static void
|
|
pika_transform_resize_adjust (const PikaVector2 *points,
|
|
gint n_points,
|
|
gint *x1,
|
|
gint *y1,
|
|
gint *x2,
|
|
gint *y2)
|
|
{
|
|
PikaVector2 top_left;
|
|
PikaVector2 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
|
|
pika_transform_resize_crop (const PikaVector2 *orig_points,
|
|
gint n_points,
|
|
gdouble aspect,
|
|
gint *x1,
|
|
gint *y1,
|
|
gint *x2,
|
|
gint *y2)
|
|
{
|
|
PikaVector2 points[5];
|
|
Rectangle r;
|
|
PikaVector2 t,a;
|
|
gint i, j;
|
|
gint min;
|
|
|
|
memcpy (points, orig_points, sizeof (PikaVector2) * 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 pika_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 */
|
|
pika_transform_resize_adjust (orig_points, n_points,
|
|
x1, y1, x2, y2);
|
|
g_printerr ("no rectangle found by algorithm, no cropping done\n");
|
|
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 PikaVector2 *points,
|
|
gint n_points,
|
|
gint p)
|
|
{
|
|
PikaVector2 a = points[p % n_points]; /* 0 1 2 3 */
|
|
PikaVector2 b = points[(p + 1) % n_points]; /* 1 2 3 0 */
|
|
PikaVector2 c = points[(p + 2) % n_points]; /* 2 3 0 1 */
|
|
PikaVector2 d = points[(p + 3) % n_points]; /* 3 0 1 2 */
|
|
PikaVector2 i1; /* intersection point */
|
|
PikaVector2 i2; /* intersection point */
|
|
PikaVector2 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 PikaVector2 *points,
|
|
gint n_points,
|
|
gint p)
|
|
{
|
|
PikaVector2 a = points[p % n_points]; /* 0 1 2 3 */
|
|
PikaVector2 b = points[(p + 1) % n_points]; /* 1 2 3 0 */
|
|
PikaVector2 c = points[(p + 2) % n_points]; /* 2 3 0 2 */
|
|
PikaVector2 d = points[(p + 3) % n_points]; /* 3 0 2 1 */
|
|
PikaVector2 i1; /* intersection point */
|
|
PikaVector2 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 PikaVector2 *points,
|
|
gint n_points,
|
|
gint p)
|
|
{
|
|
PikaVector2 a = points[ p % n_points]; /* 0 1 2 3 */
|
|
PikaVector2 b = points[(p + 1) % n_points]; /* 1 2 3 0 */
|
|
PikaVector2 c = points[(p + 2) % n_points]; /* 2 3 0 1 */
|
|
PikaVector2 d = points[(p + 3) % n_points]; /* 3 0 1 2 */
|
|
PikaVector2 i1; /* intersection point */
|
|
PikaVector2 i2; /* intersection point */
|
|
PikaVector2 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 PikaVector2 *points,
|
|
gint n_points,
|
|
gint p)
|
|
{
|
|
PikaVector2 a = points[p % n_points]; /* 0 1 2 3 */
|
|
PikaVector2 b = points[(p + 1) % n_points]; /* 1 2 3 0 */
|
|
PikaVector2 c = points[(p + 2) % n_points]; /* 2 3 0 1 */
|
|
PikaVector2 d = points[(p + 3) % n_points]; /* 3 0 1 2 */
|
|
PikaVector2 i1; /* intersection point */
|
|
PikaVector2 i2; /* intersection point */
|
|
PikaVector2 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 PikaVector2 *points,
|
|
gint n_points,
|
|
gint p)
|
|
{
|
|
PikaVector2 a = points[ p % n_points]; /* 0 1 2 3 */
|
|
PikaVector2 b = points[(p + 1) % n_points]; /* 1 2 3 0 */
|
|
PikaVector2 c = points[(p + 2) % n_points]; /* 2 3 0 1 */
|
|
PikaVector2 d = points[(p + 3) % n_points]; /* 3 0 1 2 */
|
|
PikaVector2 i1; /* intersection point */
|
|
PikaVector2 i2; /* intersection point */
|
|
PikaVector2 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 PikaVector2 *points,
|
|
gint n_points,
|
|
PikaVector2 p)
|
|
{
|
|
PikaVector2 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 PikaVector2 *points,
|
|
gint n_points,
|
|
PikaVector2 p)
|
|
{
|
|
gint i;
|
|
|
|
for (i = 0; i <= n_points; i++)
|
|
{
|
|
PikaVector2 a = points[i % n_points];
|
|
PikaVector2 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 (PikaVector2 a,
|
|
PikaVector2 b,
|
|
PikaVector2 c,
|
|
PikaVector2 d,
|
|
PikaVector2 *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 (PikaVector2 a,
|
|
PikaVector2 b,
|
|
PikaVector2 c,
|
|
PikaVector2 *i)
|
|
{
|
|
PikaVector2 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 (PikaVector2 a,
|
|
PikaVector2 b,
|
|
PikaVector2 c,
|
|
PikaVector2 *i)
|
|
{
|
|
PikaVector2 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 PikaVector2 *points,
|
|
gint n_points,
|
|
Rectangle *r,
|
|
PikaVector2 a,
|
|
PikaVector2 b,
|
|
PikaVector2 c,
|
|
PikaVector2 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;
|
|
}
|
|
}
|
|
}
|