| /* |
| * 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: |
| fprintf(stderr, "polygon: no space\n"); |
| exits("malloc failed"); |
| } |
| 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); |
| } |