home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / usr.bin / groff / pic / common.cc next >
Encoding:
C/C++ Source or Header  |  1991-04-30  |  15.2 KB  |  490 lines

  1. // -*- C++ -*-
  2. /* Copyright (C) 1989, 1990 Free Software Foundation, Inc.
  3.      Written by James Clark (jjc@jclark.uucp)
  4.  
  5. This file is part of groff.
  6.  
  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 1, or (at your option) any later
  10. version.
  11.  
  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.
  16.  
  17. You should have received a copy of the GNU General Public License along
  18. with groff; see the file LICENSE.  If not, write to the Free Software
  19. Foundation, 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.  
  26. void 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.  
  61. void 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.  
  83. int 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.  
  99. void 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.   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.  
  128. void common_output::dotted_arc(const position &start, const position ¢,
  129.                    const position &end, const line_type <)
  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.  
  153. void common_output::solid_arc(const position ¢, double rad,
  154.                   double start_angle, double end_angle,
  155.                   const line_type <)
  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.  
  166. void common_output::rounded_box(const position ¢, const distance &dim,
  167.                 double rad, const line_type <, 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.  
  189. void common_output::dashed_rounded_box(const position ¢,
  190.                        const distance &dim, double rad,
  191.                        const line_type <)
  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.  
  249. void common_output::dash_arc(const position ¢, double rad,
  250.                  double start_angle, double end_angle,
  251.                  const line_type <,
  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.  
  288. void common_output::dash_line(const position &start, const position &end,
  289.                   const line_type <,
  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.  
  325. void common_output::dotted_rounded_box(const position ¢,
  326.                        const distance &dim, double rad,
  327.                        const line_type <)
  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.  
  384. void common_output::dot_arc(const position ¢, double rad,
  385.                 double start_angle, double end_angle,
  386.                 const line_type <, 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.  
  410. void common_output::dot_line(const position &start, const position &end,
  411.                  const line_type <, 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.  
  433. void common_output::solid_rounded_box(const position ¢,
  434.                       const distance &dim, double rad,
  435.                       const line_type <)
  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.  
  468. void common_output::filled_rounded_box(const position ¢,
  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. }
  490.