~ubuntu-branches/ubuntu/dapper/groff/dapper

« back to all changes in this revision

Viewing changes to src/preproc/pic/object.cc

  • Committer: Bazaar Package Importer
  • Author(s): Colin Watson
  • Date: 2002-03-17 04:11:50 UTC
  • Revision ID: james.westby@ubuntu.com-20020317041150-wkgfawjc3gxlk0o5
Tags: upstream-1.17.2
ImportĀ upstreamĀ versionĀ 1.17.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// -*- C++ -*-
 
2
/* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
 
3
     Written by James Clark (jjc@jclark.com)
 
4
 
 
5
This file is part of groff.
 
6
 
 
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
 
10
version.
 
11
 
 
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
 
15
for more details.
 
16
 
 
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. */
 
20
 
 
21
#include "pic.h"
 
22
#include "ptable.h"
 
23
#include "object.h"
 
24
 
 
25
void print_object_list(object *);
 
26
 
 
27
line_type::line_type()
 
28
: type(solid), thickness(1.0)
 
29
{
 
30
}
 
31
 
 
32
output::output() : args(0), desired_height(0.0), desired_width(0.0)
 
33
{
 
34
}
 
35
 
 
36
output::~output()
 
37
{
 
38
  a_delete args;
 
39
}
 
40
 
 
41
void output::set_desired_width_height(double wid, double ht)
 
42
{
 
43
  desired_width = wid;
 
44
  desired_height = ht;
 
45
}
 
46
 
 
47
void output::set_args(const char *s)
 
48
{
 
49
  a_delete args;
 
50
  if (s == 0 || *s == '\0')
 
51
    args = 0;
 
52
  else
 
53
    args = strsave(s);
 
54
}
 
55
 
 
56
void output::command(const char *, const char *, int)
 
57
{
 
58
}
 
59
 
 
60
void output::set_location(const char *, int)
 
61
{
 
62
}
 
63
 
 
64
int output::supports_filled_polygons()
 
65
{
 
66
  return 0;
 
67
}
 
68
 
 
69
void output::begin_block(const position &, const position &)
 
70
{
 
71
}
 
72
 
 
73
void output::end_block()
 
74
{
 
75
}
 
76
 
 
77
double output::compute_scale(double sc, const position &ll, const position &ur)
 
78
{
 
79
  distance dim = ur - ll;
 
80
  if (desired_width != 0.0 || desired_height != 0.0) {
 
81
    sc = 0.0;
 
82
    if (desired_width != 0.0) {
 
83
      if (dim.x == 0.0)
 
84
        error("width specified for picture with zero width");
 
85
      else
 
86
        sc = dim.x/desired_width;
 
87
    }
 
88
    if (desired_height != 0.0) {
 
89
      if (dim.y == 0.0)
 
90
        error("height specified for picture with zero height");
 
91
      else {
 
92
        double tem = dim.y/desired_height;
 
93
        if (tem > sc)
 
94
          sc = tem;
 
95
      }
 
96
    }
 
97
    return sc == 0.0 ? 1.0 : sc;
 
98
  }
 
99
  else {
 
100
    if (sc <= 0.0)
 
101
      sc = 1.0;
 
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;
 
112
    }
 
113
    else
 
114
      return sc;
 
115
  }
 
116
}
 
117
 
 
118
position::position(const place &pl)
 
119
{
 
120
  if (pl.obj != 0) {
 
121
    // Use two statements to work around bug in SGI C++.
 
122
    object *tem = pl.obj;
 
123
    *this = tem->origin();
 
124
  }
 
125
  else {
 
126
    x = pl.x;
 
127
    y = pl.y;
 
128
  }
 
129
}
 
130
 
 
131
position::position() : x(0.0), y(0.0)
 
132
{
 
133
}
 
134
 
 
135
position::position(double a, double b) : x(a), y(b)
 
136
{
 
137
}
 
138
 
 
139
 
 
140
int operator==(const position &a, const position &b)
 
141
{
 
142
  return a.x == b.x && a.y == b.y;
 
143
}
 
144
 
 
145
int operator!=(const position &a, const position &b)
 
146
{
 
147
  return a.x != b.x || a.y != b.y;
 
148
}
 
149
 
 
150
position &position::operator+=(const position &a)
 
151
{
 
152
  x += a.x;
 
153
  y += a.y;
 
154
  return *this;
 
155
}
 
156
 
 
157
position &position::operator-=(const position &a)
 
158
{
 
159
  x -= a.x;
 
160
  y -= a.y;
 
161
  return *this;
 
162
}
 
163
 
 
164
position &position::operator*=(double a)
 
165
{
 
166
  x *= a;
 
167
  y *= a;
 
168
  return *this;
 
169
}
 
170
 
 
171
position &position::operator/=(double a)
 
172
{
 
173
  x /= a;
 
174
  y /= a;
 
175
  return *this;
 
176
}
 
177
 
 
178
position operator-(const position &a)
 
179
{
 
180
  return position(-a.x, -a.y);
 
181
}
 
182
 
 
183
position operator+(const position &a, const position &b)
 
184
{
 
185
  return position(a.x + b.x, a.y + b.y);
 
186
}
 
187
 
 
188
position operator-(const position &a, const position &b)
 
189
{
 
190
  return position(a.x - b.x, a.y - b.y);
 
191
}
 
192
 
 
193
position operator/(const position &a, double n)
 
194
{
 
195
  return position(a.x/n, a.y/n);
 
196
}
 
197
 
 
198
position operator*(const position &a, double n)
 
199
{
 
200
  return position(a.x*n, a.y*n);
 
201
}
 
202
 
 
203
// dot product
 
204
 
 
205
double operator*(const position &a, const position &b)
 
206
{
 
207
  return a.x*b.x + a.y*b.y;
 
208
}
 
209
 
 
210
double hypot(const position &a)
 
211
{
 
212
  return hypot(a.x, a.y);
 
213
}
 
214
 
 
215
struct arrow_head_type {
 
216
  double height;
 
217
  double width;
 
218
  int solid;
 
219
};
 
220
 
 
221
void draw_arrow(const position &pos, const distance &dir,
 
222
                const arrow_head_type &aht, const line_type &lt)
 
223
{
 
224
  double hyp = hypot(dir);
 
225
  if (hyp == 0.0) {
 
226
    error("cannot draw arrow on object with zero length");
 
227
    return;
 
228
  }
 
229
  position base = -dir;
 
230
  base *= aht.height/hyp;
 
231
  position n(dir.y, -dir.x);
 
232
  n *= aht.width/(hyp*2.0);
 
233
  line_type slt = lt;
 
234
  slt.type = line_type::solid;
 
235
  if (aht.solid && out->supports_filled_polygons()) {
 
236
    position v[3];
 
237
    v[0] = pos;
 
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);
 
242
  }
 
