2 /* Copyright (C) 1989, 1990, 1991, 1992, 2001, 2002, 2003, 2004, 2005,
4 Free Software Foundation, Inc.
5 Written by James Clark (jjc@jclark.com)
7 This file is part of groff.
9 groff is free software; you can redistribute it and/or modify it under
10 the terms of the GNU General Public License as published by the Free
11 Software Foundation; either version 2, or (at your option) any later
14 groff is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19 You should have received a copy of the GNU General Public License along
20 with groff; see the file COPYING. If not, write to the Free Software
21 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
27 void print_object_list(object
*);
29 line_type::line_type()
30 : type(solid
), thickness(1.0)
34 output::output() : args(0), desired_height(0.0), desired_width(0.0)
43 void output::set_desired_width_height(double wid
, double ht
)
49 void output::set_args(const char *s
)
52 if (s
== 0 || *s
== '\0')
58 int output::supports_filled_polygons()
63 void output::begin_block(const position
&, const position
&)
67 void output::end_block()
71 double output::compute_scale(double sc
, const position
&ll
, const position
&ur
)
73 distance dim
= ur
- ll
;
74 if (desired_width
!= 0.0 || desired_height
!= 0.0) {
76 if (desired_width
!= 0.0) {
78 error("width specified for picture with zero width");
80 sc
= dim
.x
/desired_width
;
82 if (desired_height
!= 0.0) {
84 error("height specified for picture with zero height");
86 double tem
= dim
.y
/desired_height
;
91 return sc
== 0.0 ? 1.0 : sc
;
96 distance sdim
= dim
/sc
;
97 double max_width
= 0.0;
98 lookup_variable("maxpswid", &max_width
);
99 double max_height
= 0.0;
100 lookup_variable("maxpsht", &max_height
);
101 if ((max_width
> 0.0 && sdim
.x
> max_width
)
102 || (max_height
> 0.0 && sdim
.y
> max_height
)) {
103 double xscale
= dim
.x
/max_width
;
104 double yscale
= dim
.y
/max_height
;
105 return xscale
> yscale
? xscale
: yscale
;
112 position::position(const place
&pl
)
115 // Use two statements to work around bug in SGI C++.
116 object
*tem
= pl
.obj
;
117 *this = tem
->origin();
125 position::position() : x(0.0), y(0.0)
129 position::position(double a
, double b
) : x(a
), y(b
)
134 int operator==(const position
&a
, const position
&b
)
136 return a
.x
== b
.x
&& a
.y
== b
.y
;
139 int operator!=(const position
&a
, const position
&b
)
141 return a
.x
!= b
.x
|| a
.y
!= b
.y
;
144 position
&position::operator+=(const position
&a
)
151 position
&position::operator-=(const position
&a
)
158 position
&position::operator*=(double a
)
165 position
&position::operator/=(double a
)
172 position
operator-(const position
&a
)
174 return position(-a
.x
, -a
.y
);
177 position
operator+(const position
&a
, const position
&b
)
179 return position(a
.x
+ b
.x
, a
.y
+ b
.y
);
182 position
operator-(const position
&a
, const position
&b
)
184 return position(a
.x
- b
.x
, a
.y
- b
.y
);
187 position
operator/(const position
&a
, double n
)
189 return position(a
.x
/n
, a
.y
/n
);
192 position
operator*(const position
&a
, double n
)
194 return position(a
.x
*n
, a
.y
*n
);
199 double operator*(const position
&a
, const position
&b
)
201 return a
.x
*b
.x
+ a
.y
*b
.y
;
204 double hypot(const position
&a
)
206 return groff_hypot(a
.x
, a
.y
);
209 struct arrow_head_type
{
215 void draw_arrow(const position
&pos
, const distance
&dir
,
216 const arrow_head_type
&aht
, const line_type
<
,
217 char *outline_color_for_fill
)
219 double hyp
= hypot(dir
);
221 error("cannot draw arrow on object with zero length");
224 position base
= -dir
;
225 base
*= aht
.height
/hyp
;
226 position
n(dir
.y
, -dir
.x
);
227 n
*= aht
.width
/(hyp
*2.0);
229 slt
.type
= line_type::solid
;
230 if (aht
.solid
&& out
->supports_filled_polygons()) {
233 v
[1] = pos
+ base
+ n
;
234 v
[2] = pos
+ base
- n
;
235 // fill with outline color
236 out
->set_color(outline_color_for_fill
, outline_color_for_fill
);
237 // make stroke thin to avoid arrow sticking
239 out
->polygon(v
, 3, slt
, 1);
242 // use two line segments to avoid arrow sticking
243 out
->line(pos
+ base
- n
, &pos
, 1, slt
);
244 out
->line(pos
+ base
+ n
, &pos
, 1, slt
);
248 object::object() : prev(0), next(0)
256 void object::move_by(const position
&)
264 void object::print_text()
273 struct bounding_box
{
279 void encompass(const position
&);
282 bounding_box::bounding_box()
287 void bounding_box::encompass(const position
&pos
)
306 void object::update_bounding_box(bounding_box
*)
310 position
object::origin()
312 return position(0.0,0.0);
315 position
object::north()
320 position
object::south()
325 position
object::east()
330 position
object::west()
335 position
object::north_east()
340 position
object::north_west()
345 position
object::south_east()
350 position
object::south_west()
355 position
object::start()
360 position
object::end()
365 position
object::center()
370 double object::width()
375 double object::radius()
380 double object::height()
385 place
*object::find_label(const char *)
390 segment::segment(const position
&a
, int n
, segment
*p
)
391 : is_absolute(n
), pos(a
), next(p
)
395 text_item::text_item(char *t
, const char *fn
, int ln
)
396 : next(0), text(t
), filename(fn
), lineno(ln
)
398 adj
.h
= CENTER_ADJUST
;
402 text_item::~text_item()
407 object_spec::object_spec(object_type t
) : type(t
)
412 segment_width
= segment_height
= 0.0;
413 segment_is_absolute
= 0;
420 dir
= RIGHT_DIRECTION
;
423 object_spec::~object_spec()
426 while (segment_list
!= 0) {
427 segment
*tem
= segment_list
;
428 segment_list
= segment_list
->next
;
431 object
*p
= oblist
.head
;
438 text_item
*tem
= text
;
447 class command_object
: public object
{
449 const char *filename
;
452 command_object(char *, const char *, int);
454 object_type
type() { return OTHER_OBJECT
; }
458 command_object::command_object(char *p
, const char *fn
, int ln
)
459 : s(p
), filename(fn
), lineno(ln
)
463 command_object::~command_object()
468 void command_object::print()
470 out
->command(s
, filename
, lineno
);
473 object
*make_command_object(char *s
, const char *fn
, int ln
)
475 return new command_object(s
, fn
, ln
);
478 class mark_object
: public object
{
484 object
*make_mark_object()
486 return new mark_object();
489 mark_object::mark_object()
493 object_type
mark_object::type()
498 object_list::object_list() : head(0), tail(0)
502 void object_list::append(object
*obj
)
505 obj
->next
= obj
->prev
= 0;
516 void object_list::wrap_up_block(object_list
*ol
)
519 for (p
= tail
; p
&& p
->type() != MARK_OBJECT
; p
= p
->prev
)
537 text_piece::text_piece()
538 : text(0), filename(0), lineno(-1)
540 adj
.h
= CENTER_ADJUST
;
544 text_piece::~text_piece()
549 class graphic_object
: public object
{
560 object_type
type() = 0;
562 void add_text(text_item
*, int);
563 void set_dotted(double);
564 void set_dashed(double);
565 void set_thickness(double);
566 void set_invisible();
567 void set_outline_color(char *);
568 char *get_outline_color();
569 virtual void set_fill(double);
570 virtual void set_xslanted(double);
571 virtual void set_yslanted(double);
572 virtual void set_fill_color(char *);
575 graphic_object::graphic_object()
576 : ntext(0), text(0), aligned(0), outline_color(0), color_fill(0)
580 void graphic_object::set_dotted(double wid
)
582 lt
.type
= line_type::dotted
;
586 void graphic_object::set_dashed(double wid
)
588 lt
.type
= line_type::dashed
;
592 void graphic_object::set_thickness(double th
)
597 void graphic_object::set_fill(double)
601 void graphic_object::set_xslanted(double)
605 void graphic_object::set_yslanted(double)
609 void graphic_object::set_fill_color(char *c
)
611 color_fill
= strsave(c
);
614 void graphic_object::set_outline_color(char *c
)
616 outline_color
= strsave(c
);
619 char *graphic_object::get_outline_color()
621 return outline_color
;
624 void graphic_object::set_invisible()
626 lt
.type
= line_type::invisible
;
629 void graphic_object::add_text(text_item
*t
, int a
)
634 for (p
= t
; p
; p
= p
->next
)
639 text
= new text_piece
[len
];
640 for (p
= t
, len
= 0; p
; p
= p
->next
, len
++) {
641 text
[len
].text
= p
->text
;
643 text
[len
].adj
= p
->adj
;
644 text
[len
].filename
= p
->filename
;
645 text
[len
].lineno
= p
->lineno
;
651 void graphic_object::print_text()
655 position
d(end() - start());
656 if (d
.x
!= 0.0 || d
.y
!= 0.0)
657 angle
= atan2(d
.y
, d
.x
);
660 out
->set_color(color_fill
, get_outline_color());
661 out
->text(center(), text
, ntext
, angle
);
666 graphic_object::~graphic_object()
669 ad_delete(ntext
) text
;
672 class rectangle_object
: public graphic_object
{
677 rectangle_object(const position
&);
678 double width() { return dim
.x
; }
679 double height() { return dim
.y
; }
680 position
origin() { return cent
; }
681 position
center() { return cent
; }
682 position
north() { return position(cent
.x
, cent
.y
+ dim
.y
/2.0); }
683 position
south() { return position(cent
.x
, cent
.y
- dim
.y
/2.0); }
684 position
east() { return position(cent
.x
+ dim
.x
/2.0, cent
.y
); }
685 position
west() { return position(cent
.x
- dim
.x
/2.0, cent
.y
); }
686 position
north_east() { return position(cent
.x
+ dim
.x
/2.0, cent
.y
+ dim
.y
/2.0); }
687 position
north_west() { return position(cent
.x
- dim
.x
/2.0, cent
.y
+ dim
.y
/2.0); }
688 position
south_east() { return position(cent
.x
+ dim
.x
/2.0, cent
.y
- dim
.y
/2.0); }
689 position
south_west() { return position(cent
.x
- dim
.x
/2.0, cent
.y
- dim
.y
/2.0); }
690 object_type
type() = 0;
691 void update_bounding_box(bounding_box
*);
692 void move_by(const position
&);
695 rectangle_object::rectangle_object(const position
&d
)
700 void rectangle_object::update_bounding_box(bounding_box
*p
)
702 p
->encompass(cent
- dim
/2.0);
703 p
->encompass(cent
+ dim
/2.0);
706 void rectangle_object::move_by(const position
&a
)
711 class closed_object
: public rectangle_object
{
713 closed_object(const position
&);
714 object_type
type() = 0;
715 void set_fill(double);
716 void set_xslanted(double);
717 void set_yslanted(double);
718 void set_fill_color(char *fill
);
720 double fill
; // < 0 if not filled
721 double xslanted
; // !=0 if x is slanted
722 double yslanted
; // !=0 if y is slanted
723 char *color_fill
; // = 0 if not colored
726 closed_object::closed_object(const position
&pos
)
727 : rectangle_object(pos
), fill(-1.0), xslanted(0), yslanted(0), color_fill(0)
731 void closed_object::set_fill(double f
)
737 /* accept positive and negative values */
738 void closed_object::set_xslanted(double s
)
743 /* accept positive and negative values */
744 void closed_object::set_yslanted(double s
)
750 void closed_object::set_fill_color(char *f
)
752 color_fill
= strsave(f
);
755 class box_object
: public closed_object
{
759 box_object(const position
&, double);
760 object_type
type() { return BOX_OBJECT
; }
762 position
north_east();
763 position
north_west();
764 position
south_east();
765 position
south_west();
768 box_object::box_object(const position
&pos
, double r
)
769 : closed_object(pos
), xrad(dim
.x
> 0 ? r
: -r
), yrad(dim
.y
> 0 ? r
: -r
)
773 const double CHOP_FACTOR
= 1.0 - 1.0/M_SQRT2
;
775 position
box_object::north_east()
777 return position(cent
.x
+ dim
.x
/2.0 - CHOP_FACTOR
*xrad
,
778 cent
.y
+ dim
.y
/2.0 - CHOP_FACTOR
*yrad
);
781 position
box_object::north_west()
783 return position(cent
.x
- dim
.x
/2.0 + CHOP_FACTOR
*xrad
,
784 cent
.y
+ dim
.y
/2.0 - CHOP_FACTOR
*yrad
);
787 position
box_object::south_east()
789 return position(cent
.x
+ dim
.x
/2.0 - CHOP_FACTOR
*xrad
,
790 cent
.y
- dim
.y
/2.0 + CHOP_FACTOR
*yrad
);
793 position
box_object::south_west()
795 return position(cent
.x
- dim
.x
/2.0 + CHOP_FACTOR
*xrad
,
796 cent
.y
- dim
.y
/2.0 + CHOP_FACTOR
*yrad
);
799 void box_object::print()
801 if (lt
.type
== line_type::invisible
&& fill
< 0.0 && color_fill
== 0)
803 out
->set_color(color_fill
, graphic_object::get_outline_color());
805 distance dim2
= dim
/2.0;
807 /* error("x slanted %1", xslanted); */
808 /* error("y slanted %1", yslanted); */
809 vec
[0] = cent
+ position(dim2
.x
, -(dim2
.y
- yslanted
)); /* lr */
810 vec
[1] = cent
+ position(dim2
.x
+ xslanted
, dim2
.y
+ yslanted
); /* ur */
811 vec
[2] = cent
+ position(-(dim2
.x
- xslanted
), dim2
.y
); /* ul */
812 vec
[3] = cent
+ position(-(dim2
.x
), -dim2
.y
); /* ll */
813 out
->polygon(vec
, 4, lt
, fill
);
816 distance
abs_dim(fabs(dim
.x
), fabs(dim
.y
));
817 out
->rounded_box(cent
, abs_dim
, fabs(xrad
), lt
, fill
, color_fill
);
822 graphic_object
*object_spec::make_box(position
*curpos
, direction
*dirp
)
824 static double last_box_height
;
825 static double last_box_width
;
826 static double last_box_radius
;
827 static int have_last_box
= 0;
828 if (!(flags
& HAS_HEIGHT
)) {
829 if ((flags
& IS_SAME
) && have_last_box
)
830 height
= last_box_height
;
832 lookup_variable("boxht", &height
);
834 if (!(flags
& HAS_WIDTH
)) {
835 if ((flags
& IS_SAME
) && have_last_box
)
836 width
= last_box_width
;
838 lookup_variable("boxwid", &width
);
840 if (!(flags
& HAS_RADIUS
)) {
841 if ((flags
& IS_SAME
) && have_last_box
)
842 radius
= last_box_radius
;
844 lookup_variable("boxrad", &radius
);
846 last_box_width
= width
;
847 last_box_height
= height
;
848 last_box_radius
= radius
;
850 radius
= fabs(radius
);
851 if (radius
*2.0 > fabs(width
))
852 radius
= fabs(width
/2.0);
853 if (radius
*2.0 > fabs(height
))
854 radius
= fabs(height
/2.0);
855 box_object
*p
= new box_object(position(width
, height
), radius
);
856 if (!position_rectangle(p
, curpos
, dirp
)) {
863 // return non-zero for success
865 int object_spec::position_rectangle(rectangle_object
*p
,
866 position
*curpos
, direction
*dirp
)
869 dir
= *dirp
; // ignore any direction in attribute list
873 motion
.y
= p
->height()/2.0;
876 motion
.y
= -p
->height()/2.0;
879 motion
.x
= -p
->width()/2.0;
881 case RIGHT_DIRECTION
:
882 motion
.x
= p
->width()/2.0;
887 if (flags
& HAS_AT
) {
889 if (flags
& HAS_WITH
) {
893 if (!with
->follow(here
, &offset
))
908 class block_object
: public rectangle_object
{
912 block_object(const position
&, const object_list
&ol
, PTABLE(place
) *t
);
914 place
*find_label(const char *);
916 void move_by(const position
&);
920 block_object::block_object(const position
&d
, const object_list
&ol
,
922 : rectangle_object(d
), oblist(ol
), tbl(t
)
926 block_object::~block_object()
929 object
*p
= oblist
.head
;
937 void block_object::print()
939 out
->begin_block(south_west(), north_east());
940 print_object_list(oblist
.head
);
944 static void adjust_objectless_places(PTABLE(place
) *tbl
, const position
&a
)
946 // Adjust all the labels that aren't attached to objects.
947 PTABLE_ITERATOR(place
) iter(tbl
);
950 while (iter
.next(&key
, &pl
))
951 if (key
&& csupper(key
[0]) && pl
->obj
== 0) {
957 void block_object::move_by(const position
&a
)
960 for (object
*p
= oblist
.head
; p
; p
= p
->next
)
962 adjust_objectless_places(tbl
, a
);
966 place
*block_object::find_label(const char *name
)
968 return tbl
->lookup(name
);
971 object_type
block_object::type()
976 graphic_object
*object_spec::make_block(position
*curpos
, direction
*dirp
)
979 for (object
*p
= oblist
.head
; p
; p
= p
->next
)
980 p
->update_bounding_box(&bb
);
983 position m
= -(bb
.ll
+ bb
.ur
)/2.0;
984 for (object
*p
= oblist
.head
; p
; p
= p
->next
)
986 adjust_objectless_places(tbl
, m
);
989 if (flags
& HAS_WIDTH
)
991 if (flags
& HAS_HEIGHT
)
993 block_object
*block
= new block_object(dim
, oblist
, tbl
);
994 if (!position_rectangle(block
, curpos
, dirp
)) {
999 oblist
.head
= oblist
.tail
= 0;
1003 class text_object
: public rectangle_object
{
1005 text_object(const position
&);
1006 object_type
type() { return TEXT_OBJECT
; }
1009 text_object::text_object(const position
&d
)
1010 : rectangle_object(d
)
1014 graphic_object
*object_spec::make_text(position
*curpos
, direction
*dirp
)
1016 if (!(flags
& HAS_HEIGHT
)) {
1017 lookup_variable("textht", &height
);
1019 for (text_item
*t
= text
; t
; t
= t
->next
)
1023 if (!(flags
& HAS_WIDTH
))
1024 lookup_variable("textwid", &width
);
1025 text_object
*p
= new text_object(position(width
, height
));
1026 if (!position_rectangle(p
, curpos
, dirp
)) {
1034 class ellipse_object
: public closed_object
{
1036 ellipse_object(const position
&);
1037 position
north_east() { return position(cent
.x
+ dim
.x
/(M_SQRT2
*2.0),
1038 cent
.y
+ dim
.y
/(M_SQRT2
*2.0)); }
1039 position
north_west() { return position(cent
.x
- dim
.x
/(M_SQRT2
*2.0),
1040 cent
.y
+ dim
.y
/(M_SQRT2
*2.0)); }
1041 position
south_east() { return position(cent
.x
+ dim
.x
/(M_SQRT2
*2.0),
1042 cent
.y
- dim
.y
/(M_SQRT2
*2.0)); }
1043 position
south_west() { return position(cent
.x
- dim
.x
/(M_SQRT2
*2.0),
1044 cent
.y
- dim
.y
/(M_SQRT2
*2.0)); }
1045 double radius() { return dim
.x
/2.0; }
1046 object_type
type() { return ELLIPSE_OBJECT
; }
1050 ellipse_object::ellipse_object(const position
&d
)
1055 void ellipse_object::print()
1057 if (lt
.type
== line_type::invisible
&& fill
< 0.0 && color_fill
== 0)
1059 out
->set_color(color_fill
, graphic_object::get_outline_color());
1060 out
->ellipse(cent
, dim
, lt
, fill
);
1064 graphic_object
*object_spec::make_ellipse(position
*curpos
, direction
*dirp
)
1066 static double last_ellipse_height
;
1067 static double last_ellipse_width
;
1068 static int have_last_ellipse
= 0;
1069 if (!(flags
& HAS_HEIGHT
)) {
1070 if ((flags
& IS_SAME
) && have_last_ellipse
)
1071 height
= last_ellipse_height
;
1073 lookup_variable("ellipseht", &height
);
1075 if (!(flags
& HAS_WIDTH
)) {
1076 if ((flags
& IS_SAME
) && have_last_ellipse
)
1077 width
= last_ellipse_width
;
1079 lookup_variable("ellipsewid", &width
);
1081 last_ellipse_width
= width
;
1082 last_ellipse_height
= height
;
1083 have_last_ellipse
= 1;
1084 ellipse_object
*p
= new ellipse_object(position(width
, height
));
1085 if (!position_rectangle(p
, curpos
, dirp
)) {
1092 class circle_object
: public ellipse_object
{
1094 circle_object(double);
1095 object_type
type() { return CIRCLE_OBJECT
; }
1099 circle_object::circle_object(double diam
)
1100 : ellipse_object(position(diam
, diam
))
1104 void circle_object::print()
1106 if (lt
.type
== line_type::invisible
&& fill
< 0.0 && color_fill
== 0)
1108 out
->set_color(color_fill
, graphic_object::get_outline_color());
1109 out
->circle(cent
, dim
.x
/2.0, lt
, fill
);
1113 graphic_object
*object_spec::make_circle(position
*curpos
, direction
*dirp
)
1115 static double last_circle_radius
;
1116 static int have_last_circle
= 0;
1117 if (!(flags
& HAS_RADIUS
)) {
1118 if ((flags
& IS_SAME
) && have_last_circle
)
1119 radius
= last_circle_radius
;
1121 lookup_variable("circlerad", &radius
);
1123 last_circle_radius
= radius
;
1124 have_last_circle
= 1;
1125 circle_object
*p
= new circle_object(radius
*2.0);
1126 if (!position_rectangle(p
, curpos
, dirp
)) {
1133 class move_object
: public graphic_object
{
1137 move_object(const position
&s
, const position
&e
);
1138 position
origin() { return en
; }
1139 object_type
type() { return MOVE_OBJECT
; }
1140 void update_bounding_box(bounding_box
*);
1141 void move_by(const position
&);
1144 move_object::move_object(const position
&s
, const position
&e
)
1149 void move_object::update_bounding_box(bounding_box
*p
)
1155 void move_object::move_by(const position
&a
)
1161 graphic_object
*object_spec::make_move(position
*curpos
, direction
*dirp
)
1163 static position last_move
;
1164 static int have_last_move
= 0;
1166 // No need to look at at since `at' attribute sets `from' attribute.
1167 position startpos
= (flags
& HAS_FROM
) ? from
: *curpos
;
1168 if (!(flags
& HAS_SEGMENT
)) {
1169 if ((flags
& IS_SAME
) && have_last_move
)
1170 segment_pos
= last_move
;
1174 segment_pos
.y
= segment_height
;
1176 case DOWN_DIRECTION
:
1177 segment_pos
.y
= -segment_height
;
1179 case LEFT_DIRECTION
:
1180 segment_pos
.x
= -segment_width
;
1182 case RIGHT_DIRECTION
:
1183 segment_pos
.x
= segment_width
;
1190 segment_list
= new segment(segment_pos
, segment_is_absolute
, segment_list
);
1191 // Reverse the segment_list so that it's in forward order.
1192 segment
*old
= segment_list
;
1195 segment
*tem
= old
->next
;
1196 old
->next
= segment_list
;
1200 // Compute the end position.
1201 position endpos
= startpos
;
1202 for (segment
*s
= segment_list
; s
; s
= s
->next
)
1208 last_move
= endpos
- startpos
;
1209 move_object
*p
= new move_object(startpos
, endpos
);
1214 class linear_object
: public graphic_object
{
1216 char arrow_at_start
;
1218 arrow_head_type aht
;
1222 linear_object(const position
&s
, const position
&e
);
1223 position
start() { return strt
; }
1224 position
end() { return en
; }
1225 void move_by(const position
&);
1226 void update_bounding_box(bounding_box
*) = 0;
1227 object_type
type() = 0;
1228 void add_arrows(int at_start
, int at_end
, const arrow_head_type
&);
1231 class line_object
: public linear_object
{
1236 line_object(const position
&s
, const position
&e
, position
*, int);
1238 position
origin() { return strt
; }
1239 position
center() { return (strt
+ en
)/2.0; }
1240 position
north() { return (en
.y
- strt
.y
) > 0 ? en
: strt
; }
1241 position
south() { return (en
.y
- strt
.y
) < 0 ? en
: strt
; }
1242 position
east() { return (en
.x
- strt
.x
) > 0 ? en
: strt
; }
1243 position
west() { return (en
.x
- strt
.x
) < 0 ? en
: strt
; }
1244 object_type
type() { return LINE_OBJECT
; }
1245 void update_bounding_box(bounding_box
*);
1247 void move_by(const position
&);
1250 class arrow_object
: public line_object
{
1252 arrow_object(const position
&, const position
&, position
*, int);
1253 object_type
type() { return ARROW_OBJECT
; }
1256 class spline_object
: public line_object
{
1258 spline_object(const position
&, const position
&, position
*, int);
1259 object_type
type() { return SPLINE_OBJECT
; }
1261 void update_bounding_box(bounding_box
*);
1264 linear_object::linear_object(const position
&s
, const position
&e
)
1265 : arrow_at_start(0), arrow_at_end(0), strt(s
), en(e
)
1269 void linear_object::move_by(const position
&a
)
1275 void linear_object::add_arrows(int at_start
, int at_end
,
1276 const arrow_head_type
&a
)
1278 arrow_at_start
= at_start
;
1279 arrow_at_end
= at_end
;
1283 line_object::line_object(const position
&s
, const position
&e
,
1285 : linear_object(s
, e
), v(p
), n(i
)
1289 void line_object::print()
1291 if (lt
.type
== line_type::invisible
)
1293 out
->set_color(0, graphic_object::get_outline_color());
1294 // shorten line length to avoid arrow sticking.
1296 if (arrow_at_start
) {
1297 position base
= v
[0] - strt
;
1298 double hyp
= hypot(base
);
1300 error("cannot draw arrow on object with zero length");
1303 if (aht
.solid
&& out
->supports_filled_polygons()) {
1304 base
*= aht
.height
/ hyp
;
1305 draw_arrow(strt
, strt
- v
[0], aht
, lt
,
1306 graphic_object::get_outline_color());
1309 base
*= fabs(lt
.thickness
) / hyp
/ 72 / 4;
1311 draw_arrow(sp
, sp
- v
[0], aht
, lt
,
1312 graphic_object::get_outline_color());
1316 position base
= v
[n
-1] - (n
> 1 ? v
[n
-2] : strt
);
1317 double hyp
= hypot(base
);
1319 error("cannot draw arrow on object with zero length");
1322 if (aht
.solid
&& out
->supports_filled_polygons()) {
1323 base
*= aht
.height
/ hyp
;
1324 draw_arrow(en
, v
[n
-1] - (n
> 1 ? v
[n
-2] : strt
), aht
, lt
,
1325 graphic_object::get_outline_color());
1328 base
*= fabs(lt
.thickness
) / hyp
/ 72 / 4;
1330 draw_arrow(v
[n
-1], v
[n
-1] - (n
> 1 ? v
[n
-2] : strt
), aht
, lt
,
1331 graphic_object::get_outline_color());
1334 out
->line(sp
, v
, n
, lt
);
1338 void line_object::update_bounding_box(bounding_box
*p
)
1341 for (int i
= 0; i
< n
; i
++)
1345 void line_object::move_by(const position
&pos
)
1347 linear_object::move_by(pos
);
1348 for (int i
= 0; i
< n
; i
++)
1352 void spline_object::update_bounding_box(bounding_box
*p
)
1364 [ the points for the Bezier cubic ]
1372 (1-t)^3*p1 + 3*t*(t - 1)^2*p2 + 3*t^2*(1-t)*p3 + t^3*p4
1373 [ the equation for the Bezier cubic ]
1375 = .125*q1 + .75*q2 + .125*q3
1378 for (int i
= 1; i
< n
; i
++)
1379 p
->encompass((i
== 1 ? strt
: v
[i
-2])*.125 + v
[i
-1]*.75 + v
[i
]*.125);
1382 arrow_object::arrow_object(const position
&s
, const position
&e
,
1384 : line_object(s
, e
, p
, i
)
1388 spline_object::spline_object(const position
&s
, const position
&e
,
1390 : line_object(s
, e
, p
, i
)
1394 void spline_object::print()
1396 if (lt
.type
== line_type::invisible
)
1398 out
->set_color(0, graphic_object::get_outline_color());
1399 // shorten line length for spline to avoid arrow sticking
1401 if (arrow_at_start
) {
1402 position base
= v
[0] - strt
;
1403 double hyp
= hypot(base
);
1405 error("cannot draw arrow on object with zero length");
1408 if (aht
.solid
&& out
->supports_filled_polygons()) {
1409 base
*= aht
.height
/ hyp
;
1410 draw_arrow(strt
, strt
- v
[0], aht
, lt
,
1411 graphic_object::get_outline_color());
1412 sp
= strt
+ base
*0.1; // to reserve spline shape
1414 base
*= fabs(lt
.thickness
) / hyp
/ 72 / 4;
1416 draw_arrow(sp
, sp
- v
[0], aht
, lt
,
1417 graphic_object::get_outline_color());
1421 position base
= v
[n
-1] - (n
> 1 ? v
[n
-2] : strt
);
1422 double hyp
= hypot(base
);
1424 error("cannot draw arrow on object with zero length");
1427 if (aht
.solid
&& out
->supports_filled_polygons()) {
1428 base
*= aht
.height
/ hyp
;
1429 draw_arrow(en
, v
[n
-1] - (n
> 1 ? v
[n
-2] : strt
), aht
, lt
,
1430 graphic_object::get_outline_color());
1431 v
[n
-1] = en
- base
*0.1; // to reserve spline shape
1433 base
*= fabs(lt
.thickness
) / hyp
/ 72 / 4;
1435 draw_arrow(v
[n
-1], v
[n
-1] - (n
> 1 ? v
[n
-2] : strt
), aht
, lt
,
1436 graphic_object::get_outline_color());
1439 out
->spline(sp
, v
, n
, lt
);
1443 line_object::~line_object()
1448 linear_object
*object_spec::make_line(position
*curpos
, direction
*dirp
)
1450 static position last_line
;
1451 static int have_last_line
= 0;
1453 // We handle `at' only in conjunction with `with', otherwise it is
1454 // the same as the `from' attribute.
1456 if ((flags
& HAS_AT
) && (flags
& HAS_WITH
))
1457 // handled later -- we need the end position
1459 else if (flags
& HAS_FROM
)
1463 if (!(flags
& HAS_SEGMENT
)) {
1464 if ((flags
& IS_SAME
) && (type
== LINE_OBJECT
|| type
== ARROW_OBJECT
)
1466 segment_pos
= last_line
;
1470 segment_pos
.y
= segment_height
;
1472 case DOWN_DIRECTION
:
1473 segment_pos
.y
= -segment_height
;
1475 case LEFT_DIRECTION
:
1476 segment_pos
.x
= -segment_width
;
1478 case RIGHT_DIRECTION
:
1479 segment_pos
.x
= segment_width
;
1485 segment_list
= new segment(segment_pos
, segment_is_absolute
, segment_list
);
1486 // reverse the segment_list so that it's in forward order
1487 segment
*old
= segment_list
;
1490 segment
*tem
= old
->next
;
1491 old
->next
= segment_list
;
1495 // Absolutise all movements
1496 position endpos
= startpos
;
1499 for (s
= segment_list
; s
; s
= s
->next
, nsegments
++)
1505 s
->is_absolute
= 1; // to avoid confusion
1507 if ((flags
& HAS_AT
) && (flags
& HAS_WITH
)) {
1508 // `tmpobj' works for arrows and splines too -- we only need positions
1509 line_object
tmpobj(startpos
, endpos
, 0, 0);
1514 if (!with
->follow(here
, &offset
))
1517 for (s
= segment_list
; s
; s
= s
->next
)
1524 position
*v
= new position
[nsegments
];
1526 for (s
= segment_list
; s
; s
= s
->next
, i
++)
1528 if (flags
& IS_DEFAULT_CHOPPED
) {
1529 lookup_variable("circlerad", &start_chop
);
1530 end_chop
= start_chop
;
1531 flags
|= IS_CHOPPED
;
1533 if (flags
& IS_CHOPPED
) {
1534 position start_chop_vec
, end_chop_vec
;
1535 if (start_chop
!= 0.0) {
1536 start_chop_vec
= v
[0] - startpos
;
1537 start_chop_vec
*= start_chop
/ hypot(start_chop_vec
);
1539 if (end_chop
!= 0.0) {
1540 end_chop_vec
= (v
[nsegments
- 1]
1541 - (nsegments
> 1 ? v
[nsegments
- 2] : startpos
));
1542 end_chop_vec
*= end_chop
/ hypot(end_chop_vec
);
1544 startpos
+= start_chop_vec
;
1545 v
[nsegments
- 1] -= end_chop_vec
;
1546 endpos
-= end_chop_vec
;
1550 p
= new spline_object(startpos
, endpos
, v
, nsegments
);
1553 p
= new arrow_object(startpos
, endpos
, v
, nsegments
);
1556 p
= new line_object(startpos
, endpos
, v
, nsegments
);
1562 last_line
= endpos
- startpos
;
1567 class arc_object
: public linear_object
{
1572 arc_object(int, const position
&, const position
&, const position
&);
1573 position
origin() { return cent
; }
1574 position
center() { return cent
; }
1575 double radius() { return rad
; }
1580 position
north_east();
1581 position
north_west();
1582 position
south_east();
1583 position
south_west();
1584 void update_bounding_box(bounding_box
*);
1585 object_type
type() { return ARC_OBJECT
; }
1587 void move_by(const position
&pos
);
1590 arc_object::arc_object(int cw
, const position
&s
, const position
&e
,
1592 : linear_object(s
, e
), clockwise(cw
), cent(c
)
1597 void arc_object::move_by(const position
&pos
)
1599 linear_object::move_by(pos
);
1603 // we get arc corners from the corresponding circle
1605 position
arc_object::north()
1607 position
result(cent
);
1612 position
arc_object::south()
1614 position
result(cent
);
1619 position
arc_object::east()
1621 position
result(cent
);
1626 position
arc_object::west()
1628 position
result(cent
);
1633 position
arc_object::north_east()
1635 position
result(cent
);
1636 result
.x
+= rad
/M_SQRT2
;
1637 result
.y
+= rad
/M_SQRT2
;
1641 position
arc_object::north_west()
1643 position
result(cent
);
1644 result
.x
-= rad
/M_SQRT2
;
1645 result
.y
+= rad
/M_SQRT2
;
1649 position
arc_object::south_east()
1651 position
result(cent
);
1652 result
.x
+= rad
/M_SQRT2
;
1653 result
.y
-= rad
/M_SQRT2
;
1657 position
arc_object::south_west()
1659 position
result(cent
);
1660 result
.x
-= rad
/M_SQRT2
;
1661 result
.y
-= rad
/M_SQRT2
;
1666 void arc_object::print()
1668 if (lt
.type
== line_type::invisible
)
1670 out
->set_color(0, graphic_object::get_outline_color());
1671 // handle arrow direction; make shorter line for arc
1680 if (arrow_at_start
) {
1681 double theta
= aht
.height
/ rad
;
1685 b
= position(b
.x
*cos(theta
) - b
.y
*sin(theta
),
1686 b
.x
*sin(theta
) + b
.y
*cos(theta
)) + cent
;
1691 if (aht
.solid
&& out
->supports_filled_polygons()) {
1692 draw_arrow(strt
, strt
- b
, aht
, lt
,
1693 graphic_object::get_outline_color());
1696 theta
= fabs(lt
.thickness
) / 72 / 4 / rad
;
1700 b
= position(b
.x
*cos(theta
) - b
.y
*sin(theta
),
1701 b
.x
*sin(theta
) + b
.y
*cos(theta
)) + cent
;
1702 draw_arrow(b
, b
- v
, aht
, lt
,
1703 graphic_object::get_outline_color());
1704 out
->line(b
, &v
, 1, lt
);
1708 double theta
= aht
.height
/ rad
;
1712 b
= position(b
.x
*cos(theta
) - b
.y
*sin(theta
),
1713 b
.x
*sin(theta
) + b
.y
*cos(theta
)) + cent
;
1718 if (aht
.solid
&& out
->supports_filled_polygons()) {
1719 draw_arrow(en
, en
- b
, aht
, lt
,
1720 graphic_object::get_outline_color());
1723 theta
= fabs(lt
.thickness
) / 72 / 4 / rad
;
1727 b
= position(b
.x
*cos(theta
) - b
.y
*sin(theta
),
1728 b
.x
*sin(theta
) + b
.y
*cos(theta
)) + cent
;
1729 draw_arrow(b
, b
- v
, aht
, lt
,
1730 graphic_object::get_outline_color());
1731 out
->line(b
, &v
, 1, lt
);
1734 out
->arc(sp
, cent
, ep
, lt
);
1738 inline double max(double a
, double b
)
1740 return a
> b
? a
: b
;
1743 void arc_object::update_bounding_box(bounding_box
*p
)
1747 position start_offset
= strt
- cent
;
1748 if (start_offset
.x
== 0.0 && start_offset
.y
== 0.0)
1750 position end_offset
= en
- cent
;
1751 if (end_offset
.x
== 0.0 && end_offset
.y
== 0.0)
1753 double start_quad
= atan2(start_offset
.y
, start_offset
.x
)/(M_PI
/2.0);
1754 double end_quad
= atan2(end_offset
.y
, end_offset
.x
)/(M_PI
/2.0);
1756 double temp
= start_quad
;
1757 start_quad
= end_quad
;
1760 if (start_quad
< 0.0)
1762 while (end_quad
<= start_quad
)
1764 double r
= max(hypot(start_offset
), hypot(end_offset
));
1765 for (int q
= int(start_quad
) + 1; q
< end_quad
; q
++) {
1781 p
->encompass(cent
+ offset
);
1785 // We ignore the with attribute. The at attribute always refers to the center.
1787 linear_object
*object_spec::make_arc(position
*curpos
, direction
*dirp
)
1790 int cw
= (flags
& IS_CLOCKWISE
) != 0;
1791 // compute the start
1793 if (flags
& HAS_FROM
)
1797 if (!(flags
& HAS_RADIUS
))
1798 lookup_variable("arcrad", &radius
);
1804 position
m(radius
, radius
);
1805 // Adjust the signs.
1807 if (dir
== DOWN_DIRECTION
|| dir
== LEFT_DIRECTION
)
1809 if (dir
== DOWN_DIRECTION
|| dir
== RIGHT_DIRECTION
)
1811 *dirp
= direction((dir
+ 3) % 4);
1814 if (dir
== UP_DIRECTION
|| dir
== LEFT_DIRECTION
)
1816 if (dir
== DOWN_DIRECTION
|| dir
== LEFT_DIRECTION
)
1818 *dirp
= direction((dir
+ 1) % 4);
1820 endpos
= startpos
+ m
;
1822 // compute the center
1826 else if (startpos
== endpos
)
1827 centerpos
= startpos
;
1829 position h
= (endpos
- startpos
)/2.0;
1830 double d
= hypot(h
);
1833 // make the radius big enough
1836 double alpha
= acos(d
/radius
);
1837 double theta
= atan2(h
.y
, h
.x
);
1842 centerpos
= position(cos(theta
), sin(theta
))*radius
+ startpos
;
1844 arc_object
*p
= new arc_object(cw
, startpos
, endpos
, centerpos
);
1849 graphic_object
*object_spec::make_linear(position
*curpos
, direction
*dirp
)
1852 if (type
== ARC_OBJECT
)
1853 obj
= make_arc(curpos
, dirp
);
1855 obj
= make_line(curpos
, dirp
);
1856 if (type
== ARROW_OBJECT
1857 && (flags
& (HAS_LEFT_ARROW_HEAD
|HAS_RIGHT_ARROW_HEAD
)) == 0)
1858 flags
|= HAS_RIGHT_ARROW_HEAD
;
1859 if (obj
&& (flags
& (HAS_LEFT_ARROW_HEAD
|HAS_RIGHT_ARROW_HEAD
))) {
1861 int at_start
= (flags
& HAS_LEFT_ARROW_HEAD
) != 0;
1862 int at_end
= (flags
& HAS_RIGHT_ARROW_HEAD
) != 0;
1863 if (flags
& HAS_HEIGHT
)
1866 lookup_variable("arrowht", &a
.height
);
1867 if (flags
& HAS_WIDTH
)
1870 lookup_variable("arrowwid", &a
.width
);
1872 lookup_variable("arrowhead", &solid
);
1873 a
.solid
= solid
!= 0.0;
1874 obj
->add_arrows(at_start
, at_end
, a
);
1879 object
*object_spec::make_object(position
*curpos
, direction
*dirp
)
1881 graphic_object
*obj
= 0;
1884 obj
= make_block(curpos
, dirp
);
1887 obj
= make_box(curpos
, dirp
);
1890 obj
= make_text(curpos
, dirp
);
1892 case ELLIPSE_OBJECT
:
1893 obj
= make_ellipse(curpos
, dirp
);
1896 obj
= make_circle(curpos
, dirp
);
1899 obj
= make_move(curpos
, dirp
);
1905 obj
= make_linear(curpos
, dirp
);
1914 if (flags
& IS_INVISIBLE
)
1915 obj
->set_invisible();
1917 obj
->add_text(text
, (flags
& IS_ALIGNED
) != 0);
1918 if (flags
& IS_DOTTED
)
1919 obj
->set_dotted(dash_width
);
1920 else if (flags
& IS_DASHED
)
1921 obj
->set_dashed(dash_width
);
1923 if (flags
& HAS_THICKNESS
)
1926 lookup_variable("linethick", &th
);
1927 obj
->set_thickness(th
);
1928 if (flags
& IS_OUTLINED
)
1929 obj
->set_outline_color(outlined
);
1930 if (flags
& IS_XSLANTED
)
1931 obj
->set_xslanted(xslanted
);
1932 if (flags
& IS_YSLANTED
)
1933 obj
->set_yslanted(yslanted
);
1934 if (flags
& (IS_DEFAULT_FILLED
| IS_FILLED
)) {
1935 if (flags
& IS_SHADED
)
1936 obj
->set_fill_color(shaded
);
1938 if (flags
& IS_DEFAULT_FILLED
)
1939 lookup_variable("fillval", &fill
);
1941 error("bad fill value %1", fill
);
1943 obj
->set_fill(fill
);
1950 struct string_list
{
1953 string_list(char *);
1957 string_list::string_list(char *s
)
1962 string_list::~string_list()
1967 /* A path is used to hold the argument to the `with' attribute. For
1968 example, `.nw' or `.A.s' or `.A'. The major operation on a path is to
1969 take a place and follow the path through the place to place within the
1970 place. Note that `.A.B.C.sw' will work.
1972 For compatibility with DWB pic, `with' accepts positions also (this
1973 is incorrectly documented in CSTR 116). */
1975 path::path(corner c
)
1976 : crn(c
), label_list(0), ypath(0), is_position(0)
1980 path::path(position p
)
1981 : crn(0), label_list(0), ypath(0), is_position(1)
1987 path::path(char *l
, corner c
)
1988 : crn(c
), ypath(0), is_position(0)
1990 label_list
= new string_list(l
);
1995 while (label_list
) {
1996 string_list
*tem
= label_list
;
1997 label_list
= label_list
->next
;
2003 void path::append(corner c
)
2009 void path::append(char *s
)
2012 for (p
= &label_list
; *p
; p
= &(*p
)->next
)
2014 *p
= new string_list(s
);
2017 void path::set_ypath(path
*p
)
2022 // return non-zero for success
2024 int path::follow(const place
&pl
, place
*result
) const
2032 const place
*p
= &pl
;
2033 for (string_list
*lb
= label_list
; lb
; lb
= lb
->next
)
2034 if (p
->obj
== 0 || (p
= p
->obj
->find_label(lb
->str
)) == 0) {
2035 lex_error("object does not contain a place `%1'", lb
->str
);
2038 if (crn
== 0 || p
->obj
== 0)
2041 position ps
= ((p
->obj
)->*(crn
))();
2048 if (!ypath
->follow(pl
, &tem
))
2051 if (result
->obj
!= tem
.obj
)
2057 void print_object_list(object
*p
)
2059 for (; p
; p
= p
->next
) {
2065 void print_picture(object
*obj
)
2068 for (object
*p
= obj
; p
; p
= p
->next
)
2069 p
->update_bounding_box(&bb
);
2071 lookup_variable("scale", &scale
);
2072 out
->start_picture(scale
, bb
.ll
, bb
.ur
);
2073 print_object_list(obj
);
2074 out
->finish_picture();