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
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
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. */
24 // output a dashed circle as a series of arcs
26 void common_output::dashed_circle(const position
¢
, double rad
,
29 assert(lt
.type
== line_type::dashed
);
31 slt
.type
= line_type::solid
;
32 double dash_angle
= lt
.dash_width
/rad
;
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
;
40 else if (dash_angle
< M_PI
) {
41 gap_angle
= M_PI
- dash_angle
;
45 circle(cent
, rad
, slt
, -1.0);
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
¢
, double rad
,
64 assert(lt
.type
== line_type::dotted
);
65 double gap_angle
= lt
.dash_width
/rad
;
67 if (gap_angle
>= M_PI
/2.0) {
68 // always have at least 2 dots
73 ndots
= 4*int(M_PI
/(2.0*gap_angle
));
74 gap_angle
= (M_PI
*2.0)/ndots
;
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
¢
,
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
;
93 *result
= start
+ c
*((e
*e
)/(2.0*n
));
97 // output a dashed arc as a series of arcs
99 void common_output::dashed_arc(const position
&start
, const position
¢
,
100 const position
&end
, const line_type
<
)
102 assert(lt
.type
== line_type::dashed
);
104 if (!compute_arc_center(start
, cent
, end
, &c
)) {
105 line(start
, &end
, 1, lt
);
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
);
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
¢
,
131 const position
&end
, const line_type
<
)
133 assert(lt
.type
== line_type::dotted
);
135 if (!compute_arc_center(start
, cent
, end
, &c
)) {
136 line(start
, &end
, 1, lt
);
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);
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
¢
, double rad
,
158 double start_angle
, double end_angle
,
162 slt
.type
= line_type::solid
;
163 arc(cent
+ position(cos(start_angle
), sin(start_angle
))*rad
,
165 cent
+ position(cos(end_angle
), sin(end_angle
))*rad
,
170 void common_output::rounded_box(const position
¢
, const distance
&dim
,
171 double rad
, const line_type
<
, double fill
)
174 filled_rounded_box(cent
, dim
, rad
, fill
);
176 case line_type::invisible
:
178 case line_type::dashed
:
179 dashed_rounded_box(cent
, dim
, rad
, lt
);
181 case line_type::dotted
:
182 dotted_rounded_box(cent
, dim
, rad
, lt
);
184 case line_type::solid
:
185 solid_rounded_box(cent
, dim
, rad
, lt
);
193 void common_output::dashed_rounded_box(const position
¢
,
194 const distance
&dim
, double rad
,
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
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
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
¢
, double rad
,
254 double start_angle
, double end_angle
,
256 double dash_width
, double gap_width
,
259 double length
= (end_angle
- start_angle
)*rad
;
262 if (*offsetp
>= dash_width
) {
263 double rem
= dash_width
+ gap_width
- *offsetp
;
264 if (pos
+ rem
> length
) {
265 *offsetp
+= length
- pos
;
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
;
281 solid_arc(cent
, rad
, start_angle
+ pos
/rad
,
282 start_angle
+ (pos
+ rem
)/rad
, lt
);
284 *offsetp
= dash_width
;
290 // Used by dashed_rounded_box.
292 void common_output::dash_line(const position
&start
, const position
&end
,
294 double dash_width
, double gap_width
,
297 distance dist
= end
- start
;
298 double length
= hypot(dist
);
303 if (*offsetp
>= dash_width
) {
304 double rem
= dash_width
+ gap_width
- *offsetp
;
305 if (pos
+ rem
> length
) {
306 *offsetp
+= length
- pos
;
315 double rem
= dash_width
- *offsetp
;
316 if (pos
+ rem
> length
) {
317 line(start
+ dist
*(pos
/length
), &end
, 1, lt
);
318 *offsetp
+= length
- pos
;
322 position
p(start
+ dist
*((pos
+ rem
)/length
));
323 line(start
+ dist
*(pos
/length
), &p
, 1, lt
);
325 *offsetp
= dash_width
;
331 void common_output::dotted_rounded_box(const position
¢
,
332 const distance
&dim
, double rad
,
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
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
349 double epsilon
= lt
.dash_width
/(rad
*100.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
);
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
);
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
);
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
¢
, double rad
,
391 double start_angle
, double end_angle
,
392 const line_type
<
, double gap_width
,
395 double length
= (end_angle
- start_angle
)*rad
;
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
;
414 // Used by dotted_rounded_box.
416 void common_output::dot_line(const position
&start
, const position
&end
,
417 const line_type
<
, double gap_width
,
420 distance dist
= end
- start
;
421 double length
= hypot(dist
);
425 dot(start
+ dist
*(pos
/length
), lt
);
426 double rem
= gap_width
- *offsetp
;
427 if (pos
+ rem
> length
) {
428 *offsetp
+= length
- pos
;
439 void common_output::solid_rounded_box(const position
¢
,
440 const distance
&dim
, double rad
,
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),
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
),
453 tem
= cent
+ dim
/2.0;
454 arc(tem
+ position(0.0, -rad
),
455 tem
+ position(-rad
, -rad
),
456 tem
+ position(-rad
, 0.0),
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
),
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
¢
,
475 const distance
&dim
, double rad
,
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
);
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
);