-/* xscreensaver, Copyright (c) 1992, 1995, 1996, 1997
- * Jamie Zawinski <jwz@netscape.com>
+/* xscreensaver, Copyright (c) 1992-2013 Jamie Zawinski <jwz@jwz.org>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
/* Simulation of a pair of quasi-gravitational fields, maybe sorta kinda
a little like the strong and weak electromagnetic forces. Derived from
- a Lispm screensaver by John Pezaris <pz@mit.edu>. Mouse control and
- viscosity added by "Philip Edward Cutone, III" <pc2d+@andrew.cmu.edu>.
+ a Lispm screensaver by John Pezaris <pz@mit.edu>. Viscosity added by
+ Philip Edward Cutone, III <pc2d+@andrew.cmu.edu>.
John sez:
- The simulation started out as a purely accurate gravitational simulation,
- but, with constant simulation step size, I quickly realized the field being
- simulated while grossly gravitational was, in fact, non-conservative. It
- also had the rather annoying behavior of dealing very badly with colliding
- orbs. Therefore, I implemented a negative-gravity region (with two
- thresholds; as I read your code, you only implemented one) to prevent orbs
- from every coming too close together, and added a viscosity factor if the
- speed of any orb got too fast. This provides a nice stable system with
- interesting behavior.
-
- I had experimented with a number of fields including the van der Waals
- force (very interesting orbiting behavior) and 1/r^3 gravity (not as
- interesting as 1/r^2). An even normal viscosity (rather than the
- thresholded version to bleed excess energy) is also not interesting.
- The 1/r^2, -1/r^2, -10/r^2 thresholds proved not only robust but also
- interesting -- the orbs never collided and the threshold viscosity fixed
- the non-conservational problem.
+ The simulation started out as a purely accurate gravitational
+ simulation, but, with constant simulation step size, I quickly
+ realized the field being simulated while grossly gravitational
+ was, in fact, non-conservative. It also had the rather annoying
+ behavior of dealing very badly with colliding orbs. Therefore,
+ I implemented a negative-gravity region (with two thresholds; as
+ I read your code, you only implemented one) to prevent orbs from
+ every coming too close together, and added a viscosity factor if
+ the speed of any orb got too fast. This provides a nice stable
+ system with interesting behavior.
+
+ I had experimented with a number of fields including the van der
+ Waals force (very interesting orbiting behavior) and 1/r^3
+ gravity (not as interesting as 1/r^2). An even normal viscosity
+ (rather than the thresholded version to bleed excess energy) is
+ also not interesting. The 1/r^2, -1/r^2, -10/r^2 thresholds
+ proved not only robust but also interesting -- the orbs never
+ collided and the threshold viscosity fixed the
+ non-conservational problem.
Philip sez:
- > An even normal viscosity (rather than the thresholded version to
- > bleed excess energy) is also not interesting.
+ > An even normal viscosity (rather than the thresholded version to
+ > bleed excess energy) is also not interesting.
- unless you make about 200 points.... set the viscosity to about .8
- and drag the mouse through it. it makes a nice wave which travels
- through the field.
+ unless you make about 200 points.... set the viscosity to about .8
+ and drag the mouse through it. it makes a nice wave which travels
+ through the field.
And (always the troublemaker) Joe Keane <jgk@jgk.org> sez:
- Despite what John sez, the field being simulated is always conservative.
- The real problem is that it uses a simple hack, computing acceleration
- *based only on the starting position*, instead of a real differential
- equation solver. Thus you'll always have energy coming out of nowhere,
- although it's most blatant when balls get close together. If it were
- done right, you wouldn't need viscosity or artificial limits on how
- close the balls can get.
- */
+ Despite what John sez, the field being simulated is always
+ conservative. The real problem is that it uses a simple hack,
+ computing acceleration *based only on the starting position*,
+ instead of a real differential equation solver. Thus you'll
+ always have energy coming out of nowhere, although it's most
+ blatant when balls get close together. If it were done right,
+ you wouldn't need viscosity or artificial limits on how close
+ the balls can get.
+
+ Matt <straitm@carleton.edu> sez:
+
+ Added a switch to remove the walls.
+
+ Added a switch to make the threshold viscosity optional. If
+ nomaxspeed is specified, then balls going really fast do not
+ recieve special treatment.
+
+ I've made tail mode prettier by eliminating the first erase line
+ that drew from the upper left corner to the starting position of
+ each point.
+
+ Made the balls in modes other than "balls" bounce exactly at the
+ walls. (Because the graphics for different modes are drawn
+ differently with respect to the "actual" position of the point,
+ they used to be able to run somewhat past the walls, or bounce
+ before hitting them.)
+
+ Added an option to output each ball's speed in the form of a bar
+ graph drawn on the same window as the balls. If only x or y is
+ selected, they will be represented on the appropriate axis down
+ the center of the window. If both are selected, they will both
+ be displayed along the diagonal such that the x and y bars for
+ each point start at the same place. If speed is selected, the
+ speed will be displayed down the left side. */
#include <stdio.h>
#include <math.h>
#include "screenhack.h"
#include "spline.h"
+/* The normal (and max) width for a graph bar */
+#define BAR_SIZE 11
+#define MAX_SIZE 16
+#define min(a,b) ((a)<(b)?(a):(b))
+#define max(a,b) ((a)>(b)?(a):(b))
+
+
+enum object_mode {
+ ball_mode, line_mode, polygon_mode, spline_mode, spline_filled_mode,
+ tail_mode
+};
+
+enum graph_mode {
+ graph_none, graph_x, graph_y, graph_both, graph_speed
+};
+
struct ball {
double x, y;
double vx, vy;
int hue;
};
-static struct ball *balls;
-static int npoints;
-static int threshold;
-static int delay;
-static int global_size;
-static int segments;
-static Bool glow_p;
-static Bool orbit_p;
-static XPoint *point_stack;
-static int point_stack_size, point_stack_fp;
-static XColor *colors;
-static int ncolors;
-static int fg_index;
-static int color_shift;
-
-/*flip mods for mouse interaction*/
-static Bool mouse_p;
-int mouse_x, mouse_y, mouse_mass, root_x, root_y;
-static double viscosity;
-
-static enum object_mode {
- ball_mode, line_mode, polygon_mode, spline_mode, spline_filled_mode,
- tail_mode
-} mode;
+struct state {
+ struct ball *balls;
+ double *x_vels;
+ double *y_vels;
+ double *speeds;
+ int npoints;
+ int threshold;
+ int delay;
+ int global_size;
+ int segments;
+ Bool glow_p;
+ Bool orbit_p;
+ Bool walls_p;
+ Bool maxspeed_p;
+ Bool cbounce_p;
+ XPoint *point_stack;
+ int point_stack_size, point_stack_fp;
+ XColor *colors;
+ int ncolors;
+ int fg_index;
+ int color_shift;
+ int xlim, ylim;
+ Bool no_erase_yet; /* for tail mode fix */
+ double viscosity;
-static GC draw_gc, erase_gc;
+ int mouse_ball; /* index of ball being dragged, or 0 if none. */
+ unsigned long mouse_pixel;
-#define MAX_SIZE 16
+ enum object_mode mode;
+ enum graph_mode graph_mode;
-#define min(a,b) ((a)<(b)?(a):(b))
-#define max(a,b) ((a)>(b)?(a):(b))
+ GC draw_gc, erase_gc;
-static void
-init_balls (Display *dpy, Window window)
+ int total_ticks;
+ int color_tick;
+ spline *spl;
+};
+
+
+static void *
+attraction_init (Display *dpy, Window window)
{
+ struct state *st = (struct state *) calloc (1, sizeof(*st));
int i;
XWindowAttributes xgwa;
XGCValues gcv;
- int xlim, ylim, midx, midy, r, vx, vy;
+ int midx, midy, r, vx, vy;
double th;
Colormap cmap;
- char *mode_str;
+ char *mode_str, *graph_mode_str;
+
XGetWindowAttributes (dpy, window, &xgwa);
- xlim = xgwa.width;
- ylim = xgwa.height;
+ st->xlim = xgwa.width;
+ st->ylim = xgwa.height;
cmap = xgwa.colormap;
- midx = xlim/2;
- midy = ylim/2;
- r = get_integer_resource ("radius", "Integer");
- if (r <= 0 || r > min (xlim/2, ylim/2))
- r = min (xlim/2, ylim/2) - 50;
- vx = get_integer_resource ("vx", "Integer");
- vy = get_integer_resource ("vy", "Integer");
- npoints = get_integer_resource ("points", "Integer");
- if (npoints < 1)
- npoints = 3 + (random () % 5);
- balls = (struct ball *) malloc (npoints * sizeof (struct ball));
- segments = get_integer_resource ("segments", "Integer");
- if (segments < 0) segments = 1;
- threshold = get_integer_resource ("threshold", "Integer");
- if (threshold < 0) threshold = 0;
- delay = get_integer_resource ("delay", "Integer");
- if (delay < 0) delay = 0;
- global_size = get_integer_resource ("size", "Integer");
- if (global_size < 0) global_size = 0;
- glow_p = get_boolean_resource ("glow", "Boolean");
- orbit_p = get_boolean_resource ("orbit", "Boolean");
- color_shift = get_integer_resource ("colorShift", "Integer");
- if (color_shift <= 0) color_shift = 5;
-
- /*flip mods for mouse interaction*/
- mouse_p = get_boolean_resource ("mouse", "Boolean");
- mouse_mass = get_integer_resource ("mouseSize", "Integer");
- mouse_mass = mouse_mass * mouse_mass *10;
-
- viscosity = get_float_resource ("viscosity", "Float");
-
- mode_str = get_string_resource ("mode", "Mode");
- if (! mode_str) mode = ball_mode;
- else if (!strcmp (mode_str, "balls")) mode = ball_mode;
- else if (!strcmp (mode_str, "lines")) mode = line_mode;
- else if (!strcmp (mode_str, "polygons")) mode = polygon_mode;
- else if (!strcmp (mode_str, "tails")) mode = tail_mode;
- else if (!strcmp (mode_str, "splines")) mode = spline_mode;
- else if (!strcmp (mode_str, "filled-splines")) mode = spline_filled_mode;
+ midx = st->xlim/2;
+ midy = st->ylim/2;
+ st->walls_p = get_boolean_resource (dpy, "walls", "Boolean");
+
+ /* if there aren't walls, don't set a limit on the radius */
+ r = get_integer_resource (dpy, "radius", "Integer");
+ if (r <= 0 || (r > min (st->xlim/2, st->ylim/2) && st->walls_p) )
+ r = min (st->xlim/2, st->ylim/2) - 50;
+
+ vx = get_integer_resource (dpy, "vx", "Integer");
+ vy = get_integer_resource (dpy, "vy", "Integer");
+
+ st->npoints = get_integer_resource (dpy, "points", "Integer");
+ if (st->npoints < 1)
+ st->npoints = 3 + (random () % 5);
+ st->balls = (struct ball *) malloc (st->npoints * sizeof (struct ball));
+
+ st->no_erase_yet = 1; /* for tail mode fix */
+
+ st->segments = get_integer_resource (dpy, "segments", "Integer");
+ if (st->segments < 0) st->segments = 1;
+
+ st->threshold = get_integer_resource (dpy, "threshold", "Integer");
+ if (st->threshold < 0) st->threshold = 0;
+
+ st->delay = get_integer_resource (dpy, "delay", "Integer");
+ if (st->delay < 0) st->delay = 0;
+
+ st->global_size = get_integer_resource (dpy, "size", "Integer");
+ if (st->global_size < 0) st->global_size = 0;
+
+ st->glow_p = get_boolean_resource (dpy, "glow", "Boolean");
+
+ st->orbit_p = get_boolean_resource (dpy, "orbit", "Boolean");
+
+ st->maxspeed_p = get_boolean_resource (dpy, "maxspeed", "Boolean");
+
+ st->cbounce_p = get_boolean_resource (dpy, "cbounce", "Boolean");
+
+ st->color_shift = get_integer_resource (dpy, "colorShift", "Integer");
+ if (st->color_shift <= 0) st->color_shift = 5;
+
+ st->viscosity = get_float_resource (dpy, "viscosity", "Float");
+
+ mode_str = get_string_resource (dpy, "mode", "Mode");
+ if (! mode_str) st->mode = ball_mode;
+ else if (!strcmp (mode_str, "balls")) st->mode = ball_mode;
+ else if (!strcmp (mode_str, "lines")) st->mode = line_mode;
+ else if (!strcmp (mode_str, "polygons")) st->mode = polygon_mode;
+ else if (!strcmp (mode_str, "tails")) st->mode = tail_mode;
+ else if (!strcmp (mode_str, "splines")) st->mode = spline_mode;
+ else if (!strcmp (mode_str, "filled-splines"))st->mode = spline_filled_mode;
else {
fprintf (stderr,
"%s: mode must be balls, lines, tails, polygons, splines, or\n\
exit (1);
}
- if (mode != ball_mode && mode != tail_mode) glow_p = False;
+ graph_mode_str = get_string_resource (dpy, "graphmode", "Mode");
+ if (! graph_mode_str) st->graph_mode = graph_none;
+ else if (!strcmp (graph_mode_str, "x")) st->graph_mode = graph_x;
+ else if (!strcmp (graph_mode_str, "y")) st->graph_mode = graph_y;
+ else if (!strcmp (graph_mode_str, "both")) st->graph_mode = graph_both;
+ else if (!strcmp (graph_mode_str, "speed")) st->graph_mode = graph_speed;
+ else if (!strcmp (graph_mode_str, "none")) st->graph_mode = graph_none;
+ else {
+ fprintf (stderr,
+ "%s: graphmode must be speed, x, y, both, or none, not \"%s\"\n",
+ progname, graph_mode_str);
+ exit (1);
+ }
+
+ /* only allocate memory if it is needed */
+ if(st->graph_mode != graph_none)
+ {
+ if(st->graph_mode == graph_x || st->graph_mode == graph_both)
+ st->x_vels = (double *) malloc (st->npoints * sizeof (double));
+ if(st->graph_mode == graph_y || st->graph_mode == graph_both)
+ st->y_vels = (double *) malloc (st->npoints * sizeof (double));
+ if(st->graph_mode == graph_speed)
+ st->speeds = (double *) malloc (st->npoints * sizeof (double));
+ }
+
+ if (st->mode != ball_mode && st->mode != tail_mode) st->glow_p = False;
- if (mode == polygon_mode && npoints < 3)
- mode = line_mode;
+ if (st->mode == polygon_mode && st->npoints < 3)
+ st->mode = line_mode;
- ncolors = get_integer_resource ("colors", "Colors");
- if (ncolors < 2) ncolors = 2;
- if (ncolors <= 2) mono_p = True;
- colors = 0;
+ st->ncolors = get_integer_resource (dpy, "colors", "Colors");
+ if (st->ncolors < 2) st->ncolors = 2;
+ if (st->ncolors <= 2) mono_p = True;
+ st->colors = 0;
if (!mono_p)
{
- fg_index = 0;
- switch (mode)
+ st->fg_index = 0;
+ switch (st->mode)
{
case ball_mode:
- if (glow_p)
+ if (st->glow_p)
{
int H = random() % 360;
double S1 = 0.25;
double S2 = 1.00;
double V = frand(0.25) + 0.75;
- colors = (XColor *) malloc(sizeof(*colors) * (ncolors+1));
- make_color_ramp (dpy, cmap, H, S1, V, H, S2, V, colors, &ncolors,
+ st->colors = (XColor *) malloc(sizeof(*st->colors) * (st->ncolors+1));
+ make_color_ramp (xgwa.screen, xgwa.visual, cmap,
+ H, S1, V, H, S2, V, st->colors, &st->ncolors,
False, True, False);
}
else
{
- ncolors = npoints;
- colors = (XColor *) malloc(sizeof(*colors) * (ncolors+1));
- make_random_colormap (dpy, xgwa.visual, cmap, colors, &ncolors,
+ st->ncolors = st->npoints;
+ st->colors = (XColor *) malloc(sizeof(*st->colors) * (st->ncolors+1));
+ make_random_colormap (xgwa.screen, xgwa.visual, cmap,
+ st->colors, &st->ncolors,
True, True, False, True);
}
break;
case spline_mode:
case spline_filled_mode:
case tail_mode:
- colors = (XColor *) malloc(sizeof(*colors) * (ncolors+1));
- make_smooth_colormap (dpy, xgwa.visual, cmap, colors, &ncolors,
+ st->colors = (XColor *) malloc(sizeof(*st->colors) * (st->ncolors+1));
+ make_smooth_colormap (xgwa.screen, xgwa.visual, cmap,
+ st->colors, &st->ncolors,
True, False, True);
break;
default:
}
}
- if (!mono_p && ncolors <= 2)
+ if (!mono_p && st->ncolors <= 2)
{
- if (colors) free (colors);
- colors = 0;
+ if (st->colors) free (st->colors);
+ st->colors = 0;
mono_p = True;
}
- if (mode != ball_mode)
+ st->mouse_pixel =
+ get_pixel_resource (dpy, cmap, "mouseForeground", "MouseForeground");
+ st->mouse_ball = -1;
+
+ if (st->mode != ball_mode)
{
- int size = (segments ? segments : 1);
- point_stack_size = size * (npoints + 1);
- point_stack = (XPoint *) calloc (point_stack_size, sizeof (XPoint));
- point_stack_fp = 0;
+ int size = (st->segments ? st->segments : 1);
+ st->point_stack_size = size * (st->npoints + 1);
+ st->point_stack = (XPoint *) calloc (st->point_stack_size, sizeof (XPoint));
+ st->point_stack_fp = 0;
}
- gcv.line_width = (mode == tail_mode
- ? (global_size ? global_size : (MAX_SIZE * 2 / 3))
+ gcv.line_width = (st->mode == tail_mode
+ ? (st->global_size ? st->global_size : (MAX_SIZE * 2 / 3))
: 1);
- gcv.cap_style = (mode == tail_mode ? CapRound : CapButt);
+ gcv.cap_style = (st->mode == tail_mode ? CapRound : CapButt);
if (mono_p)
- gcv.foreground = get_pixel_resource("foreground", "Foreground", dpy, cmap);
+ gcv.foreground = get_pixel_resource(dpy, cmap, "foreground", "Foreground");
else
- gcv.foreground = colors[fg_index].pixel;
- draw_gc = XCreateGC (dpy, window, GCForeground|GCLineWidth|GCCapStyle, &gcv);
+ gcv.foreground = st->colors[st->fg_index].pixel;
+ st->draw_gc = XCreateGC (dpy, window, GCForeground|GCLineWidth|GCCapStyle, &gcv);
+
+ gcv.foreground = get_pixel_resource(dpy, cmap, "background", "Background");
+ st->erase_gc = XCreateGC (dpy, window, GCForeground|GCLineWidth|GCCapStyle,&gcv);
- gcv.foreground = get_pixel_resource("background", "Background", dpy, cmap);
- erase_gc = XCreateGC (dpy, window, GCForeground|GCLineWidth|GCCapStyle,&gcv);
+#ifdef HAVE_COCOA
+ jwxyz_XSetAntiAliasing (dpy, st->draw_gc, False);
+ jwxyz_XSetAntiAliasing (dpy, st->erase_gc, False);
+#endif
-#define rand_size() min (MAX_SIZE, 8 + (random () % (MAX_SIZE - 9)))
+ /* let's make the balls bigger by default */
+#define rand_size() (3 * (8 + (random () % 7)))
- if (orbit_p && !global_size)
+ if (st->orbit_p && !st->global_size)
/* To orbit, all objects must be the same mass, or the math gets
really hairy... */
- global_size = rand_size ();
+ st->global_size = rand_size ();
+ RETRY_NO_ORBIT:
th = frand (M_PI+M_PI);
- for (i = 0; i < npoints; i++)
+ for (i = 0; i < st->npoints; i++)
{
- int new_size = (global_size ? global_size : rand_size ());
- balls [i].dx = 0;
- balls [i].dy = 0;
- balls [i].size = new_size;
- balls [i].mass = (new_size * new_size * 10);
- balls [i].x = midx + r * cos (i * ((M_PI+M_PI) / npoints) + th);
- balls [i].y = midy + r * sin (i * ((M_PI+M_PI) / npoints) + th);
- if (! orbit_p)
+ int new_size = (st->global_size ? st->global_size : rand_size ());
+ st->balls [i].dx = 0;
+ st->balls [i].dy = 0;
+ st->balls [i].size = new_size;
+ st->balls [i].mass = (new_size * new_size * 10);
+ st->balls [i].x = midx + r * cos (i * ((M_PI+M_PI) / st->npoints) + th);
+ st->balls [i].y = midy + r * sin (i * ((M_PI+M_PI) / st->npoints) + th);
+ if (! st->orbit_p)
{
- balls [i].vx = vx ? vx : ((6.0 - (random () % 11)) / 8.0);
- balls [i].vy = vy ? vy : ((6.0 - (random () % 11)) / 8.0);
+ st->balls [i].vx = vx ? vx : ((6.0 - (random () % 11)) / 8.0);
+ st->balls [i].vy = vy ? vy : ((6.0 - (random () % 11)) / 8.0);
}
- if (mono_p || mode != ball_mode)
- balls [i].pixel_index = -1;
- else if (glow_p)
- balls [i].pixel_index = 0;
+ if (mono_p || st->mode != ball_mode)
+ st->balls [i].pixel_index = -1;
+ else if (st->glow_p)
+ st->balls [i].pixel_index = 0;
else
- balls [i].pixel_index = random() % ncolors;
+ st->balls [i].pixel_index = random() % st->ncolors;
}
- if (orbit_p)
+ /* This lets modes where the points don't really have any size use the whole
+ window. Otherwise, since the points still have a positive size
+ assigned to them, they will be bounced somewhat early. Mass and size are
+ seperate, so this shouldn't cause problems. It's a bit kludgy, tho.
+ */
+ if(st->mode == line_mode || st->mode == spline_mode ||
+ st->mode == spline_filled_mode || st->mode == polygon_mode)
+ {
+ for(i = 1; i < st->npoints; i++)
+ {
+ st->balls[i].size = 0;
+ }
+ }
+
+ if (st->orbit_p)
{
double a = 0;
double v;
- double v_mult = get_float_resource ("vMult", "Float");
+ double v_mult = get_float_resource (dpy, "vMult", "Float");
if (v_mult == 0.0) v_mult = 1.0;
- for (i = 1; i < npoints; i++)
+ for (i = 1; i < st->npoints; i++)
{
- double _2ipi_n = (2 * i * M_PI / npoints);
+ double _2ipi_n = (2 * i * M_PI / st->npoints);
double x = r * cos (_2ipi_n);
double y = r * sin (_2ipi_n);
double distx = r - x;
double dist2 = (distx * distx) + (y * y);
double dist = sqrt (dist2);
- double a1 = ((balls[i].mass / dist2) *
- ((dist < threshold) ? -1.0 : 1.0) *
+ double a1 = ((st->balls[i].mass / dist2) *
+ ((dist < st->threshold) ? -1.0 : 1.0) *
(distx / dist));
a += a1;
}
if (a < 0.0)
{
- fprintf (stderr, "%s: domain error: forces on balls too great\n",
+ /* "domain error: forces on balls too great" */
+ fprintf (stderr, "%s: window too small for these orbit settings.\n",
progname);
- exit (-1);
+ st->orbit_p = False;
+ goto RETRY_NO_ORBIT;
}
v = sqrt (a * r) * v_mult;
- for (i = 0; i < npoints; i++)
+ for (i = 0; i < st->npoints; i++)
{
- double k = ((2 * i * M_PI / npoints) + th);
- balls [i].vx = -v * sin (k);
- balls [i].vy = v * cos (k);
+ double k = ((2 * i * M_PI / st->npoints) + th);
+ st->balls [i].vx = -v * sin (k);
+ st->balls [i].vy = v * cos (k);
}
}
- if (mono_p) glow_p = False;
+ if (mono_p) st->glow_p = False;
+
XClearWindow (dpy, window);
+ return st;
}
static void
-compute_force (int i, double *dx_ret, double *dy_ret)
+compute_force (struct state *st, int i, double *dx_ret, double *dy_ret)
{
int j;
double x_dist, y_dist, dist, dist2;
*dx_ret = 0;
*dy_ret = 0;
- for (j = 0; j < npoints; j++)
+ for (j = 0; j < st->npoints; j++)
{
if (i == j) continue;
- x_dist = balls [j].x - balls [i].x;
- y_dist = balls [j].y - balls [i].y;
+ x_dist = st->balls [j].x - st->balls [i].x;
+ y_dist = st->balls [j].y - st->balls [i].y;
dist2 = (x_dist * x_dist) + (y_dist * y_dist);
dist = sqrt (dist2);
if (dist > 0.1) /* the balls are not overlapping */
{
- double new_acc = ((balls[j].mass / dist2) *
- ((dist < threshold) ? -1.0 : 1.0));
+ double new_acc = ((st->balls[j].mass / dist2) *
+ ((dist < st->threshold) ? -1.0 : 1.0));
double new_acc_dist = new_acc / dist;
*dx_ret += new_acc_dist * x_dist;
*dy_ret += new_acc_dist * y_dist;
*dy_ret += (frand (10.0) - 5.0);
}
}
+}
+
+
+/* Draws meters along the diagonal for the x velocity */
+static void
+draw_meter_x(Display *dpy, Window window, struct state *st, int i, int alone)
+{
+ XWindowAttributes xgwa;
+ int x1,x2,y,w1,w2,h;
+ XGetWindowAttributes (dpy, window, &xgwa);
- if (mouse_p)
+ /* set the width of the bars to use */
+ if(xgwa.height < BAR_SIZE*st->npoints)
{
- x_dist = mouse_x - balls [i].x;
- y_dist = mouse_y - balls [i].y;
- dist2 = (x_dist * x_dist) + (y_dist * y_dist);
- dist = sqrt (dist2);
-
- if (dist > 0.1) /* the balls are not overlapping */
- {
- double new_acc = ((mouse_mass / dist2) *
- ((dist < threshold) ? -1.0 : 1.0));
- double new_acc_dist = new_acc / dist;
- *dx_ret += new_acc_dist * x_dist;
- *dy_ret += new_acc_dist * y_dist;
- }
- else
- { /* the balls are overlapping; move randomly */
- *dx_ret += (frand (10.0) - 5.0);
- *dy_ret += (frand (10.0) - 5.0);
- }
+ y = i*(xgwa.height/st->npoints);
+ h = (xgwa.height/st->npoints) - 2;
+ }
+ else
+ {
+ y = BAR_SIZE*i;
+ h = BAR_SIZE - 2;
+ }
+
+ if(alone)
+ {
+ x1 = xgwa.width/2;
+ x2 = x1;
}
+ else
+ {
+ x1 = i*(h+2);
+ if(x1 < i)
+ x1 = i;
+ x2 = x1;
+ }
+
+ if(y<1) y=i;
+ if(h<1) h=1;
+
+ w1 = (int)(20*st->x_vels[i]);
+ w2 = (int)(20*st->balls[i].vx);
+ st->x_vels[i] = st->balls[i].vx;
+
+ if (w1<0) {
+ w1=-w1;
+ x1=x1-w1;
+ }
+ if (w2<0) {
+ w2=-w2;
+ x2=x2-w2;
+ }
+ XDrawRectangle(dpy,window,st->erase_gc,x1+(h+2)/2,y,w1,h);
+ XDrawRectangle(dpy,window,st->draw_gc,x2+(h+2)/2,y,w2,h);
}
-static void
-run_balls (Display *dpy, Window window)
+/* Draws meters along the diagonal for the y velocity.
+ Is there some way to make draw_meter_x and draw_meter_y
+ one function instead of two without making them completely unreadable?
+*/
+static void
+draw_meter_y (Display *dpy, Window window, struct state *st, int i, int alone)
{
- int last_point_stack_fp = point_stack_fp;
- static int tick = 500, xlim, ylim;
- static Colormap cmap;
- int i;
+ XWindowAttributes xgwa;
+ int y1,y2,x,h1,h2,w;
+ XGetWindowAttributes (dpy, window, &xgwa);
- /*flip mods for mouse interaction*/
- Window root1, child1;
- unsigned int mask;
- if (mouse_p)
+ if(xgwa.height < BAR_SIZE*st->npoints){ /*needs to be height still */
+ x = i*(xgwa.height/st->npoints);
+ w = (xgwa.height/st->npoints) - 2;
+ }
+ else{
+ x = BAR_SIZE*i;
+ w = BAR_SIZE - 2;
+ }
+
+ if(alone)
+ {
+ y1 = xgwa.height/2;
+ y2 = y1;
+ }
+ else
{
- XQueryPointer(dpy, window, &root1, &child1,
- &root_x, &root_y, &mouse_x, &mouse_y, &mask);
+ y1 = i*(w+2);
+ if(y1 < i)
+ y1 = i;
+ y2 = y1;
}
- if (tick++ == 500)
+ if(x < 1) x = i;
+ if(w < 1) w = 1;
+
+ h1 = (int)(20*st->y_vels[i]);
+ h2 = (int)(20*st->balls[i].vy);
+ st->y_vels[i] = st->balls[i].vy;
+
+ if (h1<0) {
+ h1=-h1;
+ y1=y1-h1;
+ }
+ if (h2<0) {
+ h2=-h2;
+ y2=y2-h2;
+ }
+ XDrawRectangle(dpy,window,st->erase_gc,x,y1+(w+2)/2,w,h1);
+ XDrawRectangle(dpy,window,st->draw_gc,x,y2+(w+2)/2,w,h2);
+}
+
+
+/* Draws meters of the total speed of the balls */
+static void
+draw_meter_speed (Display *dpy, Window window, struct state *st, int i)
+{
+ XWindowAttributes xgwa;
+ int y,x1,x2,h,w1,w2;
+ XGetWindowAttributes (dpy, window, &xgwa);
+
+ if(xgwa.height < BAR_SIZE*st->npoints)
{
- XWindowAttributes xgwa;
- XGetWindowAttributes (dpy, window, &xgwa);
- tick = 0;
- xlim = xgwa.width;
- ylim = xgwa.height;
- cmap = xgwa.colormap;
+ y = i*(xgwa.height/st->npoints);
+ h = (xgwa.height/st->npoints) - 2;
}
+ else{
+ y = BAR_SIZE*i;
+ h = BAR_SIZE - 2;
+ }
- /* compute the force of attraction/repulsion among all balls */
- for (i = 0; i < npoints; i++)
- compute_force (i, &balls[i].dx, &balls[i].dy);
+ x1 = 0;
+ x2 = x1;
- /* move the balls according to the forces now in effect */
- for (i = 0; i < npoints; i++)
+ if(y < 1) y = i;
+ if(h < 1) h = 1;
+
+ w1 = (int)(5*st->speeds[i]);
+ w2 = (int)(5*(st->balls[i].vy*st->balls[i].vy+st->balls[i].vx*st->balls[i].vx));
+ st->speeds[i] = st->balls[i].vy*st->balls[i].vy+st->balls[i].vx*st->balls[i].vx;
+
+ XDrawRectangle(dpy,window,st->erase_gc,x1,y,w1,h);
+ XDrawRectangle(dpy,window,st->draw_gc, x2,y,w2,h);
+}
+
+/* Returns the position of the mouse relative to the root window.
+ */
+static void
+query_mouse (Display *dpy, Window win, int *x, int *y)
+{
+ Window root1, child1;
+ int mouse_x, mouse_y, root_x, root_y;
+ unsigned int mask;
+ if (XQueryPointer (dpy, win, &root1, &child1,
+ &root_x, &root_y, &mouse_x, &mouse_y, &mask))
{
- double old_x = balls[i].x;
- double old_y = balls[i].y;
- double new_x, new_y;
- int size = balls[i].size;
- balls[i].vx += balls[i].dx;
- balls[i].vy += balls[i].dy;
+ *x = mouse_x;
+ *y = mouse_y;
+ }
+ else
+ {
+ *x = -9999;
+ *y = -9999;
+ }
+}
- /* don't let them get too fast: impose a terminal velocity
- (actually, make the medium have friction) */
- if (balls[i].vx > 10)
+static unsigned long
+attraction_draw (Display *dpy, Window window, void *closure)
+{
+ struct state *st = (struct state *) closure;
+ int last_point_stack_fp = st->point_stack_fp;
+
+ int i, radius = st->global_size/2;
+
+ st->total_ticks++;
+
+ if(st->global_size == 0)
+ radius = (MAX_SIZE / 3);
+
+ if(st->graph_mode != graph_none)
+ {
+ if(st->graph_mode == graph_both)
{
- balls[i].vx *= 0.9;
- balls[i].dx = 0;
+ for(i = 0; i < st->npoints; i++)
+ {
+ draw_meter_x(dpy, window, st, i, 0);
+ draw_meter_y(dpy, window, st, i, 0);
+ }
}
- else if (viscosity != 1)
+ else if(st->graph_mode == graph_x)
{
- balls[i].vx *= viscosity;
+ for(i = 0; i < st->npoints; i++)
+ {
+ draw_meter_x(dpy, window, st, i, 1);
+ }
}
-
- if (balls[i].vy > 10)
+ else if(st->graph_mode == graph_y)
{
- balls[i].vy *= 0.9;
- balls[i].dy = 0;
+ for(i = 0; i < st->npoints; i++)
+ {
+ draw_meter_y(dpy, window, st, i, 1);
+ }
}
- else if (viscosity != 1)
+ else if(st->graph_mode == graph_speed)
{
- balls[i].vy *= viscosity;
+ for(i = 0; i < st->npoints; i++)
+ {
+ draw_meter_speed(dpy, window, st, i);
+ }
}
- balls[i].x += balls[i].vx;
- balls[i].y += balls[i].vy;
+ }
- /* bounce off the walls */
- if (balls[i].x >= (xlim - balls[i].size))
+ /* compute the force of attraction/repulsion among all balls */
+ for (i = 0; i < st->npoints; i++)
+ compute_force (st, i, &st->balls[i].dx, &st->balls[i].dy);
+
+ /* move the balls according to the forces now in effect */
+ for (i = 0; i < st->npoints; i++)
+ {
+ double old_x = st->balls[i].x;
+ double old_y = st->balls[i].y;
+ double new_x, new_y;
+ int size = st->balls[i].size;
+
+ st->balls[i].vx += st->balls[i].dx;
+ st->balls[i].vy += st->balls[i].dy;
+
+ /* "don't let them get too fast: impose a terminal velocity
+ (actually, make the medium have friction)"
+ Well, what this first step really does is give the medium a
+ viscosity of .9 for balls going over the speed limit. Anyway,
+ this is now optional
+ */
+ if (fabs(st->balls[i].vx) > 10 && st->maxspeed_p)
{
- balls[i].x = (xlim - balls[i].size - 1);
- if (balls[i].vx > 0)
- balls[i].vx = -balls[i].vx;
+ st->balls[i].vx *= 0.9;
+ st->balls[i].dx = 0;
}
- if (balls[i].y >= (ylim - balls[i].size))
+ if (st->viscosity != 1)
{
- balls[i].y = (ylim - balls[i].size - 1);
- if (balls[i].vy > 0)
- balls[i].vy = -balls[i].vy;
+ st->balls[i].vx *= st->viscosity;
}
- if (balls[i].x <= 0)
+
+ if (fabs(st->balls[i].vy) > 10 && st->maxspeed_p)
{
- balls[i].x = 0;
- if (balls[i].vx < 0)
- balls[i].vx = -balls[i].vx;
+ st->balls[i].vy *= 0.9;
+ st->balls[i].dy = 0;
}
- if (balls[i].y <= 0)
+ if (st->viscosity != 1)
{
- balls[i].y = 0;
- if (balls[i].vy < 0)
- balls[i].vy = -balls[i].vy;
+ st->balls[i].vy *= st->viscosity;
}
- new_x = balls[i].x;
- new_y = balls[i].y;
+ st->balls[i].x += st->balls[i].vx;
+ st->balls[i].y += st->balls[i].vy;
+
+
+ /* bounce off the walls if desired
+ note: a ball is actually its upper left corner */
+ if(st->walls_p)
+ {
+ if(st->cbounce_p) /* with correct bouncing */
+ {
+ /* so long as it's out of range, keep bouncing */
+ /* limit the maximum number to bounce to 4.*/
+ int bounce_allowed = 4;
+
+ while( bounce_allowed && (
+ (st->balls[i].x >= (st->xlim - st->balls[i].size)) ||
+ (st->balls[i].y >= (st->ylim - st->balls[i].size)) ||
+ (st->balls[i].x <= 0) ||
+ (st->balls[i].y <= 0) )
+ )
+ {
+ bounce_allowed--;
+ if (st->balls[i].x >= (st->xlim - st->balls[i].size))
+ {
+ st->balls[i].x = (2*(st->xlim - st->balls[i].size) - st->balls[i].x);
+ st->balls[i].vx = -st->balls[i].vx;
+ }
+ if (st->balls[i].y >= (st->ylim - st->balls[i].size))
+ {
+ st->balls[i].y = (2*(st->ylim - st->balls[i].size) - st->balls[i].y);
+ st->balls[i].vy = -st->balls[i].vy;
+ }
+ if (st->balls[i].x <= 0)
+ {
+ st->balls[i].x = -st->balls[i].x;
+ st->balls[i].vx = -st->balls[i].vx;
+ }
+ if (st->balls[i].y <= 0)
+ {
+ st->balls[i].y = -st->balls[i].y;
+ st->balls[i].vy = -st->balls[i].vy;
+ }
+ }
+ }
+ else /* with old bouncing */
+ {
+ if (st->balls[i].x >= (st->xlim - st->balls[i].size))
+ {
+ st->balls[i].x = (st->xlim - st->balls[i].size - 1);
+ if (st->balls[i].vx > 0) /* why is this check here? */
+ st->balls[i].vx = -st->balls[i].vx;
+ }
+ if (st->balls[i].y >= (st->ylim - st->balls[i].size))
+ {
+ st->balls[i].y = (st->ylim - st->balls[i].size - 1);
+ if (st->balls[i].vy > 0)
+ st->balls[i].vy = -st->balls[i].vy;
+ }
+ if (st->balls[i].x <= 0)
+ {
+ st->balls[i].x = 0;
+ if (st->balls[i].vx < 0)
+ st->balls[i].vx = -st->balls[i].vx;
+ }
+ if (st->balls[i].y <= 0)
+ {
+ st->balls[i].y = 0;
+ if (st->balls[i].vy < 0)
+ st->balls[i].vy = -st->balls[i].vy;
+ }
+ }
+ }
+
+ if (i == st->mouse_ball)
+ {
+ int x, y;
+ query_mouse (dpy, window, &x, &y);
+ if (st->mode == ball_mode)
+ {
+ x -= st->balls[i].size / 2;
+ y -= st->balls[i].size / 2;
+ }
+
+ st->balls[i].x = x;
+ st->balls[i].y = y;
+ }
+
+ new_x = st->balls[i].x;
+ new_y = st->balls[i].y;
if (!mono_p)
{
- if (mode == ball_mode)
+ if (st->mode == ball_mode)
{
- if (glow_p)
+ if (st->glow_p)
{
/* make color saturation be related to particle
acceleration. */
double limit = 0.5;
double s, fraction;
- double vx = balls [i].dx;
- double vy = balls [i].dy;
+ double vx = st->balls [i].dx;
+ double vy = st->balls [i].dy;
if (vx < 0) vx = -vx;
if (vy < 0) vy = -vy;
fraction = vx + vy;
if (fraction > limit) fraction = limit;
s = 1 - (fraction / limit);
- balls[i].pixel_index = (ncolors * s);
+ st->balls[i].pixel_index = (st->ncolors * s);
}
- XSetForeground (dpy, draw_gc,
- colors[balls[i].pixel_index].pixel);
+ XSetForeground (dpy, st->draw_gc,
+ (i == st->mouse_ball
+ ? st->mouse_pixel
+ : st->colors[st->balls[i].pixel_index].pixel));
}
}
- if (mode == ball_mode)
+ if (st->mode == ball_mode)
{
- XFillArc (dpy, window, erase_gc, (int) old_x, (int) old_y,
+ XFillArc (dpy, window, st->erase_gc, (int) old_x, (int) old_y,
size, size, 0, 360*64);
- XFillArc (dpy, window, draw_gc, (int) new_x, (int) new_y,
+ XFillArc (dpy, window, st->draw_gc, (int) new_x, (int) new_y,
size, size, 0, 360*64);
}
else
{
- point_stack [point_stack_fp].x = new_x;
- point_stack [point_stack_fp].y = new_y;
- point_stack_fp++;
+ st->point_stack [st->point_stack_fp].x = new_x;
+ st->point_stack [st->point_stack_fp].y = new_y;
+ st->point_stack_fp++;
}
}
/* draw the lines or polygons after computing all points */
- if (mode != ball_mode)
+ if (st->mode != ball_mode)
{
- point_stack [point_stack_fp].x = balls [0].x; /* close the polygon */
- point_stack [point_stack_fp].y = balls [0].y;
- point_stack_fp++;
- if (point_stack_fp == point_stack_size)
- point_stack_fp = 0;
- else if (point_stack_fp > point_stack_size) /* better be aligned */
+ st->point_stack [st->point_stack_fp].x = st->balls [0].x; /* close the polygon */
+ st->point_stack [st->point_stack_fp].y = st->balls [0].y;
+ st->point_stack_fp++;
+ if (st->point_stack_fp == st->point_stack_size)
+ st->point_stack_fp = 0;
+ else if (st->point_stack_fp > st->point_stack_size) /* better be aligned */
abort ();
if (!mono_p)
{
- static int tick = 0;
- if (tick++ == color_shift)
+ if (st->color_tick++ == st->color_shift)
{
- tick = 0;
- fg_index = (fg_index + 1) % ncolors;
- XSetForeground (dpy, draw_gc, colors[fg_index].pixel);
+ st->color_tick = 0;
+ st->fg_index = (st->fg_index + 1) % st->ncolors;
+ XSetForeground (dpy, st->draw_gc, st->colors[st->fg_index].pixel);
}
}
}
- switch (mode)
+ switch (st->mode)
{
case ball_mode:
break;
case line_mode:
- if (segments > 0)
- XDrawLines (dpy, window, erase_gc, point_stack + point_stack_fp,
- npoints + 1, CoordModeOrigin);
- XDrawLines (dpy, window, draw_gc, point_stack + last_point_stack_fp,
- npoints + 1, CoordModeOrigin);
+ if (st->segments > 0)
+ XDrawLines (dpy, window, st->erase_gc, st->point_stack + st->point_stack_fp,
+ st->npoints + 1, CoordModeOrigin);
+ XDrawLines (dpy, window, st->draw_gc, st->point_stack + last_point_stack_fp,
+ st->npoints + 1, CoordModeOrigin);
break;
case polygon_mode:
- if (segments > 0)
- XFillPolygon (dpy, window, erase_gc, point_stack + point_stack_fp,
- npoints + 1, (npoints == 3 ? Convex : Complex),
+ if (st->segments > 0)
+ XFillPolygon (dpy, window, st->erase_gc, st->point_stack + st->point_stack_fp,
+ st->npoints + 1, (st->npoints == 3 ? Convex : Complex),
CoordModeOrigin);
- XFillPolygon (dpy, window, draw_gc, point_stack + last_point_stack_fp,
- npoints + 1, (npoints == 3 ? Convex : Complex),
+ XFillPolygon (dpy, window, st->draw_gc, st->point_stack + last_point_stack_fp,
+ st->npoints + 1, (st->npoints == 3 ? Convex : Complex),
CoordModeOrigin);
break;
case tail_mode:
{
- for (i = 0; i < npoints; i++)
+ for (i = 0; i < st->npoints; i++)
{
- int index = point_stack_fp + i;
- int next_index = (index + (npoints + 1)) % point_stack_size;
- XDrawLine (dpy, window, erase_gc,
- point_stack [index].x,
- point_stack [index].y,
- point_stack [next_index].x,
- point_stack [next_index].y);
-
+ int index = st->point_stack_fp + i;
+ int next_index = (index + (st->npoints + 1)) % st->point_stack_size;
+ if(st->no_erase_yet == 1)
+ {
+ if(st->total_ticks >= st->segments)
+ {
+ st->no_erase_yet = 0;
+ XDrawLine (dpy, window, st->erase_gc,
+ st->point_stack [index].x + radius,
+ st->point_stack [index].y + radius,
+ st->point_stack [next_index].x + radius,
+ st->point_stack [next_index].y + radius);
+ }
+ }
+ else
+ {
+ XDrawLine (dpy, window, st->erase_gc,
+ st->point_stack [index].x + radius,
+ st->point_stack [index].y + radius,
+ st->point_stack [next_index].x + radius,
+ st->point_stack [next_index].y + radius);
+ }
index = last_point_stack_fp + i;
- next_index = (index - (npoints + 1)) % point_stack_size;
- if (next_index < 0) next_index += point_stack_size;
- if (point_stack [next_index].x == 0 &&
- point_stack [next_index].y == 0)
+ next_index = (index - (st->npoints + 1)) % st->point_stack_size;
+ if (next_index < 0) next_index += st->point_stack_size;
+ if (st->point_stack [next_index].x == 0 &&
+ st->point_stack [next_index].y == 0)
continue;
- XDrawLine (dpy, window, draw_gc,
- point_stack [index].x,
- point_stack [index].y,
- point_stack [next_index].x,
- point_stack [next_index].y);
+ XDrawLine (dpy, window, st->draw_gc,
+ st->point_stack [index].x + radius,
+ st->point_stack [index].y + radius,
+ st->point_stack [next_index].x + radius,
+ st->point_stack [next_index].y + radius);
}
}
break;
case spline_mode:
case spline_filled_mode:
{
- static spline *s = 0;
- if (! s) s = make_spline (npoints);
- if (segments > 0)
+ if (! st->spl) st->spl = make_spline (st->npoints);
+ if (st->segments > 0)
{
- for (i = 0; i < npoints; i++)
+ for (i = 0; i < st->npoints; i++)
{
- s->control_x [i] = point_stack [point_stack_fp + i].x;
- s->control_y [i] = point_stack [point_stack_fp + i].y;
+ st->spl->control_x [i] = st->point_stack [st->point_stack_fp + i].x;
+ st->spl->control_y [i] = st->point_stack [st->point_stack_fp + i].y;
}
- compute_closed_spline (s);
- if (mode == spline_filled_mode)
- XFillPolygon (dpy, window, erase_gc, s->points, s->n_points,
- (s->n_points == 3 ? Convex : Complex),
+ compute_closed_spline (st->spl);
+ if (st->mode == spline_filled_mode)
+ XFillPolygon (dpy, window, st->erase_gc, st->spl->points, st->spl->n_points,
+ (st->spl->n_points == 3 ? Convex : Complex),
CoordModeOrigin);
else
- XDrawLines (dpy, window, erase_gc, s->points, s->n_points,
+ XDrawLines (dpy, window, st->erase_gc, st->spl->points, st->spl->n_points,
CoordModeOrigin);
}
- for (i = 0; i < npoints; i++)
+ for (i = 0; i < st->npoints; i++)
{
- s->control_x [i] = point_stack [last_point_stack_fp + i].x;
- s->control_y [i] = point_stack [last_point_stack_fp + i].y;
+ st->spl->control_x [i] = st->point_stack [last_point_stack_fp + i].x;
+ st->spl->control_y [i] = st->point_stack [last_point_stack_fp + i].y;
}
- compute_closed_spline (s);
- if (mode == spline_filled_mode)
- XFillPolygon (dpy, window, draw_gc, s->points, s->n_points,
- (s->n_points == 3 ? Convex : Complex),
+ compute_closed_spline (st->spl);
+ if (st->mode == spline_filled_mode)
+ XFillPolygon (dpy, window, st->draw_gc, st->spl->points, st->spl->n_points,
+ (st->spl->n_points == 3 ? Convex : Complex),
CoordModeOrigin);
else
- XDrawLines (dpy, window, draw_gc, s->points, s->n_points,
+ XDrawLines (dpy, window, st->draw_gc, st->spl->points, st->spl->n_points,
CoordModeOrigin);
}
break;
abort ();
}
- XSync (dpy, True);
+ return st->delay;
}
-\f
-char *progclass = "Attraction";
+static void
+attraction_reshape (Display *dpy, Window window, void *closure,
+ unsigned int w, unsigned int h)
+{
+ struct state *st = (struct state *) closure;
+ st->xlim = w;
+ st->ylim = h;
+}
+
+static Bool
+attraction_event (Display *dpy, Window window, void *closure, XEvent *event)
+{
+ struct state *st = (struct state *) closure;
+
+ if (event->xany.type == ButtonPress)
+ {
+ int i;
+ if (st->mouse_ball != -1) /* second down-click? drop the ball. */
+ {
+ st->mouse_ball = -1;
+ return True;
+ }
+ else
+ {
+ /* When trying to pick up a ball, first look for a click directly
+ inside the ball; but if we don't find it, expand the radius
+ outward until we find something nearby.
+ */
+ int x = event->xbutton.x;
+ int y = event->xbutton.y;
+ float max = 10 * (st->global_size ? st->global_size : MAX_SIZE);
+ float step = max / 100;
+ float r2;
+ for (r2 = step; r2 < max; r2 += step)
+ {
+ for (i = 0; i < st->npoints; i++)
+ {
+ float d = ((st->balls[i].x - x) * (st->balls[i].x - x) +
+ (st->balls[i].y - y) * (st->balls[i].y - y));
+ float r = st->balls[i].size;
+ if (r2 > r) r = r2;
+ if (d < r*r)
+ {
+ st->mouse_ball = i;
+ return True;
+ }
+ }
+ }
+ }
+ return True;
+ }
+ else if (event->xany.type == ButtonRelease) /* drop the ball */
+ {
+ st->mouse_ball = -1;
+ return True;
+ }
+
+
-char *defaults [] = {
+ return False;
+}
+
+static void
+attraction_free (Display *dpy, Window window, void *closure)
+{
+ struct state *st = (struct state *) closure;
+
+ if (st->balls) free (st->balls);
+ if (st->x_vels) free (st->x_vels);
+ if (st->y_vels) free (st->y_vels);
+ if (st->speeds) free (st->speeds);
+ if (st->point_stack) free (st->point_stack);
+ if (st->colors) free (st->colors);
+ if (st->spl) free_spline (st->spl);
+
+ free (st);
+}
+
+\f
+static const char *attraction_defaults [] = {
".background: black",
".foreground: white",
+ "*fpsSolid: true",
"*mode: balls",
+ "*graphmode: none",
"*points: 0",
"*size: 0",
"*colors: 200",
- "*threshold: 100",
+ "*threshold: 200",
"*delay: 10000",
"*glow: false",
- "*mouseSize: 10",
- "*mouse: false",
- "*viscosity: 1",
+ "*walls: true",
+ "*maxspeed: true",
+ "*cbounce: true",
+ "*viscosity: 1.0",
"*orbit: false",
"*colorShift: 3",
"*segments: 500",
"*vMult: 0.9",
+ "*radius: 0",
+ "*vx: 0",
+ "*vy: 0",
+ "*mouseForeground: white",
+#ifdef USE_IPHONE
+ "*ignoreRotation: True",
+#endif
0
};
-XrmOptionDescRec options [] = {
+static XrmOptionDescRec attraction_options [] = {
{ "-mode", ".mode", XrmoptionSepArg, 0 },
+ { "-graphmode", ".graphmode", XrmoptionSepArg, 0 },
{ "-colors", ".colors", XrmoptionSepArg, 0 },
{ "-points", ".points", XrmoptionSepArg, 0 },
{ "-color-shift", ".colorShift", XrmoptionSepArg, 0 },
{ "-vx", ".vx", XrmoptionSepArg, 0 },
{ "-vy", ".vy", XrmoptionSepArg, 0 },
{ "-vmult", ".vMult", XrmoptionSepArg, 0 },
- { "-mouse-size", ".mouseSize", XrmoptionSepArg, 0 },
- { "-mouse", ".mouse", XrmoptionNoArg, "true" },
- { "-nomouse", ".mouse", XrmoptionNoArg, "false" },
{ "-viscosity", ".viscosity", XrmoptionSepArg, 0 },
{ "-glow", ".glow", XrmoptionNoArg, "true" },
{ "-noglow", ".glow", XrmoptionNoArg, "false" },
{ "-orbit", ".orbit", XrmoptionNoArg, "true" },
+ { "-nowalls", ".walls", XrmoptionNoArg, "false" },
+ { "-walls", ".walls", XrmoptionNoArg, "true" },
+ { "-nomaxspeed", ".maxspeed", XrmoptionNoArg, "false" },
+ { "-maxspeed", ".maxspeed", XrmoptionNoArg, "true" },
+ { "-correct-bounce", ".cbounce", XrmoptionNoArg, "false" },
+ { "-fast-bounce", ".cbounce", XrmoptionNoArg, "true" },
{ 0, 0, 0, 0 }
};
-void
-screenhack (Display *dpy, Window window)
-{
- init_balls (dpy, window);
- while (1)
- {
- run_balls (dpy, window);
- if (delay) usleep (delay);
- }
-}
+
+XSCREENSAVER_MODULE ("Attraction", attraction)