1 /* $NetBSD: geometry.cpp,v 1.1.1.1 2016/01/13 18:41:48 christos Exp $ */
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
9 This file is part of groff.
10
11 groff is free software; you can redistribute it and/or modify it under
12 the terms of the GNU General Public License as published by the Free
13 Software Foundation; either version 2, or (at your option) any later
14 version.
15
16 groff is distributed in the hope that it will be useful, but WITHOUT ANY
17 WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19 for more details.
20
21 You should have received a copy of the GNU General Public License along
22 with groff; see the file COPYING. If not, write to the Free Software
23 Foundation, 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
adjust_arc_center(const int * p,double * c)46 int 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
adjust_arc_center(const int * p,double * c)70 int 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
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 void 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