1114402Sru// -*- C++ -*-
2151497Sru/* Copyright (C) 1989, 1990, 1991, 1992, 2001, 2002, 2003, 2004, 2005
3114402Sru     Free Software Foundation, Inc.
4114402Sru     Written by James Clark (jjc@jclark.com)
5114402Sru
6114402SruThis file is part of groff.
7114402Sru
8114402Srugroff is free software; you can redistribute it and/or modify it under
9114402Sruthe terms of the GNU General Public License as published by the Free
10114402SruSoftware Foundation; either version 2, or (at your option) any later
11114402Sruversion.
12114402Sru
13114402Srugroff is distributed in the hope that it will be useful, but WITHOUT ANY
14114402SruWARRANTY; without even the implied warranty of MERCHANTABILITY or
15114402SruFITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16114402Srufor more details.
17114402Sru
18114402SruYou should have received a copy of the GNU General Public License along
19114402Sruwith groff; see the file COPYING.  If not, write to the Free Software
20151497SruFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
21114402Sru
22114402Sru#include "pic.h"
23114402Sru#include "ptable.h"
24114402Sru#include "object.h"
25114402Sru
26114402Sruvoid print_object_list(object *);
27114402Sru
28114402Sruline_type::line_type()
29114402Sru: type(solid), thickness(1.0)
30114402Sru{
31114402Sru}
32114402Sru
33114402Sruoutput::output() : args(0), desired_height(0.0), desired_width(0.0)
34114402Sru{
35114402Sru}
36114402Sru
37114402Sruoutput::~output()
38114402Sru{
39114402Sru  a_delete args;
40114402Sru}
41114402Sru
42114402Sruvoid output::set_desired_width_height(double wid, double ht)
43114402Sru{
44114402Sru  desired_width = wid;
45114402Sru  desired_height = ht;
46114402Sru}
47114402Sru
48114402Sruvoid output::set_args(const char *s)
49114402Sru{
50114402Sru  a_delete args;
51114402Sru  if (s == 0 || *s == '\0')
52114402Sru    args = 0;
53114402Sru  else
54114402Sru    args = strsave(s);
55114402Sru}
56114402Sru
57114402Sruint output::supports_filled_polygons()
58114402Sru{
59114402Sru  return 0;
60114402Sru}
61114402Sru
62114402Sruvoid output::begin_block(const position &, const position &)
63114402Sru{
64114402Sru}
65114402Sru
66114402Sruvoid output::end_block()
67114402Sru{
68114402Sru}
69114402Sru
70114402Srudouble output::compute_scale(double sc, const position &ll, const position &ur)
71114402Sru{
72114402Sru  distance dim = ur - ll;
73114402Sru  if (desired_width != 0.0 || desired_height != 0.0) {
74114402Sru    sc = 0.0;
75114402Sru    if (desired_width != 0.0) {
76114402Sru      if (dim.x == 0.0)
77114402Sru	error("width specified for picture with zero width");
78114402Sru      else
79114402Sru	sc = dim.x/desired_width;
80114402Sru    }
81114402Sru    if (desired_height != 0.0) {
82114402Sru      if (dim.y == 0.0)
83114402Sru	error("height specified for picture with zero height");
84114402Sru      else {
85114402Sru	double tem = dim.y/desired_height;
86114402Sru	if (tem > sc)
87114402Sru	  sc = tem;
88114402Sru      }
89114402Sru    }
90114402Sru    return sc == 0.0 ? 1.0 : sc;
91114402Sru  }
92114402Sru  else {
93114402Sru    if (sc <= 0.0)
94114402Sru      sc = 1.0;
95114402Sru    distance sdim = dim/sc;
96114402Sru    double max_width = 0.0;
97114402Sru    lookup_variable("maxpswid", &max_width);
98114402Sru    double max_height = 0.0;
99114402Sru    lookup_variable("maxpsht", &max_height);
100114402Sru    if ((max_width > 0.0 && sdim.x > max_width)
101114402Sru	|| (max_height > 0.0 && sdim.y > max_height)) {
102114402Sru      double xscale = dim.x/max_width;
103114402Sru      double yscale = dim.y/max_height;
104114402Sru      return xscale > yscale ? xscale : yscale;
105114402Sru    }
106114402Sru    else
107114402Sru      return sc;
108114402Sru  }
109114402Sru}
110114402Sru
111114402Sruposition::position(const place &pl)
112114402Sru{
113114402Sru  if (pl.obj != 0) {
114114402Sru    // Use two statements to work around bug in SGI C++.
115114402Sru    object *tem = pl.obj;
116114402Sru    *this = tem->origin();
117114402Sru  }
118114402Sru  else {
119114402Sru    x = pl.x;
120114402Sru    y = pl.y;
121114402Sru  }
122114402Sru}
123114402Sru
124114402Sruposition::position() : x(0.0), y(0.0)
125114402Sru{
126114402Sru}
127114402Sru
128114402Sruposition::position(double a, double b) : x(a), y(b)
129114402Sru{
130114402Sru}
131114402Sru
132114402Sru
133114402Sruint operator==(const position &a, const position &b)
134114402Sru{
135114402Sru  return a.x == b.x && a.y == b.y;
136114402Sru}
137114402Sru
138114402Sruint operator!=(const position &a, const position &b)
139114402Sru{
140114402Sru  return a.x != b.x || a.y != b.y;
141114402Sru}
142114402Sru
143114402Sruposition &position::operator+=(const position &a)
144114402Sru{
145114402Sru  x += a.x;
146114402Sru  y += a.y;
147114402Sru  return *this;
148114402Sru}
149114402Sru
150114402Sruposition &position::operator-=(const position &a)
151114402Sru{
152114402Sru  x -= a.x;
153114402Sru  y -= a.y;
154114402Sru  return *this;
155114402Sru}
156114402Sru
157114402Sruposition &position::operator*=(double a)
158114402Sru{
159114402Sru  x *= a;
160114402Sru  y *= a;
161114402Sru  return *this;
162114402Sru}
163114402Sru
164114402Sruposition &position::operator/=(double a)
165114402Sru{
166114402Sru  x /= a;
167114402Sru  y /= a;
168114402Sru  return *this;
169114402Sru}
170114402Sru
171114402Sruposition operator-(const position &a)
172114402Sru{
173114402Sru  return position(-a.x, -a.y);
174114402Sru}
175114402Sru
176114402Sruposition operator+(const position &a, const position &b)
177114402Sru{
178114402Sru  return position(a.x + b.x, a.y + b.y);
179114402Sru}
180114402Sru
181114402Sruposition operator-(const position &a, const position &b)
182114402Sru{
183114402Sru  return position(a.x - b.x, a.y - b.y);
184114402Sru}
185114402Sru
186114402Sruposition operator/(const position &a, double n)
187114402Sru{
188114402Sru  return position(a.x/n, a.y/n);
189114402Sru}
190114402Sru
191114402Sruposition operator*(const position &a, double n)
192114402Sru{
193114402Sru  return position(a.x*n, a.y*n);
194114402Sru}
195114402Sru
196114402Sru// dot product
197114402Sru
198114402Srudouble operator*(const position &a, const position &b)
199114402Sru{
200114402Sru  return a.x*b.x + a.y*b.y;
201114402Sru}
202114402Sru
203114402Srudouble hypot(const position &a)
204114402Sru{
205151497Sru  return groff_hypot(a.x, a.y);
206114402Sru}
207114402Sru
208114402Srustruct arrow_head_type {
209114402Sru  double height;
210114402Sru  double width;
211114402Sru  int solid;
212114402Sru};
213114402Sru
214114402Sruvoid draw_arrow(const position &pos, const distance &dir,
215114402Sru		const arrow_head_type &aht, const line_type &lt,
216114402Sru		char *outline_color_for_fill)
217114402Sru{
218114402Sru  double hyp = hypot(dir);
219114402Sru  if (hyp == 0.0) {
220114402Sru    error("cannot draw arrow on object with zero length");
221114402Sru    return;
222114402Sru  }
223114402Sru  position base = -dir;
224114402Sru  base *= aht.height/hyp;
225114402Sru  position n(dir.y, -dir.x);
226114402Sru  n *= aht.width/(hyp*2.0);
227114402Sru  line_type slt = lt;
228114402Sru  slt.type = line_type::solid;
229114402Sru  if (aht.solid && out->supports_filled_polygons()) {
230114402Sru    position v[3];
231114402Sru    v[0] = pos;
232114402Sru    v[1] = pos + base + n;
233114402Sru    v[2] = pos + base - n;
234114402Sru    // fill with outline color
235114402Sru    out->set_color(outline_color_for_fill, outline_color_for_fill);
236151497Sru    // make stroke thin to avoid arrow sticking
237151497Sru    slt.thickness = 0.1;
238114402Sru    out->polygon(v, 3, slt, 1);
239114402Sru  }
240114402Sru  else {
241151497Sru    // use two line segments to avoid arrow sticking
242151497Sru    out->line(pos + base - n, &pos, 1, slt);
243151497Sru    out->line(pos + base + n, &pos, 1, slt);
244114402Sru  }
245114402Sru}
246114402Sru
247114402Sruobject::object() : prev(0), next(0)
248114402Sru{
249114402Sru}
250114402Sru
251114402Sruobject::~object()
252114402Sru{
253114402Sru}
254114402Sru
255114402Sruvoid object::move_by(const position &)
256114402Sru{
257114402Sru}
258114402Sru
259114402Sruvoid object::print()
260114402Sru{
261114402Sru}
262114402Sru
263114402Sruvoid object::print_text()
264114402Sru{
265114402Sru}
266114402Sru
267114402Sruint object::blank()
268114402Sru{
269114402Sru  return 0;
270114402Sru}
271114402Sru
272114402Srustruct bounding_box {
273114402Sru  int blank;
274114402Sru  position ll;
275114402Sru  position ur;
276114402Sru
277114402Sru  bounding_box();
278114402Sru  void encompass(const position &);
279114402Sru};
280114402Sru
281114402Srubounding_box::bounding_box()
282114402Sru: blank(1)
283114402Sru{
284114402Sru}
285114402Sru
286114402Sruvoid bounding_box::encompass(const position &pos)
287114402Sru{
288114402Sru  if (blank) {
289114402Sru    ll = pos;
290114402Sru    ur = pos;
291114402Sru    blank = 0;
292114402Sru  }
293114402Sru  else {
294114402Sru    if (pos.x < ll.x)
295114402Sru      ll.x = pos.x;
296114402Sru    if (pos.y < ll.y)
297114402Sru      ll.y = pos.y;
298114402Sru    if (pos.x > ur.x)
299114402Sru      ur.x = pos.x;
300114402Sru    if (pos.y > ur.y)
301114402Sru      ur.y = pos.y;
302114402Sru  }
303114402Sru}
304114402Sru
305114402Sruvoid object::update_bounding_box(bounding_box *)
306114402Sru{
307114402Sru}
308114402Sru
309114402Sruposition object::origin()
310114402Sru{
311114402Sru  return position(0.0,0.0);
312114402Sru}
313114402Sru
314114402Sruposition object::north()
315114402Sru{
316114402Sru  return origin();
317114402Sru}
318114402Sru
319114402Sruposition object::south()
320114402Sru{
321114402Sru  return origin();
322114402Sru}
323114402Sru
324114402Sruposition object::east()
325114402Sru{
326114402Sru  return origin();
327114402Sru}
328114402Sru
329114402Sruposition object::west()
330114402Sru{
331114402Sru  return origin();
332114402Sru}
333114402Sru
334114402Sruposition object::north_east()
335114402Sru{
336114402Sru  return origin();
337114402Sru}
338114402Sru
339114402Sruposition object::north_west()
340114402Sru{
341114402Sru  return origin();
342114402Sru}
343114402Sru
344114402Sruposition object::south_east()
345114402Sru{
346114402Sru  return origin();
347114402Sru}
348114402Sru
349114402Sruposition object::south_west()
350114402Sru{
351114402Sru  return origin();
352114402Sru}
353114402Sru
354114402Sruposition object::start()
355114402Sru{
356114402Sru  return origin();
357114402Sru}
358114402Sru
359114402Sruposition object::end()
360114402Sru{
361114402Sru  return origin();
362114402Sru}
363114402Sru
364114402Sruposition object::center()
365114402Sru{
366114402Sru  return origin();
367114402Sru}
368114402Sru
369114402Srudouble object::width()
370114402Sru{
371114402Sru  return 0.0;
372114402Sru}
373114402Sru
374114402Srudouble object::radius()
375114402Sru{
376114402Sru  return 0.0;
377114402Sru}
378114402Sru
379114402Srudouble object::height()
380114402Sru{
381114402Sru  return 0.0;
382114402Sru}
383114402Sru
384114402Sruplace *object::find_label(const char *)
385114402Sru{
386114402Sru  return 0;
387114402Sru}
388114402Sru
389114402Srusegment::segment(const position &a, int n, segment *p)
390114402Sru: is_absolute(n), pos(a), next(p)
391114402Sru{
392114402Sru}
393114402Sru
394114402Srutext_item::text_item(char *t, const char *fn, int ln)
395114402Sru: next(0), text(t), filename(fn), lineno(ln)
396114402Sru{
397114402Sru  adj.h = CENTER_ADJUST;
398114402Sru  adj.v = NONE_ADJUST;
399114402Sru}
400114402Sru
401114402Srutext_item::~text_item()
402114402Sru{
403114402Sru  a_delete text;
404114402Sru}
405114402Sru
406114402Sruobject_spec::object_spec(object_type t) : type(t)
407114402Sru{
408114402Sru  flags = 0;
409114402Sru  tbl = 0;
410114402Sru  segment_list = 0;
411114402Sru  segment_width = segment_height = 0.0;
412114402Sru  segment_is_absolute = 0;
413114402Sru  text = 0;
414114402Sru  shaded = 0;
415114402Sru  outlined = 0;
416114402Sru  with = 0;
417114402Sru  dir = RIGHT_DIRECTION;
418114402Sru}
419114402Sru
420114402Sruobject_spec::~object_spec()
421114402Sru{
422114402Sru  delete tbl;
423114402Sru  while (segment_list != 0) {
424114402Sru    segment *tem = segment_list;
425114402Sru    segment_list = segment_list->next;
426114402Sru    delete tem;
427114402Sru  }
428114402Sru  object *p = oblist.head;
429114402Sru  while (p != 0) {
430114402Sru    object *tem = p;
431114402Sru    p = p->next;
432114402Sru    delete tem;
433114402Sru  }
434114402Sru  while (text != 0) {
435114402Sru    text_item *tem = text;
436114402Sru    text = text->next;
437114402Sru    delete tem;
438114402Sru  }
439114402Sru  delete with;
440114402Sru  a_delete shaded;
441114402Sru  a_delete outlined;
442114402Sru}
443114402Sru
444114402Sruclass command_object : public object {
445114402Sru  char *s;
446114402Sru  const char *filename;
447114402Sru  int lineno;
448114402Srupublic:
449114402Sru  command_object(char *, const char *, int);
450114402Sru  ~command_object();
451114402Sru  object_type type() { return OTHER_OBJECT; }
452114402Sru  void print();
453114402Sru};
454114402Sru
455114402Srucommand_object::command_object(char *p, const char *fn, int ln)
456114402Sru: s(p), filename(fn), lineno(ln)
457114402Sru{
458114402Sru}
459114402Sru
460114402Srucommand_object::~command_object()
461114402Sru{
462114402Sru  a_delete s;
463114402Sru}
464114402Sru
465114402Sruvoid command_object::print()
466114402Sru{
467114402Sru  out->command(s, filename, lineno);
468114402Sru}
469114402Sru
470114402Sruobject *make_command_object(char *s, const char *fn, int ln)
471114402Sru{
472114402Sru  return new command_object(s, fn, ln);
473114402Sru}
474114402Sru
475114402Sruclass mark_object : public object {
476114402Srupublic:
477114402Sru  mark_object();
478114402Sru  object_type type();
479114402Sru};
480114402Sru
481114402Sruobject *make_mark_object()
482114402Sru{
483114402Sru  return new mark_object();
484114402Sru}
485114402Sru
486114402Srumark_object::mark_object()
487114402Sru{
488114402Sru}
489114402Sru
490114402Sruobject_type mark_object::type()
491114402Sru{
492114402Sru  return MARK_OBJECT;
493114402Sru}
494114402Sru
495114402Sruobject_list::object_list() : head(0), tail(0)
496114402Sru{
497114402Sru}
498114402Sru
499114402Sruvoid object_list::append(object *obj)
500114402Sru{
501114402Sru  if (tail == 0) {
502114402Sru    obj->next = obj->prev = 0;
503114402Sru    head = tail = obj;
504114402Sru  }
505114402Sru  else {
506114402Sru    obj->prev = tail;
507114402Sru    obj->next = 0;
508114402Sru    tail->next = obj;
509114402Sru    tail = obj;
510114402Sru  }
511114402Sru}
512114402Sru
513114402Sruvoid object_list::wrap_up_block(object_list *ol)
514114402Sru{
515114402Sru  object *p;
516114402Sru  for (p = tail; p && p->type() != MARK_OBJECT; p = p->prev)
517114402Sru    ;
518114402Sru  assert(p != 0);
519114402Sru  ol->head = p->next;
520114402Sru  if (ol->head) {
521114402Sru    ol->tail = tail;
522114402Sru    ol->head->prev = 0;
523114402Sru  }
524114402Sru  else
525114402Sru    ol->tail = 0;
526114402Sru  tail = p->prev;
527114402Sru  if (tail)
528114402Sru    tail->next = 0;
529114402Sru  else
530114402Sru    head = 0;
531114402Sru  delete p;
532114402Sru}
533114402Sru
534114402Srutext_piece::text_piece()
535114402Sru: text(0), filename(0), lineno(-1)
536114402Sru{
537114402Sru  adj.h = CENTER_ADJUST;
538114402Sru  adj.v = NONE_ADJUST;
539114402Sru}
540114402Sru
541114402Srutext_piece::~text_piece()
542114402Sru{
543114402Sru  a_delete text;
544114402Sru}
545114402Sru
546114402Sruclass graphic_object : public object {
547114402Sru  int ntext;
548114402Sru  text_piece *text;
549114402Sru  int aligned;
550114402Sruprotected:
551114402Sru  line_type lt;
552114402Sru  char *outline_color;
553114402Sru  char *color_fill;
554114402Srupublic:
555114402Sru  graphic_object();
556114402Sru  ~graphic_object();
557114402Sru  object_type type() = 0;
558114402Sru  void print_text();
559114402Sru  void add_text(text_item *, int);
560114402Sru  void set_dotted(double);
561114402Sru  void set_dashed(double);
562114402Sru  void set_thickness(double);
563114402Sru  void set_invisible();
564114402Sru  void set_outline_color(char *);
565114402Sru  char *get_outline_color();
566114402Sru  virtual void set_fill(double);
567114402Sru  virtual void set_fill_color(char *);
568114402Sru};
569114402Sru
570114402Srugraphic_object::graphic_object()
571114402Sru: ntext(0), text(0), aligned(0), outline_color(0), color_fill(0)
572114402Sru{
573114402Sru}
574114402Sru
575114402Sruvoid graphic_object::set_dotted(double wid)
576114402Sru{
577114402Sru  lt.type = line_type::dotted;
578114402Sru  lt.dash_width = wid;
579114402Sru}
580114402Sru
581114402Sruvoid graphic_object::set_dashed(double wid)
582114402Sru{
583114402Sru  lt.type = line_type::dashed;
584114402Sru  lt.dash_width = wid;
585114402Sru}
586114402Sru
587114402Sruvoid graphic_object::set_thickness(double th)
588114402Sru{
589114402Sru  lt.thickness = th;
590114402Sru}
591114402Sru
592114402Sruvoid graphic_object::set_fill(double)
593114402Sru{
594114402Sru}
595114402Sru
596114402Sruvoid graphic_object::set_fill_color(char *c)
597114402Sru{
598114402Sru  color_fill = strsave(c);
599114402Sru}
600114402Sru
601114402Sruvoid graphic_object::set_outline_color(char *c)
602114402Sru{
603114402Sru  outline_color = strsave(c);
604114402Sru}
605114402Sru
606114402Sruchar *graphic_object::get_outline_color()
607114402Sru{
608114402Sru  return outline_color;
609114402Sru}
610114402Sru
611114402Sruvoid graphic_object::set_invisible()
612114402Sru{
613114402Sru  lt.type = line_type::invisible;
614114402Sru}
615114402Sru
616114402Sruvoid graphic_object::add_text(text_item *t, int a)
617114402Sru{
618114402Sru  aligned = a;
619114402Sru  int len = 0;
620114402Sru  text_item *p;
621114402Sru  for (p = t; p; p = p->next)
622114402Sru    len++;
623114402Sru  if (len == 0)
624114402Sru    text = 0;
625114402Sru  else {
626114402Sru    text = new text_piece[len];
627114402Sru    for (p = t, len = 0; p; p = p->next, len++) {
628114402Sru      text[len].text = p->text;
629114402Sru      p->text = 0;
630114402Sru      text[len].adj = p->adj;
631114402Sru      text[len].filename = p->filename;
632114402Sru      text[len].lineno = p->lineno;
633114402Sru    }
634114402Sru  }
635114402Sru  ntext = len;
636114402Sru}
637114402Sru
638114402Sruvoid graphic_object::print_text()
639114402Sru{
640114402Sru  double angle = 0.0;
641114402Sru  if (aligned) {
642114402Sru    position d(end() - start());
643114402Sru    if (d.x != 0.0 || d.y != 0.0)
644114402Sru      angle = atan2(d.y, d.x);
645114402Sru  }
646114402Sru  if (text != 0) {
647114402Sru    out->set_color(color_fill, get_outline_color());
648114402Sru    out->text(center(), text, ntext, angle);
649114402Sru    out->reset_color();
650114402Sru  }
651114402Sru}
652114402Sru
653114402Srugraphic_object::~graphic_object()
654114402Sru{
655114402Sru  if (text)
656114402Sru    ad_delete(ntext) text;
657114402Sru}
658114402Sru
659114402Sruclass rectangle_object : public graphic_object {
660114402Sruprotected:
661114402Sru  position cent;
662114402Sru  position dim;
663114402Srupublic:
664114402Sru  rectangle_object(const position &);
665114402Sru  double width() { return dim.x; }
666114402Sru  double height() { return dim.y; }
667114402Sru  position origin() { return cent; }
668114402Sru  position center() { return cent; }
669114402Sru  position north() { return position(cent.x, cent.y + dim.y/2.0); }
670114402Sru  position south() { return position(cent.x, cent.y - dim.y/2.0); }
671114402Sru  position east() { return position(cent.x + dim.x/2.0, cent.y); }
672114402Sru  position west() { return position(cent.x - dim.x/2.0, cent.y); }
673114402Sru  position north_east() { return position(cent.x + dim.x/2.0, cent.y + dim.y/2.0); }
674114402Sru  position north_west() { return position(cent.x - dim.x/2.0, cent.y + dim.y/2.0); }
675114402Sru  position south_east() { return position(cent.x + dim.x/2.0, cent.y - dim.y/2.0); }
676114402Sru  position south_west() { return position(cent.x - dim.x/2.0, cent.y - dim.y/2.0); }
677114402Sru  object_type type() = 0;
678114402Sru  void update_bounding_box(bounding_box *);
679114402Sru  void move_by(const position &);
680114402Sru};
681114402Sru
682114402Srurectangle_object::rectangle_object(const position &d)
683114402Sru: dim(d)
684114402Sru{
685114402Sru}
686114402Sru
687114402Sruvoid rectangle_object::update_bounding_box(bounding_box *p)
688114402Sru{
689114402Sru  p->encompass(cent - dim/2.0);
690114402Sru  p->encompass(cent + dim/2.0);
691114402Sru}
692114402Sru
693114402Sruvoid rectangle_object::move_by(const position &a)
694114402Sru{
695114402Sru  cent += a;
696114402Sru}
697114402Sru
698114402Sruclass closed_object : public rectangle_object {
699114402Srupublic:
700114402Sru  closed_object(const position &);
701114402Sru  object_type type() = 0;
702114402Sru  void set_fill(double);
703114402Sru  void set_fill_color(char *fill);
704114402Sruprotected:
705114402Sru  double fill;			// < 0 if not filled
706114402Sru  char *color_fill;		// = 0 if not colored
707114402Sru};
708114402Sru
709114402Sruclosed_object::closed_object(const position &pos)
710114402Sru: rectangle_object(pos), fill(-1.0), color_fill(0)
711114402Sru{
712114402Sru}
713114402Sru
714114402Sruvoid closed_object::set_fill(double f)
715114402Sru{
716114402Sru  assert(f >= 0.0);
717114402Sru  fill = f;
718114402Sru}
719114402Sru
720151497Sruvoid closed_object::set_fill_color(char *f)
721114402Sru{
722151497Sru  color_fill = strsave(f);
723114402Sru}
724114402Sru
725114402Sruclass box_object : public closed_object {
726114402Sru  double xrad;
727114402Sru  double yrad;
728114402Srupublic:
729114402Sru  box_object(const position &, double);
730114402Sru  object_type type() { return BOX_OBJECT; }
731114402Sru  void print();
732114402Sru  position north_east();
733114402Sru  position north_west();
734114402Sru  position south_east();
735114402Sru  position south_west();
736114402Sru};
737114402Sru
738114402Srubox_object::box_object(const position &pos, double r)
739114402Sru: closed_object(pos), xrad(dim.x > 0 ? r : -r), yrad(dim.y > 0 ? r : -r)
740114402Sru{
741114402Sru}
742114402Sru
743114402Sruconst double CHOP_FACTOR = 1.0 - 1.0/M_SQRT2;
744114402Sru
745114402Sruposition box_object::north_east()
746114402Sru{
747114402Sru  return position(cent.x + dim.x/2.0 - CHOP_FACTOR*xrad,
748114402Sru		  cent.y + dim.y/2.0 - CHOP_FACTOR*yrad);
749114402Sru}
750114402Sru
751114402Sruposition box_object::north_west()
752114402Sru{
753114402Sru  return position(cent.x - dim.x/2.0 + CHOP_FACTOR*xrad,
754114402Sru		  cent.y + dim.y/2.0 - CHOP_FACTOR*yrad);
755114402Sru}
756114402Sru
757114402Sruposition box_object::south_east()
758114402Sru{
759114402Sru  return position(cent.x + dim.x/2.0 - CHOP_FACTOR*xrad,
760114402Sru		  cent.y - dim.y/2.0 + CHOP_FACTOR*yrad);
761114402Sru}
762114402Sru
763114402Sruposition box_object::south_west()
764114402Sru{
765114402Sru  return position(cent.x - dim.x/2.0 + CHOP_FACTOR*xrad,
766114402Sru		  cent.y - dim.y/2.0 + CHOP_FACTOR*yrad);
767114402Sru}
768114402Sru
769114402Sruvoid box_object::print()
770114402Sru{
771114402Sru  if (lt.type == line_type::invisible && fill < 0.0 && color_fill == 0)
772114402Sru    return;
773114402Sru  out->set_color(color_fill, graphic_object::get_outline_color());
774114402Sru  if (xrad == 0.0) {
775114402Sru    distance dim2 = dim/2.0;
776114402Sru    position vec[4];
777114402Sru    vec[0] = cent + position(dim2.x, -dim2.y);
778114402Sru    vec[1] = cent + position(dim2.x, dim2.y);
779114402Sru    vec[2] = cent + position(-dim2.x, dim2.y);
780114402Sru    vec[3] = cent + position(-dim2.x, -dim2.y);
781114402Sru    out->polygon(vec, 4, lt, fill);
782114402Sru  }
783114402Sru  else {
784114402Sru    distance abs_dim(fabs(dim.x), fabs(dim.y));
785114402Sru    out->rounded_box(cent, abs_dim, fabs(xrad), lt, fill);
786114402Sru  }
787114402Sru  out->reset_color();
788114402Sru}
789114402Sru
790114402Srugraphic_object *object_spec::make_box(position *curpos, direction *dirp)
791114402Sru{
792114402Sru  static double last_box_height;
793114402Sru  static double last_box_width;
794114402Sru  static double last_box_radius;
795114402Sru  static int have_last_box = 0;
796114402Sru  if (!(flags & HAS_HEIGHT)) {
797114402Sru    if ((flags & IS_SAME) && have_last_box)
798114402Sru      height = last_box_height;
799114402Sru    else
800114402Sru      lookup_variable("boxht", &height);
801114402Sru  }
802114402Sru  if (!(flags & HAS_WIDTH)) {
803114402Sru    if ((flags & IS_SAME) && have_last_box)
804114402Sru      width = last_box_width;
805114402Sru    else
806114402Sru      lookup_variable("boxwid", &width);
807114402Sru  }
808114402Sru  if (!(flags & HAS_RADIUS)) {
809114402Sru    if ((flags & IS_SAME) && have_last_box)
810114402Sru      radius = last_box_radius;
811114402Sru    else
812114402Sru      lookup_variable("boxrad", &radius);
813114402Sru  }
814114402Sru  last_box_width = width;
815114402Sru  last_box_height = height;
816114402Sru  last_box_radius = radius;
817114402Sru  have_last_box = 1;
818114402Sru  radius = fabs(radius);
819114402Sru  if (radius*2.0 > fabs(width))
820114402Sru    radius = fabs(width/2.0);
821114402Sru  if (radius*2.0 > fabs(height))
822114402Sru    radius = fabs(height/2.0);
823114402Sru  box_object *p = new box_object(position(width, height), radius);
824114402Sru  if (!position_rectangle(p, curpos, dirp)) {
825114402Sru    delete p;
826114402Sru    p = 0;
827114402Sru  }
828114402Sru  return p;
829114402Sru}
830114402Sru
831114402Sru// return non-zero for success
832114402Sru
833114402Sruint object_spec::position_rectangle(rectangle_object *p,
834114402Sru				    position *curpos, direction *dirp)
835114402Sru{
836114402Sru  position pos;
837114402Sru  dir = *dirp;			// ignore any direction in attribute list
838114402Sru  position motion;
839114402Sru  switch (dir) {
840114402Sru  case UP_DIRECTION:
841114402Sru    motion.y = p->height()/2.0;
842114402Sru    break;
843114402Sru  case DOWN_DIRECTION:
844114402Sru    motion.y = -p->height()/2.0;
845114402Sru    break;
846114402Sru  case LEFT_DIRECTION:
847114402Sru    motion.x = -p->width()/2.0;
848114402Sru    break;
849114402Sru  case RIGHT_DIRECTION:
850114402Sru    motion.x = p->width()/2.0;
851114402Sru    break;
852114402Sru  default:
853114402Sru    assert(0);
854114402Sru  }
855114402Sru  if (flags & HAS_AT) {
856114402Sru    pos = at;
857114402Sru    if (flags & HAS_WITH) {
858114402Sru      place offset;
859114402Sru      place here;
860114402Sru      here.obj = p;
861114402Sru      if (!with->follow(here, &offset))
862114402Sru	return 0;
863114402Sru      pos -= offset;
864114402Sru    }
865114402Sru  }
866114402Sru  else {
867114402Sru    pos = *curpos;
868114402Sru    pos += motion;
869114402Sru  }
870114402Sru  p->move_by(pos);
871114402Sru  pos += motion;
872114402Sru  *curpos = pos;
873114402Sru  return 1;
874114402Sru}
875114402Sru
876114402Sruclass block_object : public rectangle_object {
877114402Sru  object_list oblist;
878114402Sru  PTABLE(place) *tbl;
879114402Srupublic:
880114402Sru  block_object(const position &, const object_list &ol, PTABLE(place) *t);
881114402Sru  ~block_object();
882114402Sru  place *find_label(const char *);
883114402Sru  object_type type();
884114402Sru  void move_by(const position &);
885114402Sru  void print();
886114402Sru};
887114402Sru
888114402Srublock_object::block_object(const position &d, const object_list &ol,
889114402Sru			   PTABLE(place) *t)
890114402Sru: rectangle_object(d), oblist(ol), tbl(t)
891114402Sru{
892114402Sru}
893114402Sru
894114402Srublock_object::~block_object()
895114402Sru{
896114402Sru  delete tbl;
897114402Sru  object *p = oblist.head;
898114402Sru  while (p != 0) {
899114402Sru    object *tem = p;
900114402Sru    p = p->next;
901114402Sru    delete tem;
902114402Sru  }
903114402Sru}
904114402Sru
905114402Sruvoid block_object::print()
906114402Sru{
907114402Sru  out->begin_block(south_west(), north_east());
908114402Sru  print_object_list(oblist.head);
909114402Sru  out->end_block();
910114402Sru}
911114402Sru
912114402Srustatic void adjust_objectless_places(PTABLE(place) *tbl, const position &a)
913114402Sru{
914114402Sru  // Adjust all the labels that aren't attached to objects.
915114402Sru  PTABLE_ITERATOR(place) iter(tbl);
916114402Sru  const char *key;
917114402Sru  place *pl;
918114402Sru  while (iter.next(&key, &pl))
919114402Sru    if (key && csupper(key[0]) && pl->obj == 0) {
920114402Sru      pl->x += a.x;
921114402Sru      pl->y += a.y;
922114402Sru    }
923114402Sru}
924114402Sru
925114402Sruvoid block_object::move_by(const position &a)
926114402Sru{
927114402Sru  cent += a;
928114402Sru  for (object *p = oblist.head; p; p = p->next)
929114402Sru    p->move_by(a);
930114402Sru  adjust_objectless_places(tbl, a);
931114402Sru}
932114402Sru
933114402Sru
934114402Sruplace *block_object::find_label(const char *name)
935114402Sru{
936114402Sru  return tbl->lookup(name);
937114402Sru}
938114402Sru
939114402Sruobject_type block_object::type()
940114402Sru{
941114402Sru  return BLOCK_OBJECT;
942114402Sru}
943114402Sru
944114402Srugraphic_object *object_spec::make_block(position *curpos, direction *dirp)
945114402Sru{
946114402Sru  bounding_box bb;
947114402Sru  for (object *p = oblist.head; p; p = p->next)
948114402Sru    p->update_bounding_box(&bb);
949114402Sru  position dim;
950114402Sru  if (!bb.blank) {
951114402Sru    position m = -(bb.ll + bb.ur)/2.0;
952114402Sru    for (object *p = oblist.head; p; p = p->next)
953114402Sru      p->move_by(m);
954114402Sru    adjust_objectless_places(tbl, m);
955114402Sru    dim = bb.ur - bb.ll;
956114402Sru  }
957114402Sru  if (flags & HAS_WIDTH)
958114402Sru    dim.x = width;
959114402Sru  if (flags & HAS_HEIGHT)
960114402Sru    dim.y = height;
961114402Sru  block_object *block = new block_object(dim, oblist, tbl);
962114402Sru  if (!position_rectangle(block, curpos, dirp)) {
963114402Sru    delete block;
964114402Sru    block = 0;
965114402Sru  }
966114402Sru  tbl = 0;
967114402Sru  oblist.head = oblist.tail = 0;
968114402Sru  return block;
969114402Sru}
970114402Sru
971114402Sruclass text_object : public rectangle_object {
972114402Srupublic:
973114402Sru  text_object(const position &);
974114402Sru  object_type type() { return TEXT_OBJECT; }
975114402Sru};
976114402Sru
977114402Srutext_object::text_object(const position &d)
978114402Sru: rectangle_object(d)
979114402Sru{
980114402Sru}
981114402Sru
982114402Srugraphic_object *object_spec::make_text(position *curpos, direction *dirp)
983114402Sru{
984114402Sru  if (!(flags & HAS_HEIGHT)) {
985114402Sru    lookup_variable("textht", &height);
986114402Sru    int nitems = 0;
987114402Sru    for (text_item *t = text; t; t = t->next)
988114402Sru      nitems++;
989114402Sru    height *= nitems;
990114402Sru  }
991114402Sru  if (!(flags & HAS_WIDTH))
992114402Sru    lookup_variable("textwid", &width);
993114402Sru  text_object *p = new text_object(position(width, height));
994114402Sru  if (!position_rectangle(p, curpos, dirp)) {
995114402Sru    delete p;
996114402Sru    p = 0;
997114402Sru  }
998114402Sru  return p;
999114402Sru}
1000114402Sru
1001114402Sru
1002114402Sruclass ellipse_object : public closed_object {
1003114402Srupublic:
1004114402Sru  ellipse_object(const position &);
1005114402Sru  position north_east() { return position(cent.x + dim.x/(M_SQRT2*2.0),
1006114402Sru					  cent.y + dim.y/(M_SQRT2*2.0)); }
1007114402Sru  position north_west() { return position(cent.x - dim.x/(M_SQRT2*2.0),
1008114402Sru					  cent.y + dim.y/(M_SQRT2*2.0)); }
1009114402Sru  position south_east() { return position(cent.x + dim.x/(M_SQRT2*2.0),
1010114402Sru					  cent.y - dim.y/(M_SQRT2*2.0)); }
1011114402Sru  position south_west() { return position(cent.x - dim.x/(M_SQRT2*2.0),
1012114402Sru					  cent.y - dim.y/(M_SQRT2*2.0)); }
1013114402Sru  double radius() { return dim.x/2.0; }
1014114402Sru  object_type type() { return ELLIPSE_OBJECT; }
1015114402Sru  void print();
1016114402Sru};
1017114402Sru
1018114402Sruellipse_object::ellipse_object(const position &d)
1019114402Sru: closed_object(d)
1020114402Sru{
1021114402Sru}
1022114402Sru
1023114402Sruvoid ellipse_object::print()
1024114402Sru{
1025114402Sru  if (lt.type == line_type::invisible && fill < 0.0 && color_fill == 0)
1026114402Sru    return;
1027114402Sru  out->set_color(color_fill, graphic_object::get_outline_color());
1028114402Sru  out->ellipse(cent, dim, lt, fill);
1029114402Sru  out->reset_color();
1030114402Sru}
1031114402Sru
1032114402Srugraphic_object *object_spec::make_ellipse(position *curpos, direction *dirp)
1033114402Sru{
1034114402Sru  static double last_ellipse_height;
1035114402Sru  static double last_ellipse_width;
1036114402Sru  static int have_last_ellipse = 0;
1037114402Sru  if (!(flags & HAS_HEIGHT)) {
1038114402Sru    if ((flags & IS_SAME) && have_last_ellipse)
1039114402Sru      height = last_ellipse_height;
1040114402Sru    else
1041114402Sru      lookup_variable("ellipseht", &height);
1042114402Sru  }
1043114402Sru  if (!(flags & HAS_WIDTH)) {
1044114402Sru    if ((flags & IS_SAME) && have_last_ellipse)
1045114402Sru      width = last_ellipse_width;
1046114402Sru    else
1047114402Sru      lookup_variable("ellipsewid", &width);
1048114402Sru  }
1049114402Sru  last_ellipse_width = width;
1050114402Sru  last_ellipse_height = height;
1051114402Sru  have_last_ellipse = 1;
1052114402Sru  ellipse_object *p = new ellipse_object(position(width, height));
1053114402Sru  if (!position_rectangle(p, curpos, dirp)) {
1054114402Sru    delete p;
1055114402Sru    return 0;
1056114402Sru  }
1057114402Sru  return p;
1058114402Sru}
1059114402Sru
1060114402Sruclass circle_object : public ellipse_object {
1061114402Srupublic:
1062114402Sru  circle_object(double);
1063114402Sru  object_type type() { return CIRCLE_OBJECT; }
1064114402Sru  void print();
1065114402Sru};
1066114402Sru
1067114402Srucircle_object::circle_object(double diam)
1068114402Sru: ellipse_object(position(diam, diam))
1069114402Sru{
1070114402Sru}
1071114402Sru
1072114402Sruvoid circle_object::print()
1073114402Sru{
1074114402Sru  if (lt.type == line_type::invisible && fill < 0.0 && color_fill == 0)
1075114402Sru    return;
1076114402Sru  out->set_color(color_fill, graphic_object::get_outline_color());
1077114402Sru  out->circle(cent, dim.x/2.0, lt, fill);
1078114402Sru  out->reset_color();
1079114402Sru}
1080114402Sru
1081114402Srugraphic_object *object_spec::make_circle(position *curpos, direction *dirp)
1082114402Sru{
1083114402Sru  static double last_circle_radius;
1084114402Sru  static int have_last_circle = 0;
1085114402Sru  if (!(flags & HAS_RADIUS)) {
1086114402Sru    if ((flags & IS_SAME) && have_last_circle)
1087114402Sru      radius = last_circle_radius;
1088114402Sru    else
1089114402Sru      lookup_variable("circlerad", &radius);
1090114402Sru  }
1091114402Sru  last_circle_radius = radius;
1092114402Sru  have_last_circle = 1;
1093114402Sru  circle_object *p = new circle_object(radius*2.0);
1094114402Sru  if (!position_rectangle(p, curpos, dirp)) {
1095114402Sru    delete p;
1096114402Sru    return 0;
1097114402Sru  }
1098114402Sru  return p;
1099114402Sru}
1100114402Sru
1101114402Sruclass move_object : public graphic_object {
1102114402Sru  position strt;
1103114402Sru  position en;
1104114402Srupublic:
1105114402Sru  move_object(const position &s, const position &e);
1106114402Sru  position origin() { return en; }
1107114402Sru  object_type type() { return MOVE_OBJECT; }
1108114402Sru  void update_bounding_box(bounding_box *);
1109114402Sru  void move_by(const position &);
1110114402Sru};
1111114402Sru
1112114402Srumove_object::move_object(const position &s, const position &e)
1113114402Sru: strt(s), en(e)
1114114402Sru{
1115114402Sru}
1116114402Sru
1117114402Sruvoid move_object::update_bounding_box(bounding_box *p)
1118114402Sru{
1119114402Sru  p->encompass(strt);
1120114402Sru  p->encompass(en);
1121114402Sru}
1122114402Sru
1123114402Sruvoid move_object::move_by(const position &a)
1124114402Sru{
1125114402Sru  strt += a;
1126114402Sru  en += a;
1127114402Sru}
1128114402Sru
1129114402Srugraphic_object *object_spec::make_move(position *curpos, direction *dirp)
1130114402Sru{
1131114402Sru  static position last_move;
1132114402Sru  static int have_last_move = 0;
1133114402Sru  *dirp = dir;
1134114402Sru  // No need to look at at since `at' attribute sets `from' attribute.
1135114402Sru  position startpos = (flags & HAS_FROM) ? from : *curpos;
1136114402Sru  if (!(flags & HAS_SEGMENT)) {
1137114402Sru    if ((flags & IS_SAME) && have_last_move)
1138114402Sru      segment_pos = last_move;
1139114402Sru    else {
1140114402Sru      switch (dir) {
1141114402Sru      case UP_DIRECTION:
1142114402Sru	segment_pos.y = segment_height;
1143114402Sru	break;
1144114402Sru      case DOWN_DIRECTION:
1145114402Sru	segment_pos.y = -segment_height;
1146114402Sru	break;
1147114402Sru      case LEFT_DIRECTION:
1148114402Sru	segment_pos.x = -segment_width;
1149114402Sru	break;
1150114402Sru      case RIGHT_DIRECTION:
1151114402Sru	segment_pos.x = segment_width;
1152114402Sru	break;
1153114402Sru      default:
1154114402Sru	assert(0);
1155114402Sru      }
1156114402Sru    }
1157114402Sru  }
1158114402Sru  segment_list = new segment(segment_pos, segment_is_absolute, segment_list);
1159114402Sru  // Reverse the segment_list so that it's in forward order.
1160114402Sru  segment *old = segment_list;
1161114402Sru  segment_list = 0;
1162114402Sru  while (old != 0) {
1163114402Sru    segment *tem = old->next;
1164114402Sru    old->next = segment_list;
1165114402Sru    segment_list = old;
1166114402Sru    old = tem;
1167114402Sru  }
1168114402Sru  // Compute the end position.
1169114402Sru  position endpos = startpos;
1170114402Sru  for (segment *s = segment_list; s; s = s->next)
1171114402Sru    if (s->is_absolute)
1172114402Sru      endpos = s->pos;
1173114402Sru    else
1174114402Sru      endpos += s->pos;
1175114402Sru  have_last_move = 1;
1176114402Sru  last_move = endpos - startpos;
1177114402Sru  move_object *p = new move_object(startpos, endpos);
1178114402Sru  *curpos = endpos;
1179114402Sru  return p;
1180114402Sru}
1181114402Sru
1182114402Sruclass linear_object : public graphic_object {
1183114402Sruprotected:
1184114402Sru  char arrow_at_start;
1185114402Sru  char arrow_at_end;
1186114402Sru  arrow_head_type aht;
1187114402Sru  position strt;
1188114402Sru  position en;
1189114402Srupublic:
1190114402Sru  linear_object(const position &s, const position &e);
1191114402Sru  position start() { return strt; }
1192114402Sru  position end() { return en; }
1193114402Sru  void move_by(const position &);
1194114402Sru  void update_bounding_box(bounding_box *) = 0;
1195114402Sru  object_type type() = 0;
1196114402Sru  void add_arrows(int at_start, int at_end, const arrow_head_type &);
1197114402Sru};
1198114402Sru
1199114402Sruclass line_object : public linear_object {
1200114402Sruprotected:
1201114402Sru  position *v;
1202114402Sru  int n;
1203114402Srupublic:
1204114402Sru  line_object(const position &s, const position &e, position *, int);
1205114402Sru  ~line_object();
1206114402Sru  position origin() { return strt; }
1207114402Sru  position center() { return (strt + en)/2.0; }
1208114402Sru  position north() { return (en.y - strt.y) > 0 ? en : strt; }
1209114402Sru  position south() { return (en.y - strt.y) < 0 ? en : strt; }
1210114402Sru  position east() { return (en.x - strt.x) > 0 ? en : strt; }
1211114402Sru  position west() { return (en.x - strt.x) < 0 ? en : strt; }
1212114402Sru  object_type type() { return LINE_OBJECT; }
1213114402Sru  void update_bounding_box(bounding_box *);
1214114402Sru  void print();
1215114402Sru  void move_by(const position &);
1216114402Sru};
1217114402Sru
1218114402Sruclass arrow_object : public line_object {
1219114402Srupublic:
1220114402Sru  arrow_object(const position &, const position &, position *, int);
1221114402Sru  object_type type() { return ARROW_OBJECT; }
1222114402Sru};
1223114402Sru
1224114402Sruclass spline_object : public line_object {
1225114402Srupublic:
1226114402Sru  spline_object(const position &, const position &, position *, int);
1227114402Sru  object_type type() { return SPLINE_OBJECT; }
1228114402Sru  void print();
1229114402Sru  void update_bounding_box(bounding_box *);
1230114402Sru};
1231114402Sru
1232114402Srulinear_object::linear_object(const position &s, const position &e)
1233114402Sru: arrow_at_start(0), arrow_at_end(0), strt(s), en(e)
1234114402Sru{
1235114402Sru}
1236114402Sru
1237114402Sruvoid linear_object::move_by(const position &a)
1238114402Sru{
1239114402Sru  strt += a;
1240114402Sru  en += a;
1241114402Sru}
1242114402Sru
1243114402Sruvoid linear_object::add_arrows(int at_start, int at_end,
1244114402Sru			       const arrow_head_type &a)
1245114402Sru{
1246114402Sru  arrow_at_start = at_start;
1247114402Sru  arrow_at_end = at_end;
1248114402Sru  aht = a;
1249114402Sru}
1250114402Sru
1251114402Sruline_object::line_object(const position &s, const position &e,
1252114402Sru			 position *p, int i)
1253114402Sru: linear_object(s, e), v(p), n(i)
1254114402Sru{
1255114402Sru}
1256114402Sru
1257114402Sruvoid line_object::print()
1258114402Sru{
1259114402Sru  if (lt.type == line_type::invisible)
1260114402Sru    return;
1261114402Sru  out->set_color(0, graphic_object::get_outline_color());
1262151497Sru  // shorten line length to avoid arrow sticking.
1263151497Sru  position sp = strt;
1264151497Sru  if (arrow_at_start) {
1265151497Sru    position base = v[0] - strt;
1266151497Sru    double hyp = hypot(base);
1267151497Sru    if (hyp == 0.0) {
1268151497Sru      error("cannot draw arrow on object with zero length");
1269151497Sru      return;
1270151497Sru    }
1271151497Sru    if (aht.solid && out->supports_filled_polygons()) {
1272151497Sru      base *= aht.height / hyp;
1273151497Sru      draw_arrow(strt, strt - v[0], aht, lt,
1274151497Sru		 graphic_object::get_outline_color());
1275151497Sru      sp = strt + base;
1276151497Sru    } else {
1277151497Sru      base *= fabs(lt.thickness) / hyp / 72 / 4;
1278151497Sru      sp = strt + base;
1279151497Sru      draw_arrow(sp, sp - v[0], aht, lt,
1280151497Sru		 graphic_object::get_outline_color());
1281151497Sru    }
1282151497Sru  }
1283151497Sru  if (arrow_at_end) {
1284151497Sru    position base = v[n-1] - (n > 1 ? v[n-2] : strt);
1285151497Sru    double hyp = hypot(base);
1286151497Sru    if (hyp == 0.0) {
1287151497Sru      error("cannot draw arrow on object with zero length");
1288151497Sru      return;
1289151497Sru    }
1290151497Sru    if (aht.solid && out->supports_filled_polygons()) {
1291151497Sru      base *= aht.height / hyp;
1292151497Sru      draw_arrow(en, v[n-1] - (n > 1 ? v[n-2] : strt), aht, lt,
1293151497Sru		 graphic_object::get_outline_color());
1294151497Sru      v[n-1] = en - base;
1295151497Sru    } else {
1296151497Sru      base *= fabs(lt.thickness) / hyp / 72 / 4;
1297151497Sru      v[n-1] = en - base;
1298151497Sru      draw_arrow(v[n-1], v[n-1] - (n > 1 ? v[n-2] : strt), aht, lt,
1299151497Sru		 graphic_object::get_outline_color());
1300151497Sru    }
1301151497Sru  }
1302151497Sru  out->line(sp, v, n, lt);
1303114402Sru  out->reset_color();
1304114402Sru}
1305114402Sru
1306114402Sruvoid line_object::update_bounding_box(bounding_box *p)
1307114402Sru{
1308114402Sru  p->encompass(strt);
1309114402Sru  for (int i = 0; i < n; i++)
1310114402Sru    p->encompass(v[i]);
1311114402Sru}
1312114402Sru
1313114402Sruvoid line_object::move_by(const position &pos)
1314114402Sru{
1315114402Sru  linear_object::move_by(pos);
1316114402Sru  for (int i = 0; i < n; i++)
1317114402Sru    v[i] += pos;
1318114402Sru}
1319114402Sru
1320114402Sruvoid spline_object::update_bounding_box(bounding_box *p)
1321114402Sru{
1322114402Sru  p->encompass(strt);
1323114402Sru  p->encompass(en);
1324114402Sru  /*
1325114402Sru
1326114402Sru  If
1327114402Sru
1328114402Sru  p1 = q1/2 + q2/2
1329114402Sru  p2 = q1/6 + q2*5/6
1330114402Sru  p3 = q2*5/6 + q3/6
1331114402Sru  p4 = q2/2 + q3/2
1332114402Sru  [ the points for the Bezier cubic ]
1333114402Sru
1334114402Sru  and
1335114402Sru
1336114402Sru  t = .5
1337114402Sru
1338114402Sru  then
1339114402Sru
1340114402Sru  (1-t)^3*p1 + 3*t*(t - 1)^2*p2 + 3*t^2*(1-t)*p3 + t^3*p4
1341114402Sru  [ the equation for the Bezier cubic ]
1342114402Sru
1343114402Sru  = .125*q1 + .75*q2 + .125*q3
1344114402Sru
1345114402Sru  */
1346114402Sru  for (int i = 1; i < n; i++)
1347114402Sru    p->encompass((i == 1 ? strt : v[i-2])*.125 + v[i-1]*.75 + v[i]*.125);
1348114402Sru}
1349114402Sru
1350114402Sruarrow_object::arrow_object(const position &s, const position &e,
1351114402Sru			   position *p, int i)
1352114402Sru: line_object(s, e, p, i)
1353114402Sru{
1354114402Sru}
1355114402Sru
1356114402Sruspline_object::spline_object(const position &s, const position &e,
1357114402Sru			     position *p, int i)
1358114402Sru: line_object(s, e, p, i)
1359114402Sru{
1360114402Sru}
1361114402Sru
1362114402Sruvoid spline_object::print()
1363114402Sru{
1364114402Sru  if (lt.type == line_type::invisible)
1365114402Sru    return;
1366114402Sru  out->set_color(0, graphic_object::get_outline_color());
1367151497Sru  // shorten line length for spline to avoid arrow sticking
1368151497Sru  position sp = strt;
1369151497Sru  if (arrow_at_start) {
1370151497Sru    position base = v[0] - strt;
1371151497Sru    double hyp = hypot(base);
1372151497Sru    if (hyp == 0.0) {
1373151497Sru      error("cannot draw arrow on object with zero length");
1374151497Sru      return;
1375151497Sru    }
1376151497Sru    if (aht.solid && out->supports_filled_polygons()) {
1377151497Sru      base *= aht.height / hyp;
1378151497Sru      draw_arrow(strt, strt - v[0], aht, lt,
1379151497Sru		 graphic_object::get_outline_color());
1380151497Sru      sp = strt + base*0.1; // to reserve spline shape
1381151497Sru    } else {
1382151497Sru      base *= fabs(lt.thickness) / hyp / 72 / 4;
1383151497Sru      sp = strt + base;
1384151497Sru      draw_arrow(sp, sp - v[0], aht, lt,
1385151497Sru		 graphic_object::get_outline_color());
1386151497Sru    }
1387151497Sru  }
1388151497Sru  if (arrow_at_end) {
1389151497Sru    position base = v[n-1] - (n > 1 ? v[n-2] : strt);
1390151497Sru    double hyp = hypot(base);
1391151497Sru    if (hyp == 0.0) {
1392151497Sru      error("cannot draw arrow on object with zero length");
1393151497Sru      return;
1394151497Sru    }
1395151497Sru    if (aht.solid && out->supports_filled_polygons()) {
1396151497Sru      base *= aht.height / hyp;
1397151497Sru      draw_arrow(en, v[n-1] - (n > 1 ? v[n-2] : strt), aht, lt,
1398151497Sru		 graphic_object::get_outline_color());
1399151497Sru      v[n-1] = en - base*0.1; // to reserve spline shape
1400151497Sru    } else {
1401151497Sru      base *= fabs(lt.thickness) / hyp / 72 / 4;
1402151497Sru      v[n-1] = en - base;
1403151497Sru      draw_arrow(v[n-1], v[n-1] - (n > 1 ? v[n-2] : strt), aht, lt,
1404151497Sru		 graphic_object::get_outline_color());
1405151497Sru    }
1406151497Sru  }
1407151497Sru  out->spline(sp, v, n, lt);
1408114402Sru  out->reset_color();
1409114402Sru}
1410114402Sru
1411114402Sruline_object::~line_object()
1412114402Sru{
1413114402Sru  a_delete v;
1414114402Sru}
1415114402Sru
1416114402Srulinear_object *object_spec::make_line(position *curpos, direction *dirp)
1417114402Sru{
1418114402Sru  static position last_line;
1419114402Sru  static int have_last_line = 0;
1420114402Sru  *dirp = dir;
1421114402Sru  // No need to look at at since `at' attribute sets `from' attribute.
1422114402Sru  position startpos = (flags & HAS_FROM) ? from : *curpos;
1423114402Sru  if (!(flags & HAS_SEGMENT)) {
1424114402Sru    if ((flags & IS_SAME) && (type == LINE_OBJECT || type == ARROW_OBJECT)
1425114402Sru	&& have_last_line)
1426114402Sru      segment_pos = last_line;
1427114402Sru    else
1428114402Sru      switch (dir) {
1429114402Sru      case UP_DIRECTION:
1430114402Sru	segment_pos.y = segment_height;
1431114402Sru	break;
1432114402Sru      case DOWN_DIRECTION:
1433114402Sru	segment_pos.y = -segment_height;
1434114402Sru	break;
1435114402Sru      case LEFT_DIRECTION:
1436114402Sru	segment_pos.x = -segment_width;
1437114402Sru	break;
1438114402Sru      case RIGHT_DIRECTION:
1439114402Sru	segment_pos.x = segment_width;
1440114402Sru	break;
1441114402Sru      default:
1442114402Sru	assert(0);
1443114402Sru      }
1444114402Sru  }
1445114402Sru  segment_list = new segment(segment_pos, segment_is_absolute, segment_list);
1446114402Sru  // reverse the segment_list so that it's in forward order
1447114402Sru  segment *old = segment_list;
1448114402Sru  segment_list = 0;
1449114402Sru  while (old != 0) {
1450114402Sru    segment *tem = old->next;
1451114402Sru    old->next = segment_list;
1452114402Sru    segment_list = old;
1453114402Sru    old = tem;
1454114402Sru  }
1455114402Sru  // Absolutise all movements
1456114402Sru  position endpos = startpos;
1457114402Sru  int nsegments = 0;
1458114402Sru  segment *s;
1459114402Sru  for (s = segment_list; s; s = s->next, nsegments++)
1460114402Sru    if (s->is_absolute)
1461114402Sru      endpos = s->pos;
1462114402Sru    else {
1463114402Sru      endpos += s->pos;
1464114402Sru      s->pos = endpos;
1465114402Sru      s->is_absolute = 1;	// to avoid confusion
1466114402Sru    }
1467114402Sru  // handle chop
1468114402Sru  line_object *p = 0;
1469114402Sru  position *v = new position[nsegments];
1470114402Sru  int i = 0;
1471114402Sru  for (s = segment_list; s; s = s->next, i++)
1472114402Sru    v[i] = s->pos;
1473114402Sru  if (flags & IS_DEFAULT_CHOPPED) {
1474114402Sru    lookup_variable("circlerad", &start_chop);
1475114402Sru    end_chop = start_chop;
1476114402Sru    flags |= IS_CHOPPED;
1477114402Sru  }
1478114402Sru  if (flags & IS_CHOPPED) {
1479114402Sru    position start_chop_vec, end_chop_vec;
1480114402Sru    if (start_chop != 0.0) {
1481114402Sru      start_chop_vec = v[0] - startpos;
1482114402Sru      start_chop_vec *= start_chop / hypot(start_chop_vec);
1483114402Sru    }
1484114402Sru    if (end_chop != 0.0) {
1485114402Sru      end_chop_vec = (v[nsegments - 1]
1486114402Sru		      - (nsegments > 1 ? v[nsegments - 2] : startpos));
1487114402Sru      end_chop_vec *= end_chop / hypot(end_chop_vec);
1488114402Sru    }
1489114402Sru    startpos += start_chop_vec;
1490114402Sru    v[nsegments - 1] -= end_chop_vec;
1491114402Sru    endpos -= end_chop_vec;
1492114402Sru  }
1493114402Sru  switch (type) {
1494114402Sru  case SPLINE_OBJECT:
1495114402Sru    p = new spline_object(startpos, endpos, v, nsegments);
1496114402Sru    break;
1497114402Sru  case ARROW_OBJECT:
1498114402Sru    p = new arrow_object(startpos, endpos, v, nsegments);
1499114402Sru    break;
1500114402Sru  case LINE_OBJECT:
1501114402Sru    p = new line_object(startpos, endpos, v, nsegments);
1502114402Sru    break;
1503114402Sru  default:
1504114402Sru    assert(0);
1505114402Sru  }
1506114402Sru  have_last_line = 1;
1507114402Sru  last_line = endpos - startpos;
1508114402Sru  *curpos = endpos;
1509114402Sru  return p;
1510114402Sru}
1511114402Sru
1512114402Sruclass arc_object : public linear_object {
1513114402Sru  int clockwise;
1514114402Sru  position cent;
1515114402Sru  double rad;
1516114402Srupublic:
1517114402Sru  arc_object(int, const position &, const position &, const position &);
1518114402Sru  position origin() { return cent; }
1519114402Sru  position center() { return cent; }
1520114402Sru  double radius() { return rad; }
1521114402Sru  position north();
1522114402Sru  position south();
1523114402Sru  position east();
1524114402Sru  position west();
1525114402Sru  position north_east();
1526114402Sru  position north_west();
1527114402Sru  position south_east();
1528114402Sru  position south_west();
1529114402Sru  void update_bounding_box(bounding_box *);
1530114402Sru  object_type type() { return ARC_OBJECT; }
1531114402Sru  void print();
1532114402Sru  void move_by(const position &pos);
1533114402Sru};
1534114402Sru
1535114402Sruarc_object::arc_object(int cw, const position &s, const position &e,
1536114402Sru		       const position &c)
1537114402Sru: linear_object(s, e), clockwise(cw), cent(c)
1538114402Sru{
1539114402Sru  rad = hypot(c - s);
1540114402Sru}
1541114402Sru
1542114402Sruvoid arc_object::move_by(const position &pos)
1543114402Sru{
1544114402Sru  linear_object::move_by(pos);
1545114402Sru  cent += pos;
1546114402Sru}
1547114402Sru
1548114402Sru// we get arc corners from the corresponding circle
1549114402Sru
1550114402Sruposition arc_object::north()
1551114402Sru{
1552114402Sru  position result(cent);
1553114402Sru  result.y += rad;
1554114402Sru  return result;
1555114402Sru}
1556114402Sru
1557114402Sruposition arc_object::south()
1558114402Sru{
1559114402Sru  position result(cent);
1560114402Sru  result.y -= rad;
1561114402Sru  return result;
1562114402Sru}
1563114402Sru
1564114402Sruposition arc_object::east()
1565114402Sru{
1566114402Sru  position result(cent);
1567114402Sru  result.x += rad;
1568114402Sru  return result;
1569114402Sru}
1570114402Sru
1571114402Sruposition arc_object::west()
1572114402Sru{
1573114402Sru  position result(cent);
1574114402Sru  result.x -= rad;
1575114402Sru  return result;
1576114402Sru}
1577114402Sru
1578114402Sruposition arc_object::north_east()
1579114402Sru{
1580114402Sru  position result(cent);
1581114402Sru  result.x += rad/M_SQRT2;
1582114402Sru  result.y += rad/M_SQRT2;
1583114402Sru  return result;
1584114402Sru}
1585114402Sru
1586114402Sruposition arc_object::north_west()
1587114402Sru{
1588114402Sru  position result(cent);
1589114402Sru  result.x -= rad/M_SQRT2;
1590114402Sru  result.y += rad/M_SQRT2;
1591114402Sru  return result;
1592114402Sru}
1593114402Sru
1594114402Sruposition arc_object::south_east()
1595114402Sru{
1596114402Sru  position result(cent);
1597114402Sru  result.x += rad/M_SQRT2;
1598114402Sru  result.y -= rad/M_SQRT2;
1599114402Sru  return result;
1600114402Sru}
1601114402Sru
1602114402Sruposition arc_object::south_west()
1603114402Sru{
1604114402Sru  position result(cent);
1605114402Sru  result.x -= rad/M_SQRT2;
1606114402Sru  result.y -= rad/M_SQRT2;
1607114402Sru  return result;
1608114402Sru}
1609114402Sru
1610114402Sru
1611114402Sruvoid arc_object::print()
1612114402Sru{
1613114402Sru  if (lt.type == line_type::invisible)
1614114402Sru    return;
1615114402Sru  out->set_color(0, graphic_object::get_outline_color());
1616151497Sru  // handle arrow direction; make shorter line for arc
1617151497Sru  position sp, ep, b;
1618151497Sru  if (clockwise) {
1619151497Sru    sp = en;
1620151497Sru    ep = strt;
1621151497Sru  } else {
1622151497Sru    sp = strt;
1623151497Sru    ep = en;
1624151497Sru  }
1625114402Sru  if (arrow_at_start) {
1626151497Sru    double theta = aht.height / rad;
1627151497Sru    if (clockwise)
1628151497Sru      theta = - theta;
1629151497Sru    b = strt - cent;
1630151497Sru    b = position(b.x*cos(theta) - b.y*sin(theta),
1631151497Sru		 b.x*sin(theta) + b.y*cos(theta)) + cent;
1632151497Sru    if (clockwise)
1633151497Sru      ep = b;
1634151497Sru    else
1635151497Sru      sp = b;
1636151497Sru    if (aht.solid && out->supports_filled_polygons()) {
1637151497Sru      draw_arrow(strt, strt - b, aht, lt,
1638151497Sru		 graphic_object::get_outline_color());
1639151497Sru    } else {
1640151497Sru      position v = b;
1641151497Sru      theta = fabs(lt.thickness) / 72 / 4 / rad;
1642151497Sru      if (clockwise)
1643151497Sru	theta = - theta;
1644151497Sru      b = strt - cent;
1645151497Sru      b = position(b.x*cos(theta) - b.y*sin(theta),
1646151497Sru		   b.x*sin(theta) + b.y*cos(theta)) + cent;
1647151497Sru      draw_arrow(b, b - v, aht, lt,
1648151497Sru		 graphic_object::get_outline_color());
1649151497Sru      out->line(b, &v, 1, lt);
1650151497Sru    }
1651114402Sru  }
1652114402Sru  if (arrow_at_end) {
1653151497Sru    double theta = aht.height / rad;
1654151497Sru    if (!clockwise)
1655151497Sru      theta = - theta;
1656151497Sru    b = en - cent;
1657151497Sru    b = position(b.x*cos(theta) - b.y*sin(theta),
1658151497Sru                 b.x*sin(theta) + b.y*cos(theta)) + cent;
1659151497Sru    if (clockwise)
1660151497Sru      sp = b;
1661151497Sru    else
1662151497Sru      ep = b;
1663151497Sru    if (aht.solid && out->supports_filled_polygons()) {
1664151497Sru      draw_arrow(en, en - b, aht, lt,
1665151497Sru		 graphic_object::get_outline_color());
1666151497Sru    } else {
1667151497Sru      position v = b;
1668151497Sru      theta = fabs(lt.thickness) / 72 / 4 / rad;
1669151497Sru      if (!clockwise)
1670151497Sru	theta = - theta;
1671151497Sru      b = en - cent;
1672151497Sru      b = position(b.x*cos(theta) - b.y*sin(theta),
1673151497Sru                   b.x*sin(theta) + b.y*cos(theta)) + cent;
1674151497Sru      draw_arrow(b, b - v, aht, lt,
1675151497Sru		 graphic_object::get_outline_color());
1676151497Sru      out->line(b, &v, 1, lt);
1677151497Sru    }
1678114402Sru  }
1679151497Sru  out->arc(sp, cent, ep, lt);
1680114402Sru  out->reset_color();
1681114402Sru}
1682114402Sru
1683114402Sruinline double max(double a, double b)
1684114402Sru{
1685114402Sru  return a > b ? a : b;
1686114402Sru}
1687114402Sru
1688114402Sruvoid arc_object::update_bounding_box(bounding_box *p)
1689114402Sru{
1690114402Sru  p->encompass(strt);
1691114402Sru  p->encompass(en);
1692114402Sru  position start_offset = strt - cent;
1693114402Sru  if (start_offset.x == 0.0 && start_offset.y == 0.0)
1694114402Sru    return;
1695114402Sru  position end_offset = en  - cent;
1696114402Sru  if (end_offset.x == 0.0 && end_offset.y == 0.0)
1697114402Sru    return;
1698114402Sru  double start_quad = atan2(start_offset.y, start_offset.x)/(M_PI/2.0);
1699114402Sru  double end_quad = atan2(end_offset.y, end_offset.x)/(M_PI/2.0);
1700114402Sru  if (clockwise) {
1701114402Sru    double temp = start_quad;
1702114402Sru    start_quad = end_quad;
1703114402Sru    end_quad = temp;
1704114402Sru  }
1705114402Sru  if (start_quad < 0.0)
1706114402Sru    start_quad += 4.0;
1707114402Sru  while (end_quad <= start_quad)
1708114402Sru    end_quad += 4.0;
1709151497Sru  double r = max(hypot(start_offset), hypot(end_offset));
1710114402Sru  for (int q = int(start_quad) + 1; q < end_quad; q++) {
1711114402Sru    position offset;
1712114402Sru    switch (q % 4) {
1713114402Sru    case 0:
1714151497Sru      offset.x = r;
1715114402Sru      break;
1716114402Sru    case 1:
1717151497Sru      offset.y = r;
1718114402Sru      break;
1719114402Sru    case 2:
1720151497Sru      offset.x = -r;
1721114402Sru      break;
1722114402Sru    case 3:
1723151497Sru      offset.y = -r;
1724114402Sru      break;
1725114402Sru    }
1726114402Sru    p->encompass(cent + offset);
1727114402Sru  }
1728114402Sru}
1729114402Sru
1730114402Sru// We ignore the with attribute. The at attribute always refers to the center.
1731114402Sru
1732114402Srulinear_object *object_spec::make_arc(position *curpos, direction *dirp)
1733114402Sru{
1734114402Sru  *dirp = dir;
1735114402Sru  int cw = (flags & IS_CLOCKWISE) != 0;
1736114402Sru  // compute the start
1737114402Sru  position startpos;
1738114402Sru  if (flags & HAS_FROM)
1739114402Sru    startpos = from;
1740114402Sru  else
1741114402Sru    startpos = *curpos;
1742114402Sru  if (!(flags & HAS_RADIUS))
1743114402Sru    lookup_variable("arcrad", &radius);
1744114402Sru  // compute the end
1745114402Sru  position endpos;
1746114402Sru  if (flags & HAS_TO)
1747114402Sru    endpos = to;
1748114402Sru  else {
1749114402Sru    position m(radius, radius);
1750114402Sru    // Adjust the signs.
1751114402Sru    if (cw) {
1752114402Sru      if (dir == DOWN_DIRECTION || dir == LEFT_DIRECTION)
1753114402Sru	m.x = -m.x;
1754114402Sru      if (dir == DOWN_DIRECTION || dir == RIGHT_DIRECTION)
1755114402Sru	m.y = -m.y;
1756114402Sru      *dirp = direction((dir + 3) % 4);
1757114402Sru    }
1758114402Sru    else {
1759114402Sru      if (dir == UP_DIRECTION || dir == LEFT_DIRECTION)
1760114402Sru	m.x = -m.x;
1761114402Sru      if (dir == DOWN_DIRECTION || dir == LEFT_DIRECTION)
1762114402Sru	m.y = -m.y;
1763114402Sru      *dirp = direction((dir + 1) % 4);
1764114402Sru    }
1765114402Sru    endpos = startpos + m;
1766114402Sru  }
1767114402Sru  // compute the center
1768114402Sru  position centerpos;
1769114402Sru  if (flags & HAS_AT)
1770114402Sru    centerpos = at;
1771114402Sru  else if (startpos == endpos)
1772114402Sru    centerpos = startpos;
1773114402Sru  else {
1774114402Sru    position h = (endpos - startpos)/2.0;
1775114402Sru    double d = hypot(h);
1776114402Sru    if (radius <= 0)
1777114402Sru      radius = .25;
1778114402Sru    // make the radius big enough
1779114402Sru    while (radius < d)
1780114402Sru      radius *= 2.0;
1781114402Sru    double alpha = acos(d/radius);
1782114402Sru    double theta = atan2(h.y, h.x);
1783114402Sru    if (cw)
1784114402Sru      theta -= alpha;
1785114402Sru    else
1786114402Sru      theta += alpha;
1787114402Sru    centerpos = position(cos(theta), sin(theta))*radius + startpos;
1788114402Sru  }
1789114402Sru  arc_object *p = new arc_object(cw, startpos, endpos, centerpos);
1790114402Sru  *curpos = endpos;
1791114402Sru  return p;
1792114402Sru}
1793114402Sru
1794114402Srugraphic_object *object_spec::make_linear(position *curpos, direction *dirp)
1795114402Sru{
1796114402Sru  linear_object *obj;
1797114402Sru  if (type == ARC_OBJECT)
1798114402Sru    obj = make_arc(curpos, dirp);
1799114402Sru  else
1800114402Sru    obj = make_line(curpos, dirp);
1801114402Sru  if (type == ARROW_OBJECT
1802114402Sru      && (flags & (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD)) == 0)
1803114402Sru    flags |= HAS_RIGHT_ARROW_HEAD;
1804114402Sru  if (obj && (flags & (HAS_LEFT_ARROW_HEAD|HAS_RIGHT_ARROW_HEAD))) {
1805114402Sru    arrow_head_type a;
1806114402Sru    int at_start = (flags & HAS_LEFT_ARROW_HEAD) != 0;
1807114402Sru    int at_end = (flags & HAS_RIGHT_ARROW_HEAD) != 0;
1808114402Sru    if (flags & HAS_HEIGHT)
1809114402Sru      a.height = height;
1810114402Sru    else
1811114402Sru      lookup_variable("arrowht", &a.height);
1812114402Sru    if (flags & HAS_WIDTH)
1813114402Sru      a.width = width;
1814114402Sru    else
1815114402Sru      lookup_variable("arrowwid", &a.width);
1816114402Sru    double solid;
1817114402Sru    lookup_variable("arrowhead", &solid);
1818114402Sru    a.solid = solid != 0.0;
1819114402Sru    obj->add_arrows(at_start, at_end, a);
1820114402Sru  }
1821114402Sru  return obj;
1822114402Sru}
1823114402Sru
1824114402Sruobject *object_spec::make_object(position *curpos, direction *dirp)
1825114402Sru{
1826114402Sru  graphic_object *obj = 0;
1827114402Sru  switch (type) {
1828114402Sru  case BLOCK_OBJECT:
1829114402Sru    obj = make_block(curpos, dirp);
1830114402Sru    break;
1831114402Sru  case BOX_OBJECT:
1832114402Sru    obj = make_box(curpos, dirp);
1833114402Sru    break;
1834114402Sru  case TEXT_OBJECT:
1835114402Sru    obj = make_text(curpos, dirp);
1836114402Sru    break;
1837114402Sru  case ELLIPSE_OBJECT:
1838114402Sru    obj = make_ellipse(curpos, dirp);
1839114402Sru    break;
1840114402Sru  case CIRCLE_OBJECT:
1841114402Sru    obj = make_circle(curpos, dirp);
1842114402Sru    break;
1843114402Sru  case MOVE_OBJECT:
1844114402Sru    obj = make_move(curpos, dirp);
1845114402Sru    break;
1846114402Sru  case ARC_OBJECT:
1847114402Sru  case LINE_OBJECT:
1848114402Sru  case SPLINE_OBJECT:
1849114402Sru  case ARROW_OBJECT:
1850114402Sru    obj = make_linear(curpos, dirp);
1851114402Sru    break;
1852114402Sru  case MARK_OBJECT:
1853114402Sru  case OTHER_OBJECT:
1854114402Sru  default:
1855114402Sru    assert(0);
1856114402Sru    break;
1857114402Sru  }
1858114402Sru  if (obj) {
1859114402Sru    if (flags & IS_INVISIBLE)
1860114402Sru      obj->set_invisible();
1861114402Sru    if (text != 0)
1862114402Sru      obj->add_text(text, (flags & IS_ALIGNED) != 0);
1863114402Sru    if (flags & IS_DOTTED)
1864114402Sru      obj->set_dotted(dash_width);
1865114402Sru    else if (flags & IS_DASHED)
1866114402Sru      obj->set_dashed(dash_width);
1867114402Sru    double th;
1868114402Sru    if (flags & HAS_THICKNESS)
1869114402Sru      th = thickness;
1870114402Sru    else
1871114402Sru      lookup_variable("linethick", &th);
1872114402Sru    obj->set_thickness(th);
1873114402Sru    if (flags & IS_OUTLINED)
1874114402Sru      obj->set_outline_color(outlined);
1875151497Sru    if (flags & (IS_DEFAULT_FILLED | IS_FILLED)) {
1876114402Sru      if (flags & IS_SHADED)
1877114402Sru	obj->set_fill_color(shaded);
1878114402Sru      else {
1879114402Sru	if (flags & IS_DEFAULT_FILLED)
1880114402Sru	  lookup_variable("fillval", &fill);
1881114402Sru	if (fill < 0.0)
1882114402Sru	  error("bad fill value %1", fill);
1883114402Sru	else
1884114402Sru	  obj->set_fill(fill);
1885114402Sru      }
1886114402Sru    }
1887114402Sru  }
1888114402Sru  return obj;
1889114402Sru}
1890114402Sru
1891114402Srustruct string_list {
1892114402Sru  string_list *next;
1893114402Sru  char *str;
1894114402Sru  string_list(char *);
1895114402Sru  ~string_list();
1896114402Sru};
1897114402Sru
1898114402Srustring_list::string_list(char *s)
1899114402Sru: next(0), str(s)
1900114402Sru{
1901114402Sru}
1902114402Sru
1903114402Srustring_list::~string_list()
1904114402Sru{
1905114402Sru  a_delete str;
1906114402Sru}
1907114402Sru
1908114402Sru/* A path is used to hold the argument to the `with' attribute.  For
1909114402Sru   example, `.nw' or `.A.s' or `.A'.  The major operation on a path is to
1910114402Sru   take a place and follow the path through the place to place within the
1911114402Sru   place.  Note that `.A.B.C.sw' will work.
1912114402Sru
1913114402Sru   For compatibility with DWB pic, `with' accepts positions also (this
1914114402Sru   is incorrectly documented in CSTR 116). */
1915114402Sru
1916114402Srupath::path(corner c)
1917114402Sru: crn(c), label_list(0), ypath(0), is_position(0)
1918114402Sru{
1919114402Sru}
1920114402Sru
1921114402Srupath::path(position p)
1922114402Sru: crn(0), label_list(0), ypath(0), is_position(1)
1923114402Sru{
1924114402Sru  pos.x = p.x;
1925114402Sru  pos.y = p.y;
1926114402Sru}
1927114402Sru
1928114402Srupath::path(char *l, corner c)
1929114402Sru: crn(c), ypath(0), is_position(0)
1930114402Sru{
1931114402Sru  label_list = new string_list(l);
1932114402Sru}
1933114402Sru
1934114402Srupath::~path()
1935114402Sru{
1936114402Sru  while (label_list) {
1937114402Sru    string_list *tem = label_list;
1938114402Sru    label_list = label_list->next;
1939114402Sru    delete tem;
1940114402Sru  }
1941114402Sru  delete ypath;
1942114402Sru}
1943114402Sru
1944114402Sruvoid path::append(corner c)
1945114402Sru{
1946114402Sru  assert(crn == 0);
1947114402Sru  crn = c;
1948114402Sru}
1949114402Sru
1950114402Sruvoid path::append(char *s)
1951114402Sru{
1952114402Sru  string_list **p;
1953114402Sru  for (p = &label_list; *p; p = &(*p)->next)
1954114402Sru    ;
1955114402Sru  *p = new string_list(s);
1956114402Sru}
1957114402Sru
1958114402Sruvoid path::set_ypath(path *p)
1959114402Sru{
1960114402Sru  ypath = p;
1961114402Sru}
1962114402Sru
1963114402Sru// return non-zero for success
1964114402Sru
1965114402Sruint path::follow(const place &pl, place *result) const
1966114402Sru{
1967114402Sru  if (is_position) {
1968114402Sru    result->x = pos.x;
1969114402Sru    result->y = pos.y;
1970114402Sru    result->obj = 0;
1971114402Sru    return 1;
1972114402Sru  }
1973114402Sru  const place *p = &pl;
1974114402Sru  for (string_list *lb = label_list; lb; lb = lb->next)
1975114402Sru    if (p->obj == 0 || (p = p->obj->find_label(lb->str)) == 0) {
1976114402Sru      lex_error("object does not contain a place `%1'", lb->str);
1977114402Sru      return 0;
1978114402Sru    }
1979114402Sru  if (crn == 0 || p->obj == 0)
1980114402Sru    *result = *p;
1981114402Sru  else {
1982114402Sru    position ps = ((p->obj)->*(crn))();
1983114402Sru    result->x = ps.x;
1984114402Sru    result->y = ps.y;
1985114402Sru    result->obj = 0;
1986114402Sru  }
1987114402Sru  if (ypath) {
1988114402Sru    place tem;
1989114402Sru    if (!ypath->follow(pl, &tem))
1990114402Sru      return 0;
1991114402Sru    result->y = tem.y;
1992114402Sru    if (result->obj != tem.obj)
1993114402Sru      result->obj = 0;
1994114402Sru  }
1995114402Sru  return 1;
1996114402Sru}
1997114402Sru
1998114402Sruvoid print_object_list(object *p)
1999114402Sru{
2000114402Sru  for (; p; p = p->next) {
2001114402Sru    p->print();
2002114402Sru    p->print_text();
2003114402Sru  }
2004114402Sru}
2005114402Sru
2006114402Sruvoid print_picture(object *obj)
2007114402Sru{
2008114402Sru  bounding_box bb;
2009114402Sru  for (object *p = obj; p; p = p->next)
2010114402Sru    p->update_bounding_box(&bb);
2011114402Sru  double scale;
2012114402Sru  lookup_variable("scale", &scale);
2013114402Sru  out->start_picture(scale, bb.ll, bb.ur);
2014114402Sru  print_object_list(obj);
2015114402Sru  out->finish_picture();
2016114402Sru}
2017114402Sru
2018