1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
16  */
17 
18 #include "config.h"
19 
20 #include <gdk-pixbuf/gdk-pixbuf.h>
21 #include <gegl.h>
22 
23 #include "libgimpmath/gimpmath.h"
24 
25 #include "core-types.h"
26 
27 #include "gimp.h"
28 #include "gimpgrid.h"
29 #include "gimpguide.h"
30 #include "gimpimage.h"
31 #include "gimpimage-grid.h"
32 #include "gimpimage-guides.h"
33 #include "gimpimage-snap.h"
34 
35 #include "vectors/gimpstroke.h"
36 #include "vectors/gimpvectors.h"
37 
38 #include "gimp-intl.h"
39 
40 
41 static gboolean  gimp_image_snap_distance (const gdouble  unsnapped,
42                                            const gdouble  nearest,
43                                            const gdouble  epsilon,
44                                            gdouble       *mindist,
45                                            gdouble       *target);
46 
47 
48 
49 /*  public functions  */
50 
51 gboolean
gimp_image_snap_x(GimpImage * image,gdouble x,gdouble * tx,gdouble epsilon_x,gboolean snap_to_guides,gboolean snap_to_grid,gboolean snap_to_canvas)52 gimp_image_snap_x (GimpImage *image,
53                    gdouble    x,
54                    gdouble   *tx,
55                    gdouble    epsilon_x,
56                    gboolean   snap_to_guides,
57                    gboolean   snap_to_grid,
58                    gboolean   snap_to_canvas)
59 {
60   gdouble   mindist = G_MAXDOUBLE;
61   gboolean  snapped = FALSE;
62 
63   g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
64   g_return_val_if_fail (tx != NULL, FALSE);
65 
66   *tx = x;
67 
68   if (! gimp_image_get_guides (image)) snap_to_guides = FALSE;
69   if (! gimp_image_get_grid (image))   snap_to_grid   = FALSE;
70 
71   if (! (snap_to_guides || snap_to_grid || snap_to_canvas))
72     return FALSE;
73 
74   if (x < -epsilon_x || x >= (gimp_image_get_width (image) + epsilon_x))
75     return FALSE;
76 
77   if (snap_to_guides)
78     {
79       GList *list;
80 
81       for (list = gimp_image_get_guides (image); list; list = g_list_next (list))
82         {
83           GimpGuide *guide    = list->data;
84           gint       position = gimp_guide_get_position (guide);
85 
86           if (gimp_guide_is_custom (guide))
87             continue;
88 
89           if (gimp_guide_get_orientation (guide) == GIMP_ORIENTATION_VERTICAL)
90             {
91               snapped |= gimp_image_snap_distance (x, position,
92                                                    epsilon_x,
93                                                    &mindist, tx);
94             }
95         }
96     }
97 
98   if (snap_to_grid)
99     {
100       GimpGrid *grid = gimp_image_get_grid (image);
101       gdouble   xspacing;
102       gdouble   xoffset;
103 
104       gimp_grid_get_spacing (grid, &xspacing, NULL);
105       gimp_grid_get_offset  (grid, &xoffset,  NULL);
106 
107       if (xspacing > 0.0)
108         {
109           gdouble nearest;
110 
111           nearest = xoffset + RINT ((x - xoffset) / xspacing) * xspacing;
112 
113           snapped |= gimp_image_snap_distance (x, nearest,
114                                                epsilon_x,
115                                                &mindist, tx);
116         }
117     }
118 
119   if (snap_to_canvas)
120     {
121       snapped |= gimp_image_snap_distance (x, 0,
122                                            epsilon_x,
123                                            &mindist, tx);
124       snapped |= gimp_image_snap_distance (x, gimp_image_get_width (image),
125                                            epsilon_x,
126                                            &mindist, tx);
127     }
128 
129   return snapped;
130 }
131 
132 gboolean
gimp_image_snap_y(GimpImage * image,gdouble y,gdouble * ty,gdouble epsilon_y,gboolean snap_to_guides,gboolean snap_to_grid,gboolean snap_to_canvas)133 gimp_image_snap_y (GimpImage *image,
134                    gdouble    y,
135                    gdouble   *ty,
136                    gdouble    epsilon_y,
137                    gboolean   snap_to_guides,
138                    gboolean   snap_to_grid,
139                    gboolean   snap_to_canvas)
140 {
141   gdouble    mindist = G_MAXDOUBLE;
142   gboolean   snapped = FALSE;
143 
144   g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
145   g_return_val_if_fail (ty != NULL, FALSE);
146 
147   *ty = y;
148 
149   if (! gimp_image_get_guides (image)) snap_to_guides = FALSE;
150   if (! gimp_image_get_grid (image))   snap_to_grid   = FALSE;
151 
152   if (! (snap_to_guides || snap_to_grid || snap_to_canvas))
153     return FALSE;
154 
155   if (y < -epsilon_y || y >= (gimp_image_get_height (image) + epsilon_y))
156     return FALSE;
157 
158   if (snap_to_guides)
159     {
160       GList *list;
161 
162       for (list = gimp_image_get_guides (image); list; list = g_list_next (list))
163         {
164           GimpGuide *guide    = list->data;
165           gint       position = gimp_guide_get_position (guide);
166 
167           if (gimp_guide_is_custom (guide))
168             continue;
169 
170           if (gimp_guide_get_orientation (guide) == GIMP_ORIENTATION_HORIZONTAL)
171             {
172               snapped |= gimp_image_snap_distance (y, position,
173                                                    epsilon_y,
174                                                    &mindist, ty);
175             }
176         }
177     }
178 
179   if (snap_to_grid)
180     {
181       GimpGrid *grid = gimp_image_get_grid (image);
182       gdouble   yspacing;
183       gdouble   yoffset;
184 
185       gimp_grid_get_spacing (grid, NULL, &yspacing);
186       gimp_grid_get_offset  (grid, NULL, &yoffset);
187 
188       if (yspacing > 0.0)
189         {
190           gdouble nearest;
191 
192           nearest = yoffset + RINT ((y - yoffset) / yspacing) * yspacing;
193 
194           snapped |= gimp_image_snap_distance (y, nearest,
195                                                epsilon_y,
196                                                &mindist, ty);
197         }
198     }
199 
200   if (snap_to_canvas)
201     {
202       snapped |= gimp_image_snap_distance (y, 0,
203                                            epsilon_y,
204                                            &mindist, ty);
205       snapped |= gimp_image_snap_distance (y, gimp_image_get_height (image),
206                                            epsilon_y,
207                                            &mindist, ty);
208     }
209 
210   return snapped;
211 }
212 
213 gboolean
gimp_image_snap_point(GimpImage * image,gdouble x,gdouble y,gdouble * tx,gdouble * ty,gdouble epsilon_x,gdouble epsilon_y,gboolean snap_to_guides,gboolean snap_to_grid,gboolean snap_to_canvas,gboolean snap_to_vectors,gboolean show_all)214 gimp_image_snap_point (GimpImage *image,
215                        gdouble    x,
216                        gdouble    y,
217                        gdouble   *tx,
218                        gdouble   *ty,
219                        gdouble    epsilon_x,
220                        gdouble    epsilon_y,
221                        gboolean   snap_to_guides,
222                        gboolean   snap_to_grid,
223                        gboolean   snap_to_canvas,
224                        gboolean   snap_to_vectors,
225                        gboolean   show_all)
226 {
227   gdouble  mindist_x = G_MAXDOUBLE;
228   gdouble  mindist_y = G_MAXDOUBLE;
229   gboolean snapped   = FALSE;
230 
231   g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
232   g_return_val_if_fail (tx != NULL, FALSE);
233   g_return_val_if_fail (ty != NULL, FALSE);
234 
235   *tx = x;
236   *ty = y;
237 
238   if (! gimp_image_get_guides (image))         snap_to_guides  = FALSE;
239   if (! gimp_image_get_grid (image))           snap_to_grid    = FALSE;
240   if (! gimp_image_get_active_vectors (image)) snap_to_vectors = FALSE;
241 
242   if (! (snap_to_guides || snap_to_grid || snap_to_canvas || snap_to_vectors))
243     return FALSE;
244 
245   if (! show_all &&
246       (x < -epsilon_x || x >= (gimp_image_get_width  (image) + epsilon_x) ||
247        y < -epsilon_y || y >= (gimp_image_get_height (image) + epsilon_y)))
248     {
249       /* Off-canvas grid is invisible unless "show all" option is
250        * enabled. So let's not snap to the invisible grid.
251        */
252       snap_to_grid   = FALSE;
253       snap_to_canvas = FALSE;
254     }
255 
256   if (snap_to_guides)
257     {
258       GList *list;
259 
260       for (list = gimp_image_get_guides (image); list; list = g_list_next (list))
261         {
262           GimpGuide *guide    = list->data;
263           gint       position = gimp_guide_get_position (guide);
264 
265           if (gimp_guide_is_custom (guide))
266             continue;
267 
268           switch (gimp_guide_get_orientation (guide))
269             {
270             case GIMP_ORIENTATION_HORIZONTAL:
271               snapped |= gimp_image_snap_distance (y, position,
272                                                    epsilon_y,
273                                                    &mindist_y, ty);
274               break;
275 
276             case GIMP_ORIENTATION_VERTICAL:
277               snapped |= gimp_image_snap_distance (x, position,
278                                                    epsilon_x,
279                                                    &mindist_x, tx);
280               break;
281 
282             default:
283               break;
284             }
285         }
286     }
287 
288   if (snap_to_grid)
289     {
290       GimpGrid *grid = gimp_image_get_grid (image);
291       gdouble   xspacing, yspacing;
292       gdouble   xoffset, yoffset;
293 
294       gimp_grid_get_spacing (grid, &xspacing, &yspacing);
295       gimp_grid_get_offset  (grid, &xoffset,  &yoffset);
296 
297       if (xspacing > 0.0)
298         {
299           gdouble nearest;
300 
301           nearest = xoffset + RINT ((x - xoffset) / xspacing) * xspacing;
302 
303           snapped |= gimp_image_snap_distance (x, nearest,
304                                                epsilon_x,
305                                                &mindist_x, tx);
306         }
307 
308       if (yspacing > 0.0)
309         {
310           gdouble nearest;
311 
312           nearest = yoffset + RINT ((y - yoffset) / yspacing) * yspacing;
313 
314           snapped |= gimp_image_snap_distance (y, nearest,
315                                                epsilon_y,
316                                                &mindist_y, ty);
317         }
318     }
319 
320   if (snap_to_canvas)
321     {
322       snapped |= gimp_image_snap_distance (x, 0,
323                                            epsilon_x,
324                                            &mindist_x, tx);
325       snapped |= gimp_image_snap_distance (x, gimp_image_get_width (image),
326                                            epsilon_x,
327                                            &mindist_x, tx);
328 
329       snapped |= gimp_image_snap_distance (y, 0,
330                                            epsilon_y,
331                                            &mindist_y, ty);
332       snapped |= gimp_image_snap_distance (y, gimp_image_get_height (image),
333                                            epsilon_y,
334                                            &mindist_y, ty);
335     }
336 
337   if (snap_to_vectors)
338     {
339       GimpVectors *vectors = gimp_image_get_active_vectors (image);
340       GimpStroke  *stroke  = NULL;
341       GimpCoords   coords  = { 0, 0, 0, 0, 0 };
342 
343       coords.x = x;
344       coords.y = y;
345 
346       while ((stroke = gimp_vectors_stroke_get_next (vectors, stroke)))
347         {
348           GimpCoords nearest;
349 
350           if (gimp_stroke_nearest_point_get (stroke, &coords, 1.0,
351                                              &nearest,
352                                              NULL, NULL, NULL) >= 0)
353             {
354               snapped |= gimp_image_snap_distance (x, nearest.x,
355                                                    epsilon_x,
356                                                    &mindist_x, tx);
357               snapped |= gimp_image_snap_distance (y, nearest.y,
358                                                    epsilon_y,
359                                                    &mindist_y, ty);
360             }
361         }
362     }
363 
364   return snapped;
365 }
366 
367 gboolean
gimp_image_snap_rectangle(GimpImage * image,gdouble x1,gdouble y1,gdouble x2,gdouble y2,gdouble * tx1,gdouble * ty1,gdouble epsilon_x,gdouble epsilon_y,gboolean snap_to_guides,gboolean snap_to_grid,gboolean snap_to_canvas,gboolean snap_to_vectors)368 gimp_image_snap_rectangle (GimpImage *image,
369                            gdouble    x1,
370                            gdouble    y1,
371                            gdouble    x2,
372                            gdouble    y2,
373                            gdouble   *tx1,
374                            gdouble   *ty1,
375                            gdouble    epsilon_x,
376                            gdouble    epsilon_y,
377                            gboolean   snap_to_guides,
378                            gboolean   snap_to_grid,
379                            gboolean   snap_to_canvas,
380                            gboolean   snap_to_vectors)
381 {
382   gdouble  nx, ny;
383   gdouble  mindist_x = G_MAXDOUBLE;
384   gdouble  mindist_y = G_MAXDOUBLE;
385   gdouble  x_center  = (x1 + x2) / 2.0;
386   gdouble  y_center  = (y1 + y2) / 2.0;
387   gboolean snapped   = FALSE;
388 
389   g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
390   g_return_val_if_fail (tx1 != NULL, FALSE);
391   g_return_val_if_fail (ty1 != NULL, FALSE);
392 
393   *tx1 = x1;
394   *ty1 = y1;
395 
396   if (! gimp_image_get_guides (image))         snap_to_guides  = FALSE;
397   if (! gimp_image_get_grid (image))           snap_to_grid    = FALSE;
398   if (! gimp_image_get_active_vectors (image)) snap_to_vectors = FALSE;
399 
400   if (! (snap_to_guides || snap_to_grid || snap_to_canvas || snap_to_vectors))
401     return FALSE;
402 
403   /*  left edge  */
404   if (gimp_image_snap_x (image, x1, &nx,
405                          MIN (epsilon_x, mindist_x),
406                          snap_to_guides,
407                          snap_to_grid,
408                          snap_to_canvas))
409     {
410       mindist_x = ABS (nx - x1);
411       *tx1 = nx;
412       snapped = TRUE;
413     }
414 
415   /*  right edge  */
416   if (gimp_image_snap_x (image, x2, &nx,
417                          MIN (epsilon_x, mindist_x),
418                          snap_to_guides,
419                          snap_to_grid,
420                          snap_to_canvas))
421     {
422       mindist_x = ABS (nx - x2);
423       *tx1 = RINT (x1 + (nx - x2));
424       snapped = TRUE;
425     }
426 
427   /*  center, vertical  */
428   if (gimp_image_snap_x (image, x_center, &nx,
429                          MIN (epsilon_x, mindist_x),
430                          snap_to_guides,
431                          snap_to_grid,
432                          snap_to_canvas))
433     {
434       mindist_x = ABS (nx - x_center);
435       *tx1 = RINT (x1 + (nx - x_center));
436       snapped = TRUE;
437     }
438 
439   /*  top edge  */
440   if (gimp_image_snap_y (image, y1, &ny,
441                          MIN (epsilon_y, mindist_y),
442                          snap_to_guides,
443                          snap_to_grid,
444                          snap_to_canvas))
445     {
446       mindist_y = ABS (ny - y1);
447       *ty1 = ny;
448       snapped = TRUE;
449     }
450 
451   /*  bottom edge  */
452   if (gimp_image_snap_y (image, y2, &ny,
453                          MIN (epsilon_y, mindist_y),
454                          snap_to_guides,
455                          snap_to_grid,
456                          snap_to_canvas))
457     {
458       mindist_y = ABS (ny - y2);
459       *ty1 = RINT (y1 + (ny - y2));
460       snapped = TRUE;
461     }
462 
463   /*  center, horizontal  */
464   if (gimp_image_snap_y (image, y_center, &ny,
465                          MIN (epsilon_y, mindist_y),
466                          snap_to_guides,
467                          snap_to_grid,
468                          snap_to_canvas))
469     {
470       mindist_y = ABS (ny - y_center);
471       *ty1 = RINT (y1 + (ny - y_center));
472       snapped = TRUE;
473     }
474 
475   if (snap_to_vectors)
476     {
477       GimpVectors *vectors = gimp_image_get_active_vectors (image);
478       GimpStroke  *stroke  = NULL;
479       GimpCoords   coords1 = GIMP_COORDS_DEFAULT_VALUES;
480       GimpCoords   coords2 = GIMP_COORDS_DEFAULT_VALUES;
481 
482       while ((stroke = gimp_vectors_stroke_get_next (vectors, stroke)))
483         {
484           GimpCoords nearest;
485           gdouble    dist;
486 
487           /*  top edge  */
488 
489           coords1.x = x1;
490           coords1.y = y1;
491           coords2.x = x2;
492           coords2.y = y1;
493 
494           if (gimp_stroke_nearest_tangent_get (stroke, &coords1, &coords2,
495                                                1.0, &nearest,
496                                                NULL, NULL, NULL) >= 0)
497             {
498               snapped |= gimp_image_snap_distance (y1, nearest.y,
499                                                    epsilon_y,
500                                                    &mindist_y, ty1);
501             }
502 
503           if (gimp_stroke_nearest_intersection_get (stroke, &coords1, &coords2,
504                                                     1.0, &nearest,
505                                                     NULL, NULL, NULL) >= 0)
506             {
507               snapped |= gimp_image_snap_distance (x1, nearest.x,
508                                                    epsilon_x,
509                                                    &mindist_x, tx1);
510             }
511 
512           if (gimp_stroke_nearest_intersection_get (stroke, &coords2, &coords1,
513                                                     1.0, &nearest,
514                                                     NULL, NULL, NULL) >= 0)
515             {
516               dist = ABS (nearest.x - x2);
517 
518               if (dist < MIN (epsilon_x, mindist_x))
519                 {
520                   mindist_x = dist;
521                   *tx1 = RINT (x1 + (nearest.x - x2));
522                   snapped = TRUE;
523                 }
524             }
525 
526           /*  bottom edge  */
527 
528           coords1.x = x1;
529           coords1.y = y2;
530           coords2.x = x2;
531           coords2.y = y2;
532 
533           if (gimp_stroke_nearest_tangent_get (stroke, &coords1, &coords2,
534                                                1.0, &nearest,
535                                                NULL, NULL, NULL) >= 0)
536             {
537               dist = ABS (nearest.y - y2);
538 
539               if (dist < MIN (epsilon_y, mindist_y))
540                 {
541                   mindist_y = dist;
542                   *ty1 = RINT (y1 + (nearest.y - y2));
543                   snapped = TRUE;
544                 }
545             }
546 
547           if (gimp_stroke_nearest_intersection_get (stroke, &coords1, &coords2,
548                                                     1.0, &nearest,
549                                                     NULL, NULL, NULL) >= 0)
550             {
551               snapped |= gimp_image_snap_distance (x1, nearest.x,
552                                                    epsilon_x,
553                                                    &mindist_x, tx1);
554             }
555 
556           if (gimp_stroke_nearest_intersection_get (stroke, &coords2, &coords1,
557                                                     1.0, &nearest,
558                                                     NULL, NULL, NULL) >= 0)
559             {
560               dist = ABS (nearest.x - x2);
561 
562               if (dist < MIN (epsilon_x, mindist_x))
563                 {
564                   mindist_x = dist;
565                   *tx1 = RINT (x1 + (nearest.x - x2));
566                   snapped = TRUE;
567                 }
568             }
569 
570           /*  left edge  */
571 
572           coords1.x = x1;
573           coords1.y = y1;
574           coords2.x = x1;
575           coords2.y = y2;
576 
577           if (gimp_stroke_nearest_tangent_get (stroke, &coords1, &coords2,
578                                                1.0, &nearest,
579                                                NULL, NULL, NULL) >= 0)
580             {
581               snapped |= gimp_image_snap_distance (x1, nearest.x,
582                                                    epsilon_x,
583                                                    &mindist_x, tx1);
584             }
585 
586           if (gimp_stroke_nearest_intersection_get (stroke, &coords1, &coords2,
587                                                     1.0, &nearest,
588                                                     NULL, NULL, NULL) >= 0)
589             {
590               snapped |= gimp_image_snap_distance (y1, nearest.y,
591                                                    epsilon_y,
592                                                    &mindist_y, ty1);
593             }
594 
595           if (gimp_stroke_nearest_intersection_get (stroke, &coords2, &coords1,
596                                                     1.0, &nearest,
597                                                     NULL, NULL, NULL) >= 0)
598             {
599               dist = ABS (nearest.y - y2);
600 
601               if (dist < MIN (epsilon_y, mindist_y))
602                 {
603                   mindist_y = dist;
604                   *ty1 = RINT (y1 + (nearest.y - y2));
605                   snapped = TRUE;
606                 }
607             }
608 
609           /*  right edge  */
610 
611           coords1.x = x2;
612           coords1.y = y1;
613           coords2.x = x2;
614           coords2.y = y2;
615 
616           if (gimp_stroke_nearest_tangent_get (stroke, &coords1, &coords2,
617                                                1.0, &nearest,
618                                                NULL, NULL, NULL) >= 0)
619             {
620               dist = ABS (nearest.x - x2);
621 
622               if (dist < MIN (epsilon_x, mindist_x))
623                 {
624                   mindist_x = dist;
625                   *tx1 = RINT (x1 + (nearest.x - x2));
626                   snapped = TRUE;
627                 }
628             }
629 
630           if (gimp_stroke_nearest_intersection_get (stroke, &coords1, &coords2,
631                                                     1.0, &nearest,
632                                                     NULL, NULL, NULL) >= 0)
633             {
634               snapped |= gimp_image_snap_distance (y1, nearest.y,
635                                                    epsilon_y,
636                                                    &mindist_y, ty1);
637             }
638 
639           if (gimp_stroke_nearest_intersection_get (stroke, &coords2, &coords1,
640                                                     1.0, &nearest,
641                                                     NULL, NULL, NULL) >= 0)
642             {
643               dist = ABS (nearest.y - y2);
644 
645               if (dist < MIN (epsilon_y, mindist_y))
646                 {
647                   mindist_y = dist;
648                   *ty1 = RINT (y1 + (nearest.y - y2));
649                   snapped = TRUE;
650                 }
651             }
652 
653           /*  center  */
654 
655           coords1.x = x_center;
656           coords1.y = y_center;
657 
658           if (gimp_stroke_nearest_point_get (stroke, &coords1, 1.0,
659                                              &nearest,
660                                              NULL, NULL, NULL) >= 0)
661             {
662               if (gimp_image_snap_distance (x_center, nearest.x,
663                                             epsilon_x,
664                                             &mindist_x, &nx))
665                 {
666                   mindist_x = ABS (nx - x_center);
667                   *tx1 = RINT (x1 + (nx - x_center));
668                   snapped = TRUE;
669                 }
670 
671               if (gimp_image_snap_distance (y_center, nearest.y,
672                                             epsilon_y,
673                                             &mindist_y, &ny))
674                 {
675                   mindist_y = ABS (ny - y_center);
676                   *ty1 = RINT (y1 + (ny - y_center));
677                   snapped = TRUE;
678                }
679             }
680         }
681     }
682 
683   return snapped;
684 }
685 
686 /* private functions */
687 
688 /**
689  * gimp_image_snap_distance:
690  * @unsnapped: One coordinate of the unsnapped position
691  * @nearest:  One coordinate of a snapping position candidate
692  * @epsilon:  The snapping threshold
693  * @mindist:  The distance to the currently closest snapping target
694  * @target:   The currently closest snapping target
695  *
696  * Finds out if snapping occurs from position to a snapping candidate
697  * and sets the target accordingly.
698  *
699  * Return value: %TRUE if snapping occurred, %FALSE otherwise
700  */
701 static gboolean
gimp_image_snap_distance(const gdouble unsnapped,const gdouble nearest,const gdouble epsilon,gdouble * mindist,gdouble * target)702 gimp_image_snap_distance (const gdouble  unsnapped,
703                           const gdouble  nearest,
704                           const gdouble  epsilon,
705                           gdouble       *mindist,
706                           gdouble       *target)
707 {
708   const gdouble dist = ABS (nearest - unsnapped);
709 
710   if (dist < MIN (epsilon, *mindist))
711     {
712       *mindist = dist;
713       *target = nearest;
714 
715       return TRUE;
716     }
717 
718   return FALSE;
719 }
720