2 /* Copyright (C) 1989, 1990, 1991, 1992, 2001, 2002, 2003, 2004, 2005
3 Free Software Foundation, Inc.
4 Written by James Clark (jjc@jclark.com)
6 This file is part of groff.
8 groff is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 2, or (at your option) any later
13 groff is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 You should have received a copy of the GNU General Public License along
19 with groff; see the file COPYING. If not, write to the Free Software
20 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
26 void print_object_list(object
*);
28 line_type::line_type()
29 : type(solid
), thickness(1.0)
33 output::output() : args(0), desired_height(0.0), desired_width(0.0)
42 void output::set_desired_width_height(double wid
, double ht
)
48 void output::set_args(const char *s
)
51 if (s
== 0 || *s
== '\0')
57 int output::supports_filled_polygons()
62 void output::begin_block(const position
&, const position
&)
66 void output::end_block()
70 double output::compute_scale(double sc
, const position
&ll
, const position
&ur
)
72 distance dim
= ur
- ll
;
73 if (desired_width
!= 0.0 || desired_height
!= 0.0) {
75 if (desired_width
!= 0.0) {
77 error("width specified for picture with zero width");
79 sc
= dim
.x
/desired_width
;
81 if (desired_height
!= 0.0) {
83 error("height specified for picture with zero height");
85 double tem
= dim
.y
/desired_height
;
90 return sc
== 0.0 ? 1.0 : sc
;
95 distance sdim
= dim
/sc
;
96 double max_width
= 0.0;
97 lookup_variable("maxpswid", &max_width
);
98 double max_height
= 0.0;
99 lookup_variable("maxpsht", &max_height
);
100 if ((max_width
> 0.0 && sdim
.x
> max_width
)
101 || (max_height
> 0.0 && sdim
.y
> max_height
)) {
102 double xscale
= dim
.x
/max_width
;
103 double yscale
= dim
.y
/max_height
;
104 return xscale
> yscale
? xscale
: yscale
;
111 position::position(const place
&pl
)
114 // Use two statements to work around bug in SGI C++.
115 object
*tem
= pl
.obj
;
116 *this = tem
->origin();
124 position::position() : x(0.0), y(0.0)
128 position::position(double a
, double b
) : x(a
), y(b
)
133 int operator==(const position
&a
, const position
&b
)
135 return a
.x
== b
.x
&& a
.y
== b
.y
;
138 int operator!=(const position
&a
, const position
&b
)
140 return a
.x
!= b
.x
|| a
.y
!= b
.y
;
143 position
&position::operator+=(const position
&a
)
150 position
&position::operator-=(const position
&a
)
157 position
&position::operator*=(double a
)
164 position
&position::operator/=(double a
)
171 position
operator-(const position
&a
)
173 return position(-a
.x
, -a
.y
);
176 position
operator+(const position
&a
, const position
&b
)
178 return position(a
.x
+ b
.x
, a
.y
+ b
.y
);
181 position
operator-(const position
&a
, const position
&b
)
183 return position(a
.x
- b
.x
, a
.y
- b
.y
);
186 position
operator/(const position
&a
, double n
)
188 return position(a
.x
/n
, a
.y
/n
);
191 position
operator*(const position
&a
, double n
)
193 return position(a
.x
*n
, a
.y
*n
);
198 double operator*(const position
&a
, const position
&b
)
200 return a
.x
*b
.x
+ a
.y
*b
.y
;
203 double hypot(const position
&a
)
205 return groff_hypot(a
.x
, a
.y
);
208 struct arrow_head_type
{
214 void draw_arrow(const position
&pos
, const distance
&dir
,
215 const arrow_head_type
&aht
, const line_type
<
,
216 char *outline_color_for_fill
)
218 double hyp
= hypot(dir
);
220 error("cannot draw arrow on object with zero length");
223 position base
= -dir
;
224 base
*= aht
.height
/hyp
;
225 position
n(dir
.y
, -dir
.x
);
226 n
*= aht
.width
/(hyp
*2.0);
228 slt
.type
= line_type::solid
;
229 if (aht
.solid
&& out
->supports_filled_polygons()) {
232 v
[1] = pos
+ base
+ n
;
233 v
[2] = pos
+ base
- n
;
234 // fill with outline color
235 out
->set_color(outline_color_for_fill
, outline_color_for_fill
);
236 // make stroke thin to avoid arrow sticking
238 out
->polygon(v
, 3, slt
, 1);
241 // use two line segments to avoid arrow sticking
242 out
->line(pos
+ base
- n
, &pos
, 1, slt
);
243 out
->line(pos
+ base
+ n
, &pos
, 1, slt
);
247 object::object() : prev(0), next(0)
255 void object::move_by(const position
&)
263 void object::print_text()
272 struct bounding_box
{
278 void encompass(const position
&);
281 bounding_box::bounding_box()
286 void bounding_box::encompass(const position
&pos
)
305 void object::update_bounding_box(bounding_box
*)
309 position
object::origin()
311 return position(0.0,0.0);
314 position
object::north()
319 position
object::south()
324 position
object::east()
329 position
object::west()
334 position
object::north_east()
339 position
object::north_west()
344 position
object::south_east()
349 position
object::south_west()
354 position
object::start()
359 position
object::end()
364 position
object::center()
369 double object::width()
374 double object::radius()
379 double object::height()
384 place
*object::find_label(const char *)
389 segment::segment(const position
&a
, int n
, segment
*p
)
390 : is_absolute(n
), pos(a
), next(p
)
394 text_item::text_item(char *t
, const char *fn
, int ln
)
395 : next(0), text(t
), filename(fn
), lineno(ln
)
397 adj
.h
= CENTER_ADJUST
;
401 text_item::~text_item()
406 object_spec::object_spec(object_type t
) : type(t
)
411 segment_width
= segment_height
= 0.0;
412 segment_is_absolute
= 0;
417 dir
= RIGHT_DIRECTION
;
420 object_spec::~object_spec()
423 while (segment_list
!= 0) {
424 segment
*tem
= segment_list
;
425 segment_list
= segment_list
->next
;
428 object
*p
= oblist
.head
;
435 text_item
*tem
= text
;
444 class command_object
: public object
{
446 const char *filename
;
449 command_object(char *, const char *, int);
451 object_type
type() { return OTHER_OBJECT
; }
455 command_object::command_object(char *p
, const char *fn
, int ln
)
456 : s(p
), filename(fn
), lineno(ln
)
460 command_object::~command_object()
465 void command_object::print()
467 out
->command(s
, filename
, lineno
);
470 object
*make_command_object(char *s
, const char *fn
, int ln
)
472 return new command_object(s
, fn
, ln
);
475 class mark_object
: public object
{
481 object
*make_mark_object()
483 return new mark_object();
486 mark_object::mark_object()
490 object_type
mark_object::type()
495 object_list::object_list() : head(0), tail(0)
499 void object_list::append(object
*obj
)
502 obj
->next
= obj
->prev
= 0;
513 void object_list::wrap_up_block(object_list
*ol
)
516 for (p
= tail
; p
&& p
->type() != MARK_OBJECT
; p
= p
->prev
)
534 text_piece::text_piece()
535 : text(0), filename(0), lineno(-1)
537 adj
.h
= CENTER_ADJUST
;
541 text_piece::~text_piece()
546 class graphic_object
: public object
{
557 object_type
type() = 0;
559 void add_text(text_item
*, int);
560 void set_dotted(double);
561 void set_dashed(double);
562 void set_thickness(double);
563 void set_invisible();
564 void set_outline_color(char *);
565 char *get_outline_color();
566 virtual void set_fill(double);
567 virtual void set_fill_color(char *);
570 graphic_object::graphic_object()
571 : ntext(0), text(0), aligned(0), outline_color(0), color_fill(0)
575 void graphic_object::set_dotted(double wid
)
577 lt
.type
= line_type::dotted
;
581 void graphic_object::set_dashed(double wid
)
583 lt
.type
= line_type::dashed
;
587 void graphic_object::set_thickness(double th
)
592 void graphic_object::set_fill(double)
596 void graphic_object::set_fill_color(char *c
)
598 color_fill
= strsave(c
);
601 void graphic_object::set_outline_color(char *c
)
603 outline_color
= strsave(c
);
606 char *graphic_object::get_outline_color()
608 return outline_color
;
611 void graphic_object::set_invisible()
613 lt
.type
= line_type::invisible
;
616 void graphic_object::add_text(text_item
*t
, int a
)
621 for (p
= t
; p
; p
= p
->next
)
626 text
= new text_piece
[len
];
627 for (p
= t
, len
= 0; p
; p
= p
->next
, len
++) {
628 text
[len
].text
= p
->text
;
630 text
[len
].adj
= p
->adj
;
631 text
[len
].filename
= p
->filename
;
632 text
[len
].lineno
= p
->lineno
;
638 void graphic_object::print_text()
642 position
d(end() - start());
643 if (d
.x
!= 0.0 || d
.y
!= 0.0)
644 angle
= atan2(d
.y
, d
.x
);
647 out
->set_color(color_fill
, get_outline_color());
648 out
->text(center(), text
, ntext
, angle
);
653 graphic_object::~graphic_object()
656 ad_delete(ntext
) text
;
659 class rectangle_object
: public graphic_object
{
664 rectangle_object(const position
&);
665 double width() { return dim
.x
; }
666 double height() { return dim
.y
; }
667 position
origin() { return cent
; }
668 position
center() { return cent
; }
669 position
north() { return position(cent
.x
, cent
.y
+ dim
.y
/2.0); }
670 position
south() { return position(cent
.x
, cent
.y
- dim
.y
/2.0); }
671 position
east() { return position(cent
.x
+ dim
.x
/2.0, cent
.y
); }
672 position
west() { return position(cent
.x
- dim
.x
/2.0, cent
.y
); }
673 position
north_east() { return position(cent
.x
+ dim
.x
/2.0, cent
.y
+ dim
.y
/2.0); }
674 position
north_west() { return position(cent
.x
- dim
.x
/2.0, cent
.y
+ dim
.y
/2.0); }
675 position
south_east() { return position(cent
.x
+ dim
.x
/2.0, cent
.y
- dim
.y
/2.0); }
676 position
south_west() { return position(cent
.x
- dim
.x
/2.0, cent
.y
- dim
.y
/2.0); }
677 object_type
type() = 0;
678 void update_bounding_box(bounding_box
*);
679 void move_by(const position
&);
682 rectangle_object::rectangle_object(const position
&d
)
687 void rectangle_object::update_bounding_box(bounding_box
*p
)
689 p
->encompass(cent
- dim
/2.0);
690 p
->encompass(cent
+ dim
/2.0);
693 void rectangle_object::move_by(const position
&a
)
698 class closed_object
: public rectangle_object
{
700 closed_object(const position
&);
701 object_type
type() = 0;
702 void set_fill(double);
703 void set_fill_color(char *fill
);
705 double fill
; // < 0 if not filled
706 char *color_fill
; // = 0 if not colored
709 closed_object::closed_object(const position
&pos
)
710 : rectangle_object(pos
), fill(-1.0), color_fill(0)
714 void closed_object::set_fill(double f
)
720 void closed_object::set_fill_color(char *f
)
722 color_fill
= strsave(f
);
725 class box_object
: public closed_object
{
729 box_object(const position
&, double);
730 object_type
type() { return BOX_OBJECT
; }
732 position
north_east();
733 position
north_west();
734 position
south_east();
735 position
south_west();
738 box_object::box_object(const position
&pos
, double r
)
739 : closed_object(pos
), xrad(dim
.x
> 0 ? r
: -r
), yrad(dim
.y
> 0 ? r
: -r
)
743 const double CHOP_FACTOR
= 1.0 - 1.0/M_SQRT2
;
745 position
box_object::north_east()
747 return position(cent
.x
+ dim
.x
/2.0 - CHOP_FACTOR
*xrad
,
748 cent
.y
+ dim
.y
/2.0 - CHOP_FACTOR
*yrad
);
751 position
box_object::north_west()
753 return position(cent
.x
- dim
.x
/2.0 + CHOP_FACTOR
*xrad
,
754 cent
.y
+ dim
.y
/2.0 - CHOP_FACTOR
*yrad
);
757 position
box_object::south_east()
759 return position(cent
.x
+ dim
.x
/2.0 - CHOP_FACTOR
*xrad
,
760 cent
.y
- dim
.y
/2.0 + CHOP_FACTOR
*yrad
);
763 position
box_object::south_west()
765 return position(cent
.x
- dim
.x
/2.0 + CHOP_FACTOR
*xrad
,
766 cent
.y
- dim
.y
/2.0 + CHOP_FACTOR
*yrad
);
769 void box_object::print()
771 if (lt
.type
== line_type::invisible
&& fill
< 0.0 && color_fill
== 0)
773 out
->set_color(color_fill
, graphic_object::get_outline_color());
775 distance dim2
= dim
/2.0;
777 vec
[0] = cent
+ position(dim2
.x
, -dim2
.y
);
778 vec
[1] = cent
+ position(dim2
.x
, dim2
.y
);
779 vec
[2] = cent
+ position(-dim2
.x
, dim2
.y
);
780 vec
[3] = cent
+ position(-dim2
.x
, -dim2
.y
);
781 out
->polygon(vec
, 4, lt
, fill
);
784 distance
abs_dim(fabs(dim
.x
), fabs(dim
.y
));
785 out
->rounded_box(cent
, abs_dim
, fabs(xrad
), lt
, fill
);
790 graphic_object
*object_spec::make_box(position
*curpos
, direction
*dirp
)
792 static double last_box_height
;
793 static double last_box_width
;
794 static double last_box_radius
;
795 static int have_last_box
= 0;
796 if (!(flags
& HAS_HEIGHT
)) {
797 if ((flags
& IS_SAME
) && have_last_box
)
798 height
= last_box_height
;
800 lookup_variable("boxht", &height
);
802 if (!(flags
& HAS_WIDTH
)) {
803 if ((flags
& IS_SAME
) && have_last_box
)
804 width
= last_box_width
;
806 lookup_variable("boxwid", &width
);
808 if (!(flags
& HAS_RADIUS
)) {
809 if ((flags
& IS_SAME
) && have_last_box
)
810 radius
= last_box_radius
;
812 lookup_variable("boxrad", &radius
);
814 last_box_width
= width
;
815 last_box_height
= height
;
816 last_box_radius
= radius
;
818 radius
= fabs(radius
);
819 if (radius
*2.0 > fabs(width
))
820 radius
= fabs(width
/2.0);
821 if (radius
*2.0 > fabs(height
))
822 radius
= fabs(height
/2.0);
823 box_object
*p
= new box_object(position(width
, height
), radius
);
824 if (!position_rectangle(p
, curpos
, dirp
)) {
831 // return non-zero for success
833 int object_spec::position_rectangle(rectangle_object
*p
,
834 position
*curpos
, direction
*dirp
)
837 dir
= *dirp
; // ignore any direction in attribute list
841 motion
.y
= p
->height()/2.0;
844 motion
.y
= -p
->height()/2.0;
847 motion
.x
= -p
->width()/2.0;
849 case RIGHT_DIRECTION
:
850 motion
.x
= p
->width()/2.0;
855 if (flags
& HAS_AT
) {
857 if (flags
& HAS_WITH
) {
861 if (!with
->follow(here
, &offset
))
876 class block_object
: public rectangle_object
{
880 block_object(const position
&, const object_list
&ol
, PTABLE(place
) *t
);
882 place
*find_label(const char *);
884 void move_by(const position
&);
888 block_object::block_object(const position
&d
, const object_list
&ol
,
890 : rectangle_object(d
), oblist(ol
), tbl(t
)
894 block_object::~block_object()
897 object
*p
= oblist
.head
;
905 void block_object::print()
907 out
->begin_block(south_west(), north_east());
908 print_object_list(oblist
.head
);
912 static void adjust_objectless_places(PTABLE(place
) *tbl
, const position
&a
)
914 // Adjust all the labels that aren't attached to objects.
915 PTABLE_ITERATOR(place
) iter(tbl
);
918 while (iter
.next(&key
, &pl
))
919 if (key
&& csupper(key
[0]) && pl
->obj
== 0) {
925 void block_object::move_by(const position
&a
)
928 for (object
*p
= oblist
.head
; p
; p
= p
->next
)
930 adjust_objectless_places(tbl
, a
);
934 place
*block_object::find_label(const char *name
)
936 return tbl
->lookup(name
);
939 object_type
block_object::type()
944 graphic_object
*object_spec::make_block(position
*curpos
, direction
*dirp
)
947 for (object
*p
= oblist
.head
; p
; p
= p
->next
)
948 p
->update_bounding_box(&bb
);
951 position m
= -(bb
.ll
+ bb
.ur
)/2.0;
952 for (object
*p
= oblist
.head
; p
; p
= p
->next
)
954 adjust_objectless_places(tbl
, m
);
957 if (flags
& HAS_WIDTH
)
959 if (flags
& HAS_HEIGHT
)
961 block_object
*block
= new block_object(dim
, oblist
, tbl
);
962 if (!position_rectangle(block
, curpos
, dirp
)) {
967 oblist
.head
= oblist
.tail
= 0;
971 class text_object
: public rectangle_object
{
973 text_object(const position
&);
974 object_type
type() { return TEXT_OBJECT
; }
977 text_object::text_object(const position
&d
)
978 : rectangle_object(d
)
982 graphic_object
*object_spec::make_text(position
*curpos
, direction
*dirp
)
984 if (!(flags
& HAS_HEIGHT
)) {
985 lookup_variable("textht", &height
);
987 for (text_item
*t
= text
; t
; t
= t
->next
)
991 if (!(flags
& HAS_WIDTH
))
992 lookup_variable("textwid", &width
);
993 text_object
*p
= new text_object(position(width
, height
));
994 if (!position_rectangle(p
, curpos
, dirp
)) {
1002 class ellipse_object
: public closed_object
{
1004 ellipse_object(const position
&);
1005 position
north_east() { return position(cent
.x
+ dim
.x
/(M_SQRT2
*2.0),
1006 cent
.y
+ dim
.y
/(M_SQRT2
*2.0)); }
1007 position
north_west() { return position(cent
.x
- dim
.x
/(M_SQRT2
*2.0),
1008 cent
.y
+ dim
.y
/(M_SQRT2
*2.0)); }
1009 position
south_east() { return position(cent
.x
+ dim
.x
/(M_SQRT2
*2.0),
1010 cent
.y
- dim
.y
/(M_SQRT2
*2.0)); }
1011 position
south_west() { return position(cent
.x
- dim
.x
/(M_SQRT2
*2.0),
1012 cent
.y
- dim
.y
/(M_SQRT2
*2.0)); }
1013 double radius() { return dim
.x
/2.0; }
1014 object_type
type() { return ELLIPSE_OBJECT
; }
1018 ellipse_object::ellipse_object(const position
&d
)
1023 void ellipse_object::print()
1025 if (lt
.type
== line_type::invisible
&& fill
< 0.0 && color_fill
== 0)
1027 out
->set_color(color_fill
, graphic_object::get_outline_color());
1028 out
->ellipse(cent
, dim
, lt
, fill
);
1032 graphic_object
*object_spec::make_ellipse(position
*curpos
, direction
*dirp
)
1034 static double last_ellipse_height
;
1035 static double last_ellipse_width
;
1036 static int have_last_ellipse
= 0;
1037 if (!(flags
& HAS_HEIGHT
)) {
1038 if ((flags
& IS_SAME
) && have_last_ellipse
)
1039 height
= last_ellipse_height
;
1041 lookup_variable("ellipseht", &height
);
1043 if (!(flags
& HAS_WIDTH
)) {
1044 if ((flags
& IS_SAME
) && have_last_ellipse
)
1045 width
= last_ellipse_width
;
1047 lookup_variable("ellipsewid", &width
);
1049 last_ellipse_width
= width
;
1050 last_ellipse_height
= height
;
1051 have_last_ellipse
= 1;
1052 ellipse_object
*p
= new ellipse_object(position(width
, height
));
1053 if (!position_rectangle(p
, curpos
, dirp
)) {
1060 class circle_object
: public ellipse_object
{
1062 circle_object(double);
1063 object_type
type() { return CIRCLE_OBJECT
; }
1067 circle_object::circle_object(double diam
)
1068 : ellipse_object(position(diam
, diam
))
1072 void circle_object::print()
1074 if (lt
.type
== line_type::invisible
&& fill
< 0.0 && color_fill
== 0)
1076 out
->set_color(color_fill
, graphic_object::get_outline_color());
1077 out
->circle(cent
, dim
.x
/2.0, lt
, fill
);
1081 graphic_object
*object_spec::make_circle(position
*curpos
, direction
*dirp
)
1083 static double last_circle_radius
;
1084 static int have_last_circle
= 0;
1085 if (!(flags
& HAS_RADIUS
)) {
1086 if ((flags
& IS_SAME
) && have_last_circle
)
1087 radius
= last_circle_radius
;
1089 lookup_variable("circlerad", &radius
);
1091 last_circle_radius
= radius
;
1092 have_last_circle
= 1;
1093 circle_object
*p
= new circle_object(radius
*2.0);
1094 if (!position_rectangle(p
, curpos
, dirp
)) {
1101 class move_object
: public graphic_object
{
1105 move_object(const position
&s
, const position
&e
);
1106 position
origin() { return en
; }
1107 object_type
type() { return MOVE_OBJECT
; }
1108 void update_bounding_box(bounding_box
*);
1109 void move_by(const position
&);
1112 move_object::move_object(const position
&s
, const position
&e
)
1117 void move_object::update_bounding_box(bounding_box
*p
)
1123 void move_object::move_by(const position
&a
)
1129 graphic_object
*object_spec::make_move(position
*curpos
, direction
*dirp
)
1131 static position last_move
;
1132 static int have_last_move
= 0;
1134 // No need to look at at since `at' attribute sets `from' attribute.
1135 position startpos
= (flags
& HAS_FROM
) ? from
: *curpos
;
1136 if (!(flags
& HAS_SEGMENT
)) {
1137 if ((flags
& IS_SAME
) && have_last_move
)
1138 segment_pos
= last_move
;
1142 segment_pos
.y
= segment_height
;
1144 case DOWN_DIRECTION
:
1145 segment_pos
.y
= -segment_height
;
1147 case LEFT_DIRECTION
:
1148 segment_pos
.x
= -segment_width
;
1150 case RIGHT_DIRECTION
:
1151 segment_pos
.x
= segment_width
;
1158 segment_list
= new segment(segment_pos
, segment_is_absolute
, segment_list
);
1159 // Reverse the segment_list so that it's in forward order.
1160 segment
*old
= segment_list
;
1163 segment
*tem
= old
->next
;
1164 old
->next
= segment_list
;
1168 // Compute the end position.
1169 position endpos
= startpos
;
1170 for (segment
*s
= segment_list
; s
; s
= s
->next
)
1176 last_move
= endpos
- startpos
;
1177 move_object
*p
= new move_object(startpos
, endpos
);
1182 class linear_object
: public graphic_object
{
1184 char arrow_at_start
;
1186 arrow_head_type aht
;
1190 linear_object(const position
&s
, const position
&e
);
1191 position
start() { return strt
; }
1192 position
end() { return en
; }
1193 void move_by(const position
&);
1194 void update_bounding_box(bounding_box
*) = 0;
1195 object_type
type() = 0;
1196 void add_arrows(int at_start
, int at_end
, const arrow_head_type
&);
1199 class line_object
: public linear_object
{
1204 line_object(const position
&s
, const position
&e
, position
*, int);
1206 position
origin() { return strt
; }
1207 position
center() { return (strt
+ en
)/2.0; }
1208 position
north() { return (en
.y
- strt
.y
) > 0 ? en
: strt
; }
1209 position
south() { return (en
.y
- strt
.y
) < 0 ? en
: strt
; }
1210 position
east() { return (en
.x
- strt
.x
) > 0 ? en
: strt
; }
1211 position
west() { return (en
.x
- strt
.x
) < 0 ? en
: strt
; }
1212 object_type
type() { return LINE_OBJECT
; }
1213 void update_bounding_box(bounding_box
*);
1215 void move_by(const position
&);
1218 class arrow_object
: public line_object
{
1220 arrow_object(const position
&, const position
&, position
*, int);
1221 object_type
type() { return ARROW_OBJECT
; }
1224 class spline_object
: public line_object
{
1226 spline_object(const position
&, const position
&, position
*, int);
1227 object_type
type() { return SPLINE_OBJECT
; }
1229 void update_bounding_box(bounding_box
*);
1232 linear_object::linear_object(const position
&s
, const position
&e
)
1233 : arrow_at_start(0), arrow_at_end(0), strt(s
), en(e
)
1237 void linear_object::move_by(const position
&a
)
1243 void linear_object::add_arrows(int at_start
, int at_end
,
1244 const arrow_head_type
&a
)
1246 arrow_at_start
= at_start
;
1247 arrow_at_end
= at_end
;
1251 line_object::line_object(const position
&s
, const position
&e
,
1253 : linear_object(s
, e
), v(p
), n(i
)
1257 void line_object::print()
1259 if (lt
.type
== line_type::invisible
)
1261 out
->set_color(0, graphic_object::get_outline_color());
1262 // shorten line length to avoid arrow sticking.
1264 if (arrow_at_start
) {
1265 position base
= v
[0] - strt
;
1266 double hyp
= hypot(base
);
1268 error("cannot draw arrow on object with zero length");
1271 if (aht
.solid
&& out
->supports_filled_polygons()) {
1272 base
*= aht
.height
/ hyp
;
1273 draw_arrow(strt
, strt
- v
[0], aht
, lt
,
1274 graphic_object::get_outline_color());
1277 base
*= fabs(lt
.thickness
) / hyp
/ 72 / 4;
1279 draw_arrow(sp
, sp
- v
[0], aht
, lt
,
1280 graphic_object::get_outline_color());
1284 position base
= v
[n
-1] - (n
> 1 ? v
[n
-2] : strt
);
1285 double hyp
= hypot(base
);
1287 error("cannot draw arrow on object with zero length");
1290 if (aht
.solid
&& out
->supports_filled_polygons()) {
1291 base
*= aht
.height
/ hyp
;
1292 draw_arrow(en
, v
[n
-1] - (n
> 1 ? v
[n
-2] : strt
), aht
, lt
,
1293 graphic_object::get_outline_color());
1296 base
*= fabs(lt
.thickness
) / hyp
/ 72 / 4;
1298 draw_arrow(v
[n
-1], v
[n
-1] - (n
> 1 ? v
[n
-2] : strt
), aht
, lt
,
1299 graphic_object::get_outline_color());
1302 out
->line(sp
, v
, n
, lt
);
1306 void line_object::update_bounding_box(bounding_box
*p
)
1309 for (int i
= 0; i
< n
; i
++)
1313 void line_object::move_by(const position
&pos
)
1315 linear_object::move_by(pos
);
1316 for (int i
= 0; i
< n
; i
++)
1320 void spline_object::update_bounding_box(bounding_box
*p
)
1332 [ the points for the Bezier cubic ]
1340 (1-t)^3*p1 + 3*t*(t - 1)^2*p2 + 3*t^2*(1-t)*p3 + t^3*p4
1341 [ the equation for the Bezier cubic ]
1343 = .125*q1 + .75*q2 + .125*q3
1346 for (int i
= 1; i
< n
; i
++)
1347 p
->encompass((i
== 1 ? strt
: v
[i
-2])*.125 + v
[i
-1]*.75 + v
[i
]*.125);
1350 arrow_object::arrow_object(const position
&s
, const position
&e
,
1352 : line_object(s
, e
, p
, i
)
1356 spline_object::spline_object(const position
&s
, const position
&e
,
1358 : line_object(s
, e
, p
, i
)
1362 void spline_object::print()
1364 if (lt
.type
== line_type::invisible
)
1366 out
->set_color(0, graphic_object::get_outline_color());
1367 // shorten line length for spline to avoid arrow sticking
1369 if (arrow_at_start
) {
1370 position base
= v
[0] - strt
;
1371 double hyp
= hypot(base
);
1373 error("cannot draw arrow on object with zero length");
1376 if (aht
.solid
&& out
->supports_filled_polygons()) {
1377 base
*= aht
.height
/ hyp
;
1378 draw_arrow(strt
, strt
- v
[0], aht
, lt
,
1379 graphic_object::get_outline_color());
1380 sp
= strt
+ base
*0.1; // to reserve spline shape
1382 base
*= fabs(lt
.thickness
) / hyp
/ 72 / 4;
1384 draw_arrow(sp
, sp
- v
[0], aht
, lt
,
1385 graphic_object::get_outline_color());
1389 position base
= v
[n
-1] - (n
> 1 ? v
[n
-2] : strt
);
1390 double hyp
= hypot(base
);
1392 error("cannot draw arrow on object with zero length");
1395 if (aht
.solid
&& out
->supports_filled_polygons()) {
1396 base
*= aht
.height
/ hyp
;
1397 draw_arrow(en
, v
[n
-1] - (n
> 1 ? v
[n
-2] : strt
), aht
, lt
,
1398 graphic_object::get_outline_color());
1399 v
[n
-1] = en
- base
*0.1; // to reserve spline shape
1401 base
*= fabs(lt
.thickness
) / hyp
/ 72 / 4;
1403 draw_arrow(v
[n
-1], v
[n
-1] - (n
> 1 ? v
[n
-2] : strt
), aht
, lt
,
1404 graphic_object::get_outline_color());
1407 out
->spline(sp
, v
, n
, lt
);
1411 line_object::~line_object()
1416 linear_object
*object_spec::make_line(position
*curpos
, direction
*dirp
)
1418 static position last_line
;
1419 static int have_last_line
= 0;
1421 // No need to look at at since `at' attribute sets `from' attribute.
1422 position startpos
= (flags
& HAS_FROM
) ? from
: *curpos
;
1423 if (!(flags
& HAS_SEGMENT
)) {
1424 if ((flags
& IS_SAME
) && (type
== LINE_OBJECT
|| type
== ARROW_OBJECT
)
1426 segment_pos
= last_line
;
1430 segment_pos
.y
= segment_height
;
1432 case DOWN_DIRECTION
:
1433 segment_pos
.y
= -segment_height
;
1435 case LEFT_DIRECTION
:
1436 segment_pos
.x
= -segment_width
;
1438 case RIGHT_DIRECTION
:
1439 segment_pos
.x
= segment_width
;
1445 segment_list
= new segment(segment_pos
, segment_is_absolute
, segment_list
);
1446 // reverse the segment_list so that it's in forward order
1447 segment
*old
= segment_list
;
1450 segment
*tem
= old
->next
;
1451 old
->next
= segment_list
;
1455 // Absolutise all movements
1456 position endpos
= startpos
;
1459 for (s
= segment_list
; s
; s
= s
->next
, nsegments
++)
1465 s
->is_absolute
= 1; // to avoid confusion
1469 position
*v
= new position
[nsegments
];
1471 for (s
= segment_list
; s
; s
= s
->next
, i
++)
1473 if (flags
& IS_DEFAULT_CHOPPED
) {
1474 lookup_variable("circlerad", &start_chop
);
1475 end_chop
= start_chop
;
1476 flags
|= IS_CHOPPED
;
1478 if (flags
& IS_CHOPPED
) {
1479 position start_chop_vec
, end_chop_vec
;
1480 if (start_chop
!= 0.0) {
1481 start_chop_vec
= v
[0] - startpos
;
1482 start_chop_vec
*= start_chop
/ hypot(start_chop_vec
);
1484 if (end_chop
!= 0.0) {
1485 end_chop_vec
= (v
[nsegments
- 1]
1486 - (nsegments
> 1 ? v
[nsegments
- 2] : startpos
));
1487 end_chop_vec
*= end_chop
/ hypot(end_chop_vec
);
1489 startpos
+= start_chop_vec
;
1490 v
[nsegments
- 1] -= end_chop_vec
;
1491 endpos
-= end_chop_vec
;
1495 p
= new spline_object(startpos
, endpos
, v
, nsegments
);
1498 p
= new arrow_object(startpos
, endpos
, v
, nsegments
);
1501 p
= new line_object(startpos
, endpos
, v
, nsegments
);
1507 last_line
= endpos
- startpos
;
1512 class arc_object
: public linear_object
{
1517 arc_object(int, const position
&, const position
&, const position
&);
1518 position
origin() { return cent
; }
1519 position
center() { return cent
; }
1520 double radius() { return rad
; }
1525 position
north_east();
1526 position
north_west();
1527 position
south_east();
1528 position
south_west();
1529 void update_bounding_box(bounding_box
*);
1530 object_type
type() { return ARC_OBJECT
; }
1532 void move_by(const position
&pos
);
1535 arc_object::arc_object(int cw
, const position
&s
, const position
&e
,
1537 : linear_object(s
, e
), clockwise(cw
), cent(c
)
1542 void arc_object::move_by(const position
&pos
)
1544 linear_object::move_by(pos
);
1548 // we get arc corners from the corresponding circle
1550 position
arc_object::north()
1552 position
result(cent
);
1557 position
arc_object::south()
1559 position
result(cent
);
1564 position
arc_object::east()
1566 position
result(cent
);
1571 position
arc_object::west()
1573 position
result(cent
);
1578 position
arc_object::north_east()
1580 position
result(cent
);
1581 result
.x
+= rad
/M_SQRT2
;
1582 result
.y
+= rad
/M_SQRT2
;
1586 position
arc_object::north_west()
1588 position
result(cent
);
1589 result
.x
-= rad
/M_SQRT2
;
1590 result
.y
+= rad
/M_SQRT2
;
1594 position
arc_object::south_east()
1596 position
result(cent
);
1597 result
.x
+= rad
/M_SQRT2
;
1598 result
.y
-= rad
/M_SQRT2
;
1602 position
arc_object::south_west()
1604 position
result(cent
);
1605 result
.x
-= rad
/M_SQRT2
;
1606 result
.y
-= rad
/M_SQRT2
;
1611 void arc_object::print()
1613 if (lt
.type
== line_type::invisible
)
1615 out
->set_color(0, graphic_object::get_outline_color());
1616 // handle arrow direction; make shorter line for arc
1625 if (arrow_at_start
) {
1626 double theta
= aht
.height
/ rad
;
1630 b
= position(b
.x
*cos(theta
) - b
.y
*sin(theta
),
1631 b
.x
*sin(theta
) + b
.y
*cos(theta
)) + cent
;
1636 if (aht
.solid
&& out
->supports_filled_polygons()) {
1637 draw_arrow(strt
, strt
- b
, aht
, lt
,
1638 graphic_object::get_outline_color());
1641 theta
= fabs(lt
.thickness
) / 72 / 4 / rad
;
1645 b
= position(b
.x
*cos(theta
) - b
.y
*sin(theta
),
1646 b
.x
*sin(theta
) + b
.y
*cos(theta
)) + cent
;
1647 draw_arrow(b
, b
- v
, aht
, lt
,
1648 graphic_object::get_outline_color());
1649 out
->line(b
, &v
, 1, lt
);
1653 double theta
= aht
.height
/ rad
;
1657 b
= position(b
.x
*cos(theta
) - b
.y
*sin(theta
),
1658 b
.x
*sin(theta
) + b
.y
*cos(theta
)) + cent
;
1663 if (aht
.solid
&& out
->supports_filled_polygons()) {
1664 draw_arrow(en
, en
- b
, aht
, lt
,
1665 graphic_object::get_outline_color());
1668 theta
= fabs(lt
.thickness
) / 72 / 4 / rad
;
1672 b
= position(b
.x
*cos(theta
) - b
.y
*sin(theta
),
1673 b
.x
*sin(theta
) + b
.y
*cos(theta
)) + cent
;
1674 draw_arrow(b
, b
- v
, aht
, lt
,
1675 graphic_object::get_outline_color());
1676 out
->line(b
, &v
, 1, lt
);
1679 out
->arc(sp
, cent
, ep
, lt
);
1683 inline double max(double a
, double b
)
1685 return a
> b
? a
: b
;
1688 void arc_object::update_bounding_box(bounding_box
*p
)
1692 position start_offset
= strt
- cent
;
1693 if (start_offset
.x
== 0.0 && start_offset
.y
== 0.0)
1695 position end_offset
= en
- cent
;
1696 if (end_offset
.x
== 0.0 && end_offset
.y
== 0.0)
1698 double start_quad
= atan2(start_offset
.y
, start_offset
.x
)/(M_PI
/2.0);
1699 double end_quad
= atan2(end_offset
.y
, end_offset
.x
)/(M_PI
/2.0);
1701 double temp
= start_quad
;
1702 start_quad
= end_quad
;
1705 if (start_quad
< 0.0)
1707 while (end_quad
<= start_quad
)
1709 double r
= max(hypot(start_offset
), hypot(end_offset
));
1710 for (int q
= int(start_quad
) + 1; q
< end_quad
; q
++) {
1726 p
->encompass(cent
+ offset
);
1730 // We ignore the with attribute. The at attribute always refers to the center.
1732 linear_object
*object_spec::make_arc(position
*curpos
, direction
*dirp
)
1735 int cw
= (flags
& IS_CLOCKWISE
) != 0;
1736 // compute the start
1738 if (flags
& HAS_FROM
)
1742 if (!(flags
& HAS_RADIUS
))
1743 lookup_variable("arcrad", &radius
);
1749 position
m(radius
, radius
);
1750 // Adjust the signs.
1752 if (dir
== DOWN_DIRECTION
|| dir
== LEFT_DIRECTION
)
1754 if (dir
== DOWN_DIRECTION
|| dir
== RIGHT_DIRECTION
)
1756 *dirp
= direction((dir
+ 3) % 4);
1759 if (dir
== UP_DIRECTION
|| dir
== LEFT_DIRECTION
)
1761 if (dir
== DOWN_DIRECTION
|| dir
== LEFT_DIRECTION
)
1763 *dirp
= direction((dir
+ 1) % 4);
1765 endpos
= startpos
+ m
;
1767 // compute the center
1771 else if (startpos
== endpos
)
1772 centerpos
= startpos
;
1774 position h
= (endpos
- startpos
)/2.0;
1775 double d
= hypot(h
);
1778 // make the radius big enough
1781 double alpha
= acos(d
/radius
);
1782 double theta
= atan2(h
.y
, h
.x
);
1787 centerpos
= position(cos(theta
), sin(theta
))*radius
+ startpos
;
1789 arc_object
*p
= new arc_object(cw
, startpos
, endpos
, centerpos
);
1794 graphic_object
*object_spec::make_linear(position
*curpos
, direction
*dirp
)
1797 if (type
== ARC_OBJECT
)
1798 obj
= make_arc(curpos
, dirp
);
1800 obj
= make_line(curpos
, dirp
);
1801 if (type
== ARROW_OBJECT
1802 && (flags
& (HAS_LEFT_ARROW_HEAD
|HAS_RIGHT_ARROW_HEAD
)) == 0)
1803 flags
|= HAS_RIGHT_ARROW_HEAD
;
1804 if (obj
&& (flags
& (HAS_LEFT_ARROW_HEAD
|HAS_RIGHT_ARROW_HEAD
))) {
1806 int at_start
= (flags
& HAS_LEFT_ARROW_HEAD
) != 0;
1807 int at_end
= (flags
& HAS_RIGHT_ARROW_HEAD
) != 0;
1808 if (flags
& HAS_HEIGHT
)
1811 lookup_variable("arrowht", &a
.height
);
1812 if (flags
& HAS_WIDTH
)
1815 lookup_variable("arrowwid", &a
.width
);
1817 lookup_variable("arrowhead", &solid
);
1818 a
.solid
= solid
!= 0.0;
1819 obj
->add_arrows(at_start
, at_end
, a
);
1824 object
*object_spec::make_object(position
*curpos
, direction
*dirp
)
1826 graphic_object
*obj
= 0;
1829 obj
= make_block(curpos
, dirp
);
1832 obj
= make_box(curpos
, dirp
);
1835 obj
= make_text(curpos
, dirp
);
1837 case ELLIPSE_OBJECT
:
1838 obj
= make_ellipse(curpos
, dirp
);
1841 obj
= make_circle(curpos
, dirp
);
1844 obj
= make_move(curpos
, dirp
);
1850 obj
= make_linear(curpos
, dirp
);
1859 if (flags
& IS_INVISIBLE
)
1860 obj
->set_invisible();
1862 obj
->add_text(text
, (flags
& IS_ALIGNED
) != 0);
1863 if (flags
& IS_DOTTED
)
1864 obj
->set_dotted(dash_width
);
1865 else if (flags
& IS_DASHED
)
1866 obj
->set_dashed(dash_width
);
1868 if (flags
& HAS_THICKNESS
)
1871 lookup_variable("linethick", &th
);
1872 obj
->set_thickness(th
);
1873 if (flags
& IS_OUTLINED
)
1874 obj
->set_outline_color(outlined
);
1875 if (flags
& (IS_DEFAULT_FILLED
| IS_FILLED
)) {
1876 if (flags
& IS_SHADED
)
1877 obj
->set_fill_color(shaded
);
1879 if (flags
& IS_DEFAULT_FILLED
)
1880 lookup_variable("fillval", &fill
);
1882 error("bad fill value %1", fill
);
1884 obj
->set_fill(fill
);
1891 struct string_list
{
1894 string_list(char *);
1898 string_list::string_list(char *s
)
1903 string_list::~string_list()
1908 /* A path is used to hold the argument to the `with' attribute. For
1909 example, `.nw' or `.A.s' or `.A'. The major operation on a path is to
1910 take a place and follow the path through the place to place within the
1911 place. Note that `.A.B.C.sw' will work.
1913 For compatibility with DWB pic, `with' accepts positions also (this
1914 is incorrectly documented in CSTR 116). */
1916 path::path(corner c
)
1917 : crn(c
), label_list(0), ypath(0), is_position(0)
1921 path::path(position p
)
1922 : crn(0), label_list(0), ypath(0), is_position(1)
1928 path::path(char *l
, corner c
)
1929 : crn(c
), ypath(0), is_position(0)
1931 label_list
= new string_list(l
);
1936 while (label_list
) {
1937 string_list
*tem
= label_list
;
1938 label_list
= label_list
->next
;
1944 void path::append(corner c
)
1950 void path::append(char *s
)
1953 for (p
= &label_list
; *p
; p
= &(*p
)->next
)
1955 *p
= new string_list(s
);
1958 void path::set_ypath(path
*p
)
1963 // return non-zero for success
1965 int path::follow(const place
&pl
, place
*result
) const
1973 const place
*p
= &pl
;
1974 for (string_list
*lb
= label_list
; lb
; lb
= lb
->next
)
1975 if (p
->obj
== 0 || (p
= p
->obj
->find_label(lb
->str
)) == 0) {
1976 lex_error("object does not contain a place `%1'", lb
->str
);
1979 if (crn
== 0 || p
->obj
== 0)
1982 position ps
= ((p
->obj
)->*(crn
))();
1989 if (!ypath
->follow(pl
, &tem
))
1992 if (result
->obj
!= tem
.obj
)
1998 void print_object_list(object
*p
)
2000 for (; p
; p
= p
->next
) {
2006 void print_picture(object
*obj
)
2009 for (object
*p
= obj
; p
; p
= p
->next
)
2010 p
->update_bounding_box(&bb
);
2012 lookup_variable("scale", &scale
);
2013 out
->start_picture(scale
, bb
.ll
, bb
.ur
);
2014 print_object_list(obj
);
2015 out
->finish_picture();