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