/contrib/groff/src/libs/libgroff/geometry.cpp

https://bitbucket.org/freebsd/freebsd-head/ · C++ · 179 lines · 106 code · 11 blank · 62 comment · 19 complexity · 120bb7705ea71938b606815a5203d3ae MD5 · raw file

  1. // -*- C++ -*-
  2. /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004
  3. Free Software Foundation, Inc.
  4. Written by Gaius Mulley <gaius@glam.ac.uk>
  5. using adjust_arc_center() from printer.cpp, written by James Clark.
  6. This file is part of groff.
  7. groff is free software; you can redistribute it and/or modify it under
  8. the terms of the GNU General Public License as published by the Free
  9. Software Foundation; either version 2, or (at your option) any later
  10. version.
  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. You should have received a copy of the GNU General Public License along
  16. with groff; see the file COPYING. If not, write to the Free Software
  17. Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
  18. #include <stdio.h>
  19. #include <math.h>
  20. #undef MAX
  21. #define MAX(a, b) (((a) > (b)) ? (a) : (b))
  22. #undef MIN
  23. #define MIN(a, b) (((a) < (b)) ? (a) : (b))
  24. // This utility function adjusts the specified center of the
  25. // arc so that it is equidistant between the specified start
  26. // and end points. (p[0], p[1]) is a vector from the current
  27. // point to the center; (p[2], p[3]) is a vector from the
  28. // center to the end point. If the center can be adjusted,
  29. // a vector from the current point to the adjusted center is
  30. // stored in c[0], c[1] and 1 is returned. Otherwise 0 is
  31. // returned.
  32. #if 1
  33. int adjust_arc_center(const int *p, double *c)
  34. {
  35. // We move the center along a line parallel to the line between
  36. // the specified start point and end point so that the center
  37. // is equidistant between the start and end point.
  38. // It can be proved (using Lagrange multipliers) that this will
  39. // give the point nearest to the specified center that is equidistant
  40. // between the start and end point.
  41. double x = p[0] + p[2]; // (x, y) is the end point
  42. double y = p[1] + p[3];
  43. double n = x*x + y*y;
  44. if (n != 0) {
  45. c[0]= double(p[0]);
  46. c[1] = double(p[1]);
  47. double k = .5 - (c[0]*x + c[1]*y)/n;
  48. c[0] += k*x;
  49. c[1] += k*y;
  50. return 1;
  51. }
  52. else
  53. return 0;
  54. }
  55. #else
  56. int printer::adjust_arc_center(const int *p, double *c)
  57. {
  58. int x = p[0] + p[2]; // (x, y) is the end point
  59. int y = p[1] + p[3];
  60. // Start at the current point; go in the direction of the specified
  61. // center point until we reach a point that is equidistant between
  62. // the specified starting point and the specified end point. Place
  63. // the center of the arc there.
  64. double n = p[0]*double(x) + p[1]*double(y);
  65. if (n > 0) {
  66. double k = (double(x)*x + double(y)*y)/(2.0*n);
  67. // (cx, cy) is our chosen center
  68. c[0] = k*p[0];
  69. c[1] = k*p[1];
  70. return 1;
  71. }
  72. else {
  73. // We would never reach such a point. So instead start at the
  74. // specified end point of the arc. Go towards the specified
  75. // center point until we reach a point that is equidistant between
  76. // the specified start point and specified end point. Place
  77. // the center of the arc there.
  78. n = p[2]*double(x) + p[3]*double(y);
  79. if (n > 0) {
  80. double k = 1 - (double(x)*x + double(y)*y)/(2.0*n);
  81. // (c[0], c[1]) is our chosen center
  82. c[0] = p[0] + k*p[2];
  83. c[1] = p[1] + k*p[3];
  84. return 1;
  85. }
  86. else
  87. return 0;
  88. }
  89. }
  90. #endif
  91. /*
  92. * check_output_arc_limits - works out the smallest box that will encompass
  93. * an arc defined by an origin (x, y) and two
  94. * vectors (p0, p1) and (p2, p3).
  95. * (x1, y1) -> start of arc
  96. * (x1, y1) + (xv1, yv1) -> center of circle
  97. * (x1, y1) + (xv1, yv1) + (xv2, yv2) -> end of arc
  98. *
  99. * Works out in which quadrant the arc starts and
  100. * stops, and from this it determines the x, y
  101. * max/min limits. The arc is drawn clockwise.
  102. */
  103. void check_output_arc_limits(int x_1, int y_1,
  104. int xv_1, int yv_1,
  105. int xv_2, int yv_2,
  106. double c_0, double c_1,
  107. int *minx, int *maxx,
  108. int *miny, int *maxy)
  109. {
  110. int radius = (int)sqrt(c_0 * c_0 + c_1 * c_1);
  111. // clockwise direction
  112. int xcenter = x_1 + xv_1;
  113. int ycenter = y_1 + yv_1;
  114. int xend = xcenter + xv_2;
  115. int yend = ycenter + yv_2;
  116. // for convenience, transform to counterclockwise direction,
  117. // centered at the origin
  118. int xs = xend - xcenter;
  119. int ys = yend - ycenter;
  120. int xe = x_1 - xcenter;
  121. int ye = y_1 - ycenter;
  122. *minx = *maxx = xs;
  123. *miny = *maxy = ys;
  124. if (xe > *maxx)
  125. *maxx = xe;
  126. else if (xe < *minx)
  127. *minx = xe;
  128. if (ye > *maxy)
  129. *maxy = ye;
  130. else if (ye < *miny)
  131. *miny = ye;
  132. int qs, qe; // quadrants 0..3
  133. if (xs >= 0)
  134. qs = (ys >= 0) ? 0 : 3;
  135. else
  136. qs = (ys >= 0) ? 1 : 2;
  137. if (xe >= 0)
  138. qe = (ye >= 0) ? 0 : 3;
  139. else
  140. qe = (ye >= 0) ? 1 : 2;
  141. // make qs always smaller than qe
  142. if ((qs > qe)
  143. || ((qs == qe) && (double(xs) * ye < double(xe) * ys)))
  144. qe += 4;
  145. for (int i = qs; i < qe; i++)
  146. switch (i % 4) {
  147. case 0:
  148. *maxy = radius;
  149. break;
  150. case 1:
  151. *minx = -radius;
  152. break;
  153. case 2:
  154. *miny = -radius;
  155. break;
  156. case 3:
  157. *maxx = radius;
  158. break;
  159. }
  160. *minx += xcenter;
  161. *maxx += xcenter;
  162. *miny += ycenter;
  163. *maxy += ycenter;
  164. }