/* * dynadraw - * * Use a simple dynamics model to create calligraphic strokes. * * To compile: cc dynadraw.c -o dynadraw -lgl -lm * * leftmouse - used for drawing * middlemouse - clears page * rightmouse - menu * * uparrow - wider strokes * downarrow - narrower strokes * * Paul Haeberli - 1989 * */ #include #include #include #include #include #include #include #define SLIDERHIGH (15) #define SLIDERLEFT (200) #define TIMESLICE (0.005) #define MAXPOLYS (50000) typedef struct filter { float curx, cury; float velx, vely, vel; float accx, accy, acc; float angx, angy; float mass, drag; float lastx, lasty; int fixedangle; } filter; float initwidth = 1.5; float width; float odelx, odely; float curmass, curdrag; float polyverts[4*2*MAXPOLYS]; int npolys; long xsize, ysize; long xorg, yorg; filter mouse; float flerp(); float fgetmousex(); float fgetmousey(); float gettime(); unsigned long getltime(); filtersetpos(f,x,y) filter *f; float x, y; { f->curx = x; f->cury = y; f->lastx = x; f->lasty = y; f->velx = 0.0; f->vely = 0.0; f->accx = 0.0; f->accy = 0.0; } filterapply(f,mx,my) filter *f; float mx, my; { float mass, drag; float fx, fy, force; /* calculate mass and drag */ mass = flerp(1.0,160.0,curmass); drag = flerp(0.00,0.5,curdrag*curdrag); /* calculate force and acceleration */ fx = mx-f->curx; fy = my-f->cury; f->acc = sqrt(fx*fx+fy*fy); if(f->acc<0.000001) return 0; f->accx = fx/mass; f->accy = fy/mass; /* calculate new velocity */ f->velx += f->accx; f->vely += f->accy; f->vel = sqrt(f->velx*f->velx+f->vely*f->vely); f->angx = -f->vely; f->angy = f->velx; if(f->vel<0.000001) return 0; /* calculate angle of drawing tool */ f->angx /= f->vel; f->angy /= f->vel; if(f->fixedangle) { f->angx = 0.6; f->angy = 0.2; } /* apply drag */ f->velx = f->velx*(1.0-drag); f->vely = f->vely*(1.0-drag); /* update position */ f->lastx = f->curx; f->lasty = f->cury; f->curx = f->curx+f->velx; f->cury = f->cury+f->vely; return 1; } float paramval() { float p; p = (float)(getmousex()-SLIDERLEFT)/(xsize-SLIDERLEFT); if(p<0.0) return 0.0; if(p>1.0) return 1.0; return p; } main() { short val; int menu, pres; float p, mx, my; curmass = 0.5; curdrag = 0.15; prefsize(800,640); initbuzz(); winopen("dynadraw"); glcompat(GLC_OLDPOLYGON,1); subpixel(1); shademodel(FLAT); gconfig(); qdevice(LEFTMOUSE); qdevice(MIDDLEMOUSE); qdevice(MENUBUTTON); qdevice(RIGHTSHIFTKEY); qdevice(UPARROWKEY); qdevice(DOWNARROWKEY); qdevice(RIGHTSHIFTKEY); makeframe(); menu = defpup("dynadraw %t|toggle line style|save PostScript"); width = initwidth; mouse.fixedangle = 1; while(1) { switch(qread(&val)) { case REDRAW: makeframe(); break; case MIDDLEMOUSE: if(val) clearscreen(); break; case UPARROWKEY: if(val) initwidth *= 1.414213; width = initwidth; break; case DOWNARROWKEY: if(val) initwidth /= 1.414213; width = initwidth; break; case MENUBUTTON: if(val) { switch(dopup(menu)) { case 1: mouse.fixedangle = 1-mouse.fixedangle; break; case 2: savepolys(); break; } } break; case LEFTMOUSE: if(val) { my = getmousey(); if(my>0*SLIDERHIGH && my<2*SLIDERHIGH) { if(my>SLIDERHIGH) { while(getbutton(LEFTMOUSE)) { p = paramval(); if(p != curmass) { curmass = p; showsettings(); } } } else { while(getbutton(LEFTMOUSE)) { p = paramval(); if(p != curdrag) { curdrag = p; showsettings(); } } } } else { mx = 1.25*fgetmousex(); my = fgetmousey(); filtersetpos(&mouse,mx,my); odelx = 0.0; odely = 0.0; while(getbutton(LEFTMOUSE)) { mx = 1.25*fgetmousex(); my = fgetmousey(); if(filterapply(&mouse,mx,my)) { drawsegment(&mouse); color(0); buzz(); } } } } break; } } } makeframe() { reshapeviewport(); getsize(&xsize,&ysize); getorigin(&xorg,&yorg); clearscreen(); } clearscreen() { int x, y; ortho2(0.0,1.25,0.0,1.0); color(51); setpattern(0); clear(); npolys = 0; showsettings(); color(0); } showsettings() { char str[256]; int xpos; ortho2(-0.5,xsize-0.5,-0.5,ysize-0.5); color(51); rectfi(0,0,xsize,2*SLIDERHIGH); color(0); sprintf(str,"Mass %g",curmass); cmov2i(20,3+1*SLIDERHIGH); charstr(str); sprintf(str,"Drag %g",curdrag); cmov2i(20,3+0*SLIDERHIGH); charstr(str); move2i(SLIDERLEFT,0); draw2i(SLIDERLEFT,2*SLIDERHIGH); move2i(0,1*SLIDERHIGH); draw2i(xsize,1*SLIDERHIGH); move2i(0,2*SLIDERHIGH); draw2i(xsize,2*SLIDERHIGH); color(1); xpos = SLIDERLEFT+curmass*(xsize-SLIDERLEFT); rectfi(xpos,1*SLIDERHIGH,xpos+4,2*SLIDERHIGH); xpos = SLIDERLEFT+curdrag*(xsize-SLIDERLEFT); rectfi(xpos,0*SLIDERHIGH,xpos+4,1*SLIDERHIGH); ortho2(0.0,1.25,0.0,1.0); } drawsegment(f) filter *f; { float delx, dely; float wid, *fptr; float px, py, nx, ny; wid = 0.04-f->vel; wid = wid*width; if(wid<0.00001) wid = 0.00001; delx = f->angx*wid; dely = f->angy*wid; color(0); px = f->lastx; py = f->lasty; nx = f->curx; ny = f->cury; fptr = polyverts+8*npolys; bgnpolygon(); fptr[0] = px+odelx; fptr[1] = py+odely; v2f(fptr); fptr += 2; fptr[0] = px-odelx; fptr[1] = py-odely; v2f(fptr); fptr += 2; fptr[0] = nx-delx; fptr[1] = ny-dely; v2f(fptr); fptr += 2; fptr[0] = nx+delx; fptr[1] = ny+dely; v2f(fptr); fptr += 2; endpolygon(); npolys++; if(npolys>=MAXPOLYS) { fprintf(stderr,"out of polys - increase the define MAXPOLYS\n"); npolys--; } fptr -= 8; bgnclosedline(); v2f(fptr); fptr += 2; v2f(fptr); fptr += 2; v2f(fptr); fptr += 2; v2f(fptr); fptr += 2; endclosedline(); odelx = delx; odely = dely; } int buzztemp, buzzmax; /* this returns the time in 100ths of a sec since the system was rebooted */ unsigned long getltime() { struct tms ct; return times(&ct); } /* this should spin for TIMESLICE seconds afteer "calibration" */ buzz() { int i; for(i=0; i