LCOV - code coverage report
Current view: top level - preproc/pic - common.cpp (source / functions) Hit Total Coverage
Test: GNU roff Lines: 123 412 29.9 %
Date: 2026-01-16 17:51:41 Functions: 6 18 33.3 %
Legend: Lines: hit not hit

          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 &cent, double rad,
      32             :                                   const line_type &lt)
      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 &cent, double rad,
      67             :                                   const line_type &lt)
      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 &cent,
      89             :                                 const position &z0, const position &z1,
      90             :                                 const distance &dim, const line_type &lt)
      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 &cent, const distance &dim,
     130             :                                    const line_type &lt)
     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 &cent, const distance &dim,
     189             :                                    const line_type &lt)
     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 &cent,
     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 &cent,
     256             :                                const position &end, const line_type &lt)
     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 &cent,
     287             :                                const position &end, const line_type &lt)
     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 &cent, double rad,
     314             :                               double start_angle, double end_angle,
     315             :                               const line_type &lt)
     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 &cent, const distance &dim,
     327             :                                 double rad, const line_type &lt,
     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 &cent,
     351             :                                        const distance &dim, double rad,
     352             :                                        const line_type &lt)
     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 &cent, double rad,
     411             :                              double start_angle, double end_angle,
     412             :                              const line_type &lt,
     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 &lt,
     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 &cent,
     489             :                                        const distance &dim, double rad,
     490             :                                        const line_type &lt)
     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 &cent, double rad,
     548             :                             double start_angle, double end_angle,
     549             :                             const line_type &lt, 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 &lt, 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 &cent,
     598             :                                       const distance &dim, double rad,
     599             :                                       const line_type &lt)
     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 &cent,
     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:

Generated by: LCOV version 1.14