243
  else {
 
244
    position v[2];
 
245
    v[0] = pos;
 
246
    v[1] = pos + base + n;
 
247
    out->line(pos + base - n, v, 2, slt);
 
248
  }
 
249
}
 
250
 
 
251
object::object() : prev(0), next(0)
 
252
{
 
253
}
 
254
 
 
255
object::~object()
 
256
{
 
257
}
 
258
 
 
259
void object::move_by(const position &)
 
260
{
 
261
}
 
262
 
 
263
void object::print()
 
264
{
 
265
}
 
266
 
 
267
void object::print_text()
 
268
{
 
269
}
 
270
 
 
271
int object::blank()
 
272
{
 
273
  return 0;
 
274
}
 
275
 
 
276
struct bounding_box {
 
277
  int blank;
 
278
  position ll;
 
279
  position ur;
 
280
 
 
281
  bounding_box();
 
282
  void encompass(const position &);
 
283
};
 
284
 
 
285
bounding_box::bounding_box()
 
286
: blank(1)
 
287
{
 
288
}
 
289
 
 
290
void bounding_box::encompass(const position &pos)
 
291
{
 
292
  if (blank) {
 
293
    ll = pos;
 
294
    ur = pos;
 
295
    blank = 0;
 
296
  }
 
297
  else {
 
298
    if (pos.x < ll.x)
 
299
      ll.x = pos.x;
 
300
    if (pos.y < ll.y)
 
301
      ll.y = pos.y;
 
302
    if (pos.x > ur.x)
 
303
      ur.x = pos.x;
 
304
    if (pos.y > ur.y)
 
305
      ur.y = pos.y;
 
306
  }
 
307
}
 
308
 
 
309
void object::update_bounding_box(bounding_box *)
 
310
{
 
311
}
 
312
 
 
313
position object::origin()
 
314
{
 
315
  return position(0.0,0.0);
 
316
}
 
317
 
 
318
position object::north()
 
319
{
 
320
  return origin();
 
321
}
 
322
 
 
323
position object::south()
 
324
{
 
325
  return origin();
 
326
}
 
327
 
 
328
position object::east()
 
329
{
 
330
  return origin();
 
331
}
 
332
 
 
333
position object::west()
 
334
{
 
335
  return origin();
 
336
}
 
337
 
 
338
position object::north_east()
 
339
{
 
340
  return origin();
 
341
}
 
342
 
 
343
position object::north_west()
 
344
{
 
345
  return origin();
 
346
}
 
347
 
 
348
position object::south_east()
 
349
{
 
350
  return origin();
 
351
}
 
352
 
 
353
position object::south_west()
 
354
{
 
355
  return origin();
 
356
}
 
357
 
 
358
position object::start()
 
359
{
 
360
  return origin();
 
361
}
 
362
 
 
363
position object::end()
 
364
{
 
365
  return origin();
 
366
}
 
367
 
 
368
position object::center()
 
369
{
 
370
  return origin();
 
371
}
 
372
 
 
373
double object::width()
 
374
{
 
375
  return 0.0;
 
376
}
 
377
 
 
378
double object::radius()
 
379
{
 
380
  return 0.0;
 
381
}
 
382
 
 
383
double object::height()
 
384
{
 
385
  return 0.0;
 
386
}
 
387
 
 
388
place *object::find_label(const char *)
 
389
{
 
390
  return 0;
 
391
}
 
392
 
 
393
segment::segment(const position &a, int n, segment *p)
 
394
: is_absolute(n), pos(a), next(p)
 
395
{
 
396
}
 
397
 
 
398
text_item::text_item(char *t, const char *fn, int ln)
 
399
: next(0), text(t), filename(fn), lineno(ln) 
 
400
{
 
401
  adj.h = CENTER_ADJUST;
 
402
  adj.v = NONE_ADJUST;
 
403
}
 
404
 
 
405
text_item::~text_item()
 
406
{
 
407
  a_delete text;
 
408
}
 
409
 
 
410
object_spec::object_spec(object_type t) : type(t)
 
411
{
 
412
  flags = 0;
 
413
  tbl = 0;
 
414
  segment_list = 0;
 
415
  segment_width = segment_height = 0.0;
 
416
  segment_is_absolute = 0;
 
417
  text = 0;
 
418
  with = 0;
 
419
  dir = RIGHT_DIRECTION;
 
420
}
 
421
 
 
422
object_spec::~object_spec()
 
423
{
 
424
  delete tbl;
 
425
  while (segment_list != 0) {
 
426
    segment *tem = segment_list;
 
427
    segment_list = segment_list->next;
 
428
    delete tem;
 
429
  }
 
430
  object *p = oblist.head;
 
431
  while (p != 0) {
 
432
    object *tem = p;
 
433
    p = p->next;
 
434
    delete tem;
 
435
  }
 
436
  while (text != 0) {
 
437
    text_item *tem = text;
 
438
    text = text->next;
 
439
    delete tem;
 
440
  }
 
441
  delete with;
 
442
}
 
443
 
 
444
class command_object : public object {
 
445
  char *s;
 
446
  const char *filename;
 
447
  int lineno;
 
448
public:
 
449
  command_object(char *, const char *, int);
 
450
  ~command_object();
 
451
  object_type type() { return OTHER_OBJECT; }
 
452
  void print();
 
453
};
 
454
 
 
455
command_object::command_object(char *p, const char *fn, int ln)
 
456
: s(p), filename(fn), lineno(ln)
 
457
{
 
458
}
 
459
 
 
460
command_object::~command_object()
 
461
{
 
462
  a_delete s;
 
463
}
 
464
 
 
465
void command_object::print()
 
466
{
 
467
  out->command(s, filename, lineno);
 
468
}
 
469
 
 
470
object *make_command_object(char *s, const char *fn, int ln)
 
471
{
 
472
  return new command_object(s, fn, ln);
 
473
}
 
474
 
 
475
class mark_object : public object {
 
476
public:
 
477
  mark_object();
 
478
  object_type type();
 
479
};
 
480
 
 
481
object *make_mark_object()
 
482
{
 
483
  return new mark_object();
 
484
}
 
485
 
 
486
mark_object::mark_object()
 
487
{
 
488
}
 
489
 
 
490
object_type mark_object::type()
 
491
{
 
492
  return MARK_OBJECT;
 
493
}
 
494
 
 
495
object_list::object_list() : head(0), tail(0)
 
496
{
 
497
}
 
498
 
 
499
void object_list::append(object *obj)
 
500
{
 
501
  if (tail == 0) {
 
502
    obj->next = obj->prev = 0;
 
503
    head = tail = obj;
 
504
  }
 
505
  else {
 
506
    obj->prev = tail;
 
507
    obj->next = 0;
 
508
    tail->next = obj;
 
509
    tail = obj;
 
510
  }
 
511
}
 
