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