2004-01-17 Hans Breuer <hans@breuer.org>
[dia.git] / lib / diarenderer.c
blob3e6278bb7f4f8c6a0c8456e76c7f52e11173ec43
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"
25 #include "object.h"
26 #include "text.h"
28 struct _BezierApprox {
29 Point *points;
30 int numpoints;
31 int currpoint;
34 static void dia_renderer_class_init (DiaRendererClass *klass);
36 static int get_width_pixels (DiaRenderer *);
37 static int get_height_pixels (DiaRenderer *);
39 static void begin_render (DiaRenderer *);
40 static void end_render (DiaRenderer *);
42 static void set_linewidth (DiaRenderer *renderer, real linewidth);
43 static void set_linecaps (DiaRenderer *renderer, LineCaps mode);
44 static void set_linejoin (DiaRenderer *renderer, LineJoin mode);
45 static void set_linestyle (DiaRenderer *renderer, LineStyle mode);
46 static void set_dashlength (DiaRenderer *renderer, real length);
47 static void set_fillstyle (DiaRenderer *renderer, FillStyle mode);
48 static void set_font (DiaRenderer *renderer, DiaFont *font, real height);
50 static void draw_line (DiaRenderer *renderer,
51 Point *start, Point *end,
52 Color *color);
53 static void fill_rect (DiaRenderer *renderer,
54 Point *ul_corner, Point *lr_corner,
55 Color *color);
56 static void fill_polygon (DiaRenderer *renderer,
57 Point *points, int num_points,
58 Color *color);
59 static void draw_arc (DiaRenderer *renderer,
60 Point *center,
61 real width, real height,
62 real angle1, real angle2,
63 Color *color);
64 static void fill_arc (DiaRenderer *renderer,
65 Point *center,
66 real width, real height,
67 real angle1, real angle2,
68 Color *color);
69 static void draw_ellipse (DiaRenderer *renderer,
70 Point *center,
71 real width, real height,
72 Color *color);
73 static void fill_ellipse (DiaRenderer *renderer,
74 Point *center,
75 real width, real height,
76 Color *color);
77 static void draw_bezier (DiaRenderer *renderer,
78 BezPoint *points,
79 int numpoints,
80 Color *color);
81 static void fill_bezier (DiaRenderer *renderer,
82 BezPoint *points,
83 int numpoints,
84 Color *color);
85 static void draw_string (DiaRenderer *renderer,
86 const gchar *text,
87 Point *pos,
88 Alignment alignment,
89 Color *color);
90 static void draw_image (DiaRenderer *renderer,
91 Point *point,
92 real width, real height,
93 DiaImage image);
94 static void draw_text (DiaRenderer *renderer,
95 Text *text);
97 static void draw_rect (DiaRenderer *renderer,
98 Point *ul_corner, Point *lr_corner,
99 Color *color);
100 static void draw_polyline (DiaRenderer *renderer,
101 Point *points, int num_points,
102 Color *color);
103 static void draw_polygon (DiaRenderer *renderer,
104 Point *points, int num_points,
105 Color *color);
107 static real get_text_width (DiaRenderer *renderer,
108 const gchar *text, int length);
110 static void draw_rounded_rect (DiaRenderer *renderer,
111 Point *ul_corner, Point *lr_corner,
112 Color *color, real rounding);
113 static void fill_rounded_rect (DiaRenderer *renderer,
114 Point *ul_corner, Point *lr_corner,
115 Color *color, real rounding);
116 static void draw_line_with_arrows (DiaRenderer *renderer,
117 Point *start, Point *end,
118 real line_width,
119 Color *line_color,
120 Arrow *start_arrow,
121 Arrow *end_arrow);
122 static void draw_arc_with_arrows (DiaRenderer *renderer,
123 Point *start, Point *end,
124 Point *midpoint,
125 real line_width,
126 Color *color,
127 Arrow *start_arrow,
128 Arrow *end_arrow);
129 static void draw_polyline_with_arrows (DiaRenderer *renderer,
130 Point *points, int num_points,
131 real line_width,
132 Color *color,
133 Arrow *start_arrow,
134 Arrow *end_arrow);
135 static void draw_bezier_with_arrows (DiaRenderer *renderer,
136 BezPoint *points,
137 int num_points,
138 real line_width,
139 Color *color,
140 Arrow *start_arrow,
141 Arrow *end_arrow);
143 static gpointer parent_class = NULL;
145 GType
146 dia_renderer_get_type (void)
148 static GType object_type = 0;
150 if (!object_type)
152 static const GTypeInfo object_info =
154 sizeof (DiaRendererClass),
155 (GBaseInitFunc) NULL,
156 (GBaseFinalizeFunc) NULL,
157 (GClassInitFunc) dia_renderer_class_init,
158 NULL, /* class_finalize */
159 NULL, /* class_data */
160 sizeof (DiaRenderer),
161 0, /* n_preallocs */
162 NULL /* init */
165 object_type = g_type_register_static (G_TYPE_OBJECT,
166 "DiaRenderer",
167 &object_info, 0);
170 return object_type;
173 static void
174 draw_object (DiaRenderer *renderer,
175 Object *object)
177 object->ops->draw(object, renderer);
180 static void
181 renderer_finalize (GObject *object)
183 DiaRenderer *renderer = DIA_RENDERER (object);
185 if (renderer->font)
186 dia_font_unref (renderer->font);
188 if (renderer->bezier)
190 if (renderer->bezier->points)
191 g_free (renderer->bezier->points);
192 g_free (renderer->bezier);
195 G_OBJECT_CLASS (parent_class)->finalize (object);
198 static void
199 dia_renderer_class_init (DiaRendererClass *klass)
201 GObjectClass *object_class = G_OBJECT_CLASS (klass);
202 DiaRendererClass *renderer_class = DIA_RENDERER_CLASS (klass);
204 parent_class = g_type_class_peek_parent (klass);
206 object_class->finalize = renderer_finalize;
208 renderer_class->get_width_pixels = get_width_pixels;
209 renderer_class->get_height_pixels = get_height_pixels;
210 renderer_class->draw_object = draw_object;
211 renderer_class->get_text_width = get_text_width;
213 renderer_class->begin_render = begin_render;
214 renderer_class->end_render = end_render;
216 renderer_class->set_linewidth = set_linewidth;
217 renderer_class->set_linecaps = set_linecaps;
218 renderer_class->set_linejoin = set_linejoin;
219 renderer_class->set_linestyle = set_linestyle;
220 renderer_class->set_dashlength = set_dashlength;
221 renderer_class->set_fillstyle = set_fillstyle;
222 renderer_class->set_font = set_font;
224 renderer_class->draw_line = draw_line;
225 renderer_class->fill_rect = fill_rect;
226 renderer_class->fill_polygon = fill_polygon;
227 renderer_class->draw_arc = draw_arc;
228 renderer_class->fill_arc = fill_arc;
229 renderer_class->draw_ellipse = draw_ellipse;
230 renderer_class->fill_ellipse = fill_ellipse;
231 renderer_class->draw_string = draw_string;
232 renderer_class->draw_image = draw_image;
234 /* medium level functions */
235 renderer_class->draw_bezier = draw_bezier;
236 renderer_class->fill_bezier = fill_bezier;
237 renderer_class->draw_rect = draw_rect;
238 renderer_class->draw_polyline = draw_polyline;
239 renderer_class->draw_polygon = draw_polygon;
240 renderer_class->draw_text = draw_text;
242 /* highest level functions */
243 renderer_class->draw_rounded_rect = draw_rounded_rect;
244 renderer_class->fill_rounded_rect = fill_rounded_rect;
245 renderer_class->draw_line_with_arrows = draw_line_with_arrows;
246 renderer_class->draw_arc_with_arrows = draw_arc_with_arrows;
247 renderer_class->draw_polyline_with_arrows = draw_polyline_with_arrows;
248 renderer_class->draw_bezier_with_arrows = draw_bezier_with_arrows;
251 static void
252 begin_render (DiaRenderer *object)
254 g_warning ("%s::begin_render not implemented!",
255 G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (object)));
258 static void
259 end_render (DiaRenderer *object)
261 g_warning ("%s::end_render not implemented!",
262 G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (object)));
265 static void
266 set_linewidth (DiaRenderer *object, real linewidth)
268 g_warning ("%s::set_line_width not implemented!",
269 G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (object)));
272 static void
273 set_linecaps (DiaRenderer *object, LineCaps mode)
275 g_warning ("%s::set_line_caps not implemented!",
276 G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (object)));
279 static void
280 set_linejoin (DiaRenderer *renderer, LineJoin mode)
282 g_warning ("%s::set_line_join not implemented!",
283 G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (renderer)));
286 static void
287 set_linestyle (DiaRenderer *renderer, LineStyle mode)
289 g_warning ("%s::set_line_style not implemented!",
290 G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (renderer)));
293 static void
294 set_dashlength (DiaRenderer *renderer, real length)
296 g_warning ("%s::set_dash_length not implemented!",
297 G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (renderer)));
300 static void
301 set_fillstyle (DiaRenderer *renderer, FillStyle mode)
303 g_warning ("%s::set_fill_style not implemented!",
304 G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (renderer)));
307 static void
308 set_font (DiaRenderer *renderer, DiaFont *font, real height)
310 if (renderer->font)
311 dia_font_unref (renderer->font);
312 renderer->font = dia_font_ref (font);
313 renderer->font_height = height;
317 static void
318 draw_line (DiaRenderer *renderer, Point *start, Point *end, Color *color)
320 g_warning ("%s::draw_line not implemented!",
321 G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (renderer)));
324 static void
325 fill_polygon (DiaRenderer *renderer, Point *points, int num_points, Color *color)
327 g_warning ("%s::fill_polygon not implemented!",
328 G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (renderer)));
331 static void
332 draw_arc (DiaRenderer *renderer, Point *center,
333 real width, real height, real angle1, real angle2,
334 Color *color)
336 g_warning ("%s::draw_arc not implemented!",
337 G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (renderer)));
340 static void
341 fill_rect (DiaRenderer *renderer,
342 Point *ul_corner, Point *lr_corner,
343 Color *color)
345 g_warning ("%s::fill_rect not implemented!",
346 G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (renderer)));
349 static void
350 fill_arc (DiaRenderer *renderer, Point *center,
351 real width, real height, real angle1, real angle2,
352 Color *color)
354 g_warning ("%s::fill_arc not implemented!",
355 G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (renderer)));
358 static void
359 draw_ellipse (DiaRenderer *renderer, Point *center,
360 real width, real height,
361 Color *color)
363 g_warning ("%s::draw_ellipse not implemented!",
364 G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (renderer)));
367 static void
368 fill_ellipse (DiaRenderer *renderer, Point *center,
369 real width, real height, Color *color)
371 g_warning ("%s::fill_ellipse not implemented!",
372 G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (renderer)));
375 static void
376 draw_string (DiaRenderer *renderer,
377 const gchar *text, Point *pos, Alignment alignment,
378 Color *color)
380 g_warning ("%s::draw_string not implemented!",
381 G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (renderer)));
384 /** Default implementation of draw_text */
385 static void draw_text(DiaRenderer *renderer,
386 Text *text) {
387 Point pos;
388 int i;
390 DIA_RENDERER_GET_CLASS(renderer)->set_font(renderer, text->font, text->height);
392 pos = text->position;
394 for (i=0;i<text->numlines;i++) {
395 DIA_RENDERER_GET_CLASS(renderer)->draw_string(renderer,
396 text->line[i],
397 &pos, text->alignment,
398 &text->color);
399 pos.y += text->height;
403 static void
404 draw_image (DiaRenderer *renderer,
405 Point *point, real width, real height,
406 DiaImage image)
408 g_warning ("%s::draw_image not implemented!",
409 G_OBJECT_CLASS_NAME (G_OBJECT_GET_CLASS (renderer)));
414 * medium level functions, implemented by the above
416 /* Bezier implementation notes:
417 * These beziers have the following basis matrix:
418 * [-1 3 -3 1]
419 * [ 3 -6 3 0]
420 * [-3 3 0 0]
421 * [ 1 0 0 0]
422 * (At least that's what Hearn and Baker says for beziers.)
424 #define BEZIER_SUBDIVIDE_LIMIT 0.03
425 #define BEZIER_SUBDIVIDE_LIMIT_SQ (BEZIER_SUBDIVIDE_LIMIT*BEZIER_SUBDIVIDE_LIMIT)
427 static void
428 bezier_add_point(BezierApprox *bezier,
429 Point *point)
431 /* Grow if needed: */
432 if (bezier->currpoint == bezier->numpoints) {
433 bezier->numpoints += 40;
434 bezier->points = g_realloc(bezier->points,
435 bezier->numpoints*sizeof(Point));
438 bezier->points[bezier->currpoint] = *point;
440 bezier->currpoint++;
443 static void
444 bezier_add_lines(BezierApprox *bezier,
445 Point points[4])
447 Point u, v, x, y;
448 Point r[4];
449 Point s[4];
450 Point middle;
451 coord delta;
452 real v_len_sq;
454 /* Check if almost flat: */
455 u = points[1];
456 point_sub(&u, &points[0]);
457 v = points[3];
458 point_sub(&v, &points[0]);
459 y = v;
460 v_len_sq = point_dot(&v,&v);
461 if (isnan(v_len_sq)) {
462 g_warning("v_len_sq is NaN while calculating bezier curve!");
463 return;
465 if (v_len_sq < 0.000001)
466 v_len_sq = 0.000001;
467 point_scale(&y, point_dot(&u,&v)/v_len_sq);
468 x = u;
469 point_sub(&x,&y);
470 delta = point_dot(&x,&x);
471 if (delta < BEZIER_SUBDIVIDE_LIMIT_SQ) {
472 u = points[2];
473 point_sub(&u, &points[3]);
474 v = points[0];
475 point_sub(&v, &points[3]);
476 y = v;
477 v_len_sq = point_dot(&v,&v);
478 if (v_len_sq < 0.000001)
479 v_len_sq = 0.000001;
480 point_scale(&y, point_dot(&u,&v)/v_len_sq);
481 x = u;
482 point_sub(&x,&y);
483 delta = point_dot(&x,&x);
484 if (delta < BEZIER_SUBDIVIDE_LIMIT_SQ) { /* Almost flat, draw a line */
485 bezier_add_point(bezier, &points[3]);
486 return;
489 /* Subdivide into two bezier curves: */
491 middle = points[1];
492 point_add(&middle, &points[2]);
493 point_scale(&middle, 0.5);
495 r[0] = points[0];
497 r[1] = points[0];
498 point_add(&r[1], &points[1]);
499 point_scale(&r[1], 0.5);
501 r[2] = r[1];
502 point_add(&r[2], &middle);
503 point_scale(&r[2], 0.5);
505 s[3] = points[3];
507 s[2] = points[2];
508 point_add(&s[2], &points[3]);
509 point_scale(&s[2], 0.5);
511 s[1] = s[2];
512 point_add(&s[1], &middle);
513 point_scale(&s[1], 0.5);
515 r[3] = r[2];
516 point_add(&r[3], &s[1]);
517 point_scale(&r[3], 0.5);
519 s[0] = r[3];
521 bezier_add_lines(bezier, r);
522 bezier_add_lines(bezier, s);
525 static void
526 bezier_add_curve(BezierApprox *bezier,
527 Point points[4])
529 /* Is the bezier curve malformed? */
530 if ( (distance_point_point(&points[0], &points[1]) < 0.00001) &&
531 (distance_point_point(&points[2], &points[3]) < 0.00001) &&
532 (distance_point_point(&points[0], &points[3]) < 0.00001)) {
533 bezier_add_point(bezier, &points[3]);
536 bezier_add_lines(bezier, points);
539 static void
540 approximate_bezier (BezierApprox *bezier,
541 BezPoint *points, int numpoints)
543 Point curve[4];
544 int i;
546 if (points[0].type != BEZ_MOVE_TO)
547 g_warning("first BezPoint must be a BEZ_MOVE_TO");
548 curve[3] = points[0].p1;
549 bezier_add_point(bezier, &points[0].p1);
550 for (i = 1; i < numpoints; i++)
551 switch (points[i].type) {
552 case BEZ_MOVE_TO:
553 g_warning("only first BezPoint can be a BEZ_MOVE_TO");
554 curve[3] = points[i].p1;
555 break;
556 case BEZ_LINE_TO:
557 bezier_add_point(bezier, &points[i].p1);
558 curve[3] = points[i].p1;
559 break;
560 case BEZ_CURVE_TO:
561 curve[0] = curve[3];
562 curve[1] = points[i].p1;
563 curve[2] = points[i].p2;
564 curve[3] = points[i].p3;
565 bezier_add_curve(bezier, curve);
566 break;
570 /* bezier approximation with straight lines */
571 static void
572 draw_bezier (DiaRenderer *renderer,
573 BezPoint *points, int numpoints,
574 Color *color)
576 BezierApprox *bezier;
578 if (renderer->bezier)
579 bezier = renderer->bezier;
580 else
581 renderer->bezier = bezier = g_new0 (BezierApprox, 1);
583 if (bezier->points == NULL) {
584 bezier->numpoints = 30;
585 bezier->points = g_malloc(bezier->numpoints*sizeof(Point));
588 bezier->currpoint = 0;
589 approximate_bezier (bezier, points, numpoints);
591 DIA_RENDERER_GET_CLASS (renderer)->draw_polyline (renderer,
592 bezier->points,
593 bezier->currpoint,
594 color);
597 static void
598 fill_bezier (DiaRenderer *renderer,
599 BezPoint *points, int numpoints,
600 Color *color)
602 BezierApprox *bezier;
604 if (renderer->bezier)
605 bezier = renderer->bezier;
606 else
607 renderer->bezier = bezier = g_new0 (BezierApprox, 1);
609 if (bezier->points == NULL) {
610 bezier->numpoints = 30;
611 bezier->points = g_malloc(bezier->numpoints*sizeof(Point));
614 bezier->currpoint = 0;
615 approximate_bezier (bezier, points, numpoints);
617 DIA_RENDERER_GET_CLASS (renderer)->fill_polygon (renderer,
618 bezier->points,
619 bezier->currpoint,
620 color);
623 static void
624 draw_rect (DiaRenderer *renderer,
625 Point *ul_corner, Point *lr_corner,
626 Color *color)
628 DiaRendererClass *klass = DIA_RENDERER_GET_CLASS (renderer);
629 Point ur, ll;
631 ur.x = lr_corner->x;
632 ur.y = ul_corner->y;
633 ll.x = ul_corner->x;
634 ll.y = lr_corner->y;
636 klass->draw_line (renderer, ul_corner, &ur, color);
637 klass->draw_line (renderer, &ur, lr_corner, color);
638 klass->draw_line (renderer, lr_corner, &ll, color);
639 klass->draw_line (renderer, &ll, ul_corner, color);
642 static void
643 draw_polyline (DiaRenderer *renderer,
644 Point *points, int num_points,
645 Color *color)
647 DiaRendererClass *klass = DIA_RENDERER_GET_CLASS (renderer);
648 int i;
650 for (i = 0; i < num_points - 1; i++)
651 klass->draw_line (renderer, &points[i+0], &points[i+1], color);
654 static void
655 draw_polygon (DiaRenderer *renderer,
656 Point *points, int num_points,
657 Color *color)
659 DiaRendererClass *klass = DIA_RENDERER_GET_CLASS (renderer);
660 int i;
662 g_return_if_fail (1 > num_points);
664 for (i = 0; i < num_points - 1; i++)
665 klass->draw_line (renderer, &points[i+0], &points[i+1], color);
666 /* close it in any case */
667 if ( (points[0].x != points[num_points-1].x)
668 || (points[0].y != points[num_points-1].y))
669 klass->draw_line (renderer, &points[num_points-1], &points[0], color);
672 static void
673 draw_rounded_rect (DiaRenderer *renderer,
674 Point *ul_corner, Point *lr_corner,
675 Color *color, real radius)
677 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
678 Point start, end, center;
680 radius = MIN(radius, (lr_corner->x-ul_corner->x)/2);
681 radius = MIN(radius, (lr_corner->y-ul_corner->y)/2);
682 start.x = center.x = ul_corner->x+radius;
683 end.x = lr_corner->x-radius;
684 start.y = end.y = ul_corner->y;
685 renderer_ops->draw_line(renderer, &start, &end, color);
686 start.y = end.y = lr_corner->y;
687 renderer_ops->draw_line(renderer, &start, &end, color);
689 center.y = ul_corner->y+radius;
690 renderer_ops->draw_arc(renderer, &center,
691 2.0*radius, 2.0*radius,
692 90.0, 180.0, color);
693 center.x = end.x;
694 renderer_ops->draw_arc(renderer, &center,
695 2.0*radius, 2.0*radius,
696 0.0, 90.0, color);
698 start.y = ul_corner->y+radius;
699 start.x = end.x = ul_corner->x;
700 end.y = center.y = lr_corner->y-radius;
701 renderer_ops->draw_line(renderer, &start, &end, color);
702 start.x = end.x = lr_corner->x;
703 renderer_ops->draw_line(renderer, &start, &end, color);
705 center.y = lr_corner->y-radius;
706 center.x = ul_corner->x+radius;
707 renderer_ops->draw_arc(renderer, &center,
708 2.0*radius, 2.0*radius,
709 180.0, 270.0, color);
710 center.x = lr_corner->x-radius;
711 renderer_ops->draw_arc(renderer, &center,
712 2.0*radius, 2.0*radius,
713 270.0, 360.0, color);
716 static void
717 fill_rounded_rect(DiaRenderer *renderer,
718 Point *ul_corner, Point *lr_corner,
719 Color *color, real radius)
721 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
722 Point start, end, center;
724 radius = MIN(radius, (lr_corner->x-ul_corner->x)/2);
725 radius = MIN(radius, (lr_corner->y-ul_corner->y)/2);
726 start.x = center.x = ul_corner->x+radius;
727 end.x = lr_corner->x-radius;
728 start.y = ul_corner->y;
729 end.y = lr_corner->y;
730 renderer_ops->fill_rect(renderer, &start, &end, color);
732 center.y = ul_corner->y+radius;
733 renderer_ops->fill_arc(renderer, &center,
734 2.0*radius, 2.0*radius,
735 90.0, 180.0, color);
736 center.x = end.x;
737 renderer_ops->fill_arc(renderer, &center,
738 2.0*radius, 2.0*radius,
739 0.0, 90.0, color);
742 start.x = ul_corner->x;
743 start.y = ul_corner->y+radius;
744 end.x = lr_corner->x;
745 end.y = center.y = lr_corner->y-radius;
746 renderer_ops->fill_rect(renderer, &start, &end, color);
748 center.y = lr_corner->y-radius;
749 center.x = ul_corner->x+radius;
750 renderer_ops->fill_arc(renderer, &center,
751 2.0*radius, 2.0*radius,
752 180.0, 270.0, color);
753 center.x = lr_corner->x-radius;
754 renderer_ops->fill_arc(renderer, &center,
755 2.0*radius, 2.0*radius,
756 270.0, 360.0, color);
759 static void
760 draw_line_with_arrows(DiaRenderer *renderer,
761 Point *startpoint,
762 Point *endpoint,
763 real line_width,
764 Color *color,
765 Arrow *start_arrow,
766 Arrow *end_arrow)
768 Point oldstart = *startpoint;
769 Point oldend = *endpoint;
770 Point start_arrow_head;
771 Point end_arrow_head;
773 /* Calculate how to more the line to account for arrow heads */
774 if (start_arrow != NULL && start_arrow->type != ARROW_NONE) {
775 Point move_arrow, move_line;
776 calculate_arrow_point(start_arrow, startpoint, endpoint,
777 &move_arrow, &move_line,
778 line_width);
779 start_arrow_head = *startpoint;
780 point_sub(&start_arrow_head, &move_arrow);
781 point_sub(startpoint, &move_line);
783 if (end_arrow != NULL && end_arrow->type != ARROW_NONE) {
784 Point move_arrow, move_line;
785 calculate_arrow_point(end_arrow, endpoint, startpoint,
786 &move_arrow, &move_line,
787 line_width);
788 end_arrow_head = *endpoint;
789 point_sub(&end_arrow_head, &move_arrow);
790 point_sub(endpoint, &move_line);
793 DIA_RENDERER_GET_CLASS(renderer)->draw_line(renderer, startpoint, endpoint, color);
795 /* Actual arrow drawing down here so line styles aren't disturbed */
796 if (start_arrow != NULL && start_arrow->type != ARROW_NONE)
797 arrow_draw(renderer, start_arrow->type,
798 &start_arrow_head, endpoint,
799 start_arrow->length, start_arrow->width,
800 line_width,
801 color, &color_white);
802 if (end_arrow != NULL && end_arrow->type != ARROW_NONE)
803 arrow_draw(renderer, end_arrow->type,
804 &end_arrow_head, startpoint,
805 end_arrow->length, end_arrow->width,
806 line_width,
807 color, &color_white);
809 *startpoint = oldstart;
810 *endpoint = oldend;
813 static void
814 draw_polyline_with_arrows(DiaRenderer *renderer,
815 Point *points, int num_points,
816 real line_width,
817 Color *color,
818 Arrow *start_arrow,
819 Arrow *end_arrow)
821 /* Index of first and last point with a non-zero length segment */
822 int firstline = 0;
823 int lastline = num_points;
824 Point oldstart = points[firstline];
825 Point oldend = points[lastline-1];
826 Point start_arrow_head;
827 Point end_arrow_head;
829 if (start_arrow != NULL && start_arrow->type != ARROW_NONE) {
830 Point move_arrow, move_line;
831 while (firstline < num_points-1 &&
832 distance_point_point(&points[firstline],
833 &points[firstline+1]) < 0.0000001)
834 firstline++;
835 if (firstline == num_points-1)
836 firstline = 0; /* No non-zero lines, it doesn't matter. */
837 oldstart = points[firstline];
838 calculate_arrow_point(start_arrow,
839 &points[firstline], &points[firstline+1],
840 &move_arrow, &move_line,
841 line_width);
842 start_arrow_head = points[firstline];
843 point_sub(&start_arrow_head, &move_arrow);
844 point_sub(&points[firstline], &move_line);
846 if (end_arrow != NULL && end_arrow->type != ARROW_NONE) {
847 Point move_arrow, move_line;
848 while (lastline > 0 &&
849 distance_point_point(&points[lastline-1],
850 &points[lastline-2]) < 0.0000001)
851 lastline--;
852 if (lastline == 0)
853 firstline = num_points; /* No non-zero lines, it doesn't matter. */
854 oldend = points[lastline-1];
855 calculate_arrow_point(end_arrow, &points[lastline-1],
856 &points[lastline-2],
857 &move_arrow, &move_line,
858 line_width);
859 end_arrow_head = points[lastline-1];
860 point_sub(&end_arrow_head, &move_arrow);
861 point_sub(&points[lastline-1], &move_line);
863 /* Don't draw degenerate line segments at end of line */
864 DIA_RENDERER_GET_CLASS(renderer)->draw_polyline(renderer, &points[firstline],
865 lastline-firstline, color);
866 if (start_arrow != NULL && start_arrow->type != ARROW_NONE)
867 arrow_draw(renderer, start_arrow->type,
868 &start_arrow_head, &points[firstline+1],
869 start_arrow->length, start_arrow->width,
870 line_width,
871 color, &color_white);
872 if (end_arrow != NULL && end_arrow->type != ARROW_NONE)
873 arrow_draw(renderer, end_arrow->type,
874 &end_arrow_head, &points[lastline-2],
875 end_arrow->length, end_arrow->width,
876 line_width,
877 color, &color_white);
879 points[firstline] = oldstart;
880 points[lastline-1] = oldend;
883 /** Figure the equation for a line given by two points.
884 * Returns FALSE if the line is vertical (infinite a).
886 static gboolean
887 points_to_line(real *a, real *b, Point *p1, Point *p2)
889 if (fabs(p1->x - p2->x) < 0.000000001)
890 return FALSE;
891 *a = (p2->y-p1->y)/(p2->x-p1->x);
892 *b = p1->y-(*a)*p1->x;
893 return TRUE;
896 /** Find the intersection between two lines.
897 * Returns TRUE if the lines intersect in a single point.
899 static gboolean
900 intersection_line_line(Point *cross,
901 Point *p1a, Point *p1b,
902 Point *p2a, Point *p2b)
904 real a1, b1, a2, b2;
906 /* Find coefficients of lines */
907 if (!(points_to_line(&a1, &b1, p1a, p1b))) {
908 if (!(points_to_line(&a2, &b2, p2a, p2b))) {
909 if (fabs(p1a->x-p2a->x) < 0.00000001) {
910 *cross = *p1a;
911 return TRUE;
912 } else return FALSE;
914 cross->x = p1a->x;
915 cross->y = a2*(p1a->x)+b2;
916 return TRUE;
918 if (!(points_to_line(&a2, &b2, p2a, p2b))) {
919 cross->x = p2a->x;
920 cross->y = a1*(p2a->x)+b1;
921 return TRUE;
923 /* Solve */
924 if (fabs(a1-a2) < 0.000000001) {
925 if (fabs(b1-b2) < 0.000000001) {
926 *cross = *p1a;
927 return TRUE;
928 } else {
929 return FALSE;
931 } else {
933 a1x+b1 = a2x+b2;
934 a1x = a2x+b2-b1;
935 a1x-a2x = b2-b1;
936 (a1-a2)x = b2-b1;
937 x = (b2-b1)/(a1-a2)
939 cross->x = (b2-b1)/(a1-a2);
940 cross->y = a1*cross->x+b1;
941 return TRUE;
945 /** Given three points, find the center of the circle they describe.
946 * Returns FALSE if the center could not be determined (i.e. the points
947 * all lie really close together).
948 * The renderer should disappear once the debugging is done.
950 static gboolean
951 find_center_point(Point *center, Point *p1, Point *p2, Point *p3)
953 Point mid1;
954 Point mid2;
955 Point orth1;
956 Point orth2;
957 real tmp;
959 /* Find vector from middle between two points towards center */
960 mid1 = *p1;
961 point_sub(&mid1, p2);
962 point_scale(&mid1, 0.5);
963 orth1 = mid1;
964 point_add(&mid1, p2); /* Now midpoint between p1 & p2 */
965 tmp = orth1.x;
966 orth1.x = orth1.y;
967 orth1.y = -tmp;
968 point_add(&orth1, &mid1);
971 /* Again, with two other points */
972 mid2 = *p2;
973 point_sub(&mid2, p3);
974 point_scale(&mid2, 0.5);
975 orth2 = mid2;
976 point_add(&mid2, p3); /* Now midpoint between p2 & p3 */
977 tmp = orth2.x;
978 orth2.x = orth2.y;
979 orth2.y = -tmp;
980 point_add(&orth2, &mid2);
982 /* The intersection between these two is the center */
983 if (!intersection_line_line(center, &mid1, &orth1, &mid2, &orth2)) {
984 /* Degenerate circle */
985 /* Either the points are really close together, or directly apart */
986 printf("Degenerate circle\n");
987 if (fabs((p1->x + p2->x + p3->x)/3 - p1->x) < 0.0000001 &&
988 fabs((p1->y + p2->y + p3->y)/3 - p1->y) < 0.0000001)
989 return FALSE;
991 return TRUE;
993 return TRUE;
996 static real
997 point_cross(Point *p1, Point *p2)
999 return p1->x * p2->y - p2->x * p1->y;
1002 static void
1003 draw_arc_with_arrows (DiaRenderer *renderer,
1004 Point *startpoint,
1005 Point *endpoint,
1006 Point *midpoint,
1007 real line_width,
1008 Color *color,
1009 Arrow *start_arrow,
1010 Arrow *end_arrow)
1012 Point oldstart = *startpoint;
1013 Point oldend = *endpoint;
1014 Point center;
1015 real width, angle1, angle2;
1016 Point dot1, dot2;
1017 gboolean righthand;
1018 Point start_arrow_head;
1019 Point start_arrow_end;
1020 Point end_arrow_head;
1021 Point end_arrow_end;
1023 if (!find_center_point(&center, startpoint, endpoint, midpoint)) {
1024 /* Degenerate circle -- should have been caught by the drawer? */
1028 dot1 = *startpoint;
1029 point_sub(&dot1, endpoint);
1030 point_normalize(&dot1);
1031 dot2 = *midpoint;
1032 point_sub(&dot2, endpoint);
1033 point_normalize(&dot2);
1034 righthand = point_cross(&dot1, &dot2) > 0;
1036 if (start_arrow != NULL && start_arrow->type != ARROW_NONE) {
1037 Point move_arrow, move_line;
1038 real tmp;
1040 start_arrow_end = *startpoint;
1041 point_sub(&start_arrow_end, &center);
1042 tmp = start_arrow_end.x;
1043 if (righthand) {
1044 start_arrow_end.x = -start_arrow_end.y;
1045 start_arrow_end.y = tmp;
1046 } else {
1047 start_arrow_end.x = start_arrow_end.y;
1048 start_arrow_end.y = -tmp;
1050 point_add(&start_arrow_end, startpoint);
1052 calculate_arrow_point(start_arrow, startpoint, &start_arrow_end,
1053 &move_arrow, &move_line,
1054 line_width);
1055 start_arrow_head = *startpoint;
1056 point_sub(&start_arrow_head, &move_arrow);
1057 point_sub(startpoint, &move_line);
1059 if (end_arrow != NULL && end_arrow->type != ARROW_NONE) {
1060 Point move_arrow, move_line;
1061 real tmp;
1063 end_arrow_end = *endpoint;
1064 point_sub(&end_arrow_end, &center);
1065 tmp = end_arrow_end.x;
1066 if (righthand) {
1067 end_arrow_end.x = end_arrow_end.y;
1068 end_arrow_end.y = -tmp;
1069 } else {
1070 end_arrow_end.x = -end_arrow_end.y;
1071 end_arrow_end.y = tmp;
1073 point_add(&end_arrow_end, endpoint);
1075 calculate_arrow_point(end_arrow, endpoint, &end_arrow_end,
1076 &move_arrow, &move_line,
1077 line_width);
1078 end_arrow_head = *endpoint;
1079 point_sub(&end_arrow_head, &move_arrow);
1080 point_sub(endpoint, &move_line);
1083 if (!find_center_point(&center, startpoint, endpoint, midpoint)) {
1084 /* Not sure what to do here */
1085 *startpoint = oldstart;
1086 *endpoint = oldend;
1087 printf("Second degenerate circle\n");
1088 return;
1090 width = 2*distance_point_point(&center, startpoint);
1091 angle1 = -atan2(startpoint->y-center.y, startpoint->x-center.x)*180.0/M_PI;
1092 while (angle1 < 0.0) angle1 += 360.0;
1093 angle2 = -atan2(endpoint->y-center.y, endpoint->x-center.x)*180.0/M_PI;
1094 while (angle2 < 0.0) angle2 += 360.0;
1095 if (righthand) {
1096 real tmp = angle1;
1097 angle1 = angle2;
1098 angle2 = tmp;
1100 DIA_RENDERER_GET_CLASS(renderer)->draw_arc(renderer, &center, width, width,
1101 angle1, angle2, color);
1102 if (start_arrow != NULL && start_arrow->type != ARROW_NONE)
1103 arrow_draw(renderer, start_arrow->type,
1104 &start_arrow_head, &start_arrow_end,
1105 start_arrow->length, start_arrow->width,
1106 line_width,
1107 color, &color_white);
1108 if (end_arrow != NULL && end_arrow->type != ARROW_NONE)
1109 arrow_draw(renderer, end_arrow->type,
1110 &end_arrow_head, &end_arrow_end,
1111 end_arrow->length, end_arrow->width,
1112 line_width,
1113 color, &color_white);
1115 *startpoint = oldstart;
1116 *endpoint = oldend;
1119 static void
1120 draw_bezier_with_arrows(DiaRenderer *renderer,
1121 BezPoint *points,
1122 int num_points,
1123 real line_width,
1124 Color *color,
1125 Arrow *start_arrow,
1126 Arrow *end_arrow)
1128 Point startpoint, endpoint;
1129 Point start_arrow_head;
1130 Point end_arrow_head;
1132 startpoint = points[0].p1;
1133 endpoint = points[num_points-1].p3;
1135 if (start_arrow != NULL && start_arrow->type != ARROW_NONE) {
1136 Point move_arrow;
1137 Point move_line;
1138 calculate_arrow_point(start_arrow, &points[0].p1, &points[1].p1,
1139 &move_arrow, &move_line,
1140 line_width);
1141 start_arrow_head = points[0].p1;
1142 point_sub(&start_arrow_head, &move_arrow);
1143 point_sub(&points[0].p1, &move_line);
1145 if (end_arrow != NULL && end_arrow->type != ARROW_NONE) {
1146 Point move_arrow;
1147 Point move_line;
1148 calculate_arrow_point(end_arrow,
1149 &points[num_points-1].p3, &points[num_points-1].p2,
1150 &move_arrow, &move_line,
1151 line_width);
1152 end_arrow_head = points[num_points-1].p3;
1153 point_sub(&end_arrow_head, &move_arrow);
1154 point_sub(&points[num_points-1].p3, &move_line);
1156 DIA_RENDERER_GET_CLASS(renderer)->draw_bezier(renderer, points, num_points, color);
1157 if (start_arrow != NULL && start_arrow->type != ARROW_NONE)
1158 arrow_draw(renderer, start_arrow->type,
1159 &start_arrow_head, &points[1].p1,
1160 start_arrow->length, start_arrow->width,
1161 line_width,
1162 color, &color_white);
1163 if (end_arrow != NULL && end_arrow->type != ARROW_NONE)
1164 arrow_draw(renderer, end_arrow->type,
1165 &end_arrow_head, &points[num_points-1].p2,
1166 end_arrow->length, end_arrow->width,
1167 line_width,
1168 color, &color_white);
1170 points[0].p1 = startpoint;
1171 points[num_points-1].p3 = endpoint;
1176 * Should we really provide this ? It formerly was an 'interactive op'
1177 * and depends on DiaRenderer::set_font() not or properly overwritten
1179 static real
1180 get_text_width (DiaRenderer *renderer,
1181 const gchar *text, int length)
1183 real ret = 0;
1185 if (renderer->font) {
1186 char *str = g_strndup (text, length);
1188 ret = dia_font_string_width (str,
1189 renderer->font,
1190 renderer->font_height);
1191 g_free (str);
1194 return ret;
1197 static int
1198 get_width_pixels (DiaRenderer *renderer)
1200 g_return_val_if_fail (renderer->is_interactive, 0);
1201 return 0;
1204 static int
1205 get_height_pixels (DiaRenderer *renderer)
1207 g_return_val_if_fail (renderer->is_interactive, 0);
1208 return 0;
1213 * non member functions
1216 dia_renderer_get_width_pixels (DiaRenderer *renderer)
1218 return DIA_RENDERER_GET_CLASS(renderer)->get_width_pixels (renderer);
1222 dia_renderer_get_height_pixels (DiaRenderer *renderer)
1224 return DIA_RENDERER_GET_CLASS(renderer)->get_height_pixels (renderer);