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
,
408 #ifdef USE_TEXTLINE_FOR_LINES
409 pos
= text
->position
;
411 for (i
=0;i
<text
->numlines
;i
++) {
412 Point aligned_pos
= pos
;
413 aligned_pos
.x
-= text_line_get_alignment_adjustment(text
->lines
[i
],
415 DIA_RENDERER_GET_CLASS(renderer
)->draw_text_line(renderer
,
419 pos
.y
+= text
->height
;
422 DIA_RENDERER_GET_CLASS(renderer
)->set_font(renderer
, text
->font
, text
->height
);
424 pos
= text
->position
;
426 for (i
=0;i
<text
->numlines
;i
++) {
427 DIA_RENDERER_GET_CLASS(renderer
)->draw_string(renderer
,
429 &pos
, text
->alignment
,
431 pos
.y
+= text
->height
;
436 /** Default implementation of draw_text_line */
437 static void draw_text_line(DiaRenderer
*renderer
,
438 TextLine
*text_line
, Point
*pos
, Color
*color
) {
439 DIA_RENDERER_GET_CLASS(renderer
)->set_font(renderer
,
440 text_line_get_font(text_line
),
441 text_line_get_height(text_line
));
443 DIA_RENDERER_GET_CLASS(renderer
)->draw_string(renderer
,
444 text_line_get_string(text_line
),
450 draw_image (DiaRenderer
*renderer
,
451 Point
*point
, real width
, real height
,
454 g_warning ("%s::draw_image not implemented!",
455 G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (renderer
)));
460 * medium level functions, implemented by the above
462 /* Bezier implementation notes:
463 * These beziers have the following basis matrix:
468 * (At least that's what Hearn and Baker says for beziers.)
470 #define BEZIER_SUBDIVIDE_LIMIT 0.01
471 #define BEZIER_SUBDIVIDE_LIMIT_SQ (BEZIER_SUBDIVIDE_LIMIT*BEZIER_SUBDIVIDE_LIMIT)
474 bezier_add_point(BezierApprox
*bezier
,
477 /* Grow if needed: */
478 if (bezier
->currpoint
== bezier
->numpoints
) {
479 bezier
->numpoints
+= 40;
480 bezier
->points
= g_realloc(bezier
->points
,
481 bezier
->numpoints
*sizeof(Point
));
484 bezier
->points
[bezier
->currpoint
] = *point
;
490 bezier_add_lines(BezierApprox
*bezier
,
500 /* Check if almost flat: */
502 point_sub(&u
, &points
[0]);
504 point_sub(&v
, &points
[0]);
506 v_len_sq
= point_dot(&v
,&v
);
507 if (isnan(v_len_sq
)) {
508 g_warning("v_len_sq is NaN while calculating bezier curve!");
511 if (v_len_sq
< 0.000001)
513 point_scale(&y
, point_dot(&u
,&v
)/v_len_sq
);
516 delta
= point_dot(&x
,&x
);
517 if (delta
< BEZIER_SUBDIVIDE_LIMIT_SQ
) {
519 point_sub(&u
, &points
[3]);
521 point_sub(&v
, &points
[3]);
523 v_len_sq
= point_dot(&v
,&v
);
524 if (v_len_sq
< 0.000001)
526 point_scale(&y
, point_dot(&u
,&v
)/v_len_sq
);
529 delta
= point_dot(&x
,&x
);
530 if (delta
< BEZIER_SUBDIVIDE_LIMIT_SQ
) { /* Almost flat, draw a line */
531 bezier_add_point(bezier
, &points
[3]);
535 /* Subdivide into two bezier curves: */
538 point_add(&middle
, &points
[2]);
539 point_scale(&middle
, 0.5);
544 point_add(&r
[1], &points
[1]);
545 point_scale(&r
[1], 0.5);
548 point_add(&r
[2], &middle
);
549 point_scale(&r
[2], 0.5);
554 point_add(&s
[2], &points
[3]);
555 point_scale(&s
[2], 0.5);
558 point_add(&s
[1], &middle
);
559 point_scale(&s
[1], 0.5);
562 point_add(&r
[3], &s
[1]);
563 point_scale(&r
[3], 0.5);
567 bezier_add_lines(bezier
, r
);
568 bezier_add_lines(bezier
, s
);
572 bezier_add_curve(BezierApprox
*bezier
,
575 /* Is the bezier curve malformed? */
576 if ( (distance_point_point(&points
[0], &points
[1]) < 0.00001) &&
577 (distance_point_point(&points
[2], &points
[3]) < 0.00001) &&
578 (distance_point_point(&points
[0], &points
[3]) < 0.00001)) {
579 bezier_add_point(bezier
, &points
[3]);
582 bezier_add_lines(bezier
, points
);
586 approximate_bezier (BezierApprox
*bezier
,
587 BezPoint
*points
, int numpoints
)
592 if (points
[0].type
!= BEZ_MOVE_TO
)
593 g_warning("first BezPoint must be a BEZ_MOVE_TO");
594 curve
[3] = points
[0].p1
;
595 bezier_add_point(bezier
, &points
[0].p1
);
596 for (i
= 1; i
< numpoints
; i
++)
597 switch (points
[i
].type
) {
599 g_warning("only first BezPoint can be a BEZ_MOVE_TO");
600 curve
[3] = points
[i
].p1
;
603 bezier_add_point(bezier
, &points
[i
].p1
);
604 curve
[3] = points
[i
].p1
;
608 curve
[1] = points
[i
].p1
;
609 curve
[2] = points
[i
].p2
;
610 curve
[3] = points
[i
].p3
;
611 bezier_add_curve(bezier
, curve
);
616 /* bezier approximation with straight lines */
618 draw_bezier (DiaRenderer
*renderer
,
619 BezPoint
*points
, int numpoints
,
622 BezierApprox
*bezier
;
624 if (renderer
->bezier
)
625 bezier
= renderer
->bezier
;
627 renderer
->bezier
= bezier
= g_new0 (BezierApprox
, 1);
629 if (bezier
->points
== NULL
) {
630 bezier
->numpoints
= 30;
631 bezier
->points
= g_malloc(bezier
->numpoints
*sizeof(Point
));
634 bezier
->currpoint
= 0;
635 approximate_bezier (bezier
, points
, numpoints
);
637 DIA_RENDERER_GET_CLASS (renderer
)->draw_polyline (renderer
,
644 fill_bezier (DiaRenderer
*renderer
,
645 BezPoint
*points
, int numpoints
,
648 BezierApprox
*bezier
;
650 if (renderer
->bezier
)
651 bezier
= renderer
->bezier
;
653 renderer
->bezier
= bezier
= g_new0 (BezierApprox
, 1);
655 if (bezier
->points
== NULL
) {
656 bezier
->numpoints
= 30;
657 bezier
->points
= g_malloc(bezier
->numpoints
*sizeof(Point
));
660 bezier
->currpoint
= 0;
661 approximate_bezier (bezier
, points
, numpoints
);
663 DIA_RENDERER_GET_CLASS (renderer
)->fill_polygon (renderer
,
670 draw_rect (DiaRenderer
*renderer
,
671 Point
*ul_corner
, Point
*lr_corner
,
674 DiaRendererClass
*klass
= DIA_RENDERER_GET_CLASS (renderer
);
682 klass
->draw_line (renderer
, ul_corner
, &ur
, color
);
683 klass
->draw_line (renderer
, &ur
, lr_corner
, color
);
684 klass
->draw_line (renderer
, lr_corner
, &ll
, color
);
685 klass
->draw_line (renderer
, &ll
, ul_corner
, color
);
689 draw_polyline (DiaRenderer
*renderer
,
690 Point
*points
, int num_points
,
693 DiaRendererClass
*klass
= DIA_RENDERER_GET_CLASS (renderer
);
696 for (i
= 0; i
< num_points
- 1; i
++)
697 klass
->draw_line (renderer
, &points
[i
+0], &points
[i
+1], color
);
701 /* calculate the maximum possible radius for 3 points
703 given points p1,p2, and p3
704 let c = min(length(p1,p2)/2,length(p2,p3)/2)
705 let a = dot2(p1-p2, p3-p2)
706 (ie, angle between lines p1,p2 and p2,p3)
707 then maxr = c * sin(a/2)
710 calculate_min_radius( Point
*p1
, Point
*p2
, Point
*p3
)
716 c
= MIN(distance_point_point(p1
,p2
)/2,distance_point_point(p2
,p3
)/2);
717 v1
.x
= p1
->x
-p2
->x
; v1
.y
= p1
->y
-p2
->y
;
718 v2
.x
= p3
->x
-p2
->x
; v2
.y
= p3
->y
-p2
->y
;
723 /** Draw a polyline with optionally rounded corners.
724 * Based on draw_line and draw_arc, but uses draw_polyline when
725 * the rounding is too small.
728 draw_rounded_polyline (DiaRenderer
*renderer
,
729 Point
*points
, int num_points
,
730 Color
*color
, real radius
)
732 DiaRendererClass
*klass
= DIA_RENDERER_GET_CLASS (renderer
);
738 if (radius
< 0.00001) {
739 klass
->draw_polyline(renderer
, points
, num_points
, color
);
743 /* skip arc computations if we only have points */
744 if ( num_points
<= 2 ) {
746 p1
.x
= p
[i
].x
; p1
.y
= p
[i
].y
;
747 p2
.x
= p
[i
+1].x
; p2
.y
= p
[i
+1].y
;
748 klass
->draw_line(renderer
,&p1
,&p2
,color
);
753 /* full rendering 3 or more points */
754 p1
.x
= p
[i
].x
; p1
.y
= p
[i
].y
;
755 p2
.x
= p
[i
+1].x
; p2
.y
= p
[i
+1].y
;
756 for (i
= 0; i
<= num_points
- 3; i
++) {
758 real start_angle
, stop_angle
;
760 p3
.x
= p
[i
+1].x
; p3
.y
= p
[i
+1].y
;
761 p4
.x
= p
[i
+2].x
; p4
.y
= p
[i
+2].y
;
763 /* adjust the radius if it would cause odd rendering */
764 min_radius
= MIN(radius
, calculate_min_radius(&p1
,&p2
,&p4
));
765 fillet(&p1
,&p2
,&p3
,&p4
, min_radius
, &c
, &start_angle
, &stop_angle
);
766 klass
->draw_arc(renderer
, &c
, min_radius
*2, min_radius
*2,
769 klass
->draw_line(renderer
, &p1
, &p2
, color
);
770 p1
.x
= p3
.x
; p1
.y
= p3
.y
;
771 p2
.x
= p4
.x
; p2
.y
= p4
.y
;
773 klass
->draw_line(renderer
, &p3
, &p4
, color
);
777 draw_polygon (DiaRenderer
*renderer
,
778 Point
*points
, int num_points
,
781 DiaRendererClass
*klass
= DIA_RENDERER_GET_CLASS (renderer
);
784 g_return_if_fail (num_points
> 1);
786 for (i
= 0; i
< num_points
- 1; i
++)
787 klass
->draw_line (renderer
, &points
[i
+0], &points
[i
+1], color
);
788 /* close it in any case */
789 if ( (points
[0].x
!= points
[num_points
-1].x
)
790 || (points
[0].y
!= points
[num_points
-1].y
))
791 klass
->draw_line (renderer
, &points
[num_points
-1], &points
[0], color
);
795 draw_rounded_rect (DiaRenderer
*renderer
,
796 Point
*ul_corner
, Point
*lr_corner
,
797 Color
*color
, real radius
)
799 DiaRendererClass
*renderer_ops
= DIA_RENDERER_GET_CLASS (renderer
);
800 Point start
, end
, center
;
802 radius
= MIN(radius
, (lr_corner
->x
-ul_corner
->x
)/2);
803 radius
= MIN(radius
, (lr_corner
->y
-ul_corner
->y
)/2);
804 start
.x
= center
.x
= ul_corner
->x
+radius
;
805 end
.x
= lr_corner
->x
-radius
;
806 start
.y
= end
.y
= ul_corner
->y
;
807 renderer_ops
->draw_line(renderer
, &start
, &end
, color
);
808 start
.y
= end
.y
= lr_corner
->y
;
809 renderer_ops
->draw_line(renderer
, &start
, &end
, color
);
811 center
.y
= ul_corner
->y
+radius
;
812 renderer_ops
->draw_arc(renderer
, ¢er
,
813 2.0*radius
, 2.0*radius
,
816 renderer_ops
->draw_arc(renderer
, ¢er
,
817 2.0*radius
, 2.0*radius
,
820 start
.y
= ul_corner
->y
+radius
;
821 start
.x
= end
.x
= ul_corner
->x
;
822 end
.y
= center
.y
= lr_corner
->y
-radius
;
823 renderer_ops
->draw_line(renderer
, &start
, &end
, color
);
824 start
.x
= end
.x
= lr_corner
->x
;
825 renderer_ops
->draw_line(renderer
, &start
, &end
, color
);
827 center
.y
= lr_corner
->y
-radius
;
828 center
.x
= ul_corner
->x
+radius
;
829 renderer_ops
->draw_arc(renderer
, ¢er
,
830 2.0*radius
, 2.0*radius
,
831 180.0, 270.0, color
);
832 center
.x
= lr_corner
->x
-radius
;
833 renderer_ops
->draw_arc(renderer
, ¢er
,
834 2.0*radius
, 2.0*radius
,
835 270.0, 360.0, color
);
839 fill_rounded_rect(DiaRenderer
*renderer
,
840 Point
*ul_corner
, Point
*lr_corner
,
841 Color
*color
, real radius
)
843 DiaRendererClass
*renderer_ops
= DIA_RENDERER_GET_CLASS (renderer
);
844 Point start
, end
, center
;
846 radius
= MIN(radius
, (lr_corner
->x
-ul_corner
->x
)/2);
847 radius
= MIN(radius
, (lr_corner
->y
-ul_corner
->y
)/2);
848 start
.x
= center
.x
= ul_corner
->x
+radius
;
849 end
.x
= lr_corner
->x
-radius
;
850 start
.y
= ul_corner
->y
;
851 end
.y
= lr_corner
->y
;
852 renderer_ops
->fill_rect(renderer
, &start
, &end
, color
);
854 center
.y
= ul_corner
->y
+radius
;
855 renderer_ops
->fill_arc(renderer
, ¢er
,
856 2.0*radius
, 2.0*radius
,
859 renderer_ops
->fill_arc(renderer
, ¢er
,
860 2.0*radius
, 2.0*radius
,
864 start
.x
= ul_corner
->x
;
865 start
.y
= ul_corner
->y
+radius
;
866 end
.x
= lr_corner
->x
;
867 end
.y
= center
.y
= lr_corner
->y
-radius
;
868 renderer_ops
->fill_rect(renderer
, &start
, &end
, color
);
870 center
.y
= lr_corner
->y
-radius
;
871 center
.x
= ul_corner
->x
+radius
;
872 renderer_ops
->fill_arc(renderer
, ¢er
,
873 2.0*radius
, 2.0*radius
,
874 180.0, 270.0, color
);
875 center
.x
= lr_corner
->x
-radius
;
876 renderer_ops
->fill_arc(renderer
, ¢er
,
877 2.0*radius
, 2.0*radius
,
878 270.0, 360.0, color
);
882 draw_line_with_arrows(DiaRenderer
*renderer
,
890 Point oldstart
= *startpoint
;
891 Point oldend
= *endpoint
;
892 Point start_arrow_head
;
893 Point end_arrow_head
;
895 /* Calculate how to more the line to account for arrow heads */
896 if (start_arrow
!= NULL
&& start_arrow
->type
!= ARROW_NONE
) {
897 Point move_arrow
, move_line
;
898 calculate_arrow_point(start_arrow
, startpoint
, endpoint
,
899 &move_arrow
, &move_line
,
901 start_arrow_head
= *startpoint
;
902 point_sub(&start_arrow_head
, &move_arrow
);
903 point_sub(startpoint
, &move_line
);
905 if (end_arrow
!= NULL
&& end_arrow
->type
!= ARROW_NONE
) {
906 Point move_arrow
, move_line
;
907 calculate_arrow_point(end_arrow
, endpoint
, startpoint
,
908 &move_arrow
, &move_line
,
910 end_arrow_head
= *endpoint
;
911 point_sub(&end_arrow_head
, &move_arrow
);
912 point_sub(endpoint
, &move_line
);
915 DIA_RENDERER_GET_CLASS(renderer
)->draw_line(renderer
, startpoint
, endpoint
, color
);
917 /* Actual arrow drawing down here so line styles aren't disturbed */
918 if (start_arrow
!= NULL
&& start_arrow
->type
!= ARROW_NONE
)
919 arrow_draw(renderer
, start_arrow
->type
,
920 &start_arrow_head
, endpoint
,
921 start_arrow
->length
, start_arrow
->width
,
923 color
, &color_white
);
924 if (end_arrow
!= NULL
&& end_arrow
->type
!= ARROW_NONE
)
925 arrow_draw(renderer
, end_arrow
->type
,
926 &end_arrow_head
, startpoint
,
927 end_arrow
->length
, end_arrow
->width
,
929 color
, &color_white
);
931 *startpoint
= oldstart
;
936 draw_polyline_with_arrows(DiaRenderer
*renderer
,
937 Point
*points
, int num_points
,
943 /* Index of first and last point with a non-zero length segment */
945 int lastline
= num_points
;
946 Point oldstart
= points
[firstline
];
947 Point oldend
= points
[lastline
-1];
948 Point start_arrow_head
;
949 Point end_arrow_head
;
951 if (start_arrow
!= NULL
&& start_arrow
->type
!= ARROW_NONE
) {
952 Point move_arrow
, move_line
;
953 while (firstline
< num_points
-1 &&
954 distance_point_point(&points
[firstline
],
955 &points
[firstline
+1]) < 0.0000001)
957 if (firstline
== num_points
-1)
958 firstline
= 0; /* No non-zero lines, it doesn't matter. */
959 oldstart
= points
[firstline
];
960 calculate_arrow_point(start_arrow
,
961 &points
[firstline
], &points
[firstline
+1],
962 &move_arrow
, &move_line
,
964 start_arrow_head
= points
[firstline
];
965 point_sub(&start_arrow_head
, &move_arrow
);
966 point_sub(&points
[firstline
], &move_line
);
968 if (end_arrow
!= NULL
&& end_arrow
->type
!= ARROW_NONE
) {
969 Point move_arrow
, move_line
;
970 while (lastline
> 0 &&
971 distance_point_point(&points
[lastline
-1],
972 &points
[lastline
-2]) < 0.0000001)
975 firstline
= num_points
; /* No non-zero lines, it doesn't matter. */
976 oldend
= points
[lastline
-1];
977 calculate_arrow_point(end_arrow
, &points
[lastline
-1],
979 &move_arrow
, &move_line
,
981 end_arrow_head
= points
[lastline
-1];
982 point_sub(&end_arrow_head
, &move_arrow
);
983 point_sub(&points
[lastline
-1], &move_line
);
985 /* Don't draw degenerate line segments at end of line */
986 if (lastline
-firstline
> 1) /* probably hiding a bug above, but don't try to draw a negative
987 * number of points at all, fixes bug #148139 */
988 DIA_RENDERER_GET_CLASS(renderer
)->draw_polyline(renderer
, &points
[firstline
],
989 lastline
-firstline
, color
);
990 if (start_arrow
!= NULL
&& start_arrow
->type
!= ARROW_NONE
)
991 arrow_draw(renderer
, start_arrow
->type
,
992 &start_arrow_head
, &points
[firstline
+1],
993 start_arrow
->length
, start_arrow
->width
,
995 color
, &color_white
);
996 if (end_arrow
!= NULL
&& end_arrow
->type
!= ARROW_NONE
)
997 arrow_draw(renderer
, end_arrow
->type
,
998 &end_arrow_head
, &points
[lastline
-2],
999 end_arrow
->length
, end_arrow
->width
,
1001 color
, &color_white
);
1003 points
[firstline
] = oldstart
;
1004 points
[lastline
-1] = oldend
;
1008 draw_rounded_polyline_with_arrows(DiaRenderer
*renderer
,
1009 Point
*points
, int num_points
,
1016 /* Index of first and last point with a non-zero length segment */
1018 int lastline
= num_points
;
1019 Point oldstart
= points
[firstline
];
1020 Point oldend
= points
[lastline
-1];
1021 Point start_arrow_head
;
1022 Point end_arrow_head
;
1024 if (start_arrow
!= NULL
&& start_arrow
->type
!= ARROW_NONE
) {
1025 Point move_arrow
, move_line
;
1026 while (firstline
< num_points
-1 &&
1027 distance_point_point(&points
[firstline
],
1028 &points
[firstline
+1]) < 0.0000001)
1030 if (firstline
== num_points
-1)
1031 firstline
= 0; /* No non-zero lines, it doesn't matter. */
1032 oldstart
= points
[firstline
];
1033 calculate_arrow_point(start_arrow
,
1034 &points
[firstline
], &points
[firstline
+1],
1035 &move_arrow
, &move_line
,
1037 start_arrow_head
= points
[firstline
];
1038 point_sub(&start_arrow_head
, &move_arrow
);
1039 point_sub(&points
[firstline
], &move_line
);
1041 if (end_arrow
!= NULL
&& end_arrow
->type
!= ARROW_NONE
) {
1042 Point move_arrow
, move_line
;
1043 while (lastline
> 0 &&
1044 distance_point_point(&points
[lastline
-1],
1045 &points
[lastline
-2]) < 0.0000001)
1048 firstline
= num_points
; /* No non-zero lines, it doesn't matter. */
1049 oldend
= points
[lastline
-1];
1050 calculate_arrow_point(end_arrow
, &points
[lastline
-1],
1051 &points
[lastline
-2],
1052 &move_arrow
, &move_line
,
1054 end_arrow_head
= points
[lastline
-1];
1055 point_sub(&end_arrow_head
, &move_arrow
);
1056 point_sub(&points
[lastline
-1], &move_line
);
1058 /* Don't draw degenerate line segments at end of line */
1059 if (lastline
-firstline
> 1) /* only if there is something */
1060 DIA_RENDERER_GET_CLASS(renderer
)->draw_rounded_polyline(renderer
,
1064 if (start_arrow
!= NULL
&& start_arrow
->type
!= ARROW_NONE
)
1065 arrow_draw(renderer
, start_arrow
->type
,
1066 &start_arrow_head
, &points
[firstline
+1],
1067 start_arrow
->length
, start_arrow
->width
,
1069 color
, &color_white
);
1070 if (end_arrow
!= NULL
&& end_arrow
->type
!= ARROW_NONE
)
1071 arrow_draw(renderer
, end_arrow
->type
,
1072 &end_arrow_head
, &points
[lastline
-2],
1073 end_arrow
->length
, end_arrow
->width
,
1075 color
, &color_white
);
1077 points
[firstline
] = oldstart
;
1078 points
[lastline
-1] = oldend
;
1081 /** Figure the equation for a line given by two points.
1082 * Returns FALSE if the line is vertical (infinite a).
1085 points_to_line(real
*a
, real
*b
, Point
*p1
, Point
*p2
)
1087 if (fabs(p1
->x
- p2
->x
) < 0.000000001)
1089 *a
= (p2
->y
-p1
->y
)/(p2
->x
-p1
->x
);
1090 *b
= p1
->y
-(*a
)*p1
->x
;
1094 /** Find the intersection between two lines.
1095 * Returns TRUE if the lines intersect in a single point.
1098 intersection_line_line(Point
*cross
,
1099 Point
*p1a
, Point
*p1b
,
1100 Point
*p2a
, Point
*p2b
)
1102 real a1
, b1
, a2
, b2
;
1104 /* Find coefficients of lines */
1105 if (!(points_to_line(&a1
, &b1
, p1a
, p1b
))) {
1106 if (!(points_to_line(&a2
, &b2
, p2a
, p2b
))) {
1107 if (fabs(p1a
->x
-p2a
->x
) < 0.00000001) {
1110 } else return FALSE
;
1113 cross
->y
= a2
*(p1a
->x
)+b2
;
1116 if (!(points_to_line(&a2
, &b2
, p2a
, p2b
))) {
1118 cross
->y
= a1
*(p2a
->x
)+b1
;
1122 if (fabs(a1
-a2
) < 0.000000001) {
1123 if (fabs(b1
-b2
) < 0.000000001) {
1137 cross
->x
= (b2
-b1
)/(a1
-a2
);
1138 cross
->y
= a1
*cross
->x
+b1
;
1143 /** Given three points, find the center of the circle they describe.
1144 * Returns FALSE if the center could not be determined (i.e. the points
1145 * all lie really close together).
1146 * The renderer should disappear once the debugging is done.
1149 find_center_point(Point
*center
, Point
*p1
, Point
*p2
, Point
*p3
)
1157 /* Find vector from middle between two points towards center */
1159 point_sub(&mid1
, p2
);
1160 point_scale(&mid1
, 0.5);
1162 point_add(&mid1
, p2
); /* Now midpoint between p1 & p2 */
1166 point_add(&orth1
, &mid1
);
1169 /* Again, with two other points */
1171 point_sub(&mid2
, p3
);
1172 point_scale(&mid2
, 0.5);
1174 point_add(&mid2
, p3
); /* Now midpoint between p2 & p3 */
1178 point_add(&orth2
, &mid2
);
1180 /* The intersection between these two is the center */
1181 if (!intersection_line_line(center
, &mid1
, &orth1
, &mid2
, &orth2
)) {
1182 /* Degenerate circle */
1183 /* Case 1: Points are all on top of each other. Nothing to do. */
1184 if (fabs((p1
->x
+ p2
->x
+ p3
->x
)/3 - p1
->x
) < 0.0000001 &&
1185 fabs((p1
->y
+ p2
->y
+ p3
->y
)/3 - p1
->y
) < 0.0000001) {
1189 /* Case 2: Two points are on top of each other. Midpoint of
1190 * non-degenerate line is center. */
1191 if (distance_point_point_manhattan(p1
, p2
) < 0.0000001) {
1194 } else if (distance_point_point_manhattan(p1
, p3
) < 0.0000001 ||
1195 distance_point_point_manhattan(p2
, p3
) < 0.0000001) {
1199 /* Case 3: All points on a line. Nothing to do. */
1206 is_right_hand (const Point
*a
, const Point
*b
, const Point
*c
)
1211 point_sub(&dot1
, c
);
1212 point_normalize(&dot1
);
1214 point_sub(&dot2
, c
);
1215 point_normalize(&dot2
);
1216 return point_cross(&dot1
, &dot2
) > 0;
1220 draw_arc_with_arrows (DiaRenderer
*renderer
,
1229 Point oldstart
= *startpoint
;
1230 Point oldend
= *endpoint
;
1232 real width
, angle1
, angle2
, arrow_ofs
= 0.0;
1234 Point start_arrow_head
;
1235 Point start_arrow_end
;
1236 Point end_arrow_head
;
1237 Point end_arrow_end
;
1239 if (!find_center_point(¢er
, startpoint
, endpoint
, midpoint
)) {
1240 /* Degenerate circle -- should have been caught by the drawer? */
1241 printf("Degenerate\n");
1244 righthand
= is_right_hand (startpoint
, midpoint
, endpoint
);
1246 width
= 2*distance_point_point(¢er
, startpoint
);
1248 if (start_arrow
!= NULL
&& start_arrow
->type
!= ARROW_NONE
) {
1249 Point move_arrow
, move_line
;
1252 start_arrow_end
= *startpoint
;
1253 point_sub(&start_arrow_end
, ¢er
);
1254 tmp
= start_arrow_end
.x
;
1256 start_arrow_end
.x
= -start_arrow_end
.y
;
1257 start_arrow_end
.y
= tmp
;
1259 start_arrow_end
.x
= start_arrow_end
.y
;
1260 start_arrow_end
.y
= -tmp
;
1262 point_add(&start_arrow_end
, startpoint
);
1264 calculate_arrow_point(start_arrow
, startpoint
, &start_arrow_end
,
1265 &move_arrow
, &move_line
,
1267 start_arrow_head
= *startpoint
;
1268 point_sub(&start_arrow_head
, &move_arrow
);
1269 point_sub(startpoint
, &move_line
);
1270 arrow_ofs
+= sqrt (move_line
.x
* move_line
.x
+ move_line
.y
* move_line
.y
);
1272 if (end_arrow
!= NULL
&& end_arrow
->type
!= ARROW_NONE
) {
1273 Point move_arrow
, move_line
;
1276 end_arrow_end
= *endpoint
;
1277 point_sub(&end_arrow_end
, ¢er
);
1278 tmp
= end_arrow_end
.x
;
1280 end_arrow_end
.x
= end_arrow_end
.y
;
1281 end_arrow_end
.y
= -tmp
;
1283 end_arrow_end
.x
= -end_arrow_end
.y
;
1284 end_arrow_end
.y
= tmp
;
1286 point_add(&end_arrow_end
, endpoint
);
1288 calculate_arrow_point(end_arrow
, endpoint
, &end_arrow_end
,
1289 &move_arrow
, &move_line
,
1291 end_arrow_head
= *endpoint
;
1292 point_sub(&end_arrow_head
, &move_arrow
);
1293 point_sub(endpoint
, &move_line
);
1294 arrow_ofs
+= sqrt (move_line
.x
* move_line
.x
+ move_line
.y
* move_line
.y
);
1297 /* Now we possibly have new start- and endpoint. We must not
1298 * recalculate the center cause the new points lie on the tangential
1299 * approximation of the original arc arrow lines not on the arc itself.
1300 * The one thing we need to deal with is calculating the (new) angles
1301 * and get rid of the arc drawing altogether if got degenerated.
1303 * Why shouldn't we recalculate the whole thing from the new start/endpoints?
1304 * Done this way the arc does not come out the back of the arrows.
1307 angle1
= -atan2(startpoint
->y
- center
.y
, startpoint
->x
- center
.x
)*180.0/G_PI
;
1308 while (angle1
< 0.0) angle1
+= 360.0;
1309 angle2
= -atan2(endpoint
->y
- center
.y
, endpoint
->x
- center
.x
)*180.0/G_PI
;
1310 while (angle2
< 0.0) angle2
+= 360.0;
1316 /* now with the angles we can bring the startpoint back to the arc, but there must be a less expensive way to do this? */
1317 if (start_arrow
!= NULL
&& start_arrow
->type
!= ARROW_NONE
) {
1318 startpoint
->x
= cos (G_PI
* angle1
/ 180.0) * width
/ 2.0 + center
.x
;
1319 startpoint
->y
= sin (G_PI
* angle1
/ 180.0) * width
/ 2.0 + center
.y
;
1321 if (end_arrow
!= NULL
&& end_arrow
->type
!= ARROW_NONE
) {
1322 endpoint
->x
= cos (G_PI
* angle1
/ 180.0) * width
/ 2.0 + center
.x
;
1323 endpoint
->y
= sin (G_PI
* angle1
/ 180.0) * width
/ 2.0 + center
.y
;
1326 DIA_RENDERER_GET_CLASS(renderer
)->draw_arc(renderer
, ¢er
, width
, width
,
1327 angle1
, angle2
, color
);
1329 if (start_arrow
!= NULL
&& start_arrow
->type
!= ARROW_NONE
)
1330 arrow_draw(renderer
, start_arrow
->type
,
1331 &start_arrow_head
, &start_arrow_end
,
1332 start_arrow
->length
, start_arrow
->width
,
1334 color
, &color_white
);
1335 if (end_arrow
!= NULL
&& end_arrow
->type
!= ARROW_NONE
)
1336 arrow_draw(renderer
, end_arrow
->type
,
1337 &end_arrow_head
, &end_arrow_end
,
1338 end_arrow
->length
, end_arrow
->width
,
1340 color
, &color_white
);
1342 *startpoint
= oldstart
;
1347 draw_bezier_with_arrows(DiaRenderer
*renderer
,
1355 Point startpoint
, endpoint
;
1356 Point start_arrow_head
;
1357 Point end_arrow_head
;
1359 startpoint
= points
[0].p1
;
1360 endpoint
= points
[num_points
-1].p3
;
1362 if (start_arrow
!= NULL
&& start_arrow
->type
!= ARROW_NONE
) {
1365 calculate_arrow_point(start_arrow
, &points
[0].p1
, &points
[1].p1
,
1366 &move_arrow
, &move_line
,
1368 start_arrow_head
= points
[0].p1
;
1369 point_sub(&start_arrow_head
, &move_arrow
);
1370 point_sub(&points
[0].p1
, &move_line
);
1372 if (end_arrow
!= NULL
&& end_arrow
->type
!= ARROW_NONE
) {
1375 calculate_arrow_point(end_arrow
,
1376 &points
[num_points
-1].p3
, &points
[num_points
-1].p2
,
1377 &move_arrow
, &move_line
,
1379 end_arrow_head
= points
[num_points
-1].p3
;
1380 point_sub(&end_arrow_head
, &move_arrow
);
1381 point_sub(&points
[num_points
-1].p3
, &move_line
);
1383 DIA_RENDERER_GET_CLASS(renderer
)->draw_bezier(renderer
, points
, num_points
, color
);
1384 if (start_arrow
!= NULL
&& start_arrow
->type
!= ARROW_NONE
)
1385 arrow_draw(renderer
, start_arrow
->type
,
1386 &start_arrow_head
, &points
[1].p1
,
1387 start_arrow
->length
, start_arrow
->width
,
1389 color
, &color_white
);
1390 if (end_arrow
!= NULL
&& end_arrow
->type
!= ARROW_NONE
)
1391 arrow_draw(renderer
, end_arrow
->type
,
1392 &end_arrow_head
, &points
[num_points
-1].p2
,
1393 end_arrow
->length
, end_arrow
->width
,
1395 color
, &color_white
);
1397 points
[0].p1
= startpoint
;
1398 points
[num_points
-1].p3
= endpoint
;
1403 * Should we really provide this ? It formerly was an 'interactive op'
1404 * and depends on DiaRenderer::set_font() not or properly overwritten
1407 get_text_width (DiaRenderer
*renderer
,
1408 const gchar
*text
, int length
)
1412 if (renderer
->font
) {
1413 char *str
= g_strndup (text
, length
);
1415 ret
= dia_font_string_width (str
,
1417 renderer
->font_height
);
1425 get_width_pixels (DiaRenderer
*renderer
)
1427 g_return_val_if_fail (renderer
->is_interactive
, 0);
1432 get_height_pixels (DiaRenderer
*renderer
)
1434 g_return_val_if_fail (renderer
->is_interactive
, 0);
1440 * non member functions
1443 dia_renderer_get_width_pixels (DiaRenderer
*renderer
)
1445 return DIA_RENDERER_GET_CLASS(renderer
)->get_width_pixels (renderer
);
1449 dia_renderer_get_height_pixels (DiaRenderer
*renderer
)
1451 return DIA_RENDERER_GET_CLASS(renderer
)->get_height_pixels (renderer
);