/***************************************************************************** TENDRIL vers 20030819 A tendril swirls around the virtual space. There are probably too many globals here but I don't care , it was a quick n dirty program and it looks pretty. Written by Neil Robertson, July/Aug 2003 *****************************************************************************/ #include #include #include #include #define NUM_POINTS 100 #define MAX_ANGLE_INC 30 #define MAX_ANGLE_INC_ADD 5 #define RAND_PROB 50 #define MAX_GRAVITY 0.05 #define HEADLEN 20 #define WIDTH 200 #define HEIGHT 200 #define DELAY 10000 #define DPR 57.29578 /* Degrees per radian */ Display *display; Window win; GC gc[4]; struct pnt_struct { float cx,cy; float angle; float angle_inc; float radius; float xgrav; float ygrav; int gcnum; int cnt; } *point; char *disp; int width; int height; int width2; int height2; int start_pnt; int next_pnt; int max_radius; int max_radius_inc; int max_angle_inc; float radius; float radius_inc; float angle; float angle_inc; float angle_inc_add; float cx; float cy; float cx_inc; float cy_inc; float xgravity; float xgravity_inc; float ygravity; float ygravity_inc; float xscale,yscale; float fill_minx; float fill_miny; float fill_maxx; float fill_maxy; float max_gravity; int num_points; int headlen; int pnt_count; int next_gc; int inroot; int rand_prob; int clear_win; int delay; void parse_cmd_line(int argc, char **argv); void init(); void set_size_vars(); void next_point(); void draw_lines(); void set_xy(int p, float *x, float *y); void do_gravity(); /*** START HERE ***/ main(int argc, char **argv) { XEvent event; parse_cmd_line(argc,argv); init(); while(1) { /* 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)) { if (event.xconfigure.width != width) { xscale = (float)event.xconfigure.width / width; width = event.xconfigure.width; set_size_vars(); } if (event.xconfigure.height != height) { yscale = (float)event.xconfigure.height / height; height = event.xconfigure.height; set_size_vars(); } } next_point(); draw_lines(); do_gravity(); /* If delay = 0 skip usleep() since it introduces noticable delays even if passed zero */ if (delay) usleep(delay); } } /*** Get the arguments ***/ void parse_cmd_line(int argc, char **argv) { int i,o; char *opt[] = { "d", "inroot", "w", "h", "len", "grav", "head", "angleinc", "rand", "noclear", "delay" }; enum { OPT_DISPLAY, OPT_INROOT, OPT_WIDTH, OPT_HEIGHT, OPT_LENGTH, OPT_GRAVITY, OPT_HEAD, OPT_ANGLEINC, OPT_RAND, OPT_NOCLEAR, OPT_DELAY, OPT_END }; disp = NULL; inroot = 0; width = WIDTH; height = HEIGHT; num_points = NUM_POINTS; max_gravity = MAX_GRAVITY; max_angle_inc = MAX_ANGLE_INC; headlen = HEADLEN; rand_prob = RAND_PROB; clear_win = 1; delay = DELAY; 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; if (o == OPT_END || (i == argc - 1 && o != OPT_INROOT && o != OPT_NOCLEAR)) goto USAGE; switch(o) { case OPT_DISPLAY: disp = argv[++i]; break; case OPT_INROOT: inroot = 1; break; case OPT_WIDTH: width = atoi(argv[++i]); break; case OPT_HEIGHT: height = atoi(argv[++i]); break; case OPT_LENGTH: num_points = atoi(argv[++i]); break; case OPT_GRAVITY: max_gravity = (float)atoi(argv[++i]) / 1000; break; case OPT_HEAD: headlen = atoi(argv[++i]); break; case OPT_ANGLEINC: max_angle_inc = atoi(argv[++i]); break; case OPT_RAND: rand_prob = atoi(argv[++i]); break; case OPT_NOCLEAR: /* This is only for if running in the root window really */ clear_win = 0; break; case OPT_DELAY: delay = atoi(argv[++i]); break; default: goto USAGE; } } if (inroot && (width != WIDTH || height != HEIGHT)) { puts("The 'inroot' option cannot be used with the 'w' and 'h' options."); exit(1); } return; USAGE: printf("Usage: %s\n" " [-d ]\n" " [[-w ] [-h ] | [-inroot]]\n" " [-noclear]\n" " [-len ]\n" " [-grav ]\n" " [-head ]\n" " [-angleinc ]\n" " [-rand ]\n" " [-delay ]\n",argv[0]); exit(1); } /*** Init stuff not done in parse_cmd_line ***/ void init() { Window rootwin; XGCValues gcvals; XColor col,dummy; Colormap cmap; int screen,black,white; if (!(display=XOpenDisplay(disp))) { fprintf(stderr,"XOpenDisplay(): Can't connect to: %s\n",XDisplayName(disp)); exit(1); } screen=DefaultScreen(display); black=BlackPixel(display,screen); white=WhitePixel(display,screen); rootwin = RootWindow(display,screen); /* Create window unless we're running in the root */ if (inroot) { width = DisplayWidth(display,screen); height = DisplayHeight(display,screen); win = rootwin; if (clear_win) { XSetWindowBackground(display,win,black); XClearWindow(display,win); } } else { win=XCreateSimpleWindow( display,rootwin,0,0,width,height,0,white,black); XSelectInput(display,win,ExposureMask | StructureNotifyMask); XMapWindow(display,win); } /* GCs & other stuff */ cmap=DefaultColormap(display,screen); XAllocNamedColor(display,cmap,"blue",&col,&dummy); gcvals.foreground = col.pixel; gcvals.background = black; gc[0] = XCreateGC(display,win,GCForeground | GCBackground, &gcvals); XAllocNamedColor(display,cmap,"purple",&col,&dummy); gcvals.foreground = col.pixel; gcvals.background = black; gc[1] = XCreateGC(display,win,GCForeground | GCBackground, &gcvals); XAllocNamedColor(display,cmap,"red",&col,&dummy); gcvals.foreground = col.pixel; gcvals.background = black; gc[2] = XCreateGC(display,win,GCForeground | GCBackground, &gcvals); gcvals.foreground = black; gcvals.background = white; gc[3] = XCreateGC(display,win,GCForeground | GCBackground, &gcvals); /* Everything ok so far , init other vars and start */ point = (struct pnt_struct *)malloc(sizeof(struct pnt_struct) * num_points); set_size_vars(); fill_minx = width; fill_maxx = 0; fill_miny = height; fill_maxy = 0; pnt_count = 0; srandom(time(0)); start_pnt = 0; next_pnt = 0; cx = width/2; cy = height/2; cx_inc = 0; cy_inc = 0; radius = max_radius; radius_inc = 0; angle = 0; angle_inc = angle_inc_add = random() % MAX_ANGLE_INC_ADD; xscale = 1; yscale = 1; xgravity = 0; ygravity = 0; xgravity_inc = ygravity_inc = max_gravity / 50; next_gc = 0; } /*** Set variables that change if the window size changes ***/ void set_size_vars() { int min; min = (width < height) ? width : height; max_radius = min/4; max_radius_inc = min/10; width2 = width/2; height2 = height/2; } /*** Work out x,y position of next point and add to list ***/ void next_point() { float x,y; int tmp; radius += radius_inc; if (fabs(radius) > max_radius) { radius = max_radius * ((radius >= 0) - (radius < 0)); radius_inc = -radius_inc/max_radius_inc; } angle += angle_inc; angle += (angle < 0) * 360 - (angle > 360) * 360; cx += cx_inc; cy += cy_inc; if (cx < max_radius) cx = max_radius; if (cx > (tmp = width - max_radius)) cx = tmp; if (cy < max_radius) cy = max_radius; if (cy > (tmp = height - max_radius)) cy = tmp; point[next_pnt].cx = cx; point[next_pnt].cy = cy; point[next_pnt].angle = angle; point[next_pnt].angle_inc = angle_inc/10; point[next_pnt].radius = radius; point[next_pnt].xgrav = 0; point[next_pnt].ygrav = 0; point[next_pnt].gcnum = next_gc; point[next_pnt].cnt = 0; next_gc = (next_gc + 1) % 3; tmp = angle_inc + angle_inc_add; if (fabs(tmp) <= max_angle_inc) angle_inc = tmp; else angle_inc_add = -angle_inc_add; if (!(random() % rand_prob)) cx_inc = (float)(random() % width - width2) / 5; if (!(random() % rand_prob)) cy_inc = (float)(random() % height - height2) / 5; if (!(random() % rand_prob)) radius_inc = (random() % (max_radius_inc * 2 + 1)) - max_radius_inc; if (!(random() % rand_prob)) angle_inc_add = (random() % (MAX_ANGLE_INC_ADD * 2 + 1)) - MAX_ANGLE_INC_ADD; next_pnt = (next_pnt + 1) % num_points; if (next_pnt == start_pnt) start_pnt = (start_pnt + 1) % num_points; else pnt_count++; } /*** Draw lines ***/ void draw_lines() { int p1,p2; float x1,y1,x2,y2; if (pnt_count < 2) return; XFillRectangle( display, win, gc[3], (int)fill_minx,(int)fill_miny, (int)(fill_maxx - fill_minx)+2,(int)(fill_maxy - fill_miny)+2); /* Reset. This looks the wrong way around but think about it... */ fill_minx = width; fill_maxx = 0; fill_miny = height; fill_maxy = 0; p1 = start_pnt; p2 = (start_pnt + 1) % num_points; set_xy(p1,&x1,&y1); do { set_xy(p2,&x2,&y2); XDrawLine( display, win,gc[point[p1].gcnum],(int)x1,(int)y1,(int)x2,(int)y2); /* Apply gravity and rotation to points */ point[p1].cx += point[p1].xgrav; point[p1].cy += point[p1].ygrav; point[p1].angle += point[p1].angle_inc; point[p1].xgrav += xgravity; point[p1].ygrav += ygravity; if (point[p1].cnt < headlen) { if (!(point[p1].cnt % 3)) point[p1].gcnum = (point[p1].gcnum + 1) % 3; point[p1].cnt++; } else point[p1].gcnum = 0; p1 = p2; p2 = (p2 + 1) % num_points; x1 = x2; y1 = y2; } while (p2 != next_pnt); } /*** Set a point ***/ void set_xy(int p, float *x, float *y) { *x = (point[p].cx + sin(point[p].angle / DPR) * point[p].radius) * xscale; *y = (point[p].cy + cos(point[p].angle / DPR) * point[p].radius) * yscale; if (*x < fill_minx) fill_minx = *x; if (*x > fill_maxx) fill_maxx = *x; if (*y < fill_miny) fill_miny = *y; if (*y > fill_maxy) fill_maxy = *y; } /*** Set gravity pull ***/ void do_gravity() { float tmp; tmp = xgravity + xgravity_inc; if (fabs(tmp) < max_gravity) xgravity = tmp; tmp = ygravity + ygravity_inc; if (fabs(tmp) < max_gravity) ygravity = tmp; if (!(random() % rand_prob)) xgravity_inc = -xgravity_inc; if (!(random() % rand_prob)) ygravity_inc = -ygravity_inc; }