* LICENSE: Better wording.
[s-roff.git] / src / libs / libgroff / geometry.cpp
blob2bb89e415d5f33708fc82d51cf459858b401523b
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.
7 This file is part of groff.
9 groff is free software; you can redistribute it and/or modify it under
10 the terms of the GNU General Public License as published by the Free
11 Software Foundation; either version 2, or (at your option) any later
12 version.
14 groff is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 for more details.
19 You should have received a copy of the GNU General Public License along
20 with groff; see the file COPYING. If not, write to the Free Software
21 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
24 #include <stdio.h>
25 #include <math.h>
27 #undef MAX
28 #define MAX(a, b) (((a) > (b)) ? (a) : (b))
30 #undef MIN
31 #define MIN(a, b) (((a) < (b)) ? (a) : (b))
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.
43 #if 1
44 int adjust_arc_center(const int *p, double *c)
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.
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;
64 else
65 return 0;
67 #else
68 int printer::adjust_arc_center(const int *p, double *c)
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;
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;
98 else
99 return 0;
102 #endif
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
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.
118 void check_output_arc_limits(int x1, int y1,
119 int xv1, int yv1,
120 int xv2, int yv2,
121 double c0, double c1,
122 int *minx, int *maxx,
123 int *miny, int *maxy)
125 int radius = (int)sqrt(c0 * c0 + c1 * c1);
126 // clockwise direction
127 int xcenter = x1 + xv1;
128 int ycenter = y1 + yv1;
129 int xend = xcenter + xv2;
130 int yend = ycenter + yv2;
131 // for convenience, transform to counterclockwise direction,
132 // centered at the origin
133 int xs = xend - xcenter;
134 int ys = yend - ycenter;
135 int xe = x1 - xcenter;
136 int ye = y1 - ycenter;
137 *minx = *maxx = xs;
138 *miny = *maxy = ys;
139 if (xe > *maxx)
140 *maxx = xe;
141 else if (xe < *minx)
142 *minx = xe;
143 if (ye > *maxy)
144 *maxy = ye;
145 else if (ye < *miny)
146 *miny = ye;
147 int qs, qe; // quadrants 0..3
148 if (xs >= 0)
149 qs = (ys >= 0) ? 0 : 3;
150 else
151 qs = (ys >= 0) ? 1 : 2;
152 if (xe >= 0)
153 qe = (ye >= 0) ? 0 : 3;
154 else
155 qe = (ye >= 0) ? 1 : 2;
156 // make qs always smaller than qe
157 if ((qs > qe)
158 || ((qs == qe) && (double(xs) * ye < double(xe) * ys)))
159 qe += 4;
160 for (int i = qs; i < qe; i++)
161 switch (i % 4) {
162 case 0:
163 *maxy = radius;
164 break;
165 case 1:
166 *minx = -radius;
167 break;
168 case 2:
169 *miny = -radius;
170 break;
171 case 3:
172 *maxx = radius;
173 break;
175 *minx += xcenter;
176 *maxx += xcenter;
177 *miny += ycenter;
178 *maxy += ycenter;