1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * gimpstroke.c
5  * Copyright (C) 2002 Simon Budig  <simon@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 <cairo.h>
24 #include <gegl.h>
25 #include <gdk-pixbuf/gdk-pixbuf.h>
26 
27 #include "libgimpbase/gimpbase.h"
28 #include "libgimpmath/gimpmath.h"
29 
30 #include "vectors-types.h"
31 
32 #include "core/gimp-memsize.h"
33 #include "core/gimpcoords.h"
34 #include "core/gimpparamspecs.h"
35 #include "core/gimp-transform-utils.h"
36 
37 #include "gimpanchor.h"
38 #include "gimpstroke.h"
39 
40 enum
41 {
42   PROP_0,
43   PROP_CONTROL_POINTS,
44   PROP_CLOSED
45 };
46 
47 /* Prototypes */
48 
49 static void    gimp_stroke_set_property              (GObject      *object,
50                                                       guint         property_id,
51                                                       const GValue *value,
52                                                       GParamSpec   *pspec);
53 static void    gimp_stroke_get_property              (GObject      *object,
54                                                       guint         property_id,
55                                                       GValue       *value,
56                                                       GParamSpec   *pspec);
57 static void    gimp_stroke_finalize                  (GObject      *object);
58 
59 static gint64  gimp_stroke_get_memsize               (GimpObject   *object,
60                                                       gint64       *gui_size);
61 
62 static GimpAnchor * gimp_stroke_real_anchor_get      (GimpStroke       *stroke,
63                                                       const GimpCoords *coord);
64 static GimpAnchor * gimp_stroke_real_anchor_get_next (GimpStroke       *stroke,
65                                                       const GimpAnchor *prev);
66 static void         gimp_stroke_real_anchor_select   (GimpStroke       *stroke,
67                                                       GimpAnchor       *anchor,
68                                                       gboolean          selected,
69                                                       gboolean          exclusive);
70 static void    gimp_stroke_real_anchor_move_relative (GimpStroke       *stroke,
71                                                       GimpAnchor       *anchor,
72                                                       const GimpCoords *delta,
73                                                       GimpAnchorFeatureType feature);
74 static void    gimp_stroke_real_anchor_move_absolute (GimpStroke       *stroke,
75                                                       GimpAnchor       *anchor,
76                                                       const GimpCoords *delta,
77                                                       GimpAnchorFeatureType feature);
78 static void         gimp_stroke_real_anchor_convert  (GimpStroke       *stroke,
79                                                       GimpAnchor       *anchor,
80                                                       GimpAnchorFeatureType  feature);
81 static void         gimp_stroke_real_anchor_delete   (GimpStroke       *stroke,
82                                                       GimpAnchor       *anchor);
83 static gboolean     gimp_stroke_real_point_is_movable
84                                               (GimpStroke            *stroke,
85                                                GimpAnchor            *predec,
86                                                gdouble                position);
87 static void         gimp_stroke_real_point_move_relative
88                                               (GimpStroke            *stroke,
89                                                GimpAnchor            *predec,
90                                                gdouble                position,
91                                                const GimpCoords      *deltacoord,
92                                                GimpAnchorFeatureType  feature);
93 static void         gimp_stroke_real_point_move_absolute
94                                               (GimpStroke            *stroke,
95                                                GimpAnchor            *predec,
96                                                gdouble                position,
97                                                const GimpCoords      *coord,
98                                                GimpAnchorFeatureType  feature);
99 
100 static void         gimp_stroke_real_close           (GimpStroke       *stroke);
101 static GimpStroke * gimp_stroke_real_open            (GimpStroke       *stroke,
102                                                       GimpAnchor       *end_anchor);
103 static gboolean     gimp_stroke_real_anchor_is_insertable
104                                                      (GimpStroke       *stroke,
105                                                       GimpAnchor       *predec,
106                                                       gdouble           position);
107 static GimpAnchor * gimp_stroke_real_anchor_insert   (GimpStroke       *stroke,
108                                                       GimpAnchor       *predec,
109                                                       gdouble           position);
110 
111 static gboolean     gimp_stroke_real_is_extendable   (GimpStroke       *stroke,
112                                                       GimpAnchor       *neighbor);
113 
114 static GimpAnchor * gimp_stroke_real_extend (GimpStroke           *stroke,
115                                              const GimpCoords     *coords,
116                                              GimpAnchor           *neighbor,
117                                              GimpVectorExtendMode  extend_mode);
118 
119 gboolean     gimp_stroke_real_connect_stroke (GimpStroke          *stroke,
120                                               GimpAnchor          *anchor,
121                                               GimpStroke          *extension,
122                                               GimpAnchor          *neighbor);
123 
124 
125 static gboolean     gimp_stroke_real_is_empty        (GimpStroke       *stroke);
126 
127 static gdouble      gimp_stroke_real_get_length      (GimpStroke       *stroke,
128                                                       gdouble           precision);
129 static gdouble      gimp_stroke_real_get_distance    (GimpStroke       *stroke,
130                                                       const GimpCoords *coord);
131 static GArray *     gimp_stroke_real_interpolate     (GimpStroke       *stroke,
132                                                       gdouble           precision,
133                                                       gboolean         *closed);
134 static GimpStroke * gimp_stroke_real_duplicate       (GimpStroke       *stroke);
135 static GimpBezierDesc * gimp_stroke_real_make_bezier (GimpStroke       *stroke);
136 
137 static void         gimp_stroke_real_translate       (GimpStroke       *stroke,
138                                                       gdouble           offset_x,
139                                                       gdouble           offset_y);
140 static void         gimp_stroke_real_scale           (GimpStroke       *stroke,
141                                                       gdouble           scale_x,
142                                                       gdouble           scale_y);
143 static void         gimp_stroke_real_rotate          (GimpStroke       *stroke,
144                                                       gdouble           center_x,
145                                                       gdouble           center_y,
146                                                       gdouble           angle);
147 static void         gimp_stroke_real_flip            (GimpStroke       *stroke,
148                                                       GimpOrientationType flip_type,
149                                                       gdouble           axis);
150 static void         gimp_stroke_real_flip_free       (GimpStroke       *stroke,
151                                                       gdouble           x1,
152                                                       gdouble           y1,
153                                                       gdouble           x2,
154                                                       gdouble           y2);
155 static void         gimp_stroke_real_transform       (GimpStroke        *stroke,
156                                                       const GimpMatrix3 *matrix,
157                                                       GQueue            *ret_strokes);
158 
159 static GList    * gimp_stroke_real_get_draw_anchors  (GimpStroke       *stroke);
160 static GList    * gimp_stroke_real_get_draw_controls (GimpStroke       *stroke);
161 static GArray   * gimp_stroke_real_get_draw_lines    (GimpStroke       *stroke);
162 static GArray *  gimp_stroke_real_control_points_get (GimpStroke       *stroke,
163                                                       gboolean         *ret_closed);
164 static gboolean   gimp_stroke_real_get_point_at_dist (GimpStroke       *stroke,
165                                                       gdouble           dist,
166                                                       gdouble           precision,
167                                                       GimpCoords       *position,
168                                                       gdouble          *slope);
169 
170 
G_DEFINE_TYPE(GimpStroke,gimp_stroke,GIMP_TYPE_OBJECT)171 G_DEFINE_TYPE (GimpStroke, gimp_stroke, GIMP_TYPE_OBJECT)
172 
173 #define parent_class gimp_stroke_parent_class
174 
175 
176 static void
177 gimp_stroke_class_init (GimpStrokeClass *klass)
178 {
179   GObjectClass    *object_class      = G_OBJECT_CLASS (klass);
180   GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass);
181   GParamSpec      *param_spec;
182 
183   object_class->finalize          = gimp_stroke_finalize;
184   object_class->get_property      = gimp_stroke_get_property;
185   object_class->set_property      = gimp_stroke_set_property;
186 
187   gimp_object_class->get_memsize  = gimp_stroke_get_memsize;
188 
189   klass->changed                  = NULL;
190   klass->removed                  = NULL;
191 
192   klass->anchor_get               = gimp_stroke_real_anchor_get;
193   klass->anchor_get_next          = gimp_stroke_real_anchor_get_next;
194   klass->anchor_select            = gimp_stroke_real_anchor_select;
195   klass->anchor_move_relative     = gimp_stroke_real_anchor_move_relative;
196   klass->anchor_move_absolute     = gimp_stroke_real_anchor_move_absolute;
197   klass->anchor_convert           = gimp_stroke_real_anchor_convert;
198   klass->anchor_delete            = gimp_stroke_real_anchor_delete;
199 
200   klass->point_is_movable         = gimp_stroke_real_point_is_movable;
201   klass->point_move_relative      = gimp_stroke_real_point_move_relative;
202   klass->point_move_absolute      = gimp_stroke_real_point_move_absolute;
203 
204   klass->nearest_point_get        = NULL;
205   klass->nearest_tangent_get      = NULL;
206   klass->nearest_intersection_get = NULL;
207   klass->close                    = gimp_stroke_real_close;
208   klass->open                     = gimp_stroke_real_open;
209   klass->anchor_is_insertable     = gimp_stroke_real_anchor_is_insertable;
210   klass->anchor_insert            = gimp_stroke_real_anchor_insert;
211   klass->is_extendable            = gimp_stroke_real_is_extendable;
212   klass->extend                   = gimp_stroke_real_extend;
213   klass->connect_stroke           = gimp_stroke_real_connect_stroke;
214 
215   klass->is_empty                 = gimp_stroke_real_is_empty;
216   klass->get_length               = gimp_stroke_real_get_length;
217   klass->get_distance             = gimp_stroke_real_get_distance;
218   klass->get_point_at_dist        = gimp_stroke_real_get_point_at_dist;
219   klass->interpolate              = gimp_stroke_real_interpolate;
220 
221   klass->duplicate                = gimp_stroke_real_duplicate;
222   klass->make_bezier              = gimp_stroke_real_make_bezier;
223 
224   klass->translate                = gimp_stroke_real_translate;
225   klass->scale                    = gimp_stroke_real_scale;
226   klass->rotate                   = gimp_stroke_real_rotate;
227   klass->flip                     = gimp_stroke_real_flip;
228   klass->flip_free                = gimp_stroke_real_flip_free;
229   klass->transform                = gimp_stroke_real_transform;
230 
231 
232   klass->get_draw_anchors         = gimp_stroke_real_get_draw_anchors;
233   klass->get_draw_controls        = gimp_stroke_real_get_draw_controls;
234   klass->get_draw_lines           = gimp_stroke_real_get_draw_lines;
235   klass->control_points_get       = gimp_stroke_real_control_points_get;
236 
237   param_spec = g_param_spec_boxed ("gimp-anchor",
238                                    "Gimp Anchor",
239                                    "The control points of a Stroke",
240                                    GIMP_TYPE_ANCHOR,
241                                    GIMP_PARAM_WRITABLE |
242                                    G_PARAM_CONSTRUCT_ONLY);
243   g_object_class_install_property (object_class, PROP_CONTROL_POINTS,
244                                    gimp_param_spec_value_array ("control-points",
245                                                                 "Control Points",
246                                                                 "This is an ValueArray "
247                                                                 "with the initial "
248                                                                 "control points of "
249                                                                 "the new Stroke",
250                                                                 param_spec,
251                                                                 GIMP_PARAM_WRITABLE |
252                                                                 G_PARAM_CONSTRUCT_ONLY));
253 
254   g_object_class_install_property (object_class, PROP_CLOSED,
255                                    g_param_spec_boolean ("closed",
256                                                          "Close Flag",
257                                                          "this flag indicates "
258                                                          "whether the stroke "
259                                                          "is closed or not",
260                                                          FALSE,
261                                                          GIMP_PARAM_READWRITE |
262                                                          G_PARAM_CONSTRUCT_ONLY));
263 }
264 
265 static void
gimp_stroke_init(GimpStroke * stroke)266 gimp_stroke_init (GimpStroke *stroke)
267 {
268   stroke->anchors = g_queue_new ();
269 }
270 
271 static void
gimp_stroke_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)272 gimp_stroke_set_property (GObject      *object,
273                           guint         property_id,
274                           const GValue *value,
275                           GParamSpec   *pspec)
276 {
277   GimpStroke     *stroke = GIMP_STROKE (object);
278   GimpValueArray *val_array;
279   gint            length;
280   gint            i;
281 
282   switch (property_id)
283     {
284     case PROP_CLOSED:
285       stroke->closed = g_value_get_boolean (value);
286       break;
287 
288     case PROP_CONTROL_POINTS:
289       g_return_if_fail (g_queue_is_empty (stroke->anchors));
290       g_return_if_fail (value != NULL);
291 
292       val_array = g_value_get_boxed (value);
293 
294       if (val_array == NULL)
295         return;
296 
297       length = gimp_value_array_length (val_array);
298 
299       for (i = 0; i < length; i++)
300         {
301           GValue *item = gimp_value_array_index (val_array, i);
302 
303           g_return_if_fail (G_VALUE_HOLDS (item, GIMP_TYPE_ANCHOR));
304           g_queue_push_tail (stroke->anchors, g_value_dup_boxed (item));
305         }
306 
307       break;
308 
309     default:
310       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
311       break;
312     }
313 }
314 
315 static void
gimp_stroke_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)316 gimp_stroke_get_property (GObject    *object,
317                           guint       property_id,
318                           GValue     *value,
319                           GParamSpec *pspec)
320 {
321   GimpStroke *stroke = GIMP_STROKE (object);
322 
323   switch (property_id)
324     {
325     case PROP_CLOSED:
326       g_value_set_boolean (value, stroke->closed);
327       break;
328 
329     default:
330       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
331       break;
332     }
333 }
334 
335 static void
gimp_stroke_finalize(GObject * object)336 gimp_stroke_finalize (GObject *object)
337 {
338   GimpStroke *stroke = GIMP_STROKE (object);
339 
340   g_queue_free_full (stroke->anchors, (GDestroyNotify) gimp_anchor_free);
341   stroke->anchors = NULL;
342 
343   G_OBJECT_CLASS (parent_class)->finalize (object);
344 }
345 
346 static gint64
gimp_stroke_get_memsize(GimpObject * object,gint64 * gui_size)347 gimp_stroke_get_memsize (GimpObject *object,
348                          gint64     *gui_size)
349 {
350   GimpStroke *stroke  = GIMP_STROKE (object);
351   gint64      memsize = 0;
352 
353   memsize += gimp_g_queue_get_memsize (stroke->anchors, sizeof (GimpAnchor));
354 
355   return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
356                                                                   gui_size);
357 }
358 
359 void
gimp_stroke_set_ID(GimpStroke * stroke,gint id)360 gimp_stroke_set_ID (GimpStroke *stroke,
361                     gint        id)
362 {
363   g_return_if_fail (GIMP_IS_STROKE (stroke));
364   g_return_if_fail (stroke->ID == 0 /* we don't want changing IDs... */);
365 
366   stroke->ID = id;
367 }
368 
369 gint
gimp_stroke_get_ID(GimpStroke * stroke)370 gimp_stroke_get_ID (GimpStroke *stroke)
371 {
372   g_return_val_if_fail (GIMP_IS_STROKE (stroke), -1);
373 
374   return stroke->ID;
375 }
376 
377 
378 GimpAnchor *
gimp_stroke_anchor_get(GimpStroke * stroke,const GimpCoords * coord)379 gimp_stroke_anchor_get (GimpStroke       *stroke,
380                         const GimpCoords *coord)
381 {
382   g_return_val_if_fail (GIMP_IS_STROKE (stroke), NULL);
383 
384   return GIMP_STROKE_GET_CLASS (stroke)->anchor_get (stroke, coord);
385 }
386 
387 
388 gdouble
gimp_stroke_nearest_point_get(GimpStroke * stroke,const GimpCoords * coord,const gdouble precision,GimpCoords * ret_point,GimpAnchor ** ret_segment_start,GimpAnchor ** ret_segment_end,gdouble * ret_pos)389 gimp_stroke_nearest_point_get (GimpStroke       *stroke,
390                                const GimpCoords *coord,
391                                const gdouble     precision,
392                                GimpCoords       *ret_point,
393                                GimpAnchor      **ret_segment_start,
394                                GimpAnchor      **ret_segment_end,
395                                gdouble          *ret_pos)
396 {
397   g_return_val_if_fail (GIMP_IS_STROKE (stroke), FALSE);
398   g_return_val_if_fail (coord != NULL, FALSE);
399 
400   if (GIMP_STROKE_GET_CLASS (stroke)->nearest_point_get)
401     return GIMP_STROKE_GET_CLASS (stroke)->nearest_point_get (stroke,
402                                                               coord,
403                                                               precision,
404                                                               ret_point,
405                                                               ret_segment_start,
406                                                               ret_segment_end,
407                                                               ret_pos);
408   return -1;
409 }
410 
411 gdouble
gimp_stroke_nearest_tangent_get(GimpStroke * stroke,const GimpCoords * coords1,const GimpCoords * coords2,gdouble precision,GimpCoords * nearest,GimpAnchor ** ret_segment_start,GimpAnchor ** ret_segment_end,gdouble * ret_pos)412 gimp_stroke_nearest_tangent_get (GimpStroke            *stroke,
413                                  const GimpCoords      *coords1,
414                                  const GimpCoords      *coords2,
415                                  gdouble                precision,
416                                  GimpCoords            *nearest,
417                                  GimpAnchor           **ret_segment_start,
418                                  GimpAnchor           **ret_segment_end,
419                                  gdouble               *ret_pos)
420 {
421   g_return_val_if_fail (GIMP_IS_STROKE (stroke), FALSE);
422   g_return_val_if_fail (coords1 != NULL, FALSE);
423   g_return_val_if_fail (coords2 != NULL, FALSE);
424 
425   if (GIMP_STROKE_GET_CLASS (stroke)->nearest_tangent_get)
426     return GIMP_STROKE_GET_CLASS (stroke)->nearest_tangent_get (stroke,
427                                                                 coords1,
428                                                                 coords2,
429                                                                 precision,
430                                                                 nearest,
431                                                                 ret_segment_start,
432                                                                 ret_segment_end,
433                                                                 ret_pos);
434   return -1;
435 }
436 
437 gdouble
gimp_stroke_nearest_intersection_get(GimpStroke * stroke,const GimpCoords * coords1,const GimpCoords * direction,gdouble precision,GimpCoords * nearest,GimpAnchor ** ret_segment_start,GimpAnchor ** ret_segment_end,gdouble * ret_pos)438 gimp_stroke_nearest_intersection_get (GimpStroke        *stroke,
439                                       const GimpCoords  *coords1,
440                                       const GimpCoords  *direction,
441                                       gdouble            precision,
442                                       GimpCoords        *nearest,
443                                       GimpAnchor       **ret_segment_start,
444                                       GimpAnchor       **ret_segment_end,
445                                       gdouble           *ret_pos)
446 {
447   g_return_val_if_fail (GIMP_IS_STROKE (stroke), FALSE);
448   g_return_val_if_fail (coords1 != NULL, FALSE);
449   g_return_val_if_fail (direction != NULL, FALSE);
450 
451   if (GIMP_STROKE_GET_CLASS (stroke)->nearest_intersection_get)
452     return GIMP_STROKE_GET_CLASS (stroke)->nearest_intersection_get (stroke,
453                                                                      coords1,
454                                                                      direction,
455                                                                      precision,
456                                                                      nearest,
457                                                                      ret_segment_start,
458                                                                      ret_segment_end,
459                                                                      ret_pos);
460   return -1;
461 }
462 
463 static GimpAnchor *
gimp_stroke_real_anchor_get(GimpStroke * stroke,const GimpCoords * coord)464 gimp_stroke_real_anchor_get (GimpStroke       *stroke,
465                              const GimpCoords *coord)
466 {
467   gdouble     dx, dy;
468   gdouble     mindist = -1;
469   GList      *anchors;
470   GList      *list;
471   GimpAnchor *anchor = NULL;
472 
473   anchors = gimp_stroke_get_draw_controls (stroke);
474 
475   for (list = anchors; list; list = g_list_next (list))
476     {
477       dx = coord->x - GIMP_ANCHOR (list->data)->position.x;
478       dy = coord->y - GIMP_ANCHOR (list->data)->position.y;
479 
480       if (mindist < 0 || mindist > dx * dx + dy * dy)
481         {
482           mindist = dx * dx + dy * dy;
483           anchor = GIMP_ANCHOR (list->data);
484         }
485     }
486 
487   g_list_free (anchors);
488 
489   anchors = gimp_stroke_get_draw_anchors (stroke);
490 
491   for (list = anchors; list; list = g_list_next (list))
492     {
493       dx = coord->x - GIMP_ANCHOR (list->data)->position.x;
494       dy = coord->y - GIMP_ANCHOR (list->data)->position.y;
495 
496       if (mindist < 0 || mindist > dx * dx + dy * dy)
497         {
498           mindist = dx * dx + dy * dy;
499           anchor = GIMP_ANCHOR (list->data);
500         }
501     }
502 
503   g_list_free (anchors);
504 
505   return anchor;
506 }
507 
508 
509 GimpAnchor *
gimp_stroke_anchor_get_next(GimpStroke * stroke,const GimpAnchor * prev)510 gimp_stroke_anchor_get_next (GimpStroke       *stroke,
511                              const GimpAnchor *prev)
512 {
513   g_return_val_if_fail (GIMP_IS_STROKE (stroke), NULL);
514 
515   return GIMP_STROKE_GET_CLASS (stroke)->anchor_get_next (stroke, prev);
516 }
517 
518 static GimpAnchor *
gimp_stroke_real_anchor_get_next(GimpStroke * stroke,const GimpAnchor * prev)519 gimp_stroke_real_anchor_get_next (GimpStroke       *stroke,
520                                   const GimpAnchor *prev)
521 {
522   GList *list;
523 
524   if (prev)
525     {
526       list = g_queue_find (stroke->anchors, prev);
527       if (list)
528         list = g_list_next (list);
529     }
530   else
531     {
532       list = stroke->anchors->head;
533     }
534 
535   if (list)
536     return GIMP_ANCHOR (list->data);
537 
538   return NULL;
539 }
540 
541 
542 void
gimp_stroke_anchor_select(GimpStroke * stroke,GimpAnchor * anchor,gboolean selected,gboolean exclusive)543 gimp_stroke_anchor_select (GimpStroke *stroke,
544                            GimpAnchor *anchor,
545                            gboolean    selected,
546                            gboolean    exclusive)
547 {
548   g_return_if_fail (GIMP_IS_STROKE (stroke));
549 
550   GIMP_STROKE_GET_CLASS (stroke)->anchor_select (stroke, anchor,
551                                                  selected, exclusive);
552 }
553 
554 static void
gimp_stroke_real_anchor_select(GimpStroke * stroke,GimpAnchor * anchor,gboolean selected,gboolean exclusive)555 gimp_stroke_real_anchor_select (GimpStroke *stroke,
556                                 GimpAnchor *anchor,
557                                 gboolean    selected,
558                                 gboolean    exclusive)
559 {
560   GList *list = stroke->anchors->head;
561 
562   if (exclusive)
563     {
564       while (list)
565         {
566           GIMP_ANCHOR (list->data)->selected = FALSE;
567           list = g_list_next (list);
568         }
569     }
570 
571   list = g_queue_find (stroke->anchors, anchor);
572 
573   if (list)
574     GIMP_ANCHOR (list->data)->selected = selected;
575 }
576 
577 
578 void
gimp_stroke_anchor_move_relative(GimpStroke * stroke,GimpAnchor * anchor,const GimpCoords * delta,GimpAnchorFeatureType feature)579 gimp_stroke_anchor_move_relative (GimpStroke            *stroke,
580                                   GimpAnchor            *anchor,
581                                   const GimpCoords      *delta,
582                                   GimpAnchorFeatureType  feature)
583 {
584   g_return_if_fail (GIMP_IS_STROKE (stroke));
585   g_return_if_fail (anchor != NULL);
586   g_return_if_fail (g_queue_find (stroke->anchors, anchor));
587 
588   GIMP_STROKE_GET_CLASS (stroke)->anchor_move_relative (stroke, anchor,
589                                                         delta, feature);
590 }
591 
592 static void
gimp_stroke_real_anchor_move_relative(GimpStroke * stroke,GimpAnchor * anchor,const GimpCoords * delta,GimpAnchorFeatureType feature)593 gimp_stroke_real_anchor_move_relative (GimpStroke            *stroke,
594                                        GimpAnchor            *anchor,
595                                        const GimpCoords      *delta,
596                                        GimpAnchorFeatureType  feature)
597 {
598   anchor->position.x += delta->x;
599   anchor->position.y += delta->y;
600 }
601 
602 
603 void
gimp_stroke_anchor_move_absolute(GimpStroke * stroke,GimpAnchor * anchor,const GimpCoords * coord,GimpAnchorFeatureType feature)604 gimp_stroke_anchor_move_absolute (GimpStroke            *stroke,
605                                   GimpAnchor            *anchor,
606                                   const GimpCoords      *coord,
607                                   GimpAnchorFeatureType  feature)
608 {
609   g_return_if_fail (GIMP_IS_STROKE (stroke));
610   g_return_if_fail (anchor != NULL);
611   g_return_if_fail (g_queue_find (stroke->anchors, anchor));
612 
613   GIMP_STROKE_GET_CLASS (stroke)->anchor_move_absolute (stroke, anchor,
614                                                         coord, feature);
615 }
616 
617 static void
gimp_stroke_real_anchor_move_absolute(GimpStroke * stroke,GimpAnchor * anchor,const GimpCoords * coord,GimpAnchorFeatureType feature)618 gimp_stroke_real_anchor_move_absolute (GimpStroke            *stroke,
619                                        GimpAnchor            *anchor,
620                                        const GimpCoords      *coord,
621                                        GimpAnchorFeatureType  feature)
622 {
623   anchor->position.x = coord->x;
624   anchor->position.y = coord->y;
625 }
626 
627 gboolean
gimp_stroke_point_is_movable(GimpStroke * stroke,GimpAnchor * predec,gdouble position)628 gimp_stroke_point_is_movable (GimpStroke *stroke,
629                               GimpAnchor *predec,
630                               gdouble     position)
631 {
632   g_return_val_if_fail (GIMP_IS_STROKE (stroke), FALSE);
633 
634   return GIMP_STROKE_GET_CLASS (stroke)->point_is_movable (stroke, predec,
635                                                            position);
636 }
637 
638 
639 static gboolean
gimp_stroke_real_point_is_movable(GimpStroke * stroke,GimpAnchor * predec,gdouble position)640 gimp_stroke_real_point_is_movable (GimpStroke *stroke,
641                                    GimpAnchor *predec,
642                                    gdouble     position)
643 {
644   return FALSE;
645 }
646 
647 
648 void
gimp_stroke_point_move_relative(GimpStroke * stroke,GimpAnchor * predec,gdouble position,const GimpCoords * deltacoord,GimpAnchorFeatureType feature)649 gimp_stroke_point_move_relative (GimpStroke            *stroke,
650                                  GimpAnchor            *predec,
651                                  gdouble                position,
652                                  const GimpCoords      *deltacoord,
653                                  GimpAnchorFeatureType  feature)
654 {
655   g_return_if_fail (GIMP_IS_STROKE (stroke));
656 
657   GIMP_STROKE_GET_CLASS (stroke)->point_move_relative (stroke, predec,
658                                                        position, deltacoord,
659                                                        feature);
660 }
661 
662 
663 static void
gimp_stroke_real_point_move_relative(GimpStroke * stroke,GimpAnchor * predec,gdouble position,const GimpCoords * deltacoord,GimpAnchorFeatureType feature)664 gimp_stroke_real_point_move_relative (GimpStroke           *stroke,
665                                       GimpAnchor           *predec,
666                                       gdouble               position,
667                                       const GimpCoords     *deltacoord,
668                                       GimpAnchorFeatureType feature)
669 {
670   g_printerr ("gimp_stroke_point_move_relative: default implementation\n");
671 }
672 
673 
674 void
gimp_stroke_point_move_absolute(GimpStroke * stroke,GimpAnchor * predec,gdouble position,const GimpCoords * coord,GimpAnchorFeatureType feature)675 gimp_stroke_point_move_absolute (GimpStroke            *stroke,
676                                  GimpAnchor            *predec,
677                                  gdouble                position,
678                                  const GimpCoords      *coord,
679                                  GimpAnchorFeatureType  feature)
680 {
681   g_return_if_fail (GIMP_IS_STROKE (stroke));
682 
683   GIMP_STROKE_GET_CLASS (stroke)->point_move_absolute (stroke, predec,
684                                                        position, coord,
685                                                        feature);
686 }
687 
688 static void
gimp_stroke_real_point_move_absolute(GimpStroke * stroke,GimpAnchor * predec,gdouble position,const GimpCoords * coord,GimpAnchorFeatureType feature)689 gimp_stroke_real_point_move_absolute (GimpStroke           *stroke,
690                                       GimpAnchor           *predec,
691                                       gdouble               position,
692                                       const GimpCoords     *coord,
693                                       GimpAnchorFeatureType feature)
694 {
695   g_printerr ("gimp_stroke_point_move_absolute: default implementation\n");
696 }
697 
698 
699 void
gimp_stroke_close(GimpStroke * stroke)700 gimp_stroke_close (GimpStroke *stroke)
701 {
702   g_return_if_fail (GIMP_IS_STROKE (stroke));
703   g_return_if_fail (g_queue_is_empty (stroke->anchors) == FALSE);
704 
705   GIMP_STROKE_GET_CLASS (stroke)->close (stroke);
706 }
707 
708 static void
gimp_stroke_real_close(GimpStroke * stroke)709 gimp_stroke_real_close (GimpStroke *stroke)
710 {
711   stroke->closed = TRUE;
712   g_object_notify (G_OBJECT (stroke), "closed");
713 }
714 
715 
716 void
gimp_stroke_anchor_convert(GimpStroke * stroke,GimpAnchor * anchor,GimpAnchorFeatureType feature)717 gimp_stroke_anchor_convert (GimpStroke            *stroke,
718                             GimpAnchor            *anchor,
719                             GimpAnchorFeatureType  feature)
720 {
721   g_return_if_fail (GIMP_IS_STROKE (stroke));
722 
723   GIMP_STROKE_GET_CLASS (stroke)->anchor_convert (stroke, anchor, feature);
724 }
725 
726 static void
gimp_stroke_real_anchor_convert(GimpStroke * stroke,GimpAnchor * anchor,GimpAnchorFeatureType feature)727 gimp_stroke_real_anchor_convert (GimpStroke            *stroke,
728                                  GimpAnchor            *anchor,
729                                  GimpAnchorFeatureType  feature)
730 {
731   g_printerr ("gimp_stroke_anchor_convert: default implementation\n");
732 }
733 
734 
735 void
gimp_stroke_anchor_delete(GimpStroke * stroke,GimpAnchor * anchor)736 gimp_stroke_anchor_delete (GimpStroke *stroke,
737                            GimpAnchor *anchor)
738 {
739   g_return_if_fail (GIMP_IS_STROKE (stroke));
740   g_return_if_fail (anchor && anchor->type == GIMP_ANCHOR_ANCHOR);
741 
742   GIMP_STROKE_GET_CLASS (stroke)->anchor_delete (stroke, anchor);
743 }
744 
745 static void
gimp_stroke_real_anchor_delete(GimpStroke * stroke,GimpAnchor * anchor)746 gimp_stroke_real_anchor_delete (GimpStroke *stroke,
747                                 GimpAnchor *anchor)
748 {
749   g_printerr ("gimp_stroke_anchor_delete: default implementation\n");
750 }
751 
752 GimpStroke *
gimp_stroke_open(GimpStroke * stroke,GimpAnchor * end_anchor)753 gimp_stroke_open (GimpStroke *stroke,
754                   GimpAnchor *end_anchor)
755 {
756   g_return_val_if_fail (GIMP_IS_STROKE (stroke), NULL);
757   g_return_val_if_fail (end_anchor &&
758                         end_anchor->type == GIMP_ANCHOR_ANCHOR, NULL);
759 
760   return GIMP_STROKE_GET_CLASS (stroke)->open (stroke, end_anchor);
761 }
762 
763 static GimpStroke *
gimp_stroke_real_open(GimpStroke * stroke,GimpAnchor * end_anchor)764 gimp_stroke_real_open (GimpStroke *stroke,
765                        GimpAnchor *end_anchor)
766 {
767   g_printerr ("gimp_stroke_open: default implementation\n");
768   return NULL;
769 }
770 
771 gboolean
gimp_stroke_anchor_is_insertable(GimpStroke * stroke,GimpAnchor * predec,gdouble position)772 gimp_stroke_anchor_is_insertable (GimpStroke *stroke,
773                                   GimpAnchor *predec,
774                                   gdouble     position)
775 {
776   g_return_val_if_fail (GIMP_IS_STROKE (stroke), FALSE);
777 
778   return GIMP_STROKE_GET_CLASS (stroke)->anchor_is_insertable (stroke,
779                                                                predec,
780                                                                position);
781 }
782 
783 static gboolean
gimp_stroke_real_anchor_is_insertable(GimpStroke * stroke,GimpAnchor * predec,gdouble position)784 gimp_stroke_real_anchor_is_insertable (GimpStroke *stroke,
785                                        GimpAnchor *predec,
786                                        gdouble     position)
787 {
788   g_return_val_if_fail (GIMP_IS_STROKE (stroke), FALSE);
789 
790   return FALSE;
791 }
792 
793 GimpAnchor *
gimp_stroke_anchor_insert(GimpStroke * stroke,GimpAnchor * predec,gdouble position)794 gimp_stroke_anchor_insert (GimpStroke *stroke,
795                            GimpAnchor *predec,
796                            gdouble     position)
797 {
798   g_return_val_if_fail (GIMP_IS_STROKE (stroke), NULL);
799   g_return_val_if_fail (predec->type == GIMP_ANCHOR_ANCHOR, NULL);
800 
801   return GIMP_STROKE_GET_CLASS (stroke)->anchor_insert (stroke,
802                                                         predec, position);
803 }
804 
805 static GimpAnchor *
gimp_stroke_real_anchor_insert(GimpStroke * stroke,GimpAnchor * predec,gdouble position)806 gimp_stroke_real_anchor_insert (GimpStroke *stroke,
807                                 GimpAnchor *predec,
808                                 gdouble     position)
809 {
810   g_return_val_if_fail (GIMP_IS_STROKE (stroke), NULL);
811 
812   return NULL;
813 }
814 
815 
816 gboolean
gimp_stroke_is_extendable(GimpStroke * stroke,GimpAnchor * neighbor)817 gimp_stroke_is_extendable (GimpStroke *stroke,
818                            GimpAnchor *neighbor)
819 {
820   g_return_val_if_fail (GIMP_IS_STROKE (stroke), FALSE);
821 
822   return GIMP_STROKE_GET_CLASS (stroke)->is_extendable (stroke, neighbor);
823 }
824 
825 static gboolean
gimp_stroke_real_is_extendable(GimpStroke * stroke,GimpAnchor * neighbor)826 gimp_stroke_real_is_extendable (GimpStroke *stroke,
827                                 GimpAnchor *neighbor)
828 {
829   return FALSE;
830 }
831 
832 
833 GimpAnchor *
gimp_stroke_extend(GimpStroke * stroke,const GimpCoords * coords,GimpAnchor * neighbor,GimpVectorExtendMode extend_mode)834 gimp_stroke_extend (GimpStroke           *stroke,
835                     const GimpCoords     *coords,
836                     GimpAnchor           *neighbor,
837                     GimpVectorExtendMode  extend_mode)
838 {
839   g_return_val_if_fail (GIMP_IS_STROKE (stroke), NULL);
840   g_return_val_if_fail (!stroke->closed, NULL);
841 
842   return GIMP_STROKE_GET_CLASS (stroke)->extend (stroke, coords,
843                                                  neighbor, extend_mode);
844 }
845 
846 static GimpAnchor *
gimp_stroke_real_extend(GimpStroke * stroke,const GimpCoords * coords,GimpAnchor * neighbor,GimpVectorExtendMode extend_mode)847 gimp_stroke_real_extend (GimpStroke           *stroke,
848                          const GimpCoords     *coords,
849                          GimpAnchor           *neighbor,
850                          GimpVectorExtendMode  extend_mode)
851 {
852   g_printerr ("gimp_stroke_extend: default implementation\n");
853   return NULL;
854 }
855 
856 gboolean
gimp_stroke_connect_stroke(GimpStroke * stroke,GimpAnchor * anchor,GimpStroke * extension,GimpAnchor * neighbor)857 gimp_stroke_connect_stroke (GimpStroke *stroke,
858                             GimpAnchor *anchor,
859                             GimpStroke *extension,
860                             GimpAnchor *neighbor)
861 {
862   g_return_val_if_fail (GIMP_IS_STROKE (stroke), FALSE);
863   g_return_val_if_fail (GIMP_IS_STROKE (extension), FALSE);
864   g_return_val_if_fail (stroke->closed == FALSE &&
865                         extension->closed == FALSE, FALSE);
866 
867   return GIMP_STROKE_GET_CLASS (stroke)->connect_stroke (stroke, anchor,
868                                                          extension, neighbor);
869 }
870 
871 gboolean
gimp_stroke_real_connect_stroke(GimpStroke * stroke,GimpAnchor * anchor,GimpStroke * extension,GimpAnchor * neighbor)872 gimp_stroke_real_connect_stroke (GimpStroke *stroke,
873                                  GimpAnchor *anchor,
874                                  GimpStroke *extension,
875                                  GimpAnchor *neighbor)
876 {
877   g_printerr ("gimp_stroke_connect_stroke: default implementation\n");
878   return FALSE;
879 }
880 
881 gboolean
gimp_stroke_is_empty(GimpStroke * stroke)882 gimp_stroke_is_empty (GimpStroke *stroke)
883 {
884   g_return_val_if_fail (GIMP_IS_STROKE (stroke), FALSE);
885 
886   return GIMP_STROKE_GET_CLASS (stroke)->is_empty (stroke);
887 }
888 
889 static gboolean
gimp_stroke_real_is_empty(GimpStroke * stroke)890 gimp_stroke_real_is_empty (GimpStroke *stroke)
891 {
892   return g_queue_is_empty (stroke->anchors);
893 }
894 
895 
896 gdouble
gimp_stroke_get_length(GimpStroke * stroke,gdouble precision)897 gimp_stroke_get_length (GimpStroke *stroke,
898                         gdouble     precision)
899 {
900   g_return_val_if_fail (GIMP_IS_STROKE (stroke), 0.0);
901 
902   return GIMP_STROKE_GET_CLASS (stroke)->get_length (stroke, precision);
903 }
904 
905 static gdouble
gimp_stroke_real_get_length(GimpStroke * stroke,gdouble precision)906 gimp_stroke_real_get_length (GimpStroke *stroke,
907                              gdouble     precision)
908 {
909   GArray     *points;
910   gint        i;
911   gdouble     length;
912   GimpCoords  difference;
913 
914   if (g_queue_is_empty (stroke->anchors))
915     return -1;
916 
917   points = gimp_stroke_interpolate (stroke, precision, NULL);
918   if (points == NULL)
919     return -1;
920 
921   length = 0;
922 
923   for (i = 0; i < points->len - 1; i++ )
924     {
925        gimp_coords_difference (&(g_array_index (points, GimpCoords, i)),
926                                &(g_array_index (points, GimpCoords, i+1)),
927                                &difference);
928        length += gimp_coords_length (&difference);
929     }
930 
931   g_array_free(points, TRUE);
932 
933   return length;
934 }
935 
936 
937 gdouble
gimp_stroke_get_distance(GimpStroke * stroke,const GimpCoords * coord)938 gimp_stroke_get_distance (GimpStroke       *stroke,
939                           const GimpCoords *coord)
940 {
941   g_return_val_if_fail (GIMP_IS_STROKE (stroke), 0.0);
942 
943   return GIMP_STROKE_GET_CLASS (stroke)->get_distance (stroke, coord);
944 }
945 
946 static gdouble
gimp_stroke_real_get_distance(GimpStroke * stroke,const GimpCoords * coord)947 gimp_stroke_real_get_distance (GimpStroke       *stroke,
948                                const GimpCoords *coord)
949 {
950   g_printerr ("gimp_stroke_get_distance: default implementation\n");
951 
952   return 0.0;
953 }
954 
955 
956 GArray *
gimp_stroke_interpolate(GimpStroke * stroke,gdouble precision,gboolean * ret_closed)957 gimp_stroke_interpolate (GimpStroke *stroke,
958                          gdouble     precision,
959                          gboolean   *ret_closed)
960 {
961   g_return_val_if_fail (GIMP_IS_STROKE (stroke), NULL);
962 
963   return GIMP_STROKE_GET_CLASS (stroke)->interpolate (stroke, precision,
964                                                       ret_closed);
965 }
966 
967 static GArray *
gimp_stroke_real_interpolate(GimpStroke * stroke,gdouble precision,gboolean * ret_closed)968 gimp_stroke_real_interpolate (GimpStroke *stroke,
969                               gdouble     precision,
970                               gboolean   *ret_closed)
971 {
972   g_printerr ("gimp_stroke_interpolate: default implementation\n");
973 
974   return NULL;
975 }
976 
977 GimpStroke *
gimp_stroke_duplicate(GimpStroke * stroke)978 gimp_stroke_duplicate (GimpStroke *stroke)
979 {
980   g_return_val_if_fail (GIMP_IS_STROKE (stroke), NULL);
981 
982   return GIMP_STROKE_GET_CLASS (stroke)->duplicate (stroke);
983 }
984 
985 static GimpStroke *
gimp_stroke_real_duplicate(GimpStroke * stroke)986 gimp_stroke_real_duplicate (GimpStroke *stroke)
987 {
988   GimpStroke *new_stroke;
989   GList      *list;
990 
991   new_stroke = g_object_new (G_TYPE_FROM_INSTANCE (stroke),
992                              "name", gimp_object_get_name (stroke),
993                              NULL);
994 
995   g_queue_free_full (new_stroke->anchors, (GDestroyNotify) gimp_anchor_free);
996   new_stroke->anchors = g_queue_copy (stroke->anchors);
997 
998   for (list = new_stroke->anchors->head; list; list = g_list_next (list))
999     {
1000       list->data = gimp_anchor_copy (GIMP_ANCHOR (list->data));
1001     }
1002 
1003   new_stroke->closed = stroke->closed;
1004   /* we do *not* copy the ID! */
1005 
1006   return new_stroke;
1007 }
1008 
1009 
1010 GimpBezierDesc *
gimp_stroke_make_bezier(GimpStroke * stroke)1011 gimp_stroke_make_bezier (GimpStroke *stroke)
1012 {
1013   g_return_val_if_fail (GIMP_IS_STROKE (stroke), NULL);
1014 
1015   return GIMP_STROKE_GET_CLASS (stroke)->make_bezier (stroke);
1016 }
1017 
1018 static GimpBezierDesc *
gimp_stroke_real_make_bezier(GimpStroke * stroke)1019 gimp_stroke_real_make_bezier (GimpStroke *stroke)
1020 {
1021   g_printerr ("gimp_stroke_make_bezier: default implementation\n");
1022 
1023   return NULL;
1024 }
1025 
1026 
1027 void
gimp_stroke_translate(GimpStroke * stroke,gdouble offset_x,gdouble offset_y)1028 gimp_stroke_translate (GimpStroke *stroke,
1029                        gdouble     offset_x,
1030                        gdouble     offset_y)
1031 {
1032   g_return_if_fail (GIMP_IS_STROKE (stroke));
1033 
1034   GIMP_STROKE_GET_CLASS (stroke)->translate (stroke, offset_x, offset_y);
1035 }
1036 
1037 static void
gimp_stroke_real_translate(GimpStroke * stroke,gdouble offset_x,gdouble offset_y)1038 gimp_stroke_real_translate (GimpStroke *stroke,
1039                             gdouble     offset_x,
1040                             gdouble     offset_y)
1041 {
1042   GList *list;
1043 
1044   for (list = stroke->anchors->head; list; list = g_list_next (list))
1045     {
1046       GimpAnchor *anchor = list->data;
1047 
1048       anchor->position.x += offset_x;
1049       anchor->position.y += offset_y;
1050     }
1051 }
1052 
1053 
1054 void
gimp_stroke_scale(GimpStroke * stroke,gdouble scale_x,gdouble scale_y)1055 gimp_stroke_scale (GimpStroke *stroke,
1056                    gdouble     scale_x,
1057                    gdouble     scale_y)
1058 {
1059   g_return_if_fail (GIMP_IS_STROKE (stroke));
1060 
1061   GIMP_STROKE_GET_CLASS (stroke)->scale (stroke, scale_x, scale_y);
1062 }
1063 
1064 static void
gimp_stroke_real_scale(GimpStroke * stroke,gdouble scale_x,gdouble scale_y)1065 gimp_stroke_real_scale (GimpStroke *stroke,
1066                         gdouble     scale_x,
1067                         gdouble     scale_y)
1068 {
1069   GList *list;
1070 
1071   for (list = stroke->anchors->head; list; list = g_list_next (list))
1072     {
1073       GimpAnchor *anchor = list->data;
1074 
1075       anchor->position.x *= scale_x;
1076       anchor->position.y *= scale_y;
1077     }
1078 }
1079 
1080 void
gimp_stroke_rotate(GimpStroke * stroke,gdouble center_x,gdouble center_y,gdouble angle)1081 gimp_stroke_rotate (GimpStroke *stroke,
1082                     gdouble     center_x,
1083                     gdouble     center_y,
1084                     gdouble     angle)
1085 {
1086   g_return_if_fail (GIMP_IS_STROKE (stroke));
1087 
1088   GIMP_STROKE_GET_CLASS (stroke)->rotate (stroke, center_x, center_y, angle);
1089 }
1090 
1091 static void
gimp_stroke_real_rotate(GimpStroke * stroke,gdouble center_x,gdouble center_y,gdouble angle)1092 gimp_stroke_real_rotate (GimpStroke *stroke,
1093                          gdouble     center_x,
1094                          gdouble     center_y,
1095                          gdouble     angle)
1096 {
1097   GimpMatrix3  matrix;
1098 
1099   angle = angle / 180.0 * G_PI;
1100   gimp_matrix3_identity (&matrix);
1101   gimp_transform_matrix_rotate_center (&matrix, center_x, center_y, angle);
1102 
1103   gimp_stroke_transform (stroke, &matrix, NULL);
1104 }
1105 
1106 void
gimp_stroke_flip(GimpStroke * stroke,GimpOrientationType flip_type,gdouble axis)1107 gimp_stroke_flip (GimpStroke          *stroke,
1108                   GimpOrientationType  flip_type,
1109                   gdouble              axis)
1110 {
1111   g_return_if_fail (GIMP_IS_STROKE (stroke));
1112 
1113   GIMP_STROKE_GET_CLASS (stroke)->flip (stroke, flip_type, axis);
1114 }
1115 
1116 static void
gimp_stroke_real_flip(GimpStroke * stroke,GimpOrientationType flip_type,gdouble axis)1117 gimp_stroke_real_flip (GimpStroke          *stroke,
1118                        GimpOrientationType  flip_type,
1119                        gdouble              axis)
1120 {
1121   GimpMatrix3  matrix;
1122 
1123   gimp_matrix3_identity (&matrix);
1124   gimp_transform_matrix_flip (&matrix, flip_type, axis);
1125   gimp_stroke_transform (stroke, &matrix, NULL);
1126 }
1127 
1128 void
gimp_stroke_flip_free(GimpStroke * stroke,gdouble x1,gdouble y1,gdouble x2,gdouble y2)1129 gimp_stroke_flip_free (GimpStroke *stroke,
1130                        gdouble     x1,
1131                        gdouble     y1,
1132                        gdouble     x2,
1133                        gdouble     y2)
1134 {
1135   g_return_if_fail (GIMP_IS_STROKE (stroke));
1136 
1137   GIMP_STROKE_GET_CLASS (stroke)->flip_free (stroke, x1, y1, x2, y2);
1138 }
1139 
1140 static void
gimp_stroke_real_flip_free(GimpStroke * stroke,gdouble x1,gdouble y1,gdouble x2,gdouble y2)1141 gimp_stroke_real_flip_free (GimpStroke *stroke,
1142                             gdouble     x1,
1143                             gdouble     y1,
1144                             gdouble     x2,
1145                             gdouble     y2)
1146 {
1147   /* x, y, width and height parameter in gimp_transform_matrix_flip_free are unused */
1148   GimpMatrix3  matrix;
1149 
1150   gimp_matrix3_identity (&matrix);
1151   gimp_transform_matrix_flip_free (&matrix, x1, y1, x2, y2);
1152 
1153   gimp_stroke_transform (stroke, &matrix, NULL);
1154 }
1155 
1156 /* transforms 'stroke' by 'matrix'.  due to clipping, the transformation may
1157  * result in multiple strokes.
1158  *
1159  * if 'ret_strokes' is not NULL, the transformed strokes are appended to the
1160  * queue, and 'stroke' is left in an unspecified state.  one of the resulting
1161  * strokes may alias 'stroke'.
1162  *
1163  * if 'ret_strokes' is NULL, the transformation is performed in-place.  if the
1164  * transformation results in multiple strokes (which, atm, can only happen for
1165  * non-affine transformation), the result is undefined.
1166  */
1167 void
gimp_stroke_transform(GimpStroke * stroke,const GimpMatrix3 * matrix,GQueue * ret_strokes)1168 gimp_stroke_transform (GimpStroke        *stroke,
1169                        const GimpMatrix3 *matrix,
1170                        GQueue            *ret_strokes)
1171 {
1172   g_return_if_fail (GIMP_IS_STROKE (stroke));
1173 
1174   GIMP_STROKE_GET_CLASS (stroke)->transform (stroke, matrix, ret_strokes);
1175 }
1176 
1177 static void
gimp_stroke_real_transform(GimpStroke * stroke,const GimpMatrix3 * matrix,GQueue * ret_strokes)1178 gimp_stroke_real_transform (GimpStroke        *stroke,
1179                             const GimpMatrix3 *matrix,
1180                             GQueue            *ret_strokes)
1181 {
1182   GList *list;
1183 
1184   for (list = stroke->anchors->head; list; list = g_list_next (list))
1185     {
1186       GimpAnchor *anchor = list->data;
1187 
1188       gimp_matrix3_transform_point (matrix,
1189                                     anchor->position.x,
1190                                     anchor->position.y,
1191                                     &anchor->position.x,
1192                                     &anchor->position.y);
1193     }
1194 
1195   if (ret_strokes)
1196     {
1197       stroke->ID = 0;
1198 
1199       g_queue_push_tail (ret_strokes, g_object_ref (stroke));
1200     }
1201 }
1202 
1203 
1204 GList *
gimp_stroke_get_draw_anchors(GimpStroke * stroke)1205 gimp_stroke_get_draw_anchors (GimpStroke *stroke)
1206 {
1207   g_return_val_if_fail (GIMP_IS_STROKE (stroke), NULL);
1208 
1209   return GIMP_STROKE_GET_CLASS (stroke)->get_draw_anchors (stroke);
1210 }
1211 
1212 static GList *
gimp_stroke_real_get_draw_anchors(GimpStroke * stroke)1213 gimp_stroke_real_get_draw_anchors (GimpStroke *stroke)
1214 {
1215   GList *list;
1216   GList *ret_list = NULL;
1217 
1218   for (list = stroke->anchors->head; list; list = g_list_next (list))
1219     {
1220       if (GIMP_ANCHOR (list->data)->type == GIMP_ANCHOR_ANCHOR)
1221         ret_list = g_list_prepend (ret_list, list->data);
1222     }
1223 
1224   return g_list_reverse (ret_list);
1225 }
1226 
1227 
1228 GList *
gimp_stroke_get_draw_controls(GimpStroke * stroke)1229 gimp_stroke_get_draw_controls (GimpStroke *stroke)
1230 {
1231   g_return_val_if_fail (GIMP_IS_STROKE (stroke), NULL);
1232 
1233   return GIMP_STROKE_GET_CLASS (stroke)->get_draw_controls (stroke);
1234 }
1235 
1236 static GList *
gimp_stroke_real_get_draw_controls(GimpStroke * stroke)1237 gimp_stroke_real_get_draw_controls (GimpStroke *stroke)
1238 {
1239   GList *list;
1240   GList *ret_list = NULL;
1241 
1242   for (list = stroke->anchors->head; list; list = g_list_next (list))
1243     {
1244       GimpAnchor *anchor = list->data;
1245 
1246       if (anchor->type == GIMP_ANCHOR_CONTROL)
1247         {
1248           GimpAnchor *next = list->next ? list->next->data : NULL;
1249           GimpAnchor *prev = list->prev ? list->prev->data : NULL;
1250 
1251           if (next && next->type == GIMP_ANCHOR_ANCHOR && next->selected)
1252             {
1253               /* Ok, this is a hack.
1254                * The idea is to give control points at the end of a
1255                * stroke a higher priority for the interactive tool.
1256                */
1257               if (prev)
1258                 ret_list = g_list_prepend (ret_list, anchor);
1259               else
1260                 ret_list = g_list_append (ret_list, anchor);
1261             }
1262           else if (prev && prev->type == GIMP_ANCHOR_ANCHOR && prev->selected)
1263             {
1264               /* same here... */
1265               if (next)
1266                 ret_list = g_list_prepend (ret_list, anchor);
1267               else
1268                 ret_list = g_list_append (ret_list, anchor);
1269             }
1270         }
1271     }
1272 
1273   return g_list_reverse (ret_list);
1274 }
1275 
1276 
1277 GArray *
gimp_stroke_get_draw_lines(GimpStroke * stroke)1278 gimp_stroke_get_draw_lines (GimpStroke *stroke)
1279 {
1280   g_return_val_if_fail (GIMP_IS_STROKE (stroke), NULL);
1281 
1282   return GIMP_STROKE_GET_CLASS (stroke)->get_draw_lines (stroke);
1283 }
1284 
1285 static GArray *
gimp_stroke_real_get_draw_lines(GimpStroke * stroke)1286 gimp_stroke_real_get_draw_lines (GimpStroke *stroke)
1287 {
1288   GList  *list;
1289   GArray *ret_lines = NULL;
1290   gint    count = 0;
1291 
1292   for (list = stroke->anchors->head; list; list = g_list_next (list))
1293     {
1294       GimpAnchor *anchor = list->data;
1295 
1296       if (anchor->type == GIMP_ANCHOR_ANCHOR && anchor->selected)
1297         {
1298           if (list->next)
1299             {
1300               GimpAnchor *next = list->next->data;
1301 
1302               if (count == 0)
1303                 ret_lines = g_array_new (FALSE, FALSE, sizeof (GimpCoords));
1304 
1305               ret_lines = g_array_append_val (ret_lines, anchor->position);
1306               ret_lines = g_array_append_val (ret_lines, next->position);
1307               count += 1;
1308             }
1309 
1310           if (list->prev)
1311             {
1312               GimpAnchor *prev = list->prev->data;
1313 
1314               if (count == 0)
1315                 ret_lines = g_array_new (FALSE, FALSE, sizeof (GimpCoords));
1316 
1317               ret_lines = g_array_append_val (ret_lines, anchor->position);
1318               ret_lines = g_array_append_val (ret_lines, prev->position);
1319               count += 1;
1320             }
1321         }
1322     }
1323 
1324   return ret_lines;
1325 }
1326 
1327 GArray *
gimp_stroke_control_points_get(GimpStroke * stroke,gboolean * ret_closed)1328 gimp_stroke_control_points_get (GimpStroke *stroke,
1329                                 gboolean   *ret_closed)
1330 {
1331   g_return_val_if_fail (GIMP_IS_STROKE (stroke), NULL);
1332 
1333   return GIMP_STROKE_GET_CLASS (stroke)->control_points_get (stroke,
1334                                                              ret_closed);
1335 }
1336 
1337 static GArray *
gimp_stroke_real_control_points_get(GimpStroke * stroke,gboolean * ret_closed)1338 gimp_stroke_real_control_points_get (GimpStroke *stroke,
1339                                      gboolean   *ret_closed)
1340 {
1341   guint   num_anchors;
1342   GArray *ret_array;
1343   GList  *list;
1344 
1345   num_anchors = g_queue_get_length (stroke->anchors);
1346   ret_array = g_array_sized_new (FALSE, FALSE,
1347                                  sizeof (GimpAnchor), num_anchors);
1348 
1349   for (list = stroke->anchors->head; list; list = g_list_next (list))
1350     {
1351       g_array_append_vals (ret_array, list->data, 1);
1352     }
1353 
1354   if (ret_closed)
1355     *ret_closed = stroke->closed;
1356 
1357   return ret_array;
1358 }
1359 
1360 gboolean
gimp_stroke_get_point_at_dist(GimpStroke * stroke,gdouble dist,gdouble precision,GimpCoords * position,gdouble * slope)1361 gimp_stroke_get_point_at_dist (GimpStroke *stroke,
1362                                gdouble     dist,
1363                                gdouble     precision,
1364                                GimpCoords *position,
1365                                gdouble    *slope)
1366 {
1367   g_return_val_if_fail (GIMP_IS_STROKE (stroke), FALSE);
1368 
1369   return GIMP_STROKE_GET_CLASS (stroke)->get_point_at_dist (stroke,
1370                                                             dist,
1371                                                             precision,
1372                                                             position,
1373                                                             slope);
1374 }
1375 
1376 
1377 static gboolean
gimp_stroke_real_get_point_at_dist(GimpStroke * stroke,gdouble dist,gdouble precision,GimpCoords * position,gdouble * slope)1378 gimp_stroke_real_get_point_at_dist (GimpStroke *stroke,
1379                                     gdouble     dist,
1380                                     gdouble     precision,
1381                                     GimpCoords *position,
1382                                     gdouble    *slope)
1383 {
1384   GArray     *points;
1385   gint        i;
1386   gdouble     length;
1387   gdouble     segment_length;
1388   gboolean    ret = FALSE;
1389   GimpCoords  difference;
1390 
1391   points = gimp_stroke_interpolate (stroke, precision, NULL);
1392   if (points == NULL)
1393     return ret;
1394 
1395   length = 0;
1396   for (i=0; i < points->len - 1; i++)
1397     {
1398       gimp_coords_difference (&(g_array_index (points, GimpCoords , i)),
1399                               &(g_array_index (points, GimpCoords , i+1)),
1400                               &difference);
1401       segment_length = gimp_coords_length (&difference);
1402 
1403       if (segment_length == 0 || length + segment_length < dist )
1404         {
1405           length += segment_length;
1406         }
1407       else
1408         {
1409           /* x = x1 + (x2 - x1 ) u  */
1410           /* x   = x1 (1-u) + u x2  */
1411 
1412           gdouble u = (dist - length) / segment_length;
1413 
1414           gimp_coords_mix (1 - u, &(g_array_index (points, GimpCoords , i)),
1415                                u, &(g_array_index (points, GimpCoords , i+1)),
1416                            position);
1417 
1418           if (difference.x == 0)
1419             *slope = G_MAXDOUBLE;
1420           else
1421             *slope = difference.y / difference.x;
1422 
1423           ret = TRUE;
1424           break;
1425         }
1426     }
1427 
1428   g_array_free (points, TRUE);
1429 
1430   return ret;
1431 }
1432