1114402Sru// -*- C++ -*-
2151497Sru/* Copyright (C) 1989, 1990, 1991, 1992, 2003 Free Software Foundation, Inc.
3114402Sru     Written by James Clark (jjc@jclark.com)
4114402Sru
5114402SruThis file is part of groff.
6114402Sru
7114402Srugroff is free software; you can redistribute it and/or modify it under
8114402Sruthe terms of the GNU General Public License as published by the Free
9114402SruSoftware Foundation; either version 2, or (at your option) any later
10114402Sruversion.
11114402Sru
12114402Srugroff is distributed in the hope that it will be useful, but WITHOUT ANY
13114402SruWARRANTY; without even the implied warranty of MERCHANTABILITY or
14114402SruFITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15114402Srufor more details.
16114402Sru
17114402SruYou should have received a copy of the GNU General Public License along
18114402Sruwith groff; see the file COPYING.  If not, write to the Free Software
19151497SruFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
20114402Sru
21114402Sru#include "pic.h"
22114402Sru#include "common.h"
23114402Sru
24114402Sru// output a dashed circle as a series of arcs
25114402Sru
26114402Sruvoid common_output::dashed_circle(const position &cent, double rad,
27114402Sru				  const line_type &lt)
28114402Sru{
29114402Sru  assert(lt.type == line_type::dashed);
30114402Sru  line_type slt = lt;
31114402Sru  slt.type = line_type::solid;
32114402Sru  double dash_angle = lt.dash_width/rad;
33114402Sru  int ndashes;
34114402Sru  double gap_angle;
35114402Sru  if (dash_angle >= M_PI/4.0) {
36114402Sru    if (dash_angle < M_PI/2.0) {
37114402Sru      gap_angle = M_PI/2.0 - dash_angle;
38114402Sru      ndashes = 4;
39114402Sru    }
40114402Sru    else if (dash_angle < M_PI) {
41114402Sru      gap_angle = M_PI - dash_angle;
42114402Sru      ndashes = 2;
43114402Sru    }
44114402Sru    else {
45114402Sru      circle(cent, rad, slt, -1.0);
46114402Sru      return;
47114402Sru    }
48114402Sru  }
49114402Sru  else {
50114402Sru    ndashes = 4*int(ceil(M_PI/(4.0*dash_angle)));
51114402Sru    gap_angle = (M_PI*2.0)/ndashes - dash_angle;
52114402Sru  }
53114402Sru  for (int i = 0; i < ndashes; i++) {
54114402Sru    double start_angle = i*(dash_angle+gap_angle) - dash_angle/2.0;
55114402Sru    solid_arc(cent, rad, start_angle, start_angle + dash_angle, lt);
56114402Sru  }
57114402Sru}
58114402Sru
59114402Sru// output a dotted circle as a series of dots
60114402Sru
61114402Sruvoid common_output::dotted_circle(const position &cent, double rad,
62114402Sru				  const line_type &lt)
63114402Sru{
64114402Sru  assert(lt.type == line_type::dotted);
65114402Sru  double gap_angle = lt.dash_width/rad;
66114402Sru  int ndots;
67114402Sru  if (gap_angle >= M_PI/2.0) {
68114402Sru    // always have at least 2 dots
69114402Sru    gap_angle = M_PI;
70114402Sru    ndots = 2;
71114402Sru  }
72114402Sru  else {
73114402Sru    ndots = 4*int(M_PI/(2.0*gap_angle));
74114402Sru    gap_angle = (M_PI*2.0)/ndots;
75114402Sru  }
76114402Sru  double ang = 0.0;
77114402Sru  for (int i = 0; i < ndots; i++, ang += gap_angle)
78114402Sru    dot(cent + position(cos(ang), sin(ang))*rad, lt);
79114402Sru}
80114402Sru
81151497Sru// recursive function for dash drawing, used by dashed_ellipse
82151497Sru
83151497Sruvoid common_output::ellipse_arc(const position &cent,
84151497Sru				const position &z0, const position &z1,
85151497Sru				const distance &dim, const line_type &lt)
86151497Sru{
87151497Sru  assert(lt.type == line_type::solid);
88151497Sru  assert(dim.x != 0 && dim.y != 0);
89151497Sru  double eps = 0.0001;
90151497Sru  position zml = (z0 + z1) / 2;
91151497Sru  // apply affine transformation (from ellipse to circle) to compute angle
92151497Sru  // of new position, then invert transformation to get exact position
93151497Sru  double psi = atan2(zml.y / dim.y, zml.x / dim.x);
94151497Sru  position zm = position(dim.x * cos(psi), dim.y * sin(psi));
95151497Sru  // to approximate the ellipse arc with one or more circle arcs, we
96151497Sru  // first compute the radius of curvature in zm
97151497Sru  double a_2 = dim.x * dim.x;
98151497Sru  double a_4 = a_2 * a_2;
99151497Sru  double b_2 = dim.y * dim.y;
100151497Sru  double b_4 = b_2 * b_2;
101151497Sru  double e_2 = a_2 - b_2;
102151497Sru  double temp = a_4 * zm.y * zm.y + b_4 * zm.x * zm.x;
103151497Sru  double rho = sqrt(temp / a_4 / b_4 * temp / a_4 / b_4 * temp);
104151497Sru  // compute center of curvature circle
105151497Sru  position M = position(e_2 * zm.x / a_2 * zm.x / a_2 * zm.x,
106151497Sru			-e_2 * zm.y / b_2 * zm.y / b_2 * zm.y);
107151497Sru  // compute distance between circle and ellipse arc at start and end
108151497Sru  double phi0 = atan2(z0.y - M.y, z0.x - M.x);
109151497Sru  double phi1 = atan2(z1.y - M.y, z1.x - M.x);
110151497Sru  position M0 = position(rho * cos(phi0), rho * sin(phi0)) + M;
111151497Sru  position M1 = position(rho * cos(phi1), rho * sin(phi1)) + M;
112151497Sru  double dist0 = hypot(z0 - M0) / sqrt(z0 * z0);
113151497Sru  double dist1 = hypot(z1 - M1) / sqrt(z1 * z1);
114151497Sru  if (dist0 < eps && dist1 < eps)
115151497Sru    solid_arc(M + cent, rho, phi0, phi1, lt);
116151497Sru  else {
117151497Sru    ellipse_arc(cent, z0, zm, dim, lt);
118151497Sru    ellipse_arc(cent, zm, z1, dim, lt);
119151497Sru  }
120151497Sru}
121151497Sru
122151497Sru// output a dashed ellipse as a series of arcs
123151497Sru
124151497Sruvoid common_output::dashed_ellipse(const position &cent, const distance &dim,
125151497Sru				   const line_type &lt)
126151497Sru{
127151497Sru  assert(lt.type == line_type::dashed);
128151497Sru  double dim_x = dim.x / 2;
129151497Sru  double dim_y = dim.y / 2;
130151497Sru  line_type slt = lt;
131151497Sru  slt.type = line_type::solid;
132151497Sru  double dw = lt.dash_width;
133151497Sru  // we use an approximation to compute the ellipse length (found in:
134151497Sru  // Bronstein, Semendjajew, Taschenbuch der Mathematik)
135151497Sru  double lambda = (dim.x - dim.y) / (dim.x + dim.y);
136151497Sru  double le = M_PI / 2 * (dim.x + dim.y)
137151497Sru	      * ((64 - 3 * lambda * lambda * lambda * lambda )
138151497Sru		 / (64 - 16 * lambda * lambda));
139151497Sru  // for symmetry we make nmax a multiple of 8
140151497Sru  int nmax = 8 * int(le / dw / 8 + 0.5);
141151497Sru  if (nmax < 8) {
142151497Sru    nmax = 8;
143151497Sru    dw = le / 8;
144151497Sru  }
145151497Sru  int ndash = nmax / 2;
146151497Sru  double gapwidth = (le - dw * ndash) / ndash;
147151497Sru  double l = 0;
148151497Sru  position z = position(dim_x, 0);
149151497Sru  position zdot = z;
150151497Sru  int j = 0;
151151497Sru  int jmax = int(10 / lt.dash_width);
152151497Sru  for (int i = 0; i <= nmax; i++) {
153151497Sru    position zold = z;
154151497Sru    position zpre = zdot;
155151497Sru    double ld = (int(i / 2) + 0.5) * dw + int((i + 1) / 2) * gapwidth;
156151497Sru    double lold = 0;
157151497Sru    double dl = 1;
158151497Sru    // find next position for fixed arc length
159151497Sru    while (l < ld) {
160151497Sru      j++;
161151497Sru      lold = l;
162151497Sru      zold = z;
163151497Sru      double phi = j * 2 * M_PI / jmax;
164151497Sru      z = position(dim_x * cos(phi), dim_y * sin(phi));
165151497Sru      dl = hypot(z - zold);
166151497Sru      l += dl;
167151497Sru    }
168151497Sru    // interpolate linearly between the last two points,
169151497Sru    // using the length difference as the scaling factor
170151497Sru    double delta = (ld - lold) / dl;
171151497Sru    zdot = zold + (z - zold) * delta;
172151497Sru    // compute angle of new position on the affine circle
173151497Sru    // and use it to get the exact value on the ellipse
174151497Sru    double psi = atan2(zdot.y / dim_y, zdot.x / dim_x);
175151497Sru    zdot = position(dim_x * cos(psi), dim_y * sin(psi));
176151497Sru    if ((i % 2 == 0) && (i > 1))
177151497Sru      ellipse_arc(cent, zpre, zdot, dim / 2, slt);
178151497Sru  }
179151497Sru}
180151497Sru
181151497Sru// output a dotted ellipse as a series of dots
182151497Sru
183151497Sruvoid common_output::dotted_ellipse(const position &cent, const distance &dim,
184151497Sru				   const line_type &lt)
185151497Sru{
186151497Sru  assert(lt.type == line_type::dotted);
187151497Sru  double dim_x = dim.x / 2;
188151497Sru  double dim_y = dim.y / 2;
189151497Sru  line_type slt = lt;
190151497Sru  slt.type = line_type::solid;
191151497Sru  // we use an approximation to compute the ellipse length (found in:
192151497Sru  // Bronstein, Semendjajew, Taschenbuch der Mathematik)
193151497Sru  double lambda = (dim.x - dim.y) / (dim.x + dim.y);
194151497Sru  double le = M_PI / 2 * (dim.x + dim.y)
195151497Sru	      * ((64 - 3 * lambda * lambda * lambda * lambda )
196151497Sru		 / (64 - 16 * lambda * lambda));
197151497Sru  // for symmetry we make nmax a multiple of 4
198151497Sru  int ndots = 4 * int(le / lt.dash_width / 4 + 0.5);
199151497Sru  if (ndots < 4)
200151497Sru    ndots = 4;
201151497Sru  double l = 0;
202151497Sru  position z = position(dim_x, 0);
203151497Sru  int j = 0;
204151497Sru  int jmax = int(10 / lt.dash_width);
205151497Sru  for (int i = 1; i <= ndots; i++) {
206151497Sru    position zold = z;
207151497Sru    double lold = l;
208151497Sru    double ld = i * le / ndots;
209151497Sru    double dl = 1;
210151497Sru    // find next position for fixed arc length
211151497Sru    while (l < ld) {
212151497Sru      j++;
213151497Sru      lold = l;
214151497Sru      zold = z;
215151497Sru      double phi = j * 2 * M_PI / jmax;
216151497Sru      z = position(dim_x * cos(phi), dim_y * sin(phi));
217151497Sru      dl = hypot(z - zold);
218151497Sru      l += dl;
219151497Sru    }
220151497Sru    // interpolate linearly between the last two points,
221151497Sru    // using the length difference as the scaling factor
222151497Sru    double delta = (ld - lold) / dl;
223151497Sru    position zdot = zold + (z - zold) * delta;
224151497Sru    // compute angle of new position on the affine circle
225151497Sru    // and use it to get the exact value on the ellipse
226151497Sru    double psi = atan2(zdot.y / dim_y, zdot.x / dim_x);
227151497Sru    zdot = position(dim_x * cos(psi), dim_y * sin(psi));
228151497Sru    dot(cent + zdot, slt);
229151497Sru  }
230151497Sru}
231151497Sru
232114402Sru// return non-zero iff we can compute a center
233114402Sru
234114402Sruint compute_arc_center(const position &start, const position &cent,
235114402Sru		       const position &end, position *result)
236114402Sru{
237114402Sru  // This finds the point along the vector from start to cent that
238114402Sru  // is equidistant between start and end.
239114402Sru  distance c = cent - start;
240114402Sru  distance e = end - start;
241114402Sru  double n = c*e;
242114402Sru  if (n == 0.0)
243114402Sru    return 0;
244114402Sru  *result = start + c*((e*e)/(2.0*n));
245114402Sru  return 1;
246114402Sru}
247114402Sru
248114402Sru// output a dashed arc as a series of arcs
249114402Sru
250114402Sruvoid common_output::dashed_arc(const position &start, const position &cent,
251114402Sru			       const position &end, const line_type &lt)
252114402Sru{
253114402Sru  assert(lt.type == line_type::dashed);
254114402Sru  position c;
255114402Sru  if (!compute_arc_center(start, cent, end, &c)) {
256114402Sru    line(start, &end, 1, lt);
257114402Sru    return;
258114402Sru  }
259114402Sru  distance start_offset = start - c;
260114402Sru  distance end_offset = end - c;
261114402Sru  double start_angle = atan2(start_offset.y, start_offset.x);
262114402Sru  double end_angle = atan2(end_offset.y, end_offset.x);
263114402Sru  double rad = hypot(c - start);
264114402Sru  double dash_angle = lt.dash_width/rad;
265114402Sru  double total_angle = end_angle - start_angle;
266114402Sru  while (total_angle < 0)
267114402Sru    total_angle += M_PI + M_PI;
268114402Sru  if (total_angle <= dash_angle*2.0) {
269114402Sru    solid_arc(cent, rad, start_angle, end_angle, lt);
270114402Sru    return;
271114402Sru  }
272114402Sru  int ndashes = int((total_angle - dash_angle)/(dash_angle*2.0) + .5);
273114402Sru  double dash_and_gap_angle = (total_angle - dash_angle)/ndashes;
274114402Sru  for (int i = 0; i <= ndashes; i++)
275114402Sru    solid_arc(cent, rad, start_angle + i*dash_and_gap_angle,
276114402Sru	      start_angle + i*dash_and_gap_angle + dash_angle, lt);
277114402Sru}
278114402Sru
279114402Sru// output a dotted arc as a series of dots
280114402Sru
281114402Sruvoid common_output::dotted_arc(const position &start, const position &cent,
282114402Sru			       const position &end, const line_type &lt)
283114402Sru{
284114402Sru  assert(lt.type == line_type::dotted);
285114402Sru  position c;
286114402Sru  if (!compute_arc_center(start, cent, end, &c)) {
287114402Sru    line(start, &end, 1, lt);
288114402Sru    return;
289114402Sru  }
290114402Sru  distance start_offset = start - c;
291114402Sru  distance end_offset = end - c;
292114402Sru  double start_angle = atan2(start_offset.y, start_offset.x);
293114402Sru  double total_angle = atan2(end_offset.y, end_offset.x) - start_angle;
294114402Sru  while (total_angle < 0)
295114402Sru    total_angle += M_PI + M_PI;
296114402Sru  double rad = hypot(c - start);
297114402Sru  int ndots = int(total_angle/(lt.dash_width/rad) + .5);
298114402Sru  if (ndots == 0)
299114402Sru    dot(start, lt);
300114402Sru  else {
301114402Sru    for (int i = 0; i <= ndots; i++) {
302114402Sru      double a = start_angle + (total_angle*i)/ndots;
303114402Sru      dot(cent + position(cos(a), sin(a))*rad, lt);
304114402Sru    }
305114402Sru  }
306114402Sru}
307114402Sru
308114402Sruvoid common_output::solid_arc(const position &cent, double rad,
309114402Sru			      double start_angle, double end_angle,
310114402Sru			      const line_type &lt)
311114402Sru{
312114402Sru  line_type slt = lt;
313114402Sru  slt.type = line_type::solid;
314114402Sru  arc(cent + position(cos(start_angle), sin(start_angle))*rad,
315114402Sru      cent,
316114402Sru      cent + position(cos(end_angle), sin(end_angle))*rad,
317114402Sru      slt);
318114402Sru}
319114402Sru
320114402Sru
321114402Sruvoid common_output::rounded_box(const position &cent, const distance &dim,
322114402Sru				double rad, const line_type &lt, double fill)
323114402Sru{
324114402Sru  if (fill >= 0.0)
325114402Sru    filled_rounded_box(cent, dim, rad, fill);
326114402Sru  switch (lt.type) {
327114402Sru  case line_type::invisible:
328114402Sru    break;
329114402Sru  case line_type::dashed:
330114402Sru    dashed_rounded_box(cent, dim, rad, lt);
331114402Sru    break;
332114402Sru  case line_type::dotted:
333114402Sru    dotted_rounded_box(cent, dim, rad, lt);
334114402Sru    break;
335114402Sru  case line_type::solid:
336114402Sru    solid_rounded_box(cent, dim, rad, lt);
337114402Sru    break;
338114402Sru  default:
339114402Sru    assert(0);
340114402Sru  }
341114402Sru}
342114402Sru
343114402Sru
344114402Sruvoid common_output::dashed_rounded_box(const position &cent,
345114402Sru				       const distance &dim, double rad,
346114402Sru				       const line_type &lt)
347114402Sru{
348114402Sru  line_type slt = lt;
349114402Sru  slt.type = line_type::solid;
350114402Sru
351114402Sru  double hor_length = dim.x + (M_PI/2.0 - 2.0)*rad;
352114402Sru  int n_hor_dashes = int(hor_length/(lt.dash_width*2.0) + .5);
353114402Sru  double hor_gap_width = (n_hor_dashes != 0
354114402Sru			  ? hor_length/n_hor_dashes - lt.dash_width
355114402Sru			  : 0.0);
356114402Sru
357114402Sru  double vert_length = dim.y + (M_PI/2.0 - 2.0)*rad;
358114402Sru  int n_vert_dashes = int(vert_length/(lt.dash_width*2.0) + .5);
359114402Sru  double vert_gap_width = (n_vert_dashes != 0
360114402Sru			   ? vert_length/n_vert_dashes - lt.dash_width
361114402Sru			   : 0.0);
362114402Sru  // Note that each corner arc has to be split into two for dashing,
363114402Sru  // because one part is dashed using vert_gap_width, and the other
364114402Sru  // using hor_gap_width.
365114402Sru  double offset = lt.dash_width/2.0;
366114402Sru  dash_arc(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad,
367114402Sru	   -M_PI/4.0, 0, slt, lt.dash_width, vert_gap_width, &offset);
368114402Sru  dash_line(cent + position(dim.x/2.0, -dim.y/2.0 + rad),
369114402Sru	    cent + position(dim.x/2.0, dim.y/2.0 - rad),
370114402Sru	    slt, lt.dash_width, vert_gap_width, &offset);
371114402Sru  dash_arc(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad,
372114402Sru	   0, M_PI/4.0, slt, lt.dash_width, vert_gap_width, &offset);
373114402Sru
374114402Sru  offset = lt.dash_width/2.0;
375114402Sru  dash_arc(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad,
376114402Sru	   M_PI/4.0, M_PI/2, slt, lt.dash_width, hor_gap_width, &offset);
377114402Sru  dash_line(cent + position(dim.x/2.0 - rad, dim.y/2.0),
378114402Sru	    cent + position(-dim.x/2.0 + rad, dim.y/2.0),
379114402Sru	    slt, lt.dash_width, hor_gap_width, &offset);
380114402Sru  dash_arc(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad,
381114402Sru	   M_PI/2, 3*M_PI/4.0, slt, lt.dash_width, hor_gap_width, &offset);
382114402Sru
383114402Sru  offset = lt.dash_width/2.0;
384114402Sru  dash_arc(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad,
385114402Sru	   3.0*M_PI/4.0, M_PI, slt, lt.dash_width, vert_gap_width, &offset);
386114402Sru  dash_line(cent + position(-dim.x/2.0, dim.y/2.0 - rad),
387114402Sru	    cent + position(-dim.x/2.0, -dim.y/2.0 + rad),
388114402Sru	    slt, lt.dash_width, vert_gap_width, &offset);
389114402Sru  dash_arc(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad,
390114402Sru	   M_PI, 5.0*M_PI/4.0, slt, lt.dash_width, vert_gap_width, &offset);
391114402Sru
392114402Sru  offset = lt.dash_width/2.0;
393114402Sru  dash_arc(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad,
394114402Sru	   5*M_PI/4.0, 3*M_PI/2.0, slt, lt.dash_width, hor_gap_width, &offset);
395114402Sru  dash_line(cent + position(-dim.x/2.0 + rad, -dim.y/2.0),
396114402Sru	    cent + position(dim.x/2.0 - rad, -dim.y/2.0),
397114402Sru	    slt, lt.dash_width, hor_gap_width, &offset);
398114402Sru  dash_arc(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad,
399114402Sru	   3*M_PI/2, 7*M_PI/4, slt, lt.dash_width, hor_gap_width, &offset);
400114402Sru}
401114402Sru
402114402Sru// Used by dashed_rounded_box.
403114402Sru
404114402Sruvoid common_output::dash_arc(const position &cent, double rad,
405114402Sru			     double start_angle, double end_angle,
406114402Sru			     const line_type &lt,
407114402Sru			     double dash_width, double gap_width,
408114402Sru			     double *offsetp)
409114402Sru{
410114402Sru  double length = (end_angle - start_angle)*rad;
411114402Sru  double pos = 0.0;
412114402Sru  for (;;) {
413114402Sru    if (*offsetp >= dash_width) {
414114402Sru      double rem = dash_width + gap_width - *offsetp;
415114402Sru      if (pos + rem > length) {
416114402Sru	*offsetp += length - pos;
417114402Sru	break;
418114402Sru      }
419114402Sru      else {
420114402Sru	pos += rem;
421114402Sru	*offsetp = 0.0;
422114402Sru      }
423114402Sru    }
424114402Sru    else {
425114402Sru      double rem = dash_width  - *offsetp;
426114402Sru      if (pos + rem > length) {
427114402Sru	solid_arc(cent, rad, start_angle + pos/rad, end_angle, lt);
428114402Sru	*offsetp += length - pos;
429114402Sru	break;
430114402Sru      }
431114402Sru      else {
432114402Sru	solid_arc(cent, rad, start_angle + pos/rad,
433114402Sru		  start_angle + (pos + rem)/rad, lt);
434114402Sru	pos += rem;
435114402Sru	*offsetp = dash_width;
436114402Sru      }
437114402Sru    }
438114402Sru  }
439114402Sru}
440114402Sru
441114402Sru// Used by dashed_rounded_box.
442114402Sru
443114402Sruvoid common_output::dash_line(const position &start, const position &end,
444114402Sru			      const line_type &lt,
445114402Sru			      double dash_width, double gap_width,
446114402Sru			      double *offsetp)
447114402Sru{
448114402Sru  distance dist = end - start;
449114402Sru  double length = hypot(dist);
450114402Sru  if (length == 0.0)
451114402Sru    return;
452114402Sru  double pos = 0.0;
453114402Sru  for (;;) {
454114402Sru    if (*offsetp >= dash_width) {
455114402Sru      double rem = dash_width + gap_width - *offsetp;
456114402Sru      if (pos + rem > length) {
457114402Sru	*offsetp += length - pos;
458114402Sru	break;
459114402Sru      }
460114402Sru      else {
461114402Sru	pos += rem;
462114402Sru	*offsetp = 0.0;
463114402Sru      }
464114402Sru    }
465114402Sru    else {
466114402Sru      double rem = dash_width  - *offsetp;
467114402Sru      if (pos + rem > length) {
468114402Sru	line(start + dist*(pos/length), &end, 1, lt);
469114402Sru	*offsetp += length - pos;
470114402Sru	break;
471114402Sru      }
472114402Sru      else {
473114402Sru	position p(start + dist*((pos + rem)/length));
474114402Sru	line(start + dist*(pos/length), &p, 1, lt);
475114402Sru	pos += rem;
476114402Sru	*offsetp = dash_width;
477114402Sru      }
478114402Sru    }
479114402Sru  }
480114402Sru}
481114402Sru
482114402Sruvoid common_output::dotted_rounded_box(const position &cent,
483114402Sru				       const distance &dim, double rad,
484114402Sru				       const line_type &lt)
485114402Sru{
486114402Sru  line_type slt = lt;
487114402Sru  slt.type = line_type::solid;
488114402Sru
489114402Sru  double hor_length = dim.x + (M_PI/2.0 - 2.0)*rad;
490114402Sru  int n_hor_dots = int(hor_length/lt.dash_width + .5);
491114402Sru  double hor_gap_width = (n_hor_dots != 0
492114402Sru			  ? hor_length/n_hor_dots
493114402Sru			  : lt.dash_width);
494114402Sru
495114402Sru  double vert_length = dim.y + (M_PI/2.0 - 2.0)*rad;
496114402Sru  int n_vert_dots = int(vert_length/lt.dash_width + .5);
497114402Sru  double vert_gap_width = (n_vert_dots != 0
498114402Sru			   ? vert_length/n_vert_dots
499114402Sru			   : lt.dash_width);
500114402Sru  double epsilon = lt.dash_width/(rad*100.0);
501114402Sru
502114402Sru  double offset = 0.0;
503114402Sru  dot_arc(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad,
504114402Sru	   -M_PI/4.0, 0, slt, vert_gap_width, &offset);
505114402Sru  dot_line(cent + position(dim.x/2.0, -dim.y/2.0 + rad),
506114402Sru	    cent + position(dim.x/2.0, dim.y/2.0 - rad),
507114402Sru	    slt, vert_gap_width, &offset);
508114402Sru  dot_arc(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad,
509114402Sru	   0, M_PI/4.0 - epsilon, slt, vert_gap_width, &offset);
510114402Sru
511114402Sru  offset = 0.0;
512114402Sru  dot_arc(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad,
513114402Sru	   M_PI/4.0, M_PI/2, slt, hor_gap_width, &offset);
514114402Sru  dot_line(cent + position(dim.x/2.0 - rad, dim.y/2.0),
515114402Sru	    cent + position(-dim.x/2.0 + rad, dim.y/2.0),
516114402Sru	    slt, hor_gap_width, &offset);
517114402Sru  dot_arc(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad,
518114402Sru	   M_PI/2, 3*M_PI/4.0 - epsilon, slt, hor_gap_width, &offset);
519114402Sru
520114402Sru  offset = 0.0;
521114402Sru  dot_arc(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad,
522114402Sru	   3.0*M_PI/4.0, M_PI, slt, vert_gap_width, &offset);
523114402Sru  dot_line(cent + position(-dim.x/2.0, dim.y/2.0 - rad),
524114402Sru	    cent + position(-dim.x/2.0, -dim.y/2.0 + rad),
525114402Sru	    slt, vert_gap_width, &offset);
526114402Sru  dot_arc(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad,
527114402Sru	   M_PI, 5.0*M_PI/4.0 - epsilon, slt, vert_gap_width, &offset);
528114402Sru
529114402Sru  offset = 0.0;
530114402Sru  dot_arc(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad,
531114402Sru	   5*M_PI/4.0, 3*M_PI/2.0, slt, hor_gap_width, &offset);
532114402Sru  dot_line(cent + position(-dim.x/2.0 + rad, -dim.y/2.0),
533114402Sru	    cent + position(dim.x/2.0 - rad, -dim.y/2.0),
534114402Sru	    slt, hor_gap_width, &offset);
535114402Sru  dot_arc(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad,
536114402Sru	   3*M_PI/2, 7*M_PI/4 - epsilon, slt, hor_gap_width, &offset);
537114402Sru}
538114402Sru
539114402Sru// Used by dotted_rounded_box.
540114402Sru
541114402Sruvoid common_output::dot_arc(const position &cent, double rad,
542114402Sru			    double start_angle, double end_angle,
543114402Sru			    const line_type &lt, double gap_width,
544114402Sru			    double *offsetp)
545114402Sru{
546114402Sru  double length = (end_angle - start_angle)*rad;
547114402Sru  double pos = 0.0;
548114402Sru  for (;;) {
549114402Sru    if (*offsetp == 0.0) {
550114402Sru      double ang = start_angle + pos/rad;
551114402Sru      dot(cent + position(cos(ang), sin(ang))*rad, lt);
552114402Sru    }
553114402Sru    double rem = gap_width - *offsetp;
554114402Sru    if (pos + rem > length) {
555114402Sru      *offsetp += length - pos;
556114402Sru      break;
557114402Sru    }
558114402Sru    else {
559114402Sru      pos += rem;
560114402Sru      *offsetp = 0.0;
561114402Sru    }
562114402Sru  }
563114402Sru}
564114402Sru
565114402Sru// Used by dotted_rounded_box.
566114402Sru
567114402Sruvoid common_output::dot_line(const position &start, const position &end,
568114402Sru			     const line_type &lt, double gap_width,
569114402Sru			     double *offsetp)
570114402Sru{
571114402Sru  distance dist = end - start;
572114402Sru  double length = hypot(dist);
573114402Sru  if (length == 0.0)
574114402Sru    return;
575114402Sru  double pos = 0.0;
576114402Sru  for (;;) {
577114402Sru    if (*offsetp == 0.0)
578114402Sru      dot(start + dist*(pos/length), lt);
579114402Sru    double rem = gap_width - *offsetp;
580114402Sru    if (pos + rem > length) {
581114402Sru      *offsetp += length - pos;
582114402Sru      break;
583114402Sru    }
584114402Sru    else {
585114402Sru      pos += rem;
586114402Sru      *offsetp = 0.0;
587114402Sru    }
588114402Sru  }
589114402Sru}
590114402Sru
591114402Sruvoid common_output::solid_rounded_box(const position &cent,
592114402Sru				      const distance &dim, double rad,
593114402Sru				      const line_type &lt)
594114402Sru{
595114402Sru  position tem = cent - dim/2.0;
596114402Sru  arc(tem + position(0.0, rad),
597114402Sru      tem + position(rad, rad),
598114402Sru      tem + position(rad, 0.0),
599114402Sru      lt);
600114402Sru  tem = cent + position(-dim.x/2.0, dim.y/2.0);
601114402Sru  arc(tem + position(rad, 0.0),
602114402Sru      tem + position(rad, -rad),
603114402Sru      tem + position(0.0, -rad),
604114402Sru      lt);
605114402Sru  tem = cent + dim/2.0;
606114402Sru  arc(tem + position(0.0, -rad),
607114402Sru      tem + position(-rad, -rad),
608114402Sru      tem + position(-rad, 0.0),
609114402Sru      lt);
610114402Sru  tem = cent + position(dim.x/2.0, -dim.y/2.0);
611114402Sru  arc(tem + position(-rad, 0.0),
612114402Sru      tem + position(-rad, rad),
613114402Sru      tem + position(0.0, rad),
614114402Sru      lt);
615114402Sru  position end;
616114402Sru  end = cent + position(-dim.x/2.0, dim.y/2.0 - rad);
617114402Sru  line(cent - dim/2.0 + position(0.0, rad), &end, 1, lt);
618114402Sru  end = cent + position(dim.x/2.0 - rad, dim.y/2.0);
619114402Sru  line(cent + position(-dim.x/2.0 + rad, dim.y/2.0), &end, 1, lt);
620114402Sru  end = cent + position(dim.x/2.0, -dim.y/2.0 + rad);
621114402Sru  line(cent + position(dim.x/2.0, dim.y/2.0 - rad), &end, 1, lt);
622114402Sru  end = cent + position(-dim.x/2.0 + rad, -dim.y/2.0);
623114402Sru  line(cent + position(dim.x/2.0 - rad, -dim.y/2.0), &end, 1, lt);
624114402Sru}
625114402Sru
626114402Sruvoid common_output::filled_rounded_box(const position &cent,
627114402Sru				       const distance &dim, double rad,
628114402Sru				       double fill)
629114402Sru{
630114402Sru  line_type ilt;
631114402Sru  ilt.type = line_type::invisible;
632114402Sru  circle(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad, ilt, fill);
633114402Sru  circle(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad, ilt, fill);
634114402Sru  circle(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad, ilt, fill);
635114402Sru  circle(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad, ilt, fill);
636114402Sru  position vec[4];
637114402Sru  vec[0] = cent + position(dim.x/2.0, dim.y/2.0 - rad);
638114402Sru  vec[1] = cent + position(-dim.x/2.0, dim.y/2.0 - rad);
639114402Sru  vec[2] = cent + position(-dim.x/2.0, -dim.y/2.0 + rad);
640114402Sru  vec[3] = cent + position(dim.x/2.0, -dim.y/2.0 + rad);
641114402Sru  polygon(vec, 4, ilt, fill);
642114402Sru  vec[0] = cent + position(dim.x/2.0 - rad, dim.y/2.0);
643114402Sru  vec[1] = cent + position(-dim.x/2.0 + rad, dim.y/2.0);
644114402Sru  vec[2] = cent + position(-dim.x/2.0 + rad, -dim.y/2.0);
645114402Sru  vec[3] = cent + position(dim.x/2.0 - rad, -dim.y/2.0);
646114402Sru  polygon(vec, 4, ilt, fill);
647114402Sru}
648