2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002
3 Free Software Foundation, Inc.
4 Written by Gaius Mulley <gaius@glam.ac.uk>
5 using adjust_arc_center() from printer.cc, written by James Clark.
7 This file is part of groff.
9 groff is free software; you can redistribute it and/or modify it under
10 the terms of the GNU General Public License as published by the Free
11 Software Foundation; either version 2, or (at your option) any later
14 groff is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19 You should have received a copy of the GNU General Public License along
20 with groff; see the file COPYING. If not, write to the Free Software
21 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
28 #define MAX(a, b) (((a) > (b)) ? (a) : (b))
31 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
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
44 int adjust_arc_center(const int *p, double *c)
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.
53 double x = p[0] + p[2]; // (x, y) is the end point
54 double y = p[1] + p[3];
59 double k = .5 - (c[0]*x + c[1]*y)/n;
68 int printer::adjust_arc_center(const int *p, double *c)
70 int x = p[0] + p[2]; // (x, y) is the end point
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);
78 double k = (double(x)*x + double(y)*y)/(2.0*n);
79 // (cx, cy) is our chosen center
85 // We would never reach such a point. So instead start at the
86 // specified end point of the arc. Go towards the specified
87 // center point until we reach a point that is equidistant between
88 // the specified start point and specified end point. Place
89 // the center of the arc there.
90 n = p[2]*double(x) + p[3]*double(y);
92 double k = 1 - (double(x)*x + double(y)*y)/(2.0*n);
93 // (c[0], c[1]) is our chosen center
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
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.
117 * [I'm sure there is a better way to do this, but
118 * I don't know how. Please can someone let me
119 * know or "improve" this function.]
122 void check_output_arc_limits(int x1, int y1,
125 double c0, double c1,
126 int *minx, int *maxx,
127 int *miny, int *maxy)
129 int radius = (int)sqrt(c0*c0 + c1*c1);
130 int x2 = x1 + xv1 + xv2; // end of arc is (x2, y2)
131 int y2 = y1 + yv1 + yv2;
133 // firstly lets use the `circle' limitation
134 *minx = x1 + xv1 - radius;
135 *maxx = x1 + xv1 + radius;
136 *miny = y1 + yv1 - radius;
137 *maxy = y1 + yv1 + radius;
139 /* now to see which min/max can be reduced and increased for the limits of
147 * NB. (x1+xv1, y1+yv1) is at the origin
149 * below we ask a nested question
150 * (i) from which quadrant does the first vector start?
151 * (ii) into which quadrant does the second vector go?
152 * from the 16 possible answers we determine the limits of the arc
154 if (xv1 > 0 && yv1 > 0) {
155 // first vector in Q3
156 if (xv2 >= 0 && yv2 >= 0 ) {
161 else if (xv2 < 0 && yv2 >= 0) {
166 else if (xv2 >= 0 && yv2 < 0) {
170 else if (xv2 < 0 && yv2 < 0) {
179 // xv2, yv2 could all be zero?
183 else if (xv1 > 0 && yv1 < 0) {
184 // first vector in Q2
185 if (xv2 >= 0 && yv2 >= 0) {
191 else if (xv2 < 0 && yv2 >= 0) {
200 // otherwise almost full circle anyway
203 else if (xv2 >= 0 && yv2 < 0) {
208 else if (xv2 < 0 && yv2 < 0) {
213 else if (xv1 <= 0 && yv1 <= 0) {
214 // first vector in Q1
215 if (xv2 >= 0 && yv2 >= 0) {
224 // nearly full circle
227 else if (xv2 < 0 && yv2 >= 0) {
231 else if (xv2 >= 0 && yv2 < 0) {
237 else if (xv2 < 0 && yv2 < 0) {
243 else if (xv1 <= 0 && yv1 > 0) {
244 // first vector in Q4
245 if (xv2 >= 0 && yv2 >= 0) {
249 else if (xv2 < 0 && yv2 >= 0) {
254 else if (xv2 >= 0 && yv2 < 0) {
263 // nearly full circle
266 else if (xv2 < 0 && yv2 < 0) {
274 // this should *never* happen but if it does it means a case above is wrong
275 // this code is only present for safety sake
277 fprintf(stderr, "assert failed *minx > *maxx\n");
282 fprintf(stderr, "assert failed *miny > *maxy\n");