512
 
 
513
void object_list::wrap_up_block(object_list *ol)
 
514
{
 
515
  object *p;
 
516
  for (p = tail; p && p->type() != MARK_OBJECT; p = p->prev)
 
517
    ;
 
518
  assert(p != 0);
 
519
  ol->head = p->next;
 
520
  if (ol->head) {
 
521
    ol->tail = tail;
 
522
    ol->head->prev = 0;
 
523
  }
 
524
  else
 
525
    ol->tail = 0;
 
526
  tail = p->prev;
 
527
  if (tail)
 
528
    tail->next = 0;
 
529
  else
 
530
    head = 0;
 
531
  delete p;
 
532
}
 
533
 
 
534
text_piece::text_piece()
 
535
: text(0), filename(0), lineno(-1)
 
536
{
 
537
  adj.h = CENTER_ADJUST;
 
538
  adj.v = NONE_ADJUST;
 
539
}
 
540
 
 
541
text_piece::~text_piece()
 
542
{
 
543
  a_delete text;
 
544
}
 
545
 
 
546
class graphic_object : public object {
 
547
  int ntext;
 
548
  text_piece *text;
 
549
  int aligned;
 
550
protected:
 
551
  line_type lt;
 
552
public:
 
553
  graphic_object();
 
554
  ~graphic_object();
 
555
  object_type type() = 0;
 
556
  void print_text();
 
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);
 
563
};
 
564
 
 
565
graphic_object::graphic_object() : ntext(0), text(0), aligned(0)
 
566
{
 
567
}
 
568
 
 
569
void graphic_object::set_dotted(double wid)
 
570
{
 
571
  lt.type = line_type::dotted;
 
572
  lt.dash_width = wid;
 
573
}
 
574
 
 
575
void graphic_object::set_dashed(double wid)
 
576
{
 
577
  lt.type = line_type::dashed;
 
578
  lt.dash_width = wid;
 
579
}
 
580
 
 
581
void graphic_object::set_thickness(double th)
 
582
{
 
583
  lt.thickness = th;
 
584
}
 
585
 
 
586
void graphic_object::set_fill(double)
 
587
{
 
588
}
 
589
 
 
590
void graphic_object::set_invisible()
 
591
{
 
592
  lt.type = line_type::invisible;
 
593
}
 
594
 
 
595
void graphic_object::add_text(text_item *t, int a)
 
596
{
 
597
  aligned = a;
 
598
  int len = 0;
 
599
  text_item *p;
 
600
  for (p = t; p; p = p->next)
 
601
    len++;
 
602
  if (len == 0)
 
603
    text = 0;
 
604
  else {
 
605
    text = new text_piece[len];
 
606
    for (p = t, len = 0; p; p = p->next, len++) {
 
607
      text[len].text = p->text;
 
608
      p->text = 0;
 
609
      text[len].adj = p->adj;
 
610
      text[len].filename = p->filename;
 
611
      text[len].lineno = p->lineno;
 
612
    }
 
613
  }
 
614
  ntext = len;
 
615
}
 
616
 
 
617
void graphic_object::print_text()
 
618
{
 
619
  double angle = 0.0;
 
620
  if (aligned) {
 
621
    position d(end() - start());
 
622
    if (d.x != 0.0 || d.y != 0.0)
 
623
      angle = atan2(d.y, d.x);
 
624
  }
 
625
  if (text != 0)
 
626
    out->text(center(), text, ntext, angle);
 
627
}
 
628
 
 
629
graphic_object::~graphic_object()
 
630
{
 
631
  if (text)
 
632
    ad_delete(ntext) text;
 
633
}
 
634
 
 
635
class rectangle_object : public graphic_object {
 
636
protected:
 
637
  position cent;
 
638
  position dim;
 
639
public:
 
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 &);
 
656
};
 
657
 
 
658
rectangle_object::rectangle_object(const position &d)
 
659
: dim(d)
 
660
{
 
661
}
 
662
 
 
663
void rectangle_object::update_bounding_box(bounding_box *p)
 
664
{
 
665
  p->encompass(cent - dim/2.0);
 
666
  p->encompass(cent + dim/2.0);
 
667
}
 
668
 
 
669
void rectangle_object::move_by(const position &a)
 
670
{
 
671
  cent += a;
 
672
}
 
673
 
 
674
class closed_object : public rectangle_object {
 
675
public:
 
676
  closed_object(const position &);
 
677
  object_type type() = 0;
 
678
  void set_fill(double);
 
679
protected:
 
680
  double fill;                  // < 0 if not filled
 
681
};
 
682
 
 
683
closed_object::closed_object(const position &pos)
 
684
: rectangle_object(pos), fill(-1.0)
 
685
{
 
686
}
 
687
 
 
688
void closed_object::set_fill(double f)
 
689
{
 
690
  assert(f >= 0.0);
 
691
  fill = f;
 
692
}
 
693
 
 
694
 
 
695
class box_object : public closed_object {
 
696
  double xrad;
 
697
  double yrad;
 
698
public:
 
699
  box_object(const position &, double);
 
700
  object_type type() { return BOX_OBJECT; }
 
701
  void print();
 
702
  position north_east();
 
703
  position north_west();
 
704
  position south_east();
 
705
  position south_west();
 
706
};
 
707
 
 
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)
 
710
{
 
711
}
 
712
 
 
713
const double CHOP_FACTOR = 1.0 - 1.0/M_SQRT2;
 
714
 
 
715
position box_object::north_east()
 
716
{
 
717
  return position(cent.x + dim.x/2.0 - CHOP_FACTOR*xrad,
 
718
                  cent.y + dim.y/2.0 - CHOP_FACTOR*yrad);
 
719
}
 
720
 
 
721
position box_object::north_west()
 
722
{
 
723
  return position(cent.x - dim.x/2.0 + CHOP_FACTOR*xrad,
 
724
                  cent.y + dim.y/2.0 - CHOP_FACTOR*yrad);
 
725
}
 
726
 
 
727
position box_object::south_east()
 
728
{
 
729
  return position(cent.x + dim.x/2.0 - CHOP_FACTOR*xrad,
 
730
                  cent.y - dim.y/2.0 + CHOP_FACTOR*yrad);
 
731
}
 
732
 
 
733
position box_object::south_west()
 
734
{
 
735
  return position(cent.x - dim.x/2.0 + CHOP_FACTOR*xrad,
 
736
                  cent.y - dim.y/2.0 + CHOP_FACTOR*yrad);
 
737
}
 
738
 
 
739
void box_object::print()
 
740
{
 
741
  if (lt.type == line_type::invisible && fill < 0.0)
 
742
    return;
 
743
  if (xrad == 0.0) {
 
744
    distance dim2 = dim/2.0;
 
745
    position vec[4];
 
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);
 
751
  }
 
