ellipse.c 4.7 KB

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