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 Lesser 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 * Lesser 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., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
23 * @short_description: A stroked path entity
25 * The #AdgPath object is peraphs the simplest entity.
27 * It contains a pointer to the desired #cairo_path_t structure.
31 #include "adg-path-private.h"
32 #include "adg-line-style.h"
34 #include "adg-canvas.h"
38 #define PARENT_CLASS ((AdgEntityClass *) adg_path_parent_class)
39 #define ARC_TOLERANCE 0.1
42 typedef enum _Direction Direction
;
49 static void finalize (GObject
*object
);
50 static void render (AdgEntity
*entity
,
52 static void add_portion (AdgPath
*path
,
53 cairo_path_data_type_t type
,
55 /* Adapted from cairo-1.3.8 */
56 static double arc_error_normalized (double angle
);
57 static double arc_max_angle_for_tolerance_normalized
59 static int arc_segments_needed (double angle
,
62 static void arc_segment (AdgPath
*path
,
68 static void arc_in_direction (AdgPath
*path
,
77 G_DEFINE_TYPE(AdgPath
, adg_path
, ADG_TYPE_ENTITY
);
81 adg_path_class_init(AdgPathClass
*klass
)
83 GObjectClass
*gobject_class
;
84 AdgEntityClass
*entity_class
;
86 gobject_class
= (GObjectClass
*) klass
;
87 entity_class
= (AdgEntityClass
*) klass
;
89 g_type_class_add_private(klass
, sizeof(AdgPathPrivate
));
91 gobject_class
->finalize
= finalize
;
93 entity_class
->render
= render
;
97 adg_path_init(AdgPath
*path
)
99 AdgPathPrivate
*priv
= G_TYPE_INSTANCE_GET_PRIVATE(path
, ADG_TYPE_PATH
,
102 priv
->cairo_path
.status
= CAIRO_STATUS_SUCCESS
;
103 priv
->cairo_path
.data
= NULL
;
104 priv
->cairo_path
.num_data
= 0;
107 priv
->create_func
= NULL
;
108 priv
->user_data
= NULL
;
114 finalize(GObject
*object
)
116 adg_path_clear((AdgPath
*) object
);
118 ((GObjectClass
*) PARENT_CLASS
)->finalize(object
);
122 render(AdgEntity
*entity
, cairo_t
*cr
)
124 AdgPath
*path
= (AdgPath
*) entity
;
126 if (!adg_entity_model_applied(entity
)
127 && path
->priv
->create_func
!= NULL
)
128 path
->priv
->create_func(entity
, path
->priv
->user_data
);
130 adg_entity_apply(entity
, ADG_SLOT_LINE_STYLE
, cr
);
131 cairo_append_path(cr
, &path
->priv
->cairo_path
);
134 PARENT_CLASS
->render(entity
, cr
);
138 add_portion(AdgPath
*path
, cairo_path_data_type_t type
, ...)
140 AdgPathPrivate
*priv
= path
->priv
;
141 cairo_path_data_t portion
;
145 priv
->portions
= g_array_sized_new(FALSE
, FALSE
,
146 sizeof(cairo_path_data_t
), 10);
148 portion
.header
.type
= type
;
149 va_start(var_args
, type
);
152 case CAIRO_PATH_CLOSE_PATH
:
153 portion
.header
.length
= 1;
154 priv
->portions
= g_array_append_val(priv
->portions
, portion
);
155 portion
.point
.x
= 0.;
156 portion
.point
.y
= 0.;
159 case CAIRO_PATH_MOVE_TO
:
160 portion
.header
.length
= 2;
161 priv
->portions
= g_array_append_val(priv
->portions
, portion
);
162 portion
.point
.x
= va_arg(var_args
, double);
163 portion
.point
.y
= va_arg(var_args
, double);
164 priv
->portions
= g_array_append_val(priv
->portions
, portion
);
167 case CAIRO_PATH_LINE_TO
:
168 portion
.header
.length
= 2;
169 priv
->portions
= g_array_append_val(priv
->portions
, portion
);
170 portion
.point
.x
= va_arg(var_args
, double);
171 portion
.point
.y
= va_arg(var_args
, double);
172 priv
->portions
= g_array_append_val(priv
->portions
, portion
);
175 case CAIRO_PATH_CURVE_TO
:
176 portion
.header
.length
= 4;
177 priv
->portions
= g_array_append_val(priv
->portions
, portion
);
178 portion
.point
.x
= va_arg(var_args
, double);
179 portion
.point
.y
= va_arg(var_args
, double);
180 priv
->portions
= g_array_append_val(priv
->portions
, portion
);
181 portion
.point
.x
= va_arg(var_args
, double);
182 portion
.point
.y
= va_arg(var_args
, double);
183 priv
->portions
= g_array_append_val(priv
->portions
, portion
);
184 portion
.point
.x
= va_arg(var_args
, double);
185 portion
.point
.y
= va_arg(var_args
, double);
186 priv
->portions
= g_array_append_val(priv
->portions
, portion
);
190 g_assert_not_reached();
193 priv
->cairo_path
.data
= (cairo_path_data_t
*) priv
->portions
->data
;
194 priv
->cairo_path
.num_data
= priv
->portions
->len
;
195 priv
->cairo_path
.status
= CAIRO_STATUS_SUCCESS
;
197 priv
->cp
.x
= portion
.point
.x
;
198 priv
->cp
.y
= portion
.point
.y
;
204 * @create_func: an #AdgCallback callback
205 * @user_data: user pointer
207 * Creates a new path entity using @create_func as creation callback.
209 * Return value: the new entity
212 adg_path_new(AdgCallback create_func
, gpointer user_data
)
215 AdgPathPrivate
*priv
;
217 entity
= (AdgEntity
*) g_object_new(ADG_TYPE_PATH
, NULL
);
218 priv
= ((AdgPath
*) entity
)->priv
;
220 priv
->create_func
= create_func
;
221 priv
->user_data
= user_data
;
227 adg_path_clear(AdgPath
*path
)
229 AdgPathPrivate
*priv
;
231 g_return_if_fail(ADG_IS_PATH(path
));
235 g_array_free(priv
->portions
, TRUE
);
236 priv
->portions
= NULL
;
237 priv
->cairo_path
.status
= CAIRO_STATUS_SUCCESS
;
238 priv
->cairo_path
.data
= NULL
;
239 priv
->cairo_path
.num_data
= 0;
246 adg_path_get_cairo_path(AdgPath
*path
)
248 g_return_val_if_fail(ADG_IS_PATH(path
), NULL
);
250 return &path
->priv
->cairo_path
;
255 adg_path_chain_ymirror(AdgPath
*path
)
257 AdgPathPrivate
*priv
;
258 cairo_path_t
*cairo_path
;
259 cairo_path_data_t
*src
;
260 cairo_path_data_t
*p_src
, *p_dst
;
262 gint n_data
, n_point
;
263 double last_x
, last_y
;
265 g_return_if_fail(ADG_IS_PATH(path
));
268 cairo_path
= &priv
->cairo_path
;
270 g_return_if_fail(cairo_path
->num_data
> 2);
271 g_return_if_fail(cairo_path
->data
->header
.type
== CAIRO_PATH_MOVE_TO
);
274 g_memdup(cairo_path
->data
,
275 cairo_path
->num_data
* sizeof(cairo_path_data_t
));
277 g_array_set_size(priv
->portions
, cairo_path
->num_data
* 2);
280 (cairo_path_data_t
*) priv
->portions
->data
+
281 cairo_path
->num_data
* 2;
285 last_x
= p_src
->point
.x
;
286 last_y
= p_src
->point
.y
;
291 while (n_data
< cairo_path
->num_data
) {
292 length
= p_src
->header
.length
;
294 p_dst
->header
.type
= p_src
->header
.type
;
295 p_dst
->header
.length
= length
;
298 for (n_point
= 1; n_point
< length
- 1; ++n_point
) {
299 p_dst
[length
- n_point
- 1].point
.x
= p_src
->point
.x
;
300 p_dst
[length
- n_point
- 1].point
.y
= -p_src
->point
.y
;
304 p_dst
[length
- 1].point
.x
= last_x
;
305 p_dst
[length
- 1].point
.y
= -last_y
;
306 last_x
= p_src
->point
.x
;
307 last_y
= p_src
->point
.y
;
313 p_dst
->header
.type
= CAIRO_PATH_LINE_TO
;
314 p_dst
->header
.length
= 2;
316 p_dst
->point
.x
= last_x
;
317 p_dst
->point
.y
= -last_y
;
321 cairo_path
->num_data
= cairo_path
->num_data
* 2;
322 cairo_path
->data
= (cairo_path_data_t
*) priv
->portions
->data
;
326 adg_path_dump(AdgPath
*path
)
328 cairo_path_data_t
*data
;
329 gint n_data
, n_point
;
331 g_return_if_fail(ADG_IS_PATH(path
));
333 for (n_data
= 0; n_data
< path
->priv
->cairo_path
.num_data
; ++n_data
) {
334 data
= path
->priv
->cairo_path
.data
+ n_data
;
336 switch (data
->header
.type
) {
337 case CAIRO_PATH_MOVE_TO
:
340 case CAIRO_PATH_LINE_TO
:
343 case CAIRO_PATH_CURVE_TO
:
344 g_print("Curve to ");
346 case CAIRO_PATH_CLOSE_PATH
:
347 g_print("Path close");
350 g_print("Unknown entity (%d)", data
->header
.type
);
354 for (n_point
= 1; n_point
< data
->header
.length
; ++n_point
)
355 g_print("(%lf, %lf) ", data
[n_point
].point
.x
,
356 data
[n_point
].point
.y
);
358 n_data
+= n_point
- 1;
367 adg_path_get_current_point(AdgPath
*path
, double *x
, double *y
)
369 AdgPathPrivate
*priv
;
371 g_return_val_if_fail(ADG_IS_PATH(path
), FALSE
);
385 adg_path_close(AdgPath
*path
)
387 g_return_if_fail(ADG_IS_PATH(path
));
389 add_portion(path
, CAIRO_PATH_CLOSE_PATH
);
393 adg_path_arc(AdgPath
*path
,
395 double y
, double radius
, double angle1
, double angle2
)
397 g_return_if_fail(ADG_IS_PATH(path
));
398 g_return_if_fail(radius
> 0.0);
400 while (angle2
< angle1
)
403 adg_path_line_to(path
, x
+ radius
* cos(angle1
),
404 y
+ radius
* sin(angle1
));
405 arc_in_direction(path
, x
, y
, radius
, angle1
, angle2
,
410 adg_path_arc_negative(AdgPath
*path
,
413 double radius
, double angle1
, double angle2
)
415 g_return_if_fail(ADG_IS_PATH(path
));
416 g_return_if_fail(radius
> 0.0);
418 while (angle2
> angle1
)
421 adg_path_line_to(path
, x
+ radius
* cos(angle1
),
422 y
+ radius
* sin(angle1
));
423 arc_in_direction(path
, x
, y
, radius
, angle2
, angle1
,
428 adg_path_curve_to(AdgPath
*path
,
430 double y1
, double x2
, double y2
, double x3
, double y3
)
432 g_return_if_fail(ADG_IS_PATH(path
));
434 add_portion(path
, CAIRO_PATH_CURVE_TO
, x1
, y1
, x2
, y2
, x3
, y3
);
438 adg_path_line_to(AdgPath
*path
, double x
, double y
)
440 g_return_if_fail(ADG_IS_PATH(path
));
442 add_portion(path
, CAIRO_PATH_LINE_TO
, x
, y
);
446 adg_path_move_to(AdgPath
*path
, double x
, double y
)
448 g_return_if_fail(ADG_IS_PATH(path
));
450 add_portion(path
, CAIRO_PATH_MOVE_TO
, x
, y
);
454 adg_path_rectangle(AdgPath
*path
,
455 double x
, double y
, double width
, double height
)
457 g_return_if_fail(ADG_IS_PATH(path
));
459 adg_path_move_to(path
, x
, y
);
460 adg_path_rel_line_to(path
, width
, 0);
461 adg_path_rel_line_to(path
, 0, height
);
462 adg_path_rel_line_to(path
, -width
, 0);
463 adg_path_close(path
);
467 adg_path_rel_curve_to(AdgPath
*path
,
470 double dx2
, double dy2
, double dx3
, double dy3
)
474 g_return_if_fail(ADG_IS_PATH(path
));
475 g_return_if_fail(adg_path_get_current_point(path
, &x
, &y
));
477 adg_path_curve_to(path
, x
+ dx1
, y
+ dy1
, x
+ dx2
, y
+ dy2
, x
+ dx3
,
482 adg_path_rel_line_to(AdgPath
*path
, double dx
, double dy
)
486 g_return_if_fail(ADG_IS_PATH(path
));
487 g_return_if_fail(adg_path_get_current_point(path
, &x
, &y
));
489 adg_path_line_to(path
, x
+ dx
, y
+ dy
);
493 adg_path_rel_move_to(AdgPath
*path
, double dx
, double dy
)
497 g_return_if_fail(ADG_IS_PATH(path
));
498 g_return_if_fail(adg_path_get_current_point(path
, &x
, &y
));
500 adg_path_move_to(path
, x
+ dx
, y
+ dy
);
504 /* The following code is adapted from cairo-1.3.8 */
507 arc_error_normalized(double angle
)
509 return 2.0 / 27.0 * pow(sin(angle
/ 4), 6) / pow(cos(angle
/ 4), 2);
513 arc_max_angle_for_tolerance_normalized(double tolerance
)
518 /* Use table lookup to reduce search time in most cases. */
524 M_PI
/ 1.0, 0.0185185185185185036127}, {
525 M_PI
/ 2.0, 0.000272567143730179811158}, {
526 M_PI
/ 3.0, 2.38647043651461047433e-05}, {
527 M_PI
/ 4.0, 4.2455377443222443279e-06}, {
528 M_PI
/ 5.0, 1.11281001494389081528e-06}, {
529 M_PI
/ 6.0, 3.72662000942734705475e-07}, {
530 M_PI
/ 7.0, 1.47783685574284411325e-07}, {
531 M_PI
/ 8.0, 6.63240432022601149057e-08}, {
532 M_PI
/ 9.0, 3.2715520137536980553e-08}, {
533 M_PI
/ 10.0, 1.73863223499021216974e-08}, {
534 M_PI
/ 11.0, 9.81410988043554039085e-09},};
535 int table_size
= (sizeof(table
) / sizeof(table
[0]));
537 for (i
= 0; i
< table_size
; i
++)
538 if (table
[i
].error
< tolerance
)
539 return table
[i
].angle
;
544 error
= arc_error_normalized(angle
);
546 while (error
> tolerance
);
551 /* XXX: 22-12-2006 Fontana Nicola <ntd at entidi.it>
552 * Removed the ctm parameter and the calculation of the major axis: I use
553 * the radius directly, instead. Hopefully, this will break only the
554 * calculations for ellipses ...
557 arc_segments_needed(double angle
, double radius
, double tolerance
)
561 max_angle
= arc_max_angle_for_tolerance_normalized(tolerance
/ radius
);
563 return (int) ceil(angle
/ max_angle
);
567 arc_segment(AdgPath
*path
,
569 double yc
, double radius
, double angle_A
, double angle_B
)
571 double r_sin_A
, r_cos_A
;
572 double r_sin_B
, r_cos_B
;
575 r_sin_A
= radius
* sin(angle_A
);
576 r_cos_A
= radius
* cos(angle_A
);
577 r_sin_B
= radius
* sin(angle_B
);
578 r_cos_B
= radius
* cos(angle_B
);
580 h
= 4.0 / 3.0 * tan((angle_B
- angle_A
) / 4.0);
582 adg_path_curve_to(path
,
583 xc
+ r_cos_A
- h
* r_sin_A
,
584 yc
+ r_sin_A
+ h
* r_cos_A
,
585 xc
+ r_cos_B
+ h
* r_sin_B
,
586 yc
+ r_sin_B
- h
* r_cos_B
,
587 xc
+ r_cos_B
, yc
+ r_sin_B
);
591 arc_in_direction(AdgPath
*path
,
595 double angle_min
, double angle_max
, Direction dir
)
597 while (angle_max
- angle_min
> 4 * M_PI
)
598 angle_max
-= 2 * M_PI
;
600 /* Recurse if drawing arc larger than pi */
601 if (angle_max
- angle_min
> M_PI
) {
602 double angle_mid
= angle_min
+ (angle_max
- angle_min
) / 2.0;
603 /* XXX: Something tells me this block could be condensed. */
604 if (dir
== DIRECTION_FORWARD
) {
605 arc_in_direction(path
, xc
, yc
, radius
,
606 angle_min
, angle_mid
, dir
);
608 arc_in_direction(path
, xc
, yc
, radius
,
609 angle_mid
, angle_max
, dir
);
611 arc_in_direction(path
, xc
, yc
, radius
,
612 angle_mid
, angle_max
, dir
);
614 arc_in_direction(path
, xc
, yc
, radius
,
615 angle_min
, angle_mid
, dir
);
619 double angle
, angle_step
;
621 /* XXX: 22-12-2006 Fontana Nicola <ntd at entidi.it>
622 * Used the ARC_TOLERANCE constant instead of the cairo context
623 * dependent variable, because I do not have any cairo context here.
626 arc_segments_needed(angle_max
- angle_min
, radius
,
628 angle_step
= (angle_max
- angle_min
) / (double) segments
;
630 if (dir
== DIRECTION_FORWARD
) {
634 angle_step
= -angle_step
;
637 for (i
= 0; i
< segments
; i
++, angle
+= angle_step
)
638 arc_segment(path
, xc
, yc
, radius
, angle
, angle
+ angle_step
);