source: trunk/third/xscreensaver/hacks/attraction.c @ 20148

Revision 20148, 28.8 KB checked in by ghudson, 21 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r20147, which included commits to RCS files with non-trunk default branches.
Line 
1/* xscreensaver, Copyright (c) 1992, 1995, 1996, 1997, 1998, 2001
2 *  Jamie Zawinski <jwz@jwz.org>
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation.  No representations are made about the suitability of this
9 * software for any purpose.  It is provided "as is" without express or
10 * implied warranty.
11 */
12
13/* Simulation of a pair of quasi-gravitational fields, maybe sorta kinda
14   a little like the strong and weak electromagnetic forces.  Derived from
15   a Lispm screensaver by John Pezaris <pz@mit.edu>.  Mouse control and
16   viscosity added by "Philip Edward Cutone, III" <pc2d+@andrew.cmu.edu>.
17
18   John sez:
19
20       The simulation started out as a purely accurate gravitational
21       simulation, but, with constant simulation step size, I quickly
22       realized the field being simulated while grossly gravitational
23       was, in fact, non-conservative.  It also had the rather annoying
24       behavior of dealing very badly with colliding orbs.  Therefore,
25       I implemented a negative-gravity region (with two thresholds; as
26       I read your code, you only implemented one) to prevent orbs from
27       every coming too close together, and added a viscosity factor if
28       the speed of any orb got too fast.  This provides a nice stable
29       system with interesting behavior.
30
31       I had experimented with a number of fields including the van der
32       Waals force (very interesting orbiting behavior) and 1/r^3
33       gravity (not as interesting as 1/r^2).  An even normal viscosity
34       (rather than the thresholded version to bleed excess energy) is
35       also not interesting.  The 1/r^2, -1/r^2, -10/r^2 thresholds
36       proved not only robust but also interesting -- the orbs never
37       collided and the threshold viscosity fixed the
38       non-conservational problem.
39
40   Philip sez:
41       > An even normal viscosity (rather than the thresholded version to
42       > bleed excess energy) is also not interesting.
43
44       unless you make about 200 points.... set the viscosity to about .8
45       and drag the mouse through it.   it makes a nice wave which travels
46       through the field.
47
48   And (always the troublemaker) Joe Keane <jgk@jgk.org> sez:
49
50       Despite what John sez, the field being simulated is always
51       conservative.  The real problem is that it uses a simple hack,
52       computing acceleration *based only on the starting position*,
53       instead of a real differential equation solver.  Thus you'll
54       always have energy coming out of nowhere, although it's most
55       blatant when balls get close together.  If it were done right,
56       you wouldn't need viscosity or artificial limits on how close
57       the balls can get.
58
59   Matt <straitm@carleton.edu> sez:
60
61       Added a switch to remove the walls.
62
63       Added a switch to make the threshold viscosity optional.  If
64       nomaxspeed is specified, then balls going really fast do not
65       recieve special treatment.
66
67       I've made tail mode prettier by eliminating the first erase line
68       that drew from the upper left corner to the starting position of
69       each point.
70
71       Made the balls in modes other than "balls" bounce exactly at the
72       walls.  (Because the graphics for different modes are drawn
73       differently with respect to the "actual" position of the point,
74       they used to be able to run somewhat past the walls, or bounce
75       before hitting them.)
76
77       Added an option to output each ball's speed in the form of a bar
78       graph drawn on the same window as the balls.  If only x or y is
79       selected, they will be represented on the appropriate axis down
80       the center of the window.  If both are selected, they will both
81       be displayed along the diagonal such that the x and y bars for
82       each point start at the same place.  If speed is selected, the
83       speed will be displayed down the left side.  */
84
85#include <stdio.h>
86#include <math.h>
87#include "screenhack.h"
88#include "spline.h"
89
90struct ball {
91  double x, y;
92  double vx, vy;
93  double dx, dy;
94  double mass;
95  int size;
96  int pixel_index;
97  int hue;
98};
99
100static struct ball *balls;
101static double *x_vels;
102static double *y_vels;
103static double *speeds;
104static int npoints;
105static int threshold;
106static int delay;
107static int global_size;
108static int segments;
109static Bool glow_p;
110static Bool orbit_p;
111static Bool walls_p;
112static Bool maxspeed_p;
113static Bool cbounce_p;
114static XPoint *point_stack;
115static int point_stack_size, point_stack_fp;
116static XColor *colors;
117static int ncolors;
118static int fg_index;
119static int color_shift;
120Bool no_erase_yet; /* for tail mode fix */
121
122/*flip mods for mouse interaction*/
123static Bool mouse_p;
124int mouse_x, mouse_y, mouse_mass, root_x, root_y;
125static double viscosity;
126
127static enum object_mode {
128  ball_mode, line_mode, polygon_mode, spline_mode, spline_filled_mode,
129  tail_mode
130} mode;
131
132static enum graph_mode {
133  graph_none, graph_x, graph_y, graph_both, graph_speed
134} graph_mode;
135
136static GC draw_gc, erase_gc;
137
138/* The normal (and max) width for a graph bar */
139#define BAR_SIZE 11
140#define MAX_SIZE 16
141#define min(a,b) ((a)<(b)?(a):(b))
142#define max(a,b) ((a)>(b)?(a):(b))
143
144static void
145init_balls (Display *dpy, Window window)
146{
147  int i;
148  XWindowAttributes xgwa;
149  XGCValues gcv;
150  int xlim, ylim, midx, midy, r, vx, vy;
151  double th;
152  Colormap cmap;
153  char *mode_str, *graph_mode_str;
154
155  XGetWindowAttributes (dpy, window, &xgwa);
156  xlim = xgwa.width;
157  ylim = xgwa.height;
158  cmap = xgwa.colormap;
159  midx = xlim/2;
160  midy = ylim/2;
161  walls_p = get_boolean_resource ("walls", "Boolean");
162
163  /* if there aren't walls, don't set a limit on the radius */
164  r = get_integer_resource ("radius", "Integer");
165  if (r <= 0 || (r > min (xlim/2, ylim/2) && walls_p) )
166    r = min (xlim/2, ylim/2) - 50;
167
168  vx = get_integer_resource ("vx", "Integer");
169  vy = get_integer_resource ("vy", "Integer");
170
171  npoints = get_integer_resource ("points", "Integer");
172  if (npoints < 1)
173    npoints = 3 + (random () % 5);
174  balls = (struct ball *) malloc (npoints * sizeof (struct ball));
175
176  no_erase_yet = 1; /* for tail mode fix */
177
178  segments = get_integer_resource ("segments", "Integer");
179  if (segments < 0) segments = 1;
180
181  threshold = get_integer_resource ("threshold", "Integer");
182  if (threshold < 0) threshold = 0;
183
184  delay = get_integer_resource ("delay", "Integer");
185  if (delay < 0) delay = 0;
186
187  global_size = get_integer_resource ("size", "Integer");
188  if (global_size < 0) global_size = 0;
189
190  glow_p = get_boolean_resource ("glow", "Boolean");
191
192  orbit_p = get_boolean_resource ("orbit", "Boolean");
193
194  maxspeed_p = get_boolean_resource ("maxspeed", "Boolean");
195
196  cbounce_p = get_boolean_resource ("cbounce", "Boolean");
197
198  color_shift = get_integer_resource ("colorShift", "Integer");
199  if (color_shift <= 0) color_shift = 5;
200
201  /*flip mods for mouse interaction*/
202  mouse_p = get_boolean_resource ("mouse", "Boolean");
203  mouse_mass = get_integer_resource ("mouseSize", "Integer");
204  mouse_mass =  mouse_mass *  mouse_mass *10;
205
206  viscosity = get_float_resource ("viscosity", "Float");
207
208  mode_str = get_string_resource ("mode", "Mode");
209  if (! mode_str) mode = ball_mode;
210  else if (!strcmp (mode_str, "balls"))         mode = ball_mode;
211  else if (!strcmp (mode_str, "lines"))         mode = line_mode;
212  else if (!strcmp (mode_str, "polygons"))      mode = polygon_mode;
213  else if (!strcmp (mode_str, "tails"))         mode = tail_mode;
214  else if (!strcmp (mode_str, "splines"))       mode = spline_mode;
215  else if (!strcmp (mode_str, "filled-splines"))mode = spline_filled_mode;
216  else {
217    fprintf (stderr,
218             "%s: mode must be balls, lines, tails, polygons, splines, or\n\
219        filled-splines, not \"%s\"\n",
220             progname, mode_str);
221    exit (1);
222  }
223
224  graph_mode_str = get_string_resource ("graphmode", "Mode");
225  if (! graph_mode_str) graph_mode = graph_none;
226  else if (!strcmp (graph_mode_str, "x"))       graph_mode = graph_x;
227  else if (!strcmp (graph_mode_str, "y"))       graph_mode = graph_y;
228  else if (!strcmp (graph_mode_str, "both"))    graph_mode = graph_both;
229  else if (!strcmp (graph_mode_str, "speed"))   graph_mode = graph_speed;
230  else if (!strcmp (graph_mode_str, "none"))    graph_mode = graph_none;
231  else {
232    fprintf (stderr,
233         "%s: graphmode must be speed, x, y, both, or none, not \"%s\"\n",
234         progname, graph_mode_str);
235    exit (1);
236  }
237
238  /* only allocate memory if it is needed */
239  if(graph_mode != graph_none)
240  {
241    if(graph_mode == graph_x || graph_mode == graph_both)
242      x_vels = (double *) malloc (npoints * sizeof (double));
243    if(graph_mode == graph_y || graph_mode == graph_both)
244      y_vels = (double *) malloc (npoints * sizeof (double));
245    if(graph_mode == graph_speed)
246      speeds = (double *) malloc (npoints * sizeof (double));
247  }
248
249  if (mode != ball_mode && mode != tail_mode) glow_p = False;
250 
251  if (mode == polygon_mode && npoints < 3)
252    mode = line_mode;
253
254  ncolors = get_integer_resource ("colors", "Colors");
255  if (ncolors < 2) ncolors = 2;
256  if (ncolors <= 2) mono_p = True;
257  colors = 0;
258
259  if (!mono_p)
260    {
261      fg_index = 0;
262      switch (mode)
263        {
264        case ball_mode:
265          if (glow_p)
266            {
267              int H = random() % 360;
268              double S1 = 0.25;
269              double S2 = 1.00;
270              double V = frand(0.25) + 0.75;
271              colors = (XColor *) malloc(sizeof(*colors) * (ncolors+1));
272              make_color_ramp (dpy, cmap, H, S1, V, H, S2, V, colors, &ncolors,
273                               False, True, False);
274            }
275          else
276            {
277              ncolors = npoints;
278              colors = (XColor *) malloc(sizeof(*colors) * (ncolors+1));
279              make_random_colormap (dpy, xgwa.visual, cmap, colors, &ncolors,
280                                    True, True, False, True);
281            }
282          break;
283        case line_mode:
284        case polygon_mode:
285        case spline_mode:
286        case spline_filled_mode:
287        case tail_mode:
288          colors = (XColor *) malloc(sizeof(*colors) * (ncolors+1));
289          make_smooth_colormap (dpy, xgwa.visual, cmap, colors, &ncolors,
290                                True, False, True);
291          break;
292        default:
293          abort ();
294        }
295    }
296
297  if (!mono_p && ncolors <= 2)
298    {
299      if (colors) free (colors);
300      colors = 0;
301      mono_p = True;
302    }
303
304  if (mode != ball_mode)
305    {
306      int size = (segments ? segments : 1);
307      point_stack_size = size * (npoints + 1);
308      point_stack = (XPoint *) calloc (point_stack_size, sizeof (XPoint));
309      point_stack_fp = 0;
310    }
311
312  gcv.line_width = (mode == tail_mode
313                    ? (global_size ? global_size : (MAX_SIZE * 2 / 3))
314                    : 1);
315  gcv.cap_style = (mode == tail_mode ? CapRound : CapButt);
316
317  if (mono_p)
318    gcv.foreground = get_pixel_resource("foreground", "Foreground", dpy, cmap);
319  else
320    gcv.foreground = colors[fg_index].pixel;
321  draw_gc = XCreateGC (dpy, window, GCForeground|GCLineWidth|GCCapStyle, &gcv);
322
323  gcv.foreground = get_pixel_resource("background", "Background", dpy, cmap);
324  erase_gc = XCreateGC (dpy, window, GCForeground|GCLineWidth|GCCapStyle,&gcv);
325
326
327#define rand_size() min (MAX_SIZE, 8 + (random () % (MAX_SIZE - 9)))
328
329  if (orbit_p && !global_size)
330    /* To orbit, all objects must be the same mass, or the math gets
331       really hairy... */
332    global_size = rand_size ();
333
334  th = frand (M_PI+M_PI);
335  for (i = 0; i < npoints; i++)
336    {
337      int new_size = (global_size ? global_size : rand_size ());
338      balls [i].dx = 0;
339      balls [i].dy = 0;
340      balls [i].size = new_size;
341      balls [i].mass = (new_size * new_size * 10);
342      balls [i].x = midx + r * cos (i * ((M_PI+M_PI) / npoints) + th);
343      balls [i].y = midy + r * sin (i * ((M_PI+M_PI) / npoints) + th);
344      if (! orbit_p)
345        {
346          balls [i].vx = vx ? vx : ((6.0 - (random () % 11)) / 8.0);
347          balls [i].vy = vy ? vy : ((6.0 - (random () % 11)) / 8.0);
348        }
349      if (mono_p || mode != ball_mode)
350        balls [i].pixel_index = -1;
351      else if (glow_p)
352        balls [i].pixel_index = 0;
353      else
354        balls [i].pixel_index = random() % ncolors;
355    }
356
357  /*  This lets modes where the points don't really have any size use the whole
358      window.  Otherwise, since the points still have a positive size
359      assigned to them, they will be bounced somewhat early.  Mass and size are
360      seperate, so this shouldn't cause problems.  It's a bit kludgy, tho.
361  */
362  if(mode == line_mode || mode == spline_mode ||
363     mode == spline_filled_mode || mode == polygon_mode)
364    {
365        for(i = 1; i < npoints; i++)
366          {
367                balls[i].size = 0;
368          }
369     }
370   
371  if (orbit_p)
372    {
373      double a = 0;
374      double v;
375      double v_mult = get_float_resource ("vMult", "Float");
376      if (v_mult == 0.0) v_mult = 1.0;
377
378      for (i = 1; i < npoints; i++)
379        {
380          double _2ipi_n = (2 * i * M_PI / npoints);
381          double x = r * cos (_2ipi_n);
382          double y = r * sin (_2ipi_n);
383          double distx = r - x;
384          double dist2 = (distx * distx) + (y * y);
385          double dist = sqrt (dist2);
386          double a1 = ((balls[i].mass / dist2) *
387                       ((dist < threshold) ? -1.0 : 1.0) *
388                       (distx / dist));
389          a += a1;
390        }
391      if (a < 0.0)
392        {
393          fprintf (stderr, "%s: domain error: forces on balls too great\n",
394                   progname);
395          exit (-1);
396        }
397      v = sqrt (a * r) * v_mult;
398      for (i = 0; i < npoints; i++)
399        {
400          double k = ((2 * i * M_PI / npoints) + th);
401          balls [i].vx = -v * sin (k);
402          balls [i].vy =  v * cos (k);
403        }
404    }
405
406  if (mono_p) glow_p = False;
407
408  XClearWindow (dpy, window);
409}
410
411static void
412compute_force (int i, double *dx_ret, double *dy_ret)
413{
414  int j;
415  double x_dist, y_dist, dist, dist2;
416  *dx_ret = 0;
417  *dy_ret = 0;
418  for (j = 0; j < npoints; j++)
419    {
420      if (i == j) continue;
421      x_dist = balls [j].x - balls [i].x;
422      y_dist = balls [j].y - balls [i].y;
423      dist2 = (x_dist * x_dist) + (y_dist * y_dist);
424      dist = sqrt (dist2);
425             
426      if (dist > 0.1) /* the balls are not overlapping */
427        {
428          double new_acc = ((balls[j].mass / dist2) *
429                            ((dist < threshold) ? -1.0 : 1.0));
430          double new_acc_dist = new_acc / dist;
431          *dx_ret += new_acc_dist * x_dist;
432          *dy_ret += new_acc_dist * y_dist;
433        }
434      else
435        {               /* the balls are overlapping; move randomly */
436          *dx_ret += (frand (10.0) - 5.0);
437          *dy_ret += (frand (10.0) - 5.0);
438        }
439    }
440
441  if (mouse_p)
442    {
443      x_dist = mouse_x - balls [i].x;
444      y_dist = mouse_y - balls [i].y;
445      dist2 = (x_dist * x_dist) + (y_dist * y_dist);
446      dist = sqrt (dist2);
447       
448      if (dist > 0.1) /* the balls are not overlapping */
449        {
450          double new_acc = ((mouse_mass / dist2) *
451                            ((dist < threshold) ? -1.0 : 1.0));
452          double new_acc_dist = new_acc / dist;
453          *dx_ret += new_acc_dist * x_dist;
454          *dy_ret += new_acc_dist * y_dist;
455        }
456      else
457        {               /* the balls are overlapping; move randomly */
458          *dx_ret += (frand (10.0) - 5.0);
459          *dy_ret += (frand (10.0) - 5.0);
460        }
461    }
462}
463
464
465/* Draws meters along the diagonal for the x velocity */
466static void
467draw_meter_x(Display *dpy, Window window, GC draw_gc,
468             struct ball *balls, int i, int alone)
469{
470  XWindowAttributes xgwa;
471  int x1,x2,y,w1,w2,h;
472  XGetWindowAttributes (dpy, window, &xgwa);
473
474  /* set the width of the bars to use */
475  if(xgwa.height < BAR_SIZE*npoints)
476    {
477      y = i*(xgwa.height/npoints);
478      h = (xgwa.height/npoints) - 2;
479    }
480  else
481    {
482      y = BAR_SIZE*i;
483      h = BAR_SIZE - 2;
484    }
485 
486  if(alone)
487    {
488      x1 = xgwa.width/2;
489      x2 = x1;
490    }
491  else
492    {
493      x1 = i*(h+2);
494      if(x1 < i)
495        x1 = i;
496      x2 = x1;
497    }
498
499  if(y<1) y=i; 
500  if(h<1) h=1;
501
502  w1 = (int)(20*x_vels[i]);
503  w2 = (int)(20*balls[i].vx);
504  x_vels[i] = balls[i].vx;
505
506  if (w1<0) {
507    w1=-w1;
508    x1=x1-w1;
509  }
510  if (w2<0) {
511    w2=-w2;
512    x2=x2-w2;
513  }
514  XDrawRectangle(dpy,window,erase_gc,x1+(h+2)/2,y,w1,h);
515  XDrawRectangle(dpy,window,draw_gc,x2+(h+2)/2,y,w2,h);
516}
517
518/* Draws meters along the diagonal for the y velocity.
519   Is there some way to make draw_meter_x and draw_meter_y
520   one function instead of two without making them completely unreadable?
521*/
522static void
523draw_meter_y (Display *dpy, Window window, GC draw_gc,
524              struct ball *balls, int i, int alone)
525{
526  XWindowAttributes xgwa;
527  int y1,y2,x,h1,h2,w;
528  XGetWindowAttributes (dpy, window, &xgwa);
529
530  if(xgwa.height < BAR_SIZE*npoints){  /*needs to be height still */
531    x = i*(xgwa.height/npoints);
532    w = (xgwa.height/npoints) - 2;
533  }
534  else{
535    x = BAR_SIZE*i;
536    w = BAR_SIZE - 2;
537  }
538
539  if(alone)
540    {
541      y1 = xgwa.height/2;
542      y2 = y1;
543    }
544  else
545    {
546      y1 = i*(w+2);
547      if(y1 < i)
548        y1 = i;
549      y2 = y1;
550    }
551
552  if(x < 1) x = i; 
553  if(w < 1) w = 1;
554
555  h1 = (int)(20*y_vels[i]);
556  h2 = (int)(20*balls[i].vy);
557  y_vels[i] = balls[i].vy;
558
559  if (h1<0) {
560    h1=-h1;
561    y1=y1-h1;
562  }
563  if (h2<0) {
564    h2=-h2;
565    y2=y2-h2;
566  }
567  XDrawRectangle(dpy,window,erase_gc,x,y1+(w+2)/2,w,h1);
568  XDrawRectangle(dpy,window,draw_gc,x,y2+(w+2)/2,w,h2);
569}
570
571
572/* Draws meters of the total speed of the balls */
573static void
574draw_meter_speed (Display *dpy, Window window, GC draw_gc,
575                  struct ball *balls, int i)
576{
577  XWindowAttributes xgwa;
578  int y,x1,x2,h,w1,w2;
579  XGetWindowAttributes (dpy, window, &xgwa);
580
581  if(xgwa.height < BAR_SIZE*npoints)
582    {
583      y = i*(xgwa.height/npoints);
584      h = (xgwa.height/npoints) - 2;
585    }
586  else{
587    y = BAR_SIZE*i;
588    h = BAR_SIZE - 2;
589  }
590
591  x1 = 0;
592  x2 = x1;
593
594  if(y < 1) y = i; 
595  if(h < 1) h = 1;
596
597  w1 = (int)(5*speeds[i]);
598  w2 = (int)(5*(balls[i].vy*balls[i].vy+balls[i].vx*balls[i].vx));
599  speeds[i] =    balls[i].vy*balls[i].vy+balls[i].vx*balls[i].vx;
600
601  XDrawRectangle(dpy,window,erase_gc,x1,y,w1,h);
602  XDrawRectangle(dpy,window,draw_gc, x2,y,w2,h);
603}
604
605static void
606run_balls (Display *dpy, Window window, int total_ticks)
607{
608  int last_point_stack_fp = point_stack_fp;
609  static int tick = 500, xlim, ylim;
610  static Colormap cmap;
611 
612  Window root1, child1;  /*flip mods for mouse interaction*/
613  unsigned int mask;
614
615  int i, radius = global_size/2;
616  if(global_size == 0)
617    radius = (MAX_SIZE / 3);
618
619  if(graph_mode != graph_none)
620    {
621      if(graph_mode == graph_both)
622        {
623          for(i = 0; i < npoints; i++)
624            {
625              draw_meter_x(dpy,window,draw_gc, balls, i, 0);
626              draw_meter_y(dpy,window,draw_gc, balls, i, 0);
627            }
628        }
629      else if(graph_mode == graph_x)
630        {
631          for(i = 0; i < npoints; i++)
632            {
633              draw_meter_x(dpy,window,draw_gc, balls, i, 1);
634            }
635        }
636      else if(graph_mode == graph_y)
637        {
638          for(i = 0; i < npoints; i++)
639            {
640              draw_meter_y(dpy,window,draw_gc, balls, i, 1);
641            }
642        }
643      else if(graph_mode == graph_speed)
644        {
645          for(i = 0; i < npoints; i++)
646            {
647              draw_meter_speed(dpy,window,draw_gc, balls, i);
648            }
649        }
650
651    }
652
653  if (mouse_p)
654    {
655      XQueryPointer(dpy, window, &root1, &child1,
656                    &root_x, &root_y, &mouse_x, &mouse_y, &mask);
657    }
658
659  if (tick++ == 500)
660    {
661      XWindowAttributes xgwa;
662      XGetWindowAttributes (dpy, window, &xgwa);
663      tick = 0;
664      xlim = xgwa.width;
665      ylim = xgwa.height;
666      cmap = xgwa.colormap;
667    }
668
669  /* compute the force of attraction/repulsion among all balls */
670  for (i = 0; i < npoints; i++)
671    compute_force (i, &balls[i].dx, &balls[i].dy);
672
673  /* move the balls according to the forces now in effect */
674  for (i = 0; i < npoints; i++)
675    {
676      double old_x = balls[i].x;
677      double old_y = balls[i].y;
678      double new_x, new_y;
679      int size = balls[i].size;
680      balls[i].vx += balls[i].dx;
681      balls[i].vy += balls[i].dy;
682
683      /* "don't let them get too fast: impose a terminal velocity
684         (actually, make the medium have friction)"
685         Well, what this first step really does is give the medium a
686         viscosity of .9 for balls going over the speed limit.  Anyway,
687         this is now optional
688      */
689      if (balls[i].vx > 10 && maxspeed_p)
690        {
691          balls[i].vx *= 0.9;
692          balls[i].dx = 0;
693        }
694      else if (viscosity != 1)
695        {
696          balls[i].vx *= viscosity;
697        }
698
699      if (balls[i].vy > 10 && maxspeed_p)
700        {
701          balls[i].vy *= 0.9;
702          balls[i].dy = 0;
703        }
704      else if (viscosity != 1)
705        {
706          balls[i].vy *= viscosity;
707        }
708
709      balls[i].x += balls[i].vx;
710      balls[i].y += balls[i].vy;
711
712
713      /* bounce off the walls if desired
714         note: a ball is actually its upper left corner */
715      if(walls_p)
716        {
717          if(cbounce_p)  /* with correct bouncing */
718            {
719              /* so long as it's out of range, keep bouncing */
720       
721              while( (balls[i].x >= (xlim - balls[i].size)) ||
722                     (balls[i].y >= (ylim - balls[i].size)) ||
723                     (balls[i].x <= 0) ||
724                     (balls[i].y <= 0) )
725                {
726                  if (balls[i].x >= (xlim - balls[i].size))
727                    {
728                      balls[i].x = (2*(xlim - balls[i].size) - balls[i].x);
729                      balls[i].vx = -balls[i].vx;
730                    }
731                  if (balls[i].y >= (ylim - balls[i].size))
732                    {
733                      balls[i].y = (2*(ylim - balls[i].size) - balls[i].y);
734                      balls[i].vy = -balls[i].vy;
735                    }
736                  if (balls[i].x <= 0)
737                    {
738                      balls[i].x = -balls[i].x;
739                      balls[i].vx = -balls[i].vx;
740                    }
741                  if (balls[i].y <= 0)
742                    {
743                      balls[i].y = -balls[i].y;
744                      balls[i].vy = -balls[i].vy;
745                    }
746                }
747            }
748          else  /* with old bouncing */
749            {
750              if (balls[i].x >= (xlim - balls[i].size))
751                {
752                  balls[i].x = (xlim - balls[i].size - 1);
753                  if (balls[i].vx > 0) /* why is this check here? */
754                    balls[i].vx = -balls[i].vx;
755                }
756              if (balls[i].y >= (ylim - balls[i].size))
757                {
758                  balls[i].y = (ylim - balls[i].size - 1);
759                  if (balls[i].vy > 0)
760                    balls[i].vy = -balls[i].vy;
761                }
762              if (balls[i].x <= 0)
763                {
764                  balls[i].x = 0;
765                  if (balls[i].vx < 0)
766                    balls[i].vx = -balls[i].vx;
767                }
768              if (balls[i].y <= 0)
769                {
770                  balls[i].y = 0;
771                  if (balls[i].vy < 0)
772                    balls[i].vy = -balls[i].vy;
773                }
774            }
775        }
776      new_x = balls[i].x;
777      new_y = balls[i].y;
778
779      if (!mono_p)
780        {
781          if (mode == ball_mode)
782            {
783              if (glow_p)
784                {
785                  /* make color saturation be related to particle
786                     acceleration. */
787                  double limit = 0.5;
788                  double s, fraction;
789                  double vx = balls [i].dx;
790                  double vy = balls [i].dy;
791                  if (vx < 0) vx = -vx;
792                  if (vy < 0) vy = -vy;
793                  fraction = vx + vy;
794                  if (fraction > limit) fraction = limit;
795
796                  s = 1 - (fraction / limit);
797                  balls[i].pixel_index = (ncolors * s);
798                }
799              XSetForeground (dpy, draw_gc,
800                              colors[balls[i].pixel_index].pixel);
801            }
802        }
803
804      if (mode == ball_mode)
805        {
806          XFillArc (dpy, window, erase_gc, (int) old_x, (int) old_y,
807                    size, size, 0, 360*64);
808          XFillArc (dpy, window, draw_gc,  (int) new_x, (int) new_y,
809                    size, size, 0, 360*64);
810        }
811      else
812        {
813          point_stack [point_stack_fp].x = new_x;
814          point_stack [point_stack_fp].y = new_y;
815          point_stack_fp++;
816        }
817    }
818
819  /* draw the lines or polygons after computing all points */
820  if (mode != ball_mode)
821    {
822      point_stack [point_stack_fp].x = balls [0].x; /* close the polygon */
823      point_stack [point_stack_fp].y = balls [0].y;
824      point_stack_fp++;
825      if (point_stack_fp == point_stack_size)
826        point_stack_fp = 0;
827      else if (point_stack_fp > point_stack_size) /* better be aligned */
828        abort ();
829      if (!mono_p)
830        {
831          static int tick = 0;
832          if (tick++ == color_shift)
833            {
834              tick = 0;
835              fg_index = (fg_index + 1) % ncolors;
836              XSetForeground (dpy, draw_gc, colors[fg_index].pixel);
837            }
838        }
839    }
840
841  switch (mode)
842    {
843    case ball_mode:
844      break;
845    case line_mode:
846      if (segments > 0)
847        XDrawLines (dpy, window, erase_gc, point_stack + point_stack_fp,
848                    npoints + 1, CoordModeOrigin);
849      XDrawLines (dpy, window, draw_gc, point_stack + last_point_stack_fp,
850                  npoints + 1, CoordModeOrigin);
851      break;
852    case polygon_mode:
853      if (segments > 0)
854        XFillPolygon (dpy, window, erase_gc, point_stack + point_stack_fp,
855                      npoints + 1, (npoints == 3 ? Convex : Complex),
856                      CoordModeOrigin);
857      XFillPolygon (dpy, window, draw_gc, point_stack + last_point_stack_fp,
858                    npoints + 1, (npoints == 3 ? Convex : Complex),
859                    CoordModeOrigin);
860      break;
861    case tail_mode:
862      {
863        for (i = 0; i < npoints; i++)
864          {
865            int index = point_stack_fp + i;
866            int next_index = (index + (npoints + 1)) % point_stack_size;
867            if(no_erase_yet == 1)
868              {
869                if(total_ticks >= segments)
870                  {
871                    no_erase_yet = 0;
872                    XDrawLine (dpy, window, erase_gc,
873                               point_stack [index].x + radius,
874                               point_stack [index].y + radius,
875                               point_stack [next_index].x + radius,
876                               point_stack [next_index].y + radius);
877                  }
878              }
879            else
880              {
881                XDrawLine (dpy, window, erase_gc,
882                           point_stack [index].x + radius,
883                           point_stack [index].y + radius,
884                           point_stack [next_index].x + radius,
885                           point_stack [next_index].y + radius);
886              }
887            index = last_point_stack_fp + i;
888            next_index = (index - (npoints + 1)) % point_stack_size;
889            if (next_index < 0) next_index += point_stack_size;
890            if (point_stack [next_index].x == 0 &&
891                point_stack [next_index].y == 0)
892              continue;
893            XDrawLine (dpy, window, draw_gc,
894                       point_stack [index].x + radius,
895                       point_stack [index].y + radius,
896                       point_stack [next_index].x + radius,
897                       point_stack [next_index].y + radius);
898          }
899      }
900      break;
901    case spline_mode:
902    case spline_filled_mode:
903      {
904        static spline *s = 0;
905        if (! s) s = make_spline (npoints);
906        if (segments > 0)
907          {
908            for (i = 0; i < npoints; i++)
909              {
910                s->control_x [i] = point_stack [point_stack_fp + i].x;
911                s->control_y [i] = point_stack [point_stack_fp + i].y;
912              }
913            compute_closed_spline (s);
914            if (mode == spline_filled_mode)
915              XFillPolygon (dpy, window, erase_gc, s->points, s->n_points,
916                            (s->n_points == 3 ? Convex : Complex),
917                            CoordModeOrigin);
918            else
919              XDrawLines (dpy, window, erase_gc, s->points, s->n_points,
920                          CoordModeOrigin);
921          }
922        for (i = 0; i < npoints; i++)
923          {
924            s->control_x [i] = point_stack [last_point_stack_fp + i].x;
925            s->control_y [i] = point_stack [last_point_stack_fp + i].y;
926          }
927        compute_closed_spline (s);
928        if (mode == spline_filled_mode)
929          XFillPolygon (dpy, window, draw_gc, s->points, s->n_points,
930                        (s->n_points == 3 ? Convex : Complex),
931                        CoordModeOrigin);
932        else
933          XDrawLines (dpy, window, draw_gc, s->points, s->n_points,
934                      CoordModeOrigin);
935      }
936      break;
937    default:
938      abort ();
939    }
940
941  XSync (dpy, False);
942}
943
944
945char *progclass = "Attraction";
946
947char *defaults [] = {
948  ".background: black",
949  ".foreground: white",
950  "*mode:       balls",
951  "*graphmode:  none",
952  "*points:     0",
953  "*size:       0",
954  "*colors:     200",
955  "*threshold:  100",
956  "*delay:      10000",
957  "*glow:       false",
958  "*mouseSize:  10",
959  "*walls:      true",
960  "*maxspeed:   true",
961  "*cbounce:    true",
962  "*mouse:      false",
963  "*viscosity:  1",
964  "*orbit:      false",
965  "*colorShift: 3",
966  "*segments:   500",
967  "*vMult:      0.9",
968  0
969};
970
971XrmOptionDescRec options [] = {
972  { "-mode",            ".mode",        XrmoptionSepArg, 0 },
973  { "-graphmode",       ".graphmode",   XrmoptionSepArg, 0 },
974  { "-colors",          ".colors",      XrmoptionSepArg, 0 },
975  { "-points",          ".points",      XrmoptionSepArg, 0 },
976  { "-color-shift",     ".colorShift",  XrmoptionSepArg, 0 },
977  { "-threshold",       ".threshold",   XrmoptionSepArg, 0 },
978  { "-segments",        ".segments",    XrmoptionSepArg, 0 },
979  { "-delay",           ".delay",       XrmoptionSepArg, 0 },
980  { "-size",            ".size",        XrmoptionSepArg, 0 },
981  { "-radius",          ".radius",      XrmoptionSepArg, 0 },
982  { "-vx",              ".vx",          XrmoptionSepArg, 0 },
983  { "-vy",              ".vy",          XrmoptionSepArg, 0 },
984  { "-vmult",           ".vMult",       XrmoptionSepArg, 0 },
985  { "-mouse-size",      ".mouseSize",   XrmoptionSepArg, 0 },
986  { "-viscosity",       ".viscosity",   XrmoptionSepArg, 0 },
987  { "-mouse",           ".mouse",       XrmoptionNoArg, "true" },
988  { "-nomouse",         ".mouse",       XrmoptionNoArg, "false" },
989  { "-glow",            ".glow",        XrmoptionNoArg, "true" },
990  { "-noglow",          ".glow",        XrmoptionNoArg, "false" },
991  { "-orbit",           ".orbit",       XrmoptionNoArg, "true" },
992  { "-nowalls",         ".walls",       XrmoptionNoArg, "false" },
993  { "-walls",           ".walls",       XrmoptionNoArg, "true" },
994  { "-nomaxspeed",      ".maxspeed",    XrmoptionNoArg, "false" },
995  { "-maxspeed",        ".maxspeed",    XrmoptionNoArg, "true" },
996  { "-correct-bounce",  ".cbounce",     XrmoptionNoArg, "false" },
997  { "-fast-bounce",     ".cbounce",     XrmoptionNoArg, "true" },
998  { 0, 0, 0, 0 }
999};
1000
1001void
1002screenhack (Display *dpy, Window window)
1003{
1004  /* for tail mode fix */
1005  int total_ticks = 0;
1006
1007  init_balls (dpy, window);
1008  while (1)
1009    {
1010      total_ticks++;
1011      run_balls (dpy, window, total_ticks);
1012      screenhack_handle_events (dpy);
1013      if (delay)
1014        usleep (delay);
1015    }
1016}
Note: See TracBrowser for help on using the repository browser.