123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248 |
- #include <u.h>
- #include <libc.h>
- #include <draw.h>
- #include <memdraw.h>
- #include <memlayer.h>
- /*
- * ellipse(dst, c, a, b, t, src, sp)
- * draws an ellipse centered at c with semiaxes a,b>=0
- * and semithickness t>=0, or filled if t<0. point sp
- * in src maps to c in dst
- *
- * very thick skinny ellipses are brushed with circles (slow)
- * others are approximated by filling between 2 ellipses
- * criterion for very thick when b<a: t/b > 0.5*x/(1-x)
- * where x = b/a
- */
- typedef struct Param Param;
- typedef struct State State;
- static void bellipse(int, State*, Param*);
- static void erect(int, int, int, int, Param*);
- static void eline(int, int, int, int, Param*);
- struct Param {
- Memimage *dst;
- Memimage *src;
- Point c;
- int t;
- Point sp;
- Memimage *disc;
- int op;
- };
- /*
- * denote residual error by e(x,y) = b^2*x^2 + a^2*y^2 - a^2*b^2
- * e(x,y) = 0 on ellipse, e(x,y) < 0 inside, e(x,y) > 0 outside
- */
- struct State {
- int a;
- int x;
- vlong a2; /* a^2 */
- vlong b2; /* b^2 */
- vlong b2x; /* b^2 * x */
- vlong a2y; /* a^2 * y */
- vlong c1;
- vlong c2; /* test criteria */
- vlong ee; /* ee = e(x+1/2,y-1/2) - (a^2+b^2)/4 */
- vlong dxe;
- vlong dye;
- vlong d2xe;
- vlong d2ye;
- };
- static
- State*
- newstate(State *s, int a, int b)
- {
- s->x = 0;
- s->a = a;
- s->a2 = (vlong)(a*a);
- s->b2 = (vlong)(b*b);
- s->b2x = (vlong)0;
- s->a2y = s->a2*(vlong)b;
- s->c1 = -((s->a2>>2) + (vlong)(a&1) + s->b2);
- s->c2 = -((s->b2>>2) + (vlong)(b&1));
- s->ee = -s->a2y;
- s->dxe = (vlong)0;
- s->dye = s->ee<<1;
- s->d2xe = s->b2<<1;
- s->d2ye = s->a2<<1;
- return s;
- }
- /*
- * return x coord of rightmost pixel on next scan line
- */
- static
- int
- step(State *s)
- {
- while(s->x < s->a) {
- if(s->ee+s->b2x <= s->c1 || /* e(x+1,y-1/2) <= 0 */
- s->ee+s->a2y <= s->c2) { /* e(x+1/2,y) <= 0 (rare) */
- s->dxe += s->d2xe;
- s->ee += s->dxe;
- s->b2x += s->b2;
- s->x++;
- continue;
- }
- s->dye += s->d2ye;
- s->ee += s->dye;
- s->a2y -= s->a2;
- if(s->ee-s->a2y <= s->c2) { /* e(x+1/2,y-1) <= 0 */
- s->dxe += s->d2xe;
- s->ee += s->dxe;
- s->b2x += s->b2;
- return s->x++;
- }
- break;
- }
- return s->x;
- }
- void
- memellipse(Memimage *dst, Point c, int a, int b, int t, Memimage *src, Point sp, int op)
- {
- State in, out;
- int y, inb, inx, outx, u;
- Param p;
- if(a < 0)
- a = -a;
- if(b < 0)
- b = -b;
- p.dst = dst;
- p.src = src;
- p.c = c;
- p.t = t;
- p.sp = subpt(sp, c);
- p.disc = nil;
- p.op = op;
- u = (t<<1)*(a-b);
- if(b<a && u>b*b || a<b && -u>a*a) {
- /* if(b<a&&(t<<1)>b*b/a || a<b&&(t<<1)>a*a/b) # very thick */
- bellipse(b, newstate(&in, a, b), &p);
- return;
- }
- if(t < 0) {
- inb = -1;
- newstate(&out, a, y = b);
- } else {
- inb = b - t;
- newstate(&out, a+t, y = b+t);
- }
- if(t > 0)
- newstate(&in, a-t, inb);
- inx = 0;
- for( ; y>=0; y--) {
- outx = step(&out);
- if(y > inb) {
- erect(-outx, y, outx, y, &p);
- if(y != 0)
- erect(-outx, -y, outx, -y, &p);
- continue;
- }
- if(t > 0) {
- inx = step(&in);
- if(y == inb)
- inx = 0;
- } else if(inx > outx)
- inx = outx;
- erect(inx, y, outx, y, &p);
- if(y != 0)
- erect(inx, -y, outx, -y, &p);
- erect(-outx, y, -inx, y, &p);
- if(y != 0)
- erect(-outx, -y, -inx, -y, &p);
- inx = outx + 1;
- }
- }
- static Point p00 = {0, 0};
- /*
- * a brushed ellipse
- */
- static
- void
- bellipse(int y, State *s, Param *p)
- {
- int t, ox, oy, x, nx;
- t = p->t;
- p->disc = allocmemimage(Rect(-t,-t,t+1,t+1), GREY1);
- if(p->disc == nil)
- return;
- memfillcolor(p->disc, DTransparent);
- memellipse(p->disc, p00, t, t, -1, memopaque, p00, p->op);
- oy = y;
- ox = 0;
- nx = x = step(s);
- do {
- while(nx==x && y-->0)
- nx = step(s);
- y++;
- eline(-x,-oy,-ox, -y, p);
- eline(ox,-oy, x, -y, p);
- eline(-x, y,-ox, oy, p);
- eline(ox, y, x, oy, p);
- ox = x+1;
- x = nx;
- y--;
- oy = y;
- } while(oy > 0);
- }
- /*
- * a rectangle with closed (not half-open) coordinates expressed
- * relative to the center of the ellipse
- */
- static
- void
- erect(int x0, int y0, int x1, int y1, Param *p)
- {
- Rectangle r;
- /* print("R %d,%d %d,%d\n", x0, y0, x1, y1); /**/
- r = Rect(p->c.x+x0, p->c.y+y0, p->c.x+x1+1, p->c.y+y1+1);
- memdraw(p->dst, r, p->src, addpt(p->sp, r.min), memopaque, p00, p->op);
- }
- /*
- * a brushed point similarly specified
- */
- static
- void
- epoint(int x, int y, Param *p)
- {
- Point p0;
- Rectangle r;
- /* print("P%d %d,%d\n", p->t, x, y); /**/
- p0 = Pt(p->c.x+x, p->c.y+y);
- r = Rpt(addpt(p0, p->disc->r.min), addpt(p0, p->disc->r.max));
- memdraw(p->dst, r, p->src, addpt(p->sp, r.min), p->disc, p->disc->r.min, p->op);
- }
- /*
- * a brushed horizontal or vertical line similarly specified
- */
- static
- void
- eline(int x0, int y0, int x1, int y1, Param *p)
- {
- /* print("L%d %d,%d %d,%d\n", p->t, x0, y0, x1, y1); /**/
- if(x1 > x0+1)
- erect(x0+1, y0-p->t, x1-1, y1+p->t, p);
- else if(y1 > y0+1)
- erect(x0-p->t, y0+1, x1+p->t, y1-1, p);
- epoint(x0, y0, p);
- if(x1-x0 || y1-y0)
- epoint(x1, y1, p);
- }
|