752
  else {
 
753
    distance abs_dim(fabs(dim.x), fabs(dim.y));
 
754
    out->rounded_box(cent, abs_dim, fabs(xrad), lt, fill);
 
755
  }
 
756
}
 
757
 
 
758
graphic_object *object_spec::make_box(position *curpos, direction *dirp)
 
759
{
 
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;
 
767
    else
 
768
      lookup_variable("boxht", &height);
 
769
  }
 
770
  if (!(flags & HAS_WIDTH)) {
 
771
    if ((flags & IS_SAME) && have_last_box)
 
772
      width = last_box_width;
 
773
    else
 
774
      lookup_variable("boxwid", &width);
 
775
  }
 
776
  if (!(flags & HAS_RADIUS)) {
 
777
    if ((flags & IS_SAME) && have_last_box)
 
778
      radius = last_box_radius;
 
779
    else
 
780
      lookup_variable("boxrad", &radius);
 
781
  }
 
782
  last_box_width = width;
 
783
  last_box_height = height;
 
784
  last_box_radius = radius;
 
785
  have_last_box = 1;
 
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)) {
 
793
    delete p;
 
794
    p = 0;
 
795
  }
 
796
  return p;
 
797
}
 
798
 
 
799
// return non-zero for success
 
800
 
 
801
int object_spec::position_rectangle(rectangle_object *p,
 
802
                                    position *curpos, direction *dirp)
 
803
{
 
804
  position pos;
 
805
  dir = *dirp;                  // ignore any direction in attribute list
 
806
  position motion;
 
807
  switch (dir) {
 
808
  case UP_DIRECTION:
 
809
    motion.y = p->height()/2.0;
 
810
    break;
 
811
  case DOWN_DIRECTION:
 
812
    motion.y = -p->height()/2.0;
 
813
    break;
 
814
  case LEFT_DIRECTION:
 
815
    motion.x = -p->width()/2.0;
 
816
    break;
 
817
  case RIGHT_DIRECTION:
 
818
    motion.x = p->width()/2.0;
 
819
    break;
 
820
  default:
 
821
    assert(0);
 
822
  }
 
823
  if (flags & HAS_AT) {
 
824
    pos = at;
 
825
    if (flags & HAS_WITH) {
 
826
      place offset;
 
827
      place here;
 
828
      here.obj = p;
 
829
      if (!with->follow(here, &offset))
 
830
        return 0;
 
831
      pos -= offset;
 
832
    }
 
833
  }
 
834
  else {
 
835
    pos = *curpos;
 
836
    pos += motion;
 
837
  }
 
838
  p->move_by(pos);
 
839
  pos += motion;
 
840
  *curpos = pos;
 
841
  return 1;
 
842
}
 
843
 
 
844
class block_object : public rectangle_object {
 
845
  object_list oblist;
 
846
  PTABLE(place) *tbl;
 
847
public:
 
848
  block_object(const position &, const object_list &ol, PTABLE(place) *t);
 
849
  ~block_object();
 
850
  place *find_label(const char *);
 
851
  object_type type();
 
852
  void move_by(const position &);
 
853
  void print();
 
854
};
 
855
 
 
856
block_object::block_object(const position &d, const object_list &ol,
 
857
                           PTABLE(place) *t)
 
858
: rectangle_object(d), oblist(ol), tbl(t)
 
859
{
 
860
}
 
861
 
 
862
block_object::~block_object()
 
863
{
 
864
  delete tbl;
 
865
  object *p = oblist.head;
 
866
  while (p != 0) {
 
867
    object *tem = p;
 
868
    p = p->next;
 
869
    delete tem;
 
870
  }
 
871
}
 
872
 
 
873
void block_object::print()
 
874
{
 
875
  out->begin_block(south_west(), north_east());
 
876
  print_object_list(oblist.head);
 
877
  out->end_block();
 
878
}
 
879
 
 
880
static void adjust_objectless_places(PTABLE(place) *tbl, const position &a)
 
881
{
 
882
  // Adjust all the labels that aren't attached to objects.
 
883
  PTABLE_ITERATOR(place) iter(tbl);
 
884
  const char *key;
 
885
  place *pl;
 
886
  while (iter.next(&key, &pl))
 
887
    if (key && csupper(key[0]) && pl->obj == 0) {
 
888
      pl->x += a.x;
 
889
      pl->y += a.y;
 
890
    }
 
891
}
 
892
 
 
893
void block_object::move_by(const position &a)
 
894
{
 
895
  cent += a;
 
896
  for (object *p = oblist.head; p; p = p->next)
 
897
    p->move_by(a);
 
898
  adjust_objectless_places(tbl, a);
 
899
}
 
900
 
 
901
 
 
902
place *block_object::find_label(const char *name)
 
903
{
 
904
  return tbl->lookup(name);
 
905
}
 
906
 
 
907
object_type block_object::type()
 
908
{
 
909
  return BLOCK_OBJECT;
 
910
}
 
911
 
 
912
graphic_object *object_spec::make_block(position *curpos, direction *dirp)
 
913
{
 
914
  bounding_box bb;
 
915
  for (object *p = oblist.head; p; p = p->next)
 
916
    p->update_bounding_box(&bb);
 
917
  position dim;
 
918
  if (!bb.blank) {
 
919
    position m = -(bb.ll + bb.ur)/2.0;
 
920
    for (object *p = oblist.head; p; p = p->next)
 
921
      p->move_by(m);
 
922
    adjust_objectless_places(tbl, m);
 
923
    dim = bb.ur - bb.ll;
 
924
  }
 
925
  if (flags & HAS_WIDTH)
 
926
    dim.x = width;
 
927
  if (flags & HAS_HEIGHT)
 
928
    dim.y = height;
 
929
  block_object *block = new block_object(dim, oblist, tbl);
 
930
  if (!position_rectangle(block, curpos, dirp)) {
 
931
    delete block;
 
932
    block = 0;
 
933
  }
 
934
  tbl = 0;
 
935
  oblist.head = oblist.tail = 0;
 
936
  return block;
 
937
}
 
938
 
 
939
class text_object : public rectangle_object {
 
940
public:
 
941
  text_object(const position &);
 
942
  object_type type() { return TEXT_OBJECT; }
 
943
};
 
944
 
 
945
text_object::text_object(const position &d)
 
946
: rectangle_object(d)
 
947
{
 
948
}
 
949
 
 
950
graphic_object *object_spec::make_text(position *curpos, direction *dirp)
 
