groff before CVS: release 1.06
[s-roff.git] / pic / common.cc
blob0f61c750eb32c905e3272fdc011de921512e2504
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.com)
5 This file is part of groff.
7 groff is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
12 groff is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
17 You should have received a copy of the GNU General Public License along
18 with groff; see the file COPYING. If not, write to the Free Software
19 Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
21 #include "pic.h"
22 #include "common.h"
24 // output a dashed circle as a series of arcs
26 void common_output::dashed_circle(const position &cent, double rad,
27 const line_type &lt)
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;
40 else if (dash_angle < M_PI) {
41 gap_angle = M_PI - dash_angle;
42 ndashes = 2;
44 else {
45 circle(cent, rad, slt, -1.0);
46 return;
49 else {
50 ndashes = 4*int(ceil(M_PI/(4.0*dash_angle)));
51 gap_angle = (M_PI*2.0)/ndashes - dash_angle;
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);
59 // output a dotted circle as a series of dots
61 void common_output::dotted_circle(const position &cent, double rad,
62 const line_type &lt)
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;
72 else {
73 ndots = 4*int(M_PI/(2.0*gap_angle));
74 gap_angle = (M_PI*2.0)/ndots;
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);
81 // return non-zero iff we can compute a center
83 int compute_arc_center(const position &start, const position &cent,
84 const position &end, position *result)
86 // This finds the point along the vector from start to cent that
87 // is equidistant between start and end.
88 distance c = cent - start;
89 distance e = end - start;
90 double n = c*e;
91 if (n == 0.0)
92 return 0;
93 *result = start + c*((e*e)/(2.0*n));
94 return 1;
97 // output a dashed arc as a series of arcs
99 void common_output::dashed_arc(const position &start, const position &cent,
100 const position &end, const line_type &lt)
102 assert(lt.type == line_type::dashed);
103 position c;
104 if (!compute_arc_center(start, cent, end, &c)) {
105 line(start, &end, 1, lt);
106 return;
108 distance start_offset = start - c;
109 distance end_offset = end - c;
110 double start_angle = atan2(start_offset.y, start_offset.x);
111 double end_angle = atan2(end_offset.y, end_offset.x);
112 double rad = hypot(c - start);
113 double dash_angle = lt.dash_width/rad;
114 double total_angle = end_angle - start_angle;
115 while (total_angle < 0)
116 total_angle += M_PI + M_PI;
117 if (total_angle <= dash_angle*2.0) {
118 solid_arc(cent, rad, start_angle, end_angle, lt);
119 return;
121 int ndashes = int((total_angle - dash_angle)/(dash_angle*2.0) + .5);
122 double dash_and_gap_angle = (total_angle - dash_angle)/ndashes;
123 for (int i = 0; i <= ndashes; i++)
124 solid_arc(cent, rad, start_angle + i*dash_and_gap_angle,
125 start_angle + i*dash_and_gap_angle + dash_angle, lt);
128 // output a dotted arc as a series of dots
130 void common_output::dotted_arc(const position &start, const position &cent,
131 const position &end, const line_type &lt)
133 assert(lt.type == line_type::dotted);
134 position c;
135 if (!compute_arc_center(start, cent, end, &c)) {
136 line(start, &end, 1, lt);
137 return;
139 distance start_offset = start - c;
140 distance end_offset = end - c;
141 double start_angle = atan2(start_offset.y, start_offset.x);
142 double total_angle = atan2(end_offset.y, end_offset.x) - start_angle;
143 while (total_angle < 0)
144 total_angle += M_PI + M_PI;
145 double rad = hypot(c - start);
146 int ndots = int(total_angle/(lt.dash_width/rad) + .5);
147 if (ndots == 0)
148 dot(start, lt);
149 else {
150 for (int i = 0; i <= ndots; i++) {
151 double a = start_angle + (total_angle*i)/ndots;
152 dot(cent + position(cos(a), sin(a))*rad, lt);
157 void common_output::solid_arc(const position &cent, double rad,
158 double start_angle, double end_angle,
159 const line_type &lt)
161 line_type slt = lt;
162 slt.type = line_type::solid;
163 arc(cent + position(cos(start_angle), sin(start_angle))*rad,
164 cent,
165 cent + position(cos(end_angle), sin(end_angle))*rad,
166 slt);
170 void common_output::rounded_box(const position &cent, const distance &dim,
171 double rad, const line_type &lt, double fill)
173 if (fill >= 0.0)
174 filled_rounded_box(cent, dim, rad, fill);
175 switch (lt.type) {
176 case line_type::invisible:
177 break;
178 case line_type::dashed:
179 dashed_rounded_box(cent, dim, rad, lt);
180 break;
181 case line_type::dotted:
182 dotted_rounded_box(cent, dim, rad, lt);
183 break;
184 case line_type::solid:
185 solid_rounded_box(cent, dim, rad, lt);
186 break;
187 default:
188 assert(0);
193 void common_output::dashed_rounded_box(const position &cent,
194 const distance &dim, double rad,
195 const line_type &lt)
197 line_type slt = lt;
198 slt.type = line_type::solid;
200 double hor_length = dim.x + (M_PI/2.0 - 2.0)*rad;
201 int n_hor_dashes = int(hor_length/(lt.dash_width*2.0) + .5);
202 double hor_gap_width = (n_hor_dashes != 0
203 ? hor_length/n_hor_dashes - lt.dash_width
204 : 0.0);
206 double vert_length = dim.y + (M_PI/2.0 - 2.0)*rad;
207 int n_vert_dashes = int(vert_length/(lt.dash_width*2.0) + .5);
208 double vert_gap_width = (n_vert_dashes != 0
209 ? vert_length/n_vert_dashes - lt.dash_width
210 : 0.0);
211 // Note that each corner arc has to be split into two for dashing,
212 // because one part is dashed using vert_gap_width, and the other
213 // using hor_gap_width.
214 double offset = lt.dash_width/2.0;
215 dash_arc(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad,
216 -M_PI/4.0, 0, slt, lt.dash_width, vert_gap_width, &offset);
217 dash_line(cent + position(dim.x/2.0, -dim.y/2.0 + rad),
218 cent + position(dim.x/2.0, dim.y/2.0 - rad),
219 slt, lt.dash_width, vert_gap_width, &offset);
220 dash_arc(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad,
221 0, M_PI/4.0, slt, lt.dash_width, vert_gap_width, &offset);
223 offset = lt.dash_width/2.0;
224 dash_arc(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad,
225 M_PI/4.0, M_PI/2, slt, lt.dash_width, hor_gap_width, &offset);
226 dash_line(cent + position(dim.x/2.0 - rad, dim.y/2.0),
227 cent + position(-dim.x/2.0 + rad, dim.y/2.0),
228 slt, lt.dash_width, hor_gap_width, &offset);
229 dash_arc(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad,
230 M_PI/2, 3*M_PI/4.0, slt, lt.dash_width, hor_gap_width, &offset);
232 offset = lt.dash_width/2.0;
233 dash_arc(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad,
234 3.0*M_PI/4.0, M_PI, slt, lt.dash_width, vert_gap_width, &offset);
235 dash_line(cent + position(-dim.x/2.0, dim.y/2.0 - rad),
236 cent + position(-dim.x/2.0, -dim.y/2.0 + rad),
237 slt, lt.dash_width, vert_gap_width, &offset);
238 dash_arc(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad,
239 M_PI, 5.0*M_PI/4.0, slt, lt.dash_width, vert_gap_width, &offset);
241 offset = lt.dash_width/2.0;
242 dash_arc(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad,
243 5*M_PI/4.0, 3*M_PI/2.0, slt, lt.dash_width, hor_gap_width, &offset);
244 dash_line(cent + position(-dim.x/2.0 + rad, -dim.y/2.0),
245 cent + position(dim.x/2.0 - rad, -dim.y/2.0),
246 slt, lt.dash_width, hor_gap_width, &offset);
247 dash_arc(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad,
248 3*M_PI/2, 7*M_PI/4, slt, lt.dash_width, hor_gap_width, &offset);
251 // Used by dashed_rounded_box.
253 void common_output::dash_arc(const position &cent, double rad,
254 double start_angle, double end_angle,
255 const line_type &lt,
256 double dash_width, double gap_width,
257 double *offsetp)
259 double length = (end_angle - start_angle)*rad;
260 double pos = 0.0;
261 for (;;) {
262 if (*offsetp >= dash_width) {
263 double rem = dash_width + gap_width - *offsetp;
264 if (pos + rem > length) {
265 *offsetp += length - pos;
266 break;
268 else {
269 pos += rem;
270 *offsetp = 0.0;
273 else {
274 double rem = dash_width - *offsetp;
275 if (pos + rem > length) {
276 solid_arc(cent, rad, start_angle + pos/rad, end_angle, lt);
277 *offsetp += length - pos;
278 break;
280 else {
281 solid_arc(cent, rad, start_angle + pos/rad,
282 start_angle + (pos + rem)/rad, lt);
283 pos += rem;
284 *offsetp = dash_width;
290 // Used by dashed_rounded_box.
292 void common_output::dash_line(const position &start, const position &end,
293 const line_type &lt,
294 double dash_width, double gap_width,
295 double *offsetp)
297 distance dist = end - start;
298 double length = hypot(dist);
299 if (length == 0.0)
300 return;
301 double pos = 0.0;
302 for (;;) {
303 if (*offsetp >= dash_width) {
304 double rem = dash_width + gap_width - *offsetp;
305 if (pos + rem > length) {
306 *offsetp += length - pos;
307 break;
309 else {
310 pos += rem;
311 *offsetp = 0.0;
314 else {
315 double rem = dash_width - *offsetp;
316 if (pos + rem > length) {
317 line(start + dist*(pos/length), &end, 1, lt);
318 *offsetp += length - pos;
319 break;
321 else {
322 position p(start + dist*((pos + rem)/length));
323 line(start + dist*(pos/length), &p, 1, lt);
324 pos += rem;
325 *offsetp = dash_width;
331 void common_output::dotted_rounded_box(const position &cent,
332 const distance &dim, double rad,
333 const line_type &lt)
335 line_type slt = lt;
336 slt.type = line_type::solid;
338 double hor_length = dim.x + (M_PI/2.0 - 2.0)*rad;
339 int n_hor_dots = int(hor_length/lt.dash_width + .5);
340 double hor_gap_width = (n_hor_dots != 0
341 ? hor_length/n_hor_dots
342 : lt.dash_width);
344 double vert_length = dim.y + (M_PI/2.0 - 2.0)*rad;
345 int n_vert_dots = int(vert_length/lt.dash_width + .5);
346 double vert_gap_width = (n_vert_dots != 0
347 ? vert_length/n_vert_dots
348 : lt.dash_width);
349 double epsilon = lt.dash_width/(rad*100.0);
351 double offset = 0.0;
352 dot_arc(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad,
353 -M_PI/4.0, 0, slt, vert_gap_width, &offset);
354 dot_line(cent + position(dim.x/2.0, -dim.y/2.0 + rad),
355 cent + position(dim.x/2.0, dim.y/2.0 - rad),
356 slt, vert_gap_width, &offset);
357 dot_arc(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad,
358 0, M_PI/4.0 - epsilon, slt, vert_gap_width, &offset);
360 offset = 0.0;
361 dot_arc(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad,
362 M_PI/4.0, M_PI/2, slt, hor_gap_width, &offset);
363 dot_line(cent + position(dim.x/2.0 - rad, dim.y/2.0),
364 cent + position(-dim.x/2.0 + rad, dim.y/2.0),
365 slt, hor_gap_width, &offset);
366 dot_arc(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad,
367 M_PI/2, 3*M_PI/4.0 - epsilon, slt, hor_gap_width, &offset);
369 offset = 0.0;
370 dot_arc(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad,
371 3.0*M_PI/4.0, M_PI, slt, vert_gap_width, &offset);
372 dot_line(cent + position(-dim.x/2.0, dim.y/2.0 - rad),
373 cent + position(-dim.x/2.0, -dim.y/2.0 + rad),
374 slt, vert_gap_width, &offset);
375 dot_arc(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad,
376 M_PI, 5.0*M_PI/4.0 - epsilon, slt, vert_gap_width, &offset);
378 offset = 0.0;
379 dot_arc(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad,
380 5*M_PI/4.0, 3*M_PI/2.0, slt, hor_gap_width, &offset);
381 dot_line(cent + position(-dim.x/2.0 + rad, -dim.y/2.0),
382 cent + position(dim.x/2.0 - rad, -dim.y/2.0),
383 slt, hor_gap_width, &offset);
384 dot_arc(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad,
385 3*M_PI/2, 7*M_PI/4 - epsilon, slt, hor_gap_width, &offset);
388 // Used by dotted_rounded_box.
390 void common_output::dot_arc(const position &cent, double rad,
391 double start_angle, double end_angle,
392 const line_type &lt, double gap_width,
393 double *offsetp)
395 double length = (end_angle - start_angle)*rad;
396 double pos = 0.0;
397 for (;;) {
398 if (*offsetp == 0.0) {
399 double ang = start_angle + pos/rad;
400 dot(cent + position(cos(ang), sin(ang))*rad, lt);
402 double rem = gap_width - *offsetp;
403 if (pos + rem > length) {
404 *offsetp += length - pos;
405 break;
407 else {
408 pos += rem;
409 *offsetp = 0.0;
414 // Used by dotted_rounded_box.
416 void common_output::dot_line(const position &start, const position &end,
417 const line_type &lt, double gap_width,
418 double *offsetp)
420 distance dist = end - start;
421 double length = hypot(dist);
422 double pos = 0.0;
423 for (;;) {
424 if (*offsetp == 0.0)
425 dot(start + dist*(pos/length), lt);
426 double rem = gap_width - *offsetp;
427 if (pos + rem > length) {
428 *offsetp += length - pos;
429 break;
431 else {
432 pos += rem;
433 *offsetp = 0.0;
439 void common_output::solid_rounded_box(const position &cent,
440 const distance &dim, double rad,
441 const line_type &lt)
443 position tem = cent - dim/2.0;
444 arc(tem + position(0.0, rad),
445 tem + position(rad, rad),
446 tem + position(rad, 0.0),
447 lt);
448 tem = cent + position(-dim.x/2.0, dim.y/2.0);
449 arc(tem + position(rad, 0.0),
450 tem + position(rad, -rad),
451 tem + position(0.0, -rad),
452 lt);
453 tem = cent + dim/2.0;
454 arc(tem + position(0.0, -rad),
455 tem + position(-rad, -rad),
456 tem + position(-rad, 0.0),
457 lt);
458 tem = cent + position(dim.x/2.0, -dim.y/2.0);
459 arc(tem + position(-rad, 0.0),
460 tem + position(-rad, rad),
461 tem + position(0.0, rad),
462 lt);
463 position end;
464 end = cent + position(-dim.x/2.0, dim.y/2.0 - rad);
465 line(cent - dim/2.0 + position(0.0, rad), &end, 1, lt);
466 end = cent + position(dim.x/2.0 - rad, dim.y/2.0);
467 line(cent + position(-dim.x/2.0 + rad, dim.y/2.0), &end, 1, lt);
468 end = cent + position(dim.x/2.0, -dim.y/2.0 + rad);
469 line(cent + position(dim.x/2.0, dim.y/2.0 - rad), &end, 1, lt);
470 end = cent + position(-dim.x/2.0 + rad, -dim.y/2.0);
471 line(cent + position(dim.x/2.0 - rad, -dim.y/2.0), &end, 1, lt);
474 void common_output::filled_rounded_box(const position &cent,
475 const distance &dim, double rad,
476 double fill)
478 line_type ilt;
479 ilt.type = line_type::invisible;
480 circle(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad, ilt, fill);
481 circle(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad, ilt, fill);
482 circle(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad, ilt, fill);
483 circle(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad, ilt, fill);
484 position vec[4];
485 vec[0] = cent + position(dim.x/2.0, dim.y/2.0 - rad);
486 vec[1] = cent + position(-dim.x/2.0, dim.y/2.0 - rad);
487 vec[2] = cent + position(-dim.x/2.0, -dim.y/2.0 + rad);
488 vec[3] = cent + position(dim.x/2.0, -dim.y/2.0 + rad);
489 polygon(vec, 4, ilt, fill);
490 vec[0] = cent + position(dim.x/2.0 - rad, dim.y/2.0);
491 vec[1] = cent + position(-dim.x/2.0 + rad, dim.y/2.0);
492 vec[2] = cent + position(-dim.x/2.0 + rad, -dim.y/2.0);
493 vec[3] = cent + position(dim.x/2.0 - rad, -dim.y/2.0);
494 polygon(vec, 4, ilt, fill);