1 /* ADG - Automatic Drawing Generation
2 * Copyright (C) 2007-2008, Nicola Fontana <ntd at entidi.it>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
24 * @short_description: A stroked path entity
26 * The #AdgPath object is peraphs the simplest entity.
28 * It contains a pointer to the desired #cairo_path_t structure.
32 #include "adg-path-private.h"
33 #include "adg-line-style.h"
35 #include "adg-canvas.h"
39 #define PARENT_CLASS ((AdgEntityClass *) adg_path_parent_class)
40 #define ARC_TOLERANCE 0.1
49 typedef enum _Direction Direction
;
57 static void get_property (GObject
*object
,
61 static void set_property (GObject
*object
,
65 static void finalize (GObject
*object
);
66 static const AdgLineStyle
*
67 get_line_style (AdgEntity
*entity
);
68 static void set_line_style (AdgEntity
*entity
,
69 AdgLineStyle
*line_style
);
70 static void render (AdgEntity
*entity
,
72 static void add_portion (AdgPath
*path
,
73 cairo_path_data_type_t type
,
75 /* Adapted from cairo-1.3.8 */
76 static double arc_error_normalized (double angle
);
77 static double arc_max_angle_for_tolerance_normalized
79 static int arc_segments_needed (double angle
,
82 static void arc_segment (AdgPath
*path
,
88 static void arc_in_direction (AdgPath
*path
,
97 G_DEFINE_TYPE (AdgPath
, adg_path
, ADG_TYPE_ENTITY
);
101 adg_path_class_init (AdgPathClass
*klass
)
103 GObjectClass
*gobject_class
;
104 AdgEntityClass
*entity_class
;
107 gobject_class
= (GObjectClass
*) klass
;
108 entity_class
= (AdgEntityClass
*) klass
;
110 g_type_class_add_private (klass
, sizeof (AdgPathPrivate
));
112 gobject_class
->get_property
= get_property
;
113 gobject_class
->set_property
= set_property
;
114 gobject_class
->finalize
= finalize
;
116 entity_class
->get_line_style
= get_line_style
;
117 entity_class
->set_line_style
= set_line_style
;
118 entity_class
->render
= render
;
120 param
= g_param_spec_boxed ("line-style",
122 P_("Line style to use while rendering the path"),
125 g_object_class_install_property (gobject_class
, PROP_LINE_STYLE
, param
);
129 adg_path_init (AdgPath
*path
)
131 AdgPathPrivate
*priv
= G_TYPE_INSTANCE_GET_PRIVATE (path
, ADG_TYPE_PATH
,
134 priv
->line_style
= NULL
;
135 priv
->cairo_path
.status
= CAIRO_STATUS_SUCCESS
;
136 priv
->cairo_path
.data
= NULL
;
137 priv
->cairo_path
.num_data
= 0;
140 priv
->create_func
= NULL
;
141 priv
->user_data
= NULL
;
147 get_property (GObject
*object
,
152 AdgPathPrivate
*priv
= ((AdgPath
*) object
)->priv
;
156 case PROP_LINE_STYLE
:
157 g_value_set_boxed (value
, priv
->line_style
);
160 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
166 set_property (GObject
*object
,
171 AdgPath
*path
= (AdgPath
*) object
;
175 case PROP_LINE_STYLE
:
176 path
->priv
->line_style
= g_value_get_boxed (value
);
179 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
186 finalize (GObject
*object
)
188 adg_path_clear ((AdgPath
*) object
);
190 ((GObjectClass
*) PARENT_CLASS
)->finalize (object
);
194 static const AdgLineStyle
*
195 get_line_style (AdgEntity
*entity
)
197 return ((AdgPath
*) entity
)->priv
->line_style
;
201 set_line_style (AdgEntity
*entity
,
202 AdgLineStyle
*line_style
)
204 ((AdgPath
*) entity
)->priv
->line_style
= line_style
;
205 g_object_notify (G_OBJECT (entity
), "line-style");
209 render (AdgEntity
*entity
,
212 AdgPath
*path
= (AdgPath
*) entity
;
214 if (!adg_entity_model_applied (entity
) && path
->priv
->create_func
!= NULL
)
215 path
->priv
->create_func (entity
, path
->priv
->user_data
);
217 adg_style_apply (adg_entity_get_line_style (entity
), cr
);
218 cairo_append_path (cr
, &path
->priv
->cairo_path
);
221 PARENT_CLASS
->render (entity
, cr
);
225 add_portion (AdgPath
*path
,
226 cairo_path_data_type_t type
,
229 AdgPathPrivate
*priv
= path
->priv
;
230 cairo_path_data_t portion
;
234 priv
->portions
= g_array_sized_new (FALSE
, FALSE
,
235 sizeof (cairo_path_data_t
),
236 10 /* Preallocated elements */);
238 portion
.header
.type
= type
;
239 va_start (var_args
, type
);
243 case CAIRO_PATH_CLOSE_PATH
:
244 portion
.header
.length
= 1;
245 priv
->portions
= g_array_append_val (priv
->portions
, portion
);
246 portion
.point
.x
= 0.;
247 portion
.point
.y
= 0.;
250 case CAIRO_PATH_MOVE_TO
:
251 portion
.header
.length
= 2;
252 priv
->portions
= g_array_append_val (priv
->portions
, portion
);
253 portion
.point
.x
= va_arg (var_args
, double);
254 portion
.point
.y
= va_arg (var_args
, double);
255 priv
->portions
= g_array_append_val (priv
->portions
, portion
);
258 case CAIRO_PATH_LINE_TO
:
259 portion
.header
.length
= 2;
260 priv
->portions
= g_array_append_val (priv
->portions
, portion
);
261 portion
.point
.x
= va_arg (var_args
, double);
262 portion
.point
.y
= va_arg (var_args
, double);
263 priv
->portions
= g_array_append_val (priv
->portions
, portion
);
266 case CAIRO_PATH_CURVE_TO
:
267 portion
.header
.length
= 4;
268 priv
->portions
= g_array_append_val (priv
->portions
, portion
);
269 portion
.point
.x
= va_arg (var_args
, double);
270 portion
.point
.y
= va_arg (var_args
, double);
271 priv
->portions
= g_array_append_val (priv
->portions
, portion
);
272 portion
.point
.x
= va_arg (var_args
, double);
273 portion
.point
.y
= va_arg (var_args
, double);
274 priv
->portions
= g_array_append_val (priv
->portions
, portion
);
275 portion
.point
.x
= va_arg (var_args
, double);
276 portion
.point
.y
= va_arg (var_args
, double);
277 priv
->portions
= g_array_append_val (priv
->portions
, portion
);
281 g_assert_not_reached ();
284 priv
->cairo_path
.data
= (cairo_path_data_t
*) priv
->portions
->data
;
285 priv
->cairo_path
.num_data
= priv
->portions
->len
;
286 priv
->cairo_path
.status
= CAIRO_STATUS_SUCCESS
;
288 priv
->cp
.x
= portion
.point
.x
;
289 priv
->cp
.y
= portion
.point
.y
;
295 * @create_func: an #AdgCallback callback
296 * @user_data: user pointer
298 * Creates a new path entity using @create_func as creation callback.
300 * Return value: the new entity
303 adg_path_new (AdgCallback create_func
,
307 AdgPathPrivate
*priv
;
309 entity
= (AdgEntity
*) g_object_new (ADG_TYPE_PATH
, NULL
);
310 priv
= ((AdgPath
*) entity
)->priv
;
312 priv
->create_func
= create_func
;
313 priv
->user_data
= user_data
;
319 adg_path_clear (AdgPath
*path
)
321 AdgPathPrivate
*priv
;
323 g_return_if_fail (ADG_IS_PATH (path
));
327 g_array_free (priv
->portions
, TRUE
);
328 priv
->portions
= NULL
;
329 priv
->cairo_path
.status
= CAIRO_STATUS_SUCCESS
;
330 priv
->cairo_path
.data
= NULL
;
331 priv
->cairo_path
.num_data
= 0;
338 adg_path_get_cairo_path (AdgPath
*path
)
340 g_return_val_if_fail (ADG_IS_PATH (path
), NULL
);
342 return &path
->priv
->cairo_path
;
347 adg_path_chain_ymirror (AdgPath
*path
)
349 AdgPathPrivate
*priv
;
350 cairo_path_t
*cairo_path
;
351 cairo_path_data_t
*src
;
352 cairo_path_data_t
*p_src
, *p_dst
;
354 gint n_data
, n_point
;
355 double last_x
, last_y
;
357 g_return_if_fail (ADG_IS_PATH (path
));
360 cairo_path
= &priv
->cairo_path
;
362 g_return_if_fail (cairo_path
->num_data
> 2);
363 g_return_if_fail (cairo_path
->data
->header
.type
== CAIRO_PATH_MOVE_TO
);
365 src
= g_memdup (cairo_path
->data
, cairo_path
->num_data
* sizeof (cairo_path_data_t
));
366 priv
->portions
= g_array_set_size (priv
->portions
, cairo_path
->num_data
* 2);
368 p_dst
= (cairo_path_data_t
*) priv
->portions
->data
+ cairo_path
->num_data
* 2;
372 last_x
= p_src
->point
.x
;
373 last_y
= p_src
->point
.y
;
378 while (n_data
< cairo_path
->num_data
)
380 length
= p_src
->header
.length
;
382 p_dst
->header
.type
= p_src
->header
.type
;
383 p_dst
->header
.length
= length
;
386 for (n_point
= 1; n_point
< length
- 1; ++ n_point
)
388 p_dst
[length
-n_point
-1].point
.x
= p_src
->point
.x
;
389 p_dst
[length
-n_point
-1].point
.y
= -p_src
->point
.y
;
393 p_dst
[length
-1].point
.x
= last_x
;
394 p_dst
[length
-1].point
.y
= -last_y
;
395 last_x
= p_src
->point
.x
;
396 last_y
= p_src
->point
.y
;
402 p_dst
->header
.type
= CAIRO_PATH_LINE_TO
;
403 p_dst
->header
.length
= 2;
405 p_dst
->point
.x
= last_x
;
406 p_dst
->point
.y
= -last_y
;
410 cairo_path
->num_data
= cairo_path
->num_data
* 2;
411 cairo_path
->data
= (cairo_path_data_t
*) priv
->portions
->data
;
415 adg_path_dump (AdgPath
*path
)
417 cairo_path_data_t
*data
;
418 gint n_data
, n_point
;
420 g_return_if_fail (ADG_IS_PATH (path
));
422 for (n_data
= 0; n_data
< path
->priv
->cairo_path
.num_data
; ++ n_data
)
424 data
= path
->priv
->cairo_path
.data
+ n_data
;
426 switch (data
->header
.type
)
428 case CAIRO_PATH_MOVE_TO
:
429 g_print ("Move to ");
431 case CAIRO_PATH_LINE_TO
:
432 g_print ("Line to ");
434 case CAIRO_PATH_CURVE_TO
:
435 g_print ("Curve to ");
437 case CAIRO_PATH_CLOSE_PATH
:
438 g_print ("Path close");
441 g_print ("Unknown entity (%d)", data
->header
.type
);
445 for (n_point
= 1; n_point
< data
->header
.length
; ++ n_point
)
446 g_print ("(%lf, %lf) ", data
[n_point
].point
.x
, data
[n_point
].point
.y
);
457 adg_path_get_current_point (AdgPath
*path
,
461 AdgPathPrivate
*priv
;
463 g_return_val_if_fail (ADG_IS_PATH (path
), FALSE
);
477 adg_path_close (AdgPath
*path
)
479 g_return_if_fail (ADG_IS_PATH (path
));
481 add_portion (path
, CAIRO_PATH_CLOSE_PATH
);
485 adg_path_arc (AdgPath
*path
,
492 g_return_if_fail (ADG_IS_PATH (path
));
493 g_return_if_fail (radius
> 0.0);
495 while (angle2
< angle1
)
498 adg_path_line_to (path
, x
+ radius
* cos (angle1
), y
+ radius
* sin (angle1
));
499 arc_in_direction (path
, x
, y
, radius
, angle1
, angle2
, DIRECTION_FORWARD
);
503 adg_path_arc_negative (AdgPath
*path
,
510 g_return_if_fail (ADG_IS_PATH (path
));
511 g_return_if_fail (radius
> 0.0);
513 while (angle2
> angle1
)
516 adg_path_line_to (path
, x
+ radius
* cos (angle1
), y
+ radius
* sin (angle1
));
517 arc_in_direction (path
, x
, y
, radius
, angle2
, angle1
, DIRECTION_REVERSE
);
521 adg_path_curve_to (AdgPath
*path
,
529 g_return_if_fail (ADG_IS_PATH (path
));
531 add_portion (path
, CAIRO_PATH_CURVE_TO
, x1
, y1
, x2
, y2
, x3
, y3
);
535 adg_path_line_to (AdgPath
*path
,
539 g_return_if_fail (ADG_IS_PATH (path
));
541 add_portion (path
, CAIRO_PATH_LINE_TO
, x
, y
);
545 adg_path_move_to (AdgPath
*path
,
549 g_return_if_fail (ADG_IS_PATH (path
));
551 add_portion (path
, CAIRO_PATH_MOVE_TO
, x
, y
);
555 adg_path_rectangle (AdgPath
*path
,
561 g_return_if_fail (ADG_IS_PATH (path
));
563 adg_path_move_to (path
, x
, y
);
564 adg_path_rel_line_to (path
, width
, 0);
565 adg_path_rel_line_to (path
, 0, height
);
566 adg_path_rel_line_to (path
, -width
, 0);
567 adg_path_close (path
);
571 adg_path_rel_curve_to (AdgPath
*path
,
581 g_return_if_fail (ADG_IS_PATH (path
));
582 g_return_if_fail (adg_path_get_current_point (path
, &x
, &y
));
584 adg_path_curve_to (path
, x
+dx1
, y
+dy1
, x
+dx2
, y
+dy2
, x
+dx3
, y
+dy3
);
588 adg_path_rel_line_to (AdgPath
*path
,
594 g_return_if_fail (ADG_IS_PATH (path
));
595 g_return_if_fail (adg_path_get_current_point (path
, &x
, &y
));
597 adg_path_line_to (path
, x
+dx
, y
+dy
);
601 adg_path_rel_move_to (AdgPath
*path
,
607 g_return_if_fail (ADG_IS_PATH (path
));
608 g_return_if_fail (adg_path_get_current_point (path
, &x
, &y
));
610 adg_path_move_to (path
, x
+dx
, y
+dy
);
614 /* The following code is adapted from cairo-1.3.8 */
617 arc_error_normalized (double angle
)
619 return 2.0/27.0 * pow (sin (angle
/ 4), 6) / pow (cos (angle
/ 4), 2);
623 arc_max_angle_for_tolerance_normalized (double tolerance
)
628 /* Use table lookup to reduce search time in most cases. */
634 { M_PI
/ 1.0, 0.0185185185185185036127 },
635 { M_PI
/ 2.0, 0.000272567143730179811158 },
636 { M_PI
/ 3.0, 2.38647043651461047433e-05 },
637 { M_PI
/ 4.0, 4.2455377443222443279e-06 },
638 { M_PI
/ 5.0, 1.11281001494389081528e-06 },
639 { M_PI
/ 6.0, 3.72662000942734705475e-07 },
640 { M_PI
/ 7.0, 1.47783685574284411325e-07 },
641 { M_PI
/ 8.0, 6.63240432022601149057e-08 },
642 { M_PI
/ 9.0, 3.2715520137536980553e-08 },
643 { M_PI
/ 10.0, 1.73863223499021216974e-08 },
644 { M_PI
/ 11.0, 9.81410988043554039085e-09 },
646 int table_size
= (sizeof (table
) / sizeof (table
[0]));
648 for (i
= 0; i
< table_size
; i
++)
649 if (table
[i
].error
< tolerance
)
650 return table
[i
].angle
;
656 error
= arc_error_normalized (angle
);
658 while (error
> tolerance
);
663 /* XXX: 22-12-2006 Fontana Nicola <ntd at entidi.it>
664 * Removed the ctm parameter and the calculation of the major axis: I use
665 * the radius directly, instead. Hopefully, this will break only the
666 * calculations for ellipses ...
669 arc_segments_needed (double angle
,
675 max_angle
= arc_max_angle_for_tolerance_normalized (tolerance
/ radius
);
677 return (int) ceil (angle
/ max_angle
);
681 arc_segment (AdgPath
*path
,
688 double r_sin_A
, r_cos_A
;
689 double r_sin_B
, r_cos_B
;
692 r_sin_A
= radius
* sin (angle_A
);
693 r_cos_A
= radius
* cos (angle_A
);
694 r_sin_B
= radius
* sin (angle_B
);
695 r_cos_B
= radius
* cos (angle_B
);
697 h
= 4.0/3.0 * tan ((angle_B
- angle_A
) / 4.0);
699 adg_path_curve_to (path
,
700 xc
+ r_cos_A
- h
* r_sin_A
,
701 yc
+ r_sin_A
+ h
* r_cos_A
,
702 xc
+ r_cos_B
+ h
* r_sin_B
,
703 yc
+ r_sin_B
- h
* r_cos_B
,
709 arc_in_direction (AdgPath
*path
,
717 while (angle_max
- angle_min
> 4 * M_PI
)
718 angle_max
-= 2 * M_PI
;
720 /* Recurse if drawing arc larger than pi */
721 if (angle_max
- angle_min
> M_PI
)
723 double angle_mid
= angle_min
+ (angle_max
- angle_min
) / 2.0;
724 /* XXX: Something tells me this block could be condensed. */
725 if (dir
== DIRECTION_FORWARD
)
727 arc_in_direction (path
, xc
, yc
, radius
,
728 angle_min
, angle_mid
,
731 arc_in_direction (path
, xc
, yc
, radius
,
732 angle_mid
, angle_max
,
737 arc_in_direction (path
, xc
, yc
, radius
,
738 angle_mid
, angle_max
,
741 arc_in_direction (path
, xc
, yc
, radius
,
742 angle_min
, angle_mid
,
749 double angle
, angle_step
;
751 /* XXX: 22-12-2006 Fontana Nicola <ntd at entidi.it>
752 * Used the ARC_TOLERANCE constant instead of the cairo context
753 * dependent variable, because I do not have any cairo context here.
755 segments
= arc_segments_needed (angle_max
- angle_min
, radius
, ARC_TOLERANCE
);
756 angle_step
= (angle_max
- angle_min
) / (double) segments
;
758 if (dir
== DIRECTION_FORWARD
)
765 angle_step
= - angle_step
;
768 for (i
= 0; i
< segments
; i
++, angle
+= angle_step
)
769 arc_segment (path
, xc
, yc
, radius
, angle
, angle
+ angle_step
);