951
{
 
952
  if (!(flags & HAS_HEIGHT)) {
 
953
    lookup_variable("textht", &height);
 
954
    int nitems = 0;
 
955
    for (text_item *t = text; t; t = t->next)
 
956
      nitems++;
 
957
    height *= nitems;
 
958
  }
 
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)) {
 
963
    delete p;
 
964
    p = 0;
 
965
  }
 
966
  return p;
 
967
}
 
968
 
 
969
 
 
970
class ellipse_object : public closed_object {
 
971
public:
 
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; }
 
983
  void print();
 
984
};
 
985
 
 
986
ellipse_object::ellipse_object(const position &d)
 
987
: closed_object(d)
 
988
{
 
989
}
 
990
 
 
991
void ellipse_object::print()
 
992
{
 
993
  if (lt.type == line_type::invisible && fill < 0.0)
 
994
    return;
 
995
  out->ellipse(cent, dim, lt, fill);
 
996
}
 
997
 
 
998
graphic_object *object_spec::make_ellipse(position *curpos, direction *dirp)
 
999
{
 
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;
 
1006
    else
 
1007
      lookup_variable("ellipseht", &height);
 
1008
  }
 
1009
  if (!(flags & HAS_WIDTH)) {
 
1010
    if ((flags & IS_SAME) && have_last_ellipse)
 
1011
      width = last_ellipse_width;
 
1012
    else
 
1013
      lookup_variable("ellipsewid", &width);
 
1014
  }
 
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)) {
 
1020
    delete p;
 
1021
    return 0;
 
1022
  }
 
1023
  return p;
 
1024
}
 
1025
 
 
1026
class circle_object : public ellipse_object {
 
1027
public:
 
1028
  circle_object(double);
 
1029
  object_type type() { return CIRCLE_OBJECT; }
 
1030
  void print();
 
1031
};
 
1032
 
 
1033
circle_object::circle_object(double diam)
 
1034
: ellipse_object(position(diam, diam))
 
1035
{
 
1036
}
 
1037
 
 
1038
void circle_object::print()
 
1039
{
 
1040
  if (lt.type == line_type::invisible && fill < 0.0)
 
1041
    return;
 
1042
  out->circle(cent, dim.x/2.0, lt, fill);
 
1043
}
 
1044
 
 
1045
graphic_object *object_spec::make_circle(position *curpos, direction *dirp)
 
1046
{
 
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;
 
1052
    else
 
1053
      lookup_variable("circlerad", &radius);
 
1054
  }
 
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)) {
 
1059
    delete p;
 
1060
    return 0;
 
1061
  }
 
1062
  return p;
 
1063
}
 
1064
 
 
1065
class move_object : public graphic_object {
 
1066
  position strt;
 
1067
  position en;
 
1068
public:
 
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 &);
 
1074
};
 
1075
 
 
1076
move_object::move_object(const position &s, const position &e)
 
1077
: strt(s), en(e)
 
1078
{
 
1079
}
 
1080
 
 
1081
void move_object::update_bounding_box(bounding_box *p)
 
1082
{
 
1083
  p->encompass(strt);
 
1084
  p->encompass(en);
 
1085
}
 
1086
 
 
1087
void move_object::move_by(const position &a)
 
1088
{
 
1089
  strt += a;
 
1090
  en += a;
 
1091
}
 
1092
 
 
1093
graphic_object *object_spec::make_move(position *curpos, direction *dirp)
 
1094
{
 
1095
  static position last_move;
 
1096
  static int have_last_move = 0;
 
1097
  *dirp = dir;
 
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;
 
1103
    else {
 
1104
      switch (dir) {
 
1105
      case UP_DIRECTION:
 
1106
        segment_pos.y = segment_height;
 
1107
        break;
 
1108
      case DOWN_DIRECTION:
 
1109
        segment_pos.y = -segment_height;
 
1110
        break;
 
1111
      case LEFT_DIRECTION:
 
1112
        segment_pos.x = -segment_width;
 
1113
        break;
 
1114
      case RIGHT_DIRECTION:
 
1115
        segment_pos.x = segment_width;
 
1116
        break;
 
1117
      default:
 
1118
        assert(0);
 
1119
      }
 
1120
    }
 
1121
  }
 
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;
 
1125
  segment_list = 0;
 
1126
  while (old != 0) {
 
1127
    segment *tem = old->next;
 
1128
    old->next = segment_list;
 
1129
    segment_list = old;
 
1130
    old = tem;
 
1131
  }
 
1132
  // Compute the end position.
 
1133
  position endpos = startpos;
 
1134
  for (segment *s = segment_list; s; s = s->next)
 
1135
    if (s->is_absolute)
 
1136
      endpos = s->pos;
 
1137
    else 
 
1138
      endpos += s->pos;
 
1139
  have_last_move = 1;
 
1140
  last_move = endpos - startpos;
 
1141
  move_object *p = new move_object(startpos, endpos);
 
1142
  *curpos = endpos;
 
1143
  return p;
 
1144
}
 
1145
 
 
1146
class linear_object : public graphic_object {
 
1147
protected:
 
1148
  char arrow_at_start;
 
1149
  char arrow_at_end;
 
1150
  arrow_head_type aht;
 
1151
  position strt;
 
1152
  position en;
 
1153
public:
 
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 &);
 
1161
};
 
1162
 
 
1163
class line_object : public linear_object {
 
1164
protected:
 
1165
  position *v;
 
1166
  int n;
 
1167
public:
 
1168
  line_object(const position &s, const position &e, position *, int);
 
1169
  ~line_object();
 
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 *);
 
1178
  void print();
 
1179
  void move_by(const position &);
 
1180
};
 
1181
 
 
1182
class arrow_object : public line_object {
 
1183
public:
 
1184
  arrow_object(const position &, const position &, position *, int);
 
1185
  object_type type() { return ARROW_OBJECT; }
 
1186
};
 
1187
 
 
1188
class spline_object : public line_object {
 
1189
public:
 
1190
  spline_object(const position &, const position &, position *, int);
 
1191
  object_type type() { return SPLINE_OBJECT; }
 
1192
  void print();
 
1193
  void update_bounding_box(bounding_box *);
 
1194
};
 
1195
 
 
1196
linear_object::linear_object(const position &s, const position &e)
 
1197
: arrow_at_start(0), arrow_at_end(0), strt(s), en(e)
 
1198
{
 
1199
}
 
1200
 
 
1201
void linear_object::move_by(const position &a)
 
1202
{
 
1203
  strt += a;
 
1204
  en += a;
 
1205
}
 
1206
 
 
1207
void linear_object::add_arrows(int at_start, int at_end,
 
1208
                               const arrow_head_type &a)
 
1209
{
 
1210
  arrow_at_start = at_start;
 
1211
  arrow_at_end = at_end;
 
1212
  aht = a;
 
1213
}
 
1214
 
 
1215
line_object::line_object(const position &s, const position &e,
 
1216
                         position *p, int i)
 
