1114402Sru// -*- C++ -*- 2151497Sru/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004 3114402Sru Free Software Foundation, Inc. 4114402Sru Written by Gaius Mulley <gaius@glam.ac.uk> 5114402Sru using adjust_arc_center() from printer.cpp, written by James Clark. 6114402Sru 7114402SruThis file is part of groff. 8114402Sru 9114402Srugroff is free software; you can redistribute it and/or modify it under 10114402Sruthe terms of the GNU General Public License as published by the Free 11114402SruSoftware Foundation; either version 2, or (at your option) any later 12114402Sruversion. 13114402Sru 14114402Srugroff is distributed in the hope that it will be useful, but WITHOUT ANY 15114402SruWARRANTY; without even the implied warranty of MERCHANTABILITY or 16114402SruFITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 17114402Srufor more details. 18114402Sru 19114402SruYou should have received a copy of the GNU General Public License along 20114402Sruwith groff; see the file COPYING. If not, write to the Free Software 21151497SruFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */ 22114402Sru 23114402Sru 24114402Sru#include <stdio.h> 25114402Sru#include <math.h> 26114402Sru 27114402Sru#undef MAX 28114402Sru#define MAX(a, b) (((a) > (b)) ? (a) : (b)) 29114402Sru 30114402Sru#undef MIN 31114402Sru#define MIN(a, b) (((a) < (b)) ? (a) : (b)) 32114402Sru 33114402Sru 34114402Sru// This utility function adjusts the specified center of the 35114402Sru// arc so that it is equidistant between the specified start 36114402Sru// and end points. (p[0], p[1]) is a vector from the current 37114402Sru// point to the center; (p[2], p[3]) is a vector from the 38114402Sru// center to the end point. If the center can be adjusted, 39114402Sru// a vector from the current point to the adjusted center is 40114402Sru// stored in c[0], c[1] and 1 is returned. Otherwise 0 is 41114402Sru// returned. 42114402Sru 43114402Sru#if 1 44114402Sruint adjust_arc_center(const int *p, double *c) 45114402Sru{ 46114402Sru // We move the center along a line parallel to the line between 47114402Sru // the specified start point and end point so that the center 48114402Sru // is equidistant between the start and end point. 49114402Sru // It can be proved (using Lagrange multipliers) that this will 50114402Sru // give the point nearest to the specified center that is equidistant 51114402Sru // between the start and end point. 52114402Sru 53114402Sru double x = p[0] + p[2]; // (x, y) is the end point 54114402Sru double y = p[1] + p[3]; 55114402Sru double n = x*x + y*y; 56114402Sru if (n != 0) { 57114402Sru c[0]= double(p[0]); 58114402Sru c[1] = double(p[1]); 59114402Sru double k = .5 - (c[0]*x + c[1]*y)/n; 60114402Sru c[0] += k*x; 61114402Sru c[1] += k*y; 62114402Sru return 1; 63114402Sru } 64114402Sru else 65114402Sru return 0; 66114402Sru} 67114402Sru#else 68114402Sruint printer::adjust_arc_center(const int *p, double *c) 69114402Sru{ 70114402Sru int x = p[0] + p[2]; // (x, y) is the end point 71114402Sru int y = p[1] + p[3]; 72114402Sru // Start at the current point; go in the direction of the specified 73114402Sru // center point until we reach a point that is equidistant between 74114402Sru // the specified starting point and the specified end point. Place 75114402Sru // the center of the arc there. 76114402Sru double n = p[0]*double(x) + p[1]*double(y); 77114402Sru if (n > 0) { 78114402Sru double k = (double(x)*x + double(y)*y)/(2.0*n); 79114402Sru // (cx, cy) is our chosen center 80114402Sru c[0] = k*p[0]; 81114402Sru c[1] = k*p[1]; 82114402Sru return 1; 83114402Sru } 84114402Sru else { 85114402Sru // We would never reach such a point. So instead start at the 86114402Sru // specified end point of the arc. Go towards the specified 87114402Sru // center point until we reach a point that is equidistant between 88114402Sru // the specified start point and specified end point. Place 89114402Sru // the center of the arc there. 90114402Sru n = p[2]*double(x) + p[3]*double(y); 91114402Sru if (n > 0) { 92114402Sru double k = 1 - (double(x)*x + double(y)*y)/(2.0*n); 93114402Sru // (c[0], c[1]) is our chosen center 94114402Sru c[0] = p[0] + k*p[2]; 95114402Sru c[1] = p[1] + k*p[3]; 96114402Sru return 1; 97114402Sru } 98114402Sru else 99114402Sru return 0; 100114402Sru } 101114402Sru} 102114402Sru#endif 103114402Sru 104114402Sru 105114402Sru/* 106114402Sru * check_output_arc_limits - works out the smallest box that will encompass 107114402Sru * an arc defined by an origin (x, y) and two 108114402Sru * vectors (p0, p1) and (p2, p3). 109114402Sru * (x1, y1) -> start of arc 110114402Sru * (x1, y1) + (xv1, yv1) -> center of circle 111114402Sru * (x1, y1) + (xv1, yv1) + (xv2, yv2) -> end of arc 112114402Sru * 113114402Sru * Works out in which quadrant the arc starts and 114114402Sru * stops, and from this it determines the x, y 115114402Sru * max/min limits. The arc is drawn clockwise. 116114402Sru */ 117114402Sru 118151497Sruvoid check_output_arc_limits(int x_1, int y_1, 119151497Sru int xv_1, int yv_1, 120151497Sru int xv_2, int yv_2, 121151497Sru double c_0, double c_1, 122114402Sru int *minx, int *maxx, 123114402Sru int *miny, int *maxy) 124114402Sru{ 125151497Sru int radius = (int)sqrt(c_0 * c_0 + c_1 * c_1); 126151497Sru // clockwise direction 127151497Sru int xcenter = x_1 + xv_1; 128151497Sru int ycenter = y_1 + yv_1; 129151497Sru int xend = xcenter + xv_2; 130151497Sru int yend = ycenter + yv_2; 131151497Sru // for convenience, transform to counterclockwise direction, 132151497Sru // centered at the origin 133151497Sru int xs = xend - xcenter; 134151497Sru int ys = yend - ycenter; 135151497Sru int xe = x_1 - xcenter; 136151497Sru int ye = y_1 - ycenter; 137151497Sru *minx = *maxx = xs; 138151497Sru *miny = *maxy = ys; 139151497Sru if (xe > *maxx) 140151497Sru *maxx = xe; 141151497Sru else if (xe < *minx) 142151497Sru *minx = xe; 143151497Sru if (ye > *maxy) 144151497Sru *maxy = ye; 145151497Sru else if (ye < *miny) 146151497Sru *miny = ye; 147151497Sru int qs, qe; // quadrants 0..3 148151497Sru if (xs >= 0) 149151497Sru qs = (ys >= 0) ? 0 : 3; 150151497Sru else 151151497Sru qs = (ys >= 0) ? 1 : 2; 152151497Sru if (xe >= 0) 153151497Sru qe = (ye >= 0) ? 0 : 3; 154151497Sru else 155151497Sru qe = (ye >= 0) ? 1 : 2; 156151497Sru // make qs always smaller than qe 157151497Sru if ((qs > qe) 158151497Sru || ((qs == qe) && (double(xs) * ye < double(xe) * ys))) 159151497Sru qe += 4; 160151497Sru for (int i = qs; i < qe; i++) 161151497Sru switch (i % 4) { 162151497Sru case 0: 163151497Sru *maxy = radius; 164151497Sru break; 165151497Sru case 1: 166151497Sru *minx = -radius; 167151497Sru break; 168151497Sru case 2: 169151497Sru *miny = -radius; 170151497Sru break; 171151497Sru case 3: 172151497Sru *maxx = radius; 173151497Sru break; 174114402Sru } 175151497Sru *minx += xcenter; 176151497Sru *maxx += xcenter; 177151497Sru *miny += ycenter; 178151497Sru *maxy += ycenter; 179114402Sru} 180