/* kaleidescope, Copyright (c) 1997, 2006 Ron Tapia <tapia@nmia.com>
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation. No representations are made about the suitability of this
* software for any purpose. It is provided "as is" without express or
* implied warranty.
*/
/*
* The above, for lack of a better copyright statement in easy reach
* was just lifted from the xscreensaver source.
*
* One of the odd things about this hack is that the radial motion of the
* segments depends on roundoff error alone.
*
* I tried to make the source easy to add other shapes. So far, I've
* only messed with elipses and I couldn't do much with them that looked
* cool. A nice addition would be to add some sort of spline based shapes.
* Maybe rectangles would look nice.
*
*/
#include <stdio.h>
#include <math.h>
#include <time.h>
#include "screenhack.h"
#include "spline.h"
#define NEWX(x,y) ((x*g->costheta) + (y*g->sintheta))
#define NEWY(x,y) ((y*g->costheta) - (x*g->sintheta))
typedef struct state GLOBAL;
typedef struct Obj OBJECT;
struct Obj {
int type;
int time;
void (*propigate) (GLOBAL *, OBJECT *);
void (*draw) (GLOBAL *, OBJECT *);
void (*init) (GLOBAL *, OBJECT *);
void *cur, *root;
};
typedef struct KSEGMENT {
struct KSEGMENT *next, *next0;
XColor color;
int drawn;
short int x1,y1,x2,y2; /* these are in the natural coordinate system */
XSegment *xsegments; /* these are in the X coordinate system */
} Ksegment;
struct state {
int xoff, yoff; /* offset of origin xmax/2, ymax/2 */
int xmax, ymax; /* width, height of window */
float costheta, sintheta;
int symmetry;
int ntrails;
int nsegments;
int narcs;
int nobjects;
int local_rotation;
int global_rotation;
int spring_constant;
Colormap cmap;
GC draw_gc;
GC erase_gc;
unsigned int default_fg_pixel;
Display *dpy;
Window window;
unsigned long delay;
unsigned short redmin,redrange,greenmin,greenrange,bluemin,bluerange;
int color_mode;
OBJECT *objects;
int counter;
int done_once;
};
static const char *kaleidescope_defaults [] = {
".background: black",
".foreground: white",
"*fpsSolid: true",
"*color_mode: nice",
"*symmetry: 11",
"*ntrails: 100",
"*nsegments: 7",
"*narcs: 0",
"*local_rotation: -59",
"*global_rotation: 1",
"*spring_constant: 5",
"*delay: 20000",
"*redmin: 30000",
"*redrange: 20000",
"*greenmin: 30000",
"*greenrange: 20000",
"*bluemin: 30000",
"*bluerange: 20000",
#ifdef HAVE_MOBILE
"*ignoreRotation: True",
#endif
0
};
static XrmOptionDescRec kaleidescope_options [] = {
{ "-color_mode", ".color_mode", XrmoptionSepArg, 0 },
{ "-symmetry", ".symmetry", XrmoptionSepArg, 0 },
{ "-nsegments", ".nsegments", XrmoptionSepArg, 0 },
{ "-ntrails", ".ntrails", XrmoptionSepArg, 0 },
{ "-local_rotation", ".local_rotation", XrmoptionSepArg, 0 },
{ "-global_rotation", ".global_rotation", XrmoptionSepArg, 0 },
{ "-delay", ".delay", XrmoptionSepArg, 0 },
{ "-spring_constant", ".spring_constant", XrmoptionSepArg, 0 },
{ "-redmin", ".redmin", XrmoptionSepArg, 0 },
{ "-redrange", ".redrange", XrmoptionSepArg, 0 },
{ "-bluemin", ".bluemin", XrmoptionSepArg, 0 },
{ "-bluerange", ".bluerange", XrmoptionSepArg, 0 },
{ "-greenmin", ".greenmin", XrmoptionSepArg, 0 },
{ "-greenrange", ".greenrange", XrmoptionSepArg, 0 },
{ 0, 0, 0, 0 }
};
/* END global variables */
static void
krandom_color(GLOBAL *g, XColor *color)
{
if((g->color_mode == 0) || (g->color_mode == 1)) {
color->blue = (random() % g->bluerange) + g->bluemin;
color->green = (random() % g->greenrange) + g->greenmin;
color->red = (random() % g->redrange) + g->redmin;
if(!XAllocColor(g->dpy, g->cmap, color)) {
color->pixel = g->default_fg_pixel;
}
return;
} else {
color->pixel = g->default_fg_pixel;
return;
}
}
static void
kcopy_color(XColor *to, XColor *from)
{
to->red = from->red;
to->green = from->green;
to->blue = from->blue;
to->pixel = from->pixel;
}
static void
kcycle_color(GLOBAL *g,
XColor *color,
unsigned short redstep,
unsigned short greenstep,
unsigned short bluestep)
{
unsigned short red,green,blue;
if (! g->color_mode) {
XColor copy;
color->flags = DoRed|DoGreen|DoBlue;
color->red = (red = color->red) - redstep;
color->green = (green = color->green) - greenstep;
color->blue = (blue = color->blue) - bluestep;
copy = *color;
if(!XAllocColor(g->dpy, g->cmap, color)) {
/* printf("couldn't alloc color...\n"); */
color->pixel = g->default_fg_pixel;
}
copy.pixel = color->pixel;
*color = copy;
color->red = red - redstep;
color->green = green- greenstep;
color->blue = blue - bluestep;
return;
}
}
static Ksegment *
create_ksegment (GLOBAL *g)
{
Ksegment *seg, *prev;
XColor new_color;
int i;
unsigned short redstep,bluestep,greenstep;
krandom_color(g, &new_color);
redstep = new_color.red/(2 * g->ntrails);
greenstep = new_color.green/(2 * g->ntrails);
bluestep = new_color.blue/(2 * g->ntrails);
seg = (Ksegment *) malloc(sizeof(Ksegment));
seg->xsegments = (XSegment *) malloc(g->symmetry * sizeof(XSegment));
prev = seg;
for(i=0; i< (g->ntrails - 1); i++) {
kcycle_color(g, &new_color,redstep,greenstep,bluestep);
kcopy_color(&(prev->color), &new_color);
prev->next = (Ksegment*)calloc(1,sizeof(Ksegment));
(prev->next)->xsegments = (XSegment*)calloc(g->symmetry, sizeof(XSegment));
prev->drawn = 0;
prev->next0 = prev->next;
prev = (prev->next);
}
prev->drawn = 0;
prev->next = seg;
kcopy_color(&(prev->color), &new_color);
return seg;
}
static void
init_ksegment (GLOBAL *g, OBJECT *obj)
{
/* Give the segment some random values */
((Ksegment *)obj->cur)->x1 = (g->xoff ? random() % g->xoff : 0);
((Ksegment *)obj->cur)->y1 = (g->yoff ? random() % g->yoff : 0);
((Ksegment *)obj->cur)->x2 = (g->xoff ? random() % g->xoff : 0);
((Ksegment *)obj->cur)->y2 = (g->yoff ? random() % g->yoff : 0);
}
static void
draw_ksegment (GLOBAL *g, OBJECT *obj)
{
register short x1, y1, x2, y2;
int dx, dy;
int i;
g->counter++;
x1 = ((Ksegment *)obj->cur)->x1; /* in the natural coordinate system */
y1 = ((Ksegment *)obj->cur)->y1;
x2 = ((Ksegment *)obj->cur)->x2;
y2 = ((Ksegment *)obj->cur)->y2;
dx = x2 - x1;
dy = y2 - y1;
/* maybe throw away values and start over */
if( ((dx*dx) + (dy * dy)) < 100) {
init_ksegment (g, obj);
x1 = ((Ksegment *)obj->cur)->x1; /* in the natural coordinate system */
y1 = ((Ksegment *)obj->cur)->y1;
x2 = ((Ksegment *)obj->cur)->x2;
y2 = ((Ksegment *)obj->cur)->y2;
}
for (i=0; i<g->symmetry; i++) {
(((Ksegment *)obj->cur)->xsegments)[i].x1 = NEWX(x1,y1);
(((Ksegment *)obj->cur)->xsegments)[i].y1 = NEWY(x1,y1);
(((Ksegment *)obj->cur)->xsegments)[i].x2 = NEWX(x2,y2);
(((Ksegment *)obj->cur)->xsegments)[i].y2 = NEWY(x2,y2);
(((Ksegment *)obj->cur)->xsegments)[i].x1 = (x1 = (((Ksegment *)obj->cur)->xsegments)[i].x1) + g->xoff;
(((Ksegment *)obj->cur)->xsegments)[i].y1 = (y1 = (((Ksegment *)obj->cur)->xsegments)[i].y1) + g->yoff;
(((Ksegment *)obj->cur)->xsegments)[i].x2 = (x2 = (((Ksegment *)obj->cur)->xsegments)[i].x2) + g->xoff;
(((Ksegment *)obj->cur)->xsegments)[i].y2 = (y2 = (((Ksegment *)obj->cur)->xsegments)[i].y2) + g->yoff;
}
XSetForeground(g->dpy, g->draw_gc, (((Ksegment *)obj->cur)->color).pixel);
XDrawSegments(g->dpy, g->window, g->draw_gc, ((Ksegment *)obj->cur)->xsegments, g->symmetry);
((Ksegment *)obj->cur)->drawn = 1;
if (((((Ksegment *)obj->cur)->next)->drawn) != 0) {
XDrawSegments(g->dpy, g->window, g->erase_gc, ((Ksegment *)obj->cur)->next->xsegments, g->symmetry);
}
}
static void
propigate_ksegment(GLOBAL *g, OBJECT *obj)
{
int t;
short int x1,y1,x2,y2;
short int midx,midy,nmidx,nmidy;
float lsin, lcos, gsin, gcos;
lsin = sin((2*M_PI/10000)*g->local_rotation);
lcos = cos((2*M_PI/10000)*g->local_rotation);
gsin = sin((2*M_PI/10000)*g->global_rotation);
gcos = cos((2*M_PI/10000)*g->global_rotation);
t=obj->time;
obj->time = t + 1;
x1 = ((Ksegment *) obj->cur)->x1;
y1 = ((Ksegment *) obj->cur)->y1;
x2 = ((Ksegment *) obj->cur)->x2;
y2 = ((Ksegment *) obj->cur)->y2;
midx = (x1 + x2)/2;
midy = (y1 + y2)/2;
nmidx = midx*gcos + midy*gsin;
nmidy = midy*gcos - midx*gsin;
x1 = x1 - midx;
x2 = x2 - midx;
y1 = y1 - midy;
y2 = y2 - midy;
/* This is where we move to the next ksegment... */
obj->cur = ((Ksegment *)obj->cur)->next;
((Ksegment *)obj->cur)->x1 = ((x1*lcos) + (y1*lsin)) + nmidx;
((Ksegment *)obj->cur)->y1 = ((y1*lcos) - (x1*lsin)) + nmidy;
((Ksegment *)obj->cur)->x2 = ((x2*lcos) + (y2*lsin)) + nmidx;
((Ksegment *)obj->cur)->y2 = ((y2*lcos) - (x2*lsin)) + nmidy;
return ;
}
static void
init_objects (GLOBAL *g)
{
int i;
for (i=0; i<g->nobjects; i++) {
(g->objects[i].init)(g, g->objects + i);
}
}
static void
create_objects (GLOBAL *g)
{
int i;
g->objects = (OBJECT *) malloc(g->nobjects * sizeof(OBJECT));
for (i=0; i< g->nsegments; i++) {
g->objects[i].cur = g->objects[i].root = create_ksegment(g);
g->objects[i].type = 1;
g->objects[i].time = 0;
g->objects[i].propigate = propigate_ksegment;
g->objects[i].draw = draw_ksegment;
g->objects[i].init = init_ksegment;
}
/* Here we can add creation functions for other object types. */
}
static void
propigate_objects (GLOBAL *g)
{
int i;
for(i=0; i<g->nobjects; i++) {
g->objects[i].propigate(g, g->objects + i);
}
}
static void
draw_objects (GLOBAL *g)
{
int i;
for(i=0; i<g->nobjects; i++) {
g->objects[i].draw(g, g->objects + i);
}
}
static void
init_g (GLOBAL *g)
{
XWindowAttributes xgwa;
XGCValues gcv;
char *color_mode_str;
g->symmetry = get_integer_resource(g->dpy, "symmetry", "Integer");
g->ntrails = get_integer_resource(g->dpy, "ntrails" , "Integer");
g->nsegments = get_integer_resource(g->dpy, "nsegments", "Integer");
g->narcs = get_integer_resource(g->dpy, "narcs", "Integer");
g->local_rotation = get_integer_resource(g->dpy, "local_rotation", "Integer");
g->global_rotation = get_integer_resource(g->dpy, "global_rotation", "Integer");
g->spring_constant = get_integer_resource(g->dpy, "spring_constant", "Integer");
g->delay = get_integer_resource(g->dpy, "delay", "Integer");
g->nobjects = g->nsegments + g->narcs;
color_mode_str = get_string_resource(g->dpy, "color_mode", "color_mode");
/* make into an enum... */
if(!color_mode_str) {
g->color_mode = 0;
} else if (!strcmp(color_mode_str, "greedy")) {
g->color_mode = 0;
} else if (!strcmp(color_mode_str, "nice")) {
g->color_mode = 1;
} else {
g->color_mode = 2;
}
if (color_mode_str) free(color_mode_str);
XGetWindowAttributes (g->dpy, g->window, &xgwa);
g->xmax = xgwa.width;
g->ymax = xgwa.height;
g->xoff = g->xmax/2;
g->yoff = g->ymax/2;
g->costheta = cos(2*M_PI/g->symmetry);
g->sintheta = sin(2*M_PI/g->symmetry);
g->cmap = xgwa.colormap;
g->redmin = get_integer_resource(g->dpy, "redmin", "Integer");
g->redrange = get_integer_resource(g->dpy, "redrange", "Integer");
g->greenmin = get_integer_resource(g->dpy, "greenmin", "Integer");
g->greenrange = get_integer_resource(g->dpy, "greenrange", "Integer");
g->bluemin = get_integer_resource(g->dpy, "bluemin", "Integer");
g->bluerange = get_integer_resource(g->dpy, "bluerange", "Integer");
gcv.line_width = 1;
gcv.cap_style = CapRound;
gcv.foreground = g->default_fg_pixel = get_pixel_resource (g->dpy, g->cmap, "foreground", "Foreground");
g->draw_gc = XCreateGC (g->dpy, g->window, GCForeground|GCLineWidth|GCCapStyle, &gcv);
gcv.foreground = get_pixel_resource (g->dpy, g->cmap, "background", "Background");
g->erase_gc = XCreateGC (g->dpy, g->window, GCForeground|GCLineWidth|GCCapStyle,&gcv);
# ifdef HAVE_JWXYZ
jwxyz_XSetAntiAliasing (g->dpy, g->draw_gc, False);
jwxyz_XSetAntiAliasing (g->dpy, g->erase_gc, False);
# endif
}
static void *
kaleidescope_init (Display *dpy, Window window)
{
GLOBAL *g = (GLOBAL *) calloc (1, sizeof(*g));
g->dpy = dpy;
g->window = window;
init_g (g);
create_objects(g);
init_objects (g);
return g;
}
static unsigned long
kaleidescope_draw (Display *dpy, Window window, void *closure)
{
GLOBAL *g = (GLOBAL *) closure;
if (g->done_once)
propigate_objects(g);
else
g->done_once = 1;
draw_objects (g);
return g->delay;
}
static void
kaleidescope_reshape (Display *dpy, Window window, void *closure,
unsigned int w, unsigned int h)
{
GLOBAL *g = (GLOBAL *) closure;
g->xmax = w;
g->ymax = h;
g->xoff = g->xmax/2;
g->yoff = g->ymax/2;
}
static Bool
kaleidescope_event (Display *dpy, Window window, void *closure, XEvent *event)
{
return False;
}
static void
kaleidescope_free (Display *dpy, Window window, void *closure)
{
GLOBAL *g = (GLOBAL *) closure;
int i;
for (i = 0; i < g->nsegments; i++) {
Ksegment *s = g->objects[i].root;
while (s) {
Ksegment *p = s;
s = s->next0;
free (p->xsegments);
free (p);
}
}
free (g->objects);
XFreeGC (dpy, g->draw_gc);
XFreeGC (dpy, g->erase_gc);
free (g);
}
XSCREENSAVER_MODULE ("Kaleidescope", kaleidescope)