tkTrig.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:40k
源码类别:

通讯编程

开发平台:

Visual C++

  1. /* 
  2.  * tkTrig.c --
  3.  *
  4.  * This file contains a collection of trigonometry utility
  5.  * routines that are used by Tk and in particular by the
  6.  * canvas code.  It also has miscellaneous geometry functions
  7.  * used by canvases.
  8.  *
  9.  * Copyright (c) 1992-1994 The Regents of the University of California.
  10.  * Copyright (c) 1994-1997 Sun Microsystems, Inc.
  11.  *
  12.  * See the file "license.terms" for information on usage and redistribution
  13.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  14.  *
  15.  * RCS: @(#) $Id: tkTrig.c,v 1.4 1999/12/14 06:52:33 hobbs Exp $
  16.  */
  17. #include <stdio.h>
  18. #include "tkInt.h"
  19. #include "tkPort.h"
  20. #include "tkCanvas.h"
  21. #undef MIN
  22. #define MIN(a,b) (((a) < (b)) ? (a) : (b))
  23. #undef MAX
  24. #define MAX(a,b) (((a) > (b)) ? (a) : (b))
  25. #ifndef PI
  26. #   define PI 3.14159265358979323846
  27. #endif /* PI */
  28. /*
  29.  *--------------------------------------------------------------
  30.  *
  31.  * TkLineToPoint --
  32.  *
  33.  * Compute the distance from a point to a finite line segment.
  34.  *
  35.  * Results:
  36.  * The return value is the distance from the line segment
  37.  * whose end-points are *end1Ptr and *end2Ptr to the point
  38.  * given by *pointPtr.
  39.  *
  40.  * Side effects:
  41.  * None.
  42.  *
  43.  *--------------------------------------------------------------
  44.  */
  45. double
  46. TkLineToPoint(end1Ptr, end2Ptr, pointPtr)
  47.     double end1Ptr[2]; /* Coordinates of first end-point of line. */
  48.     double end2Ptr[2]; /* Coordinates of second end-point of line. */
  49.     double pointPtr[2]; /* Points to coords for point. */
  50. {
  51.     double x, y;
  52.     /*
  53.      * Compute the point on the line that is closest to the
  54.      * point.  This must be done separately for vertical edges,
  55.      * horizontal edges, and other edges.
  56.      */
  57.     if (end1Ptr[0] == end2Ptr[0]) {
  58. /*
  59.  * Vertical edge.
  60.  */
  61. x = end1Ptr[0];
  62. if (end1Ptr[1] >= end2Ptr[1]) {
  63.     y = MIN(end1Ptr[1], pointPtr[1]);
  64.     y = MAX(y, end2Ptr[1]);
  65. } else {
  66.     y = MIN(end2Ptr[1], pointPtr[1]);
  67.     y = MAX(y, end1Ptr[1]);
  68. }
  69.     } else if (end1Ptr[1] == end2Ptr[1]) {
  70. /*
  71.  * Horizontal edge.
  72.  */
  73. y = end1Ptr[1];
  74. if (end1Ptr[0] >= end2Ptr[0]) {
  75.     x = MIN(end1Ptr[0], pointPtr[0]);
  76.     x = MAX(x, end2Ptr[0]);
  77. } else {
  78.     x = MIN(end2Ptr[0], pointPtr[0]);
  79.     x = MAX(x, end1Ptr[0]);
  80. }
  81.     } else {
  82. double m1, b1, m2, b2;
  83. /*
  84.  * The edge is neither horizontal nor vertical.  Convert the
  85.  * edge to a line equation of the form y = m1*x + b1.  Then
  86.  * compute a line perpendicular to this edge but passing
  87.  * through the point, also in the form y = m2*x + b2.
  88.  */
  89. m1 = (end2Ptr[1] - end1Ptr[1])/(end2Ptr[0] - end1Ptr[0]);
  90. b1 = end1Ptr[1] - m1*end1Ptr[0];
  91. m2 = -1.0/m1;
  92. b2 = pointPtr[1] - m2*pointPtr[0];
  93. x = (b2 - b1)/(m1 - m2);
  94. y = m1*x + b1;
  95. if (end1Ptr[0] > end2Ptr[0]) {
  96.     if (x > end1Ptr[0]) {
  97. x = end1Ptr[0];
  98. y = end1Ptr[1];
  99.     } else if (x < end2Ptr[0]) {
  100. x = end2Ptr[0];
  101. y = end2Ptr[1];
  102.     }
  103. } else {
  104.     if (x > end2Ptr[0]) {
  105. x = end2Ptr[0];
  106. y = end2Ptr[1];
  107.     } else if (x < end1Ptr[0]) {
  108. x = end1Ptr[0];
  109. y = end1Ptr[1];
  110.     }
  111. }
  112.     }
  113.     /*
  114.      * Compute the distance to the closest point.
  115.      */
  116.     return hypot(pointPtr[0] - x, pointPtr[1] - y);
  117. }
  118. /*
  119.  *--------------------------------------------------------------
  120.  *
  121.  * TkLineToArea --
  122.  *
  123.  * Determine whether a line lies entirely inside, entirely
  124.  * outside, or overlapping a given rectangular area.
  125.  *
  126.  * Results:
  127.  * -1 is returned if the line given by end1Ptr and end2Ptr
  128.  * is entirely outside the rectangle given by rectPtr.  0 is
  129.  * returned if the polygon overlaps the rectangle, and 1 is
  130.  * returned if the polygon is entirely inside the rectangle.
  131.  *
  132.  * Side effects:
  133.  * None.
  134.  *
  135.  *--------------------------------------------------------------
  136.  */
  137. int
  138. TkLineToArea(end1Ptr, end2Ptr, rectPtr)
  139.     double end1Ptr[2]; /* X and y coordinates for one endpoint
  140.  * of line. */
  141.     double end2Ptr[2]; /* X and y coordinates for other endpoint
  142.  * of line. */
  143.     double rectPtr[4]; /* Points to coords for rectangle, in the
  144.  * order x1, y1, x2, y2.  X1 must be no
  145.  * larger than x2, and y1 no larger than y2. */
  146. {
  147.     int inside1, inside2;
  148.     /*
  149.      * First check the two points individually to see whether they
  150.      * are inside the rectangle or not.
  151.      */
  152.     inside1 = (end1Ptr[0] >= rectPtr[0]) && (end1Ptr[0] <= rectPtr[2])
  153.     && (end1Ptr[1] >= rectPtr[1]) && (end1Ptr[1] <= rectPtr[3]);
  154.     inside2 = (end2Ptr[0] >= rectPtr[0]) && (end2Ptr[0] <= rectPtr[2])
  155.     && (end2Ptr[1] >= rectPtr[1]) && (end2Ptr[1] <= rectPtr[3]);
  156.     if (inside1 != inside2) {
  157. return 0;
  158.     }
  159.     if (inside1 & inside2) {
  160. return 1;
  161.     }
  162.     /*
  163.      * Both points are outside the rectangle, but still need to check
  164.      * for intersections between the line and the rectangle.  Horizontal
  165.      * and vertical lines are particularly easy, so handle them
  166.      * separately.
  167.      */
  168.     if (end1Ptr[0] == end2Ptr[0]) {
  169. /*
  170.  * Vertical line.
  171.  */
  172.     
  173. if (((end1Ptr[1] >= rectPtr[1]) ^ (end2Ptr[1] >= rectPtr[1]))
  174. && (end1Ptr[0] >= rectPtr[0])
  175. && (end1Ptr[0] <= rectPtr[2])) {
  176.     return 0;
  177. }
  178.     } else if (end1Ptr[1] == end2Ptr[1]) {
  179. /*
  180.  * Horizontal line.
  181.  */
  182.     
  183. if (((end1Ptr[0] >= rectPtr[0]) ^ (end2Ptr[0] >= rectPtr[0]))
  184. && (end1Ptr[1] >= rectPtr[1])
  185. && (end1Ptr[1] <= rectPtr[3])) {
  186.     return 0;
  187. }
  188.     } else {
  189. double m, x, y, low, high;
  190.     
  191. /*
  192.  * Diagonal line.  Compute slope of line and use
  193.  * for intersection checks against each of the
  194.  * sides of the rectangle: left, right, bottom, top.
  195.  */
  196.     
  197. m = (end2Ptr[1] - end1Ptr[1])/(end2Ptr[0] - end1Ptr[0]);
  198. if (end1Ptr[0] < end2Ptr[0]) {
  199.     low = end1Ptr[0];  high = end2Ptr[0];
  200. } else {
  201.     low = end2Ptr[0]; high = end1Ptr[0];
  202. }
  203.     
  204. /*
  205.  * Left edge.
  206.  */
  207.     
  208. y = end1Ptr[1] + (rectPtr[0] - end1Ptr[0])*m;
  209. if ((rectPtr[0] >= low) && (rectPtr[0] <= high)
  210. && (y >= rectPtr[1]) && (y <= rectPtr[3])) {
  211.     return 0;
  212. }
  213.     
  214. /*
  215.  * Right edge.
  216.  */
  217.     
  218. y += (rectPtr[2] - rectPtr[0])*m;
  219. if ((y >= rectPtr[1]) && (y <= rectPtr[3])
  220. && (rectPtr[2] >= low) && (rectPtr[2] <= high)) {
  221.     return 0;
  222. }
  223.     
  224. /*
  225.  * Bottom edge.
  226.  */
  227.     
  228. if (end1Ptr[1] < end2Ptr[1]) {
  229.     low = end1Ptr[1];  high = end2Ptr[1];
  230. } else {
  231.     low = end2Ptr[1]; high = end1Ptr[1];
  232. }
  233. x = end1Ptr[0] + (rectPtr[1] - end1Ptr[1])/m;
  234. if ((x >= rectPtr[0]) && (x <= rectPtr[2])
  235. && (rectPtr[1] >= low) && (rectPtr[1] <= high)) {
  236.     return 0;
  237. }
  238.     
  239. /*
  240.  * Top edge.
  241.  */
  242.     
  243. x += (rectPtr[3] - rectPtr[1])/m;
  244. if ((x >= rectPtr[0]) && (x <= rectPtr[2])
  245. && (rectPtr[3] >= low) && (rectPtr[3] <= high)) {
  246.     return 0;
  247. }
  248.     }
  249.     return -1;
  250. }
  251. /*
  252.  *--------------------------------------------------------------
  253.  *
  254.  * TkThickPolyLineToArea --
  255.  *
  256.  * This procedure is called to determine whether a connected
  257.  * series of line segments lies entirely inside, entirely
  258.  * outside, or overlapping a given rectangular area.
  259.  *
  260.  * Results:
  261.  * -1 is returned if the lines are entirely outside the area,
  262.  * 0 if they overlap, and 1 if they are entirely inside the
  263.  * given area.
  264.  *
  265.  * Side effects:
  266.  * None.
  267.  *
  268.  *--------------------------------------------------------------
  269.  */
  270. /* ARGSUSED */
  271. int
  272. TkThickPolyLineToArea(coordPtr, numPoints, width, capStyle, joinStyle, rectPtr)
  273.     double *coordPtr; /* Points to an array of coordinates for
  274.  * the polyline:  x0, y0, x1, y1, ... */
  275.     int numPoints; /* Total number of points at *coordPtr. */
  276.     double width; /* Width of each line segment. */
  277.     int capStyle; /* How are end-points of polyline drawn?
  278.  * CapRound, CapButt, or CapProjecting. */
  279.     int joinStyle; /* How are joints in polyline drawn?
  280.  * JoinMiter, JoinRound, or JoinBevel. */
  281.     double *rectPtr; /* Rectangular area to check against. */
  282. {
  283.     double radius, poly[10];
  284.     int count;
  285.     int changedMiterToBevel; /* Non-zero means that a mitered corner
  286.  * had to be treated as beveled after all
  287.  * because the angle was < 11 degrees. */
  288.     int inside; /* Tentative guess about what to return,
  289.  * based on all points seen so far:  one
  290.  * means everything seen so far was
  291.  * inside the area;  -1 means everything
  292.  * was outside the area.  0 means overlap
  293.  * has been found. */ 
  294.     radius = width/2.0;
  295.     inside = -1;
  296.     if ((coordPtr[0] >= rectPtr[0]) && (coordPtr[0] <= rectPtr[2])
  297.     && (coordPtr[1] >= rectPtr[1]) && (coordPtr[1] <= rectPtr[3])) {
  298. inside = 1;
  299.     }
  300.     /*
  301.      * Iterate through all of the edges of the line, computing a polygon
  302.      * for each edge and testing the area against that polygon.  In
  303.      * addition, there are additional tests to deal with rounded joints
  304.      * and caps.
  305.      */
  306.     changedMiterToBevel = 0;
  307.     for (count = numPoints; count >= 2; count--, coordPtr += 2) {
  308. /*
  309.  * If rounding is done around the first point of the edge
  310.  * then test a circular region around the point with the
  311.  * area.
  312.  */
  313. if (((capStyle == CapRound) && (count == numPoints))
  314. || ((joinStyle == JoinRound) && (count != numPoints))) {
  315.     poly[0] = coordPtr[0] - radius;
  316.     poly[1] = coordPtr[1] - radius;
  317.     poly[2] = coordPtr[0] + radius;
  318.     poly[3] = coordPtr[1] + radius;
  319.     if (TkOvalToArea(poly, rectPtr) != inside) {
  320. return 0;
  321.     }
  322. }
  323. /*
  324.  * Compute the polygonal shape corresponding to this edge,
  325.  * consisting of two points for the first point of the edge
  326.  * and two points for the last point of the edge.
  327.  */
  328. if (count == numPoints) {
  329.     TkGetButtPoints(coordPtr+2, coordPtr, width,
  330.     capStyle == CapProjecting, poly, poly+2);
  331. } else if ((joinStyle == JoinMiter) && !changedMiterToBevel) {
  332.     poly[0] = poly[6];
  333.     poly[1] = poly[7];
  334.     poly[2] = poly[4];
  335.     poly[3] = poly[5];
  336. } else {
  337.     TkGetButtPoints(coordPtr+2, coordPtr, width, 0, poly, poly+2);
  338.     /*
  339.      * If the last joint was beveled, then also check a
  340.      * polygon comprising the last two points of the previous
  341.      * polygon and the first two from this polygon;  this checks
  342.      * the wedges that fill the beveled joint.
  343.      */
  344.     if ((joinStyle == JoinBevel) || changedMiterToBevel) {
  345. poly[8] = poly[0];
  346. poly[9] = poly[1];
  347. if (TkPolygonToArea(poly, 5, rectPtr) != inside) {
  348.     return 0;
  349. }
  350. changedMiterToBevel = 0;
  351.     }
  352. }
  353. if (count == 2) {
  354.     TkGetButtPoints(coordPtr, coordPtr+2, width,
  355.     capStyle == CapProjecting, poly+4, poly+6);
  356. } else if (joinStyle == JoinMiter) {
  357.     if (TkGetMiterPoints(coordPtr, coordPtr+2, coordPtr+4,
  358.     (double) width, poly+4, poly+6) == 0) {
  359. changedMiterToBevel = 1;
  360. TkGetButtPoints(coordPtr, coordPtr+2, width, 0, poly+4,
  361. poly+6);
  362.     }
  363. } else {
  364.     TkGetButtPoints(coordPtr, coordPtr+2, width, 0, poly+4, poly+6);
  365. }
  366. poly[8] = poly[0];
  367. poly[9] = poly[1];
  368. if (TkPolygonToArea(poly, 5, rectPtr) != inside) {
  369.     return 0;
  370. }
  371.     }
  372.     /*
  373.      * If caps are rounded, check the cap around the final point
  374.      * of the line.
  375.      */
  376.     if (capStyle == CapRound) {
  377. poly[0] = coordPtr[0] - radius;
  378. poly[1] = coordPtr[1] - radius;
  379. poly[2] = coordPtr[0] + radius;
  380. poly[3] = coordPtr[1] + radius;
  381. if (TkOvalToArea(poly, rectPtr) != inside) {
  382.     return 0;
  383. }
  384.     }
  385.     return inside;
  386. }
  387. /*
  388.  *--------------------------------------------------------------
  389.  *
  390.  * TkPolygonToPoint --
  391.  *
  392.  * Compute the distance from a point to a polygon.
  393.  *
  394.  * Results:
  395.  * The return value is 0.0 if the point referred to by
  396.  * pointPtr is within the polygon referred to by polyPtr
  397.  * and numPoints.  Otherwise the return value is the
  398.  * distance of the point from the polygon.
  399.  *
  400.  * Side effects:
  401.  * None.
  402.  *
  403.  *--------------------------------------------------------------
  404.  */
  405. double
  406. TkPolygonToPoint(polyPtr, numPoints, pointPtr)
  407.     double *polyPtr; /* Points to an array coordinates for
  408.  * closed polygon:  x0, y0, x1, y1, ...
  409.  * The polygon may be self-intersecting. */
  410.     int numPoints; /* Total number of points at *polyPtr. */
  411.     double *pointPtr; /* Points to coords for point. */
  412. {
  413.     double bestDist; /* Closest distance between point and
  414.  * any edge in polygon. */
  415.     int intersections; /* Number of edges in the polygon that
  416.  * intersect a ray extending vertically
  417.  * upwards from the point to infinity. */
  418.     int count;
  419.     register double *pPtr;
  420.     /*
  421.      * Iterate through all of the edges in the polygon, updating
  422.      * bestDist and intersections.
  423.      *
  424.      * TRICKY POINT:  when computing intersections, include left
  425.      * x-coordinate of line within its range, but not y-coordinate.
  426.      * Otherwise if the point lies exactly below a vertex we'll
  427.      * count it as two intersections.
  428.      */
  429.     bestDist = 1.0e36;
  430.     intersections = 0;
  431.     for (count = numPoints, pPtr = polyPtr; count > 1; count--, pPtr += 2) {
  432. double x, y, dist;
  433. /*
  434.  * Compute the point on the current edge closest to the point
  435.  * and update the intersection count.  This must be done
  436.  * separately for vertical edges, horizontal edges, and
  437.  * other edges.
  438.  */
  439. if (pPtr[2] == pPtr[0]) {
  440.     /*
  441.      * Vertical edge.
  442.      */
  443.     x = pPtr[0];
  444.     if (pPtr[1] >= pPtr[3]) {
  445. y = MIN(pPtr[1], pointPtr[1]);
  446. y = MAX(y, pPtr[3]);
  447.     } else {
  448. y = MIN(pPtr[3], pointPtr[1]);
  449. y = MAX(y, pPtr[1]);
  450.     }
  451. } else if (pPtr[3] == pPtr[1]) {
  452.     /*
  453.      * Horizontal edge.
  454.      */
  455.     y = pPtr[1];
  456.     if (pPtr[0] >= pPtr[2]) {
  457. x = MIN(pPtr[0], pointPtr[0]);
  458. x = MAX(x, pPtr[2]);
  459. if ((pointPtr[1] < y) && (pointPtr[0] < pPtr[0])
  460. && (pointPtr[0] >= pPtr[2])) {
  461.     intersections++;
  462. }
  463.     } else {
  464. x = MIN(pPtr[2], pointPtr[0]);
  465. x = MAX(x, pPtr[0]);
  466. if ((pointPtr[1] < y) && (pointPtr[0] < pPtr[2])
  467. && (pointPtr[0] >= pPtr[0])) {
  468.     intersections++;
  469. }
  470.     }
  471. } else {
  472.     double m1, b1, m2, b2;
  473.     int lower; /* Non-zero means point below line. */
  474.     /*
  475.      * The edge is neither horizontal nor vertical.  Convert the
  476.      * edge to a line equation of the form y = m1*x + b1.  Then
  477.      * compute a line perpendicular to this edge but passing
  478.      * through the point, also in the form y = m2*x + b2.
  479.      */
  480.     m1 = (pPtr[3] - pPtr[1])/(pPtr[2] - pPtr[0]);
  481.     b1 = pPtr[1] - m1*pPtr[0];
  482.     m2 = -1.0/m1;
  483.     b2 = pointPtr[1] - m2*pointPtr[0];
  484.     x = (b2 - b1)/(m1 - m2);
  485.     y = m1*x + b1;
  486.     if (pPtr[0] > pPtr[2]) {
  487. if (x > pPtr[0]) {
  488.     x = pPtr[0];
  489.     y = pPtr[1];
  490. } else if (x < pPtr[2]) {
  491.     x = pPtr[2];
  492.     y = pPtr[3];
  493. }
  494.     } else {
  495. if (x > pPtr[2]) {
  496.     x = pPtr[2];
  497.     y = pPtr[3];
  498. } else if (x < pPtr[0]) {
  499.     x = pPtr[0];
  500.     y = pPtr[1];
  501. }
  502.     }
  503.     lower = (m1*pointPtr[0] + b1) > pointPtr[1];
  504.     if (lower && (pointPtr[0] >= MIN(pPtr[0], pPtr[2]))
  505.     && (pointPtr[0] < MAX(pPtr[0], pPtr[2]))) {
  506. intersections++;
  507.     }
  508. }
  509. /*
  510.  * Compute the distance to the closest point, and see if that
  511.  * is the best distance seen so far.
  512.  */
  513. dist = hypot(pointPtr[0] - x, pointPtr[1] - y);
  514. if (dist < bestDist) {
  515.     bestDist = dist;
  516. }
  517.     }
  518.     /*
  519.      * We've processed all of the points.  If the number of intersections
  520.      * is odd, the point is inside the polygon.
  521.      */
  522.     if (intersections & 0x1) {
  523. return 0.0;
  524.     }
  525.     return bestDist;
  526. }
  527. /*
  528.  *--------------------------------------------------------------
  529.  *
  530.  * TkPolygonToArea --
  531.  *
  532.  * Determine whether a polygon lies entirely inside, entirely
  533.  * outside, or overlapping a given rectangular area.
  534.  *
  535.  * Results:
  536.  * -1 is returned if the polygon given by polyPtr and numPoints
  537.  * is entirely outside the rectangle given by rectPtr.  0 is
  538.  * returned if the polygon overlaps the rectangle, and 1 is
  539.  * returned if the polygon is entirely inside the rectangle.
  540.  *
  541.  * Side effects:
  542.  * None.
  543.  *
  544.  *--------------------------------------------------------------
  545.  */
  546. int
  547. TkPolygonToArea(polyPtr, numPoints, rectPtr)
  548.     double *polyPtr; /* Points to an array coordinates for
  549.  * closed polygon:  x0, y0, x1, y1, ...
  550.  * The polygon may be self-intersecting. */
  551.     int numPoints; /* Total number of points at *polyPtr. */
  552.     register double *rectPtr; /* Points to coords for rectangle, in the
  553.  * order x1, y1, x2, y2.  X1 and y1 must
  554.  * be lower-left corner. */
  555. {
  556.     int state; /* State of all edges seen so far (-1 means
  557.  * outside, 1 means inside, won't ever be
  558.  * 0). */
  559.     int count;
  560.     register double *pPtr;
  561.     /*
  562.      * Iterate over all of the edges of the polygon and test them
  563.      * against the rectangle.  Can quit as soon as the state becomes
  564.      * "intersecting".
  565.      */
  566.     state = TkLineToArea(polyPtr, polyPtr+2, rectPtr);
  567.     if (state == 0) {
  568. return 0;
  569.     }
  570.     for (pPtr = polyPtr+2, count = numPoints-1; count >= 2;
  571.     pPtr += 2, count--) {
  572. if (TkLineToArea(pPtr, pPtr+2, rectPtr) != state) {
  573.     return 0;
  574. }
  575.     }
  576.     /*
  577.      * If all of the edges were inside the rectangle we're done.
  578.      * If all of the edges were outside, then the rectangle could
  579.      * still intersect the polygon (if it's entirely enclosed).
  580.      * Call TkPolygonToPoint to figure this out.
  581.      */
  582.     if (state == 1) {
  583. return 1;
  584.     }
  585.     if (TkPolygonToPoint(polyPtr, numPoints, rectPtr) == 0.0) {
  586. return 0;
  587.     }
  588.     return -1;
  589. }
  590. /*
  591.  *--------------------------------------------------------------
  592.  *
  593.  * TkOvalToPoint --
  594.  *
  595.  * Computes the distance from a given point to a given
  596.  * oval, in canvas units.
  597.  *
  598.  * Results:
  599.  * The return value is 0 if the point given by *pointPtr is
  600.  * inside the oval.  If the point isn't inside the
  601.  * oval then the return value is approximately the distance
  602.  * from the point to the oval.  If the oval is filled, then
  603.  * anywhere in the interior is considered "inside";  if
  604.  * the oval isn't filled, then "inside" means only the area
  605.  * occupied by the outline.
  606.  *
  607.  * Side effects:
  608.  * None.
  609.  *
  610.  *--------------------------------------------------------------
  611.  */
  612. /* ARGSUSED */
  613. double
  614. TkOvalToPoint(ovalPtr, width, filled, pointPtr)
  615.     double ovalPtr[4]; /* Pointer to array of four coordinates
  616.  * (x1, y1, x2, y2) defining oval's bounding
  617.  * box. */
  618.     double width; /* Width of outline for oval. */
  619.     int filled; /* Non-zero means oval should be treated as
  620.  * filled;  zero means only consider outline. */
  621.     double pointPtr[2]; /* Coordinates of point. */
  622. {
  623.     double xDelta, yDelta, scaledDistance, distToOutline, distToCenter;
  624.     double xDiam, yDiam;
  625.     /*
  626.      * Compute the distance between the center of the oval and the
  627.      * point in question, using a coordinate system where the oval
  628.      * has been transformed to a circle with unit radius.
  629.      */
  630.     xDelta = (pointPtr[0] - (ovalPtr[0] + ovalPtr[2])/2.0);
  631.     yDelta = (pointPtr[1] - (ovalPtr[1] + ovalPtr[3])/2.0);
  632.     distToCenter = hypot(xDelta, yDelta);
  633.     scaledDistance = hypot(xDelta / ((ovalPtr[2] + width - ovalPtr[0])/2.0),
  634.     yDelta / ((ovalPtr[3] + width - ovalPtr[1])/2.0));
  635.     /*
  636.      * If the scaled distance is greater than 1 then it means no
  637.      * hit.  Compute the distance from the point to the edge of
  638.      * the circle, then scale this distance back to the original
  639.      * coordinate system.
  640.      *
  641.      * Note: this distance isn't completely accurate.  It's only
  642.      * an approximation, and it can overestimate the correct
  643.      * distance when the oval is eccentric.
  644.      */
  645.     if (scaledDistance > 1.0) {
  646. return (distToCenter/scaledDistance) * (scaledDistance - 1.0);
  647.     }
  648.     /*
  649.      * Scaled distance less than 1 means the point is inside the
  650.      * outer edge of the oval.  If this is a filled oval, then we
  651.      * have a hit.  Otherwise, do the same computation as above
  652.      * (scale back to original coordinate system), but also check
  653.      * to see if the point is within the width of the outline.
  654.      */
  655.     if (filled) {
  656. return 0.0;
  657.     }
  658.     if (scaledDistance > 1E-10) {
  659. distToOutline = (distToCenter/scaledDistance) * (1.0 - scaledDistance)
  660. - width;
  661.     } else {
  662. /*
  663.  * Avoid dividing by a very small number (it could cause an
  664.  * arithmetic overflow).  This problem occurs if the point is
  665.  * very close to the center of the oval.
  666.  */
  667. xDiam = ovalPtr[2] - ovalPtr[0];
  668. yDiam = ovalPtr[3] - ovalPtr[1];
  669. if (xDiam < yDiam) {
  670.     distToOutline = (xDiam - width)/2;
  671. } else {
  672.     distToOutline = (yDiam - width)/2;
  673. }
  674.     }
  675.     if (distToOutline < 0.0) {
  676. return 0.0;
  677.     }
  678.     return distToOutline;
  679. }
  680. /*
  681.  *--------------------------------------------------------------
  682.  *
  683.  * TkOvalToArea --
  684.  *
  685.  * Determine whether an oval lies entirely inside, entirely
  686.  * outside, or overlapping a given rectangular area.
  687.  *
  688.  * Results:
  689.  * -1 is returned if the oval described by ovalPtr is entirely
  690.  * outside the rectangle given by rectPtr.  0 is returned if the
  691.  * oval overlaps the rectangle, and 1 is returned if the oval
  692.  * is entirely inside the rectangle.
  693.  *
  694.  * Side effects:
  695.  * None.
  696.  *
  697.  *--------------------------------------------------------------
  698.  */
  699. int
  700. TkOvalToArea(ovalPtr, rectPtr)
  701.     register double *ovalPtr; /* Points to coordinates definining the
  702.  * bounding rectangle for the oval: x1, y1,
  703.  * x2, y2.  X1 must be less than x2 and y1
  704.  * less than y2. */
  705.     register double *rectPtr; /* Points to coords for rectangle, in the
  706.  * order x1, y1, x2, y2.  X1 and y1 must
  707.  * be lower-left corner. */
  708. {
  709.     double centerX, centerY, radX, radY, deltaX, deltaY;
  710.     /*
  711.      * First, see if oval is entirely inside rectangle or entirely
  712.      * outside rectangle.
  713.      */
  714.     if ((rectPtr[0] <= ovalPtr[0]) && (rectPtr[2] >= ovalPtr[2])
  715.     && (rectPtr[1] <= ovalPtr[1]) && (rectPtr[3] >= ovalPtr[3])) {
  716. return 1;
  717.     }
  718.     if ((rectPtr[2] < ovalPtr[0]) || (rectPtr[0] > ovalPtr[2])
  719.     || (rectPtr[3] < ovalPtr[1]) || (rectPtr[1] > ovalPtr[3])) {
  720. return -1;
  721.     }
  722.     /*
  723.      * Next, go through the rectangle side by side.  For each side
  724.      * of the rectangle, find the point on the side that is closest
  725.      * to the oval's center, and see if that point is inside the
  726.      * oval.  If at least one such point is inside the oval, then
  727.      * the rectangle intersects the oval.
  728.      */
  729.     centerX = (ovalPtr[0] + ovalPtr[2])/2;
  730.     centerY = (ovalPtr[1] + ovalPtr[3])/2;
  731.     radX = (ovalPtr[2] - ovalPtr[0])/2;
  732.     radY = (ovalPtr[3] - ovalPtr[1])/2;
  733.     deltaY = rectPtr[1] - centerY;
  734.     if (deltaY < 0.0) {
  735. deltaY = centerY - rectPtr[3];
  736. if (deltaY < 0.0) {
  737.     deltaY = 0;
  738. }
  739.     }
  740.     deltaY /= radY;
  741.     deltaY *= deltaY;
  742.     /*
  743.      * Left side:
  744.      */
  745.     deltaX = (rectPtr[0] - centerX)/radX;
  746.     deltaX *= deltaX;
  747.     if ((deltaX + deltaY) <= 1.0) {
  748. return 0;
  749.     }
  750.     /*
  751.      * Right side:
  752.      */
  753.     deltaX = (rectPtr[2] - centerX)/radX;
  754.     deltaX *= deltaX;
  755.     if ((deltaX + deltaY) <= 1.0) {
  756. return 0;
  757.     }
  758.     deltaX = rectPtr[0] - centerX;
  759.     if (deltaX < 0.0) {
  760. deltaX = centerX - rectPtr[2];
  761. if (deltaX < 0.0) {
  762.     deltaX = 0;
  763. }
  764.     }
  765.     deltaX /= radX;
  766.     deltaX *= deltaX;
  767.     /*
  768.      * Bottom side:
  769.      */
  770.     deltaY = (rectPtr[1] - centerY)/radY;
  771.     deltaY *= deltaY;
  772.     if ((deltaX + deltaY) < 1.0) {
  773. return 0;
  774.     }
  775.     /*
  776.      * Top side:
  777.      */
  778.     deltaY = (rectPtr[3] - centerY)/radY;
  779.     deltaY *= deltaY;
  780.     if ((deltaX + deltaY) < 1.0) {
  781. return 0;
  782.     }
  783.     return -1;
  784. }
  785. /*
  786.  *--------------------------------------------------------------
  787.  *
  788.  * TkIncludePoint --
  789.  *
  790.  * Given a point and a generic canvas item header, expand
  791.  * the item's bounding box if needed to include the point.
  792.  *
  793.  * Results:
  794.  * None.
  795.  *
  796.  * Side effects:
  797.  * The boudn.
  798.  *
  799.  *--------------------------------------------------------------
  800.  */
  801. /* ARGSUSED */
  802. void
  803. TkIncludePoint(itemPtr, pointPtr)
  804.     register Tk_Item *itemPtr; /* Item whose bounding box is
  805.  * being calculated. */
  806.     double *pointPtr; /* Address of two doubles giving
  807.  * x and y coordinates of point. */
  808. {
  809.     int tmp;
  810.     tmp = (int) (pointPtr[0] + 0.5);
  811.     if (tmp < itemPtr->x1) {
  812. itemPtr->x1 = tmp;
  813.     }
  814.     if (tmp > itemPtr->x2) {
  815. itemPtr->x2 = tmp;
  816.     }
  817.     tmp = (int) (pointPtr[1] + 0.5);
  818.     if (tmp < itemPtr->y1) {
  819. itemPtr->y1 = tmp;
  820.     }
  821.     if (tmp > itemPtr->y2) {
  822. itemPtr->y2 = tmp;
  823.     }
  824. }
  825. /*
  826.  *--------------------------------------------------------------
  827.  *
  828.  * TkBezierScreenPoints --
  829.  *
  830.  * Given four control points, create a larger set of XPoints
  831.  * for a Bezier spline based on the points.
  832.  *
  833.  * Results:
  834.  * The array at *xPointPtr gets filled in with numSteps XPoints
  835.  * corresponding to the Bezier spline defined by the four 
  836.  * control points.  Note:  no output point is generated for the
  837.  * first input point, but an output point *is* generated for
  838.  * the last input point.
  839.  *
  840.  * Side effects:
  841.  * None.
  842.  *
  843.  *--------------------------------------------------------------
  844.  */
  845. void
  846. TkBezierScreenPoints(canvas, control, numSteps, xPointPtr)
  847.     Tk_Canvas canvas; /* Canvas in which curve is to be
  848.  * drawn. */
  849.     double control[]; /* Array of coordinates for four
  850.  * control points:  x0, y0, x1, y1,
  851.  * ... x3 y3. */
  852.     int numSteps; /* Number of curve points to
  853.  * generate.  */
  854.     register XPoint *xPointPtr; /* Where to put new points. */
  855. {
  856.     int i;
  857.     double u, u2, u3, t, t2, t3;
  858.     for (i = 1; i <= numSteps; i++, xPointPtr++) {
  859. t = ((double) i)/((double) numSteps);
  860. t2 = t*t;
  861. t3 = t2*t;
  862. u = 1.0 - t;
  863. u2 = u*u;
  864. u3 = u2*u;
  865. Tk_CanvasDrawableCoords(canvas,
  866. (control[0]*u3 + 3.0 * (control[2]*t*u2 + control[4]*t2*u)
  867.     + control[6]*t3),
  868. (control[1]*u3 + 3.0 * (control[3]*t*u2 + control[5]*t2*u)
  869.     + control[7]*t3),
  870. &xPointPtr->x, &xPointPtr->y);
  871.     }
  872. }
  873. /*
  874.  *--------------------------------------------------------------
  875.  *
  876.  * TkBezierPoints --
  877.  *
  878.  * Given four control points, create a larger set of points
  879.  * for a Bezier spline based on the points.
  880.  *
  881.  * Results:
  882.  * The array at *coordPtr gets filled in with 2*numSteps
  883.  * coordinates, which correspond to the Bezier spline defined
  884.  * by the four control points.  Note:  no output point is
  885.  * generated for the first input point, but an output point
  886.  * *is* generated for the last input point.
  887.  *
  888.  * Side effects:
  889.  * None.
  890.  *
  891.  *--------------------------------------------------------------
  892.  */
  893. void
  894. TkBezierPoints(control, numSteps, coordPtr)
  895.     double control[]; /* Array of coordinates for four
  896.  * control points:  x0, y0, x1, y1,
  897.  * ... x3 y3. */
  898.     int numSteps; /* Number of curve points to
  899.  * generate.  */
  900.     register double *coordPtr; /* Where to put new points. */
  901. {
  902.     int i;
  903.     double u, u2, u3, t, t2, t3;
  904.     for (i = 1; i <= numSteps; i++, coordPtr += 2) {
  905. t = ((double) i)/((double) numSteps);
  906. t2 = t*t;
  907. t3 = t2*t;
  908. u = 1.0 - t;
  909. u2 = u*u;
  910. u3 = u2*u;
  911. coordPtr[0] = control[0]*u3
  912. + 3.0 * (control[2]*t*u2 + control[4]*t2*u) + control[6]*t3;
  913. coordPtr[1] = control[1]*u3
  914. + 3.0 * (control[3]*t*u2 + control[5]*t2*u) + control[7]*t3;
  915.     }
  916. }
  917. /*
  918.  *--------------------------------------------------------------
  919.  *
  920.  * TkMakeBezierCurve --
  921.  *
  922.  * Given a set of points, create a new set of points that fit
  923.  * parabolic splines to the line segments connecting the original
  924.  * points.  Produces output points in either of two forms.
  925.  *
  926.  * Note: in spite of this procedure's name, it does *not* generate
  927.  * Bezier curves.  Since only three control points are used for
  928.  * each curve segment, not four, the curves are actually just
  929.  * parabolic.
  930.  *
  931.  * Results:
  932.  * Either or both of the xPoints or dblPoints arrays are filled
  933.  * in.  The return value is the number of points placed in the
  934.  * arrays.  Note:  if the first and last points are the same, then
  935.  * a closed curve is generated.
  936.  *
  937.  * Side effects:
  938.  * None.
  939.  *
  940.  *--------------------------------------------------------------
  941.  */
  942. int
  943. TkMakeBezierCurve(canvas, pointPtr, numPoints, numSteps, xPoints, dblPoints)
  944.     Tk_Canvas canvas; /* Canvas in which curve is to be
  945.  * drawn. */
  946.     double *pointPtr; /* Array of input coordinates:  x0,
  947.  * y0, x1, y1, etc.. */
  948.     int numPoints; /* Number of points at pointPtr. */
  949.     int numSteps; /* Number of steps to use for each
  950.  * spline segments (determines
  951.  * smoothness of curve). */
  952.     XPoint xPoints[]; /* Array of XPoints to fill in (e.g.
  953.  * for display.  NULL means don't
  954.  * fill in any XPoints. */
  955.     double dblPoints[]; /* Array of points to fill in as
  956.  * doubles, in the form x0, y0,
  957.  * x1, y1, ....  NULL means don't
  958.  * fill in anything in this form. 
  959.  * Caller must make sure that this
  960.  * array has enough space. */
  961. {
  962.     int closed, outputPoints, i;
  963.     int numCoords = numPoints*2;
  964.     double control[8];
  965.     /*
  966.      * If the curve is a closed one then generate a special spline
  967.      * that spans the last points and the first ones.  Otherwise
  968.      * just put the first point into the output.
  969.      */
  970.     if (!pointPtr) {
  971. /* Of pointPtr == NULL, this function returns an upper limit.
  972.  * of the array size to store the coordinates. This can be
  973.  * used to allocate storage, before the actual coordinates
  974.  * are calculated. */
  975. return 1 + numPoints * numSteps;
  976.     }
  977.     outputPoints = 0;
  978.     if ((pointPtr[0] == pointPtr[numCoords-2])
  979.     && (pointPtr[1] == pointPtr[numCoords-1])) {
  980. closed = 1;
  981. control[0] = 0.5*pointPtr[numCoords-4] + 0.5*pointPtr[0];
  982. control[1] = 0.5*pointPtr[numCoords-3] + 0.5*pointPtr[1];
  983. control[2] = 0.167*pointPtr[numCoords-4] + 0.833*pointPtr[0];
  984. control[3] = 0.167*pointPtr[numCoords-3] + 0.833*pointPtr[1];
  985. control[4] = 0.833*pointPtr[0] + 0.167*pointPtr[2];
  986. control[5] = 0.833*pointPtr[1] + 0.167*pointPtr[3];
  987. control[6] = 0.5*pointPtr[0] + 0.5*pointPtr[2];
  988. control[7] = 0.5*pointPtr[1] + 0.5*pointPtr[3];
  989. if (xPoints != NULL) {
  990.     Tk_CanvasDrawableCoords(canvas, control[0], control[1],
  991.     &xPoints->x, &xPoints->y);
  992.     TkBezierScreenPoints(canvas, control, numSteps, xPoints+1);
  993.     xPoints += numSteps+1;
  994. }
  995. if (dblPoints != NULL) {
  996.     dblPoints[0] = control[0];
  997.     dblPoints[1] = control[1];
  998.     TkBezierPoints(control, numSteps, dblPoints+2);
  999.     dblPoints += 2*(numSteps+1);
  1000. }
  1001. outputPoints += numSteps+1;
  1002.     } else {
  1003. closed = 0;
  1004. if (xPoints != NULL) {
  1005.     Tk_CanvasDrawableCoords(canvas, pointPtr[0], pointPtr[1],
  1006.     &xPoints->x, &xPoints->y);
  1007.     xPoints += 1;
  1008. }
  1009. if (dblPoints != NULL) {
  1010.     dblPoints[0] = pointPtr[0];
  1011.     dblPoints[1] = pointPtr[1];
  1012.     dblPoints += 2;
  1013. }
  1014. outputPoints += 1;
  1015.     }
  1016.     for (i = 2; i < numPoints; i++, pointPtr += 2) {
  1017. /*
  1018.  * Set up the first two control points.  This is done
  1019.  * differently for the first spline of an open curve
  1020.  * than for other cases.
  1021.  */
  1022. if ((i == 2) && !closed) {
  1023.     control[0] = pointPtr[0];
  1024.     control[1] = pointPtr[1];
  1025.     control[2] = 0.333*pointPtr[0] + 0.667*pointPtr[2];
  1026.     control[3] = 0.333*pointPtr[1] + 0.667*pointPtr[3];
  1027. } else {
  1028.     control[0] = 0.5*pointPtr[0] + 0.5*pointPtr[2];
  1029.     control[1] = 0.5*pointPtr[1] + 0.5*pointPtr[3];
  1030.     control[2] = 0.167*pointPtr[0] + 0.833*pointPtr[2];
  1031.     control[3] = 0.167*pointPtr[1] + 0.833*pointPtr[3];
  1032. }
  1033. /*
  1034.  * Set up the last two control points.  This is done
  1035.  * differently for the last spline of an open curve
  1036.  * than for other cases.
  1037.  */
  1038. if ((i == (numPoints-1)) && !closed) {
  1039.     control[4] = .667*pointPtr[2] + .333*pointPtr[4];
  1040.     control[5] = .667*pointPtr[3] + .333*pointPtr[5];
  1041.     control[6] = pointPtr[4];
  1042.     control[7] = pointPtr[5];
  1043. } else {
  1044.     control[4] = .833*pointPtr[2] + .167*pointPtr[4];
  1045.     control[5] = .833*pointPtr[3] + .167*pointPtr[5];
  1046.     control[6] = 0.5*pointPtr[2] + 0.5*pointPtr[4];
  1047.     control[7] = 0.5*pointPtr[3] + 0.5*pointPtr[5];
  1048. }
  1049. /*
  1050.  * If the first two points coincide, or if the last
  1051.  * two points coincide, then generate a single
  1052.  * straight-line segment by outputting the last control
  1053.  * point.
  1054.  */
  1055. if (((pointPtr[0] == pointPtr[2]) && (pointPtr[1] == pointPtr[3]))
  1056. || ((pointPtr[2] == pointPtr[4])
  1057. && (pointPtr[3] == pointPtr[5]))) {
  1058.     if (xPoints != NULL) {
  1059. Tk_CanvasDrawableCoords(canvas, control[6], control[7],
  1060. &xPoints[0].x, &xPoints[0].y);
  1061. xPoints++;
  1062.     }
  1063.     if (dblPoints != NULL) {
  1064. dblPoints[0] = control[6];
  1065. dblPoints[1] = control[7];
  1066. dblPoints += 2;
  1067.     }
  1068.     outputPoints += 1;
  1069.     continue;
  1070. }
  1071. /*
  1072.  * Generate a Bezier spline using the control points.
  1073.  */
  1074. if (xPoints != NULL) {
  1075.     TkBezierScreenPoints(canvas, control, numSteps, xPoints);
  1076.     xPoints += numSteps;
  1077. }
  1078. if (dblPoints != NULL) {
  1079.     TkBezierPoints(control, numSteps, dblPoints);
  1080.     dblPoints += 2*numSteps;
  1081. }
  1082. outputPoints += numSteps;
  1083.     }
  1084.     return outputPoints;
  1085. }
  1086. /*
  1087.  *--------------------------------------------------------------
  1088.  *
  1089.  * TkMakeBezierPostscript --
  1090.  *
  1091.  * This procedure generates Postscript commands that create
  1092.  * a path corresponding to a given Bezier curve.
  1093.  *
  1094.  * Results:
  1095.  * None.  Postscript commands to generate the path are appended
  1096.  * to the interp's result.
  1097.  *
  1098.  * Side effects:
  1099.  * None.
  1100.  *
  1101.  *--------------------------------------------------------------
  1102.  */
  1103. void
  1104. TkMakeBezierPostscript(interp, canvas, pointPtr, numPoints)
  1105.     Tcl_Interp *interp; /* Interpreter in whose result the
  1106.  * Postscript is to be stored. */
  1107.     Tk_Canvas canvas; /* Canvas widget for which the
  1108.  * Postscript is being generated. */
  1109.     double *pointPtr; /* Array of input coordinates:  x0,
  1110.  * y0, x1, y1, etc.. */
  1111.     int numPoints; /* Number of points at pointPtr. */
  1112. {
  1113.     int closed, i;
  1114.     int numCoords = numPoints*2;
  1115.     double control[8];
  1116.     char buffer[200];
  1117.     /*
  1118.      * If the curve is a closed one then generate a special spline
  1119.      * that spans the last points and the first ones.  Otherwise
  1120.      * just put the first point into the path.
  1121.      */
  1122.     if ((pointPtr[0] == pointPtr[numCoords-2])
  1123.     && (pointPtr[1] == pointPtr[numCoords-1])) {
  1124. closed = 1;
  1125. control[0] = 0.5*pointPtr[numCoords-4] + 0.5*pointPtr[0];
  1126. control[1] = 0.5*pointPtr[numCoords-3] + 0.5*pointPtr[1];
  1127. control[2] = 0.167*pointPtr[numCoords-4] + 0.833*pointPtr[0];
  1128. control[3] = 0.167*pointPtr[numCoords-3] + 0.833*pointPtr[1];
  1129. control[4] = 0.833*pointPtr[0] + 0.167*pointPtr[2];
  1130. control[5] = 0.833*pointPtr[1] + 0.167*pointPtr[3];
  1131. control[6] = 0.5*pointPtr[0] + 0.5*pointPtr[2];
  1132. control[7] = 0.5*pointPtr[1] + 0.5*pointPtr[3];
  1133. sprintf(buffer, "%.15g %.15g moveton%.15g %.15g %.15g %.15g %.15g %.15g curveton",
  1134. control[0], Tk_CanvasPsY(canvas, control[1]),
  1135. control[2], Tk_CanvasPsY(canvas, control[3]),
  1136. control[4], Tk_CanvasPsY(canvas, control[5]),
  1137. control[6], Tk_CanvasPsY(canvas, control[7]));
  1138.     } else {
  1139. closed = 0;
  1140. control[6] = pointPtr[0];
  1141. control[7] = pointPtr[1];
  1142. sprintf(buffer, "%.15g %.15g moveton",
  1143. control[6], Tk_CanvasPsY(canvas, control[7]));
  1144.     }
  1145.     Tcl_AppendResult(interp, buffer, (char *) NULL);
  1146.     /*
  1147.      * Cycle through all the remaining points in the curve, generating
  1148.      * a curve section for each vertex in the linear path.
  1149.      */
  1150.     for (i = numPoints-2, pointPtr += 2; i > 0; i--, pointPtr += 2) {
  1151. control[2] = 0.333*control[6] + 0.667*pointPtr[0];
  1152. control[3] = 0.333*control[7] + 0.667*pointPtr[1];
  1153. /*
  1154.  * Set up the last two control points.  This is done
  1155.  * differently for the last spline of an open curve
  1156.  * than for other cases.
  1157.  */
  1158. if ((i == 1) && !closed) {
  1159.     control[6] = pointPtr[2];
  1160.     control[7] = pointPtr[3];
  1161. } else {
  1162.     control[6] = 0.5*pointPtr[0] + 0.5*pointPtr[2];
  1163.     control[7] = 0.5*pointPtr[1] + 0.5*pointPtr[3];
  1164. }
  1165. control[4] = 0.333*control[6] + 0.667*pointPtr[0];
  1166. control[5] = 0.333*control[7] + 0.667*pointPtr[1];
  1167. sprintf(buffer, "%.15g %.15g %.15g %.15g %.15g %.15g curveton",
  1168. control[2], Tk_CanvasPsY(canvas, control[3]),
  1169. control[4], Tk_CanvasPsY(canvas, control[5]),
  1170. control[6], Tk_CanvasPsY(canvas, control[7]));
  1171. Tcl_AppendResult(interp, buffer, (char *) NULL);
  1172.     }
  1173. }
  1174. /*
  1175.  *--------------------------------------------------------------
  1176.  *
  1177.  * TkGetMiterPoints --
  1178.  *
  1179.  * Given three points forming an angle, compute the
  1180.  * coordinates of the inside and outside points of
  1181.  * the mitered corner formed by a line of a given
  1182.  * width at that angle.
  1183.  *
  1184.  * Results:
  1185.  * If the angle formed by the three points is less than
  1186.  * 11 degrees then 0 is returned and m1 and m2 aren't
  1187.  * modified.  Otherwise 1 is returned and the points at
  1188.  * m1 and m2 are filled in with the positions of the points
  1189.  * of the mitered corner.
  1190.  *
  1191.  * Side effects:
  1192.  * None.
  1193.  *
  1194.  *--------------------------------------------------------------
  1195.  */
  1196. int
  1197. TkGetMiterPoints(p1, p2, p3, width, m1, m2)
  1198.     double p1[]; /* Points to x- and y-coordinates of point
  1199.  * before vertex. */
  1200.     double p2[]; /* Points to x- and y-coordinates of vertex
  1201.  * for mitered joint. */
  1202.     double p3[]; /* Points to x- and y-coordinates of point
  1203.  * after vertex. */
  1204.     double width; /* Width of line.  */
  1205.     double m1[]; /* Points to place to put "left" vertex
  1206.  * point (see as you face from p1 to p2). */
  1207.     double m2[]; /* Points to place to put "right" vertex
  1208.  * point. */
  1209. {
  1210.     double theta1; /* Angle of segment p2-p1. */
  1211.     double theta2; /* Angle of segment p2-p3. */
  1212.     double theta; /* Angle between line segments (angle
  1213.  * of joint). */
  1214.     double theta3; /* Angle that bisects theta1 and
  1215.  * theta2 and points to m1. */
  1216.     double dist; /* Distance of miter points from p2. */
  1217.     double deltaX, deltaY; /* X and y offsets cooresponding to
  1218.  * dist (fudge factors for bounding
  1219.  * box). */
  1220.     double p1x, p1y, p2x, p2y, p3x, p3y;
  1221.     static double elevenDegrees = (11.0*2.0*PI)/360.0;
  1222.     /*
  1223.      * Round the coordinates to integers to mimic what happens when the
  1224.      * line segments are displayed; without this code, the bounding box
  1225.      * of a mitered line can be miscomputed greatly.
  1226.      */
  1227.     p1x = floor(p1[0]+0.5);
  1228.     p1y = floor(p1[1]+0.5);
  1229.     p2x = floor(p2[0]+0.5);
  1230.     p2y = floor(p2[1]+0.5);
  1231.     p3x = floor(p3[0]+0.5);
  1232.     p3y = floor(p3[1]+0.5);
  1233.     if (p2y == p1y) {
  1234. theta1 = (p2x < p1x) ? 0 : PI;
  1235.     } else if (p2x == p1x) {
  1236. theta1 = (p2y < p1y) ? PI/2.0 : -PI/2.0;
  1237.     } else {
  1238. theta1 = atan2(p1y - p2y, p1x - p2x);
  1239.     }
  1240.     if (p3y == p2y) {
  1241. theta2 = (p3x > p2x) ? 0 : PI;
  1242.     } else if (p3x == p2x) {
  1243. theta2 = (p3y > p2y) ? PI/2.0 : -PI/2.0;
  1244.     } else {
  1245. theta2 = atan2(p3y - p2y, p3x - p2x);
  1246.     }
  1247.     theta = theta1 - theta2;
  1248.     if (theta > PI) {
  1249. theta -= 2*PI;
  1250.     } else if (theta < -PI) {
  1251. theta += 2*PI;
  1252.     }
  1253.     if ((theta < elevenDegrees) && (theta > -elevenDegrees)) {
  1254. return 0;
  1255.     }
  1256.     dist = 0.5*width/sin(0.5*theta);
  1257.     if (dist < 0.0) {
  1258. dist = -dist;
  1259.     }
  1260.     /*
  1261.      * Compute theta3 (make sure that it points to the left when
  1262.      * looking from p1 to p2).
  1263.      */
  1264.     theta3 = (theta1 + theta2)/2.0;
  1265.     if (sin(theta3 - (theta1 + PI)) < 0.0) {
  1266. theta3 += PI;
  1267.     }
  1268.     deltaX = dist*cos(theta3);
  1269.     m1[0] = p2x + deltaX;
  1270.     m2[0] = p2x - deltaX;
  1271.     deltaY = dist*sin(theta3);
  1272.     m1[1] = p2y + deltaY;
  1273.     m2[1] = p2y - deltaY;
  1274.     return 1;
  1275. }
  1276. /*
  1277.  *--------------------------------------------------------------
  1278.  *
  1279.  * TkGetButtPoints --
  1280.  *
  1281.  * Given two points forming a line segment, compute the
  1282.  * coordinates of two endpoints of a rectangle formed by
  1283.  * bloating the line segment until it is width units wide.
  1284.  *
  1285.  * Results:
  1286.  * There is no return value.  M1 and m2 are filled in to
  1287.  * correspond to m1 and m2 in the diagram below:
  1288.  *
  1289.  *    ----------------* m1
  1290.  *    |
  1291.  * p1 *---------------* p2
  1292.  *    |
  1293.  *    ----------------* m2
  1294.  *
  1295.  * M1 and m2 will be W units apart, with p2 centered between
  1296.  * them and m1-m2 perpendicular to p1-p2.  However, if
  1297.  * "project" is true then m1 and m2 will be as follows:
  1298.  *
  1299.  *    -------------------* m1
  1300.  *   p2  |
  1301.  * p1 *---------------*  |
  1302.  *       |
  1303.  *    -------------------* m2
  1304.  *
  1305.  * In this case p2 will be width/2 units from the segment m1-m2.
  1306.  *
  1307.  * Side effects:
  1308.  * None.
  1309.  *
  1310.  *--------------------------------------------------------------
  1311.  */
  1312. void
  1313. TkGetButtPoints(p1, p2, width, project, m1, m2)
  1314.     double p1[]; /* Points to x- and y-coordinates of point
  1315.  * before vertex. */
  1316.     double p2[]; /* Points to x- and y-coordinates of vertex
  1317.  * for mitered joint. */
  1318.     double width; /* Width of line.  */
  1319.     int project; /* Non-zero means project p2 by an additional
  1320.  * width/2 before computing m1 and m2. */
  1321.     double m1[]; /* Points to place to put "left" result
  1322.  * point, as you face from p1 to p2. */
  1323.     double m2[]; /* Points to place to put "right" result
  1324.  * point. */
  1325. {
  1326.     double length; /* Length of p1-p2 segment. */
  1327.     double deltaX, deltaY; /* Increments in coords. */
  1328.     width *= 0.5;
  1329.     length = hypot(p2[0] - p1[0], p2[1] - p1[1]);
  1330.     if (length == 0.0) {
  1331. m1[0] = m2[0] = p2[0];
  1332. m1[1] = m2[1] = p2[1];
  1333.     } else {
  1334. deltaX = -width * (p2[1] - p1[1]) / length;
  1335. deltaY = width * (p2[0] - p1[0]) / length;
  1336. m1[0] = p2[0] + deltaX;
  1337. m2[0] = p2[0] - deltaX;
  1338. m1[1] = p2[1] + deltaY;
  1339. m2[1] = p2[1] - deltaY;
  1340. if (project) {
  1341.     m1[0] += deltaY;
  1342.     m2[0] += deltaY;
  1343.     m1[1] -= deltaX;
  1344.     m2[1] -= deltaX;
  1345. }
  1346.     }
  1347. }