1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * gimpcanvasitem-utils.c
5 * Copyright (C) 2010 Michael Natterer <mitch@gimp.org>
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21 #include "config.h"
22
23 #include <gegl.h>
24 #include <gtk/gtk.h>
25
26 #include "libgimpmath/gimpmath.h"
27
28 #include "display-types.h"
29
30 #include "core/gimpimage.h"
31
32 #include "vectors/gimpanchor.h"
33 #include "vectors/gimpbezierstroke.h"
34 #include "vectors/gimpvectors.h"
35
36 #include "gimpcanvasitem.h"
37 #include "gimpcanvasitem-utils.h"
38 #include "gimpdisplay.h"
39 #include "gimpdisplayshell.h"
40 #include "gimpdisplayshell-transform.h"
41
42
43 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)44 gimp_canvas_item_on_handle (GimpCanvasItem *item,
45 gdouble x,
46 gdouble y,
47 GimpHandleType type,
48 gdouble handle_x,
49 gdouble handle_y,
50 gint width,
51 gint height,
52 GimpHandleAnchor anchor)
53 {
54 GimpDisplayShell *shell;
55 gdouble tx, ty;
56 gdouble handle_tx, handle_ty;
57
58 g_return_val_if_fail (GIMP_IS_CANVAS_ITEM (item), FALSE);
59
60 shell = gimp_canvas_item_get_shell (item);
61
62 gimp_display_shell_zoom_xy_f (shell,
63 x, y,
64 &tx, &ty);
65 gimp_display_shell_zoom_xy_f (shell,
66 handle_x, handle_y,
67 &handle_tx, &handle_ty);
68
69 switch (type)
70 {
71 case GIMP_HANDLE_SQUARE:
72 case GIMP_HANDLE_FILLED_SQUARE:
73 case GIMP_HANDLE_CROSS:
74 case GIMP_HANDLE_CROSSHAIR:
75 gimp_canvas_item_shift_to_north_west (anchor,
76 handle_tx, handle_ty,
77 width, height,
78 &handle_tx, &handle_ty);
79
80 return (tx == CLAMP (tx, handle_tx, handle_tx + width) &&
81 ty == CLAMP (ty, handle_ty, handle_ty + height));
82
83 case GIMP_HANDLE_CIRCLE:
84 case GIMP_HANDLE_FILLED_CIRCLE:
85 gimp_canvas_item_shift_to_center (anchor,
86 handle_tx, handle_ty,
87 width, height,
88 &handle_tx, &handle_ty);
89
90 /* FIXME */
91 if (width != height)
92 width = (width + height) / 2;
93
94 width /= 2;
95
96 return ((SQR (handle_tx - tx) + SQR (handle_ty - ty)) < SQR (width));
97
98 default:
99 g_warning ("%s: invalid handle type %d", G_STRFUNC, type);
100 break;
101 }
102
103 return FALSE;
104 }
105
106 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)107 gimp_canvas_item_on_vectors_handle (GimpCanvasItem *item,
108 GimpVectors *vectors,
109 const GimpCoords *coord,
110 gint width,
111 gint height,
112 GimpAnchorType preferred,
113 gboolean exclusive,
114 GimpAnchor **ret_anchor,
115 GimpStroke **ret_stroke)
116 {
117 GimpStroke *stroke = NULL;
118 GimpStroke *pref_stroke = NULL;
119 GimpAnchor *anchor = NULL;
120 GimpAnchor *pref_anchor = NULL;
121 gdouble dx, dy;
122 gdouble pref_mindist = -1;
123 gdouble mindist = -1;
124
125 g_return_val_if_fail (GIMP_IS_CANVAS_ITEM (item), FALSE);
126 g_return_val_if_fail (GIMP_IS_VECTORS (vectors), FALSE);
127 g_return_val_if_fail (coord != NULL, FALSE);
128
129 if (ret_anchor) *ret_anchor = NULL;
130 if (ret_stroke) *ret_stroke = NULL;
131
132 while ((stroke = gimp_vectors_stroke_get_next (vectors, stroke)))
133 {
134 GList *anchor_list;
135 GList *list;
136
137 anchor_list = g_list_concat (gimp_stroke_get_draw_anchors (stroke),
138 gimp_stroke_get_draw_controls (stroke));
139
140 for (list = anchor_list; list; list = g_list_next (list))
141 {
142 dx = coord->x - GIMP_ANCHOR (list->data)->position.x;
143 dy = coord->y - GIMP_ANCHOR (list->data)->position.y;
144
145 if (mindist < 0 || mindist > dx * dx + dy * dy)
146 {
147 mindist = dx * dx + dy * dy;
148 anchor = GIMP_ANCHOR (list->data);
149
150 if (ret_stroke)
151 *ret_stroke = stroke;
152 }
153
154 if ((pref_mindist < 0 || pref_mindist > dx * dx + dy * dy) &&
155 GIMP_ANCHOR (list->data)->type == preferred)
156 {
157 pref_mindist = dx * dx + dy * dy;
158 pref_anchor = GIMP_ANCHOR (list->data);
159 pref_stroke = stroke;
160 }
161 }
162
163 g_list_free (anchor_list);
164 }
165
166 /* If the data passed into ret_anchor is a preferred anchor, return it. */
167 if (ret_anchor && *ret_anchor &&
168 gimp_canvas_item_on_handle (item,
169 coord->x,
170 coord->y,
171 GIMP_HANDLE_CIRCLE,
172 (*ret_anchor)->position.x,
173 (*ret_anchor)->position.y,
174 width, height,
175 GIMP_HANDLE_ANCHOR_CENTER) &&
176 (*ret_anchor)->type == preferred)
177 {
178 if (ret_stroke) *ret_stroke = pref_stroke;
179
180 return TRUE;
181 }
182
183 if (pref_anchor && gimp_canvas_item_on_handle (item,
184 coord->x,
185 coord->y,
186 GIMP_HANDLE_CIRCLE,
187 pref_anchor->position.x,
188 pref_anchor->position.y,
189 width, height,
190 GIMP_HANDLE_ANCHOR_CENTER))
191 {
192 if (ret_anchor) *ret_anchor = pref_anchor;
193 if (ret_stroke) *ret_stroke = pref_stroke;
194
195 return TRUE;
196 }
197 else if (!exclusive && anchor &&
198 gimp_canvas_item_on_handle (item,
199 coord->x,
200 coord->y,
201 GIMP_HANDLE_CIRCLE,
202 anchor->position.x,
203 anchor->position.y,
204 width, height,
205 GIMP_HANDLE_ANCHOR_CENTER))
206 {
207 if (ret_anchor)
208 *ret_anchor = anchor;
209
210 /* *ret_stroke already set correctly. */
211 return TRUE;
212 }
213
214 if (ret_anchor)
215 *ret_anchor = NULL;
216 if (ret_stroke)
217 *ret_stroke = NULL;
218
219 return FALSE;
220 }
221
222 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)223 gimp_canvas_item_on_vectors_curve (GimpCanvasItem *item,
224 GimpVectors *vectors,
225 const GimpCoords *coord,
226 gint width,
227 gint height,
228 GimpCoords *ret_coords,
229 gdouble *ret_pos,
230 GimpAnchor **ret_segment_start,
231 GimpAnchor **ret_segment_end,
232 GimpStroke **ret_stroke)
233 {
234 GimpStroke *stroke = NULL;
235 GimpAnchor *segment_start;
236 GimpAnchor *segment_end;
237 GimpCoords min_coords = GIMP_COORDS_DEFAULT_VALUES;
238 GimpCoords cur_coords;
239 gdouble min_dist, cur_dist, cur_pos;
240
241 g_return_val_if_fail (GIMP_IS_CANVAS_ITEM (item), FALSE);
242 g_return_val_if_fail (GIMP_IS_VECTORS (vectors), FALSE);
243 g_return_val_if_fail (coord != NULL, FALSE);
244
245 if (ret_coords) *ret_coords = *coord;
246 if (ret_pos) *ret_pos = -1.0;
247 if (ret_segment_start) *ret_segment_start = NULL;
248 if (ret_segment_end) *ret_segment_end = NULL;
249 if (ret_stroke) *ret_stroke = NULL;
250
251 min_dist = -1.0;
252
253 while ((stroke = gimp_vectors_stroke_get_next (vectors, stroke)))
254 {
255 cur_dist = gimp_stroke_nearest_point_get (stroke, coord, 1.0,
256 &cur_coords,
257 &segment_start,
258 &segment_end,
259 &cur_pos);
260
261 if (cur_dist >= 0 && (min_dist < 0 || cur_dist < min_dist))
262 {
263 min_dist = cur_dist;
264 min_coords = cur_coords;
265
266 if (ret_coords) *ret_coords = cur_coords;
267 if (ret_pos) *ret_pos = cur_pos;
268 if (ret_segment_start) *ret_segment_start = segment_start;
269 if (ret_segment_end) *ret_segment_end = segment_end;
270 if (ret_stroke) *ret_stroke = stroke;
271 }
272 }
273
274 if (min_dist >= 0 &&
275 gimp_canvas_item_on_handle (item,
276 coord->x,
277 coord->y,
278 GIMP_HANDLE_CIRCLE,
279 min_coords.x,
280 min_coords.y,
281 width, height,
282 GIMP_HANDLE_ANCHOR_CENTER))
283 {
284 return TRUE;
285 }
286
287 return FALSE;
288 }
289
290 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)291 gimp_canvas_item_on_vectors (GimpCanvasItem *item,
292 const GimpCoords *coords,
293 gint width,
294 gint height,
295 GimpCoords *ret_coords,
296 gdouble *ret_pos,
297 GimpAnchor **ret_segment_start,
298 GimpAnchor **ret_segment_end,
299 GimpStroke **ret_stroke,
300 GimpVectors **ret_vectors)
301 {
302 GimpDisplayShell *shell;
303 GimpImage *image;
304 GList *all_vectors;
305 GList *list;
306
307 g_return_val_if_fail (GIMP_IS_CANVAS_ITEM (item), FALSE);
308 g_return_val_if_fail (coords != NULL, FALSE);
309
310 shell = gimp_canvas_item_get_shell (item);
311 image = gimp_display_get_image (shell->display);
312
313 if (ret_coords) *ret_coords = *coords;
314 if (ret_pos) *ret_pos = -1.0;
315 if (ret_segment_start) *ret_segment_start = NULL;
316 if (ret_segment_end) *ret_segment_end = NULL;
317 if (ret_stroke) *ret_stroke = NULL;
318 if (ret_vectors) *ret_vectors = NULL;
319
320 all_vectors = gimp_image_get_vectors_list (image);
321
322 for (list = all_vectors; list; list = g_list_next (list))
323 {
324 GimpVectors *vectors = list->data;
325
326 if (! gimp_item_get_visible (GIMP_ITEM (vectors)))
327 continue;
328
329 if (gimp_canvas_item_on_vectors_curve (item,
330 vectors, coords,
331 width, height,
332 ret_coords,
333 ret_pos,
334 ret_segment_start,
335 ret_segment_end,
336 ret_stroke))
337 {
338 if (ret_vectors)
339 *ret_vectors = vectors;
340
341 g_list_free (all_vectors);
342
343 return TRUE;
344 }
345 }
346
347 g_list_free (all_vectors);
348
349 return FALSE;
350 }
351
352 void
gimp_canvas_item_shift_to_north_west(GimpHandleAnchor anchor,gdouble x,gdouble y,gint width,gint height,gdouble * shifted_x,gdouble * shifted_y)353 gimp_canvas_item_shift_to_north_west (GimpHandleAnchor anchor,
354 gdouble x,
355 gdouble y,
356 gint width,
357 gint height,
358 gdouble *shifted_x,
359 gdouble *shifted_y)
360 {
361 switch (anchor)
362 {
363 case GIMP_HANDLE_ANCHOR_CENTER:
364 x -= width / 2;
365 y -= height / 2;
366 break;
367
368 case GIMP_HANDLE_ANCHOR_NORTH:
369 x -= width / 2;
370 break;
371
372 case GIMP_HANDLE_ANCHOR_NORTH_WEST:
373 /* nothing, this is the default */
374 break;
375
376 case GIMP_HANDLE_ANCHOR_NORTH_EAST:
377 x -= width;
378 break;
379
380 case GIMP_HANDLE_ANCHOR_SOUTH:
381 x -= width / 2;
382 y -= height;
383 break;
384
385 case GIMP_HANDLE_ANCHOR_SOUTH_WEST:
386 y -= height;
387 break;
388
389 case GIMP_HANDLE_ANCHOR_SOUTH_EAST:
390 x -= width;
391 y -= height;
392 break;
393
394 case GIMP_HANDLE_ANCHOR_WEST:
395 y -= height / 2;
396 break;
397
398 case GIMP_HANDLE_ANCHOR_EAST:
399 x -= width;
400 y -= height / 2;
401 break;
402
403 default:
404 break;
405 }
406
407 if (shifted_x)
408 *shifted_x = x;
409
410 if (shifted_y)
411 *shifted_y = y;
412 }
413
414 void
gimp_canvas_item_shift_to_center(GimpHandleAnchor anchor,gdouble x,gdouble y,gint width,gint height,gdouble * shifted_x,gdouble * shifted_y)415 gimp_canvas_item_shift_to_center (GimpHandleAnchor anchor,
416 gdouble x,
417 gdouble y,
418 gint width,
419 gint height,
420 gdouble *shifted_x,
421 gdouble *shifted_y)
422 {
423 switch (anchor)
424 {
425 case GIMP_HANDLE_ANCHOR_CENTER:
426 /* nothing, this is the default */
427 break;
428
429 case GIMP_HANDLE_ANCHOR_NORTH:
430 y += height / 2;
431 break;
432
433 case GIMP_HANDLE_ANCHOR_NORTH_WEST:
434 x += width / 2;
435 y += height / 2;
436 break;
437
438 case GIMP_HANDLE_ANCHOR_NORTH_EAST:
439 x -= width / 2;
440 y += height / 2;
441 break;
442
443 case GIMP_HANDLE_ANCHOR_SOUTH:
444 y -= height / 2;
445 break;
446
447 case GIMP_HANDLE_ANCHOR_SOUTH_WEST:
448 x += width / 2;
449 y -= height / 2;
450 break;
451
452 case GIMP_HANDLE_ANCHOR_SOUTH_EAST:
453 x -= width / 2;
454 y -= height / 2;
455 break;
456
457 case GIMP_HANDLE_ANCHOR_WEST:
458 x += width / 2;
459 break;
460
461 case GIMP_HANDLE_ANCHOR_EAST:
462 x -= width / 2;
463 break;
464
465 default:
466 break;
467 }
468
469 if (shifted_x)
470 *shifted_x = x;
471
472 if (shifted_y)
473 *shifted_y = y;
474 }
475