ref: 9ced712a2ad70557a8ceb89ff3b969b92c85f68c
dir: /sys/src/cmd/plot/libplot/fill.c/
/* * fill -- polygon tiler * Updating the edgelist from scanline to scanline could be quicker if no * edges cross: we can just merge the incoming edges. If the scan-line * filling routine were a parameter, we could do textured * polygons, polyblt, and other such stuff. */ #include "mplot.h" typedef enum{ Odd=1, Nonzero=~0 }Windrule; typedef struct edge Edge; struct edge{ Point p; /* point of crossing current scan-line */ int maxy; /* scan line at which to discard edge */ int dx; /* x increment if x fraction<1 */ int dx1; /* x increment if x fraction>=1 */ int x; /* x fraction, scaled by den */ int num; /* x fraction increment for unit y change, scaled by den */ int den; /* x fraction increment for unit x change, scaled by num */ int dwind; /* increment of winding number on passing this edge */ Edge *next; /* next edge on current scanline */ Edge *prev; /* previous edge on current scanline */ }; static void insert(Edge *ep, Edge **yp){ while(*yp && (*yp)->p.x<ep->p.x) yp=&(*yp)->next; ep->next=*yp; *yp=ep; if(ep->next){ ep->prev=ep->next->prev; ep->next->prev=ep; if(ep->prev) ep->prev->next=ep; } else ep->prev=0; } static void polygon(int cnt[], double *pts[], Windrule w, int v){ Edge *edges, *ep, *nextep, **ylist, **eylist, **yp; Point p, q, p0, p1, p10; int i, dy, nbig, y, left, right, wind, nwind, nvert; int *cntp; double **ptsp, *xp; nvert=0; for(cntp=cnt;*cntp;cntp++) nvert+=*cntp; edges=(Edge *)malloc(nvert*sizeof(Edge)); if(edges==0){ NoSpace: sysfatal("polygon: no space"); } ylist=(Edge **)malloc(Dy(screen->r)*sizeof(Edge *)); if(ylist==0) goto NoSpace; eylist=ylist+Dy(screen->r); for(yp=ylist;yp!=eylist;yp++) *yp=0; ep=edges; for(cntp=cnt,ptsp=pts;*cntp;cntp++,ptsp++){ p.x=SCX((*ptsp)[*cntp*2-2]); p.y=SCY((*ptsp)[*cntp*2-1]); nvert=*cntp; for(xp=*ptsp,i=0;i!=nvert;xp+=2,i++){ q=p; p.x=SCX(xp[0]); p.y=SCY(xp[1]); if(p.y==q.y) continue; if(p.y<q.y){ p0=p; p1=q; ep->dwind=1; } else{ p0=q; p1=p; ep->dwind=-1; } if(p1.y<=screen->r.min.y) continue; if(p0.y>=screen->r.max.y) continue; ep->p=p0; if(p1.y>screen->r.max.y) ep->maxy=screen->r.max.y; else ep->maxy=p1.y; p10=subpt(p1, p0); if(p10.x>=0){ ep->dx=p10.x/p10.y; ep->dx1=ep->dx+1; } else{ p10.x=-p10.x; ep->dx=-(p10.x/p10.y); /* this nonsense rounds toward zero */ ep->dx1=ep->dx-1; } ep->x=0; ep->num=p10.x%p10.y; ep->den=p10.y; if(ep->p.y<screen->r.min.y){ dy=screen->r.min.y-ep->p.y; ep->x+=dy*ep->num; nbig=ep->x/ep->den; ep->p.x+=ep->dx1*nbig+ep->dx*(dy-nbig); ep->x%=ep->den; ep->p.y=screen->r.min.y; } insert(ep, ylist+(ep->p.y-screen->r.min.y)); ep++; } } left = 0; for(yp=ylist,y=screen->r.min.y;yp!=eylist;yp++,y++){ wind=0; for(ep=*yp;ep;ep=nextep){ nwind=wind+ep->dwind; if(nwind&w){ /* inside */ if(!(wind&w)){ left=ep->p.x; if(left<screen->r.min.x) left=screen->r.min.x; } } else if(wind&w){ right=ep->p.x; if(right>=screen->r.max.x) right=screen->r.max.x; #define BART_BUG_FIXED /* what goes on here?? -rob */ #ifdef BART_BUG_FIXED if(right>left) line(screen, Pt(left, y), Pt(right, y), Endsquare, Endsquare, 0, getcolor(v), ZP); #else if(right>left){ switch(v){ default: segment(&screen, Pt(left, y), Pt(right, y), ~0, D&~S); segment(&screen, Pt(left, y), Pt(right, y), v, f); break; case 0: segment(&screen, Pt(left, y), Pt(right, y), ~0, D&~S); break; case 3: segment(&screen, Pt(left, y), Pt(right, y), v, f); break; } } #endif } wind=nwind; nextep=ep->next; if(++ep->p.y!=ep->maxy){ ep->x+=ep->num; if(ep->x>=ep->den){ ep->x-=ep->den; ep->p.x+=ep->dx1; } else ep->p.x+=ep->dx; insert(ep, yp+1); } } } free((char *)edges); free((char *)ylist); } void fill(int num[], double *ff[]){ polygon(num, ff, Odd, e1->foregr); }