168 lines
4.0 KiB
C
168 lines
4.0 KiB
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:
|
|
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);
|
|
}
|