common.cpp revision 114402
1// -*- C++ -*- 2/* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc. 3 Written by James Clark (jjc@jclark.com) 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 2, 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 COPYING. If not, write to the Free Software 19Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 ¢, double rad, 27 const line_type <) 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 ¢, double rad, 62 const line_type <) 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 ¢, 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 ¢, 100 const position &end, const line_type <) 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 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; 120 } 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); 126} 127 128// output a dotted arc as a series of dots 129 130void common_output::dotted_arc(const position &start, const position ¢, 131 const position &end, const line_type <) 132{ 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; 138 } 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); 153 } 154 } 155} 156 157void common_output::solid_arc(const position ¢, double rad, 158 double start_angle, double end_angle, 159 const line_type <) 160{ 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); 167} 168 169 170void common_output::rounded_box(const position ¢, const distance &dim, 171 double rad, const line_type <, double fill) 172{ 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); 189 } 190} 191 192 193void common_output::dashed_rounded_box(const position ¢, 194 const distance &dim, double rad, 195 const line_type <) 196{ 197 line_type slt = lt; 198 slt.type = line_type::solid; 199 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); 205 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); 222 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); 231 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); 240 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); 249} 250 251// Used by dashed_rounded_box. 252 253void common_output::dash_arc(const position ¢, double rad, 254 double start_angle, double end_angle, 255 const line_type <, 256 double dash_width, double gap_width, 257 double *offsetp) 258{ 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; 267 } 268 else { 269 pos += rem; 270 *offsetp = 0.0; 271 } 272 } 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; 279 } 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; 285 } 286 } 287 } 288} 289 290// Used by dashed_rounded_box. 291 292void common_output::dash_line(const position &start, const position &end, 293 const line_type <, 294 double dash_width, double gap_width, 295 double *offsetp) 296{ 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; 308 } 309 else { 310 pos += rem; 311 *offsetp = 0.0; 312 } 313 } 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; 320 } 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; 326 } 327 } 328 } 329} 330 331void common_output::dotted_rounded_box(const position ¢, 332 const distance &dim, double rad, 333 const line_type <) 334{ 335 line_type slt = lt; 336 slt.type = line_type::solid; 337 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); 343 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); 350 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); 359 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); 368 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); 377 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); 386} 387 388// Used by dotted_rounded_box. 389 390void common_output::dot_arc(const position ¢, double rad, 391 double start_angle, double end_angle, 392 const line_type <, double gap_width, 393 double *offsetp) 394{ 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); 401 } 402 double rem = gap_width - *offsetp; 403 if (pos + rem > length) { 404 *offsetp += length - pos; 405 break; 406 } 407 else { 408 pos += rem; 409 *offsetp = 0.0; 410 } 411 } 412} 413 414// Used by dotted_rounded_box. 415 416void common_output::dot_line(const position &start, const position &end, 417 const line_type <, double gap_width, 418 double *offsetp) 419{ 420 distance dist = end - start; 421 double length = hypot(dist); 422 if (length == 0.0) 423 return; 424 double pos = 0.0; 425 for (;;) { 426 if (*offsetp == 0.0) 427 dot(start + dist*(pos/length), lt); 428 double rem = gap_width - *offsetp; 429 if (pos + rem > length) { 430 *offsetp += length - pos; 431 break; 432 } 433 else { 434 pos += rem; 435 *offsetp = 0.0; 436 } 437 } 438} 439 440void common_output::solid_rounded_box(const position ¢, 441 const distance &dim, double rad, 442 const line_type <) 443{ 444 position tem = cent - dim/2.0; 445 arc(tem + position(0.0, rad), 446 tem + position(rad, rad), 447 tem + position(rad, 0.0), 448 lt); 449 tem = cent + position(-dim.x/2.0, dim.y/2.0); 450 arc(tem + position(rad, 0.0), 451 tem + position(rad, -rad), 452 tem + position(0.0, -rad), 453 lt); 454 tem = cent + dim/2.0; 455 arc(tem + position(0.0, -rad), 456 tem + position(-rad, -rad), 457 tem + position(-rad, 0.0), 458 lt); 459 tem = cent + position(dim.x/2.0, -dim.y/2.0); 460 arc(tem + position(-rad, 0.0), 461 tem + position(-rad, rad), 462 tem + position(0.0, rad), 463 lt); 464 position end; 465 end = cent + position(-dim.x/2.0, dim.y/2.0 - rad); 466 line(cent - dim/2.0 + position(0.0, rad), &end, 1, lt); 467 end = cent + position(dim.x/2.0 - rad, dim.y/2.0); 468 line(cent + position(-dim.x/2.0 + rad, dim.y/2.0), &end, 1, lt); 469 end = cent + position(dim.x/2.0, -dim.y/2.0 + rad); 470 line(cent + position(dim.x/2.0, dim.y/2.0 - rad), &end, 1, lt); 471 end = cent + position(-dim.x/2.0 + rad, -dim.y/2.0); 472 line(cent + position(dim.x/2.0 - rad, -dim.y/2.0), &end, 1, lt); 473} 474 475void common_output::filled_rounded_box(const position ¢, 476 const distance &dim, double rad, 477 double fill) 478{ 479 line_type ilt; 480 ilt.type = line_type::invisible; 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 circle(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad, ilt, fill); 485 position vec[4]; 486 vec[0] = cent + position(dim.x/2.0, dim.y/2.0 - rad); 487 vec[1] = cent + position(-dim.x/2.0, dim.y/2.0 - rad); 488 vec[2] = cent + position(-dim.x/2.0, -dim.y/2.0 + rad); 489 vec[3] = cent + position(dim.x/2.0, -dim.y/2.0 + rad); 490 polygon(vec, 4, ilt, fill); 491 vec[0] = cent + position(dim.x/2.0 - rad, dim.y/2.0); 492 vec[1] = cent + position(-dim.x/2.0 + rad, dim.y/2.0); 493 vec[2] = cent + position(-dim.x/2.0 + rad, -dim.y/2.0); 494 vec[3] = cent + position(dim.x/2.0 - rad, -dim.y/2.0); 495 polygon(vec, 4, ilt, fill); 496} 497