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