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