386BSD 0.1 development
[unix-history] / usr / src / usr.bin / groff / pic / common.cc
CommitLineData
e79d4cdd
WJ
1// -*- C++ -*-
2/* Copyright (C) 1989, 1990 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.uucp)
4
5This file is part of groff.
6
7groff is free software; you can redistribute it and/or modify it under
8the terms of the GNU General Public License as published by the Free
9Software Foundation; either version 1, or (at your option) any later
10version.
11
12groff is distributed in the hope that it will be useful, but WITHOUT ANY
13WARRANTY; without even the implied warranty of MERCHANTABILITY or
14FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15for more details.
16
17You should have received a copy of the GNU General Public License along
18with groff; see the file LICENSE. If not, write to the Free Software
19Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
20
21#include "pic.h"
22#include "common.h"
23
24// output a dashed circle as a series of arcs
25
26void common_output::dashed_circle(const position &cent, double rad,
27 const line_type &lt)
28{
29 assert(lt.type == line_type::dashed);
30 line_type slt = lt;
31 slt.type = line_type::solid;
32 double dash_angle = lt.dash_width/rad;
33 int ndashes;
34 double gap_angle;
35 if (dash_angle >= M_PI/4.0) {
36 if (dash_angle < M_PI/2.0) {
37 gap_angle = M_PI/2.0 - dash_angle;
38 ndashes = 4;
39 }
40 else if (dash_angle < M_PI) {
41 gap_angle = M_PI - dash_angle;
42 ndashes = 2;
43 }
44 else {
45 circle(cent, rad, slt, -1.0);
46 return;
47 }
48 }
49 else {
50 ndashes = 4*int(ceil(M_PI/(4.0*dash_angle)));
51 gap_angle = (M_PI*2.0)/ndashes - dash_angle;
52 }
53 for (int i = 0; i < ndashes; i++) {
54 double start_angle = i*(dash_angle+gap_angle) - dash_angle/2.0;
55 solid_arc(cent, rad, start_angle, start_angle + dash_angle, lt);
56 }
57}
58
59// output a dotted circle as a series of dots
60
61void common_output::dotted_circle(const position &cent, double rad,
62 const line_type &lt)
63{
64 assert(lt.type == line_type::dotted);
65 double gap_angle = lt.dash_width/rad;
66 int ndots;
67 if (gap_angle >= M_PI/2.0) {
68 // always have at least 2 dots
69 gap_angle = M_PI;
70 ndots = 2;
71 }
72 else {
73 ndots = 4*int(M_PI/(2.0*gap_angle));
74 gap_angle = (M_PI*2.0)/ndots;
75 }
76 double ang = 0.0;
77 for (int i = 0; i < ndots; i++, ang += gap_angle)
78 dot(cent + position(cos(ang), sin(ang))*rad, lt);
79}
80
81// return non-zero iff we can compute a center
82
83int compute_arc_center(const position &start, const position &cent,
84 const position &end, position *result)
85{
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;
95}
96
97// output a dashed arc as a series of arcs
98
99void common_output::dashed_arc(const position &start, const position &cent,
100 const position &end, const line_type &lt)
101{
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;
107 }
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 if (total_angle <= dash_angle*2.0) {
116 solid_arc(cent, rad, start_angle, end_angle, lt);
117 return;
118 }
119 int ndashes = int((total_angle - dash_angle)/(dash_angle*2.0) + .5);
120 double dash_and_gap_angle = (total_angle - dash_angle)/ndashes;
121 for (int i = 0; i <= ndashes; i++)
122 solid_arc(cent, rad, start_angle + i*dash_and_gap_angle,
123 start_angle + i*dash_and_gap_angle + dash_angle, lt);
124}
125
126// output a dotted arc as a series of dots
127
128void common_output::dotted_arc(const position &start, const position &cent,
129 const position &end, const line_type &lt)
130{
131 assert(lt.type == line_type::dotted);
132 position c;
133 if (!compute_arc_center(start, cent, end, &c)) {
134 line(start, &end, 1, lt);
135 return;
136 }
137 distance start_offset = start - c;
138 distance end_offset = end - c;
139 double start_angle = atan2(start_offset.y, start_offset.x);
140 double total_angle = atan2(end_offset.y, end_offset.x) - start_angle;
141 double rad = hypot(c - start);
142 int ndots = int(total_angle/(lt.dash_width/rad) + .5);
143 if (ndots == 0)
144 dot(start, lt);
145 else {
146 for (int i = 0; i <= ndots; i++) {
147 double a = start_angle + (total_angle*i)/ndots;
148 dot(cent + position(cos(a), sin(a))*rad, lt);
149 }
150 }
151}
152
153void common_output::solid_arc(const position &cent, double rad,
154 double start_angle, double end_angle,
155 const line_type &lt)
156{
157 line_type slt = lt;
158 slt.type = line_type::solid;
159 arc(cent + position(cos(start_angle), sin(start_angle))*rad,
160 cent,
161 cent + position(cos(end_angle), sin(end_angle))*rad,
162 slt);
163}
164
165
166void common_output::rounded_box(const position &cent, const distance &dim,
167 double rad, const line_type &lt, double fill)
168{
169 if (fill >= 0.0)
170 filled_rounded_box(cent, dim, rad, fill);
171 switch (lt.type) {
172 case line_type::invisible:
173 break;
174 case line_type::dashed:
175 dashed_rounded_box(cent, dim, rad, lt);
176 break;
177 case line_type::dotted:
178 dotted_rounded_box(cent, dim, rad, lt);
179 break;
180 case line_type::solid:
181 solid_rounded_box(cent, dim, rad, lt);
182 break;
183 default:
184 assert(0);
185 }
186}
187
188
189void common_output::dashed_rounded_box(const position &cent,
190 const distance &dim, double rad,
191 const line_type &lt)
192{
193 line_type slt = lt;
194 slt.type = line_type::solid;
195
196 double hor_length = dim.x + (M_PI/2.0 - 2.0)*rad;
197 int n_hor_dashes = int(hor_length/(lt.dash_width*2.0) + .5);
198 double hor_gap_width = (n_hor_dashes != 0
199 ? hor_length/n_hor_dashes - lt.dash_width
200 : 0.0);
201
202 double vert_length = dim.y + (M_PI/2.0 - 2.0)*rad;
203 int n_vert_dashes = int(vert_length/(lt.dash_width*2.0) + .5);
204 double vert_gap_width = (n_vert_dashes != 0
205 ? vert_length/n_vert_dashes - lt.dash_width
206 : 0.0);
207 // Note that each corner arc has to be split into two for dashing,
208 // because one part is dashed using vert_gap_width, and the other
209 // using hor_gap_width.
210 double offset = lt.dash_width/2.0;
211 dash_arc(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad,
212 -M_PI/4.0, 0, slt, lt.dash_width, vert_gap_width, &offset);
213 dash_line(cent + position(dim.x/2.0, -dim.y/2.0 + rad),
214 cent + position(dim.x/2.0, dim.y/2.0 - rad),
215 slt, lt.dash_width, vert_gap_width, &offset);
216 dash_arc(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad,
217 0, M_PI/4.0, slt, lt.dash_width, vert_gap_width, &offset);
218
219 offset = lt.dash_width/2.0;
220 dash_arc(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad,
221 M_PI/4.0, M_PI/2, slt, lt.dash_width, hor_gap_width, &offset);
222 dash_line(cent + position(dim.x/2.0 - rad, dim.y/2.0),
223 cent + position(-dim.x/2.0 + rad, dim.y/2.0),
224 slt, lt.dash_width, hor_gap_width, &offset);
225 dash_arc(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad,
226 M_PI/2, 3*M_PI/4.0, slt, lt.dash_width, hor_gap_width, &offset);
227
228 offset = lt.dash_width/2.0;
229 dash_arc(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad,
230 3.0*M_PI/4.0, M_PI, slt, lt.dash_width, vert_gap_width, &offset);
231 dash_line(cent + position(-dim.x/2.0, dim.y/2.0 - rad),
232 cent + position(-dim.x/2.0, -dim.y/2.0 + rad),
233 slt, lt.dash_width, vert_gap_width, &offset);
234 dash_arc(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad,
235 M_PI, 5.0*M_PI/4.0, slt, lt.dash_width, vert_gap_width, &offset);
236
237 offset = lt.dash_width/2.0;
238 dash_arc(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad,
239 5*M_PI/4.0, 3*M_PI/2.0, slt, lt.dash_width, hor_gap_width, &offset);
240 dash_line(cent + position(-dim.x/2.0 + rad, -dim.y/2.0),
241 cent + position(dim.x/2.0 - rad, -dim.y/2.0),
242 slt, lt.dash_width, hor_gap_width, &offset);
243 dash_arc(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad,
244 3*M_PI/2, 7*M_PI/4, slt, lt.dash_width, hor_gap_width, &offset);
245}
246
247// Used by dashed_rounded_box.
248
249void common_output::dash_arc(const position &cent, double rad,
250 double start_angle, double end_angle,
251 const line_type &lt,
252 double dash_width, double gap_width,
253 double *offsetp)
254{
255 double length = (end_angle - start_angle)*rad;
256 double pos = 0.0;
257 for (;;) {
258 if (*offsetp >= dash_width) {
259 double rem = dash_width + gap_width - *offsetp;
260 if (pos + rem > length) {
261 *offsetp += length - pos;
262 break;
263 }
264 else {
265 pos += rem;
266 *offsetp = 0.0;
267 }
268 }
269 else {
270 double rem = dash_width - *offsetp;
271 if (pos + rem > length) {
272 solid_arc(cent, rad, start_angle + pos/rad, end_angle, lt);
273 *offsetp += length - pos;
274 break;
275 }
276 else {
277 solid_arc(cent, rad, start_angle + pos/rad,
278 start_angle + (pos + rem)/rad, lt);
279 pos += rem;
280 *offsetp = dash_width;
281 }
282 }
283 }
284}
285
286// Used by dashed_rounded_box.
287
288void common_output::dash_line(const position &start, const position &end,
289 const line_type &lt,
290 double dash_width, double gap_width,
291 double *offsetp)
292{
293 distance dist = end - start;
294 double length = hypot(dist);
295 double pos = 0.0;
296 for (;;) {
297 if (*offsetp >= dash_width) {
298 double rem = dash_width + gap_width - *offsetp;
299 if (pos + rem > length) {
300 *offsetp += length - pos;
301 break;
302 }
303 else {
304 pos += rem;
305 *offsetp = 0.0;
306 }
307 }
308 else {
309 double rem = dash_width - *offsetp;
310 if (pos + rem > length) {
311 line(start + dist*(pos/length), &end, 1, lt);
312 *offsetp += length - pos;
313 break;
314 }
315 else {
316 position p(start + dist*((pos + rem)/length));
317 line(start + dist*(pos/length), &p, 1, lt);
318 pos += rem;
319 *offsetp = dash_width;
320 }
321 }
322 }
323}
324
325void common_output::dotted_rounded_box(const position &cent,
326 const distance &dim, double rad,
327 const line_type &lt)
328{
329 line_type slt = lt;
330 slt.type = line_type::solid;
331
332 double hor_length = dim.x + (M_PI/2.0 - 2.0)*rad;
333 int n_hor_dots = int(hor_length/lt.dash_width + .5);
334 double hor_gap_width = (n_hor_dots != 0
335 ? hor_length/n_hor_dots
336 : lt.dash_width);
337
338 double vert_length = dim.y + (M_PI/2.0 - 2.0)*rad;
339 int n_vert_dots = int(vert_length/lt.dash_width + .5);
340 double vert_gap_width = (n_vert_dots != 0
341 ? vert_length/n_vert_dots
342 : lt.dash_width);
343 double epsilon = lt.dash_width/(rad*100.0);
344
345 double offset = 0.0;
346 dot_arc(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad,
347 -M_PI/4.0, 0, slt, vert_gap_width, &offset);
348 dot_line(cent + position(dim.x/2.0, -dim.y/2.0 + rad),
349 cent + position(dim.x/2.0, dim.y/2.0 - rad),
350 slt, vert_gap_width, &offset);
351 dot_arc(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad,
352 0, M_PI/4.0 - epsilon, slt, vert_gap_width, &offset);
353
354 offset = 0.0;
355 dot_arc(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad,
356 M_PI/4.0, M_PI/2, slt, hor_gap_width, &offset);
357 dot_line(cent + position(dim.x/2.0 - rad, dim.y/2.0),
358 cent + position(-dim.x/2.0 + rad, dim.y/2.0),
359 slt, hor_gap_width, &offset);
360 dot_arc(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad,
361 M_PI/2, 3*M_PI/4.0 - epsilon, slt, hor_gap_width, &offset);
362
363 offset = 0.0;
364 dot_arc(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad,
365 3.0*M_PI/4.0, M_PI, slt, vert_gap_width, &offset);
366 dot_line(cent + position(-dim.x/2.0, dim.y/2.0 - rad),
367 cent + position(-dim.x/2.0, -dim.y/2.0 + rad),
368 slt, vert_gap_width, &offset);
369 dot_arc(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad,
370 M_PI, 5.0*M_PI/4.0 - epsilon, slt, vert_gap_width, &offset);
371
372 offset = 0.0;
373 dot_arc(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad,
374 5*M_PI/4.0, 3*M_PI/2.0, slt, hor_gap_width, &offset);
375 dot_line(cent + position(-dim.x/2.0 + rad, -dim.y/2.0),
376 cent + position(dim.x/2.0 - rad, -dim.y/2.0),
377 slt, hor_gap_width, &offset);
378 dot_arc(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad,
379 3*M_PI/2, 7*M_PI/4 - epsilon, slt, hor_gap_width, &offset);
380}
381
382// Used by dotted_rounded_box.
383
384void common_output::dot_arc(const position &cent, double rad,
385 double start_angle, double end_angle,
386 const line_type &lt, double gap_width,
387 double *offsetp)
388{
389 double length = (end_angle - start_angle)*rad;
390 double pos = 0.0;
391 for (;;) {
392 if (*offsetp == 0.0) {
393 double ang = start_angle + pos/rad;
394 dot(cent + position(cos(ang), sin(ang))*rad, lt);
395 }
396 double rem = gap_width - *offsetp;
397 if (pos + rem > length) {
398 *offsetp += length - pos;
399 break;
400 }
401 else {
402 pos += rem;
403 *offsetp = 0.0;
404 }
405 }
406}
407
408// Used by dotted_rounded_box.
409
410void common_output::dot_line(const position &start, const position &end,
411 const line_type &lt, double gap_width,
412 double *offsetp)
413{
414 distance dist = end - start;
415 double length = hypot(dist);
416 double pos = 0.0;
417 for (;;) {
418 if (*offsetp == 0.0)
419 dot(start + dist*(pos/length), lt);
420 double rem = gap_width - *offsetp;
421 if (pos + rem > length) {
422 *offsetp += length - pos;
423 break;
424 }
425 else {
426 pos += rem;
427 *offsetp = 0.0;
428 }
429 }
430}
431
432
433void common_output::solid_rounded_box(const position &cent,
434 const distance &dim, double rad,
435 const line_type &lt)
436{
437 position tem = cent - dim/2.0;
438 arc(tem + position(0.0, rad),
439 tem + position(rad, rad),
440 tem + position(rad, 0.0),
441 lt);
442 tem = cent + position(-dim.x/2.0, dim.y/2.0);
443 arc(tem + position(rad, 0.0),
444 tem + position(rad, -rad),
445 tem + position(0.0, -rad),
446 lt);
447 tem = cent + dim/2.0;
448 arc(tem + position(0.0, -rad),
449 tem + position(-rad, -rad),
450 tem + position(-rad, 0.0),
451 lt);
452 tem = cent + position(dim.x/2.0, -dim.y/2.0);
453 arc(tem + position(-rad, 0.0),
454 tem + position(-rad, rad),
455 tem + position(0.0, rad),
456 lt);
457 position end;
458 end = cent + position(-dim.x/2.0, dim.y/2.0 - rad);
459 line(cent - dim/2.0 + position(0.0, rad), &end, 1, lt);
460 end = cent + position(dim.x/2.0 - rad, dim.y/2.0);
461 line(cent + position(-dim.x/2.0 + rad, dim.y/2.0), &end, 1, lt);
462 end = cent + position(dim.x/2.0, -dim.y/2.0 + rad);
463 line(cent + position(dim.x/2.0, dim.y/2.0 - rad), &end, 1, lt);
464 end = cent + position(-dim.x/2.0 + rad, -dim.y/2.0);
465 line(cent + position(dim.x/2.0 - rad, -dim.y/2.0), &end, 1, lt);
466}
467
468void common_output::filled_rounded_box(const position &cent,
469 const distance &dim, double rad,
470 double fill)
471{
472 line_type ilt;
473 ilt.type = line_type::invisible;
474 circle(cent + position(dim.x/2.0 - rad, dim.y/2.0 - rad), rad, ilt, fill);
475 circle(cent + position(-dim.x/2.0 + rad, dim.y/2.0 - rad), rad, ilt, fill);
476 circle(cent + position(-dim.x/2.0 + rad, -dim.y/2.0 + rad), rad, ilt, fill);
477 circle(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad, ilt, fill);
478 position vec[4];
479 vec[0] = cent + position(dim.x/2.0, dim.y/2.0 - rad);
480 vec[1] = cent + position(-dim.x/2.0, dim.y/2.0 - rad);
481 vec[2] = cent + position(-dim.x/2.0, -dim.y/2.0 + rad);
482 vec[3] = cent + position(dim.x/2.0, -dim.y/2.0 + rad);
483 polygon(vec, 4, ilt, fill);
484 vec[0] = cent + position(dim.x/2.0 - rad, dim.y/2.0);
485 vec[1] = cent + position(-dim.x/2.0 + rad, dim.y/2.0);
486 vec[2] = cent + position(-dim.x/2.0 + rad, -dim.y/2.0);
487 vec[3] = cent + position(dim.x/2.0 - rad, -dim.y/2.0);
488 polygon(vec, 4, ilt, fill);
489}