1217
: linear_object(s, e), v(p), n(i)
 
1218
{
 
1219
}
 
1220
 
 
1221
void line_object::print()
 
1222
{
 
1223
  if (lt.type == line_type::invisible)
 
1224
    return;
 
1225
  out->line(strt, v, n, lt);
 
1226
  if (arrow_at_start)
 
1227
    draw_arrow(strt, strt-v[0], aht, lt);
 
1228
  if (arrow_at_end)
 
1229
    draw_arrow(en, v[n-1] - (n > 1 ? v[n - 2] : strt), aht, lt);
 
1230
}
 
1231
 
 
1232
void line_object::update_bounding_box(bounding_box *p)
 
1233
{
 
1234
  p->encompass(strt);
 
1235
  for (int i = 0; i < n; i++)
 
1236
    p->encompass(v[i]);
 
1237
}
 
1238
 
 
1239
void line_object::move_by(const position &pos)
 
1240
{
 
1241
  linear_object::move_by(pos);
 
1242
  for (int i = 0; i < n; i++)
 
1243
    v[i] += pos;
 
1244
}
 
1245
  
 
1246
void spline_object::update_bounding_box(bounding_box *p)
 
1247
{
 
1248
  p->encompass(strt);
 
1249
  p->encompass(en);
 
1250
  /*
 
1251
 
 
1252
  If
 
1253
 
 
1254
  p1 = q1/2 + q2/2
 
1255
  p2 = q1/6 + q2*5/6
 
1256
  p3 = q2*5/6 + q3/6
 
1257
  p4 = q2/2 + q3/2
 
1258
  [ the points for the Bezier cubic ]
 
1259
 
 
1260
  and
 
1261
 
 
1262
  t = .5
 
1263
 
 
1264
  then
 
1265
 
 
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 ]
 
1268
 
 
1269
  = .125*q1 + .75*q2 + .125*q3
 
1270
 
 
1271
  */
 
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);
 
1274
}
 
1275
 
 
1276
arrow_object::arrow_object(const position &s, const position &e,
 
1277
                           position *p, int i)
 
1278
: line_object(s, e, p, i)
 
1279
{
 
1280
}
 
1281
 
 
1282
spline_object::spline_object(const position &s, const position &e,
 
1283
                             position *p, int i)
 
1284
: line_object(s, e, p, i)
 
1285
{
 
1286
}
 
1287
 
 
1288
void spline_object::print()
 
1289
{
 
1290
  if (lt.type == line_type::invisible)
 
1291
    return;
 
1292
  out->spline(strt, v, n, lt);
 
1293
  if (arrow_at_start)
 
1294
    draw_arrow(strt, strt-v[0], aht, lt);
 
1295
  if (arrow_at_end)
 
1296
    draw_arrow(en, v[n-1] - (n > 1 ? v[n - 2] : strt), aht, lt);
 
1297
}
 
1298
 
 
1299
line_object::~line_object()
 
1300
{
 
1301
  a_delete v;
 
1302
}
 
1303
 
 
1304
linear_object *object_spec::make_line(position *curpos, direction *dirp)
 
1305
{
 
1306
  static position last_line;
 
1307
  static int have_last_line = 0;
 
1308
  *dirp = dir;
 
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)
 
1313
        && have_last_line)
 
1314
      segment_pos = last_line;
 
1315
    else 
 
1316
      switch (dir) {
 
1317
      case UP_DIRECTION:
 
1318
        segment_pos.y = segment_height;
 
1319
        break;
 
1320
      case DOWN_DIRECTION:
 
1321
        segment_pos.y = -segment_height;
 
1322
        break;
 
1323
      case LEFT_DIRECTION:
 
1324
        segment_pos.x = -segment_width;
 
1325
        break;
 
1326
      case RIGHT_DIRECTION:
 
1327
        segment_pos.x = segment_width;
 
1328
        break;
 
1329
      default:
 
1330
        assert(0);
 
1331
      }
 
1332
  }
 
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;
 
1336
  segment_list = 0;
 
1337
  while (old != 0) {
 
1338
    segment *tem = old->next;
 
1339
    old->next = segment_list;
 
1340
    segment_list = old;
 
1341
    old = tem;
 
1342
  }
 
1343
  // Absolutise all movements
 
1344
  position endpos = startpos;
 
1345
  int nsegments = 0;
 
1346
  segment *s;
 
1347
  for (s = segment_list; s; s = s->next, nsegments++)
 
1348
    if (s->is_absolute)
 
1349
      endpos = s->pos;
 
1350
    else {
 
1351
      endpos += s->pos;
 
1352
      s->pos = endpos;
 
1353
      s->is_absolute = 1;       // to avoid confusion
 
1354
    }
 
1355
  // handle chop
 
1356
  line_object *p = 0;
 
1357
  position *v = new position[nsegments];
 
1358
  int i = 0;
 
1359
  for (s = segment_list; s; s = s->next, i++)
 
1360
    v[i] = s->pos;
 
1361
  if (flags & IS_DEFAULT_CHOPPED) {
 
1362
    lookup_variable("circlerad", &start_chop);
 
1363
    end_chop = start_chop;
 
1364
    flags |= IS_CHOPPED;
 
1365
  }
 
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);
 
1371
    }
 
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);
 
1376
    }
 
1377
    startpos += start_chop_vec;
 
1378
    v[nsegments - 1] -= end_chop_vec;
 
1379
    endpos -= end_chop_vec;
 
1380
  }
 
1381
  switch (type) {
 
1382
  case SPLINE_OBJECT:
 
1383
    p = new spline_object(startpos, endpos, v, nsegments);
 
1384
    break;
 
1385
  case ARROW_OBJECT:
 
1386
    p = new arrow_object(startpos, endpos, v, nsegments);
 
1387
    break;
 
1388
  case LINE_OBJECT:
 
1389
    p = new line_object(startpos, endpos, v, nsegments);
 
1390
    break;
 
1391
  default:
 
1392
    assert(0);
 
1393
  }
 
1394
  have_last_line = 1;
 
1395
  last_line = endpos - startpos;
 
1396
  *curpos = endpos;
 
1397
  return p;
 
1398
}
 
1399
 
 
1400
class arc_object : public linear_object {
 
1401
  int clockwise;
 
1402
  position cent;
 
1403
  double rad;
 
1404
public:
 
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; }
 
1409
  position north();
 
1410
  position south();
 
1411
  position east();
 
1412
  position west();
 
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; }
 
1419
  void print();
 
1420
  void move_by(const position &pos);
 
1421
};
 
1422
 
 
1423
arc_object::arc_object(int cw, const position &s, const position &e,
 
1424
                       const position &c)
 
1425
: linear_object(s, e), clockwise(cw), cent(c)
 
