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