/* 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 Spencer Kimball and Peter Mattis * * pikatoolsheargrid.c * Copyright (C) 2017 Michael Natterer * * 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 . */ #include "config.h" #include #include #include "libpikabase/pikabase.h" #include "libpikamath/pikamath.h" #include "display-types.h" #include "core/pika-transform-utils.h" #include "core/pika-utils.h" #include "pikadisplayshell.h" #include "pikatoolsheargrid.h" #include "pika-intl.h" enum { PROP_0, PROP_ORIENTATION, PROP_SHEAR_X, PROP_SHEAR_Y }; struct _PikaToolShearGridPrivate { PikaOrientationType orientation; gdouble shear_x; gdouble shear_y; gdouble last_x; gdouble last_y; }; /* local function prototypes */ static void pika_tool_shear_grid_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void pika_tool_shear_grid_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static gint pika_tool_shear_grid_button_press (PikaToolWidget *widget, const PikaCoords *coords, guint32 time, GdkModifierType state, PikaButtonPressType press_type); static void pika_tool_shear_grid_motion (PikaToolWidget *widget, const PikaCoords *coords, guint32 time, GdkModifierType state); G_DEFINE_TYPE_WITH_PRIVATE (PikaToolShearGrid, pika_tool_shear_grid, PIKA_TYPE_TOOL_TRANSFORM_GRID) #define parent_class pika_tool_shear_grid_parent_class static void pika_tool_shear_grid_class_init (PikaToolShearGridClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); PikaToolWidgetClass *widget_class = PIKA_TOOL_WIDGET_CLASS (klass); object_class->set_property = pika_tool_shear_grid_set_property; object_class->get_property = pika_tool_shear_grid_get_property; widget_class->button_press = pika_tool_shear_grid_button_press; widget_class->motion = pika_tool_shear_grid_motion; g_object_class_install_property (object_class, PROP_ORIENTATION, g_param_spec_enum ("orientation", NULL, NULL, PIKA_TYPE_ORIENTATION_TYPE, PIKA_ORIENTATION_UNKNOWN, PIKA_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_class_install_property (object_class, PROP_SHEAR_X, g_param_spec_double ("shear-x", NULL, NULL, -PIKA_MAX_IMAGE_SIZE, PIKA_MAX_IMAGE_SIZE, 0.0, PIKA_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_object_class_install_property (object_class, PROP_SHEAR_Y, g_param_spec_double ("shear-y", NULL, NULL, -PIKA_MAX_IMAGE_SIZE, PIKA_MAX_IMAGE_SIZE, 0.0, PIKA_PARAM_READWRITE | G_PARAM_CONSTRUCT)); } static void pika_tool_shear_grid_init (PikaToolShearGrid *grid) { grid->private = pika_tool_shear_grid_get_instance_private (grid); g_object_set (grid, "inside-function", PIKA_TRANSFORM_FUNCTION_SHEAR, "outside-function", PIKA_TRANSFORM_FUNCTION_SHEAR, "use-corner-handles", FALSE, "use-perspective-handles", FALSE, "use-side-handles", FALSE, "use-shear-handles", FALSE, "use-center-handle", FALSE, "use-pivot-handle", FALSE, NULL); } static void pika_tool_shear_grid_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { PikaToolShearGrid *grid = PIKA_TOOL_SHEAR_GRID (object); PikaToolShearGridPrivate *private = grid->private; switch (property_id) { case PROP_ORIENTATION: private->orientation = g_value_get_enum (value); break; case PROP_SHEAR_X: private->shear_x = g_value_get_double (value); break; case PROP_SHEAR_Y: private->shear_y = g_value_get_double (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void pika_tool_shear_grid_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { PikaToolShearGrid *grid = PIKA_TOOL_SHEAR_GRID (object); PikaToolShearGridPrivate *private = grid->private; switch (property_id) { case PROP_ORIENTATION: g_value_set_enum (value, private->orientation); break; case PROP_SHEAR_X: g_value_set_double (value, private->shear_x); break; case PROP_SHEAR_Y: g_value_set_double (value, private->shear_y); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static gint pika_tool_shear_grid_button_press (PikaToolWidget *widget, const PikaCoords *coords, guint32 time, GdkModifierType state, PikaButtonPressType press_type) { PikaToolShearGrid *grid = PIKA_TOOL_SHEAR_GRID (widget); PikaToolShearGridPrivate *private = grid->private; private->last_x = coords->x; private->last_y = coords->y; return 1; } void pika_tool_shear_grid_motion (PikaToolWidget *widget, const PikaCoords *coords, guint32 time, GdkModifierType state) { PikaToolShearGrid *grid = PIKA_TOOL_SHEAR_GRID (widget); PikaToolShearGridPrivate *private = grid->private; gdouble diffx = coords->x - private->last_x; gdouble diffy = coords->y - private->last_y; gdouble amount = 0.0; PikaMatrix3 transform; PikaMatrix3 *t; gdouble x1, y1; gdouble x2, y2; gdouble tx1, ty1; gdouble tx2, ty2; gdouble tx3, ty3; gdouble tx4, ty4; gdouble current_x; gdouble current_y; g_object_get (widget, "transform", &t, "x1", &x1, "y1", &y1, "x2", &x2, "y2", &y2, NULL); pika_matrix3_transform_point (t, x1, y1, &tx1, &ty1); pika_matrix3_transform_point (t, x2, y1, &tx2, &ty2); pika_matrix3_transform_point (t, x1, y2, &tx3, &ty3); pika_matrix3_transform_point (t, x2, y2, &tx4, &ty4); g_free (t); current_x = coords->x; current_y = coords->y; diffx = current_x - private->last_x; diffy = current_y - private->last_y; /* If we haven't yet decided on which way to control shearing * decide using the maximum differential */ if (private->orientation == PIKA_ORIENTATION_UNKNOWN) { #define MIN_MOVE 5 if (ABS (diffx) > MIN_MOVE || ABS (diffy) > MIN_MOVE) { if (ABS (diffx) > ABS (diffy)) { private->orientation = PIKA_ORIENTATION_HORIZONTAL; private->shear_x = 0.0; } else { private->orientation = PIKA_ORIENTATION_VERTICAL; private->shear_y = 0.0; } } /* set the current coords to the last ones */ else { current_x = private->last_x; current_y = private->last_y; } } /* if the direction is known, keep track of the magnitude */ if (private->orientation == PIKA_ORIENTATION_HORIZONTAL) { if (current_y > (ty1 + ty3) / 2) private->shear_x += diffx; else private->shear_x -= diffx; amount = private->shear_x; } else if (private->orientation == PIKA_ORIENTATION_VERTICAL) { if (current_x > (tx1 + tx2) / 2) private->shear_y += diffy; else private->shear_y -= diffy; amount = private->shear_y; } pika_matrix3_identity (&transform); pika_transform_matrix_shear (&transform, x1, y1, x2 - x1, y2 - y1, private->orientation, amount); g_object_set (widget, "transform", &transform, "orientation", private->orientation, "shear-x", private->shear_x, "shear_y", private->shear_y, NULL); private->last_x = current_x; private->last_y = current_y; } /* public functions */ PikaToolWidget * pika_tool_shear_grid_new (PikaDisplayShell *shell, gdouble x1, gdouble y1, gdouble x2, gdouble y2, PikaOrientationType orientation, gdouble shear_x, gdouble shear_y) { PikaMatrix3 transform; gdouble amount; g_return_val_if_fail (PIKA_IS_DISPLAY_SHELL (shell), NULL); if (orientation == PIKA_ORIENTATION_HORIZONTAL) amount = shear_x; else amount = shear_y; pika_matrix3_identity (&transform); pika_transform_matrix_shear (&transform, x1, y1, x2 - x1, y2 - y1, orientation, amount); return g_object_new (PIKA_TYPE_TOOL_SHEAR_GRID, "shell", shell, "transform", &transform, "x1", x1, "y1", y1, "x2", x2, "y2", y2, "orientation", orientation, "shear-x", shear_x, "shear-y", shear_y, NULL); }