1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
4 * diarenderer.c - GObject based dia renderer base class
5 * Copyright (C) 1998-2002 Various Dia developers
6 * Copyright (C) 2002 Hans Breuer (refactoring)
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 #include "diarenderer.h"
29 struct _BezierApprox
{
35 static void dia_renderer_class_init (DiaRendererClass
*klass
);
37 static int get_width_pixels (DiaRenderer
*);
38 static int get_height_pixels (DiaRenderer
*);
40 static void begin_render (DiaRenderer
*);
41 static void end_render (DiaRenderer
*);
43 static void set_linewidth (DiaRenderer
*renderer
, real linewidth
);
44 static void set_linecaps (DiaRenderer
*renderer
, LineCaps mode
);
45 static void set_linejoin (DiaRenderer
*renderer
, LineJoin mode
);
46 static void set_linestyle (DiaRenderer
*renderer
, LineStyle mode
);
47 static void set_dashlength (DiaRenderer
*renderer
, real length
);
48 static void set_fillstyle (DiaRenderer
*renderer
, FillStyle mode
);
49 static void set_font (DiaRenderer
*renderer
, DiaFont
*font
, real height
);
51 static void draw_line (DiaRenderer
*renderer
,
52 Point
*start
, Point
*end
,
54 static void fill_rect (DiaRenderer
*renderer
,
55 Point
*ul_corner
, Point
*lr_corner
,
57 static void fill_polygon (DiaRenderer
*renderer
,
58 Point
*points
, int num_points
,
60 static void draw_arc (DiaRenderer
*renderer
,
62 real width
, real height
,
63 real angle1
, real angle2
,
65 static void fill_arc (DiaRenderer
*renderer
,
67 real width
, real height
,
68 real angle1
, real angle2
,
70 static void draw_ellipse (DiaRenderer
*renderer
,
72 real width
, real height
,
74 static void fill_ellipse (DiaRenderer
*renderer
,
76 real width
, real height
,
78 static void draw_bezier (DiaRenderer
*renderer
,
82 static void fill_bezier (DiaRenderer
*renderer
,
86 static void draw_string (DiaRenderer
*renderer
,
91 static void draw_image (DiaRenderer
*renderer
,
93 real width
, real height
,
95 static void draw_text (DiaRenderer
*renderer
,
97 static void draw_text_line (DiaRenderer
*renderer
,
98 TextLine
*text_line
, Point
*pos
, Color
*color
);
100 static void draw_rect (DiaRenderer
*renderer
,
101 Point
*ul_corner
, Point
*lr_corner
,
103 static void draw_polyline (DiaRenderer
*renderer
,
104 Point
*points
, int num_points
,
106 static void draw_rounded_polyline (DiaRenderer
*renderer
,
107 Point
*points
, int num_points
,
108 Color
*color
, real radius
);
109 static void draw_polygon (DiaRenderer
*renderer
,
110 Point
*points
, int num_points
,
113 static real
get_text_width (DiaRenderer
*renderer
,
114 const gchar
*text
, int length
);
116 static void draw_rounded_rect (DiaRenderer
*renderer
,
117 Point
*ul_corner
, Point
*lr_corner
,
118 Color
*color
, real radius
);
119 static void fill_rounded_rect (DiaRenderer
*renderer
,
120 Point
*ul_corner
, Point
*lr_corner
,
121 Color
*color
, real radius
);
122 static void draw_line_with_arrows (DiaRenderer
*renderer
,
123 Point
*start
, Point
*end
,
128 static void draw_arc_with_arrows (DiaRenderer
*renderer
,
129 Point
*start
, Point
*end
,
135 static void draw_polyline_with_arrows (DiaRenderer
*renderer
,
136 Point
*points
, int num_points
,
141 static void draw_rounded_polyline_with_arrows (DiaRenderer
*renderer
,
142 Point
*points
, int num_points
,
149 static void draw_bezier_with_arrows (DiaRenderer
*renderer
,
157 static gpointer parent_class
= NULL
;
160 dia_renderer_get_type (void)
162 static GType object_type
= 0;
166 static const GTypeInfo object_info
=
168 sizeof (DiaRendererClass
),
169 (GBaseInitFunc
) NULL
,
170 (GBaseFinalizeFunc
) NULL
,
171 (GClassInitFunc
) dia_renderer_class_init
,
172 NULL
, /* class_finalize */
173 NULL
, /* class_data */
174 sizeof (DiaRenderer
),
179 object_type
= g_type_register_static (G_TYPE_OBJECT
,
188 draw_object (DiaRenderer
*renderer
,
191 object
->ops
->draw(object
, renderer
);
195 renderer_finalize (GObject
*object
)
197 DiaRenderer
*renderer
= DIA_RENDERER (object
);
200 dia_font_unref (renderer
->font
);
202 if (renderer
->bezier
)
204 if (renderer
->bezier
->points
)
205 g_free (renderer
->bezier
->points
);
206 g_free (renderer
->bezier
);
209 G_OBJECT_CLASS (parent_class
)->finalize (object
);
213 dia_renderer_class_init (DiaRendererClass
*klass
)
215 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
216 DiaRendererClass
*renderer_class
= DIA_RENDERER_CLASS (klass
);
218 parent_class
= g_type_class_peek_parent (klass
);
220 object_class
->finalize
= renderer_finalize
;
222 renderer_class
->get_width_pixels
= get_width_pixels
;
223 renderer_class
->get_height_pixels
= get_height_pixels
;
224 renderer_class
->draw_object
= draw_object
;
225 renderer_class
->get_text_width
= get_text_width
;
227 renderer_class
->begin_render
= begin_render
;
228 renderer_class
->end_render
= end_render
;
230 renderer_class
->set_linewidth
= set_linewidth
;
231 renderer_class
->set_linecaps
= set_linecaps
;
232 renderer_class
->set_linejoin
= set_linejoin
;
233 renderer_class
->set_linestyle
= set_linestyle
;
234 renderer_class
->set_dashlength
= set_dashlength
;
235 renderer_class
->set_fillstyle
= set_fillstyle
;
236 renderer_class
->set_font
= set_font
;
238 renderer_class
->draw_line
= draw_line
;
239 renderer_class
->fill_rect
= fill_rect
;
240 renderer_class
->fill_polygon
= fill_polygon
;
241 renderer_class
->draw_arc
= draw_arc
;
242 renderer_class
->fill_arc
= fill_arc
;
243 renderer_class
->draw_ellipse
= draw_ellipse
;
244 renderer_class
->fill_ellipse
= fill_ellipse
;
245 renderer_class
->draw_string
= draw_string
;
246 renderer_class
->draw_image
= draw_image
;
248 /* medium level functions */
249 renderer_class
->draw_bezier
= draw_bezier
;
250 renderer_class
->fill_bezier
= fill_bezier
;
251 renderer_class
->draw_rect
= draw_rect
;
252 renderer_class
->draw_rounded_polyline
= draw_rounded_polyline
;
253 renderer_class
->draw_polyline
= draw_polyline
;
254 renderer_class
->draw_polygon
= draw_polygon
;
255 renderer_class
->draw_text
= draw_text
;
256 renderer_class
->draw_text_line
= draw_text_line
;
258 /* highest level functions */
259 renderer_class
->draw_rounded_rect
= draw_rounded_rect
;
260 renderer_class
->fill_rounded_rect
= fill_rounded_rect
;
261 renderer_class
->draw_line_with_arrows
= draw_line_with_arrows
;
262 renderer_class
->draw_arc_with_arrows
= draw_arc_with_arrows
;
263 renderer_class
->draw_polyline_with_arrows
= draw_polyline_with_arrows
;
264 renderer_class
->draw_rounded_polyline_with_arrows
= draw_rounded_polyline_with_arrows
;
265 renderer_class
->draw_bezier_with_arrows
= draw_bezier_with_arrows
;
269 begin_render (DiaRenderer
*object
)
271 g_warning ("%s::begin_render not implemented!",
272 G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (object
)));
276 end_render (DiaRenderer
*object
)
278 g_warning ("%s::end_render not implemented!",
279 G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (object
)));
283 set_linewidth (DiaRenderer
*object
, real linewidth
)
285 g_warning ("%s::set_line_width not implemented!",
286 G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (object
)));
290 set_linecaps (DiaRenderer
*object
, LineCaps mode
)
292 g_warning ("%s::set_line_caps not implemented!",
293 G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (object
)));
297 set_linejoin (DiaRenderer
*renderer
, LineJoin mode
)
299 g_warning ("%s::set_line_join not implemented!",
300 G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (renderer
)));
304 set_linestyle (DiaRenderer
*renderer
, LineStyle mode
)
306 g_warning ("%s::set_line_style not implemented!",
307 G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (renderer
)));
311 set_dashlength (DiaRenderer
*renderer
, real length
)
313 g_warning ("%s::set_dash_length not implemented!",
314 G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (renderer
)));
318 set_fillstyle (DiaRenderer
*renderer
, FillStyle mode
)
320 g_warning ("%s::set_fill_style not implemented!",
321 G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (renderer
)));
325 set_font (DiaRenderer
*renderer
, DiaFont
*font
, real height
)
327 /* if it's the same font we must ref it first */
330 dia_font_unref (renderer
->font
);
331 renderer
->font
= font
;
332 renderer
->font_height
= height
;
337 draw_line (DiaRenderer
*renderer
, Point
*start
, Point
*end
, Color
*color
)
339 g_warning ("%s::draw_line not implemented!",
340 G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (renderer
)));
344 fill_polygon (DiaRenderer
*renderer
, Point
*points
, int num_points
, Color
*color
)
346 g_warning ("%s::fill_polygon not implemented!",
347 G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (renderer
)));
351 draw_arc (DiaRenderer
*renderer
, Point
*center
,
352 real width
, real height
, real angle1
, real angle2
,
355 g_warning ("%s::draw_arc not implemented!",
356 G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (renderer
)));
360 fill_rect (DiaRenderer
*renderer
,
361 Point
*ul_corner
, Point
*lr_corner
,
364 g_warning ("%s::fill_rect not implemented!",
365 G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (renderer
)));
369 fill_arc (DiaRenderer
*renderer
, Point
*center
,
370 real width
, real height
, real angle1
, real angle2
,
373 g_warning ("%s::fill_arc not implemented!",
374 G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (renderer
)));
378 draw_ellipse (DiaRenderer
*renderer
, Point
*center
,
379 real width
, real height
,
382 g_warning ("%s::draw_ellipse not implemented!",
383 G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (renderer
)));
387 fill_ellipse (DiaRenderer
*renderer
, Point
*center
,
388 real width
, real height
, Color
*color
)
390 g_warning ("%s::fill_ellipse not implemented!",
391 G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (renderer
)));
395 draw_string (DiaRenderer
*renderer
,
396 const gchar
*text
, Point
*pos
, Alignment alignment
,
399 g_warning ("%s::draw_string not implemented!",
400 G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (renderer
)));
403 /** Default implementation of draw_text */
404 static void draw_text(DiaRenderer
*renderer
,
409 DIA_RENDERER_GET_CLASS(renderer
)->set_font(renderer
, text
->font
, text
->height
);
411 pos
= text
->position
;
413 for (i
=0;i
<text
->numlines
;i
++) {
414 DIA_RENDERER_GET_CLASS(renderer
)->draw_string(renderer
,
416 &pos
, text
->alignment
,
418 pos
.y
+= text
->height
;
422 /** Default implementation of draw_text_line */
423 static void draw_text_line(DiaRenderer
*renderer
,
424 TextLine
*text_line
, Point
*pos
, Color
*color
) {
425 DIA_RENDERER_GET_CLASS(renderer
)->set_font(renderer
,
426 text_line_get_font(text_line
),
427 text_line_get_height(text_line
));
429 DIA_RENDERER_GET_CLASS(renderer
)->draw_string(renderer
,
430 text_line_get_string(text_line
),
436 draw_image (DiaRenderer
*renderer
,
437 Point
*point
, real width
, real height
,
440 g_warning ("%s::draw_image not implemented!",
441 G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (renderer
)));
446 * medium level functions, implemented by the above
448 /* Bezier implementation notes:
449 * These beziers have the following basis matrix:
454 * (At least that's what Hearn and Baker says for beziers.)
456 #define BEZIER_SUBDIVIDE_LIMIT 0.01
457 #define BEZIER_SUBDIVIDE_LIMIT_SQ (BEZIER_SUBDIVIDE_LIMIT*BEZIER_SUBDIVIDE_LIMIT)
460 bezier_add_point(BezierApprox
*bezier
,
463 /* Grow if needed: */
464 if (bezier
->currpoint
== bezier
->numpoints
) {
465 bezier
->numpoints
+= 40;
466 bezier
->points
= g_realloc(bezier
->points
,
467 bezier
->numpoints
*sizeof(Point
));
470 bezier
->points
[bezier
->currpoint
] = *point
;
476 bezier_add_lines(BezierApprox
*bezier
,
486 /* Check if almost flat: */
488 point_sub(&u
, &points
[0]);
490 point_sub(&v
, &points
[0]);
492 v_len_sq
= point_dot(&v
,&v
);
493 if (isnan(v_len_sq
)) {
494 g_warning("v_len_sq is NaN while calculating bezier curve!");
497 if (v_len_sq
< 0.000001)
499 point_scale(&y
, point_dot(&u
,&v
)/v_len_sq
);
502 delta
= point_dot(&x
,&x
);
503 if (delta
< BEZIER_SUBDIVIDE_LIMIT_SQ
) {
505 point_sub(&u
, &points
[3]);
507 point_sub(&v
, &points
[3]);
509 v_len_sq
= point_dot(&v
,&v
);
510 if (v_len_sq
< 0.000001)
512 point_scale(&y
, point_dot(&u
,&v
)/v_len_sq
);
515 delta
= point_dot(&x
,&x
);
516 if (delta
< BEZIER_SUBDIVIDE_LIMIT_SQ
) { /* Almost flat, draw a line */
517 bezier_add_point(bezier
, &points
[3]);
521 /* Subdivide into two bezier curves: */
524 point_add(&middle
, &points
[2]);
525 point_scale(&middle
, 0.5);
530 point_add(&r
[1], &points
[1]);
531 point_scale(&r
[1], 0.5);
534 point_add(&r
[2], &middle
);
535 point_scale(&r
[2], 0.5);
540 point_add(&s
[2], &points
[3]);
541 point_scale(&s
[2], 0.5);
544 point_add(&s
[1], &middle
);
545 point_scale(&s
[1], 0.5);
548 point_add(&r
[3], &s
[1]);
549 point_scale(&r
[3], 0.5);
553 bezier_add_lines(bezier
, r
);
554 bezier_add_lines(bezier
, s
);
558 bezier_add_curve(BezierApprox
*bezier
,
561 /* Is the bezier curve malformed? */
562 if ( (distance_point_point(&points
[0], &points
[1]) < 0.00001) &&
563 (distance_point_point(&points
[2], &points
[3]) < 0.00001) &&
564 (distance_point_point(&points
[0], &points
[3]) < 0.00001)) {
565 bezier_add_point(bezier
, &points
[3]);
568 bezier_add_lines(bezier
, points
);
572 approximate_bezier (BezierApprox
*bezier
,
573 BezPoint
*points
, int numpoints
)
578 if (points
[0].type
!= BEZ_MOVE_TO
)
579 g_warning("first BezPoint must be a BEZ_MOVE_TO");
580 curve
[3] = points
[0].p1
;
581 bezier_add_point(bezier
, &points
[0].p1
);
582 for (i
= 1; i
< numpoints
; i
++)
583 switch (points
[i
].type
) {
585 g_warning("only first BezPoint can be a BEZ_MOVE_TO");
586 curve
[3] = points
[i
].p1
;
589 bezier_add_point(bezier
, &points
[i
].p1
);
590 curve
[3] = points
[i
].p1
;
594 curve
[1] = points
[i
].p1
;
595 curve
[2] = points
[i
].p2
;
596 curve
[3] = points
[i
].p3
;
597 bezier_add_curve(bezier
, curve
);
602 /* bezier approximation with straight lines */
604 draw_bezier (DiaRenderer
*renderer
,
605 BezPoint
*points
, int numpoints
,
608 BezierApprox
*bezier
;
610 if (renderer
->bezier
)
611 bezier
= renderer
->bezier
;
613 renderer
->bezier
= bezier
= g_new0 (BezierApprox
, 1);
615 if (bezier
->points
== NULL
) {
616 bezier
->numpoints
= 30;
617 bezier
->points
= g_malloc(bezier
->numpoints
*sizeof(Point
));
620 bezier
->currpoint
= 0;
621 approximate_bezier (bezier
, points
, numpoints
);
623 DIA_RENDERER_GET_CLASS (renderer
)->draw_polyline (renderer
,
630 fill_bezier (DiaRenderer
*renderer
,
631 BezPoint
*points
, int numpoints
,
634 BezierApprox
*bezier
;
636 if (renderer
->bezier
)
637 bezier
= renderer
->bezier
;
639 renderer
->bezier
= bezier
= g_new0 (BezierApprox
, 1);
641 if (bezier
->points
== NULL
) {
642 bezier
->numpoints
= 30;
643 bezier
->points
= g_malloc(bezier
->numpoints
*sizeof(Point
));
646 bezier
->currpoint
= 0;
647 approximate_bezier (bezier
, points
, numpoints
);
649 DIA_RENDERER_GET_CLASS (renderer
)->fill_polygon (renderer
,
656 draw_rect (DiaRenderer
*renderer
,
657 Point
*ul_corner
, Point
*lr_corner
,
660 DiaRendererClass
*klass
= DIA_RENDERER_GET_CLASS (renderer
);
668 klass
->draw_line (renderer
, ul_corner
, &ur
, color
);
669 klass
->draw_line (renderer
, &ur
, lr_corner
, color
);
670 klass
->draw_line (renderer
, lr_corner
, &ll
, color
);
671 klass
->draw_line (renderer
, &ll
, ul_corner
, color
);
675 draw_polyline (DiaRenderer
*renderer
,
676 Point
*points
, int num_points
,
679 DiaRendererClass
*klass
= DIA_RENDERER_GET_CLASS (renderer
);
682 for (i
= 0; i
< num_points
- 1; i
++)
683 klass
->draw_line (renderer
, &points
[i
+0], &points
[i
+1], color
);
687 /* calculate the maximum possible radius for 3 points
689 given points p1,p2, and p3
690 let c = min(length(p1,p2)/2,length(p2,p3)/2)
691 let a = dot2(p1-p2, p3-p2)
692 (ie, angle between lines p1,p2 and p2,p3)
693 then maxr = c * sin(a/2)
696 calculate_min_radius( Point
*p1
, Point
*p2
, Point
*p3
)
702 c
= MIN(distance_point_point(p1
,p2
)/2,distance_point_point(p2
,p3
)/2);
703 v1
.x
= p1
->x
-p2
->x
; v1
.y
= p1
->y
-p2
->y
;
704 v2
.x
= p3
->x
-p2
->x
; v2
.y
= p3
->y
-p2
->y
;
709 /** Draw a polyline with optionally rounded corners.
710 * Based on draw_line and draw_arc, but uses draw_polyline when
711 * the rounding is too small.
714 draw_rounded_polyline (DiaRenderer
*renderer
,
715 Point
*points
, int num_points
,
716 Color
*color
, real radius
)
718 DiaRendererClass
*klass
= DIA_RENDERER_GET_CLASS (renderer
);
724 if (radius
< 0.00001) {
725 klass
->draw_polyline(renderer
, points
, num_points
, color
);
729 /* skip arc computations if we only have points */
730 if ( num_points
<= 2 ) {
732 p1
.x
= p
[i
].x
; p1
.y
= p
[i
].y
;
733 p2
.x
= p
[i
+1].x
; p2
.y
= p
[i
+1].y
;
734 klass
->draw_line(renderer
,&p1
,&p2
,color
);
739 /* full rendering 3 or more points */
740 p1
.x
= p
[i
].x
; p1
.y
= p
[i
].y
;
741 p2
.x
= p
[i
+1].x
; p2
.y
= p
[i
+1].y
;
742 for (i
= 0; i
<= num_points
- 3; i
++) {
744 real start_angle
, stop_angle
;
746 p3
.x
= p
[i
+1].x
; p3
.y
= p
[i
+1].y
;
747 p4
.x
= p
[i
+2].x
; p4
.y
= p
[i
+2].y
;
749 /* adjust the radius if it would cause odd rendering */
750 min_radius
= MIN(radius
, calculate_min_radius(&p1
,&p2
,&p4
));
751 fillet(&p1
,&p2
,&p3
,&p4
, min_radius
, &c
, &start_angle
, &stop_angle
);
752 klass
->draw_arc(renderer
, &c
, min_radius
*2, min_radius
*2,
755 klass
->draw_line(renderer
, &p1
, &p2
, color
);
756 p1
.x
= p3
.x
; p1
.y
= p3
.y
;
757 p2
.x
= p4
.x
; p2
.y
= p4
.y
;
759 klass
->draw_line(renderer
, &p3
, &p4
, color
);
763 draw_polygon (DiaRenderer
*renderer
,
764 Point
*points
, int num_points
,
767 DiaRendererClass
*klass
= DIA_RENDERER_GET_CLASS (renderer
);
770 g_return_if_fail (num_points
> 1);
772 for (i
= 0; i
< num_points
- 1; i
++)
773 klass
->draw_line (renderer
, &points
[i
+0], &points
[i
+1], color
);
774 /* close it in any case */
775 if ( (points
[0].x
!= points
[num_points
-1].x
)
776 || (points
[0].y
!= points
[num_points
-1].y
))
777 klass
->draw_line (renderer
, &points
[num_points
-1], &points
[0], color
);
781 draw_rounded_rect (DiaRenderer
*renderer
,
782 Point
*ul_corner
, Point
*lr_corner
,
783 Color
*color
, real radius
)
785 DiaRendererClass
*renderer_ops
= DIA_RENDERER_GET_CLASS (renderer
);
786 Point start
, end
, center
;
788 radius
= MIN(radius
, (lr_corner
->x
-ul_corner
->x
)/2);
789 radius
= MIN(radius
, (lr_corner
->y
-ul_corner
->y
)/2);
790 start
.x
= center
.x
= ul_corner
->x
+radius
;
791 end
.x
= lr_corner
->x
-radius
;
792 start
.y
= end
.y
= ul_corner
->y
;
793 renderer_ops
->draw_line(renderer
, &start
, &end
, color
);
794 start
.y
= end
.y
= lr_corner
->y
;
795 renderer_ops
->draw_line(renderer
, &start
, &end
, color
);
797 center
.y
= ul_corner
->y
+radius
;
798 renderer_ops
->draw_arc(renderer
, ¢er
,
799 2.0*radius
, 2.0*radius
,
802 renderer_ops
->draw_arc(renderer
, ¢er
,
803 2.0*radius
, 2.0*radius
,
806 start
.y
= ul_corner
->y
+radius
;
807 start
.x
= end
.x
= ul_corner
->x
;
808 end
.y
= center
.y
= lr_corner
->y
-radius
;
809 renderer_ops
->draw_line(renderer
, &start
, &end
, color
);
810 start
.x
= end
.x
= lr_corner
->x
;
811 renderer_ops
->draw_line(renderer
, &start
, &end
, color
);
813 center
.y
= lr_corner
->y
-radius
;
814 center
.x
= ul_corner
->x
+radius
;
815 renderer_ops
->draw_arc(renderer
, ¢er
,
816 2.0*radius
, 2.0*radius
,
817 180.0, 270.0, color
);
818 center
.x
= lr_corner
->x
-radius
;
819 renderer_ops
->draw_arc(renderer
, ¢er
,
820 2.0*radius
, 2.0*radius
,
821 270.0, 360.0, color
);
825 fill_rounded_rect(DiaRenderer
*renderer
,
826 Point
*ul_corner
, Point
*lr_corner
,
827 Color
*color
, real radius
)
829 DiaRendererClass
*renderer_ops
= DIA_RENDERER_GET_CLASS (renderer
);
830 Point start
, end
, center
;
832 radius
= MIN(radius
, (lr_corner
->x
-ul_corner
->x
)/2);
833 radius
= MIN(radius
, (lr_corner
->y
-ul_corner
->y
)/2);
834 start
.x
= center
.x
= ul_corner
->x
+radius
;
835 end
.x
= lr_corner
->x
-radius
;
836 start
.y
= ul_corner
->y
;
837 end
.y
= lr_corner
->y
;
838 renderer_ops
->fill_rect(renderer
, &start
, &end
, color
);
840 center
.y
= ul_corner
->y
+radius
;
841 renderer_ops
->fill_arc(renderer
, ¢er
,
842 2.0*radius
, 2.0*radius
,
845 renderer_ops
->fill_arc(renderer
, ¢er
,
846 2.0*radius
, 2.0*radius
,
850 start
.x
= ul_corner
->x
;
851 start
.y
= ul_corner
->y
+radius
;
852 end
.x
= lr_corner
->x
;
853 end
.y
= center
.y
= lr_corner
->y
-radius
;
854 renderer_ops
->fill_rect(renderer
, &start
, &end
, color
);
856 center
.y
= lr_corner
->y
-radius
;
857 center
.x
= ul_corner
->x
+radius
;
858 renderer_ops
->fill_arc(renderer
, ¢er
,
859 2.0*radius
, 2.0*radius
,
860 180.0, 270.0, color
);
861 center
.x
= lr_corner
->x
-radius
;
862 renderer_ops
->fill_arc(renderer
, ¢er
,
863 2.0*radius
, 2.0*radius
,
864 270.0, 360.0, color
);
868 draw_line_with_arrows(DiaRenderer
*renderer
,
876 Point oldstart
= *startpoint
;
877 Point oldend
= *endpoint
;
878 Point start_arrow_head
;
879 Point end_arrow_head
;
881 /* Calculate how to more the line to account for arrow heads */
882 if (start_arrow
!= NULL
&& start_arrow
->type
!= ARROW_NONE
) {
883 Point move_arrow
, move_line
;
884 calculate_arrow_point(start_arrow
, startpoint
, endpoint
,
885 &move_arrow
, &move_line
,
887 start_arrow_head
= *startpoint
;
888 point_sub(&start_arrow_head
, &move_arrow
);
889 point_sub(startpoint
, &move_line
);
891 if (end_arrow
!= NULL
&& end_arrow
->type
!= ARROW_NONE
) {
892 Point move_arrow
, move_line
;
893 calculate_arrow_point(end_arrow
, endpoint
, startpoint
,
894 &move_arrow
, &move_line
,
896 end_arrow_head
= *endpoint
;
897 point_sub(&end_arrow_head
, &move_arrow
);
898 point_sub(endpoint
, &move_line
);
901 DIA_RENDERER_GET_CLASS(renderer
)->draw_line(renderer
, startpoint
, endpoint
, color
);
903 /* Actual arrow drawing down here so line styles aren't disturbed */
904 if (start_arrow
!= NULL
&& start_arrow
->type
!= ARROW_NONE
)
905 arrow_draw(renderer
, start_arrow
->type
,
906 &start_arrow_head
, endpoint
,
907 start_arrow
->length
, start_arrow
->width
,
909 color
, &color_white
);
910 if (end_arrow
!= NULL
&& end_arrow
->type
!= ARROW_NONE
)
911 arrow_draw(renderer
, end_arrow
->type
,
912 &end_arrow_head
, startpoint
,
913 end_arrow
->length
, end_arrow
->width
,
915 color
, &color_white
);
917 *startpoint
= oldstart
;
922 draw_polyline_with_arrows(DiaRenderer
*renderer
,
923 Point
*points
, int num_points
,
929 /* Index of first and last point with a non-zero length segment */
931 int lastline
= num_points
;
932 Point oldstart
= points
[firstline
];
933 Point oldend
= points
[lastline
-1];
934 Point start_arrow_head
;
935 Point end_arrow_head
;
937 if (start_arrow
!= NULL
&& start_arrow
->type
!= ARROW_NONE
) {
938 Point move_arrow
, move_line
;
939 while (firstline
< num_points
-1 &&
940 distance_point_point(&points
[firstline
],
941 &points
[firstline
+1]) < 0.0000001)
943 if (firstline
== num_points
-1)
944 firstline
= 0; /* No non-zero lines, it doesn't matter. */
945 oldstart
= points
[firstline
];
946 calculate_arrow_point(start_arrow
,
947 &points
[firstline
], &points
[firstline
+1],
948 &move_arrow
, &move_line
,
950 start_arrow_head
= points
[firstline
];
951 point_sub(&start_arrow_head
, &move_arrow
);
952 point_sub(&points
[firstline
], &move_line
);
954 if (end_arrow
!= NULL
&& end_arrow
->type
!= ARROW_NONE
) {
955 Point move_arrow
, move_line
;
956 while (lastline
> 0 &&
957 distance_point_point(&points
[lastline
-1],
958 &points
[lastline
-2]) < 0.0000001)
961 firstline
= num_points
; /* No non-zero lines, it doesn't matter. */
962 oldend
= points
[lastline
-1];
963 calculate_arrow_point(end_arrow
, &points
[lastline
-1],
965 &move_arrow
, &move_line
,
967 end_arrow_head
= points
[lastline
-1];
968 point_sub(&end_arrow_head
, &move_arrow
);
969 point_sub(&points
[lastline
-1], &move_line
);
971 /* Don't draw degenerate line segments at end of line */
972 if (lastline
-firstline
> 1) /* probably hiding a bug above, but don't try to draw a negative
973 * number of points at all, fixes bug #148139 */
974 DIA_RENDERER_GET_CLASS(renderer
)->draw_polyline(renderer
, &points
[firstline
],
975 lastline
-firstline
, color
);
976 if (start_arrow
!= NULL
&& start_arrow
->type
!= ARROW_NONE
)
977 arrow_draw(renderer
, start_arrow
->type
,
978 &start_arrow_head
, &points
[firstline
+1],
979 start_arrow
->length
, start_arrow
->width
,
981 color
, &color_white
);
982 if (end_arrow
!= NULL
&& end_arrow
->type
!= ARROW_NONE
)
983 arrow_draw(renderer
, end_arrow
->type
,
984 &end_arrow_head
, &points
[lastline
-2],
985 end_arrow
->length
, end_arrow
->width
,
987 color
, &color_white
);
989 points
[firstline
] = oldstart
;
990 points
[lastline
-1] = oldend
;
994 draw_rounded_polyline_with_arrows(DiaRenderer
*renderer
,
995 Point
*points
, int num_points
,
1002 /* Index of first and last point with a non-zero length segment */
1004 int lastline
= num_points
;
1005 Point oldstart
= points
[firstline
];
1006 Point oldend
= points
[lastline
-1];
1007 Point start_arrow_head
;
1008 Point end_arrow_head
;
1010 if (start_arrow
!= NULL
&& start_arrow
->type
!= ARROW_NONE
) {
1011 Point move_arrow
, move_line
;
1012 while (firstline
< num_points
-1 &&
1013 distance_point_point(&points
[firstline
],
1014 &points
[firstline
+1]) < 0.0000001)
1016 if (firstline
== num_points
-1)
1017 firstline
= 0; /* No non-zero lines, it doesn't matter. */
1018 oldstart
= points
[firstline
];
1019 calculate_arrow_point(start_arrow
,
1020 &points
[firstline
], &points
[firstline
+1],
1021 &move_arrow
, &move_line
,
1023 start_arrow_head
= points
[firstline
];
1024 point_sub(&start_arrow_head
, &move_arrow
);
1025 point_sub(&points
[firstline
], &move_line
);
1027 if (end_arrow
!= NULL
&& end_arrow
->type
!= ARROW_NONE
) {
1028 Point move_arrow
, move_line
;
1029 while (lastline
> 0 &&
1030 distance_point_point(&points
[lastline
-1],
1031 &points
[lastline
-2]) < 0.0000001)
1034 firstline
= num_points
; /* No non-zero lines, it doesn't matter. */
1035 oldend
= points
[lastline
-1];
1036 calculate_arrow_point(end_arrow
, &points
[lastline
-1],
1037 &points
[lastline
-2],
1038 &move_arrow
, &move_line
,
1040 end_arrow_head
= points
[lastline
-1];
1041 point_sub(&end_arrow_head
, &move_arrow
);
1042 point_sub(&points
[lastline
-1], &move_line
);
1044 /* Don't draw degenerate line segments at end of line */
1045 if (lastline
-firstline
> 1) /* only if there is something */
1046 DIA_RENDERER_GET_CLASS(renderer
)->draw_rounded_polyline(renderer
,
1050 if (start_arrow
!= NULL
&& start_arrow
->type
!= ARROW_NONE
)
1051 arrow_draw(renderer
, start_arrow
->type
,
1052 &start_arrow_head
, &points
[firstline
+1],
1053 start_arrow
->length
, start_arrow
->width
,
1055 color
, &color_white
);
1056 if (end_arrow
!= NULL
&& end_arrow
->type
!= ARROW_NONE
)
1057 arrow_draw(renderer
, end_arrow
->type
,
1058 &end_arrow_head
, &points
[lastline
-2],
1059 end_arrow
->length
, end_arrow
->width
,
1061 color
, &color_white
);
1063 points
[firstline
] = oldstart
;
1064 points
[lastline
-1] = oldend
;
1067 /** Figure the equation for a line given by two points.
1068 * Returns FALSE if the line is vertical (infinite a).
1071 points_to_line(real
*a
, real
*b
, Point
*p1
, Point
*p2
)
1073 if (fabs(p1
->x
- p2
->x
) < 0.000000001)
1075 *a
= (p2
->y
-p1
->y
)/(p2
->x
-p1
->x
);
1076 *b
= p1
->y
-(*a
)*p1
->x
;
1080 /** Find the intersection between two lines.
1081 * Returns TRUE if the lines intersect in a single point.
1084 intersection_line_line(Point
*cross
,
1085 Point
*p1a
, Point
*p1b
,
1086 Point
*p2a
, Point
*p2b
)
1088 real a1
, b1
, a2
, b2
;
1090 /* Find coefficients of lines */
1091 if (!(points_to_line(&a1
, &b1
, p1a
, p1b
))) {
1092 if (!(points_to_line(&a2
, &b2
, p2a
, p2b
))) {
1093 if (fabs(p1a
->x
-p2a
->x
) < 0.00000001) {
1096 } else return FALSE
;
1099 cross
->y
= a2
*(p1a
->x
)+b2
;
1102 if (!(points_to_line(&a2
, &b2
, p2a
, p2b
))) {
1104 cross
->y
= a1
*(p2a
->x
)+b1
;
1108 if (fabs(a1
-a2
) < 0.000000001) {
1109 if (fabs(b1
-b2
) < 0.000000001) {
1123 cross
->x
= (b2
-b1
)/(a1
-a2
);
1124 cross
->y
= a1
*cross
->x
+b1
;
1129 /** Given three points, find the center of the circle they describe.
1130 * Returns FALSE if the center could not be determined (i.e. the points
1131 * all lie really close together).
1132 * The renderer should disappear once the debugging is done.
1135 find_center_point(Point
*center
, Point
*p1
, Point
*p2
, Point
*p3
)
1143 /* Find vector from middle between two points towards center */
1145 point_sub(&mid1
, p2
);
1146 point_scale(&mid1
, 0.5);
1148 point_add(&mid1
, p2
); /* Now midpoint between p1 & p2 */
1152 point_add(&orth1
, &mid1
);
1155 /* Again, with two other points */
1157 point_sub(&mid2
, p3
);
1158 point_scale(&mid2
, 0.5);
1160 point_add(&mid2
, p3
); /* Now midpoint between p2 & p3 */
1164 point_add(&orth2
, &mid2
);
1166 /* The intersection between these two is the center */
1167 if (!intersection_line_line(center
, &mid1
, &orth1
, &mid2
, &orth2
)) {
1168 /* Degenerate circle */
1169 /* Case 1: Points are all on top of each other. Nothing to do. */
1170 if (fabs((p1
->x
+ p2
->x
+ p3
->x
)/3 - p1
->x
) < 0.0000001 &&
1171 fabs((p1
->y
+ p2
->y
+ p3
->y
)/3 - p1
->y
) < 0.0000001) {
1175 /* Case 2: Two points are on top of each other. Midpoint of
1176 * non-degenerate line is center. */
1177 if (distance_point_point_manhattan(p1
, p2
) < 0.0000001) {
1180 } else if (distance_point_point_manhattan(p1
, p3
) < 0.0000001 ||
1181 distance_point_point_manhattan(p2
, p3
) < 0.0000001) {
1185 /* Case 3: All points on a line. Nothing to do. */
1192 is_right_hand (const Point
*a
, const Point
*b
, const Point
*c
)
1197 point_sub(&dot1
, c
);
1198 point_normalize(&dot1
);
1200 point_sub(&dot2
, c
);
1201 point_normalize(&dot2
);
1202 return point_cross(&dot1
, &dot2
) > 0;
1206 draw_arc_with_arrows (DiaRenderer
*renderer
,
1215 Point oldstart
= *startpoint
;
1216 Point oldend
= *endpoint
;
1218 real width
, angle1
, angle2
, arrow_ofs
= 0.0;
1220 Point start_arrow_head
;
1221 Point start_arrow_end
;
1222 Point end_arrow_head
;
1223 Point end_arrow_end
;
1225 if (!find_center_point(¢er
, startpoint
, endpoint
, midpoint
)) {
1226 /* Degenerate circle -- should have been caught by the drawer? */
1227 printf("Degenerate\n");
1230 righthand
= is_right_hand (startpoint
, midpoint
, endpoint
);
1232 width
= 2*distance_point_point(¢er
, startpoint
);
1234 if (start_arrow
!= NULL
&& start_arrow
->type
!= ARROW_NONE
) {
1235 Point move_arrow
, move_line
;
1238 start_arrow_end
= *startpoint
;
1239 point_sub(&start_arrow_end
, ¢er
);
1240 tmp
= start_arrow_end
.x
;
1242 start_arrow_end
.x
= -start_arrow_end
.y
;
1243 start_arrow_end
.y
= tmp
;
1245 start_arrow_end
.x
= start_arrow_end
.y
;
1246 start_arrow_end
.y
= -tmp
;
1248 point_add(&start_arrow_end
, startpoint
);
1250 calculate_arrow_point(start_arrow
, startpoint
, &start_arrow_end
,
1251 &move_arrow
, &move_line
,
1253 start_arrow_head
= *startpoint
;
1254 point_sub(&start_arrow_head
, &move_arrow
);
1255 point_sub(startpoint
, &move_line
);
1256 arrow_ofs
+= sqrt (move_line
.x
* move_line
.x
+ move_line
.y
* move_line
.y
);
1258 if (end_arrow
!= NULL
&& end_arrow
->type
!= ARROW_NONE
) {
1259 Point move_arrow
, move_line
;
1262 end_arrow_end
= *endpoint
;
1263 point_sub(&end_arrow_end
, ¢er
);
1264 tmp
= end_arrow_end
.x
;
1266 end_arrow_end
.x
= end_arrow_end
.y
;
1267 end_arrow_end
.y
= -tmp
;
1269 end_arrow_end
.x
= -end_arrow_end
.y
;
1270 end_arrow_end
.y
= tmp
;
1272 point_add(&end_arrow_end
, endpoint
);
1274 calculate_arrow_point(end_arrow
, endpoint
, &end_arrow_end
,
1275 &move_arrow
, &move_line
,
1277 end_arrow_head
= *endpoint
;
1278 point_sub(&end_arrow_head
, &move_arrow
);
1279 point_sub(endpoint
, &move_line
);
1280 arrow_ofs
+= sqrt (move_line
.x
* move_line
.x
+ move_line
.y
* move_line
.y
);
1283 /* Now we possibly have new start- and endpoint. We must not
1284 * recalculate the center cause the new points lie on the tangential
1285 * approximation of the original arc arrow lines not on the arc itself.
1286 * The one thing we need to deal with is calculating the (new) angles
1287 * and get rid of the arc drawing altogether if got degenerated.
1289 * Why shouldn't we recalculate the whole thing from the new start/endpoints?
1290 * Done this way the arc does not come out the back of the arrows.
1293 angle1
= -atan2(startpoint
->y
- center
.y
, startpoint
->x
- center
.x
)*180.0/G_PI
;
1294 while (angle1
< 0.0) angle1
+= 360.0;
1295 angle2
= -atan2(endpoint
->y
- center
.y
, endpoint
->x
- center
.x
)*180.0/G_PI
;
1296 while (angle2
< 0.0) angle2
+= 360.0;
1302 /* now with the angles we can bring the startpoint back to the arc, but there must be a less expensive way to do this? */
1303 if (start_arrow
!= NULL
&& start_arrow
->type
!= ARROW_NONE
) {
1304 startpoint
->x
= cos (G_PI
* angle1
/ 180.0) * width
/ 2.0 + center
.x
;
1305 startpoint
->y
= sin (G_PI
* angle1
/ 180.0) * width
/ 2.0 + center
.y
;
1307 if (end_arrow
!= NULL
&& end_arrow
->type
!= ARROW_NONE
) {
1308 endpoint
->x
= cos (G_PI
* angle1
/ 180.0) * width
/ 2.0 + center
.x
;
1309 endpoint
->y
= sin (G_PI
* angle1
/ 180.0) * width
/ 2.0 + center
.y
;
1312 DIA_RENDERER_GET_CLASS(renderer
)->draw_arc(renderer
, ¢er
, width
, width
,
1313 angle1
, angle2
, color
);
1315 if (start_arrow
!= NULL
&& start_arrow
->type
!= ARROW_NONE
)
1316 arrow_draw(renderer
, start_arrow
->type
,
1317 &start_arrow_head
, &start_arrow_end
,
1318 start_arrow
->length
, start_arrow
->width
,
1320 color
, &color_white
);
1321 if (end_arrow
!= NULL
&& end_arrow
->type
!= ARROW_NONE
)
1322 arrow_draw(renderer
, end_arrow
->type
,
1323 &end_arrow_head
, &end_arrow_end
,
1324 end_arrow
->length
, end_arrow
->width
,
1326 color
, &color_white
);
1328 *startpoint
= oldstart
;
1333 draw_bezier_with_arrows(DiaRenderer
*renderer
,
1341 Point startpoint
, endpoint
;
1342 Point start_arrow_head
;
1343 Point end_arrow_head
;
1345 startpoint
= points
[0].p1
;
1346 endpoint
= points
[num_points
-1].p3
;
1348 if (start_arrow
!= NULL
&& start_arrow
->type
!= ARROW_NONE
) {
1351 calculate_arrow_point(start_arrow
, &points
[0].p1
, &points
[1].p1
,
1352 &move_arrow
, &move_line
,
1354 start_arrow_head
= points
[0].p1
;
1355 point_sub(&start_arrow_head
, &move_arrow
);
1356 point_sub(&points
[0].p1
, &move_line
);
1358 if (end_arrow
!= NULL
&& end_arrow
->type
!= ARROW_NONE
) {
1361 calculate_arrow_point(end_arrow
,
1362 &points
[num_points
-1].p3
, &points
[num_points
-1].p2
,
1363 &move_arrow
, &move_line
,
1365 end_arrow_head
= points
[num_points
-1].p3
;
1366 point_sub(&end_arrow_head
, &move_arrow
);
1367 point_sub(&points
[num_points
-1].p3
, &move_line
);
1369 DIA_RENDERER_GET_CLASS(renderer
)->draw_bezier(renderer
, points
, num_points
, color
);
1370 if (start_arrow
!= NULL
&& start_arrow
->type
!= ARROW_NONE
)
1371 arrow_draw(renderer
, start_arrow
->type
,
1372 &start_arrow_head
, &points
[1].p1
,
1373 start_arrow
->length
, start_arrow
->width
,
1375 color
, &color_white
);
1376 if (end_arrow
!= NULL
&& end_arrow
->type
!= ARROW_NONE
)
1377 arrow_draw(renderer
, end_arrow
->type
,
1378 &end_arrow_head
, &points
[num_points
-1].p2
,
1379 end_arrow
->length
, end_arrow
->width
,
1381 color
, &color_white
);
1383 points
[0].p1
= startpoint
;
1384 points
[num_points
-1].p3
= endpoint
;
1389 * Should we really provide this ? It formerly was an 'interactive op'
1390 * and depends on DiaRenderer::set_font() not or properly overwritten
1393 get_text_width (DiaRenderer
*renderer
,
1394 const gchar
*text
, int length
)
1398 if (renderer
->font
) {
1399 char *str
= g_strndup (text
, length
);
1401 ret
= dia_font_string_width (str
,
1403 renderer
->font_height
);
1411 get_width_pixels (DiaRenderer
*renderer
)
1413 g_return_val_if_fail (renderer
->is_interactive
, 0);
1418 get_height_pixels (DiaRenderer
*renderer
)
1420 g_return_val_if_fail (renderer
->is_interactive
, 0);
1426 * non member functions
1429 dia_renderer_get_width_pixels (DiaRenderer
*renderer
)
1431 return DIA_RENDERER_GET_CLASS(renderer
)->get_width_pixels (renderer
);
1435 dia_renderer_get_height_pixels (DiaRenderer
*renderer
)
1437 return DIA_RENDERER_GET_CLASS(renderer
)->get_height_pixels (renderer
);