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