home *** CD-ROM | disk | FTP | other *** search
/ Aminet 10 / aminetcdnumber101996.iso / Aminet / util / gnu / groff_src.lha / groff-1.10src / pic / common.cc next >
C/C++ Source or Header  |  1995-06-22  |  16KB  |  498 lines

  1. // -*- C++ -*-
  2. /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
  3.      Written by James Clark (jjc@jclark.com)
  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 2, 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 COPYING.  If not, write to the Free Software
  19. Foundation, 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.  
  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.   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.  
  130. void 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.  
  157. void 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.  
  170. void 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.  
  193. void 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.  
  253. void 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.  
  292. void 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.  
  331. void 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.  
  390. void 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.  
  416. void 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.  
  440.  
  441. void common_output::solid_rounded_box(const position ¢,
  442.                       const distance &dim, double rad,
  443.                       const line_type <)
  444. {
  445.   position tem = cent - dim/2.0;
  446.   arc(tem + position(0.0, rad),
  447.       tem + position(rad, rad),
  448.       tem + position(rad, 0.0),
  449.       lt);
  450.   tem = cent + position(-dim.x/2.0, dim.y/2.0);
  451.   arc(tem + position(rad, 0.0),
  452.       tem + position(rad, -rad),
  453.       tem + position(0.0, -rad),
  454.       lt);
  455.   tem = cent + dim/2.0;
  456.   arc(tem + position(0.0, -rad),
  457.       tem + position(-rad, -rad),
  458.       tem + position(-rad, 0.0),
  459.       lt);
  460.   tem = cent + position(dim.x/2.0, -dim.y/2.0);
  461.   arc(tem + position(-rad, 0.0),
  462.       tem + position(-rad, rad),
  463.       tem + position(0.0, rad),
  464.       lt);
  465.   position end;
  466.   end = cent + position(-dim.x/2.0, dim.y/2.0 - rad);
  467.   line(cent - dim/2.0 + position(0.0, rad), &end, 1, lt);
  468.   end = cent + position(dim.x/2.0 - rad, dim.y/2.0);
  469.   line(cent + position(-dim.x/2.0 + rad, dim.y/2.0), &end, 1, lt);
  470.   end = cent + position(dim.x/2.0, -dim.y/2.0 + rad);
  471.   line(cent + position(dim.x/2.0, dim.y/2.0 - rad), &end, 1, lt);
  472.   end = cent + position(-dim.x/2.0 + rad, -dim.y/2.0);
  473.   line(cent + position(dim.x/2.0 - rad, -dim.y/2.0), &end, 1, lt);
  474. }
  475.  
  476. void common_output::filled_rounded_box(const position ¢,
  477.                        const distance &dim, double rad,
  478.                        double fill)
  479. {
  480.   line_type ilt;
  481.   ilt.type = line_type::invisible;
  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.   circle(cent + position(dim.x/2.0 - rad, -dim.y/2.0 + rad), rad, ilt, fill);
  486.   position vec[4];
  487.   vec[0] = cent + position(dim.x/2.0, dim.y/2.0 - rad);
  488.   vec[1] = cent + position(-dim.x/2.0, dim.y/2.0 - rad);
  489.   vec[2] = cent + position(-dim.x/2.0, -dim.y/2.0 + rad);
  490.   vec[3] = cent + position(dim.x/2.0, -dim.y/2.0 + rad);
  491.   polygon(vec, 4, ilt, fill);
  492.   vec[0] = cent + position(dim.x/2.0 - rad, dim.y/2.0);
  493.   vec[1] = cent + position(-dim.x/2.0 + rad, dim.y/2.0);
  494.   vec[2] = cent + position(-dim.x/2.0 + rad, -dim.y/2.0);
  495.   vec[3] = cent + position(dim.x/2.0 - rad, -dim.y/2.0);
  496.   polygon(vec, 4, ilt, fill);
  497. }
  498.