1114402Sru// -*- C++ -*-
2151497Sru/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004
3114402Sru   Free Software Foundation, Inc.
4114402Sru     Written by Gaius Mulley <gaius@glam.ac.uk>
5114402Sru     using adjust_arc_center() from printer.cpp, written by James Clark.
6114402Sru
7114402SruThis file is part of groff.
8114402Sru
9114402Srugroff is free software; you can redistribute it and/or modify it under
10114402Sruthe terms of the GNU General Public License as published by the Free
11114402SruSoftware Foundation; either version 2, or (at your option) any later
12114402Sruversion.
13114402Sru
14114402Srugroff is distributed in the hope that it will be useful, but WITHOUT ANY
15114402SruWARRANTY; without even the implied warranty of MERCHANTABILITY or
16114402SruFITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
17114402Srufor more details.
18114402Sru
19114402SruYou should have received a copy of the GNU General Public License along
20114402Sruwith groff; see the file COPYING.  If not, write to the Free Software
21151497SruFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
22114402Sru
23114402Sru
24114402Sru#include <stdio.h>
25114402Sru#include <math.h>
26114402Sru
27114402Sru#undef	MAX
28114402Sru#define MAX(a, b)  (((a) > (b)) ? (a) : (b))
29114402Sru
30114402Sru#undef	MIN
31114402Sru#define MIN(a, b)  (((a) < (b)) ? (a) : (b))
32114402Sru
33114402Sru
34114402Sru// This utility function adjusts the specified center of the
35114402Sru// arc so that it is equidistant between the specified start
36114402Sru// and end points.  (p[0], p[1]) is a vector from the current
37114402Sru// point to the center; (p[2], p[3]) is a vector from the
38114402Sru// center to the end point.  If the center can be adjusted,
39114402Sru// a vector from the current point to the adjusted center is
40114402Sru// stored in c[0], c[1] and 1 is returned.  Otherwise 0 is
41114402Sru// returned.
42114402Sru
43114402Sru#if 1
44114402Sruint adjust_arc_center(const int *p, double *c)
45114402Sru{
46114402Sru  // We move the center along a line parallel to the line between
47114402Sru  // the specified start point and end point so that the center
48114402Sru  // is equidistant between the start and end point.
49114402Sru  // It can be proved (using Lagrange multipliers) that this will
50114402Sru  // give the point nearest to the specified center that is equidistant
51114402Sru  // between the start and end point.
52114402Sru
53114402Sru  double x = p[0] + p[2];	// (x, y) is the end point
54114402Sru  double y = p[1] + p[3];
55114402Sru  double n = x*x + y*y;
56114402Sru  if (n != 0) {
57114402Sru    c[0]= double(p[0]);
58114402Sru    c[1] = double(p[1]);
59114402Sru    double k = .5 - (c[0]*x + c[1]*y)/n;
60114402Sru    c[0] += k*x;
61114402Sru    c[1] += k*y;
62114402Sru    return 1;
63114402Sru  }
64114402Sru  else
65114402Sru    return 0;
66114402Sru}
67114402Sru#else
68114402Sruint printer::adjust_arc_center(const int *p, double *c)
69114402Sru{
70114402Sru  int x = p[0] + p[2];	// (x, y) is the end point
71114402Sru  int y = p[1] + p[3];
72114402Sru  // Start at the current point; go in the direction of the specified
73114402Sru  // center point until we reach a point that is equidistant between
74114402Sru  // the specified starting point and the specified end point.  Place
75114402Sru  // the center of the arc there.
76114402Sru  double n = p[0]*double(x) + p[1]*double(y);
77114402Sru  if (n > 0) {
78114402Sru    double k = (double(x)*x + double(y)*y)/(2.0*n);
79114402Sru    // (cx, cy) is our chosen center
80114402Sru    c[0] = k*p[0];
81114402Sru    c[1] = k*p[1];
82114402Sru    return 1;
83114402Sru  }
84114402Sru  else {
85114402Sru    // We would never reach such a point.  So instead start at the
86114402Sru    // specified end point of the arc.  Go towards the specified
87114402Sru    // center point until we reach a point that is equidistant between
88114402Sru    // the specified start point and specified end point.  Place
89114402Sru    // the center of the arc there.
90114402Sru    n = p[2]*double(x) + p[3]*double(y);
91114402Sru    if (n > 0) {
92114402Sru      double k = 1 - (double(x)*x + double(y)*y)/(2.0*n);
93114402Sru      // (c[0], c[1]) is our chosen center
94114402Sru      c[0] = p[0] + k*p[2];
95114402Sru      c[1] = p[1] + k*p[3];
96114402Sru      return 1;
97114402Sru    }
98114402Sru    else
99114402Sru      return 0;
100114402Sru  }
101114402Sru}
102114402Sru#endif
103114402Sru
104114402Sru
105114402Sru/*
106114402Sru *  check_output_arc_limits - works out the smallest box that will encompass
107114402Sru *                            an arc defined by an origin (x, y) and two
108114402Sru *                            vectors (p0, p1) and (p2, p3).
109114402Sru *                            (x1, y1) -> start of arc
110114402Sru *                            (x1, y1) + (xv1, yv1) -> center of circle
111114402Sru *                            (x1, y1) + (xv1, yv1) + (xv2, yv2) -> end of arc
112114402Sru *
113114402Sru *                            Works out in which quadrant the arc starts and
114114402Sru *                            stops, and from this it determines the x, y
115114402Sru *                            max/min limits.  The arc is drawn clockwise.
116114402Sru */
117114402Sru
118151497Sruvoid check_output_arc_limits(int x_1, int y_1,
119151497Sru			     int xv_1, int yv_1,
120151497Sru			     int xv_2, int yv_2,
121151497Sru			     double c_0, double c_1,
122114402Sru			     int *minx, int *maxx,
123114402Sru			     int *miny, int *maxy)
124114402Sru{
125151497Sru  int radius = (int)sqrt(c_0 * c_0 + c_1 * c_1);
126151497Sru  // clockwise direction
127151497Sru  int xcenter = x_1 + xv_1;
128151497Sru  int ycenter = y_1 + yv_1;
129151497Sru  int xend = xcenter + xv_2;
130151497Sru  int yend = ycenter + yv_2;
131151497Sru  // for convenience, transform to counterclockwise direction,
132151497Sru  // centered at the origin
133151497Sru  int xs = xend - xcenter;
134151497Sru  int ys = yend - ycenter;
135151497Sru  int xe = x_1 - xcenter;
136151497Sru  int ye = y_1 - ycenter;
137151497Sru  *minx = *maxx = xs;
138151497Sru  *miny = *maxy = ys;
139151497Sru  if (xe > *maxx)
140151497Sru    *maxx = xe;
141151497Sru  else if (xe < *minx)
142151497Sru    *minx = xe;
143151497Sru  if (ye > *maxy)
144151497Sru    *maxy = ye;
145151497Sru  else if (ye < *miny)
146151497Sru    *miny = ye;
147151497Sru  int qs, qe;			// quadrants 0..3
148151497Sru  if (xs >= 0)
149151497Sru    qs = (ys >= 0) ? 0 : 3;
150151497Sru  else
151151497Sru    qs = (ys >= 0) ? 1 : 2;
152151497Sru  if (xe >= 0)
153151497Sru    qe = (ye >= 0) ? 0 : 3;
154151497Sru  else
155151497Sru    qe = (ye >= 0) ? 1 : 2;
156151497Sru  // make qs always smaller than qe
157151497Sru  if ((qs > qe)
158151497Sru      || ((qs == qe) && (double(xs) * ye < double(xe) * ys)))
159151497Sru    qe += 4;
160151497Sru  for (int i = qs; i < qe; i++)
161151497Sru    switch (i % 4) {
162151497Sru    case 0:
163151497Sru      *maxy = radius;
164151497Sru      break;
165151497Sru    case 1:
166151497Sru      *minx = -radius;
167151497Sru      break;
168151497Sru    case 2:
169151497Sru      *miny = -radius;
170151497Sru      break;
171151497Sru    case 3:
172151497Sru      *maxx = radius;
173151497Sru      break;
174114402Sru    }
175151497Sru  *minx += xcenter;
176151497Sru  *maxx += xcenter;
177151497Sru  *miny += ycenter;
178151497Sru  *maxy += ycenter;
179114402Sru}
180