-/* xscreensaver, Copyright (c) 1992 Jamie Zawinski <jwz@lucid.com>
+/* xscreensaver, Copyright (c) 1992, 1995, 1996, 1997, 1998
+ * 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>.
+ a Lispm screensaver by John Pezaris <pz@mit.edu>. Mouse control and
+ viscosity added by "Philip Edward Cutone, III" <pc2d+@andrew.cmu.edu>.
John sez:
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.
+
+ 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.
*/
-#include "screenhack.h"
-#include "spline.h"
#include <stdio.h>
#include <math.h>
-#if __STDC__
-#include <values.h>
-#endif
+#include "screenhack.h"
+#include "spline.h"
struct ball {
- float x, y;
- float vx, vy;
- float dx, dy;
- float mass;
+ double x, y;
+ double vx, vy;
+ double dx, dy;
+ double mass;
int size;
- XColor color;
+ int pixel_index;
int hue;
};
-static unsigned int default_fg_pixel;
static struct ball *balls;
static int npoints;
static int threshold;
static Bool glow_p;
static Bool orbit_p;
static XPoint *point_stack;
-static int point_stack_size, point_stack_fp, pixel_stack_fp, pixel_stack_size;
-static unsigned long *pixel_stack;
-static unsigned int color_shift;
+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;
-static enum color_mode {
- cycle_mode, random_mode
-} cmode;
-
static GC draw_gc, erase_gc;
#define MAX_SIZE 16
#define max(a,b) ((a)>(b)?(a):(b))
static void
-init_balls (dpy, window)
- Display *dpy;
- Window window;
+init_balls (Display *dpy, Window window)
{
int i;
XWindowAttributes xgwa;
glow_p = get_boolean_resource ("glow", "Boolean");
orbit_p = get_boolean_resource ("orbit", "Boolean");
color_shift = get_integer_resource ("colorShift", "Integer");
- if (color_shift >= 360) color_shift = 5;
+ 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;
exit (1);
}
- mode_str = get_string_resource ("colorMode", "ColorMode");
- if (! mode_str) cmode = cycle_mode;
- else if (!strcmp (mode_str, "cycle")) cmode = cycle_mode;
- else if (!strcmp (mode_str, "random")) cmode = random_mode;
- else {
- fprintf (stderr, "%s: colorMode must be cycle or random, not \"%s\"\n",
- progname, mode_str);
- exit (1);
- }
-
if (mode != ball_mode && mode != tail_mode) glow_p = False;
if (mode == polygon_mode && npoints < 3)
mode = line_mode;
+ ncolors = get_integer_resource ("colors", "Colors");
+ if (ncolors < 2) ncolors = 2;
+ if (ncolors <= 2) mono_p = True;
+ colors = 0;
+
+ if (!mono_p)
+ {
+ fg_index = 0;
+ switch (mode)
+ {
+ case ball_mode:
+ if (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,
+ False, True, False);
+ }
+ else
+ {
+ ncolors = npoints;
+ colors = (XColor *) malloc(sizeof(*colors) * (ncolors+1));
+ make_random_colormap (dpy, xgwa.visual, cmap, colors, &ncolors,
+ True, True, False, True);
+ }
+ break;
+ case line_mode:
+ case polygon_mode:
+ 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,
+ True, False, True);
+ break;
+ default:
+ abort ();
+ }
+ }
+
+ if (!mono_p && ncolors <= 2)
+ {
+ if (colors) free (colors);
+ colors = 0;
+ mono_p = True;
+ }
+
if (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;
- if (segments > 0)
- pixel_stack_size = segments;
- else
- pixel_stack_size = (360 / color_shift);
- pixel_stack = (unsigned long *)
- calloc (pixel_stack_size, sizeof (unsigned int));
- pixel_stack_fp = 0;
}
gcv.line_width = (mode == tail_mode
: 1);
gcv.cap_style = (mode == tail_mode ? CapRound : CapButt);
- gcv.foreground = default_fg_pixel =
- get_pixel_resource ("foreground", "Foreground", dpy, cmap);
+ if (mono_p)
+ gcv.foreground = get_pixel_resource("foreground", "Foreground", dpy, cmap);
+ else
+ gcv.foreground = colors[fg_index].pixel;
draw_gc = XCreateGC (dpy, window, GCForeground|GCLineWidth|GCCapStyle, &gcv);
- gcv.foreground = get_pixel_resource ("background", "Background", dpy, cmap);
+
+ gcv.foreground = get_pixel_resource("background", "Background", dpy, cmap);
erase_gc = XCreateGC (dpy, window, GCForeground|GCLineWidth|GCCapStyle,&gcv);
- if (!mono_p && mode != ball_mode)
- for (i = 0; i < pixel_stack_size; i++)
- {
- XColor color;
- color.pixel = default_fg_pixel;
- if (!XQueryColor (dpy, cmap, &color)) abort ();
- if (!XAllocColor (dpy, cmap, &color)) abort ();
- pixel_stack [i] = color.pixel;
- }
#define rand_size() min (MAX_SIZE, 8 + (random () % (MAX_SIZE - 9)))
balls [i].vx = vx ? vx : ((6.0 - (random () % 11)) / 8.0);
balls [i].vy = vy ? vy : ((6.0 - (random () % 11)) / 8.0);
}
- balls [i].color.pixel = default_fg_pixel;
- balls [i].color.flags = DoRed | DoGreen | DoBlue;
- if (!mono_p)
- {
- if (i != 0 && (glow_p || mode != ball_mode))
- balls [i].hue = balls [0].hue;
- else
- balls [i].hue = random () % 360;
- hsv_to_rgb (balls [i].hue, 1.0, 1.0,
- &balls [i].color.red, &balls [i].color.green,
- &balls [i].color.blue);
- if (!XAllocColor (dpy, cmap, &balls [i].color))
- mono_p = True; /* just give up */
- }
+ if (mono_p || mode != ball_mode)
+ balls [i].pixel_index = -1;
+ else if (glow_p)
+ balls [i].pixel_index = 0;
+ else
+ balls [i].pixel_index = random() % ncolors;
}
if (orbit_p)
}
static void
-compute_force (i, dx_ret, dy_ret)
- int i;
- float *dx_ret, *dy_ret;
+compute_force (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++)
{
- float x_dist, y_dist, dist, dist2;
-
if (i == j) continue;
x_dist = balls [j].x - balls [i].x;
y_dist = balls [j].y - balls [i].y;
if (dist > 0.1) /* the balls are not overlapping */
{
- float new_acc = ((balls[j].mass / dist2) *
- ((dist < threshold) ? -1.0 : 1.0));
- float new_acc_dist = new_acc / dist;
+ double new_acc = ((balls[j].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);
+ }
+ }
+
+ if (mouse_p)
+ {
+ 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;
}
}
static void
-run_balls (dpy, window)
- Display *dpy;
- Window window;
+run_balls (Display *dpy, Window window)
{
int last_point_stack_fp = point_stack_fp;
static int tick = 500, xlim, ylim;
static Colormap cmap;
int i;
+ /*flip mods for mouse interaction*/
+ Window root1, child1;
+ unsigned int mask;
+ if (mouse_p)
+ {
+ XQueryPointer(dpy, window, &root1, &child1,
+ &root_x, &root_y, &mouse_x, &mouse_y, &mask);
+ }
+
if (tick++ == 500)
{
XWindowAttributes xgwa;
/* move the balls according to the forces now in effect */
for (i = 0; i < npoints; i++)
{
- float old_x = balls[i].x;
- float old_y = balls[i].y;
- float new_x, new_y;
+ 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;
balls[i].vx *= 0.9;
balls[i].dx = 0;
}
+ else if (viscosity != 1)
+ {
+ balls[i].vx *= viscosity;
+ }
+
if (balls[i].vy > 10)
{
balls[i].vy *= 0.9;
balls[i].dy = 0;
}
+ else if (viscosity != 1)
+ {
+ balls[i].vy *= viscosity;
+ }
balls[i].x += balls[i].vx;
balls[i].y += balls[i].vy;
new_x = balls[i].x;
new_y = balls[i].y;
- /* make color saturation be related to particle acceleration. */
- if (glow_p)
+ if (!mono_p)
{
- float limit = 0.5;
- double s, v, fraction;
- float vx = balls [i].dx;
- float vy = balls [i].dy;
- XColor new_color;
- if (vx < 0) vx = -vx;
- if (vy < 0) vy = -vy;
- fraction = vx + vy;
- if (fraction > limit) fraction = limit;
-
- s = 1 - (fraction / limit);
- v = 1.0;
-
- s = (s * 0.75) + 0.25;
-
- hsv_to_rgb (balls [i].hue, s, v,
- &new_color.red, &new_color.green, &new_color.blue);
- if (XAllocColor (dpy, cmap, &new_color))
+ if (mode == ball_mode)
{
- XFreeColors (dpy, cmap, &balls [i].color.pixel, 1, 0);
- balls [i].color = new_color;
+ if (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;
+ 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);
+ }
+ XSetForeground (dpy, draw_gc,
+ colors[balls[i].pixel_index].pixel);
}
}
if (mode == ball_mode)
{
- if (!mono_p)
- XSetForeground (dpy, draw_gc, balls [i].color.pixel);
XFillArc (dpy, window, erase_gc, (int) old_x, (int) old_y,
size, size, 0, 360*64);
XFillArc (dpy, window, draw_gc, (int) new_x, (int) new_y,
size, size, 0, 360*64);
}
- if (mode != ball_mode)
+ else
{
point_stack [point_stack_fp].x = new_x;
point_stack [point_stack_fp].y = new_y;
abort ();
if (!mono_p)
{
- XColor color2;
- color2 = balls [0].color;
- switch (cmode)
+ static int tick = 0;
+ if (tick++ == color_shift)
{
- case cycle_mode:
- cycle_hue (&color2, color_shift);
- break;
- case random_mode:
- color2.red = random () % 65535;
- color2.green = random () % 65535;
- color2.blue = random () % 65535;
- break;
- default:
- abort ();
+ tick = 0;
+ fg_index = (fg_index + 1) % ncolors;
+ XSetForeground (dpy, draw_gc, colors[fg_index].pixel);
}
-
- if (!XAllocColor (dpy, cmap, &color2))
- {
- color2 = balls [0].color;
- if (!XAllocColor (dpy, cmap, &balls [0].color))
- abort ();
- }
- pixel_stack [pixel_stack_fp++] = balls [0].color.pixel;
- if (pixel_stack_fp >= pixel_stack_size)
- pixel_stack_fp = 0;
- XFreeColors (dpy, cmap, pixel_stack + pixel_stack_fp, 1, 0);
- balls [0].color = color2;
- XSetForeground (dpy, draw_gc, balls [0].color.pixel);
}
}
break;
case tail_mode:
{
- int i;
for (i = 0; i < npoints; i++)
{
int index = point_stack_fp + i;
case spline_mode:
case spline_filled_mode:
{
- int i;
static spline *s = 0;
if (! s) s = make_spline (npoints);
if (segments > 0)
abort ();
}
- XSync (dpy, True);
+ XSync (dpy, False);
}
\f
char *progclass = "Attraction";
char *defaults [] = {
- "*background: black",
- "*foreground: white",
+ ".background: black",
+ ".foreground: white",
"*mode: balls",
"*points: 0",
"*size: 0",
+ "*colors: 200",
"*threshold: 100",
"*delay: 10000",
"*glow: false",
+ "*mouseSize: 10",
+ "*mouse: false",
+ "*viscosity: 1",
"*orbit: false",
"*colorShift: 3",
- "*segments: 100",
+ "*segments: 500",
+ "*vMult: 0.9",
0
};
XrmOptionDescRec options [] = {
{ "-mode", ".mode", XrmoptionSepArg, 0 },
+ { "-colors", ".colors", XrmoptionSepArg, 0 },
{ "-points", ".points", XrmoptionSepArg, 0 },
+ { "-color-shift", ".colorShift", XrmoptionSepArg, 0 },
{ "-threshold", ".threshold", XrmoptionSepArg, 0 },
{ "-segments", ".segments", XrmoptionSepArg, 0 },
{ "-delay", ".delay", XrmoptionSepArg, 0 },
{ "-size", ".size", XrmoptionSepArg, 0 },
- { "-color-mode", ".colorMode", XrmoptionSepArg, 0 },
- { "-color-shift", ".colorShift", XrmoptionSepArg, 0 },
{ "-radius", ".radius", 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" }
+ { "-orbit", ".orbit", XrmoptionNoArg, "true" },
+ { 0, 0, 0, 0 }
};
-int options_size = (sizeof (options) / sizeof (options[0]));
void
-screenhack (dpy, window)
- Display *dpy;
- Window window;
+screenhack (Display *dpy, Window window)
{
init_balls (dpy, window);
while (1)
{
run_balls (dpy, window);
+ screenhack_handle_events (dpy);
if (delay) usleep (delay);
}
}