1// -*- C++ -*- 2/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004 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. 6 7This file is part of groff. 8 9groff is free software; you can redistribute it and/or modify it under 10the terms of the GNU General Public License as published by the Free 11Software Foundation; either version 2, or (at your option) any later 12version. 13 14groff is distributed in the hope that it will be useful, but WITHOUT ANY 15WARRANTY; without even the implied warranty of MERCHANTABILITY or 16FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 17for more details. 18 19You should have received a copy of the GNU General Public License along 20with groff; see the file COPYING. If not, write to the Free Software 21Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */ 22 23 24#include <stdio.h> 25#include <math.h> 26 27#undef MAX 28#define MAX(a, b) (((a) > (b)) ? (a) : (b)) 29 30#undef MIN 31#define MIN(a, b) (((a) < (b)) ? (a) : (b)) 32 33 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. 42 43#if 1 44int adjust_arc_center(const int *p, double *c) 45{ 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. 52 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; 63 } 64 else 65 return 0; 66} 67#else 68int printer::adjust_arc_center(const int *p, double *c) 69{ 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; 83 } 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; 97 } 98 else 99 return 0; 100 } 101} 102#endif 103 104 105/* 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 112 * 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. 116 */ 117 118void check_output_arc_limits(int x_1, int y_1, 119 int xv_1, int yv_1, 120 int xv_2, int yv_2, 121 double c_0, double c_1, 122 int *minx, int *maxx, 123 int *miny, int *maxy) 124{ 125 int radius = (int)sqrt(c_0 * c_0 + c_1 * c_1); 126 // clockwise direction 127 int xcenter = x_1 + xv_1; 128 int ycenter = y_1 + yv_1; 129 int xend = xcenter + xv_2; 130 int yend = ycenter + yv_2; 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 = x_1 - xcenter; 136 int ye = y_1 - 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; 174 } 175 *minx += xcenter; 176 *maxx += xcenter; 177 *miny += ycenter; 178 *maxy += ycenter; 179} 180