1426
{
 
1427
  rad = hypot(c - s);
 
1428
}
 
1429
 
 
1430
void arc_object::move_by(const position &pos)
 
1431
{
 
1432
  linear_object::move_by(pos);
 
1433
  cent += pos;
 
1434
}
 
1435
 
 
1436
// we get arc corners from the corresponding circle
 
1437
 
 
1438
position arc_object::north()
 
1439
{
 
1440
  position result(cent);
 
1441
  result.y += rad;
 
1442
  return result;
 
1443
}
 
1444
 
 
1445
position arc_object::south()
 
1446
{
 
1447
  position result(cent);
 
1448
  result.y -= rad;
 
1449
  return result;
 
1450
}
 
1451
 
 
1452
position arc_object::east()
 
1453
{
 
1454
  position result(cent);
 
1455
  result.x += rad;
 
1456
  return result;
 
1457
}
 
1458
 
 
1459
position arc_object::west()
 
1460
{
 
1461
  position result(cent);
 
1462
  result.x -= rad;
 
1463
  return result;
 
1464
}
 
1465
 
 
1466
position arc_object::north_east()
 
1467
{
 
1468
  position result(cent);
 
1469
  result.x += rad/M_SQRT2;
 
1470
  result.y += rad/M_SQRT2;
 
1471
  return result;
 
1472
}
 
1473
 
 
1474
position arc_object::north_west()
 
1475
{
 
1476
  position result(cent);
 
1477
  result.x -= rad/M_SQRT2;
 
1478
  result.y += rad/M_SQRT2;
 
1479
  return result;
 
1480
}
 
1481
 
 
1482
position arc_object::south_east()
 
1483
{
 
1484
  position result(cent);
 
1485
  result.x += rad/M_SQRT2;
 
1486
  result.y -= rad/M_SQRT2;
 
1487
  return result;
 
1488
}
 
1489
 
 
1490
position arc_object::south_west()
 
1491
{
 
1492
  position result(cent);
 
1493
  result.x -= rad/M_SQRT2;
 
1494
  result.y -= rad/M_SQRT2;
 
1495
  return result;
 
1496
}
 
1497
 
 
1498
 
 
1499
void arc_object::print()
 
1500
{
 
1501
  if (lt.type == line_type::invisible)
 
1502
    return;
 
1503
  if (clockwise)
 
1504
    out->arc(en, cent, strt, lt);
 
1505
  else
 
1506
    out->arc(strt, cent, en, lt);
 
1507
  if (arrow_at_start) {
 
1508
    position c = cent - strt;
 
1509
    draw_arrow(strt,
 
1510
               (clockwise ? position(c.y, -c.x) : position(-c.y, c.x)),
 
1511
               aht, lt);
 
1512
  }
 
1513
  if (arrow_at_end) {
 
1514
    position e = en - cent;
 
1515
    draw_arrow(en,
 
1516
               (clockwise ? position(e.y, -e.x) : position(-e.y, e.x)),
 
1517
               aht, lt);
 
1518
  }
 
1519
}
 
1520
 
 
1521
inline double max(double a, double b)
 
1522
{
 
1523
  return a > b ? a : b;
 
1524
}
 
1525
 
 
1526
void arc_object::update_bounding_box(bounding_box *p)
 
1527
{
 
1528
  p->encompass(strt);
 
1529
  p->encompass(en);
 
1530
  position start_offset = strt - cent;
 
1531
  if (start_offset.x == 0.0 && start_offset.y == 0.0)
 
1532
    return;
 
1533
  position end_offset = en  - cent;
 
1534
  if (end_offset.x == 0.0 && end_offset.y == 0.0)
 
1535
    return;
 
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);
 
1538
  if (clockwise) {
 
1539
    double temp = start_quad;
 
1540
    start_quad = end_quad;
 
1541
    end_quad = temp;
 
1542
  }
 
1543
  if (start_quad < 0.0)
 
1544
    start_quad += 4.0;
 
1545
  while (end_quad <= start_quad)
 
1546
    end_quad += 4.0;
 
1547
  double radius = max(hypot(start_offset), hypot(end_offset));
 
1548
  for (int q = int(start_quad) + 1; q < end_quad; q++) {
 
1549
    position offset;
 
1550
    switch (q % 4) {
 
1551
    case 0:
 
1552
      offset.x = radius;
 
1553
      break;
 
1554
    case 1:
 
1555
      offset.y = radius;
 
1556
      break;
 
1557
    case 2:
 
1558
      offset.x = -radius;
 
1559
      break;
 
1560
    case 3:
 
1561
      offset.y = -radius;
 
1562
      break;
 
1563
    }
 
1564
    p->encompass(cent + offset);
 
1565
  }
 
1566
}
 
1567
 
 
1568
// We ignore the with attribute. The at attribute always refers to the center.
 
1569
 
 
1570
linear_object *object_spec::make_arc(position *curpos, direction *dirp)
 
1571
{
 
1572
  *dirp = dir;
 
1573
  int cw = (flags & IS_CLOCKWISE) != 0;
 
1574
  // compute the start
 
1575
  position startpos;
 
1576
  if (flags & HAS_FROM)
 
1577
    startpos = from;
 
1578
  else
 
1579
    startpos = *curpos;
 
1580
  if (!(flags & HAS_RADIUS))
 
1581
    lookup_variable("arcrad", &radius);
 
1582
  // compute the end
 
1583
  position endpos;
 
1584
  if (flags & HAS_TO)
 
1585
    endpos = to;
 
1586
  else {
 
1587
    position m(radius, radius);
 
1588
    // Adjust the signs.
 
1589
    if (cw) {
 
1590
      if (dir == DOWN_DIRECTION || dir == LEFT_DIRECTION)
 
1591
        m.x = -m.x;
 
1592
      if (dir == DOWN_DIRECTION || dir == RIGHT_DIRECTION)
 
1593
        m.y = -m.y;
 
1594
      *dirp = direction((dir + 3) % 4);
 
1595
    }
 
1596
    else {
 
1597
      if (dir == UP_DIRECTION || dir == LEFT_DIRECTION)
 
1598
        m.x = -m.x;
 
1599
      if (dir == DOWN_DIRECTION || dir == LEFT_DIRECTION)
 
1600
        m.y = -m.y;
 
1601
      *dirp = direction((dir + 1) % 4);
 
1602
    }
 
1603
    endpos = startpos + m;
 
1604
  }
 
1605
  // compute the center
 
1606
  position centerpos;
 
1607
  if (flags & HAS_AT)
 
1608
    centerpos = at;
 
1609
  else if (startpos == endpos)
 
1610
    centerpos = startpos;
 
1611
  else {
 
1612
    position h = (endpos - startpos)/2.0;
 
1613
    double d = hypot(h);
 
1614
    if (radius <= 0)
 
1615
      radius = .25;
 
1616
    // make the radius big enough
 
1617
    while (radius < d)
 
1618
      radius *= 2.0;
 
1619
    double alpha = acos(d/radius);
 
1620
    double theta = atan2(h.y, h.x);
 
1621
    if (cw)
 
1622
      theta -= alpha;
 
1623
    else
 
1624
      theta += alpha;
 
1625
    centerpos = position(cos(theta), sin(theta))*radius + startpos;
 
1626
  }
 
1627
  arc_object *p = new arc_object(cw, startpos, endpos, centerpos);
 
1628
  *curpos = endpos;
 
1629
  return p;
 
1630
}
 
