2
/* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
3
Written by James Clark (jjc@jclark.com)
5
This file is part of groff.
7
groff 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
groff 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
25
void print_object_list(object *);
27
line_type::line_type()
28
: type(solid), thickness(1.0)
32
output::output() : args(0), desired_height(0.0), desired_width(0.0)
41
void output::set_desired_width_height(double wid, double ht)
47
void output::set_args(const char *s)
50
if (s == 0 || *s == '\0')
56
void output::command(const char *, const char *, int)
60
void output::set_location(const char *, int)
64
int output::supports_filled_polygons()
69
void output::begin_block(const position &, const position &)
73
void output::end_block()
77
double output::compute_scale(double sc, const position &ll, const position &ur)
79
distance dim = ur - ll;
80
if (desired_width != 0.0 || desired_height != 0.0) {
82
if (desired_width != 0.0) {
84
error("width specified for picture with zero width");
86
sc = dim.x/desired_width;
88
if (desired_height != 0.0) {
90
error("height specified for picture with zero height");
92
double tem = dim.y/desired_height;
97
return sc == 0.0 ? 1.0 : sc;
102
distance sdim = dim/sc;
103
double max_width = 0.0;
104
lookup_variable("maxpswid", &max_width);
105
double max_height = 0.0;
106
lookup_variable("maxpsht", &max_height);
107
if ((max_width > 0.0 && sdim.x > max_width)
108
|| (max_height > 0.0 && sdim.y > max_height)) {
109
double xscale = dim.x/max_width;
110
double yscale = dim.y/max_height;
111
return xscale > yscale ? xscale : yscale;
118
position::position(const place &pl)
121
// Use two statements to work around bug in SGI C++.
122
object *tem = pl.obj;
123
*this = tem->origin();
131
position::position() : x(0.0), y(0.0)
135
position::position(double a, double b) : x(a), y(b)
140
int operator==(const position &a, const position &b)
142
return a.x == b.x && a.y == b.y;
145
int operator!=(const position &a, const position &b)
147
return a.x != b.x || a.y != b.y;
150
position &position::operator+=(const position &a)
157
position &position::operator-=(const position &a)
164
position &position::operator*=(double a)
171
position &position::operator/=(double a)
178
position operator-(const position &a)
180
return position(-a.x, -a.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, const position &b)
190
return position(a.x - b.x, a.y - b.y);
193
position operator/(const position &a, double n)
195
return position(a.x/n, a.y/n);
198
position operator*(const position &a, double n)
200
return position(a.x*n, a.y*n);
205
double operator*(const position &a, const position &b)
207
return a.x*b.x + a.y*b.y;
210
double hypot(const position &a)
212
return hypot(a.x, a.y);
215
struct arrow_head_type {
221
void draw_arrow(const position &pos, const distance &dir,
222
const arrow_head_type &aht, const line_type <)
224
double hyp = hypot(dir);
226
error("cannot draw arrow on object with zero length");
229
position base = -dir;
230
base *= aht.height/hyp;
231
position n(dir.y, -dir.x);
232
n *= aht.width/(hyp*2.0);
234
slt.type = line_type::solid;
235
if (aht.solid && out->supports_filled_polygons()) {
238
v[1] = pos + base + n;
239
v[2] = pos + base - n;
240
// A value > 1 means fill with the current color.
241
out->polygon(v, 3, slt, 2.0);
246
v[1] = pos + base + n;
247
out->line(pos + base - n, v, 2, slt);
251
object::object() : prev(0), next(0)
259
void object::move_by(const position &)
267
void object::print_text()
276
struct bounding_box {
282
void encompass(const position &);
285
bounding_box::bounding_box()
290
void bounding_box::encompass(const position &pos)
309
void object::update_bounding_box(bounding_box *)
313
position object::origin()
315
return position(0.0,0.0);
318
position object::north()
323
position object::south()
328
position object::east()
333
position object::west()
338
position object::north_east()
343
position object::north_west()
348
position object::south_east()
353
position object::south_west()
358
position object::start()
363
position object::end()
368
position object::center()
373
double object::width()
378
double object::radius()
383
double object::height()
388
place *object::find_label(const char *)
393
segment::segment(const position &a, int n, segment *p)
394
: is_absolute(n), pos(a), next(p)
398
text_item::text_item(char *t, const char *fn, int ln)
399
: next(0), text(t), filename(fn), lineno(ln)
401
adj.h = CENTER_ADJUST;
405
text_item::~text_item()
410
object_spec::object_spec(object_type t) : type(t)
415
segment_width = segment_height = 0.0;
416
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;
444
class command_object : public object {
446
const char *filename;
449
command_object(char *, const char *, int);
451
object_type type() { return OTHER_OBJECT; }
455
command_object::command_object(char *p, const char *fn, int ln)
456
: s(p), filename(fn), lineno(ln)
460
command_object::~command_object()
465
void command_object::print()
467
out->command(s, filename, lineno);
470
object *make_command_object(char *s, const char *fn, int ln)
472
return new command_object(s, fn, ln);
475
class mark_object : public object {
481
object *make_mark_object()
483
return new mark_object();
486
mark_object::mark_object()
490
object_type mark_object::type()
495
object_list::object_list() : head(0), tail(0)
499
void object_list::append(object *obj)
502
obj->next = obj->prev = 0;
513
void object_list::wrap_up_block(object_list *ol)
516
for (p = tail; p && p->type() != MARK_OBJECT; p = p->prev)
534
text_piece::text_piece()
535
: text(0), filename(0), lineno(-1)
537
adj.h = CENTER_ADJUST;
541
text_piece::~text_piece()
546
class graphic_object : public object {
555
object_type type() = 0;
557
void add_text(text_item *, int);
558
void set_dotted(double);
559
void set_dashed(double);
560
void set_thickness(double);
561
void set_invisible();
562
virtual void set_fill(double);
565
graphic_object::graphic_object() : ntext(0), text(0), aligned(0)
569
void graphic_object::set_dotted(double wid)
571
lt.type = line_type::dotted;
575
void graphic_object::set_dashed(double wid)
577
lt.type = line_type::dashed;
581
void graphic_object::set_thickness(double th)
586
void graphic_object::set_fill(double)
590
void graphic_object::set_invisible()
592
lt.type = line_type::invisible;
595
void graphic_object::add_text(text_item *t, int a)
600
for (p = t; p; p = p->next)
605
text = new text_piece[len];
606
for (p = t, len = 0; p; p = p->next, len++) {
607
text[len].text = p->text;
609
text[len].adj = p->adj;
610
text[len].filename = p->filename;
611
text[len].lineno = p->lineno;
617
void graphic_object::print_text()
621
position d(end() - start());
622
if (d.x != 0.0 || d.y != 0.0)
623
angle = atan2(d.y, d.x);
626
out->text(center(), text, ntext, angle);
629
graphic_object::~graphic_object()
632
ad_delete(ntext) text;
635
class rectangle_object : public graphic_object {
640
rectangle_object(const position &);
641
double width() { return dim.x; }
642
double height() { return dim.y; }
643
position origin() { return cent; }
644
position center() { return cent; }
645
position north() { return position(cent.x, cent.y + dim.y/2.0); }
646
position south() { return position(cent.x, cent.y - dim.y/2.0); }
647
position east() { return position(cent.x + dim.x/2.0, cent.y); }
648
position west() { return position(cent.x - dim.x/2.0, cent.y); }
649
position north_east() { return position(cent.x + dim.x/2.0, cent.y + dim.y/2.0); }
650
position north_west() { return position(cent.x - dim.x/2.0, cent.y + dim.y/2.0); }
651
position south_east() { return position(cent.x + dim.x/2.0, cent.y - dim.y/2.0); }
652
position south_west() { return position(cent.x - dim.x/2.0, cent.y - dim.y/2.0); }
653
object_type type() = 0;
654
void update_bounding_box(bounding_box *);
655
void move_by(const position &);
658
rectangle_object::rectangle_object(const position &d)
663
void rectangle_object::update_bounding_box(bounding_box *p)
665
p->encompass(cent - dim/2.0);
666
p->encompass(cent + dim/2.0);
669
void rectangle_object::move_by(const position &a)
674
class closed_object : public rectangle_object {
676
closed_object(const position &);
677
object_type type() = 0;
678
void set_fill(double);
680
double fill; // < 0 if not filled
683
closed_object::closed_object(const position &pos)
684
: rectangle_object(pos), fill(-1.0)
688
void closed_object::set_fill(double f)
695
class box_object : public closed_object {
699
box_object(const position &, double);
700
object_type type() { return BOX_OBJECT; }
702
position north_east();
703
position north_west();
704
position south_east();
705
position south_west();
708
box_object::box_object(const position &pos, double r)
709
: closed_object(pos), xrad(dim.x > 0 ? r : -r), yrad(dim.y > 0 ? r : -r)
713
const double CHOP_FACTOR = 1.0 - 1.0/M_SQRT2;
715
position box_object::north_east()
717
return position(cent.x + dim.x/2.0 - CHOP_FACTOR*xrad,
718
cent.y + dim.y/2.0 - CHOP_FACTOR*yrad);
721
position box_object::north_west()
723
return position(cent.x - dim.x/2.0 + CHOP_FACTOR*xrad,
724
cent.y + dim.y/2.0 - CHOP_FACTOR*yrad);
727
position box_object::south_east()
729
return position(cent.x + dim.x/2.0 - CHOP_FACTOR*xrad,
730
cent.y - dim.y/2.0 + CHOP_FACTOR*yrad);
733
position box_object::south_west()
735
return position(cent.x - dim.x/2.0 + CHOP_FACTOR*xrad,
736
cent.y - dim.y/2.0 + CHOP_FACTOR*yrad);
739
void box_object::print()
741
if (lt.type == line_type::invisible && fill < 0.0)
744
distance dim2 = dim/2.0;
746
vec[0] = cent + position(dim2.x, -dim2.y);
747
vec[1] = cent + position(dim2.x, dim2.y);
748
vec[2] = cent + position(-dim2.x, dim2.y);
749
vec[3] = cent + position(-dim2.x, -dim2.y);
750
out->polygon(vec, 4, lt, fill);
753
distance abs_dim(fabs(dim.x), fabs(dim.y));
754
out->rounded_box(cent, abs_dim, fabs(xrad), lt, fill);
758
graphic_object *object_spec::make_box(position *curpos, direction *dirp)
760
static double last_box_height;
761
static double last_box_width;
762
static double last_box_radius;
763
static int have_last_box = 0;
764
if (!(flags & HAS_HEIGHT)) {
765
if ((flags & IS_SAME) && have_last_box)
766
height = last_box_height;
768
lookup_variable("boxht", &height);
770
if (!(flags & HAS_WIDTH)) {
771
if ((flags & IS_SAME) && have_last_box)
772
width = last_box_width;
774
lookup_variable("boxwid", &width);
776
if (!(flags & HAS_RADIUS)) {
777
if ((flags & IS_SAME) && have_last_box)
778
radius = last_box_radius;
780
lookup_variable("boxrad", &radius);
782
last_box_width = width;
783
last_box_height = height;
784
last_box_radius = radius;
786
radius = fabs(radius);
787
if (radius*2.0 > fabs(width))
788
radius = fabs(width/2.0);
789
if (radius*2.0 > fabs(height))
790
radius = fabs(height/2.0);
791
box_object *p = new box_object(position(width, height), radius);
792
if (!position_rectangle(p, curpos, dirp)) {
799
// return non-zero for success
801
int object_spec::position_rectangle(rectangle_object *p,
802
position *curpos, direction *dirp)
805
dir = *dirp; // ignore any direction in attribute list
809
motion.y = p->height()/2.0;
812
motion.y = -p->height()/2.0;
815
motion.x = -p->width()/2.0;
817
case RIGHT_DIRECTION:
818
motion.x = p->width()/2.0;
823
if (flags & HAS_AT) {
825
if (flags & HAS_WITH) {
829
if (!with->follow(here, &offset))
844
class block_object : public rectangle_object {
848
block_object(const position &, const object_list &ol, PTABLE(place) *t);
850
place *find_label(const char *);
852
void move_by(const position &);
856
block_object::block_object(const position &d, const object_list &ol,
858
: rectangle_object(d), oblist(ol), tbl(t)
862
block_object::~block_object()
865
object *p = oblist.head;
873
void block_object::print()
875
out->begin_block(south_west(), north_east());
876
print_object_list(oblist.head);
880
static void adjust_objectless_places(PTABLE(place) *tbl, const position &a)
882
// Adjust all the labels that aren't attached to objects.
883
PTABLE_ITERATOR(place) iter(tbl);
886
while (iter.next(&key, &pl))
887
if (key && csupper(key[0]) && pl->obj == 0) {
893
void block_object::move_by(const position &a)
896
for (object *p = oblist.head; p; p = p->next)
898
adjust_objectless_places(tbl, a);
902
place *block_object::find_label(const char *name)
904
return tbl->lookup(name);
907
object_type block_object::type()
912
graphic_object *object_spec::make_block(position *curpos, direction *dirp)
915
for (object *p = oblist.head; p; p = p->next)
916
p->update_bounding_box(&bb);
919
position m = -(bb.ll + bb.ur)/2.0;
920
for (object *p = oblist.head; p; p = p->next)
922
adjust_objectless_places(tbl, m);
925
if (flags & HAS_WIDTH)
927
if (flags & HAS_HEIGHT)
929
block_object *block = new block_object(dim, oblist, tbl);
930
if (!position_rectangle(block, curpos, dirp)) {
935
oblist.head = oblist.tail = 0;
939
class text_object : public rectangle_object {
941
text_object(const position &);
942
object_type type() { return TEXT_OBJECT; }
945
text_object::text_object(const position &d)
946
: rectangle_object(d)
950
graphic_object *object_spec::make_text(position *curpos, direction *dirp)
952
if (!(flags & HAS_HEIGHT)) {
953
lookup_variable("textht", &height);
955
for (text_item *t = text; t; t = t->next)
959
if (!(flags & HAS_WIDTH))
960
lookup_variable("textwid", &width);
961
text_object *p = new text_object(position(width, height));
962
if (!position_rectangle(p, curpos, dirp)) {
970
class ellipse_object : public closed_object {
972
ellipse_object(const position &);
973
position north_east() { return position(cent.x + dim.x/(M_SQRT2*2.0),
974
cent.y + dim.y/(M_SQRT2*2.0)); }
975
position north_west() { return position(cent.x - dim.x/(M_SQRT2*2.0),
976
cent.y + dim.y/(M_SQRT2*2.0)); }
977
position south_east() { return position(cent.x + dim.x/(M_SQRT2*2.0),
978
cent.y - dim.y/(M_SQRT2*2.0)); }
979
position south_west() { return position(cent.x - dim.x/(M_SQRT2*2.0),
980
cent.y - dim.y/(M_SQRT2*2.0)); }
981
double radius() { return dim.x/2.0; }
982
object_type type() { return ELLIPSE_OBJECT; }
986
ellipse_object::ellipse_object(const position &d)
991
void ellipse_object::print()
993
if (lt.type == line_type::invisible && fill < 0.0)
995
out->ellipse(cent, dim, lt, fill);
998
graphic_object *object_spec::make_ellipse(position *curpos, direction *dirp)
1000
static double last_ellipse_height;
1001
static double last_ellipse_width;
1002
static int have_last_ellipse = 0;
1003
if (!(flags & HAS_HEIGHT)) {
1004
if ((flags & IS_SAME) && have_last_ellipse)
1005
height = last_ellipse_height;
1007
lookup_variable("ellipseht", &height);
1009
if (!(flags & HAS_WIDTH)) {
1010
if ((flags & IS_SAME) && have_last_ellipse)
1011
width = last_ellipse_width;
1013
lookup_variable("ellipsewid", &width);
1015
last_ellipse_width = width;
1016
last_ellipse_height = height;
1017
have_last_ellipse = 1;
1018
ellipse_object *p = new ellipse_object(position(width, height));
1019
if (!position_rectangle(p, curpos, dirp)) {
1026
class circle_object : public ellipse_object {
1028
circle_object(double);
1029
object_type type() { return CIRCLE_OBJECT; }
1033
circle_object::circle_object(double diam)
1034
: ellipse_object(position(diam, diam))
1038
void circle_object::print()
1040
if (lt.type == line_type::invisible && fill < 0.0)
1042
out->circle(cent, dim.x/2.0, lt, fill);
1045
graphic_object *object_spec::make_circle(position *curpos, direction *dirp)
1047
static double last_circle_radius;
1048
static int have_last_circle = 0;
1049
if (!(flags & HAS_RADIUS)) {
1050
if ((flags & IS_SAME) && have_last_circle)
1051
radius = last_circle_radius;
1053
lookup_variable("circlerad", &radius);
1055
last_circle_radius = radius;
1056
have_last_circle = 1;
1057
circle_object *p = new circle_object(radius*2.0);
1058
if (!position_rectangle(p, curpos, dirp)) {
1065
class move_object : public graphic_object {
1069
move_object(const position &s, const position &e);
1070
position origin() { return en; }
1071
object_type type() { return MOVE_OBJECT; }
1072
void update_bounding_box(bounding_box *);
1073
void move_by(const position &);
1076
move_object::move_object(const position &s, const position &e)
1081
void move_object::update_bounding_box(bounding_box *p)
1087
void move_object::move_by(const position &a)
1093
graphic_object *object_spec::make_move(position *curpos, direction *dirp)
1095
static position last_move;
1096
static int have_last_move = 0;
1098
// No need to look at at since `at' attribute sets `from' attribute.
1099
position startpos = (flags & HAS_FROM) ? from : *curpos;
1100
if (!(flags & HAS_SEGMENT)) {
1101
if ((flags && IS_SAME) && have_last_move)
1102
segment_pos = last_move;
1106
segment_pos.y = segment_height;
1108
case DOWN_DIRECTION:
1109
segment_pos.y = -segment_height;
1111
case LEFT_DIRECTION:
1112
segment_pos.x = -segment_width;
1114
case RIGHT_DIRECTION:
1115
segment_pos.x = segment_width;
1122
segment_list = new segment(segment_pos, segment_is_absolute, segment_list);
1123
// Reverse the segment_list so that it's in forward order.
1124
segment *old = segment_list;
1127
segment *tem = old->next;
1128
old->next = segment_list;
1132
// Compute the end position.
1133
position endpos = startpos;
1134
for (segment *s = segment_list; s; s = s->next)
1140
last_move = endpos - startpos;
1141
move_object *p = new move_object(startpos, endpos);
1146
class linear_object : public graphic_object {
1148
char arrow_at_start;
1150
arrow_head_type aht;
1154
linear_object(const position &s, const position &e);
1155
position start() { return strt; }
1156
position end() { return en; }
1157
void move_by(const position &);
1158
void update_bounding_box(bounding_box *) = 0;
1159
object_type type() = 0;
1160
void add_arrows(int at_start, int at_end, const arrow_head_type &);
1163
class line_object : public linear_object {
1168
line_object(const position &s, const position &e, position *, int);
1170
position origin() { return strt; }
1171
position center() { return (strt + en)/2.0; }
1172
position north() { return (en.y - strt.y) > 0 ? en : strt; }
1173
position south() { return (en.y - strt.y) < 0 ? en : strt; }
1174
position east() { return (en.x - strt.x) > 0 ? en : strt; }
1175
position west() { return (en.x - strt.x) < 0 ? en : strt; }
1176
object_type type() { return LINE_OBJECT; }
1177
void update_bounding_box(bounding_box *);
1179
void move_by(const position &);
1182
class arrow_object : public line_object {
1184
arrow_object(const position &, const position &, position *, int);
1185
object_type type() { return ARROW_OBJECT; }
1188
class spline_object : public line_object {
1190
spline_object(const position &, const position &, position *, int);
1191
object_type type() { return SPLINE_OBJECT; }
1193
void update_bounding_box(bounding_box *);
1196
linear_object::linear_object(const position &s, const position &e)
1197
: arrow_at_start(0), arrow_at_end(0), strt(s), en(e)
1201
void linear_object::move_by(const position &a)
1207
void linear_object::add_arrows(int at_start, int at_end,
1208
const arrow_head_type &a)
1210
arrow_at_start = at_start;
1211
arrow_at_end = at_end;
1215
line_object::line_object(const position &s, const position &e,
1217
: linear_object(s, e), v(p), n(i)
1221
void line_object::print()
1223
if (lt.type == line_type::invisible)
1225
out->line(strt, v, n, lt);
1227
draw_arrow(strt, strt-v[0], aht, lt);
1229
draw_arrow(en, v[n-1] - (n > 1 ? v[n - 2] : strt), aht, lt);
1232
void line_object::update_bounding_box(bounding_box *p)
1235
for (int i = 0; i < n; i++)
1239
void line_object::move_by(const position &pos)
1241
linear_object::move_by(pos);
1242
for (int i = 0; i < n; i++)
1246
void spline_object::update_bounding_box(bounding_box *p)
1258
[ the points for the Bezier cubic ]
1266
(1-t)^3*p1 + 3*t*(t - 1)^2*p2 + 3*t^2*(1-t)*p3 + t^3*p4
1267
[ the equation for the Bezier cubic ]
1269
= .125*q1 + .75*q2 + .125*q3
1272
for (int i = 1; i < n; i++)
1273
p->encompass((i == 1 ? strt : v[i-2])*.125 + v[i-1]*.75 + v[i]*.125);
1276
arrow_object::arrow_object(const position &s, const position &e,
1278
: line_object(s, e, p, i)
1282
spline_object::spline_object(const position &s, const position &e,
1284
: line_object(s, e, p, i)
1288
void spline_object::print()
1290
if (lt.type == line_type::invisible)
1292
out->spline(strt, v, n, lt);
1294
draw_arrow(strt, strt-v[0], aht, lt);
1296
draw_arrow(en, v[n-1] - (n > 1 ? v[n - 2] : strt), aht, lt);
1299
line_object::~line_object()
1304
linear_object *object_spec::make_line(position *curpos, direction *dirp)
1306
static position last_line;
1307
static int have_last_line = 0;
1309
// No need to look at at since `at' attribute sets `from' attribute.
1310
position startpos = (flags & HAS_FROM) ? from : *curpos;
1311
if (!(flags & HAS_SEGMENT)) {
1312
if ((flags & IS_SAME) && (type == LINE_OBJECT || type == ARROW_OBJECT)
1314
segment_pos = last_line;
1318
segment_pos.y = segment_height;
1320
case DOWN_DIRECTION:
1321
segment_pos.y = -segment_height;
1323
case LEFT_DIRECTION:
1324
segment_pos.x = -segment_width;
1326
case RIGHT_DIRECTION:
1327
segment_pos.x = segment_width;
1333
segment_list = new segment(segment_pos, segment_is_absolute, segment_list);
1334
// reverse the segment_list so that it's in forward order
1335
segment *old = segment_list;
1338
segment *tem = old->next;
1339
old->next = segment_list;
1343
// Absolutise all movements
1344
position endpos = startpos;
1347
for (s = segment_list; s; s = s->next, nsegments++)
1353
s->is_absolute = 1; // to avoid confusion
1357
position *v = new position[nsegments];
1359
for (s = segment_list; s; s = s->next, i++)
1361
if (flags & IS_DEFAULT_CHOPPED) {
1362
lookup_variable("circlerad", &start_chop);
1363
end_chop = start_chop;
1364
flags |= IS_CHOPPED;
1366
if (flags & IS_CHOPPED) {
1367
position start_chop_vec, end_chop_vec;
1368
if (start_chop != 0.0) {
1369
start_chop_vec = v[0] - startpos;
1370
start_chop_vec *= start_chop / hypot(start_chop_vec);
1372
if (end_chop != 0.0) {
1373
end_chop_vec = (v[nsegments - 1]
1374
- (nsegments > 1 ? v[nsegments - 2] : startpos));
1375
end_chop_vec *= end_chop / hypot(end_chop_vec);
1377
startpos += start_chop_vec;
1378
v[nsegments - 1] -= end_chop_vec;
1379
endpos -= end_chop_vec;
1383
p = new spline_object(startpos, endpos, v, nsegments);
1386
p = new arrow_object(startpos, endpos, v, nsegments);
1389
p = new line_object(startpos, endpos, v, nsegments);
1395
last_line = endpos - startpos;
1400
class arc_object : public linear_object {
1405
arc_object(int, const position &, const position &, const position &);
1406
position origin() { return cent; }
1407
position center() { return cent; }
1408
double radius() { return rad; }
1413
position north_east();
1414
position north_west();
1415
position south_east();
1416
position south_west();
1417
void update_bounding_box(bounding_box *);
1418
object_type type() { return ARC_OBJECT; }
1420
void move_by(const position &pos);
1423
arc_object::arc_object(int cw, const position &s, const position &e,
1425
: linear_object(s, e), clockwise(cw), cent(c)
1430
void arc_object::move_by(const position &pos)
1432
linear_object::move_by(pos);
1436
// we get arc corners from the corresponding circle
1438
position arc_object::north()
1440
position result(cent);
1445
position arc_object::south()
1447
position result(cent);
1452
position arc_object::east()
1454
position result(cent);
1459
position arc_object::west()
1461
position result(cent);
1466
position arc_object::north_east()
1468
position result(cent);
1469
result.x += rad/M_SQRT2;
1470
result.y += rad/M_SQRT2;
1474
position arc_object::north_west()
1476
position result(cent);
1477
result.x -= rad/M_SQRT2;
1478
result.y += rad/M_SQRT2;
1482
position arc_object::south_east()
1484
position result(cent);
1485
result.x += rad/M_SQRT2;
1486
result.y -= rad/M_SQRT2;
1490
position arc_object::south_west()
1492
position result(cent);
1493
result.x -= rad/M_SQRT2;
1494
result.y -= rad/M_SQRT2;
1499
void arc_object::print()
1501
if (lt.type == line_type::invisible)
1504
out->arc(en, cent, strt, lt);
1506
out->arc(strt, cent, en, lt);
1507
if (arrow_at_start) {
1508
position c = cent - strt;
1510
(clockwise ? position(c.y, -c.x) : position(-c.y, c.x)),
1514
position e = en - cent;
1516
(clockwise ? position(e.y, -e.x) : position(-e.y, e.x)),
1521
inline double max(double a, double b)
1523
return a > b ? a : b;
1526
void arc_object::update_bounding_box(bounding_box *p)
1530
position start_offset = strt - cent;
1531
if (start_offset.x == 0.0 && start_offset.y == 0.0)
1533
position end_offset = en - cent;
1534
if (end_offset.x == 0.0 && end_offset.y == 0.0)
1536
double start_quad = atan2(start_offset.y, start_offset.x)/(M_PI/2.0);
1537
double end_quad = atan2(end_offset.y, end_offset.x)/(M_PI/2.0);
1539
double temp = start_quad;
1540
start_quad = end_quad;
1543
if (start_quad < 0.0)
1545
while (end_quad <= start_quad)
1547
double radius = max(hypot(start_offset), hypot(end_offset));
1548
for (int q = int(start_quad) + 1; q < end_quad; q++) {
1564
p->encompass(cent + offset);
1568
// We ignore the with attribute. The at attribute always refers to the center.
1570
linear_object *object_spec::make_arc(position *curpos, direction *dirp)
1573
int cw = (flags & IS_CLOCKWISE) != 0;
1574
// compute the start
1576
if (flags & HAS_FROM)
1580
if (!(flags & HAS_RADIUS))
1581
lookup_variable("arcrad", &radius);
1587
position m(radius, radius);
1588
// Adjust the signs.
1590
if (dir == DOWN_DIRECTION || dir == LEFT_DIRECTION)
1592
if (dir == DOWN_DIRECTION || dir == RIGHT_DIRECTION)
1594
*dirp = direction((dir + 3) % 4);
1597
if (dir == UP_DIRECTION || dir == LEFT_DIRECTION)
1599
if (dir == DOWN_DIRECTION || dir == LEFT_DIRECTION)
1601
*dirp = direction((dir + 1) % 4);
1603
endpos = startpos + m;
1605
// compute the center
1609
else if (startpos == endpos)
1610
centerpos = startpos;
1612
position h = (endpos - startpos)/2.0;
1613
double d = hypot(h);
1616
// make the radius big enough
1619
double alpha = acos(d/radius);
1620
double theta = atan2(h.y, h.x);
1625
centerpos = position(cos(theta), sin(theta))*radius + startpos;
1627
arc_object *p = new arc_object(cw, startpos, endpos, centerpos);
1632
graphic_object *object_spec::make_linear(position *curpos, direction *dirp)
1635
if (type == ARC_OBJECT)
1636
obj = make_arc(curpos, dirp);
1638
obj = make_line(curpos, dirp);
1639
if (type == ARROW_OBJECT
1640
&& (flags & (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD)) == 0)
1641
flags |= HAS_RIGHT_ARROW_HEAD;
1642
if (obj && (flags & (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD))) {
1644
int at_start = (flags & HAS_LEFT_ARROW_HEAD) != 0;
1645
int at_end = (flags & HAS_RIGHT_ARROW_HEAD) != 0;
1646
if (flags & HAS_HEIGHT)
1649
lookup_variable("arrowht", &a.height);
1650
if (flags & HAS_WIDTH)
1653
lookup_variable("arrowwid", &a.width);
1655
lookup_variable("arrowhead", &solid);
1656
a.solid = solid != 0.0;
1657
obj->add_arrows(at_start, at_end, a);
1662
object *object_spec::make_object(position *curpos, direction *dirp)
1664
graphic_object *obj = 0;
1667
obj = make_block(curpos, dirp);
1670
obj = make_box(curpos, dirp);
1673
obj = make_text(curpos, dirp);
1675
case ELLIPSE_OBJECT:
1676
obj = make_ellipse(curpos, dirp);
1679
obj = make_circle(curpos, dirp);
1682
obj = make_move(curpos, dirp);
1688
obj = make_linear(curpos, dirp);
1697
if (flags & IS_INVISIBLE)
1698
obj->set_invisible();
1700
obj->add_text(text, (flags & IS_ALIGNED) != 0);
1701
if (flags & IS_DOTTED)
1702
obj->set_dotted(dash_width);
1703
else if (flags & IS_DASHED)
1704
obj->set_dashed(dash_width);
1706
if (flags & HAS_THICKNESS)
1709
lookup_variable("linethick", &th);
1710
obj->set_thickness(th);
1711
if (flags & (IS_DEFAULT_FILLED|IS_FILLED)) {
1712
if (flags & IS_DEFAULT_FILLED)
1713
lookup_variable("fillval", &fill);
1715
error("bad fill value %1", fill);
1717
obj->set_fill(fill);
1723
struct string_list {
1726
string_list(char *);
1730
string_list::string_list(char *s)
1735
string_list::~string_list()
1740
/* A path is used to hold the argument to the with attribute. For example,
1741
`.nw' or `.A.s' or `.A'. The major operation on a path is to take a
1742
place and follow the path through the place to place within the place.
1743
Note that `.A.B.C.sw' will work. */
1745
path::path(corner c)
1746
: crn(c), label_list(0), ypath(0)
1750
path::path(char *l, corner c)
1753
label_list = new string_list(l);
1758
while (label_list) {
1759
string_list *tem = label_list;
1760
label_list = label_list->next;
1766
void path::append(corner c)
1772
void path::append(char *s)
1775
for (p = &label_list; *p; p = &(*p)->next)
1777
*p = new string_list(s);
1780
void path::set_ypath(path *p)
1785
// return non-zero for success
1787
int path::follow(const place &pl, place *result) const
1789
const place *p = &pl;
1790
for (string_list *lb = label_list; lb; lb = lb->next)
1791
if (p->obj == 0 || (p = p->obj->find_label(lb->str)) == 0) {
1792
lex_error("object does not contain a place `%1'", lb->str);
1795
if (crn == 0 || p->obj == 0)
1798
position pos = ((p->obj)->*(crn))();
1805
if (!ypath->follow(pl, &tem))
1808
if (result->obj != tem.obj)
1814
void print_object_list(object *p)
1816
for (; p; p = p->next) {
1822
void print_picture(object *obj)
1825
for (object *p = obj; p; p = p->next)
1826
p->update_bounding_box(&bb);
1828
lookup_variable("scale", &scale);
1829
out->start_picture(scale, bb.ll, bb.ur);
1830
print_object_list(obj);
1831
out->finish_picture();