/* 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. */ #include #include "appenv.h" #include "drawable.h" #include "gdisplay.h" #include "gimage_mask.h" #include "info_dialog.h" #include "palette.h" #include "rotate_tool.h" #include "selection.h" #include "tools.h" #include "transform_core.h" #include "transform_tool.h" #include "undo.h" #include "tile_manager_pvt.h" #include "libgimp/gimpintl.h" #include "libgimp/gimpsizeentry.h" #ifndef M_PI #define M_PI 3.14159265358979323846 #endif /* M_PI */ /* index into trans_info array */ #define ANGLE 0 #define REAL_ANGLE 1 #define CENTER_X 2 #define CENTER_Y 3 #define EPSILON 0.018 /* ~ 1 degree */ #define FIFTEEN_DEG (M_PI / 12.0) /* variables local to this file */ static gfloat angle_val; static gfloat center_vals[2]; /* needed for size update */ static GtkWidget *sizeentry; /* forward function declarations */ static void * rotate_tool_recalc (Tool *, void *); static void rotate_tool_motion (Tool *, void *); static void rotate_info_update (Tool *); /* callback functions for the info dialog entries */ static void rotate_angle_changed (GtkWidget *entry, gpointer data); static void rotate_center_changed (GtkWidget *entry, gpointer data); void * rotate_tool_transform (Tool *tool, gpointer gdisp_ptr, int state) { TransformCore *transform_core; GDisplay *gdisp; GtkWidget *widget; GtkWidget *spinbutton2; transform_core = (TransformCore *) tool->private; gdisp = (GDisplay *) gdisp_ptr; switch (state) { case INIT : if (!transform_info) { transform_info = info_dialog_new (_("Rotation Information")); widget = info_dialog_add_spinbutton (transform_info, _("Angle:"), &angle_val, -180, 180, 1, 15, 1, 1, 2, (GtkSignalFunc) rotate_angle_changed, tool); gtk_spin_button_set_wrap (GTK_SPIN_BUTTON (widget), TRUE); widget = info_dialog_add_scale (transform_info, "", &angle_val, -180, 180, 0.01, 0.1, 1, -1, (GtkSignalFunc) rotate_angle_changed, tool); gtk_widget_set_usize (widget, 180, 0); spinbutton2 = info_dialog_add_spinbutton (transform_info, _("Center X:"), NULL, -1, 1, 1, 10, 1, 1, 2, NULL, NULL); sizeentry = info_dialog_add_sizeentry (transform_info, _("Y:"), center_vals, 1, gdisp->dot_for_dot ? UNIT_PIXEL : gdisp->gimage->unit, "%a", TRUE, TRUE, FALSE, GIMP_SIZE_ENTRY_UPDATE_SIZE, rotate_center_changed, tool); gimp_size_entry_add_field (GIMP_SIZE_ENTRY (sizeentry), GTK_SPIN_BUTTON (spinbutton2), NULL); gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (sizeentry), 0, -4096, 4096 + gdisp->gimage->width); gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (sizeentry), 0, gdisp->gimage->xresolution, FALSE); gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (sizeentry), 0, center_vals[0]); gimp_size_entry_set_refval_boundaries (GIMP_SIZE_ENTRY (sizeentry), 1, -4096, 4096 + gdisp->gimage->height); gimp_size_entry_set_resolution (GIMP_SIZE_ENTRY (sizeentry), 1, gdisp->gimage->yresolution, FALSE); gimp_size_entry_set_refval (GIMP_SIZE_ENTRY (sizeentry), 1, center_vals[1]); gtk_table_set_row_spacing (GTK_TABLE (transform_info->info_table), 1, 6); gtk_table_set_row_spacing (GTK_TABLE (transform_info->info_table), 2, 0); } gtk_widget_set_sensitive (GTK_WIDGET (transform_info->shell), TRUE); gimp_size_entry_set_size (GIMP_SIZE_ENTRY (sizeentry), 0, transform_core->x1, transform_core->x2); gimp_size_entry_set_size (GIMP_SIZE_ENTRY (sizeentry), 1, transform_core->y1, transform_core->y2); transform_core->trans_info[ANGLE] = 0.0; transform_core->trans_info[REAL_ANGLE] = 0.0; transform_core->trans_info[CENTER_X] = (transform_core->x1 + transform_core->x2) / 2; transform_core->trans_info[CENTER_Y] = (transform_core->y1 + transform_core->y2) / 2; return NULL; break; case MOTION : rotate_tool_motion (tool, gdisp_ptr); return (rotate_tool_recalc (tool, gdisp_ptr)); break; case RECALC : return (rotate_tool_recalc (tool, gdisp_ptr)); break; case FINISH : gtk_widget_set_sensitive (GTK_WIDGET (transform_info->shell), FALSE); return rotate_tool_rotate (gdisp->gimage, gimage_active_drawable (gdisp->gimage), gdisp, transform_core->trans_info[ANGLE], transform_core->original, transform_tool_smoothing (), transform_core->transform); break; } return NULL; } Tool * tools_new_rotate_tool () { Tool * tool; TransformCore * private; tool = transform_core_new (ROTATE, INTERACTIVE); private = tool->private; /* set the rotation specific transformation attributes */ private->trans_func = rotate_tool_transform; private->trans_info[ANGLE] = 0.0; private->trans_info[REAL_ANGLE] = 0.0; private->trans_info[CENTER_X] = (private->x1 + private->x2) / 2; private->trans_info[CENTER_Y] = (private->y1 + private->y2) / 2; /* assemble the transformation matrix */ gimp_matrix_identity (private->transform); return tool; } void tools_free_rotate_tool (Tool *tool) { transform_core_free (tool); } static void rotate_info_update (Tool *tool) { TransformCore * transform_core; double angle; int cx, cy; transform_core = (TransformCore *) tool->private; angle = (transform_core->trans_info[ANGLE] * 180.0) / M_PI; cx = transform_core->cx; cy = transform_core->cy; angle_val = angle; center_vals[0] = cx; center_vals[1] = cy; info_dialog_update (transform_info); info_dialog_popup (transform_info); } static void rotate_angle_changed (GtkWidget *w, gpointer data) { Tool *tool; GDisplay *gdisp; TransformCore *transform_core; double value; tool = (Tool *)data; if (tool) { gdisp = (GDisplay *) tool->gdisp_ptr; transform_core = (TransformCore *) tool->private; value = GTK_ADJUSTMENT (w)->value * M_PI / 180.0; if (value != transform_core->trans_info[ANGLE]) { draw_core_pause (transform_core->core, tool); transform_core->trans_info[ANGLE] = value; rotate_tool_recalc (tool, gdisp); draw_core_resume (transform_core->core, tool); } } } static void rotate_center_changed (GtkWidget *w, gpointer data) { Tool *tool; GDisplay *gdisp; TransformCore *transform_core; int cx; int cy; tool = (Tool *)data; if (tool) { gdisp = (GDisplay *) tool->gdisp_ptr; transform_core = (TransformCore *) tool->private; cx = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (w), 0); cy = gimp_size_entry_get_refval (GIMP_SIZE_ENTRY (w), 1); if ((cx != transform_core->cx) || (cy != transform_core->cy)) { draw_core_pause (transform_core->core, tool); transform_core->cx = cx; transform_core->cy = cy; rotate_tool_recalc (tool, gdisp); draw_core_resume (transform_core->core, tool); } } } static void rotate_tool_motion (Tool *tool, void *gdisp_ptr) { TransformCore * transform_core; double angle1, angle2, angle; double cx, cy; double x1, y1, x2, y2; transform_core = (TransformCore *) tool->private; if (transform_core->function == HANDLE_CENTER) { transform_core->cx = transform_core->curx; transform_core->cy = transform_core->cury; return; } cx = transform_core->cx; cy = transform_core->cy; x1 = transform_core->curx - cx; x2 = transform_core->lastx - cx; y1 = cy - transform_core->cury; y2 = cy - transform_core->lasty; /* find the first angle */ angle1 = atan2 (y1, x1); /* find the angle */ angle2 = atan2 (y2, x2); angle = angle2 - angle1; if (angle > M_PI || angle < -M_PI) angle = angle2 - ((angle1 < 0) ? 2*M_PI + angle1 : angle1 - 2*M_PI); /* increment the transform tool's angle */ transform_core->trans_info[REAL_ANGLE] += angle; /* limit the angle to between 0 and 360 degrees */ if (transform_core->trans_info[REAL_ANGLE] < - M_PI) transform_core->trans_info[REAL_ANGLE] = 2 * M_PI - transform_core->trans_info[REAL_ANGLE]; else if (transform_core->trans_info[REAL_ANGLE] > M_PI) transform_core->trans_info[REAL_ANGLE] = transform_core->trans_info[REAL_ANGLE] - 2 * M_PI; /* constrain the angle to 15-degree multiples if ctrl is held down */ if (transform_core->state & GDK_CONTROL_MASK) transform_core->trans_info[ANGLE] = FIFTEEN_DEG * (int) ((transform_core->trans_info[REAL_ANGLE] + FIFTEEN_DEG / 2.0) / FIFTEEN_DEG); else transform_core->trans_info[ANGLE] = transform_core->trans_info[REAL_ANGLE]; } static void * rotate_tool_recalc (Tool *tool, void *gdisp_ptr) { TransformCore * transform_core; GDisplay * gdisp; double cx, cy; gdisp = (GDisplay *) tool->gdisp_ptr; transform_core = (TransformCore *) tool->private; cx = transform_core->cx; cy = transform_core->cy; /* assemble the transformation matrix */ gimp_matrix_identity (transform_core->transform); gimp_matrix_translate (transform_core->transform, -cx, -cy); gimp_matrix_rotate (transform_core->transform, transform_core->trans_info[ANGLE]); gimp_matrix_translate (transform_core->transform, +cx, +cy); /* transform the bounding box */ transform_bounding_box (tool); /* update the information dialog */ rotate_info_update (tool); return (void *) 1; } /* This procedure returns a valid pointer to a new selection if the * requested angle is a multiple of 90 degrees... */ void * rotate_tool_rotate (GImage *gimage, GimpDrawable *drawable, GDisplay *gdisp, double angle, TileManager *float_tiles, int interpolation, GimpMatrix matrix) { void *ret; gimp_progress *progress; progress = progress_start (gdisp, _("Rotating..."), FALSE, NULL, NULL); ret = transform_core_do (gimage, drawable, float_tiles, interpolation, matrix, progress? progress_update_and_flush:NULL, progress); if (progress) progress_end (progress); return ret; }