1// -*- C++ -*-
2/* Copyright (C) 1989, 1990, 1991, 1992, 2003 Free Software Foundation, Inc.
3     Written by James Clark (jjc@jclark.com)
4
5This file is part of groff.
6
7groff is free software; you can redistribute it and/or modify it under
8the terms of the GNU General Public License as published by the Free
9Software Foundation; either version 2, or (at your option) any later
10version.
11
12groff is distributed in the hope that it will be useful, but WITHOUT ANY
13WARRANTY; without even the implied warranty of MERCHANTABILITY or
14FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15for more details.
16
17You should have received a copy of the GNU General Public License along
18with groff; see the file COPYING.  If not, write to the Free Software
19Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
20
21#include "pic.h"
22#include "common.h"
23
24// output a dashed circle as a series of arcs
25
26void common_output::dashed_circle(const position &cent, double rad,
27				  const line_type &lt)
28{
29  assert(lt.type == line_type::dashed);
30  line_type slt = lt;
31  slt.type = line_type::solid;
32  double dash_angle = lt.dash_width/rad;
33  int ndashes;
34  double gap_angle;
35  if (dash_angle >= M_PI/4.0) {
36    if (dash_angle < M_PI/2.0) {
37      gap_angle = M_PI/2.0 - dash_angle;
38      ndashes = 4;
39    }
40    else if (dash_angle < M_PI) {
41      gap_angle = M_PI - dash_angle;
42      ndashes = 2;
43    }
44    else {
45      circle(cent, rad, slt, -1.0);
46      return;
47    }
48  }
49  else {
50    ndashes = 4*int(ceil(M_PI/(4.0*dash_angle)));
51    gap_angle = (M_PI*2.0)/ndashes - dash_angle;
52  }
53  for (int i = 0; i < ndashes; i++) {
54    double start_angle = i*(dash_angle+gap_angle) - dash_angle/2.0;
55    solid_arc(cent, rad, start_angle, start_angle + dash_angle, lt);
56  }
57}
58
59// output a dotted circle as a series of dots
60
61void common_output::dotted_circle(const position &cent, double rad,
62				  const line_type &lt)
63{
64  assert(lt.type == line_type::dotted);
65  double gap_angle = lt.dash_width/rad;
66  int ndots;
67  if (gap_angle >= M_PI/2.0) {
68    // always have at least 2 dots
69    gap_angle = M_PI;
70    ndots = 2;
71  }
72  else {
73    ndots = 4*int(M_PI/(2.0*gap_angle));
74    gap_angle = (M_PI*2.0)/ndots;
75  }
76  double ang = 0.0;
77  for (int i = 0; i < ndots; i++, ang += gap_angle)
78    dot(cent + position(cos(ang), sin(ang))*rad, lt);
79}
80
81// recursive function for dash drawing, used by dashed_ellipse
82
83void common_output::ellipse_arc(const position &cent,
84				const position &z0, const position &z1,
85				const distance &dim, const line_type &lt)
86{
87  assert(lt.type == line_type::solid);
88  assert(dim.x != 0 && dim.y != 0);
89  double eps = 0.0001;
90  position zml = (z0 + z1) / 2;
91  // apply affine transformation (from ellipse to circle) to compute angle
92  // of new position, then invert transformation to get exact position
93  double psi = atan2(zml.y / dim.y, zml.x / dim.x);
94  position zm = position(dim.x * cos(psi), dim.y * sin(psi));
95  // to approximate the ellipse arc with one or more circle arcs, we
96  // first compute the radius of curvature in zm
97  double a_2 = dim.x * dim.x;
98  double a_4 = a_2 * a_2;
99  double b_2 = dim.y * dim.y;
100  double b_4 = b_2 * b_2;
101  double e_2 = a_2 - b_2;
102  double temp = a_4 * zm.y * zm.y + b_4 * zm.x * zm.x;
103  double rho = sqrt(temp / a_4 / b_4 * temp / a_4 / b_4 * temp);
104  // compute center of curvature circle
105  position M = position(e_2 * zm.x / a_2 * zm.x / a_2 * zm.x,
106			-e_2 * zm.y / b_2 * zm.y / b_2 * zm.y);
107  // compute distance between circle and ellipse arc at start and end
108  double phi0 = atan2(z0.y - M.y, z0.x - M.x);
109  double phi1 = atan2(z1.y - M.y, z1.x - M.x);
110  position M0 = position(rho * cos(phi0), rho * sin(phi0)) + M;
111  position M1 = position(rho * cos(phi1), rho * sin(phi1)) + M;
112  double dist0 = hypot(z0 - M0) / sqrt(z0 * z0);
113  double dist1 = hypot(z1 - M1) / sqrt(z1 * z1);
114  if (dist0 < eps && dist1 < eps)
115    solid_arc(M + cent, rho, phi0, phi1, lt);
116  else {
117    ellipse_arc(cent, z0, zm, dim, lt);
118    ellipse_arc(cent, zm, z1, dim, lt);
119  }
120}
121
122// output a dashed ellipse as a series of arcs
123
124void common_output::dashed_ellipse(const position &cent, const distance &dim,
125				   const line_type &lt)
126{
127  assert(lt.type == line_type::dashed);
128  double dim_x = dim.x / 2;
129  double dim_y = dim.y / 2;
130  line_type slt = lt;
131  slt.type = line_type::solid;
132  double dw = lt.dash_width;
133  // we use an approximation to compute the ellipse length (found in:
134  // Bronstein, Semendjajew, Taschenbuch der Mathematik)
135  double lambda = (dim.x - dim.y) / (dim.x + dim.y);
136  double le = M_PI / 2 * (dim.x + dim.y)
137	      * ((64 - 3 * lambda * lambda * lambda * lambda )
138		 / (64 - 16 * lambda * lambda));
139  // for symmetry we make nmax a multiple of 8
140  int nmax = 8 * int(le / dw / 8 + 0.5);
141  if (nmax < 8) {
142    nmax = 8;
143    dw = le / 8;
144  }
145  int ndash = nmax / 2;
146  double gapwidth = (le - dw * ndash) / ndash;
147  double l = 0;
148  position z = position(dim_x, 0);
149  position zdot = z;
150  int j = 0;
151  int jmax = int(10 / lt.dash_width);
152  for (int i = 0; i <= nmax; i++) {
153    position zold = z;
154    position zpre = zdot;
155    double ld = (int(i / 2) + 0.5) * dw + int((i + 1) / 2) * gapwidth;
156    double lold = 0;
157    double dl = 1;
158    // find next position for fixed arc length
159    while (l < ld) {
160      j++;
161      lold = l;
162      zold = z;
163      double phi = j * 2 * M_PI / jmax;
164      z = position(dim_x * cos(phi), dim_y * sin(phi));
165      dl = hypot(z - zold);
166      l += dl;
167    }
168    // interpolate linearly between the last two points,
169    // using the length difference as the scaling factor
170    double delta = (ld - lold) / dl;
171    zdot = zold + (z - zold) * delta;
172    // compute angle of new position on the affine circle
173    // and use it to get the exact value on the ellipse
174    double psi = atan2(zdot.y / dim_y, zdot.x / dim_x);
175    zdot = position(dim_x * cos(psi), dim_y * sin(psi));
176    if ((i % 2 == 0) && (i > 1))
177      ellipse_arc(cent, zpre, zdot, dim / 2, slt);
178  }
179}
180
181// output a dotted ellipse as a series of dots
182
183void common_output::dotted_ellipse(const position &cent, const distance &dim,
184				   const line_type &lt)
185{
186  assert(lt.type == line_type::dotted);
187  double dim_x = dim.x / 2;
188  double dim_y = dim.y / 2;
189  line_type slt = lt;
190  slt.type = line_type::solid;
191  // we use an approximation to compute the ellipse length (found in:
192  // Bronstein, Semendjajew, Taschenbuch der Mathematik)
193  double lambda = (dim.x - dim.y) / (dim.x + dim.y);
194  double le = M_PI / 2 * (dim.x + dim.y)
195	      * ((64 - 3 * lambda * lambda * lambda * lambda )
196		 / (64 - 16 * lambda * lambda));
197  // for symmetry we make nmax a multiple of 4
198  int ndots = 4 * int(le / lt.dash_width / 4 + 0.5);
199  if (ndots < 4)
200    ndots = 4;
201  double l = 0;
202  position z = position(dim_x, 0);
203  int j = 0;
204  int jmax = int(10 / lt.dash_width);
205  for (int i = 1; i <= ndots; i++) {
206    position zold = z;
207    double lold = l;
208    double ld = i * le / ndots;
209    double dl = 1;
210    // find next position for fixed arc length
211    while (l < ld) {
212      j++;
213      lold = l;
214      zold = z;
215      double phi = j * 2 * M_PI / jmax;
216      z = position(dim_x * cos(phi), dim_y * sin(phi));
217      dl = hypot(z - zold);
218      l += dl;
219    }
220    // interpolate linearly between the last two points,
221    // using the length difference as the scaling factor
222    double delta = (ld - lold) / dl;
223    position zdot = zold + (z - zold) * delta;
224    // compute angle of new position on the affine circle
225    // and use it to get the exact value on the ellipse
226    double psi = atan2(zdot.y / dim_y, zdot.x / dim_x);
227    zdot = position(dim_x * cos(psi), dim_y * sin(psi));
228    dot(cent + zdot, slt);
229  }
230}
231
232// return non-zero iff we can compute a center
233
234int compute_arc_center(const position &start, const position &cent,
235		       const position &end, position *result)
236{
237  // This finds the point along the vector from start to cent that
238  // is equidistant between start and end.
239  distance c = cent - start;
240  distance e = end - start;
241  double n = c*e;
242  if (n == 0.0)
243    return 0;
244  *result = start + c*((e*e)/(2.0*n));
245  return 1;
246}
247
248// output a dashed arc as a series of arcs
249
250void common_output::dashed_arc(const position &start, const position &cent,
251			       const position &end, const line_type &lt)
252{
253  assert(lt.type == line_type::dashed);
254  position c;
255  if (!compute_arc_center(start, cent, end, &c)) {
256    line(start, &end, 1, lt);
257    return;
258  }
259  distance start_offset = start - c;
260  distance end_offset = end - c;
261  double start_angle = atan2(start_offset.y, start_offset.x);
262  double end_angle = atan2(end_offset.y, end_offset.x);
263  double rad = hypot(c - start);
264  double dash_angle = lt.dash_width/rad;
265  double total_angle = end_angle - start_angle;
266  while (total_angle < 0)
267    total_angle += M_PI + M_PI;
268  if (total_angle <= dash_angle*2.0) {
269    solid_arc(cent, rad, start_angle, end_angle, lt);
270    return;
271  }
272  int ndashes = int((total_angle - dash_angle)/(dash_angle*2.0) + .5);
273  double dash_and_gap_angle = (total_angle - dash_angle)/ndashes;
274  for (int i = 0; i <= ndashes; i++)
275    solid_arc(cent, rad, start_angle + i*dash_and_gap_angle,
276	      start_angle + i*dash_and_gap_angle + dash_angle, lt);
277}
278
279// output a dotted arc as a series of dots
280
281void common_output::dotted_arc(const position &start, const position &cent,
282			       const position &end, const line_type &lt)
283{
284  assert(lt.type == line_type::dotted);
285  position c;
286  if (!compute_arc_center(start, cent, end, &c)) {
287    line(start, &end, 1, lt);
288    return;
289  }
290  distance start_offset = start - c;
291  distance end_offset = end - c;
292  double start_angle = atan2(start_offset.y, start_offset.x);
293  double total_angle = atan2(end_offset.y, end_offset.x) - start_angle;
294  while (total_angle < 0)
295    total_angle += M_PI + M_PI;
296  double rad = hypot(c - start);
297  int ndots = int(total_angle/(lt.dash_width/rad) + .5);
298  if (ndots == 0)
299    dot(start, lt);
300  else {
301    for (int i = 0; i <= ndots; i++) {
302      double a = start_angle + (total_angle*i)/ndots;
303      dot(cent + position(cos(a), sin(a))*rad, lt);
304    }
305  }
306}
307
308void common_output::solid_arc(const position &cent, double rad,
309			      double start_angle, double end_angle,
310			      const line_type &lt)
311{
312  line_type slt = lt;
313  slt.type = line_type::solid;
314  arc(cent + position(cos(start_angle), sin(start_angle))*rad,
315      cent,
316      cent + position(cos(end_angle), sin(end_angle))*rad,
317      slt);
318}
319
320
321void common_output::rounded_box(const position &cent, const distance &dim,
322				double rad, const line_type &lt, double fill)
323{
324  if (fill >= 0.0)
325    filled_rounded_box(cent, dim, rad, fill);
326  switch (lt.type) {
327  case line_type::invisible:
328    break;
329  case line_type::dashed:
330    dashed_rounded_box(cent, dim, rad, lt);
331    break;
332  case line_type::dotted:
333    dotted_rounded_box(cent, dim, rad, lt);
334    break;
335  case line_type::solid:
336    solid_rounded_box(cent, dim, rad, lt);
337    break;
338  default:
339    assert(0);
340  }
341}
342
343
344void common_output::dashed_rounded_box(const position &cent,
345				       const distance &dim, double rad,
346				       const line_type &lt)
347{
348  line_type slt = lt;
349  slt.type = line_type::solid;
350
351  double hor_length = dim.x + (M_PI/2.0 - 2.0)*rad;
352  int n_hor_dashes = int(hor_length/(lt.dash_width*2.0) + .5);
353  double hor_gap_width = (n_hor_dashes != 0
354			  ? hor_length/n_hor_dashes - lt.dash_width
355			  : 0.0);
356
357  double vert_length = dim.y + (M_PI/2.0 - 2.0)*rad;
358  int n_vert_dashes = int(vert_length/(lt.dash_width*2.0) + .5);
359  double vert_gap_width = (n_vert_dashes != 0
360			   ? vert_length/n_vert_dashes - lt.dash_width
361			   : 0.0);
362  // Note that each corner arc has to be split into two for dashing,
363  // because one part is dashed using vert_gap_width, and the other
364  // using hor_gap_width.
365  double offset = lt.dash_width/2.0;
366  dash_arc(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad,
367	   -M_PI/4.0, 0, slt, lt.dash_width, vert_gap_width, &offset);
368  dash_line(cent + position(dim.x/2.0, -dim.y/2.0 + rad),
369	    cent + position(dim.x/2.0, dim.y/2.0 - rad),
370	    slt, lt.dash_width, vert_gap_width, &offset);
371  dash_arc(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad,
372	   0, M_PI/4.0, slt, lt.dash_width, vert_gap_width, &offset);
373
374  offset = lt.dash_width/2.0;
375  dash_arc(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad,
376	   M_PI/4.0, M_PI/2, slt, lt.dash_width, hor_gap_width, &offset);
377  dash_line(cent + position(dim.x/2.0 - rad, dim.y/2.0),
378	    cent + position(-dim.x/2.0 + rad, dim.y/2.0),
379	    slt, lt.dash_width, hor_gap_width, &offset);
380  dash_arc(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad,
381	   M_PI/2, 3*M_PI/4.0, slt, lt.dash_width, hor_gap_width, &offset);
382
383  offset = lt.dash_width/2.0;
384  dash_arc(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad,
385	   3.0*M_PI/4.0, M_PI, slt, lt.dash_width, vert_gap_width, &offset);
386  dash_line(cent + position(-dim.x/2.0, dim.y/2.0 - rad),
387	    cent + position(-dim.x/2.0, -dim.y/2.0 + rad),
388	    slt, lt.dash_width, vert_gap_width, &offset);
389  dash_arc(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad,
390	   M_PI, 5.0*M_PI/4.0, slt, lt.dash_width, vert_gap_width, &offset);
391
392  offset = lt.dash_width/2.0;
393  dash_arc(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad,
394	   5*M_PI/4.0, 3*M_PI/2.0, slt, lt.dash_width, hor_gap_width, &offset);
395  dash_line(cent + position(-dim.x/2.0 + rad, -dim.y/2.0),
396	    cent + position(dim.x/2.0 - rad, -dim.y/2.0),
397	    slt, lt.dash_width, hor_gap_width, &offset);
398  dash_arc(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad,
399	   3*M_PI/2, 7*M_PI/4, slt, lt.dash_width, hor_gap_width, &offset);
400}
401
402// Used by dashed_rounded_box.
403
404void common_output::dash_arc(const position &cent, double rad,
405			     double start_angle, double end_angle,
406			     const line_type &lt,
407			     double dash_width, double gap_width,
408			     double *offsetp)
409{
410  double length = (end_angle - start_angle)*rad;
411  double pos = 0.0;
412  for (;;) {
413    if (*offsetp >= dash_width) {
414      double rem = dash_width + gap_width - *offsetp;
415      if (pos + rem > length) {
416	*offsetp += length - pos;
417	break;
418      }
419      else {
420	pos += rem;
421	*offsetp = 0.0;
422      }
423    }
424    else {
425      double rem = dash_width  - *offsetp;
426      if (pos + rem > length) {
427	solid_arc(cent, rad, start_angle + pos/rad, end_angle, lt);
428	*offsetp += length - pos;
429	break;
430      }
431      else {
432	solid_arc(cent, rad, start_angle + pos/rad,
433		  start_angle + (pos + rem)/rad, lt);
434	pos += rem;
435	*offsetp = dash_width;
436      }
437    }
438  }
439}
440
441// Used by dashed_rounded_box.
442
443void common_output::dash_line(const position &start, const position &end,
444			      const line_type &lt,
445			      double dash_width, double gap_width,
446			      double *offsetp)
447{
448  distance dist = end - start;
449  double length = hypot(dist);
450  if (length == 0.0)
451    return;
452  double pos = 0.0;
453  for (;;) {
454    if (*offsetp >= dash_width) {
455      double rem = dash_width + gap_width - *offsetp;
456      if (pos + rem > length) {
457	*offsetp += length - pos;
458	break;
459      }
460      else {
461	pos += rem;
462	*offsetp = 0.0;
463      }
464    }
465    else {
466      double rem = dash_width  - *offsetp;
467      if (pos + rem > length) {
468	line(start + dist*(pos/length), &end, 1, lt);
469	*offsetp += length - pos;
470	break;
471      }
472      else {
473	position p(start + dist*((pos + rem)/length));
474	line(start + dist*(pos/length), &p, 1, lt);
475	pos += rem;
476	*offsetp = dash_width;
477      }
478    }
479  }
480}
481
482void common_output::dotted_rounded_box(const position &cent,
483				       const distance &dim, double rad,
484				       const line_type &lt)
485{
486  line_type slt = lt;
487  slt.type = line_type::solid;
488
489  double hor_length = dim.x + (M_PI/2.0 - 2.0)*rad;
490  int n_hor_dots = int(hor_length/lt.dash_width + .5);
491  double hor_gap_width = (n_hor_dots != 0
492			  ? hor_length/n_hor_dots
493			  : lt.dash_width);
494
495  double vert_length = dim.y + (M_PI/2.0 - 2.0)*rad;
496  int n_vert_dots = int(vert_length/lt.dash_width + .5);
497  double vert_gap_width = (n_vert_dots != 0
498			   ? vert_length/n_vert_dots
499			   : lt.dash_width);
500  double epsilon = lt.dash_width/(rad*100.0);
501
502  double offset = 0.0;
503  dot_arc(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad,
504	   -M_PI/4.0, 0, slt, vert_gap_width, &offset);
505  dot_line(cent + position(dim.x/2.0, -dim.y/2.0 + rad),
506	    cent + position(dim.x/2.0, dim.y/2.0 - rad),
507	    slt, vert_gap_width, &offset);
508  dot_arc(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad,
509	   0, M_PI/4.0 - epsilon, slt, vert_gap_width, &offset);
510
511  offset = 0.0;
512  dot_arc(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad,
513	   M_PI/4.0, M_PI/2, slt, hor_gap_width, &offset);
514  dot_line(cent + position(dim.x/2.0 - rad, dim.y/2.0),
515	    cent + position(-dim.x/2.0 + rad, dim.y/2.0),
516	    slt, hor_gap_width, &offset);
517  dot_arc(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad,
518	   M_PI/2, 3*M_PI/4.0 - epsilon, slt, hor_gap_width, &offset);
519
520  offset = 0.0;
521  dot_arc(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad,
522	   3.0*M_PI/4.0, M_PI, slt, vert_gap_width, &offset);
523  dot_line(cent + position(-dim.x/2.0, dim.y/2.0 - rad),
524	    cent + position(-dim.x/2.0, -dim.y/2.0 + rad),
525	    slt, vert_gap_width, &offset);
526  dot_arc(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad,
527	   M_PI, 5.0*M_PI/4.0 - epsilon, slt, vert_gap_width, &offset);
528
529  offset = 0.0;
530  dot_arc(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad,
531	   5*M_PI/4.0, 3*M_PI/2.0, slt, hor_gap_width, &offset);
532  dot_line(cent + position(-dim.x/2.0 + rad, -dim.y/2.0),
533	    cent + position(dim.x/2.0 - rad, -dim.y/2.0),
534	    slt, hor_gap_width, &offset);
535  dot_arc(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad,
536	   3*M_PI/2, 7*M_PI/4 - epsilon, slt, hor_gap_width, &offset);
537}
538
539// Used by dotted_rounded_box.
540
541void common_output::dot_arc(const position &cent, double rad,
542			    double start_angle, double end_angle,
543			    const line_type &lt, double gap_width,
544			    double *offsetp)
545{
546  double length = (end_angle - start_angle)*rad;
547  double pos = 0.0;
548  for (;;) {
549    if (*offsetp == 0.0) {
550      double ang = start_angle + pos/rad;
551      dot(cent + position(cos(ang), sin(ang))*rad, lt);
552    }
553    double rem = gap_width - *offsetp;
554    if (pos + rem > length) {
555      *offsetp += length - pos;
556      break;
557    }
558    else {
559      pos += rem;
560      *offsetp = 0.0;
561    }
562  }
563}
564
565// Used by dotted_rounded_box.
566
567void common_output::dot_line(const position &start, const position &end,
568			     const line_type &lt, double gap_width,
569			     double *offsetp)
570{
571  distance dist = end - start;
572  double length = hypot(dist);
573  if (length == 0.0)
574    return;
575  double pos = 0.0;
576  for (;;) {
577    if (*offsetp == 0.0)
578      dot(start + dist*(pos/length), lt);
579    double rem = gap_width - *offsetp;
580    if (pos + rem > length) {
581      *offsetp += length - pos;
582      break;
583    }
584    else {
585      pos += rem;
586      *offsetp = 0.0;
587    }
588  }
589}
590
591void common_output::solid_rounded_box(const position &cent,
592				      const distance &dim, double rad,
593				      const line_type &lt)
594{
595  position tem = cent - dim/2.0;
596  arc(tem + position(0.0, rad),
597      tem + position(rad, rad),
598      tem + position(rad, 0.0),
599      lt);
600  tem = cent + position(-dim.x/2.0, dim.y/2.0);
601  arc(tem + position(rad, 0.0),
602      tem + position(rad, -rad),
603      tem + position(0.0, -rad),
604      lt);
605  tem = cent + dim/2.0;
606  arc(tem + position(0.0, -rad),
607      tem + position(-rad, -rad),
608      tem + position(-rad, 0.0),
609      lt);
610  tem = cent + position(dim.x/2.0, -dim.y/2.0);
611  arc(tem + position(-rad, 0.0),
612      tem + position(-rad, rad),
613      tem + position(0.0, rad),
614      lt);
615  position end;
616  end = cent + position(-dim.x/2.0, dim.y/2.0 - rad);
617  line(cent - dim/2.0 + position(0.0, rad), &end, 1, lt);
618  end = cent + position(dim.x/2.0 - rad, dim.y/2.0);
619  line(cent + position(-dim.x/2.0 + rad, dim.y/2.0), &end, 1, lt);
620  end = cent + position(dim.x/2.0, -dim.y/2.0 + rad);
621  line(cent + position(dim.x/2.0, dim.y/2.0 - rad), &end, 1, lt);
622  end = cent + position(-dim.x/2.0 + rad, -dim.y/2.0);
623  line(cent + position(dim.x/2.0 - rad, -dim.y/2.0), &end, 1, lt);
624}
625
626void common_output::filled_rounded_box(const position &cent,
627				       const distance &dim, double rad,
628				       double fill)
629{
630  line_type ilt;
631  ilt.type = line_type::invisible;
632  circle(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad, ilt, fill);
633  circle(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad, ilt, fill);
634  circle(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad, ilt, fill);
635  circle(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad, ilt, fill);
636  position vec[4];
637  vec[0] = cent + position(dim.x/2.0, dim.y/2.0 - rad);
638  vec[1] = cent + position(-dim.x/2.0, dim.y/2.0 - rad);
639  vec[2] = cent + position(-dim.x/2.0, -dim.y/2.0 + rad);
640  vec[3] = cent + position(dim.x/2.0, -dim.y/2.0 + rad);
641  polygon(vec, 4, ilt, fill);
642  vec[0] = cent + position(dim.x/2.0 - rad, dim.y/2.0);
643  vec[1] = cent + position(-dim.x/2.0 + rad, dim.y/2.0);
644  vec[2] = cent + position(-dim.x/2.0 + rad, -dim.y/2.0);
645  vec[3] = cent + position(dim.x/2.0 - rad, -dim.y/2.0);
646  polygon(vec, 4, ilt, fill);
647}
648