arcgen.c 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. #include <stdio.h>
  2. #include <math.h>
  3. #include "pic.h"
  4. #include "y.tab.h"
  5. void arc_extreme(double, double, double, double, double, double);
  6. int quadrant(double x, double y);
  7. obj *arcgen(int type) /* handles circular and (eventually) elliptical arcs */
  8. {
  9. static double prevw = HT10;
  10. static double prevh = HT5;
  11. static double prevrad = HT2;
  12. static int dtox[2][4] ={ 1, -1, -1, 1, 1, 1, -1, -1 };
  13. static int dtoy[2][4] ={ 1, 1, -1, -1, -1, 1, 1, -1 };
  14. static int dctrx[2][4] ={ 0, -1, 0, 1, 0, 1, 0, -1 };
  15. static int dctry[2][4] ={ 1, 0, -1, 0, -1, 0, 1, 0 };
  16. static int nexthv[2][4] ={ U_DIR, L_DIR, D_DIR, R_DIR, D_DIR, R_DIR, U_DIR, L_DIR };
  17. double dx2, dy2, ht, phi, r, d;
  18. int i, head, to, at, cw, invis, ddtype, battr;
  19. obj *p, *ppos;
  20. double fromx, fromy, tox, toy, fillval = 0;
  21. Attr *ap;
  22. prevrad = getfval("arcrad");
  23. prevh = getfval("arrowht");
  24. prevw = getfval("arrowwid");
  25. fromx = curx;
  26. fromy = cury;
  27. head = to = at = cw = invis = ddtype = battr = 0;
  28. for (i = 0; i < nattr; i++) {
  29. ap = &attr[i];
  30. switch (ap->a_type) {
  31. case TEXTATTR:
  32. savetext(ap->a_sub, ap->a_val.p);
  33. break;
  34. case HEAD:
  35. head += ap->a_val.i;
  36. break;
  37. case INVIS:
  38. invis = INVIS;
  39. break;
  40. case HEIGHT: /* length of arrowhead */
  41. prevh = ap->a_val.f;
  42. break;
  43. case WIDTH: /* width of arrowhead */
  44. prevw = ap->a_val.f;
  45. break;
  46. case RADIUS:
  47. prevrad = ap->a_val.f;
  48. break;
  49. case DIAMETER:
  50. prevrad = ap->a_val.f / 2;
  51. break;
  52. case CW:
  53. cw = 1;
  54. break;
  55. case FROM: /* start point of arc */
  56. ppos = ap->a_val.o;
  57. fromx = ppos->o_x;
  58. fromy = ppos->o_y;
  59. break;
  60. case TO: /* end point of arc */
  61. ppos = ap->a_val.o;
  62. tox = ppos->o_x;
  63. toy = ppos->o_y;
  64. to++;
  65. break;
  66. case AT: /* center of arc */
  67. ppos = ap->a_val.o;
  68. curx = ppos->o_x;
  69. cury = ppos->o_y;
  70. at = 1;
  71. break;
  72. case UP:
  73. hvmode = U_DIR;
  74. break;
  75. case DOWN:
  76. hvmode = D_DIR;
  77. break;
  78. case RIGHT:
  79. hvmode = R_DIR;
  80. break;
  81. case LEFT:
  82. hvmode = L_DIR;
  83. break;
  84. case FILL:
  85. battr |= FILLBIT;
  86. if (ap->a_sub == DEFAULT)
  87. fillval = getfval("fillval");
  88. else
  89. fillval = ap->a_val.f;
  90. break;
  91. }
  92. }
  93. if (!at && !to) { /* the defaults are mostly OK */
  94. curx = fromx + prevrad * dctrx[cw][hvmode];
  95. cury = fromy + prevrad * dctry[cw][hvmode];
  96. tox = fromx + prevrad * dtox[cw][hvmode];
  97. toy = fromy + prevrad * dtoy[cw][hvmode];
  98. hvmode = nexthv[cw][hvmode];
  99. }
  100. else if (!at) {
  101. dx2 = (tox - fromx) / 2;
  102. dy2 = (toy - fromy) / 2;
  103. phi = atan2(dy2, dx2) + (cw ? -PI/2 : PI/2);
  104. if (prevrad <= 0.0)
  105. prevrad = dx2*dx2+dy2*dy2;
  106. for (r=prevrad; (d = r*r - (dx2*dx2+dy2*dy2)) <= 0.0; r *= 2)
  107. ; /* this kludge gets around too-small radii */
  108. prevrad = r;
  109. ht = sqrt(d);
  110. curx = fromx + dx2 + ht * cos(phi);
  111. cury = fromy + dy2 + ht * sin(phi);
  112. dprintf("dx2,dy2=%g,%g, phi=%g, r,ht=%g,%g\n",
  113. dx2, dy2, phi, r, ht);
  114. }
  115. else if (at && !to) { /* do we have all the cases??? */
  116. tox = fromx + prevrad * dtox[cw][hvmode];
  117. toy = fromy + prevrad * dtoy[cw][hvmode];
  118. hvmode = nexthv[cw][hvmode];
  119. }
  120. if (cw) { /* interchange roles of from-to and heads */
  121. double temp;
  122. temp = fromx; fromx = tox; tox = temp;
  123. temp = fromy; fromy = toy; toy = temp;
  124. if (head == HEAD1)
  125. head = HEAD2;
  126. else if (head == HEAD2)
  127. head = HEAD1;
  128. }
  129. p = makenode(type, 7);
  130. arc_extreme(fromx, fromy, tox, toy, curx, cury);
  131. p->o_val[0] = fromx;
  132. p->o_val[1] = fromy;
  133. p->o_val[2] = tox;
  134. p->o_val[3] = toy;
  135. if (cw) {
  136. curx = fromx;
  137. cury = fromy;
  138. } else {
  139. curx = tox;
  140. cury = toy;
  141. }
  142. p->o_val[4] = prevw;
  143. p->o_val[5] = prevh;
  144. p->o_val[6] = prevrad;
  145. p->o_attr = head | (cw ? CW_ARC : 0) | invis | ddtype | battr;
  146. p->o_fillval = fillval;
  147. if (head)
  148. p->o_nhead = getfval("arrowhead");
  149. dprintf("arc rad %g at %g %g from %g %g to %g %g head %g %g\n",
  150. prevrad, p->o_x, p->o_y,
  151. p->o_val[0], p->o_val[1], p->o_val[2], p->o_val[3], p->o_val[4], p->o_val[5]);
  152. return(p);
  153. }
  154. /***************************************************************************
  155. bounding box of a circular arc Eric Grosse 24 May 84
  156. Conceptually, this routine generates a list consisting of the start,
  157. end, and whichever north, east, south, and west points lie on the arc.
  158. The bounding box is then the range of this list.
  159. list = {start,end}
  160. j = quadrant(start)
  161. k = quadrant(end)
  162. if( j==k && long way 'round ) append north,west,south,east
  163. else
  164. while( j != k )
  165. append center+radius*[j-th of north,west,south,east unit vectors]
  166. j += 1 (mod 4)
  167. return( bounding box of list )
  168. The following code implements this, with simple optimizations.
  169. ***********************************************************************/
  170. void arc_extreme(double x0, double y0, double x1, double y1, double xc, double yc)
  171. /* start, end, center */
  172. {
  173. /* assumes center isn't too far out */
  174. double r, xmin, ymin, xmax, ymax;
  175. int j, k;
  176. x0 -= xc; y0 -= yc; /* move to center */
  177. x1 -= xc; y1 -= yc;
  178. xmin = (x0<x1)?x0:x1; ymin = (y0<y1)?y0:y1;
  179. xmax = (x0>x1)?x0:x1; ymax = (y0>y1)?y0:y1;
  180. r = sqrt(x0*x0 + y0*y0);
  181. if (r > 0.0) {
  182. j = quadrant(x0,y0);
  183. k = quadrant(x1,y1);
  184. if (j == k && y1*x0 < x1*y0) {
  185. /* viewed as complex numbers, if Im(z1/z0)<0, arc is big */
  186. if( xmin > -r) xmin = -r; if( ymin > -r) ymin = -r;
  187. if( xmax < r) xmax = r; if( ymax < r) ymax = r;
  188. } else {
  189. while (j != k) {
  190. switch (j) {
  191. case 1: if( ymax < r) ymax = r; break; /* north */
  192. case 2: if( xmin > -r) xmin = -r; break; /* west */
  193. case 3: if( ymin > -r) ymin = -r; break; /* south */
  194. case 4: if( xmax < r) xmax = r; break; /* east */
  195. }
  196. j = j%4 + 1;
  197. }
  198. }
  199. }
  200. xmin += xc; ymin += yc;
  201. xmax += xc; ymax += yc;
  202. extreme(xmin, ymin);
  203. extreme(xmax, ymax);
  204. }
  205. quadrant(double x, double y)
  206. {
  207. if ( x>=0.0 && y> 0.0) return(1);
  208. else if( x< 0.0 && y>=0.0) return(2);
  209. else if( x<=0.0 && y< 0.0) return(3);
  210. else if( x> 0.0 && y<=0.0) return(4);
  211. else return 0; /* shut up lint */
  212. }