/*
* dynadraw -
*
* Use a simple dynamics model to create caligraphic 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
http://www.sgi.com/grafica/dyna/dynadraw.c
(possibly inaccurate URL)
01/1995