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