geometry.cpp revision 114402
1// -*- C++ -*-
2/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003
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
7This file is part of groff.
8
9groff is free software; you can redistribute it and/or modify it under
10the terms of the GNU General Public License as published by the Free
11Software Foundation; either version 2, or (at your option) any later
12version.
13
14groff is distributed in the hope that it will be useful, but WITHOUT ANY
15WARRANTY; without even the implied warranty of MERCHANTABILITY or
16FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
17for more details.
18
19You should have received a copy of the GNU General Public License along
20with groff; see the file COPYING.  If not, write to the Free Software
21Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
22
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
44int 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  double x = p[0] + p[2];	// (x, y) is the end point
54  double y = p[1] + p[3];
55  double n = x*x + y*y;
56  if (n != 0) {
57    c[0]= double(p[0]);
58    c[1] = double(p[1]);
59    double k = .5 - (c[0]*x + c[1]*y)/n;
60    c[0] += k*x;
61    c[1] += k*y;
62    return 1;
63  }
64  else
65    return 0;
66}
67#else
68int 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 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);
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 *                            [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.]
120 */
121
122void check_output_arc_limits(int x1, int y1,
123			     int xv1, int yv1,
124			     int xv2, int yv2,
125			     double c0, double c1,
126			     int *minx, int *maxx,
127			     int *miny, int *maxy)
128{
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;
132
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;
138
139  /*  now to see which min/max can be reduced and increased for the limits of
140   *  the arc
141   *
142   *       Q2   |   Q1
143   *       -----+-----
144   *       Q3   |   Q4
145   *
146   *
147   *  NB. (x1+xv1, y1+yv1) is at the origin
148   *
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
153   */
154  if (xv1 > 0 && yv1 > 0) {
155    // first vector in Q3
156    if (xv2 >= 0 && yv2 >= 0 ) {
157      // second in Q1
158      *maxx = x2;
159      *miny = y1;
160    }
161    else if (xv2 < 0 && yv2 >= 0) {
162      // second in Q2
163      *maxx = x2;
164      *miny = y1;
165    }
166    else if (xv2 >= 0 && yv2 < 0) {
167      // second in Q4
168      *miny = MIN(y1, y2);
169    }
170    else if (xv2 < 0 && yv2 < 0) {
171      // second in Q3
172      if (x1 >= x2) {
173	*minx = x2;
174	*maxx = x1;
175	*miny = MIN(y1, y2);
176	*maxy = MAX(y1, y2);
177      }
178      else {
179	// xv2, yv2 could all be zero?
180      }
181    }
182  }
183  else if (xv1 > 0 && yv1 < 0) {
184    // first vector in Q2
185    if (xv2 >= 0 && yv2 >= 0) {
186      // second in Q1
187      *maxx = MAX(x1, x2);
188      *minx = MIN(x1, x2);
189      *miny = y1;
190    }
191    else if (xv2 < 0 && yv2 >= 0) {
192      // second in Q2
193      if (x1 < x2) {
194	*maxx = x2;
195	*minx = x1;
196	*miny = MIN(y1, y2);
197	*maxy = MAX(y1, y2);
198      }
199      else {
200	// otherwise almost full circle anyway
201      }
202    }
203    else if (xv2 >= 0 && yv2 < 0) {
204      // second in Q4
205      *miny = y2;
206      *minx = x1;
207    }
208    else if (xv2 < 0 && yv2 < 0) {
209      // second in Q3
210      *minx = MIN(x1, x2);
211    }
212  }
213  else if (xv1 <= 0 && yv1 <= 0) {
214    // first vector in Q1
215    if (xv2 >= 0 && yv2 >= 0) {
216      // second in Q1
217      if (x1 < x2) {
218	*minx = x1;
219	*maxx = x2;
220	*miny = MIN(y1, y2);
221	*maxy = MAX(y1, y2);
222      }
223      else {
224	// nearly full circle
225      }
226    }
227    else if (xv2 < 0 && yv2 >= 0) {
228      // second in Q2
229      *maxy = MAX(y1, y2);
230    }
231    else if (xv2 >= 0 && yv2 < 0) {
232      // second in Q4
233      *miny = MIN(y1, y2);
234      *maxy = MAX(y1, y2);
235      *minx = MIN(x1, x2);
236    }
237    else if (xv2 < 0 && yv2 < 0) {
238      // second in Q3
239      *minx = x2;
240      *maxy = y1;
241    }
242  }
243  else if (xv1 <= 0 && yv1 > 0) {
244    // first vector in Q4
245    if (xv2 >= 0 && yv2 >= 0) {
246      // second in Q1
247      *maxx = MAX(x1, x2);
248    }
249    else if (xv2 < 0 && yv2 >= 0) {
250      // second in Q2
251      *maxy = MAX(y1, y2);
252      *maxx = MAX(x1, x2);
253    }
254    else if (xv2 >= 0 && yv2 < 0) {
255      // second in Q4
256      if (x1 >= x2) {
257	*miny = MIN(y1, y2);
258	*maxy = MAX(y1, y2);
259	*minx = MIN(x1, x2);
260	*maxx = MAX(x2, x2);
261      }
262      else {
263	// nearly full circle
264      }
265    }
266    else if (xv2 < 0 && yv2 < 0) {
267      // second in Q3
268      *maxy = MAX(y1, y2);
269      *minx = MIN(x1, x2);
270      *maxx = MAX(x1, x2);
271    }
272  }
273
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
276  if (*maxx < *minx) {
277    fprintf(stderr, "assert failed *minx > *maxx\n");
278    fflush(stderr);
279    *maxx = *minx;
280  }
281  if (*maxy < *miny) {
282    fprintf(stderr, "assert failed *miny > *maxy\n");
283    fflush(stderr);
284    *maxy = *miny;
285  }
286}
287