diff options
Diffstat (limited to 'app/display/gimpcanvasitem-utils.c')
-rw-r--r-- | app/display/gimpcanvasitem-utils.c | 474 |
1 files changed, 474 insertions, 0 deletions
diff --git a/app/display/gimpcanvasitem-utils.c b/app/display/gimpcanvasitem-utils.c new file mode 100644 index 0000000..4c23592 --- /dev/null +++ b/app/display/gimpcanvasitem-utils.c @@ -0,0 +1,474 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpcanvasitem-utils.c + * Copyright (C) 2010 Michael Natterer <mitch@gimp.org> + * + * 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 <gegl.h> +#include <gtk/gtk.h> + +#include "libgimpmath/gimpmath.h" + +#include "display-types.h" + +#include "core/gimpimage.h" + +#include "vectors/gimpanchor.h" +#include "vectors/gimpbezierstroke.h" +#include "vectors/gimpvectors.h" + +#include "gimpcanvasitem.h" +#include "gimpcanvasitem-utils.h" +#include "gimpdisplay.h" +#include "gimpdisplayshell.h" +#include "gimpdisplayshell-transform.h" + + +gboolean +gimp_canvas_item_on_handle (GimpCanvasItem *item, + gdouble x, + gdouble y, + GimpHandleType type, + gdouble handle_x, + gdouble handle_y, + gint width, + gint height, + GimpHandleAnchor anchor) +{ + GimpDisplayShell *shell; + gdouble tx, ty; + gdouble handle_tx, handle_ty; + + g_return_val_if_fail (GIMP_IS_CANVAS_ITEM (item), FALSE); + + shell = gimp_canvas_item_get_shell (item); + + gimp_display_shell_zoom_xy_f (shell, + x, y, + &tx, &ty); + gimp_display_shell_zoom_xy_f (shell, + handle_x, handle_y, + &handle_tx, &handle_ty); + + switch (type) + { + case GIMP_HANDLE_SQUARE: + case GIMP_HANDLE_FILLED_SQUARE: + case GIMP_HANDLE_CROSS: + case GIMP_HANDLE_CROSSHAIR: + gimp_canvas_item_shift_to_north_west (anchor, + handle_tx, handle_ty, + width, height, + &handle_tx, &handle_ty); + + return (tx == CLAMP (tx, handle_tx, handle_tx + width) && + ty == CLAMP (ty, handle_ty, handle_ty + height)); + + case GIMP_HANDLE_CIRCLE: + case GIMP_HANDLE_FILLED_CIRCLE: + gimp_canvas_item_shift_to_center (anchor, + handle_tx, handle_ty, + width, height, + &handle_tx, &handle_ty); + + /* FIXME */ + if (width != height) + width = (width + height) / 2; + + width /= 2; + + return ((SQR (handle_tx - tx) + SQR (handle_ty - ty)) < SQR (width)); + + default: + g_warning ("%s: invalid handle type %d", G_STRFUNC, type); + break; + } + + return FALSE; +} + +gboolean +gimp_canvas_item_on_vectors_handle (GimpCanvasItem *item, + GimpVectors *vectors, + const GimpCoords *coord, + gint width, + gint height, + GimpAnchorType preferred, + gboolean exclusive, + GimpAnchor **ret_anchor, + GimpStroke **ret_stroke) +{ + GimpStroke *stroke = NULL; + GimpStroke *pref_stroke = NULL; + GimpAnchor *anchor = NULL; + GimpAnchor *pref_anchor = NULL; + gdouble dx, dy; + gdouble pref_mindist = -1; + gdouble mindist = -1; + + g_return_val_if_fail (GIMP_IS_CANVAS_ITEM (item), FALSE); + g_return_val_if_fail (GIMP_IS_VECTORS (vectors), FALSE); + g_return_val_if_fail (coord != NULL, FALSE); + + if (ret_anchor) *ret_anchor = NULL; + if (ret_stroke) *ret_stroke = NULL; + + while ((stroke = gimp_vectors_stroke_get_next (vectors, stroke))) + { + GList *anchor_list; + GList *list; + + anchor_list = g_list_concat (gimp_stroke_get_draw_anchors (stroke), + gimp_stroke_get_draw_controls (stroke)); + + for (list = anchor_list; list; list = g_list_next (list)) + { + dx = coord->x - GIMP_ANCHOR (list->data)->position.x; + dy = coord->y - GIMP_ANCHOR (list->data)->position.y; + + if (mindist < 0 || mindist > dx * dx + dy * dy) + { + mindist = dx * dx + dy * dy; + anchor = GIMP_ANCHOR (list->data); + + if (ret_stroke) + *ret_stroke = stroke; + } + + if ((pref_mindist < 0 || pref_mindist > dx * dx + dy * dy) && + GIMP_ANCHOR (list->data)->type == preferred) + { + pref_mindist = dx * dx + dy * dy; + pref_anchor = GIMP_ANCHOR (list->data); + pref_stroke = stroke; + } + } + + g_list_free (anchor_list); + } + + /* If the data passed into ret_anchor is a preferred anchor, return it. */ + if (ret_anchor && *ret_anchor && + gimp_canvas_item_on_handle (item, + coord->x, + coord->y, + GIMP_HANDLE_CIRCLE, + (*ret_anchor)->position.x, + (*ret_anchor)->position.y, + width, height, + GIMP_HANDLE_ANCHOR_CENTER) && + (*ret_anchor)->type == preferred) + { + if (ret_stroke) *ret_stroke = pref_stroke; + + return TRUE; + } + + if (pref_anchor && gimp_canvas_item_on_handle (item, + coord->x, + coord->y, + GIMP_HANDLE_CIRCLE, + pref_anchor->position.x, + pref_anchor->position.y, + width, height, + GIMP_HANDLE_ANCHOR_CENTER)) + { + if (ret_anchor) *ret_anchor = pref_anchor; + if (ret_stroke) *ret_stroke = pref_stroke; + + return TRUE; + } + else if (!exclusive && anchor && + gimp_canvas_item_on_handle (item, + coord->x, + coord->y, + GIMP_HANDLE_CIRCLE, + anchor->position.x, + anchor->position.y, + width, height, + GIMP_HANDLE_ANCHOR_CENTER)) + { + if (ret_anchor) + *ret_anchor = anchor; + + /* *ret_stroke already set correctly. */ + return TRUE; + } + + if (ret_anchor) + *ret_anchor = NULL; + if (ret_stroke) + *ret_stroke = NULL; + + return FALSE; +} + +gboolean +gimp_canvas_item_on_vectors_curve (GimpCanvasItem *item, + GimpVectors *vectors, + const GimpCoords *coord, + gint width, + gint height, + GimpCoords *ret_coords, + gdouble *ret_pos, + GimpAnchor **ret_segment_start, + GimpAnchor **ret_segment_end, + GimpStroke **ret_stroke) +{ + GimpStroke *stroke = NULL; + GimpAnchor *segment_start; + GimpAnchor *segment_end; + GimpCoords min_coords = GIMP_COORDS_DEFAULT_VALUES; + GimpCoords cur_coords; + gdouble min_dist, cur_dist, cur_pos; + + g_return_val_if_fail (GIMP_IS_CANVAS_ITEM (item), FALSE); + g_return_val_if_fail (GIMP_IS_VECTORS (vectors), FALSE); + g_return_val_if_fail (coord != NULL, FALSE); + + if (ret_coords) *ret_coords = *coord; + if (ret_pos) *ret_pos = -1.0; + if (ret_segment_start) *ret_segment_start = NULL; + if (ret_segment_end) *ret_segment_end = NULL; + if (ret_stroke) *ret_stroke = NULL; + + min_dist = -1.0; + + while ((stroke = gimp_vectors_stroke_get_next (vectors, stroke))) + { + cur_dist = gimp_stroke_nearest_point_get (stroke, coord, 1.0, + &cur_coords, + &segment_start, + &segment_end, + &cur_pos); + + if (cur_dist >= 0 && (min_dist < 0 || cur_dist < min_dist)) + { + min_dist = cur_dist; + min_coords = cur_coords; + + if (ret_coords) *ret_coords = cur_coords; + if (ret_pos) *ret_pos = cur_pos; + if (ret_segment_start) *ret_segment_start = segment_start; + if (ret_segment_end) *ret_segment_end = segment_end; + if (ret_stroke) *ret_stroke = stroke; + } + } + + if (min_dist >= 0 && + gimp_canvas_item_on_handle (item, + coord->x, + coord->y, + GIMP_HANDLE_CIRCLE, + min_coords.x, + min_coords.y, + width, height, + GIMP_HANDLE_ANCHOR_CENTER)) + { + return TRUE; + } + + return FALSE; +} + +gboolean +gimp_canvas_item_on_vectors (GimpCanvasItem *item, + const GimpCoords *coords, + gint width, + gint height, + GimpCoords *ret_coords, + gdouble *ret_pos, + GimpAnchor **ret_segment_start, + GimpAnchor **ret_segment_end, + GimpStroke **ret_stroke, + GimpVectors **ret_vectors) +{ + GimpDisplayShell *shell; + GimpImage *image; + GList *all_vectors; + GList *list; + + g_return_val_if_fail (GIMP_IS_CANVAS_ITEM (item), FALSE); + g_return_val_if_fail (coords != NULL, FALSE); + + shell = gimp_canvas_item_get_shell (item); + image = gimp_display_get_image (shell->display); + + if (ret_coords) *ret_coords = *coords; + if (ret_pos) *ret_pos = -1.0; + if (ret_segment_start) *ret_segment_start = NULL; + if (ret_segment_end) *ret_segment_end = NULL; + if (ret_stroke) *ret_stroke = NULL; + if (ret_vectors) *ret_vectors = NULL; + + all_vectors = gimp_image_get_vectors_list (image); + + for (list = all_vectors; list; list = g_list_next (list)) + { + GimpVectors *vectors = list->data; + + if (! gimp_item_get_visible (GIMP_ITEM (vectors))) + continue; + + if (gimp_canvas_item_on_vectors_curve (item, + vectors, coords, + width, height, + ret_coords, + ret_pos, + ret_segment_start, + ret_segment_end, + ret_stroke)) + { + if (ret_vectors) + *ret_vectors = vectors; + + g_list_free (all_vectors); + + return TRUE; + } + } + + g_list_free (all_vectors); + + return FALSE; +} + +void +gimp_canvas_item_shift_to_north_west (GimpHandleAnchor anchor, + gdouble x, + gdouble y, + gint width, + gint height, + gdouble *shifted_x, + gdouble *shifted_y) +{ + switch (anchor) + { + case GIMP_HANDLE_ANCHOR_CENTER: + x -= width / 2; + y -= height / 2; + break; + + case GIMP_HANDLE_ANCHOR_NORTH: + x -= width / 2; + break; + + case GIMP_HANDLE_ANCHOR_NORTH_WEST: + /* nothing, this is the default */ + break; + + case GIMP_HANDLE_ANCHOR_NORTH_EAST: + x -= width; + break; + + case GIMP_HANDLE_ANCHOR_SOUTH: + x -= width / 2; + y -= height; + break; + + case GIMP_HANDLE_ANCHOR_SOUTH_WEST: + y -= height; + break; + + case GIMP_HANDLE_ANCHOR_SOUTH_EAST: + x -= width; + y -= height; + break; + + case GIMP_HANDLE_ANCHOR_WEST: + y -= height / 2; + break; + + case GIMP_HANDLE_ANCHOR_EAST: + x -= width; + y -= height / 2; + break; + + default: + break; + } + + if (shifted_x) + *shifted_x = x; + + if (shifted_y) + *shifted_y = y; +} + +void +gimp_canvas_item_shift_to_center (GimpHandleAnchor anchor, + gdouble x, + gdouble y, + gint width, + gint height, + gdouble *shifted_x, + gdouble *shifted_y) +{ + switch (anchor) + { + case GIMP_HANDLE_ANCHOR_CENTER: + /* nothing, this is the default */ + break; + + case GIMP_HANDLE_ANCHOR_NORTH: + y += height / 2; + break; + + case GIMP_HANDLE_ANCHOR_NORTH_WEST: + x += width / 2; + y += height / 2; + break; + + case GIMP_HANDLE_ANCHOR_NORTH_EAST: + x -= width / 2; + y += height / 2; + break; + + case GIMP_HANDLE_ANCHOR_SOUTH: + y -= height / 2; + break; + + case GIMP_HANDLE_ANCHOR_SOUTH_WEST: + x += width / 2; + y -= height / 2; + break; + + case GIMP_HANDLE_ANCHOR_SOUTH_EAST: + x -= width / 2; + y -= height / 2; + break; + + case GIMP_HANDLE_ANCHOR_WEST: + x += width / 2; + break; + + case GIMP_HANDLE_ANCHOR_EAST: + x -= width / 2; + break; + + default: + break; + } + + if (shifted_x) + *shifted_x = x; + + if (shifted_y) + *shifted_y = y; +} |