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