1631
 
 
1632
graphic_object *object_spec::make_linear(position *curpos, direction *dirp)
 
1633
{
 
1634
  linear_object *obj;
 
1635
  if (type == ARC_OBJECT)
 
1636
    obj = make_arc(curpos, dirp);
 
1637
  else
 
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))) {
 
1643
    arrow_head_type a;
 
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)
 
1647
      a.height = height;
 
1648
    else
 
1649
      lookup_variable("arrowht", &a.height);
 
1650
    if (flags & HAS_WIDTH)
 
1651
      a.width = width;
 
1652
    else
 
1653
      lookup_variable("arrowwid", &a.width);
 
1654
    double solid;
 
1655
    lookup_variable("arrowhead", &solid);
 
1656
    a.solid = solid != 0.0;
 
1657
    obj->add_arrows(at_start, at_end, a);
 
1658
  }
 
1659
  return obj;
 
1660
}
 
1661
 
 
1662
object *object_spec::make_object(position *curpos, direction *dirp)
 
1663
{
 
1664
  graphic_object *obj = 0;
 
1665
  switch (type) {
 
1666
  case BLOCK_OBJECT:
 
1667
    obj = make_block(curpos, dirp);
 
1668
    break;
 
1669
  case BOX_OBJECT:
 
1670
    obj = make_box(curpos, dirp);
 
1671
    break;
 
1672
  case TEXT_OBJECT:
 
1673
    obj = make_text(curpos, dirp);
 
1674
    break;
 
1675
  case ELLIPSE_OBJECT:
 
1676
    obj = make_ellipse(curpos, dirp);
 
1677
    break;
 
1678
  case CIRCLE_OBJECT:
 
1679
    obj = make_circle(curpos, dirp);
 
1680
    break;
 
1681
  case MOVE_OBJECT:
 
1682
    obj = make_move(curpos, dirp);
 
1683
    break;
 
1684
  case ARC_OBJECT:
 
1685
  case LINE_OBJECT:
 
1686
  case SPLINE_OBJECT:
 
1687
  case ARROW_OBJECT:
 
1688
    obj = make_linear(curpos, dirp);
 
1689
    break;
 
1690
  case MARK_OBJECT:
 
1691
  case OTHER_OBJECT:
 
1692
  default:
 
1693
    assert(0);
 
1694
    break;
 
1695
  }
 
1696
  if (obj) {
 
1697
    if (flags & IS_INVISIBLE)
 
1698
      obj->set_invisible();
 
1699
    if (text != 0)
 
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);
 
1705
    double th;
 
1706
    if (flags & HAS_THICKNESS)
 
1707
      th = thickness;
 
1708
    else
 
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);
 
1714
      if (fill < 0.0)
 
1715
        error("bad fill value %1", fill);
 
1716
      else
 
1717
        obj->set_fill(fill);
 
1718
    }
 
1719
  }
 
1720
  return obj;
 
1721
}
 
1722
 
 
1723
struct string_list {
 
1724
  string_list *next;
 
1725
  char *str;
 
1726
  string_list(char *);
 
1727
  ~string_list();
 
1728
};
 
1729
 
 
1730
string_list::string_list(char *s)
 
1731
: next(0), str(s)
 
1732
{
 
1733
}
 
1734
 
 
1735
string_list::~string_list()
 
1736
{
 
1737
  a_delete str;
 
1738
}
 
1739
  
 
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. */
 
1744
 
 
1745
path::path(corner c)
 
1746
: crn(c), label_list(0), ypath(0)
 
1747
{
 
1748
}
 
1749
 
 
1750
path::path(char *l, corner c)
 
1751
: crn(c), ypath(0)
 
1752
{
 
1753
  label_list = new string_list(l);
 
1754
}
 
1755
 
 
1756
path::~path()
 
1757
{
 
1758
  while (label_list) {
 
1759
    string_list *tem = label_list;
 
1760
    label_list = label_list->next;
 
1761
    delete tem;
 
1762
  }
 
1763
  delete ypath;
 
1764
}
 
1765
 
 
1766
void path::append(corner c)
 
1767
{
 
1768
  assert(crn == 0);
 
1769
  crn = c;
 
1770
}
 
1771
 
 
1772
void path::append(char *s)
 
1773
{
 
1774
  string_list **p;
 
1775
  for (p = &label_list; *p; p = &(*p)->next)
 
1776
    ;
 
1777
  *p = new string_list(s);
 
1778
}
 
1779
 
 
1780
void path::set_ypath(path *p)
 
1781
{
 
1782
  ypath = p;
 
1783
}
 
1784
 
 
1785
// return non-zero for success
 
1786
 
 
1787
int path::follow(const place &pl, place *result) const
 
1788
{
 
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);
 
1793
      return 0;
 
1794
    }
 
1795
  if (crn == 0 || p->obj == 0)
 
1796
    *result = *p;
 
1797
  else {
 
1798
    position pos = ((p->obj)->*(crn))();
 
1799
    result->x = pos.x;
 
1800
    result->y = pos.y;
 
1801
    result->obj = 0;
 
1802
  }
 
1803
  if (ypath) {
 
1804
    place tem;
 
1805
    if (!ypath->follow(pl, &tem))
 
1806
      return 0;
 
1807
    result->y = tem.y;
 
1808
    if (result->obj != tem.obj)
 
1809
      result->obj = 0;
 
1810
  }
 
1811
  return 1;
 
1812
}
 
1813
 
 
1814
void print_object_list(object *p)
 
1815
{
 
1816
  for (; p; p = p->next) {
 
1817
    p->print();
 
1818
    p->print_text();
 
1819
  }
 
1820
}
 
1821
 
 
1822
void print_picture(object *obj)
 
1823
{
 
1824
  bounding_box bb;
 
1825
  for (object *p = obj; p; p = p->next)
 
1826
    p->update_bounding_box(&bb);
 
1827
  double scale;
 
1828
  lookup_variable("scale", &scale);
 
1829
  out->start_picture(scale, bb.ll, bb.ur);
 
1830
  print_object_list(obj);
 
1831
  out->finish_picture();
 
1832
}
 
1833