ellipse.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. /*
  2. * This file is part of the UCB release of Plan 9. It is subject to the license
  3. * terms in the LICENSE file found in the top-level directory of this
  4. * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
  5. * part of the UCB release of Plan 9, including this file, may be copied,
  6. * modified, propagated, or distributed except according to the terms contained
  7. * in the LICENSE file.
  8. */
  9. #include <u.h>
  10. #include <libc.h>
  11. #include <draw.h>
  12. #include <memdraw.h>
  13. #include <memlayer.h>
  14. /*
  15. * ellipse(dst, c, a, b, t, src, sp)
  16. * draws an ellipse centered at c with semiaxes a,b>=0
  17. * and semithickness t>=0, or filled if t<0. point sp
  18. * in src maps to c in dst
  19. *
  20. * very thick skinny ellipses are brushed with circles (slow)
  21. * others are approximated by filling between 2 ellipses
  22. * criterion for very thick when b<a: t/b > 0.5*x/(1-x)
  23. * where x = b/a
  24. */
  25. typedef struct Param Param;
  26. typedef struct State State;
  27. static void bellipse(int, State*, Param*);
  28. static void erect(int, int, int, int, Param*);
  29. static void eline(int, int, int, int, Param*);
  30. struct Param {
  31. Memimage *dst;
  32. Memimage *src;
  33. Point c;
  34. int t;
  35. Point sp;
  36. Memimage *disc;
  37. int op;
  38. };
  39. /*
  40. * denote residual error by e(x,y) = b^2*x^2 + a^2*y^2 - a^2*b^2
  41. * e(x,y) = 0 on ellipse, e(x,y) < 0 inside, e(x,y) > 0 outside
  42. */
  43. struct State {
  44. int a;
  45. int x;
  46. int64_t a2; /* a^2 */
  47. int64_t b2; /* b^2 */
  48. int64_t b2x; /* b^2 * x */
  49. int64_t a2y; /* a^2 * y */
  50. int64_t c1;
  51. int64_t c2; /* test criteria */
  52. int64_t ee; /* ee = e(x+1/2,y-1/2) - (a^2+b^2)/4 */
  53. int64_t dxe;
  54. int64_t dye;
  55. int64_t d2xe;
  56. int64_t d2ye;
  57. };
  58. static
  59. State*
  60. newstate(State *s, int a, int b)
  61. {
  62. s->x = 0;
  63. s->a = a;
  64. s->a2 = (int64_t)(a*a);
  65. s->b2 = (int64_t)(b*b);
  66. s->b2x = (int64_t)0;
  67. s->a2y = s->a2*(int64_t)b;
  68. s->c1 = -((s->a2>>2) + (int64_t)(a&1) + s->b2);
  69. s->c2 = -((s->b2>>2) + (int64_t)(b&1));
  70. s->ee = -s->a2y;
  71. s->dxe = (int64_t)0;
  72. s->dye = s->ee<<1;
  73. s->d2xe = s->b2<<1;
  74. s->d2ye = s->a2<<1;
  75. return s;
  76. }
  77. /*
  78. * return x coord of rightmost pixel on next scan line
  79. */
  80. static
  81. int
  82. step(State *s)
  83. {
  84. while(s->x < s->a) {
  85. if(s->ee+s->b2x <= s->c1 || /* e(x+1,y-1/2) <= 0 */
  86. s->ee+s->a2y <= s->c2) { /* e(x+1/2,y) <= 0 (rare) */
  87. s->dxe += s->d2xe;
  88. s->ee += s->dxe;
  89. s->b2x += s->b2;
  90. s->x++;
  91. continue;
  92. }
  93. s->dye += s->d2ye;
  94. s->ee += s->dye;
  95. s->a2y -= s->a2;
  96. if(s->ee-s->a2y <= s->c2) { /* e(x+1/2,y-1) <= 0 */
  97. s->dxe += s->d2xe;
  98. s->ee += s->dxe;
  99. s->b2x += s->b2;
  100. return s->x++;
  101. }
  102. break;
  103. }
  104. return s->x;
  105. }
  106. void
  107. memellipse(Memimage *dst, Point c, int a, int b, int t, Memimage *src, Point sp, int op)
  108. {
  109. State in, out;
  110. int y, inb, inx, outx, u;
  111. Param p;
  112. if(a < 0)
  113. a = -a;
  114. if(b < 0)
  115. b = -b;
  116. p.dst = dst;
  117. p.src = src;
  118. p.c = c;
  119. p.t = t;
  120. p.sp = subpt(sp, c);
  121. p.disc = nil;
  122. p.op = op;
  123. u = (t<<1)*(a-b);
  124. if(b<a && u>b*b || a<b && -u>a*a) {
  125. /* if(b<a&&(t<<1)>b*b/a || a<b&&(t<<1)>a*a/b) # very thick */
  126. bellipse(b, newstate(&in, a, b), &p);
  127. return;
  128. }
  129. if(t < 0) {
  130. inb = -1;
  131. newstate(&out, a, y = b);
  132. } else {
  133. inb = b - t;
  134. newstate(&out, a+t, y = b+t);
  135. }
  136. if(t > 0)
  137. newstate(&in, a-t, inb);
  138. inx = 0;
  139. for( ; y>=0; y--) {
  140. outx = step(&out);
  141. if(y > inb) {
  142. erect(-outx, y, outx, y, &p);
  143. if(y != 0)
  144. erect(-outx, -y, outx, -y, &p);
  145. continue;
  146. }
  147. if(t > 0) {
  148. inx = step(&in);
  149. if(y == inb)
  150. inx = 0;
  151. } else if(inx > outx)
  152. inx = outx;
  153. erect(inx, y, outx, y, &p);
  154. if(y != 0)
  155. erect(inx, -y, outx, -y, &p);
  156. erect(-outx, y, -inx, y, &p);
  157. if(y != 0)
  158. erect(-outx, -y, -inx, -y, &p);
  159. inx = outx + 1;
  160. }
  161. }
  162. static Point p00 = {0, 0};
  163. /*
  164. * a brushed ellipse
  165. */
  166. static
  167. void
  168. bellipse(int y, State *s, Param *p)
  169. {
  170. int t, ox, oy, x, nx;
  171. t = p->t;
  172. p->disc = allocmemimage(Rect(-t,-t,t+1,t+1), GREY1);
  173. if(p->disc == nil)
  174. return;
  175. memfillcolor(p->disc, DTransparent);
  176. memellipse(p->disc, p00, t, t, -1, memopaque, p00, p->op);
  177. oy = y;
  178. ox = 0;
  179. nx = x = step(s);
  180. do {
  181. while(nx==x && y-->0)
  182. nx = step(s);
  183. y++;
  184. eline(-x,-oy,-ox, -y, p);
  185. eline(ox,-oy, x, -y, p);
  186. eline(-x, y,-ox, oy, p);
  187. eline(ox, y, x, oy, p);
  188. ox = x+1;
  189. x = nx;
  190. y--;
  191. oy = y;
  192. } while(oy > 0);
  193. }
  194. /*
  195. * a rectangle with closed (not half-open) coordinates expressed
  196. * relative to the center of the ellipse
  197. */
  198. static
  199. void
  200. erect(int x0, int y0, int x1, int y1, Param *p)
  201. {
  202. Rectangle r;
  203. /* print("R %d,%d %d,%d\n", x0, y0, x1, y1); */
  204. r = Rect(p->c.x+x0, p->c.y+y0, p->c.x+x1+1, p->c.y+y1+1);
  205. memdraw(p->dst, r, p->src, addpt(p->sp, r.min), memopaque, p00, p->op);
  206. }
  207. /*
  208. * a brushed point similarly specified
  209. */
  210. static
  211. void
  212. epoint(int x, int y, Param *p)
  213. {
  214. Point p0;
  215. Rectangle r;
  216. /* print("P%d %d,%d\n", p->t, x, y); */
  217. p0 = Pt(p->c.x+x, p->c.y+y);
  218. r = Rpt(addpt(p0, p->disc->r.min), addpt(p0, p->disc->r.max));
  219. memdraw(p->dst, r, p->src, addpt(p->sp, r.min), p->disc, p->disc->r.min, p->op);
  220. }
  221. /*
  222. * a brushed horizontal or vertical line similarly specified
  223. */
  224. static
  225. void
  226. eline(int x0, int y0, int x1, int y1, Param *p)
  227. {
  228. /* print("L%d %d,%d %d,%d\n", p->t, x0, y0, x1, y1); */
  229. if(x1 > x0+1)
  230. erect(x0+1, y0-p->t, x1-1, y1+p->t, p);
  231. else if(y1 > y0+1)
  232. erect(x0-p->t, y0+1, x1+p->t, y1-1, p);
  233. epoint(x0, y0, p);
  234. if(x1-x0 || y1-y0)
  235. epoint(x1, y1, p);
  236. }