/***************************************************************************** DRIFT vers 20030819 Draws linked parametric curves to form bezier curves linked to for a loop. For: t = 0 -> 1 (start to end of curve) x0,y0 = start point x1,y1 = attractor point x2,y2 = end point then x(t) = (1-t)^2*x0 + 2*(1-t)*t*x1 + t^2*x2 y(t) = (1-t)^2*y0 + 2*(1-t)*t*y1 + t^2*y2 See: http://www.dcs.kcl.ac.uk/teaching/units/cs3grs/curvestutor/bezier1.html Written by Neil Robertson, August 2003 *****************************************************************************/ #include #include #include #include #include #define WIDTH 300 #define HEIGHT 300 #define MAX_ADD 5 /* Point count must be an even number since a loop has to have an even number of points (eg 5 attractor points , 5 join points) */ #define POINTCNT 40 #define LINECNT 10 #define TINC 0.05 #define DELAY 10000 #define COLOUR "white" char *disp; Display *display; Window win; XdbeBackBuffer backbuf; XdbeSwapInfo swapinfo; Drawable drw; XPoint *parray; GC gcfill; GC *gc; float **x; float **y; float *xadd; float *yadd; int pointcnt; int join_points; int linecnt; int cycle_inc; int width; int height; float tinc; int delay; int inroot; int max_add; int use_dbe; char *colour; short fill_minx; short fill_miny; short fill_maxx; short fill_maxy; /*** Forward declarations ***/ void parse_cmd_line(int argc, char **argv); void setup_X(); void setup_start_points(); void mainloop(); float new_add_value(); /*** Start here ***/ main(int argc, char **argv) { parse_cmd_line(argc,argv); setup_X(); setup_start_points(); mainloop(); } #define BADARGS (i == argc - 1 || argv[i+1][0] == '-') /*** Parse the command line and init all the variables ***/ void parse_cmd_line(int argc, char **argv) { int i,o,size; char *opt[] = { "d", "w", "h", "dbe", "cyc", "tinc", "delay", "inroot", "lines", "points", "dist", "col", "join" }; enum { OPT_DISPLAY, OPT_WIDTH, OPT_HEIGHT, OPT_DBE, OPT_CYCLE, OPT_TINC, OPT_DELAY, OPT_INROOT, OPT_LINES, OPT_POINTS, OPT_DIST, OPT_COLOUR, OPT_JOIN, OPT_END }; disp = NULL; width = WIDTH; height = HEIGHT; pointcnt = POINTCNT; linecnt = LINECNT; use_dbe = 0; cycle_inc = 0; tinc = TINC; delay = DELAY; max_add = MAX_ADD; inroot = 0; colour = COLOUR; join_points = 0; for(i=1;i < argc;++i) { if (argv[i][0] != '-') goto USAGE; for(o=0;o != OPT_END;++o) if (!strcmp(opt[o],argv[i]+1)) break; switch(o) { case OPT_DISPLAY: if (BADARGS) goto USAGE; disp = argv[++i]; break; case OPT_WIDTH: if (BADARGS) goto USAGE; width = atoi(argv[++i]); break; case OPT_HEIGHT: if (BADARGS) goto USAGE; height = atoi(argv[++i]); break; case OPT_DBE: use_dbe = 1; break; case OPT_CYCLE: if (BADARGS) goto USAGE; cycle_inc = atoi(argv[++i]);; break; case OPT_TINC: if (BADARGS) goto USAGE; tinc = atof(argv[++i]); if (tinc <= 0 || tinc >= 1) { puts("ERROR: tinc must be in the range > 0 and < 1"); exit(1); } break; case OPT_DELAY: if (BADARGS) goto USAGE; delay = atoi(argv[++i]); break; case OPT_INROOT: inroot = 1; break; case OPT_LINES: if (BADARGS) goto USAGE; linecnt = atoi(argv[++i]); break; case OPT_POINTS: if (BADARGS) goto USAGE; pointcnt = atoi(argv[++i]); if ((pointcnt % 2)) { puts("ERROR: points must be an even number"); exit(1); } break; case OPT_DIST: if (BADARGS) goto USAGE; max_add = atoi(argv[++i]); break; case OPT_COLOUR: if (BADARGS) goto USAGE; colour = argv[++i]; break; case OPT_JOIN: join_points = 1; break; default: goto USAGE; } } if (cycle_inc > linecnt - 1) { puts("ERROR: colour cycle increment must be < line count"); exit(1); } if (inroot && (width != WIDTH || height != HEIGHT)) { puts("ERROR: The 'inroot' option cannot be used with the 'w' and 'h' options.") ; exit(1); } /* Setup arrays */ size = sizeof(float); x = (float **)malloc(size * linecnt); y = (float **)malloc(size * linecnt); gc = (GC *)malloc(sizeof(GC) * linecnt); xadd = (float *)malloc(size * pointcnt); yadd = (float *)malloc(size * pointcnt); for(i=0;i < linecnt;++i) { x[i] = (float *)malloc(size * pointcnt); y[i] = (float *)malloc(size * pointcnt); } parray = (XPoint *)malloc( sizeof(XPoint) * (int)((float)pointcnt * (1 / tinc + 1) / 2)); return; USAGE: printf("Usage: %s\n" " -d : display to connect to\n" " -w : window width\n" " -h : window height\n" " -inroot : run in root window\n" " -lines : sets number of trail lines\n" " -points : sets total number of curve points\n" " -join : join the points together\n" " -dbe : uses the X double buffer extension\n" " -col /random : sets colour to use\n" " -cyc : colour cycling increment\n" " -tinc : sets curve iteration size\n" " -dist : max distance to add on to move lines\n" " -delay : sets update delay in microseconds\n\n" "All arguments are optional.\n",argv[0]); exit(1); } /*** Set up the graphics contexts ***/ void setup_X() { Window rootwin; Colormap cmap; XColor col,actual; XGCValues gcvals; XTextProperty title_prop; int screen,black,i,ran; unsigned short redadd,greenadd,blueadd; char colstr[15]; char *title = "..ooOO DRIFT OOoo.."; srandom(time(0)); if (!(display=XOpenDisplay(disp))) { fprintf(stderr,"XOpenDisplay(): Can't connect to: %s\n", XDisplayName(disp)); exit(1); } screen=DefaultScreen(display); rootwin = RootWindow(display,screen); black=BlackPixel(display,screen); cmap=DefaultColormap(display,screen); /* Create the window (unless we're in the root) and back buffer */ if (inroot) { win = rootwin; width = DisplayWidth(display,screen); height = DisplayHeight(display,screen); XSetWindowBackground(display,win,black); XClearWindow(display,win); } else { win = XCreateSimpleWindow(display,rootwin,0,0,width,height,0,0,black); /* Set title */ XStringListToTextProperty(&title,1,&title_prop); XSetWMProperties(display,win,&title_prop,NULL,NULL,0,NULL,NULL,NULL); } if (use_dbe) { backbuf = XdbeAllocateBackBufferName(display,win,XdbeBackground); swapinfo.swap_window = win; swapinfo.swap_action = XdbeBackground; drw = backbuf; } else drw = win; /* Create graphics contexts */ gcvals.foreground = black; gcfill = XCreateGC(display,win,GCForeground,&gcvals); if (!strcmp(colour,"random")) ran = 1; else { ran = 0; if (!XParseColor(display,cmap,colour,&col)) { printf("ERROR: Unknown colour: %s\n",colour); exit(1); } redadd = col.red / linecnt; greenadd = col.green / linecnt; blueadd = col.blue / linecnt; } gcvals.background = black; for(i=0;i < linecnt;++i) { if (ran) sprintf(colstr,"#%04X%04X%04X", random()%65536,random()%65536,random()%65536); else sprintf(colstr,"#%04X%04X%04X", redadd * (i+1),greenadd * (i+1),blueadd * (i+1)); if (!XAllocNamedColor(display,cmap,colstr,&col,&actual)) printf("ERROR: Can't allocate colour: %s\n",colstr); gcvals.foreground=col.pixel; gc[i] = XCreateGC(display,win,GCForeground | GCBackground,&gcvals); } /* Select event type we want to receive */ XSelectInput(display,win,ExposureMask | StructureNotifyMask); /* Display the window */ XMapWindow(display,win); /* Set up screen clear vars */ fill_minx = width; fill_maxx = 0; fill_miny = height; fill_maxy = 0; } /*** Setup the initial line points ***/ void setup_start_points() { int i,prev; /* Set the attactor (middle) points of the curves. The curves don't actually go through these but they define its shape, the points either sides are averages of the attractor points they're between so we get smooth curve joins. */ for(i=1;i < pointcnt;i+=2) { x[0][i] = random()%(width/2) + width/4; y[0][i] = random()%(height/2) + height/4; xadd[i] = new_add_value(); yadd[i] = new_add_value(); } /* Setup join points (either side of attractors) */ for(i=0;i < pointcnt-1;i+=2) { prev = (i ? i-1 : pointcnt-1); /* First point uses last attractor */ x[0][i] = x[0][prev] + (x[0][i+1] - x[0][prev])/2; y[0][i] = y[0][prev] + (y[0][i+1] - y[0][prev])/2; } } /*** Do the business ***/ void mainloop() { XEvent event; int startline,currline,prevline; int i,j,l,p,col,startcol,mpcnt; int prev,next; float t,msq,sq; mpcnt = pointcnt - 1; startline = currline = 0; for(startcol=0;;startcol = (startcol + cycle_inc) % linecnt) { /* Ignore expose events. We only select on them so window appears */ XCheckTypedEvent(display,Expose,&event); /* We won't get these if running in root window */ if (XCheckTypedEvent(display,ConfigureNotify,&event)) { width = event.xconfigure.width; height = event.xconfigure.height; } if (!use_dbe) { XFillRectangle( display, win, gcfill, fill_minx, fill_miny, (fill_maxx - fill_minx)+2, (fill_maxy - fill_miny)+2); fill_minx = width; fill_maxx = 0; fill_miny = height; fill_maxy = 0; } /* Draw the curves. It would be more efficient to have the 2 inner loops reversed as then sq & msq would only be calculated once for each set of points but this would make line drawing much more difficult as the points wouldn't be adjacent. */ for(i=0,l=startline,col=startcol;i < linecnt;++i) { for(j=1,p=0;j < pointcnt;j+=2) { next = (j == mpcnt ? 0 : j+1); for(t=0;t < 1;t += tinc,++p) { sq = t * t; msq = (1-t) * (1-t); parray[p].x = (short) (msq * x[l][j-1] + 2 * (1-t) * t * x[l][j] + sq * x[l][next]); parray[p].y = (short) (height - (msq * y[l][j-1] + 2 * (1-t) * t * y[l][j] + sq * y[l][next])); if (parray[p].x < fill_minx) fill_minx = parray[p].x; if (parray[p].y < fill_miny) fill_miny = parray[p].y; if (parray[p].x > fill_maxx) fill_maxx = parray[p].x; if (parray[p].y > fill_maxy) fill_maxy = parray[p].y; } } /* Draw all the points/lines for the given line (and GC which is why we can't do all points at the end of outside loop) */ if (join_points) { XDrawLines( display,drw,gc[col],parray,p,CoordModeOrigin); /* Join last point to first */ XDrawLine( display,drw,gc[col], parray[0].x,parray[0].y, parray[p-1].x,parray[p-1].y); } else XDrawPoints( display,drw,gc[col],parray,p,CoordModeOrigin); l = (l + 1) % linecnt; col = (col + 1) % linecnt; } if (use_dbe) XdbeSwapBuffers(display,&swapinfo,1); else XFlush(display); /* Even if passed a parameter of 0 usleep() causes a perceptable delay so if delay = 0 then bypass it */ if (delay) usleep(delay); prevline = currline; currline = (currline + 1) % linecnt; if (currline == startline) startline = (startline + 1) % linecnt; /* Setup attractor points for next line */ for(i=1;i < pointcnt;i+=2) { x[currline][i] = x[prevline][i] + xadd[i]; y[currline][i] = y[prevline][i] + yadd[i]; if (x[currline][i] < 0) { x[currline][i] = 0; xadd[i] = -xadd[i]; } else if (x[currline][i] > width) { x[currline][i] = width; xadd[i] = -xadd[i]; } if (y[currline][i] < 0) { y[currline][i] = 0; yadd[i] = -yadd[i]; } else if (y[currline][i] > height) { y[currline][i] = height; yadd[i] = -yadd[i]; } if (!(random()%100)) xadd[i] = new_add_value(); if (!(random()%100)) yadd[i] = new_add_value(); } /* Setup join points */ for(i=0;i < mpcnt;i+=2) { prev = (i ? i-1 : pointcnt-1); x[currline][i] = x[currline][prev] + (x[currline][i+1] - x[currline][prev])/2; y[currline][i] = y[currline][prev] + (y[currline][i+1] - y[currline][prev])/2; } } } /*** Return random float value between -max_add and max_add ***/ float new_add_value() { return (float)((random() % (max_add * 200)) - max_add * 100)/100; }