2 * Copyright (c) 2014 - 2017 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
4 * Copyright (C) 1989 - 1992, 2001 - 2007 Free Software Foundation, Inc.
5 * Written by James Clark (jjc@jclark.com)
7 * This is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2, or (at your option) any later
12 * This is distributed in the hope that it will be useful, but WITHOUT ANY
13 * WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 * You should have received a copy of the GNU General Public License along
18 * with groff; see the file COPYING. If not, write to the Free Software
19 * Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA.
23 #include "pic-config.h"
29 void print_object_list(object
*);
31 line_type::line_type()
32 : type(solid
), thickness(1.0)
36 output::output() : args(0), desired_height(0.0), desired_width(0.0)
45 void output::set_desired_width_height(double wid
, double ht
)
51 void output::set_args(const char *s
)
54 if (s
== 0 || *s
== '\0')
60 int output::supports_filled_polygons()
65 void output::begin_block(const position
&, const position
&)
69 void output::end_block()
73 double output::compute_scale(double sc
, const position
&ll
, const position
&ur
)
75 distance dim
= ur
- ll
;
76 if (desired_width
!= 0.0 || desired_height
!= 0.0) {
78 if (desired_width
!= 0.0) {
80 error("width specified for picture with zero width");
82 sc
= dim
.x
/desired_width
;
84 if (desired_height
!= 0.0) {
86 error("height specified for picture with zero height");
88 double tem
= dim
.y
/desired_height
;
93 return sc
== 0.0 ? 1.0 : sc
;
98 distance sdim
= dim
/sc
;
99 double max_width
= 0.0;
100 lookup_variable("maxpswid", &max_width
);
101 double max_height
= 0.0;
102 lookup_variable("maxpsht", &max_height
);
103 if ((max_width
> 0.0 && sdim
.x
> max_width
)
104 || (max_height
> 0.0 && sdim
.y
> max_height
)) {
105 double xscale
= dim
.x
/max_width
;
106 double yscale
= dim
.y
/max_height
;
107 return xscale
> yscale
? xscale
: yscale
;
114 position::position(const place
&pl
)
117 // Use two statements to work around bug in SGI C++.
118 object
*tem
= pl
.obj
;
119 *this = tem
->origin();
127 position::position() : x(0.0), y(0.0)
131 position::position(double a
, double b
) : x(a
), y(b
)
135 int operator==(const position
&a
, const position
&b
)
137 return a
.x
== b
.x
&& a
.y
== b
.y
;
140 int operator!=(const position
&a
, const position
&b
)
142 return a
.x
!= b
.x
|| a
.y
!= b
.y
;
145 position
&position::operator+=(const position
&a
)
152 position
&position::operator-=(const position
&a
)
159 position
&position::operator*=(double a
)
166 position
&position::operator/=(double a
)
173 position
operator-(const position
&a
)
175 return position(-a
.x
, -a
.y
);
178 position
operator+(const position
&a
, const position
&b
)
180 return position(a
.x
+ b
.x
, a
.y
+ b
.y
);
183 position
operator-(const position
&a
, const position
&b
)
185 return position(a
.x
- b
.x
, a
.y
- b
.y
);
188 position
operator/(const position
&a
, double n
)
190 return position(a
.x
/n
, a
.y
/n
);
193 position
operator*(const position
&a
, double n
)
195 return position(a
.x
*n
, a
.y
*n
);
200 double operator*(const position
&a
, const position
&b
)
202 return a
.x
*b
.x
+ a
.y
*b
.y
;
205 double hypot(const position
&a
)
207 return groff_hypot(a
.x
, a
.y
);
210 struct arrow_head_type
{
216 void draw_arrow(const position
&pos
, const distance
&dir
,
217 const arrow_head_type
&aht
, const line_type
<
,
218 char *outline_color_for_fill
)
220 double hyp
= hypot(dir
);
222 error("cannot draw arrow on object with zero length");
225 position base
= -dir
;
226 base
*= aht
.height
/hyp
;
227 position
n(dir
.y
, -dir
.x
);
228 n
*= aht
.width
/(hyp
*2.0);
230 slt
.type
= line_type::solid
;
231 if (aht
.solid
&& out
->supports_filled_polygons()) {
234 v
[1] = pos
+ base
+ n
;
235 v
[2] = pos
+ base
- n
;
236 // fill with outline color
237 out
->set_color(outline_color_for_fill
, outline_color_for_fill
);
238 // make stroke thin to avoid arrow sticking
240 out
->polygon(v
, 3, slt
, 1);
243 // use two line segments to avoid arrow sticking
244 out
->line(pos
+ base
- n
, &pos
, 1, slt
);
245 out
->line(pos
+ base
+ n
, &pos
, 1, slt
);
249 object::object() : prev(0), next(0)
257 void object::move_by(const position
&)
265 void object::print_text()
274 struct bounding_box
{
280 void encompass(const position
&);
283 bounding_box::bounding_box()
288 void bounding_box::encompass(const position
&pos
)
307 void object::update_bounding_box(bounding_box
*)
311 position
object::origin()
313 return position(0.0,0.0);
316 position
object::north()
321 position
object::south()
326 position
object::east()
331 position
object::west()
336 position
object::north_east()
341 position
object::north_west()
346 position
object::south_east()
351 position
object::south_west()
356 position
object::start()
361 position
object::end()
366 position
object::center()
371 double object::width()
376 double object::radius()
381 double object::height()
386 place
*object::find_label(const char *)
391 segment::segment(const position
&a
, int n
, segment
*p
)
392 : is_absolute(n
), pos(a
), next(p
)
396 text_item::text_item(char *t
, const char *fn
, int ln
)
397 : next(0), text(t
), filename(fn
), lineno(ln
)
399 adj
.h
= CENTER_ADJUST
;
403 text_item::~text_item()
408 object_spec::object_spec(object_type t
) : type(t
)
413 segment_width
= segment_height
= 0.0;
414 segment_is_absolute
= 0;
421 dir
= RIGHT_DIRECTION
;
424 object_spec::~object_spec()
427 while (segment_list
!= 0) {
428 segment
*tem
= segment_list
;
429 segment_list
= segment_list
->next
;
432 object
*p
= oblist
.head
;
439 text_item
*tem
= text
;
452 const char *filename
;
456 command_object(char *, const char *, int);
458 object_type
type() { return OTHER_OBJECT
; }
462 command_object::command_object(char *p
, const char *fn
, int ln
)
463 : s(p
), filename(fn
), lineno(ln
)
467 command_object::~command_object()
472 void command_object::print()
474 out
->command(s
, filename
, lineno
);
477 object
*make_command_object(char *s
, const char *fn
, int ln
)
479 return new command_object(s
, fn
, ln
);
490 object
*make_mark_object()
492 return new mark_object();
495 mark_object::mark_object()
499 object_type
mark_object::type()
504 object_list::object_list() : head(0), tail(0)
508 void object_list::append(object
*obj
)
511 obj
->next
= obj
->prev
= 0;
522 void object_list::wrap_up_block(object_list
*ol
)
525 for (p
= tail
; p
&& p
->type() != MARK_OBJECT
; p
= p
->prev
)
543 text_piece::text_piece()
544 : text(0), filename(0), lineno(-1)
546 adj
.h
= CENTER_ADJUST
;
550 text_piece::~text_piece()
570 object_type
type() = 0;
572 void add_text(text_item
*, int);
573 void set_dotted(double);
574 void set_dashed(double);
575 void set_thickness(double);
576 void set_invisible();
577 void set_outline_color(char *);
578 char *get_outline_color();
579 virtual void set_fill(double);
580 virtual void set_xslanted(double);
581 virtual void set_yslanted(double);
582 virtual void set_fill_color(char *);
585 graphic_object::graphic_object()
586 : ntext(0), text(0), aligned(0), outline_color(0), color_fill(0)
590 void graphic_object::set_dotted(double wid
)
592 lt
.type
= line_type::dotted
;
596 void graphic_object::set_dashed(double wid
)
598 lt
.type
= line_type::dashed
;
602 void graphic_object::set_thickness(double th
)
607 void graphic_object::set_fill(double)
611 void graphic_object::set_xslanted(double)
615 void graphic_object::set_yslanted(double)
619 void graphic_object::set_fill_color(char *c
)
621 color_fill
= strsave(c
);
624 void graphic_object::set_outline_color(char *c
)
626 outline_color
= strsave(c
);
629 char *graphic_object::get_outline_color()
631 return outline_color
;
634 void graphic_object::set_invisible()
636 lt
.type
= line_type::invisible
;
639 void graphic_object::add_text(text_item
*t
, int a
)
644 for (p
= t
; p
; p
= p
->next
)
649 text
= new text_piece
[len
];
650 for (p
= t
, len
= 0; p
; p
= p
->next
, len
++) {
651 text
[len
].text
= p
->text
;
653 text
[len
].adj
= p
->adj
;
654 text
[len
].filename
= p
->filename
;
655 text
[len
].lineno
= p
->lineno
;
661 void graphic_object::print_text()
665 position
d(end() - start());
666 if (d
.x
!= 0.0 || d
.y
!= 0.0)
667 angle
= atan2(d
.y
, d
.x
);
670 out
->set_color(color_fill
, get_outline_color());
671 out
->text(center(), text
, ntext
, angle
);
676 graphic_object::~graphic_object()
679 ad_delete(ntext
) text
;
682 class rectangle_object
683 : public graphic_object
690 rectangle_object(const position
&);
691 double width() { return dim
.x
; }
692 double height() { return dim
.y
; }
693 position
origin() { return cent
; }
694 position
center() { return cent
; }
695 position
north() { return position(cent
.x
, cent
.y
+ dim
.y
/2.0); }
696 position
south() { return position(cent
.x
, cent
.y
- dim
.y
/2.0); }
697 position
east() { return position(cent
.x
+ dim
.x
/2.0, cent
.y
); }
698 position
west() { return position(cent
.x
- dim
.x
/2.0, cent
.y
); }
699 position
north_east() { return position(cent
.x
+ dim
.x
/2.0, cent
.y
+ dim
.y
/2.0); }
700 position
north_west() { return position(cent
.x
- dim
.x
/2.0, cent
.y
+ dim
.y
/2.0); }
701 position
south_east() { return position(cent
.x
+ dim
.x
/2.0, cent
.y
- dim
.y
/2.0); }
702 position
south_west() { return position(cent
.x
- dim
.x
/2.0, cent
.y
- dim
.y
/2.0); }
703 object_type
type() = 0;
704 void update_bounding_box(bounding_box
*);
705 void move_by(const position
&);
708 rectangle_object::rectangle_object(const position
&d
)
713 void rectangle_object::update_bounding_box(bounding_box
*p
)
715 p
->encompass(cent
- dim
/2.0);
716 p
->encompass(cent
+ dim
/2.0);
719 void rectangle_object::move_by(const position
&a
)
725 : public rectangle_object
728 double fill
; // < 0 if not filled
729 double xslanted
; // !=0 if x is slanted
730 double yslanted
; // !=0 if y is slanted
731 char *color_fill
; // = 0 if not colored
734 closed_object(const position
&);
735 object_type
type() = 0;
736 void set_fill(double);
737 void set_xslanted(double);
738 void set_yslanted(double);
739 void set_fill_color(char *fill
);
742 closed_object::closed_object(const position
&pos
)
743 : rectangle_object(pos
), fill(-1.0), xslanted(0), yslanted(0), color_fill(0)
747 void closed_object::set_fill(double f
)
753 /* accept positive and negative values */
754 void closed_object::set_xslanted(double s
)
759 /* accept positive and negative values */
760 void closed_object::set_yslanted(double s
)
766 void closed_object::set_fill_color(char *f
)
768 color_fill
= strsave(f
);
772 : public closed_object
778 box_object(const position
&, double);
779 object_type
type() { return BOX_OBJECT
; }
781 position
north_east();
782 position
north_west();
783 position
south_east();
784 position
south_west();
787 box_object::box_object(const position
&pos
, double r
)
788 : closed_object(pos
), xrad(dim
.x
> 0 ? r
: -r
), yrad(dim
.y
> 0 ? r
: -r
)
792 const double CHOP_FACTOR
= 1.0 - 1.0/M_SQRT2
;
794 position
box_object::north_east()
796 return position(cent
.x
+ dim
.x
/2.0 - CHOP_FACTOR
*xrad
,
797 cent
.y
+ dim
.y
/2.0 - CHOP_FACTOR
*yrad
);
800 position
box_object::north_west()
802 return position(cent
.x
- dim
.x
/2.0 + CHOP_FACTOR
*xrad
,
803 cent
.y
+ dim
.y
/2.0 - CHOP_FACTOR
*yrad
);
806 position
box_object::south_east()
808 return position(cent
.x
+ dim
.x
/2.0 - CHOP_FACTOR
*xrad
,
809 cent
.y
- dim
.y
/2.0 + CHOP_FACTOR
*yrad
);
812 position
box_object::south_west()
814 return position(cent
.x
- dim
.x
/2.0 + CHOP_FACTOR
*xrad
,
815 cent
.y
- dim
.y
/2.0 + CHOP_FACTOR
*yrad
);
818 void box_object::print()
820 if (lt
.type
== line_type::invisible
&& fill
< 0.0 && color_fill
== 0)
822 out
->set_color(color_fill
, graphic_object::get_outline_color());
824 distance dim2
= dim
/2.0;
826 /* error("x slanted %1", xslanted); */
827 /* error("y slanted %1", yslanted); */
828 vec
[0] = cent
+ position(dim2
.x
, -(dim2
.y
- yslanted
)); /* lr */
829 vec
[1] = cent
+ position(dim2
.x
+ xslanted
, dim2
.y
+ yslanted
); /* ur */
830 vec
[2] = cent
+ position(-(dim2
.x
- xslanted
), dim2
.y
); /* ul */
831 vec
[3] = cent
+ position(-(dim2
.x
), -dim2
.y
); /* ll */
832 out
->polygon(vec
, 4, lt
, fill
);
835 distance
abs_dim(fabs(dim
.x
), fabs(dim
.y
));
836 out
->rounded_box(cent
, abs_dim
, fabs(xrad
), lt
, fill
, color_fill
);
841 graphic_object
*object_spec::make_box(position
*curpos
, direction
*dirp
)
843 static double last_box_height
;
844 static double last_box_width
;
845 static double last_box_radius
;
846 static int have_last_box
= 0;
847 if (!(flags
& HAS_HEIGHT
)) {
848 if ((flags
& IS_SAME
) && have_last_box
)
849 height
= last_box_height
;
851 lookup_variable("boxht", &height
);
853 if (!(flags
& HAS_WIDTH
)) {
854 if ((flags
& IS_SAME
) && have_last_box
)
855 width
= last_box_width
;
857 lookup_variable("boxwid", &width
);
859 if (!(flags
& HAS_RADIUS
)) {
860 if ((flags
& IS_SAME
) && have_last_box
)
861 radius
= last_box_radius
;
863 lookup_variable("boxrad", &radius
);
865 last_box_width
= width
;
866 last_box_height
= height
;
867 last_box_radius
= radius
;
869 radius
= fabs(radius
);
870 if (radius
*2.0 > fabs(width
))
871 radius
= fabs(width
/2.0);
872 if (radius
*2.0 > fabs(height
))
873 radius
= fabs(height
/2.0);
874 box_object
*p
= new box_object(position(width
, height
), radius
);
875 if (!position_rectangle(p
, curpos
, dirp
)) {
882 // return non-zero for success
884 int object_spec::position_rectangle(rectangle_object
*p
,
885 position
*curpos
, direction
*dirp
)
888 dir
= *dirp
; // ignore any direction in attribute list
892 motion
.y
= p
->height()/2.0;
895 motion
.y
= -p
->height()/2.0;
898 motion
.x
= -p
->width()/2.0;
900 case RIGHT_DIRECTION
:
901 motion
.x
= p
->width()/2.0;
906 if (flags
& HAS_AT
) {
908 if (flags
& HAS_WITH
) {
912 if (!with
->follow(here
, &offset
))
928 : public rectangle_object
934 block_object(const position
&, const object_list
&ol
, PTABLE(place
) *t
);
936 place
*find_label(const char *);
938 void move_by(const position
&);
942 block_object::block_object(const position
&d
, const object_list
&ol
,
944 : rectangle_object(d
), oblist(ol
), tbl(t
)
948 block_object::~block_object()
951 object
*p
= oblist
.head
;
959 void block_object::print()
961 out
->begin_block(south_west(), north_east());
962 print_object_list(oblist
.head
);
966 static void adjust_objectless_places(PTABLE(place
) *tbl
, const position
&a
)
968 // Adjust all the labels that aren't attached to objects.
969 PTABLE_ITERATOR(place
) iter(tbl
);
972 while (iter
.next(&key
, &pl
))
973 if (key
&& csupper(key
[0]) && pl
->obj
== 0) {
979 void block_object::move_by(const position
&a
)
982 for (object
*p
= oblist
.head
; p
; p
= p
->next
)
984 adjust_objectless_places(tbl
, a
);
987 place
*block_object::find_label(const char *name
)
989 return tbl
->lookup(name
);
992 object_type
block_object::type()
997 graphic_object
*object_spec::make_block(position
*curpos
, direction
*dirp
)
1000 for (object
*p
= oblist
.head
; p
; p
= p
->next
)
1001 p
->update_bounding_box(&bb
);
1004 position m
= -(bb
.ll
+ bb
.ur
)/2.0;
1005 for (object
*p
= oblist
.head
; p
; p
= p
->next
)
1007 adjust_objectless_places(tbl
, m
);
1008 dim
= bb
.ur
- bb
.ll
;
1010 if (flags
& HAS_WIDTH
)
1012 if (flags
& HAS_HEIGHT
)
1014 block_object
*block
= new block_object(dim
, oblist
, tbl
);
1015 if (!position_rectangle(block
, curpos
, dirp
)) {
1020 oblist
.head
= oblist
.tail
= 0;
1024 class text_object
: public rectangle_object
{
1026 text_object(const position
&);
1027 object_type
type() { return TEXT_OBJECT
; }
1030 text_object::text_object(const position
&d
)
1031 : rectangle_object(d
)
1035 graphic_object
*object_spec::make_text(position
*curpos
, direction
*dirp
)
1037 if (!(flags
& HAS_HEIGHT
)) {
1038 lookup_variable("textht", &height
);
1040 for (text_item
*t
= text
; t
; t
= t
->next
)
1044 if (!(flags
& HAS_WIDTH
))
1045 lookup_variable("textwid", &width
);
1046 text_object
*p
= new text_object(position(width
, height
));
1047 if (!position_rectangle(p
, curpos
, dirp
)) {
1054 class ellipse_object
1055 : public closed_object
1058 ellipse_object(const position
&);
1059 position
north_east() { return position(cent
.x
+ dim
.x
/(M_SQRT2
*2.0),
1060 cent
.y
+ dim
.y
/(M_SQRT2
*2.0)); }
1061 position
north_west() { return position(cent
.x
- dim
.x
/(M_SQRT2
*2.0),
1062 cent
.y
+ dim
.y
/(M_SQRT2
*2.0)); }
1063 position
south_east() { return position(cent
.x
+ dim
.x
/(M_SQRT2
*2.0),
1064 cent
.y
- dim
.y
/(M_SQRT2
*2.0)); }
1065 position
south_west() { return position(cent
.x
- dim
.x
/(M_SQRT2
*2.0),
1066 cent
.y
- dim
.y
/(M_SQRT2
*2.0)); }
1067 double radius() { return dim
.x
/2.0; }
1068 object_type
type() { return ELLIPSE_OBJECT
; }
1072 ellipse_object::ellipse_object(const position
&d
)
1077 void ellipse_object::print()
1079 if (lt
.type
== line_type::invisible
&& fill
< 0.0 && color_fill
== 0)
1081 out
->set_color(color_fill
, graphic_object::get_outline_color());
1082 out
->ellipse(cent
, dim
, lt
, fill
);
1086 graphic_object
*object_spec::make_ellipse(position
*curpos
, direction
*dirp
)
1088 static double last_ellipse_height
;
1089 static double last_ellipse_width
;
1090 static int have_last_ellipse
= 0;
1091 if (!(flags
& HAS_HEIGHT
)) {
1092 if ((flags
& IS_SAME
) && have_last_ellipse
)
1093 height
= last_ellipse_height
;
1095 lookup_variable("ellipseht", &height
);
1097 if (!(flags
& HAS_WIDTH
)) {
1098 if ((flags
& IS_SAME
) && have_last_ellipse
)
1099 width
= last_ellipse_width
;
1101 lookup_variable("ellipsewid", &width
);
1103 last_ellipse_width
= width
;
1104 last_ellipse_height
= height
;
1105 have_last_ellipse
= 1;
1106 ellipse_object
*p
= new ellipse_object(position(width
, height
));
1107 if (!position_rectangle(p
, curpos
, dirp
)) {
1115 : public ellipse_object
1118 circle_object(double);
1119 object_type
type() { return CIRCLE_OBJECT
; }
1123 circle_object::circle_object(double diam
)
1124 : ellipse_object(position(diam
, diam
))
1128 void circle_object::print()
1130 if (lt
.type
== line_type::invisible
&& fill
< 0.0 && color_fill
== 0)
1132 out
->set_color(color_fill
, graphic_object::get_outline_color());
1133 out
->circle(cent
, dim
.x
/2.0, lt
, fill
);
1137 graphic_object
*object_spec::make_circle(position
*curpos
, direction
*dirp
)
1139 static double last_circle_radius
;
1140 static int have_last_circle
= 0;
1141 if (!(flags
& HAS_RADIUS
)) {
1142 if ((flags
& IS_SAME
) && have_last_circle
)
1143 radius
= last_circle_radius
;
1145 lookup_variable("circlerad", &radius
);
1147 last_circle_radius
= radius
;
1148 have_last_circle
= 1;
1149 circle_object
*p
= new circle_object(radius
*2.0);
1150 if (!position_rectangle(p
, curpos
, dirp
)) {
1158 : public graphic_object
1164 move_object(const position
&s
, const position
&e
);
1165 position
origin() { return en
; }
1166 object_type
type() { return MOVE_OBJECT
; }
1167 void update_bounding_box(bounding_box
*);
1168 void move_by(const position
&);
1171 move_object::move_object(const position
&s
, const position
&e
)
1176 void move_object::update_bounding_box(bounding_box
*p
)
1182 void move_object::move_by(const position
&a
)
1188 graphic_object
*object_spec::make_move(position
*curpos
, direction
*dirp
)
1190 static position last_move
;
1191 static int have_last_move
= 0;
1193 // No need to look at at since `at' attribute sets `from' attribute.
1194 position startpos
= (flags
& HAS_FROM
) ? from
: *curpos
;
1195 if (!(flags
& HAS_SEGMENT
)) {
1196 if ((flags
& IS_SAME
) && have_last_move
)
1197 segment_pos
= last_move
;
1201 segment_pos
.y
= segment_height
;
1203 case DOWN_DIRECTION
:
1204 segment_pos
.y
= -segment_height
;
1206 case LEFT_DIRECTION
:
1207 segment_pos
.x
= -segment_width
;
1209 case RIGHT_DIRECTION
:
1210 segment_pos
.x
= segment_width
;
1217 segment_list
= new segment(segment_pos
, segment_is_absolute
, segment_list
);
1218 // Reverse the segment_list so that it's in forward order.
1219 segment
*old
= segment_list
;
1222 segment
*tem
= old
->next
;
1223 old
->next
= segment_list
;
1227 // Compute the end position.
1228 position endpos
= startpos
;
1229 for (segment
*s
= segment_list
; s
; s
= s
->next
)
1235 last_move
= endpos
- startpos
;
1236 move_object
*p
= new move_object(startpos
, endpos
);
1242 : public graphic_object
1245 char arrow_at_start
;
1247 arrow_head_type aht
;
1252 linear_object(const position
&s
, const position
&e
);
1253 position
start() { return strt
; }
1254 position
end() { return en
; }
1255 void move_by(const position
&);
1256 void update_bounding_box(bounding_box
*) = 0;
1257 object_type
type() = 0;
1258 void add_arrows(int at_start
, int at_end
, const arrow_head_type
&);
1262 : public linear_object
1269 line_object(const position
&s
, const position
&e
, position
*, int);
1271 position
origin() { return strt
; }
1272 position
center() { return (strt
+ en
)/2.0; }
1273 position
north() { return (en
.y
- strt
.y
) > 0 ? en
: strt
; }
1274 position
south() { return (en
.y
- strt
.y
) < 0 ? en
: strt
; }
1275 position
east() { return (en
.x
- strt
.x
) > 0 ? en
: strt
; }
1276 position
west() { return (en
.x
- strt
.x
) < 0 ? en
: strt
; }
1277 object_type
type() { return LINE_OBJECT
; }
1278 void update_bounding_box(bounding_box
*);
1280 void move_by(const position
&);
1284 : public line_object
1287 arrow_object(const position
&, const position
&, position
*, int);
1288 object_type
type() { return ARROW_OBJECT
; }
1292 : public line_object
1295 spline_object(const position
&, const position
&, position
*, int);
1296 object_type
type() { return SPLINE_OBJECT
; }
1298 void update_bounding_box(bounding_box
*);
1301 linear_object::linear_object(const position
&s
, const position
&e
)
1302 : arrow_at_start(0), arrow_at_end(0), strt(s
), en(e
)
1306 void linear_object::move_by(const position
&a
)
1312 void linear_object::add_arrows(int at_start
, int at_end
,
1313 const arrow_head_type
&a
)
1315 arrow_at_start
= at_start
;
1316 arrow_at_end
= at_end
;
1320 line_object::line_object(const position
&s
, const position
&e
,
1322 : linear_object(s
, e
), v(p
), n(i
)
1326 void line_object::print()
1328 if (lt
.type
== line_type::invisible
)
1330 out
->set_color(0, graphic_object::get_outline_color());
1331 // shorten line length to avoid arrow sticking.
1333 if (arrow_at_start
) {
1334 position base
= v
[0] - strt
;
1335 double hyp
= hypot(base
);
1337 error("cannot draw arrow on object with zero length");
1340 if (aht
.solid
&& out
->supports_filled_polygons()) {
1341 base
*= aht
.height
/ hyp
;
1342 draw_arrow(strt
, strt
- v
[0], aht
, lt
,
1343 graphic_object::get_outline_color());
1346 base
*= fabs(lt
.thickness
) / hyp
/ 72 / 4;
1348 draw_arrow(sp
, sp
- v
[0], aht
, lt
,
1349 graphic_object::get_outline_color());
1353 position base
= v
[n
-1] - (n
> 1 ? v
[n
-2] : strt
);
1354 double hyp
= hypot(base
);
1356 error("cannot draw arrow on object with zero length");
1359 if (aht
.solid
&& out
->supports_filled_polygons()) {
1360 base
*= aht
.height
/ hyp
;
1361 draw_arrow(en
, v
[n
-1] - (n
> 1 ? v
[n
-2] : strt
), aht
, lt
,
1362 graphic_object::get_outline_color());
1365 base
*= fabs(lt
.thickness
) / hyp
/ 72 / 4;
1367 draw_arrow(v
[n
-1], v
[n
-1] - (n
> 1 ? v
[n
-2] : strt
), aht
, lt
,
1368 graphic_object::get_outline_color());
1371 out
->line(sp
, v
, n
, lt
);
1375 void line_object::update_bounding_box(bounding_box
*p
)
1378 for (int i
= 0; i
< n
; i
++)
1382 void line_object::move_by(const position
&pos
)
1384 linear_object::move_by(pos
);
1385 for (int i
= 0; i
< n
; i
++)
1389 void spline_object::update_bounding_box(bounding_box
*p
)
1401 [ the points for the Bezier cubic ]
1409 (1-t)^3*p1 + 3*t*(t - 1)^2*p2 + 3*t^2*(1-t)*p3 + t^3*p4
1410 [ the equation for the Bezier cubic ]
1412 = .125*q1 + .75*q2 + .125*q3
1415 for (int i
= 1; i
< n
; i
++)
1416 p
->encompass((i
== 1 ? strt
: v
[i
-2])*.125 + v
[i
-1]*.75 + v
[i
]*.125);
1419 arrow_object::arrow_object(const position
&s
, const position
&e
,
1421 : line_object(s
, e
, p
, i
)
1425 spline_object::spline_object(const position
&s
, const position
&e
,
1427 : line_object(s
, e
, p
, i
)
1431 void spline_object::print()
1433 if (lt
.type
== line_type::invisible
)
1435 out
->set_color(0, graphic_object::get_outline_color());
1436 // shorten line length for spline to avoid arrow sticking
1438 if (arrow_at_start
) {
1439 position base
= v
[0] - strt
;
1440 double hyp
= hypot(base
);
1442 error("cannot draw arrow on object with zero length");
1445 if (aht
.solid
&& out
->supports_filled_polygons()) {
1446 base
*= aht
.height
/ hyp
;
1447 draw_arrow(strt
, strt
- v
[0], aht
, lt
,
1448 graphic_object::get_outline_color());
1449 sp
= strt
+ base
*0.1; // to reserve spline shape
1451 base
*= fabs(lt
.thickness
) / hyp
/ 72 / 4;
1453 draw_arrow(sp
, sp
- v
[0], aht
, lt
,
1454 graphic_object::get_outline_color());
1458 position base
= v
[n
-1] - (n
> 1 ? v
[n
-2] : strt
);
1459 double hyp
= hypot(base
);
1461 error("cannot draw arrow on object with zero length");
1464 if (aht
.solid
&& out
->supports_filled_polygons()) {
1465 base
*= aht
.height
/ hyp
;
1466 draw_arrow(en
, v
[n
-1] - (n
> 1 ? v
[n
-2] : strt
), aht
, lt
,
1467 graphic_object::get_outline_color());
1468 v
[n
-1] = en
- base
*0.1; // to reserve spline shape
1470 base
*= fabs(lt
.thickness
) / hyp
/ 72 / 4;
1472 draw_arrow(v
[n
-1], v
[n
-1] - (n
> 1 ? v
[n
-2] : strt
), aht
, lt
,
1473 graphic_object::get_outline_color());
1476 out
->spline(sp
, v
, n
, lt
);
1480 line_object::~line_object()
1485 linear_object
*object_spec::make_line(position
*curpos
, direction
*dirp
)
1487 static position last_line
;
1488 static int have_last_line
= 0;
1490 // We handle `at' only in conjunction with `with', otherwise it is
1491 // the same as the `from' attribute.
1493 if ((flags
& HAS_AT
) && (flags
& HAS_WITH
))
1494 // handled later -- we need the end position
1496 else if (flags
& HAS_FROM
)
1500 if (!(flags
& HAS_SEGMENT
)) {
1501 if ((flags
& IS_SAME
) && (type
== LINE_OBJECT
|| type
== ARROW_OBJECT
)
1503 segment_pos
= last_line
;
1507 segment_pos
.y
= segment_height
;
1509 case DOWN_DIRECTION
:
1510 segment_pos
.y
= -segment_height
;
1512 case LEFT_DIRECTION
:
1513 segment_pos
.x
= -segment_width
;
1515 case RIGHT_DIRECTION
:
1516 segment_pos
.x
= segment_width
;
1522 segment_list
= new segment(segment_pos
, segment_is_absolute
, segment_list
);
1523 // reverse the segment_list so that it's in forward order
1524 segment
*old
= segment_list
;
1527 segment
*tem
= old
->next
;
1528 old
->next
= segment_list
;
1532 // Absolutise all movements
1533 position endpos
= startpos
;
1536 for (s
= segment_list
; s
; s
= s
->next
, nsegments
++)
1542 s
->is_absolute
= 1; // to avoid confusion
1544 if ((flags
& HAS_AT
) && (flags
& HAS_WITH
)) {
1545 // `tmpobj' works for arrows and splines too -- we only need positions
1546 line_object
tmpobj(startpos
, endpos
, 0, 0);
1551 if (!with
->follow(here
, &offset
))
1554 for (s
= segment_list
; s
; s
= s
->next
)
1561 position
*v
= new position
[nsegments
];
1563 for (s
= segment_list
; s
; s
= s
->next
, i
++)
1565 if (flags
& IS_DEFAULT_CHOPPED
) {
1566 lookup_variable("circlerad", &start_chop
);
1567 end_chop
= start_chop
;
1568 flags
|= IS_CHOPPED
;
1570 if (flags
& IS_CHOPPED
) {
1571 position start_chop_vec
, end_chop_vec
;
1572 if (start_chop
!= 0.0) {
1573 start_chop_vec
= v
[0] - startpos
;
1574 start_chop_vec
*= start_chop
/ hypot(start_chop_vec
);
1576 if (end_chop
!= 0.0) {
1577 end_chop_vec
= (v
[nsegments
- 1]
1578 - (nsegments
> 1 ? v
[nsegments
- 2] : startpos
));
1579 end_chop_vec
*= end_chop
/ hypot(end_chop_vec
);
1581 startpos
+= start_chop_vec
;
1582 v
[nsegments
- 1] -= end_chop_vec
;
1583 endpos
-= end_chop_vec
;
1587 p
= new spline_object(startpos
, endpos
, v
, nsegments
);
1590 p
= new arrow_object(startpos
, endpos
, v
, nsegments
);
1593 p
= new line_object(startpos
, endpos
, v
, nsegments
);
1599 last_line
= endpos
- startpos
;
1605 : public linear_object
1612 arc_object(int, const position
&, const position
&, const position
&);
1613 position
origin() { return cent
; }
1614 position
center() { return cent
; }
1615 double radius() { return rad
; }
1620 position
north_east();
1621 position
north_west();
1622 position
south_east();
1623 position
south_west();
1624 void update_bounding_box(bounding_box
*);
1625 object_type
type() { return ARC_OBJECT
; }
1627 void move_by(const position
&pos
);
1630 arc_object::arc_object(int cw
, const position
&s
, const position
&e
,
1632 : linear_object(s
, e
), clockwise(cw
), cent(c
)
1637 void arc_object::move_by(const position
&pos
)
1639 linear_object::move_by(pos
);
1643 // we get arc corners from the corresponding circle
1645 position
arc_object::north()
1647 position
result(cent
);
1652 position
arc_object::south()
1654 position
result(cent
);
1659 position
arc_object::east()
1661 position
result(cent
);
1666 position
arc_object::west()
1668 position
result(cent
);
1673 position
arc_object::north_east()
1675 position
result(cent
);
1676 result
.x
+= rad
/M_SQRT2
;
1677 result
.y
+= rad
/M_SQRT2
;
1681 position
arc_object::north_west()
1683 position
result(cent
);
1684 result
.x
-= rad
/M_SQRT2
;
1685 result
.y
+= rad
/M_SQRT2
;
1689 position
arc_object::south_east()
1691 position
result(cent
);
1692 result
.x
+= rad
/M_SQRT2
;
1693 result
.y
-= rad
/M_SQRT2
;
1697 position
arc_object::south_west()
1699 position
result(cent
);
1700 result
.x
-= rad
/M_SQRT2
;
1701 result
.y
-= rad
/M_SQRT2
;
1705 void arc_object::print()
1707 if (lt
.type
== line_type::invisible
)
1709 out
->set_color(0, graphic_object::get_outline_color());
1710 // handle arrow direction; make shorter line for arc
1719 if (arrow_at_start
) {
1720 double theta
= aht
.height
/ rad
;
1724 b
= position(b
.x
*cos(theta
) - b
.y
*sin(theta
),
1725 b
.x
*sin(theta
) + b
.y
*cos(theta
)) + cent
;
1730 if (aht
.solid
&& out
->supports_filled_polygons()) {
1731 draw_arrow(strt
, strt
- b
, aht
, lt
,
1732 graphic_object::get_outline_color());
1735 theta
= fabs(lt
.thickness
) / 72 / 4 / rad
;
1739 b
= position(b
.x
*cos(theta
) - b
.y
*sin(theta
),
1740 b
.x
*sin(theta
) + b
.y
*cos(theta
)) + cent
;
1741 draw_arrow(b
, b
- v
, aht
, lt
,
1742 graphic_object::get_outline_color());
1743 out
->line(b
, &v
, 1, lt
);
1747 double theta
= aht
.height
/ rad
;
1751 b
= position(b
.x
*cos(theta
) - b
.y
*sin(theta
),
1752 b
.x
*sin(theta
) + b
.y
*cos(theta
)) + cent
;
1757 if (aht
.solid
&& out
->supports_filled_polygons()) {
1758 draw_arrow(en
, en
- b
, aht
, lt
,
1759 graphic_object::get_outline_color());
1762 theta
= fabs(lt
.thickness
) / 72 / 4 / rad
;
1766 b
= position(b
.x
*cos(theta
) - b
.y
*sin(theta
),
1767 b
.x
*sin(theta
) + b
.y
*cos(theta
)) + cent
;
1768 draw_arrow(b
, b
- v
, aht
, lt
,
1769 graphic_object::get_outline_color());
1770 out
->line(b
, &v
, 1, lt
);
1773 out
->arc(sp
, cent
, ep
, lt
);
1777 inline double max(double a
, double b
)
1779 return a
> b
? a
: b
;
1782 void arc_object::update_bounding_box(bounding_box
*p
)
1786 position start_offset
= strt
- cent
;
1787 if (start_offset
.x
== 0.0 && start_offset
.y
== 0.0)
1789 position end_offset
= en
- cent
;
1790 if (end_offset
.x
== 0.0 && end_offset
.y
== 0.0)
1792 double start_quad
= atan2(start_offset
.y
, start_offset
.x
)/(M_PI
/2.0);
1793 double end_quad
= atan2(end_offset
.y
, end_offset
.x
)/(M_PI
/2.0);
1795 double temp
= start_quad
;
1796 start_quad
= end_quad
;
1799 if (start_quad
< 0.0)
1801 while (end_quad
<= start_quad
)
1803 double r
= max(hypot(start_offset
), hypot(end_offset
));
1804 for (int q
= int(start_quad
) + 1; q
< end_quad
; q
++) {
1820 p
->encompass(cent
+ offset
);
1824 // We ignore the width attribute. The at attribute always refers to the center.
1826 linear_object
*object_spec::make_arc(position
*curpos
, direction
*dirp
)
1829 int cw
= (flags
& IS_CLOCKWISE
) != 0;
1830 // compute the start
1832 if (flags
& HAS_FROM
)
1836 if (!(flags
& HAS_RADIUS
))
1837 lookup_variable("arcrad", &radius
);
1843 position
m(radius
, radius
);
1844 // Adjust the signs.
1846 if (dir
== DOWN_DIRECTION
|| dir
== LEFT_DIRECTION
)
1848 if (dir
== DOWN_DIRECTION
|| dir
== RIGHT_DIRECTION
)
1850 *dirp
= direction((dir
+ 3) % 4);
1853 if (dir
== UP_DIRECTION
|| dir
== LEFT_DIRECTION
)
1855 if (dir
== DOWN_DIRECTION
|| dir
== LEFT_DIRECTION
)
1857 *dirp
= direction((dir
+ 1) % 4);
1859 endpos
= startpos
+ m
;
1861 // compute the center
1865 else if (startpos
== endpos
)
1866 centerpos
= startpos
;
1868 position h
= (endpos
- startpos
)/2.0;
1869 double d
= hypot(h
);
1872 // make the radius big enough
1875 double alpha
= acos(d
/radius
);
1876 double theta
= atan2(h
.y
, h
.x
);
1881 centerpos
= position(cos(theta
), sin(theta
))*radius
+ startpos
;
1883 arc_object
*p
= new arc_object(cw
, startpos
, endpos
, centerpos
);
1888 graphic_object
*object_spec::make_linear(position
*curpos
, direction
*dirp
)
1891 if (type
== ARC_OBJECT
)
1892 obj
= make_arc(curpos
, dirp
);
1894 obj
= make_line(curpos
, dirp
);
1895 if (type
== ARROW_OBJECT
1896 && (flags
& (HAS_LEFT_ARROW_HEAD
|HAS_RIGHT_ARROW_HEAD
)) == 0)
1897 flags
|= HAS_RIGHT_ARROW_HEAD
;
1898 if (obj
&& (flags
& (HAS_LEFT_ARROW_HEAD
|HAS_RIGHT_ARROW_HEAD
))) {
1900 int at_start
= (flags
& HAS_LEFT_ARROW_HEAD
) != 0;
1901 int at_end
= (flags
& HAS_RIGHT_ARROW_HEAD
) != 0;
1902 if (flags
& HAS_HEIGHT
)
1905 lookup_variable("arrowht", &a
.height
);
1906 if (flags
& HAS_WIDTH
)
1909 lookup_variable("arrowwid", &a
.width
);
1911 lookup_variable("arrowhead", &solid
);
1912 a
.solid
= solid
!= 0.0;
1913 obj
->add_arrows(at_start
, at_end
, a
);
1918 object
*object_spec::make_object(position
*curpos
, direction
*dirp
)
1920 graphic_object
*obj
= 0;
1923 obj
= make_block(curpos
, dirp
);
1926 obj
= make_box(curpos
, dirp
);
1929 obj
= make_text(curpos
, dirp
);
1931 case ELLIPSE_OBJECT
:
1932 obj
= make_ellipse(curpos
, dirp
);
1935 obj
= make_circle(curpos
, dirp
);
1938 obj
= make_move(curpos
, dirp
);
1944 obj
= make_linear(curpos
, dirp
);
1953 if (flags
& IS_INVISIBLE
)
1954 obj
->set_invisible();
1956 obj
->add_text(text
, (flags
& IS_ALIGNED
) != 0);
1957 if (flags
& IS_DOTTED
)
1958 obj
->set_dotted(dash_width
);
1959 else if (flags
& IS_DASHED
)
1960 obj
->set_dashed(dash_width
);
1962 if (flags
& HAS_THICKNESS
)
1965 lookup_variable("linethick", &th
);
1966 obj
->set_thickness(th
);
1967 if (flags
& IS_OUTLINED
)
1968 obj
->set_outline_color(outlined
);
1969 if (flags
& IS_XSLANTED
)
1970 obj
->set_xslanted(xslanted
);
1971 if (flags
& IS_YSLANTED
)
1972 obj
->set_yslanted(yslanted
);
1973 if (flags
& (IS_DEFAULT_FILLED
| IS_FILLED
)) {
1974 if (flags
& IS_SHADED
)
1975 obj
->set_fill_color(shaded
);
1977 if (flags
& IS_DEFAULT_FILLED
)
1978 lookup_variable("fillval", &fill
);
1980 error("bad fill value %1", fill
);
1982 obj
->set_fill(fill
);
1994 string_list(char *);
1998 string_list::string_list(char *s
)
2003 string_list::~string_list()
2008 /* A path is used to hold the argument to the `with' attribute. For
2009 example, `.nw' or `.A.s' or `.A'. The major operation on a path is to
2010 take a place and follow the path through the place to place within the
2011 place. Note that `.A.B.C.sw' will work.
2013 For compatibility with DWB pic, `with' accepts positions also (this
2014 is incorrectly documented in CSTR 116). */
2016 path::path(corner c
)
2017 : crn(c
), label_list(0), ypath(0), is_position(0)
2021 path::path(position p
)
2022 : crn(0), label_list(0), ypath(0), is_position(1)
2028 path::path(char *l
, corner c
)
2029 : crn(c
), ypath(0), is_position(0)
2031 label_list
= new string_list(l
);
2036 while (label_list
) {
2037 string_list
*tem
= label_list
;
2038 label_list
= label_list
->next
;
2044 void path::append(corner c
)
2050 void path::append(char *s
)
2053 for (p
= &label_list
; *p
; p
= &(*p
)->next
)
2055 *p
= new string_list(s
);
2058 void path::set_ypath(path
*p
)
2063 // return non-zero for success
2065 int path::follow(const place
&pl
, place
*result
) const
2073 const place
*p
= &pl
;
2074 for (string_list
*lb
= label_list
; lb
; lb
= lb
->next
)
2075 if (p
->obj
== 0 || (p
= p
->obj
->find_label(lb
->str
)) == 0) {
2076 lex_error("object does not contain a place `%1'", lb
->str
);
2079 if (crn
== 0 || p
->obj
== 0)
2082 position ps
= ((p
->obj
)->*(crn
))();
2089 if (!ypath
->follow(pl
, &tem
))
2092 if (result
->obj
!= tem
.obj
)
2098 void print_object_list(object
*p
)
2100 for (; p
; p
= p
->next
) {
2106 void print_picture(object
*obj
)
2109 for (object
*p
= obj
; p
; p
= p
->next
)
2110 p
->update_bounding_box(&bb
);
2112 lookup_variable("scale", &scale
);
2113 out
->start_picture(scale
, bb
.ll
, bb
.ur
);
2114 print_object_list(obj
);
2115 out
->finish_picture();