/* 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 * * pikacanvasitem-utils.c * Copyright (C) 2010 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 "libpikamath/pikamath.h" #include "display-types.h" #include "core/pikaimage.h" #include "vectors/pikaanchor.h" #include "vectors/pikabezierstroke.h" #include "vectors/pikavectors.h" #include "pikacanvasitem.h" #include "pikacanvasitem-utils.h" #include "pikadisplay.h" #include "pikadisplayshell.h" #include "pikadisplayshell-transform.h" gboolean pika_canvas_item_on_handle (PikaCanvasItem *item, gdouble x, gdouble y, PikaHandleType type, gdouble handle_x, gdouble handle_y, gint width, gint height, PikaHandleAnchor anchor) { PikaDisplayShell *shell; gdouble tx, ty; gdouble handle_tx, handle_ty; g_return_val_if_fail (PIKA_IS_CANVAS_ITEM (item), FALSE); shell = pika_canvas_item_get_shell (item); pika_display_shell_zoom_xy_f (shell, x, y, &tx, &ty); pika_display_shell_zoom_xy_f (shell, handle_x, handle_y, &handle_tx, &handle_ty); switch (type) { case PIKA_HANDLE_SQUARE: case PIKA_HANDLE_FILLED_SQUARE: case PIKA_HANDLE_CROSS: case PIKA_HANDLE_CROSSHAIR: pika_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 PIKA_HANDLE_CIRCLE: case PIKA_HANDLE_FILLED_CIRCLE: pika_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 pika_canvas_item_on_vectors_handle (PikaCanvasItem *item, PikaVectors *vectors, const PikaCoords *coord, gint width, gint height, PikaAnchorType preferred, gboolean exclusive, PikaAnchor **ret_anchor, PikaStroke **ret_stroke) { PikaStroke *stroke = NULL; PikaStroke *pref_stroke = NULL; PikaAnchor *anchor = NULL; PikaAnchor *pref_anchor = NULL; gdouble dx, dy; gdouble pref_mindist = -1; gdouble mindist = -1; g_return_val_if_fail (PIKA_IS_CANVAS_ITEM (item), FALSE); g_return_val_if_fail (PIKA_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 = pika_vectors_stroke_get_next (vectors, stroke))) { GList *anchor_list; GList *list; anchor_list = g_list_concat (pika_stroke_get_draw_anchors (stroke), pika_stroke_get_draw_controls (stroke)); for (list = anchor_list; list; list = g_list_next (list)) { dx = coord->x - PIKA_ANCHOR (list->data)->position.x; dy = coord->y - PIKA_ANCHOR (list->data)->position.y; if (mindist < 0 || mindist > dx * dx + dy * dy) { mindist = dx * dx + dy * dy; anchor = PIKA_ANCHOR (list->data); if (ret_stroke) *ret_stroke = stroke; } if ((pref_mindist < 0 || pref_mindist > dx * dx + dy * dy) && PIKA_ANCHOR (list->data)->type == preferred) { pref_mindist = dx * dx + dy * dy; pref_anchor = PIKA_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 && pika_canvas_item_on_handle (item, coord->x, coord->y, PIKA_HANDLE_CIRCLE, (*ret_anchor)->position.x, (*ret_anchor)->position.y, width, height, PIKA_HANDLE_ANCHOR_CENTER) && (*ret_anchor)->type == preferred) { if (ret_stroke) *ret_stroke = pref_stroke; return TRUE; } if (pref_anchor && pika_canvas_item_on_handle (item, coord->x, coord->y, PIKA_HANDLE_CIRCLE, pref_anchor->position.x, pref_anchor->position.y, width, height, PIKA_HANDLE_ANCHOR_CENTER)) { if (ret_anchor) *ret_anchor = pref_anchor; if (ret_stroke) *ret_stroke = pref_stroke; return TRUE; } else if (!exclusive && anchor && pika_canvas_item_on_handle (item, coord->x, coord->y, PIKA_HANDLE_CIRCLE, anchor->position.x, anchor->position.y, width, height, PIKA_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 pika_canvas_item_on_vectors_curve (PikaCanvasItem *item, PikaVectors *vectors, const PikaCoords *coord, gint width, gint height, PikaCoords *ret_coords, gdouble *ret_pos, PikaAnchor **ret_segment_start, PikaAnchor **ret_segment_end, PikaStroke **ret_stroke) { PikaStroke *stroke = NULL; PikaAnchor *segment_start; PikaAnchor *segment_end; PikaCoords min_coords = PIKA_COORDS_DEFAULT_VALUES; PikaCoords cur_coords; gdouble min_dist, cur_dist, cur_pos; g_return_val_if_fail (PIKA_IS_CANVAS_ITEM (item), FALSE); g_return_val_if_fail (PIKA_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 = pika_vectors_stroke_get_next (vectors, stroke))) { cur_dist = pika_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 && pika_canvas_item_on_handle (item, coord->x, coord->y, PIKA_HANDLE_CIRCLE, min_coords.x, min_coords.y, width, height, PIKA_HANDLE_ANCHOR_CENTER)) { return TRUE; } return FALSE; } gboolean pika_canvas_item_on_vectors (PikaCanvasItem *item, const PikaCoords *coords, gint width, gint height, PikaCoords *ret_coords, gdouble *ret_pos, PikaAnchor **ret_segment_start, PikaAnchor **ret_segment_end, PikaStroke **ret_stroke, PikaVectors **ret_vectors) { PikaDisplayShell *shell; PikaImage *image; GList *all_vectors; GList *list; g_return_val_if_fail (PIKA_IS_CANVAS_ITEM (item), FALSE); g_return_val_if_fail (coords != NULL, FALSE); shell = pika_canvas_item_get_shell (item); image = pika_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 = pika_image_get_vectors_list (image); for (list = all_vectors; list; list = g_list_next (list)) { PikaVectors *vectors = list->data; if (! pika_item_get_visible (PIKA_ITEM (vectors))) continue; if (pika_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 pika_canvas_item_shift_to_north_west (PikaHandleAnchor anchor, gdouble x, gdouble y, gint width, gint height, gdouble *shifted_x, gdouble *shifted_y) { switch (anchor) { case PIKA_HANDLE_ANCHOR_CENTER: x -= width / 2; y -= height / 2; break; case PIKA_HANDLE_ANCHOR_NORTH: x -= width / 2; break; case PIKA_HANDLE_ANCHOR_NORTH_WEST: /* nothing, this is the default */ break; case PIKA_HANDLE_ANCHOR_NORTH_EAST: x -= width; break; case PIKA_HANDLE_ANCHOR_SOUTH: x -= width / 2; y -= height; break; case PIKA_HANDLE_ANCHOR_SOUTH_WEST: y -= height; break; case PIKA_HANDLE_ANCHOR_SOUTH_EAST: x -= width; y -= height; break; case PIKA_HANDLE_ANCHOR_WEST: y -= height / 2; break; case PIKA_HANDLE_ANCHOR_EAST: x -= width; y -= height / 2; break; default: break; } if (shifted_x) *shifted_x = x; if (shifted_y) *shifted_y = y; } void pika_canvas_item_shift_to_center (PikaHandleAnchor anchor, gdouble x, gdouble y, gint width, gint height, gdouble *shifted_x, gdouble *shifted_y) { switch (anchor) { case PIKA_HANDLE_ANCHOR_CENTER: /* nothing, this is the default */ break; case PIKA_HANDLE_ANCHOR_NORTH: y += height / 2; break; case PIKA_HANDLE_ANCHOR_NORTH_WEST: x += width / 2; y += height / 2; break; case PIKA_HANDLE_ANCHOR_NORTH_EAST: x -= width / 2; y += height / 2; break; case PIKA_HANDLE_ANCHOR_SOUTH: y -= height / 2; break; case PIKA_HANDLE_ANCHOR_SOUTH_WEST: x += width / 2; y -= height / 2; break; case PIKA_HANDLE_ANCHOR_SOUTH_EAST: x -= width / 2; y -= height / 2; break; case PIKA_HANDLE_ANCHOR_WEST: x += width / 2; break; case PIKA_HANDLE_ANCHOR_EAST: x -= width / 2; break; default: break; } if (shifted_x) *shifted_x = x; if (shifted_y) *shifted_y = y; }