Home | History | Annotate | Line # | Download | only in libgroff
      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 (at) 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
     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
     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 
    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