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