4 /* Copyright (C) 1989, 1990, 1991, 1992, 2001, 2002, 2003, 2004, 2005
5 Free Software Foundation, Inc.
6 Written by James Clark (jjc@jclark.com)
8 This file is part of groff.
10 groff is free software; you can redistribute it and/or modify it under
11 the terms of the GNU General Public License as published by the Free
12 Software Foundation; either version 2, or (at your option) any later
15 groff is distributed in the hope that it will be useful, but WITHOUT ANY
16 WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 You should have received a copy of the GNU General Public License along
21 with groff; see the file COPYING. If not, write to the Free Software
22 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
28 void print_object_list(object
*);
30 line_type::line_type()
31 : type(solid
), thickness(1.0)
35 output::output() : args(0), desired_height(0.0), desired_width(0.0)
44 void output::set_desired_width_height(double wid
, double ht
)
50 void output::set_args(const char *s
)
53 if (s
== 0 || *s
== '\0')
59 int output::supports_filled_polygons()
64 void output::begin_block(const position
&, const position
&)
68 void output::end_block()
72 double output::compute_scale(double sc
, const position
&ll
, const position
&ur
)
74 distance dim
= ur
- ll
;
75 if (desired_width
!= 0.0 || desired_height
!= 0.0) {
77 if (desired_width
!= 0.0) {
79 error("width specified for picture with zero width");
81 sc
= dim
.x
/desired_width
;
83 if (desired_height
!= 0.0) {
85 error("height specified for picture with zero height");
87 double tem
= dim
.y
/desired_height
;
92 return sc
== 0.0 ? 1.0 : sc
;
97 distance sdim
= dim
/sc
;
98 double max_width
= 0.0;
99 lookup_variable("maxpswid", &max_width
);
100 double max_height
= 0.0;
101 lookup_variable("maxpsht", &max_height
);
102 if ((max_width
> 0.0 && sdim
.x
> max_width
)
103 || (max_height
> 0.0 && sdim
.y
> max_height
)) {
104 double xscale
= dim
.x
/max_width
;
105 double yscale
= dim
.y
/max_height
;
106 return xscale
> yscale
? xscale
: yscale
;
113 position::position(const place
&pl
)
116 // Use two statements to work around bug in SGI C++.
117 object
*tem
= pl
.obj
;
118 *this = tem
->origin();
126 position::position() : x(0.0), y(0.0)
130 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;
419 dir
= RIGHT_DIRECTION
;
422 object_spec::~object_spec()
425 while (segment_list
!= 0) {
426 segment
*tem
= segment_list
;
427 segment_list
= segment_list
->next
;
430 object
*p
= oblist
.head
;
437 text_item
*tem
= text
;
446 class command_object
: public object
{
448 const char *filename
;
451 command_object(char *, const char *, int);
453 object_type
type() { return OTHER_OBJECT
; }
457 command_object::command_object(char *p
, const char *fn
, int ln
)
458 : s(p
), filename(fn
), lineno(ln
)
462 command_object::~command_object()
467 void command_object::print()
469 out
->command(s
, filename
, lineno
);
472 object
*make_command_object(char *s
, const char *fn
, int ln
)
474 return new command_object(s
, fn
, ln
);
477 class mark_object
: public object
{
483 object
*make_mark_object()
485 return new mark_object();
488 mark_object::mark_object()
492 object_type
mark_object::type()
497 object_list::object_list() : head(0), tail(0)
501 void object_list::append(object
*obj
)
504 obj
->next
= obj
->prev
= 0;
515 void object_list::wrap_up_block(object_list
*ol
)
518 for (p
= tail
; p
&& p
->type() != MARK_OBJECT
; p
= p
->prev
)
536 text_piece::text_piece()
537 : text(0), filename(0), lineno(-1)
539 adj
.h
= CENTER_ADJUST
;
543 text_piece::~text_piece()
548 class graphic_object
: public object
{
559 object_type
type() = 0;
561 void add_text(text_item
*, int);
562 void set_dotted(double);
563 void set_dashed(double);
564 void set_thickness(double);
565 void set_invisible();
566 void set_outline_color(char *);
567 char *get_outline_color();
568 virtual void set_fill(double);
569 virtual void set_fill_color(char *);
572 graphic_object::graphic_object()
573 : ntext(0), text(0), aligned(0), outline_color(0), color_fill(0)
577 void graphic_object::set_dotted(double wid
)
579 lt
.type
= line_type::dotted
;
583 void graphic_object::set_dashed(double wid
)
585 lt
.type
= line_type::dashed
;
589 void graphic_object::set_thickness(double th
)
594 void graphic_object::set_fill(double)
598 void graphic_object::set_fill_color(char *c
)
600 color_fill
= strsave(c
);
603 void graphic_object::set_outline_color(char *c
)
605 outline_color
= strsave(c
);
608 char *graphic_object::get_outline_color()
610 return outline_color
;
613 void graphic_object::set_invisible()
615 lt
.type
= line_type::invisible
;
618 void graphic_object::add_text(text_item
*t
, int a
)
623 for (p
= t
; p
; p
= p
->next
)
628 text
= new text_piece
[len
];
629 for (p
= t
, len
= 0; p
; p
= p
->next
, len
++) {
630 text
[len
].text
= p
->text
;
632 text
[len
].adj
= p
->adj
;
633 text
[len
].filename
= p
->filename
;
634 text
[len
].lineno
= p
->lineno
;
640 void graphic_object::print_text()
644 position
d(end() - start());
645 if (d
.x
!= 0.0 || d
.y
!= 0.0)
646 angle
= atan2(d
.y
, d
.x
);
649 out
->set_color(color_fill
, get_outline_color());
650 out
->text(center(), text
, ntext
, angle
);
655 graphic_object::~graphic_object()
658 ad_delete(ntext
) text
;
661 class rectangle_object
: public graphic_object
{
666 rectangle_object(const position
&);
667 double width() { return dim
.x
; }
668 double height() { return dim
.y
; }
669 position
origin() { return cent
; }
670 position
center() { return cent
; }
671 position
north() { return position(cent
.x
, cent
.y
+ dim
.y
/2.0); }
672 position
south() { return position(cent
.x
, cent
.y
- dim
.y
/2.0); }
673 position
east() { return position(cent
.x
+ dim
.x
/2.0, cent
.y
); }
674 position
west() { return position(cent
.x
- dim
.x
/2.0, cent
.y
); }
675 position
north_east() { return position(cent
.x
+ dim
.x
/2.0, cent
.y
+ dim
.y
/2.0); }
676 position
north_west() { return position(cent
.x
- dim
.x
/2.0, cent
.y
+ dim
.y
/2.0); }
677 position
south_east() { return position(cent
.x
+ dim
.x
/2.0, cent
.y
- dim
.y
/2.0); }
678 position
south_west() { return position(cent
.x
- dim
.x
/2.0, cent
.y
- dim
.y
/2.0); }
679 object_type
type() = 0;
680 void update_bounding_box(bounding_box
*);
681 void move_by(const position
&);
684 rectangle_object::rectangle_object(const position
&d
)
689 void rectangle_object::update_bounding_box(bounding_box
*p
)
691 p
->encompass(cent
- dim
/2.0);
692 p
->encompass(cent
+ dim
/2.0);
695 void rectangle_object::move_by(const position
&a
)
700 class closed_object
: public rectangle_object
{
702 closed_object(const position
&);
703 object_type
type() = 0;
704 void set_fill(double);
705 void set_fill_color(char *fill
);
707 double fill
; // < 0 if not filled
708 char *color_fill
; // = 0 if not colored
711 closed_object::closed_object(const position
&pos
)
712 : rectangle_object(pos
), fill(-1.0), color_fill(0)
716 void closed_object::set_fill(double f
)
722 void closed_object::set_fill_color(char *f
)
724 color_fill
= strsave(f
);
727 class box_object
: public closed_object
{
731 box_object(const position
&, double);
732 object_type
type() { return BOX_OBJECT
; }
734 position
north_east();
735 position
north_west();
736 position
south_east();
737 position
south_west();
740 box_object::box_object(const position
&pos
, double r
)
741 : closed_object(pos
), xrad(dim
.x
> 0 ? r
: -r
), yrad(dim
.y
> 0 ? r
: -r
)
745 const double CHOP_FACTOR
= 1.0 - 1.0/M_SQRT2
;
747 position
box_object::north_east()
749 return position(cent
.x
+ dim
.x
/2.0 - CHOP_FACTOR
*xrad
,
750 cent
.y
+ dim
.y
/2.0 - CHOP_FACTOR
*yrad
);
753 position
box_object::north_west()
755 return position(cent
.x
- dim
.x
/2.0 + CHOP_FACTOR
*xrad
,
756 cent
.y
+ dim
.y
/2.0 - CHOP_FACTOR
*yrad
);
759 position
box_object::south_east()
761 return position(cent
.x
+ dim
.x
/2.0 - CHOP_FACTOR
*xrad
,
762 cent
.y
- dim
.y
/2.0 + CHOP_FACTOR
*yrad
);
765 position
box_object::south_west()
767 return position(cent
.x
- dim
.x
/2.0 + CHOP_FACTOR
*xrad
,
768 cent
.y
- dim
.y
/2.0 + CHOP_FACTOR
*yrad
);
771 void box_object::print()
773 if (lt
.type
== line_type::invisible
&& fill
< 0.0 && color_fill
== 0)
775 out
->set_color(color_fill
, graphic_object::get_outline_color());
777 distance dim2
= dim
/2.0;
779 vec
[0] = cent
+ position(dim2
.x
, -dim2
.y
);
780 vec
[1] = cent
+ position(dim2
.x
, dim2
.y
);
781 vec
[2] = cent
+ position(-dim2
.x
, dim2
.y
);
782 vec
[3] = cent
+ position(-dim2
.x
, -dim2
.y
);
783 out
->polygon(vec
, 4, lt
, fill
);
786 distance
abs_dim(fabs(dim
.x
), fabs(dim
.y
));
787 out
->rounded_box(cent
, abs_dim
, fabs(xrad
), lt
, fill
);
792 graphic_object
*object_spec::make_box(position
*curpos
, direction
*dirp
)
794 static double last_box_height
;
795 static double last_box_width
;
796 static double last_box_radius
;
797 static int have_last_box
= 0;
798 if (!(flags
& HAS_HEIGHT
)) {
799 if ((flags
& IS_SAME
) && have_last_box
)
800 height
= last_box_height
;
802 lookup_variable("boxht", &height
);
804 if (!(flags
& HAS_WIDTH
)) {
805 if ((flags
& IS_SAME
) && have_last_box
)
806 width
= last_box_width
;
808 lookup_variable("boxwid", &width
);
810 if (!(flags
& HAS_RADIUS
)) {
811 if ((flags
& IS_SAME
) && have_last_box
)
812 radius
= last_box_radius
;
814 lookup_variable("boxrad", &radius
);
816 last_box_width
= width
;
817 last_box_height
= height
;
818 last_box_radius
= radius
;
820 radius
= fabs(radius
);
821 if (radius
*2.0 > fabs(width
))
822 radius
= fabs(width
/2.0);
823 if (radius
*2.0 > fabs(height
))
824 radius
= fabs(height
/2.0);
825 box_object
*p
= new box_object(position(width
, height
), radius
);
826 if (!position_rectangle(p
, curpos
, dirp
)) {
833 // return non-zero for success
835 int object_spec::position_rectangle(rectangle_object
*p
,
836 position
*curpos
, direction
*dirp
)
839 dir
= *dirp
; // ignore any direction in attribute list
843 motion
.y
= p
->height()/2.0;
846 motion
.y
= -p
->height()/2.0;
849 motion
.x
= -p
->width()/2.0;
851 case RIGHT_DIRECTION
:
852 motion
.x
= p
->width()/2.0;
857 if (flags
& HAS_AT
) {
859 if (flags
& HAS_WITH
) {
863 if (!with
->follow(here
, &offset
))
878 class block_object
: public rectangle_object
{
882 block_object(const position
&, const object_list
&ol
, PTABLE(place
) *t
);
884 place
*find_label(const char *);
886 void move_by(const position
&);
890 block_object::block_object(const position
&d
, const object_list
&ol
,
892 : rectangle_object(d
), oblist(ol
), tbl(t
)
896 block_object::~block_object()
899 object
*p
= oblist
.head
;
907 void block_object::print()
909 out
->begin_block(south_west(), north_east());
910 print_object_list(oblist
.head
);
914 static void adjust_objectless_places(PTABLE(place
) *tbl
, const position
&a
)
916 // Adjust all the labels that aren't attached to objects.
917 PTABLE_ITERATOR(place
) iter(tbl
);
920 while (iter
.next(&key
, &pl
))
921 if (key
&& csupper(key
[0]) && pl
->obj
== 0) {
927 void block_object::move_by(const position
&a
)
930 for (object
*p
= oblist
.head
; p
; p
= p
->next
)
932 adjust_objectless_places(tbl
, a
);
936 place
*block_object::find_label(const char *name
)
938 return tbl
->lookup(name
);
941 object_type
block_object::type()
946 graphic_object
*object_spec::make_block(position
*curpos
, direction
*dirp
)
949 for (object
*p
= oblist
.head
; p
; p
= p
->next
)
950 p
->update_bounding_box(&bb
);
953 position m
= -(bb
.ll
+ bb
.ur
)/2.0;
954 for (object
*p
= oblist
.head
; p
; p
= p
->next
)
956 adjust_objectless_places(tbl
, m
);
959 if (flags
& HAS_WIDTH
)
961 if (flags
& HAS_HEIGHT
)
963 block_object
*block
= new block_object(dim
, oblist
, tbl
);
964 if (!position_rectangle(block
, curpos
, dirp
)) {
969 oblist
.head
= oblist
.tail
= 0;
973 class text_object
: public rectangle_object
{
975 text_object(const position
&);
976 object_type
type() { return TEXT_OBJECT
; }
979 text_object::text_object(const position
&d
)
980 : rectangle_object(d
)
984 graphic_object
*object_spec::make_text(position
*curpos
, direction
*dirp
)
986 if (!(flags
& HAS_HEIGHT
)) {
987 lookup_variable("textht", &height
);
989 for (text_item
*t
= text
; t
; t
= t
->next
)
993 if (!(flags
& HAS_WIDTH
))
994 lookup_variable("textwid", &width
);
995 text_object
*p
= new text_object(position(width
, height
));
996 if (!position_rectangle(p
, curpos
, dirp
)) {
1004 class ellipse_object
: public closed_object
{
1006 ellipse_object(const position
&);
1007 position
north_east() { return position(cent
.x
+ dim
.x
/(M_SQRT2
*2.0),
1008 cent
.y
+ dim
.y
/(M_SQRT2
*2.0)); }
1009 position
north_west() { return position(cent
.x
- dim
.x
/(M_SQRT2
*2.0),
1010 cent
.y
+ dim
.y
/(M_SQRT2
*2.0)); }
1011 position
south_east() { return position(cent
.x
+ dim
.x
/(M_SQRT2
*2.0),
1012 cent
.y
- dim
.y
/(M_SQRT2
*2.0)); }
1013 position
south_west() { return position(cent
.x
- dim
.x
/(M_SQRT2
*2.0),
1014 cent
.y
- dim
.y
/(M_SQRT2
*2.0)); }
1015 double radius() { return dim
.x
/2.0; }
1016 object_type
type() { return ELLIPSE_OBJECT
; }
1020 ellipse_object::ellipse_object(const position
&d
)
1025 void ellipse_object::print()
1027 if (lt
.type
== line_type::invisible
&& fill
< 0.0 && color_fill
== 0)
1029 out
->set_color(color_fill
, graphic_object::get_outline_color());
1030 out
->ellipse(cent
, dim
, lt
, fill
);
1034 graphic_object
*object_spec::make_ellipse(position
*curpos
, direction
*dirp
)
1036 static double last_ellipse_height
;
1037 static double last_ellipse_width
;
1038 static int have_last_ellipse
= 0;
1039 if (!(flags
& HAS_HEIGHT
)) {
1040 if ((flags
& IS_SAME
) && have_last_ellipse
)
1041 height
= last_ellipse_height
;
1043 lookup_variable("ellipseht", &height
);
1045 if (!(flags
& HAS_WIDTH
)) {
1046 if ((flags
& IS_SAME
) && have_last_ellipse
)
1047 width
= last_ellipse_width
;
1049 lookup_variable("ellipsewid", &width
);
1051 last_ellipse_width
= width
;
1052 last_ellipse_height
= height
;
1053 have_last_ellipse
= 1;
1054 ellipse_object
*p
= new ellipse_object(position(width
, height
));
1055 if (!position_rectangle(p
, curpos
, dirp
)) {
1062 class circle_object
: public ellipse_object
{
1064 circle_object(double);
1065 object_type
type() { return CIRCLE_OBJECT
; }
1069 circle_object::circle_object(double diam
)
1070 : ellipse_object(position(diam
, diam
))
1074 void circle_object::print()
1076 if (lt
.type
== line_type::invisible
&& fill
< 0.0 && color_fill
== 0)
1078 out
->set_color(color_fill
, graphic_object::get_outline_color());
1079 out
->circle(cent
, dim
.x
/2.0, lt
, fill
);
1083 graphic_object
*object_spec::make_circle(position
*curpos
, direction
*dirp
)
1085 static double last_circle_radius
;
1086 static int have_last_circle
= 0;
1087 if (!(flags
& HAS_RADIUS
)) {
1088 if ((flags
& IS_SAME
) && have_last_circle
)
1089 radius
= last_circle_radius
;
1091 lookup_variable("circlerad", &radius
);
1093 last_circle_radius
= radius
;
1094 have_last_circle
= 1;
1095 circle_object
*p
= new circle_object(radius
*2.0);
1096 if (!position_rectangle(p
, curpos
, dirp
)) {
1103 class move_object
: public graphic_object
{
1107 move_object(const position
&s
, const position
&e
);
1108 position
origin() { return en
; }
1109 object_type
type() { return MOVE_OBJECT
; }
1110 void update_bounding_box(bounding_box
*);
1111 void move_by(const position
&);
1114 move_object::move_object(const position
&s
, const position
&e
)
1119 void move_object::update_bounding_box(bounding_box
*p
)
1125 void move_object::move_by(const position
&a
)
1131 graphic_object
*object_spec::make_move(position
*curpos
, direction
*dirp
)
1133 static position last_move
;
1134 static int have_last_move
= 0;
1136 // No need to look at at since `at' attribute sets `from' attribute.
1137 position startpos
= (flags
& HAS_FROM
) ? from
: *curpos
;
1138 if (!(flags
& HAS_SEGMENT
)) {
1139 if ((flags
& IS_SAME
) && have_last_move
)
1140 segment_pos
= last_move
;
1144 segment_pos
.y
= segment_height
;
1146 case DOWN_DIRECTION
:
1147 segment_pos
.y
= -segment_height
;
1149 case LEFT_DIRECTION
:
1150 segment_pos
.x
= -segment_width
;
1152 case RIGHT_DIRECTION
:
1153 segment_pos
.x
= segment_width
;
1160 segment_list
= new segment(segment_pos
, segment_is_absolute
, segment_list
);
1161 // Reverse the segment_list so that it's in forward order.
1162 segment
*old
= segment_list
;
1165 segment
*tem
= old
->next
;
1166 old
->next
= segment_list
;
1170 // Compute the end position.
1171 position endpos
= startpos
;
1172 for (segment
*s
= segment_list
; s
; s
= s
->next
)
1178 last_move
= endpos
- startpos
;
1179 move_object
*p
= new move_object(startpos
, endpos
);
1184 class linear_object
: public graphic_object
{
1186 char arrow_at_start
;
1188 arrow_head_type aht
;
1192 linear_object(const position
&s
, const position
&e
);
1193 position
start() { return strt
; }
1194 position
end() { return en
; }
1195 void move_by(const position
&);
1196 void update_bounding_box(bounding_box
*) = 0;
1197 object_type
type() = 0;
1198 void add_arrows(int at_start
, int at_end
, const arrow_head_type
&);
1201 class line_object
: public linear_object
{
1206 line_object(const position
&s
, const position
&e
, position
*, int);
1208 position
origin() { return strt
; }
1209 position
center() { return (strt
+ en
)/2.0; }
1210 position
north() { return (en
.y
- strt
.y
) > 0 ? en
: strt
; }
1211 position
south() { return (en
.y
- strt
.y
) < 0 ? en
: strt
; }
1212 position
east() { return (en
.x
- strt
.x
) > 0 ? en
: strt
; }
1213 position
west() { return (en
.x
- strt
.x
) < 0 ? en
: strt
; }
1214 object_type
type() { return LINE_OBJECT
; }
1215 void update_bounding_box(bounding_box
*);
1217 void move_by(const position
&);
1220 class arrow_object
: public line_object
{
1222 arrow_object(const position
&, const position
&, position
*, int);
1223 object_type
type() { return ARROW_OBJECT
; }
1226 class spline_object
: public line_object
{
1228 spline_object(const position
&, const position
&, position
*, int);
1229 object_type
type() { return SPLINE_OBJECT
; }
1231 void update_bounding_box(bounding_box
*);
1234 linear_object::linear_object(const position
&s
, const position
&e
)
1235 : arrow_at_start(0), arrow_at_end(0), strt(s
), en(e
)
1239 void linear_object::move_by(const position
&a
)
1245 void linear_object::add_arrows(int at_start
, int at_end
,
1246 const arrow_head_type
&a
)
1248 arrow_at_start
= at_start
;
1249 arrow_at_end
= at_end
;
1253 line_object::line_object(const position
&s
, const position
&e
,
1255 : linear_object(s
, e
), v(p
), n(i
)
1259 void line_object::print()
1261 if (lt
.type
== line_type::invisible
)
1263 out
->set_color(0, graphic_object::get_outline_color());
1264 // shorten line length to avoid arrow sticking.
1266 if (arrow_at_start
) {
1267 position base
= v
[0] - strt
;
1268 double hyp
= hypot(base
);
1270 error("cannot draw arrow on object with zero length");
1273 if (aht
.solid
&& out
->supports_filled_polygons()) {
1274 base
*= aht
.height
/ hyp
;
1275 draw_arrow(strt
, strt
- v
[0], aht
, lt
,
1276 graphic_object::get_outline_color());
1279 base
*= fabs(lt
.thickness
) / hyp
/ 72 / 4;
1281 draw_arrow(sp
, sp
- v
[0], aht
, lt
,
1282 graphic_object::get_outline_color());
1286 position base
= v
[n
-1] - (n
> 1 ? v
[n
-2] : strt
);
1287 double hyp
= hypot(base
);
1289 error("cannot draw arrow on object with zero length");
1292 if (aht
.solid
&& out
->supports_filled_polygons()) {
1293 base
*= aht
.height
/ hyp
;
1294 draw_arrow(en
, v
[n
-1] - (n
> 1 ? v
[n
-2] : strt
), aht
, lt
,
1295 graphic_object::get_outline_color());
1298 base
*= fabs(lt
.thickness
) / hyp
/ 72 / 4;
1300 draw_arrow(v
[n
-1], v
[n
-1] - (n
> 1 ? v
[n
-2] : strt
), aht
, lt
,
1301 graphic_object::get_outline_color());
1304 out
->line(sp
, v
, n
, lt
);
1308 void line_object::update_bounding_box(bounding_box
*p
)
1311 for (int i
= 0; i
< n
; i
++)
1315 void line_object::move_by(const position
&pos
)
1317 linear_object::move_by(pos
);
1318 for (int i
= 0; i
< n
; i
++)
1322 void spline_object::update_bounding_box(bounding_box
*p
)
1334 [ the points for the Bezier cubic ]
1342 (1-t)^3*p1 + 3*t*(t - 1)^2*p2 + 3*t^2*(1-t)*p3 + t^3*p4
1343 [ the equation for the Bezier cubic ]
1345 = .125*q1 + .75*q2 + .125*q3
1348 for (int i
= 1; i
< n
; i
++)
1349 p
->encompass((i
== 1 ? strt
: v
[i
-2])*.125 + v
[i
-1]*.75 + v
[i
]*.125);
1352 arrow_object::arrow_object(const position
&s
, const position
&e
,
1354 : line_object(s
, e
, p
, i
)
1358 spline_object::spline_object(const position
&s
, const position
&e
,
1360 : line_object(s
, e
, p
, i
)
1364 void spline_object::print()
1366 if (lt
.type
== line_type::invisible
)
1368 out
->set_color(0, graphic_object::get_outline_color());
1369 // shorten line length for spline to avoid arrow sticking
1371 if (arrow_at_start
) {
1372 position base
= v
[0] - strt
;
1373 double hyp
= hypot(base
);
1375 error("cannot draw arrow on object with zero length");
1378 if (aht
.solid
&& out
->supports_filled_polygons()) {
1379 base
*= aht
.height
/ hyp
;
1380 draw_arrow(strt
, strt
- v
[0], aht
, lt
,
1381 graphic_object::get_outline_color());
1382 sp
= strt
+ base
*0.1; // to reserve spline shape
1384 base
*= fabs(lt
.thickness
) / hyp
/ 72 / 4;
1386 draw_arrow(sp
, sp
- v
[0], aht
, lt
,
1387 graphic_object::get_outline_color());
1391 position base
= v
[n
-1] - (n
> 1 ? v
[n
-2] : strt
);
1392 double hyp
= hypot(base
);
1394 error("cannot draw arrow on object with zero length");
1397 if (aht
.solid
&& out
->supports_filled_polygons()) {
1398 base
*= aht
.height
/ hyp
;
1399 draw_arrow(en
, v
[n
-1] - (n
> 1 ? v
[n
-2] : strt
), aht
, lt
,
1400 graphic_object::get_outline_color());
1401 v
[n
-1] = en
- base
*0.1; // to reserve spline shape
1403 base
*= fabs(lt
.thickness
) / hyp
/ 72 / 4;
1405 draw_arrow(v
[n
-1], v
[n
-1] - (n
> 1 ? v
[n
-2] : strt
), aht
, lt
,
1406 graphic_object::get_outline_color());
1409 out
->spline(sp
, v
, n
, lt
);
1413 line_object::~line_object()
1418 linear_object
*object_spec::make_line(position
*curpos
, direction
*dirp
)
1420 static position last_line
;
1421 static int have_last_line
= 0;
1423 // No need to look at at since `at' attribute sets `from' attribute.
1424 position startpos
= (flags
& HAS_FROM
) ? from
: *curpos
;
1425 if (!(flags
& HAS_SEGMENT
)) {
1426 if ((flags
& IS_SAME
) && (type
== LINE_OBJECT
|| type
== ARROW_OBJECT
)
1428 segment_pos
= last_line
;
1432 segment_pos
.y
= segment_height
;
1434 case DOWN_DIRECTION
:
1435 segment_pos
.y
= -segment_height
;
1437 case LEFT_DIRECTION
:
1438 segment_pos
.x
= -segment_width
;
1440 case RIGHT_DIRECTION
:
1441 segment_pos
.x
= segment_width
;
1447 segment_list
= new segment(segment_pos
, segment_is_absolute
, segment_list
);
1448 // reverse the segment_list so that it's in forward order
1449 segment
*old
= segment_list
;
1452 segment
*tem
= old
->next
;
1453 old
->next
= segment_list
;
1457 // Absolutise all movements
1458 position endpos
= startpos
;
1461 for (s
= segment_list
; s
; s
= s
->next
, nsegments
++)
1467 s
->is_absolute
= 1; // to avoid confusion
1471 position
*v
= new position
[nsegments
];
1473 for (s
= segment_list
; s
; s
= s
->next
, i
++)
1475 if (flags
& IS_DEFAULT_CHOPPED
) {
1476 lookup_variable("circlerad", &start_chop
);
1477 end_chop
= start_chop
;
1478 flags
|= IS_CHOPPED
;
1480 if (flags
& IS_CHOPPED
) {
1481 position start_chop_vec
, end_chop_vec
;
1482 if (start_chop
!= 0.0) {
1483 start_chop_vec
= v
[0] - startpos
;
1484 start_chop_vec
*= start_chop
/ hypot(start_chop_vec
);
1486 if (end_chop
!= 0.0) {
1487 end_chop_vec
= (v
[nsegments
- 1]
1488 - (nsegments
> 1 ? v
[nsegments
- 2] : startpos
));
1489 end_chop_vec
*= end_chop
/ hypot(end_chop_vec
);
1491 startpos
+= start_chop_vec
;
1492 v
[nsegments
- 1] -= end_chop_vec
;
1493 endpos
-= end_chop_vec
;
1497 p
= new spline_object(startpos
, endpos
, v
, nsegments
);
1500 p
= new arrow_object(startpos
, endpos
, v
, nsegments
);
1503 p
= new line_object(startpos
, endpos
, v
, nsegments
);
1509 last_line
= endpos
- startpos
;
1514 class arc_object
: public linear_object
{
1519 arc_object(int, const position
&, const position
&, const position
&);
1520 position
origin() { return cent
; }
1521 position
center() { return cent
; }
1522 double radius() { return rad
; }
1527 position
north_east();
1528 position
north_west();
1529 position
south_east();
1530 position
south_west();
1531 void update_bounding_box(bounding_box
*);
1532 object_type
type() { return ARC_OBJECT
; }
1534 void move_by(const position
&pos
);
1537 arc_object::arc_object(int cw
, const position
&s
, const position
&e
,
1539 : linear_object(s
, e
), clockwise(cw
), cent(c
)
1544 void arc_object::move_by(const position
&pos
)
1546 linear_object::move_by(pos
);
1550 // we get arc corners from the corresponding circle
1552 position
arc_object::north()
1554 position
result(cent
);
1559 position
arc_object::south()
1561 position
result(cent
);
1566 position
arc_object::east()
1568 position
result(cent
);
1573 position
arc_object::west()
1575 position
result(cent
);
1580 position
arc_object::north_east()
1582 position
result(cent
);
1583 result
.x
+= rad
/M_SQRT2
;
1584 result
.y
+= rad
/M_SQRT2
;
1588 position
arc_object::north_west()
1590 position
result(cent
);
1591 result
.x
-= rad
/M_SQRT2
;
1592 result
.y
+= rad
/M_SQRT2
;
1596 position
arc_object::south_east()
1598 position
result(cent
);
1599 result
.x
+= rad
/M_SQRT2
;
1600 result
.y
-= rad
/M_SQRT2
;
1604 position
arc_object::south_west()
1606 position
result(cent
);
1607 result
.x
-= rad
/M_SQRT2
;
1608 result
.y
-= rad
/M_SQRT2
;
1613 void arc_object::print()
1615 if (lt
.type
== line_type::invisible
)
1617 out
->set_color(0, graphic_object::get_outline_color());
1618 // handle arrow direction; make shorter line for arc
1627 if (arrow_at_start
) {
1628 double theta
= aht
.height
/ rad
;
1632 b
= position(b
.x
*cos(theta
) - b
.y
*sin(theta
),
1633 b
.x
*sin(theta
) + b
.y
*cos(theta
)) + cent
;
1638 if (aht
.solid
&& out
->supports_filled_polygons()) {
1639 draw_arrow(strt
, strt
- b
, aht
, lt
,
1640 graphic_object::get_outline_color());
1643 theta
= fabs(lt
.thickness
) / 72 / 4 / rad
;
1647 b
= position(b
.x
*cos(theta
) - b
.y
*sin(theta
),
1648 b
.x
*sin(theta
) + b
.y
*cos(theta
)) + cent
;
1649 draw_arrow(b
, b
- v
, aht
, lt
,
1650 graphic_object::get_outline_color());
1651 out
->line(b
, &v
, 1, lt
);
1655 double theta
= aht
.height
/ rad
;
1659 b
= position(b
.x
*cos(theta
) - b
.y
*sin(theta
),
1660 b
.x
*sin(theta
) + b
.y
*cos(theta
)) + cent
;
1665 if (aht
.solid
&& out
->supports_filled_polygons()) {
1666 draw_arrow(en
, en
- b
, aht
, lt
,
1667 graphic_object::get_outline_color());
1670 theta
= fabs(lt
.thickness
) / 72 / 4 / rad
;
1674 b
= position(b
.x
*cos(theta
) - b
.y
*sin(theta
),
1675 b
.x
*sin(theta
) + b
.y
*cos(theta
)) + cent
;
1676 draw_arrow(b
, b
- v
, aht
, lt
,
1677 graphic_object::get_outline_color());
1678 out
->line(b
, &v
, 1, lt
);
1681 out
->arc(sp
, cent
, ep
, lt
);
1685 inline double max(double a
, double b
)
1687 return a
> b
? a
: b
;
1690 void arc_object::update_bounding_box(bounding_box
*p
)
1694 position start_offset
= strt
- cent
;
1695 if (start_offset
.x
== 0.0 && start_offset
.y
== 0.0)
1697 position end_offset
= en
- cent
;
1698 if (end_offset
.x
== 0.0 && end_offset
.y
== 0.0)
1700 double start_quad
= atan2(start_offset
.y
, start_offset
.x
)/(M_PI
/2.0);
1701 double end_quad
= atan2(end_offset
.y
, end_offset
.x
)/(M_PI
/2.0);
1703 double temp
= start_quad
;
1704 start_quad
= end_quad
;
1707 if (start_quad
< 0.0)
1709 while (end_quad
<= start_quad
)
1711 double r
= max(hypot(start_offset
), hypot(end_offset
));
1712 for (int q
= int(start_quad
) + 1; q
< end_quad
; q
++) {
1728 p
->encompass(cent
+ offset
);
1732 // We ignore the with attribute. The at attribute always refers to the center.
1734 linear_object
*object_spec::make_arc(position
*curpos
, direction
*dirp
)
1737 int cw
= (flags
& IS_CLOCKWISE
) != 0;
1738 // compute the start
1740 if (flags
& HAS_FROM
)
1744 if (!(flags
& HAS_RADIUS
))
1745 lookup_variable("arcrad", &radius
);
1751 position
m(radius
, radius
);
1752 // Adjust the signs.
1754 if (dir
== DOWN_DIRECTION
|| dir
== LEFT_DIRECTION
)
1756 if (dir
== DOWN_DIRECTION
|| dir
== RIGHT_DIRECTION
)
1758 *dirp
= direction((dir
+ 3) % 4);
1761 if (dir
== UP_DIRECTION
|| dir
== LEFT_DIRECTION
)
1763 if (dir
== DOWN_DIRECTION
|| dir
== LEFT_DIRECTION
)
1765 *dirp
= direction((dir
+ 1) % 4);
1767 endpos
= startpos
+ m
;
1769 // compute the center
1773 else if (startpos
== endpos
)
1774 centerpos
= startpos
;
1776 position h
= (endpos
- startpos
)/2.0;
1777 double d
= hypot(h
);
1780 // make the radius big enough
1783 double alpha
= acos(d
/radius
);
1784 double theta
= atan2(h
.y
, h
.x
);
1789 centerpos
= position(cos(theta
), sin(theta
))*radius
+ startpos
;
1791 arc_object
*p
= new arc_object(cw
, startpos
, endpos
, centerpos
);
1796 graphic_object
*object_spec::make_linear(position
*curpos
, direction
*dirp
)
1799 if (type
== ARC_OBJECT
)
1800 obj
= make_arc(curpos
, dirp
);
1802 obj
= make_line(curpos
, dirp
);
1803 if (type
== ARROW_OBJECT
1804 && (flags
& (HAS_LEFT_ARROW_HEAD
|HAS_RIGHT_ARROW_HEAD
)) == 0)
1805 flags
|= HAS_RIGHT_ARROW_HEAD
;
1806 if (obj
&& (flags
& (HAS_LEFT_ARROW_HEAD
|HAS_RIGHT_ARROW_HEAD
))) {
1808 int at_start
= (flags
& HAS_LEFT_ARROW_HEAD
) != 0;
1809 int at_end
= (flags
& HAS_RIGHT_ARROW_HEAD
) != 0;
1810 if (flags
& HAS_HEIGHT
)
1813 lookup_variable("arrowht", &a
.height
);
1814 if (flags
& HAS_WIDTH
)
1817 lookup_variable("arrowwid", &a
.width
);
1819 lookup_variable("arrowhead", &solid
);
1820 a
.solid
= solid
!= 0.0;
1821 obj
->add_arrows(at_start
, at_end
, a
);
1826 object
*object_spec::make_object(position
*curpos
, direction
*dirp
)
1828 graphic_object
*obj
= 0;
1831 obj
= make_block(curpos
, dirp
);
1834 obj
= make_box(curpos
, dirp
);
1837 obj
= make_text(curpos
, dirp
);
1839 case ELLIPSE_OBJECT
:
1840 obj
= make_ellipse(curpos
, dirp
);
1843 obj
= make_circle(curpos
, dirp
);
1846 obj
= make_move(curpos
, dirp
);
1852 obj
= make_linear(curpos
, dirp
);
1861 if (flags
& IS_INVISIBLE
)
1862 obj
->set_invisible();
1864 obj
->add_text(text
, (flags
& IS_ALIGNED
) != 0);
1865 if (flags
& IS_DOTTED
)
1866 obj
->set_dotted(dash_width
);
1867 else if (flags
& IS_DASHED
)
1868 obj
->set_dashed(dash_width
);
1870 if (flags
& HAS_THICKNESS
)
1873 lookup_variable("linethick", &th
);
1874 obj
->set_thickness(th
);
1875 if (flags
& IS_OUTLINED
)
1876 obj
->set_outline_color(outlined
);
1877 if (flags
& (IS_DEFAULT_FILLED
| IS_FILLED
)) {
1878 if (flags
& IS_SHADED
)
1879 obj
->set_fill_color(shaded
);
1881 if (flags
& IS_DEFAULT_FILLED
)
1882 lookup_variable("fillval", &fill
);
1884 error("bad fill value %1", fill
);
1886 obj
->set_fill(fill
);
1893 struct string_list
{
1896 string_list(char *);
1900 string_list::string_list(char *s
)
1905 string_list::~string_list()
1910 /* A path is used to hold the argument to the `with' attribute. For
1911 example, `.nw' or `.A.s' or `.A'. The major operation on a path is to
1912 take a place and follow the path through the place to place within the
1913 place. Note that `.A.B.C.sw' will work.
1915 For compatibility with DWB pic, `with' accepts positions also (this
1916 is incorrectly documented in CSTR 116). */
1918 path::path(corner c
)
1919 : crn(c
), label_list(0), ypath(0), is_position(0)
1923 path::path(position p
)
1924 : crn(0), label_list(0), ypath(0), is_position(1)
1930 path::path(char *l
, corner c
)
1931 : crn(c
), ypath(0), is_position(0)
1933 label_list
= new string_list(l
);
1938 while (label_list
) {
1939 string_list
*tem
= label_list
;
1940 label_list
= label_list
->next
;
1946 void path::append(corner c
)
1952 void path::append(char *s
)
1955 for (p
= &label_list
; *p
; p
= &(*p
)->next
)
1957 *p
= new string_list(s
);
1960 void path::set_ypath(path
*p
)
1965 // return non-zero for success
1967 int path::follow(const place
&pl
, place
*result
) const
1975 const place
*p
= &pl
;
1976 for (string_list
*lb
= label_list
; lb
; lb
= lb
->next
)
1977 if (p
->obj
== 0 || (p
= p
->obj
->find_label(lb
->str
)) == 0) {
1978 lex_error("object does not contain a place `%1'", lb
->str
);
1981 if (crn
== 0 || p
->obj
== 0)
1984 position ps
= ((p
->obj
)->*(crn
))();
1991 if (!ypath
->follow(pl
, &tem
))
1994 if (result
->obj
!= tem
.obj
)
2000 void print_object_list(object
*p
)
2002 for (; p
; p
= p
->next
) {
2008 void print_picture(object
*obj
)
2011 for (object
*p
= obj
; p
; p
= p
->next
)
2012 p
->update_bounding_box(&bb
);
2014 lookup_variable("scale", &scale
);
2015 out
->start_picture(scale
, bb
.ll
, bb
.ur
);
2016 print_object_list(obj
);
2017 out
->finish_picture();